AAOS CarMediaService 服务框架

文章目录

前言

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

MediaSession

MediaSession 框架规范了音视频应用中界面与播放器之间的通信接口,实现界面与播放器之间的完全解耦。MediaSession 框架定义了媒体会话和媒体控制器两个重要的类,它们为构建多媒体播放器应用提供了一个完善的技术架构。

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<ComponentName> 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的实现
  • 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<ResolveInfo> 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);
             }
         }
     }
  2. 注册MediaSessionActive的回调

    当服务端的MediaSession 设置为active的时候,回调到这个SessionChangedListener中

  3. 启动MediaConnectService

    MediaConnectorService会调用MediaConnectorService的onStartCommand。

    onStartCommand中会获取当前应用的MediaControl设置到PlaybackViewModel。

    PlaybackViewModel利用这个control对MediaSession的service端进行控制。

    控制是在service端实现mediaControl的onPrepare、onPlay等实现的。

    如果MediaSession当前是播放的状态那么会stop掉。如果不是的话 会先进行prepare操作。

cpp 复制代码
    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 的回调

    SessionChanged回调到onActiveSessionsChanged。回调的参数是所有active的mediaControl。这里面会遍历所有control,如果control的状态是playing 并且其MediaSource跟当前存储的PrimaryMediaSource不一样的话 会更新control的MediaSource到PrimaryMediaSource。
cpp 复制代码
    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的回调

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

cpp 复制代码
        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界面的信息和控制的控件。

相关推荐
邪恶的贝利亚4 小时前
FFplay 音视频同步机制解析:以音频为基准的时间校准与动态帧调整策略
音视频
路溪非溪5 小时前
音频/AI/BLE/WIFI/玩具/商业等方向的论坛网站总结
音视频
flex88886 小时前
FramePack - 开源 AI 视频生成工具
人工智能·开源·音视频
EasyDSS10 小时前
WebRTC技术下的EasyRTC音视频实时通话SDK,助力车载通信打造安全高效的智能出行体验
人工智能·音视频
Eric.Lee202110 小时前
python opencv 将不同shape尺寸的图片制作video视频
python·opencv·音视频
灰色人生qwer20 小时前
使用WebSocket实现跨多个服务器传输音频及实时语音识别
websocket·音视频·实时传输
小鱼仙官1 天前
Ubuntu 编译SRS和ZLMediaKit用于视频推拉流
音视频
摆烂仙君1 天前
视频分辨率增强与自动补帧
音视频
海姐软件测试1 天前
抖音视频上传功能测试全维度拆解——从基础功能到隐藏缺陷的深度挖掘
功能测试·音视频
DogDaoDao1 天前
视频图像压缩领域中 DCT 的 DC 系数和 AC 系数详解
图像处理·音视频·视频编解码·dct·图像压缩·变换编码·离散余弦变换