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

相关推荐
冷凝女子2 小时前
【QT】海康视频及openCv抓拍正脸接口
qt·opencv·音视频·海康
安步当歌3 小时前
【WebRTC】视频编码链路中各个类的简单分析——VideoStreamEncoder
音视频·webrtc·视频编解码·video-codec
顾北川_野3 小时前
Android CALL关于电话音频和紧急电话设置和获取
android·音视频
顶呱呱程序3 小时前
2-143 基于matlab-GUI的脉冲响应不变法实现音频滤波功能
算法·matlab·音视频·matlab-gui·音频滤波·脉冲响应不变法
EasyCVR4 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
runing_an_min4 小时前
ffmpeg 视频滤镜:屏蔽边框杂色- fillborders
ffmpeg·音视频·fillborders
我喜欢就喜欢17 小时前
基于qt vs下的视频播放
开发语言·qt·音视频
安步当歌18 小时前
【WebRTC】视频采集模块中各个类的简单分析
音视频·webrtc·视频编解码·video-codec
EasyGBS18 小时前
国标GB28181公网直播EasyGBS国标GB28181软件管理解决方案
大数据·网络·音视频·媒体·视频监控·gb28181
Johnstons21 小时前
AnaTraf | 网络性能监控系统保障音视频质量的秘籍
网络·音视频·网络流量监控·网络流量分析·npmd