• 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 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 使用流程

    首先获取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);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    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 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    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);
            }
        }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • SessionChanged 的回调
      在这里插入图片描述
      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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • MediaController的回调
      在这里插入图片描述

      这个会回调到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();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    总结

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

  • 相关阅读:
    python网络爬虫笔记15:使用js验证获取网页的请求头信息和请求参数
    1-STM32之GPIO点亮LED
    数据结构-树状数组
    Java数据输入
    企业IP地址管理(IPAM)
    【NodeJs-5天学习】第二天篇④ ——项目模块化
    如何设计神经网络结构,神经网络特征可视化
    第三十八章 在 UNIX®、Linux 和 macOS 上使用 IRIS(三)
    密钥密码学(三)
    [7]能不能说一说浏览器缓存
  • 原文地址:https://blog.csdn.net/H2008066215019910120/article/details/133934830