AAOS CarMediaService 服务框架

文章目录

      • 前言
      • MediaSession
      • CarMediaService
        • 作用是什么?提供了哪些接口?如何使用?
        • CarMediaService的实现
        • 总结

前言

CarMediaService 是AAOS中统一管理媒体播放控制、信息显示和用户交互等功能的服务。这一服务依赖于android MediaSession框架。 所以首先需要简单的了解一下MediaSession。

MediaSession

MediaSession 框架规范了音视频应用中界面与播放器之间的通信接口,实现界面与播放器之间的完全解耦。MediaSession 框架定义了媒体会话和媒体控制器两个重要的类,它们为构建多媒体播放器应用提供了一个完善的技术架构。
AAOS CarMediaService 服务框架_第1张图片
AAOS上面的MediaSession的控制框图如上主要是MediaControl和MediaSession两个类直接的交互。

  • MediaControl是UI端控制Service端的类,在AAOS中所有的app播放控制客户端的实现都是carMediaApp中MediaControl的实现的(包括蓝牙audio localplayer界面中暂停播放,下一首 上一首等等)。

  • MediaSession是服务端, 这个服务端包括(蓝牙的src\com\android\bluetooth,和/apps/Car/LocalMediaPlayer)。这这里面实现了Mediassion 的callback 用来响应client 端UI的控制。 而响应之后的状态改变可以通过继承MediaControl的callback 在客户端实现。

CarMediaService

作用是什么?提供了哪些接口?如何使用?
  • 用途

    CarMediaService通过CarMediaManager给外部应用提供使用的API。
    这些api允许开发者控制车辆中的主要媒体源,以及获取与这一媒体源相关的更新信息。通过这个 API 来实现媒体播放控制、信息显示和用户交互等功能。

  • 接口

    CarMediaManger是客户端 通过AIDL的接口 调用到CarMediaService中。 提供的AIDIL的接口如下:

  1. 获取/设置提供模式下当前活动的媒体源。
    其中模式包括播放和浏览: 是指应用的操作可以是浏览媒体 或者是操作播放媒体。
    其中MediaSource的理解:可以认为是执行具体播放操作的一个应用 是媒体播放控制的对象比如蓝牙音乐播放器、本地的播放器等等。
  2. 注册/反注册回调,监听媒体活动源的更新。同样模式可以是浏览或者播放。

综上可以看到carMediaService 是实现所有媒体相关的ui 浏览和控制的统一管理。 监听媒体源的变化,控制活动和非活动媒体源的播放、退出、暂停等等。

interface ICarMedia {
    /** Gets the currently active media source for the provided mode */
    ComponentName getMediaSource(int mode);
    /** Sets the currently active media source for the provided mode */
    void setMediaSource(in ComponentName mediaSource, int mode);
    /** Register a callback that receives updates to the active media source */
    void registerMediaSourceListener(in ICarMediaSourceListener callback, int mode);
    /** Unregister a callback that receives updates to the active media source */
    void unregisterMediaSourceListener(in ICarMediaSourceListener callback, int mode);
    /** Retrieve a list of media sources, ordered by most recently used */
    List getLastMediaSources(int mode);
    /** Returns whether the browse and playback sources can be changed independently. */
    boolean isIndependentPlaybackConfig();
    /** Sets whether the browse and playback sources can be changed independently. */
    void setIndependentPlaybackConfig(boolean independent);
}
  • 使用流程

首先获取CarMediaManager,然后注册MediaSource变化的监听,在mediasource有变化的时候更新ui。

        mHandler = new Handler(application.getMainLooper());
        mMediaSourceListener = componentName -> mHandler.post(
                () -> updateModelState(mInputFactory.getMediaSource(componentName)));
        try {
            mCarMediaManager = mInputFactory.getCarMediaManager(mCar);
            mCarMediaManager.addMediaSourceListener(mMediaSourceListener, mode);
            MediaSource src = mInputFactory.getMediaSource(mCarMediaManager.getMediaSource(mode));
            if (Log.isLoggable(TAG, Log.INFO)) {
                Log.i(TAG, "Initializing with " + src);
            }
            updateModelState(src);
        } catch (CarNotConnectedException e) {
            Log.e(TAG, "Car not connected", e);
        }
CarMediaService的实现

AAOS CarMediaService 服务框架_第2张图片

  • carMediaService中初始化流程
  1. 获取mPrimaryMediaComponents
    获取的方式是通过遍历系统的所有package、然后查看有MediaBroswerService的package。
    在AAOS 默认Bluetooth有MediaBroswerService,所以默认的package是com.android.bluetooth。
private ComponentName getMediaService(@NonNull ComponentName componentName) {
        String packageName = componentName.getPackageName();
        String className = componentName.getClassName();

        PackageManager packageManager = mContext.getPackageManager();
        Intent mediaIntent = new Intent();
        mediaIntent.setPackage(packageName);
        mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
        List mediaServices = packageManager.queryIntentServicesAsUser(mediaIntent,
                PackageManager.GET_RESOLVED_FILTER, ActivityManager.getCurrentUser());

        for (ResolveInfo service : mediaServices) {
            String serviceName = service.serviceInfo.name;
            if (!TextUtils.isEmpty(serviceName)
                    // If className is not specified, returns the first service in the package;
                    // otherwise returns the matched service.
                    // TODO(b/136274456): find a proper way to handle the case where there are
                    //  multiple services and the className is not specified.

                    && (TextUtils.isEmpty(className) || serviceName.equals(className))) {
                return new ComponentName(packageName, serviceName);
            }
        }
    }
  1. 注册MediaSessionActive的回调
    当服务端的MediaSession 设置为active的时候,回调到这个SessionChangedListener中

  2. 启动MediaConnectService
    MediaConnectorService会调用MediaConnectorService的onStartCommand。
    onStartCommand中会获取当前应用的MediaControl设置到PlaybackViewModel。
    PlaybackViewModel利用这个control对MediaSession的service端进行控制。
    控制是在service端实现mediaControl的onPrepare、onPlay等实现的。
    如果MediaSession当前是播放的状态那么会stop掉。如果不是的话 会先进行prepare操作。

    private void initUser(@UserIdInt int userId) {
            mPrimaryMediaComponents[MEDIA_SOURCE_MODE_PLAYBACK] = isCurrentUserEphemeral()
                    ? getDefaultMediaSource() : getLastMediaSource(MEDIA_SOURCE_MODE_PLAYBACK);
            mPrimaryMediaComponents[MEDIA_SOURCE_MODE_BROWSE] = isCurrentUserEphemeral()
                    ? getDefaultMediaSource() : getLastMediaSource(MEDIA_SOURCE_MODE_BROWSE);
            mActiveUserMediaController = null;

            updateMediaSessionCallbackForCurrentUser();
            notifyListeners(MEDIA_SOURCE_MODE_PLAYBACK);
            notifyListeners(MEDIA_SOURCE_MODE_BROWSE);

          startMediaConnectorService(shouldStartPlayback(mPlayOnBootConfig), currentUser);
        }
    }


  • SessionChanged 的回调
    AAOS CarMediaService 服务框架_第3张图片
    SessionChanged回调到onActiveSessionsChanged。回调的参数是所有active的mediaControl。这里面会遍历所有control,如果control的状态是playing 并且其MediaSource跟当前存储的PrimaryMediaSource不一样的话 会更新control的MediaSource到PrimaryMediaSource。
    private class SessionChangedListener implements OnActiveSessionsChangedListener {
        @Override
        public void onActiveSessionsChanged(List<MediaController> controllers) {
            if (ActivityManager.getCurrentUser() != mCurrentUser) {
                Slog.e(CarLog.TAG_MEDIA, "Active session callback for old user: " + mCurrentUser);
                return;
            }
            mMediaSessionUpdater.registerCallbacks(controllers);
        }
    }
  • MediaController的回调
    AAOS CarMediaService 服务框架_第4张图片

    这个会回调到onPlaybackStateChanged,回调的状态是playing而且跟回调之前的状态不一样的时候 也会调用setPrimaryMediaSource进行primary mediasource的更新。

        public void onPlaybackStateChanged(@Nullable PlaybackState state) {
            if (state.getState() == PlaybackState.STATE_PLAYING
                    && state.getState() != mPreviousPlaybackState) {
                ComponentName mediaSource = getMediaSource(mMediaController.getPackageName(),
                        getClassName(mMediaController));
                if (mediaSource != null) {
                    setPrimaryMediaSource(mediaSource, MEDIA_SOURCE_MODE_PLAYBACK);
                }
            }
            mPreviousPlaybackState = state.getState();
        }
    }
总结

CarMediaService 的实现是通过注册MediaSessionActive 和 MediaController的callback 来监听当前用户所有应用 有关媒体播放浏览等事件。当事件发生时
更新AAOS界面的信息和控制的控件。

你可能感兴趣的:(AAOS,音视频,MediaSession,CarMediaService)