车载蓝牙音乐流程简单分析

关键类:

/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java

/packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java

一、音乐播放状态

CPP中通过JNI接口将接从手机端接收来的播放状态回调到AvrcpControllerService的onPlayStatusChanged方法,然后通过状态机AvrcpControllerStateMachine处理。

// Called by JNI on changes of play status
    private synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {
        if (DBG) {
            Log.d(TAG, "onPlayStatusChanged " + playStatus);
        }
        int playbackState = PlaybackStateCompat.STATE_NONE;
        switch (playStatus) {
            case JNI_PLAY_STATUS_STOPPED:
                playbackState = PlaybackStateCompat.STATE_STOPPED;
                break;
            case JNI_PLAY_STATUS_PLAYING:
                playbackState = PlaybackStateCompat.STATE_PLAYING;
                break;
            case JNI_PLAY_STATUS_PAUSED:
                playbackState = PlaybackStateCompat.STATE_PAUSED;
                break;
            case JNI_PLAY_STATUS_FWD_SEEK:
                playbackState = PlaybackStateCompat.STATE_FAST_FORWARDING;
                break;
            case JNI_PLAY_STATUS_REV_SEEK:
                playbackState = PlaybackStateCompat.STATE_REWINDING;
                break;
            default:
                playbackState = PlaybackStateCompat.STATE_NONE;
        }
        BluetoothDevice device = mAdapter.getRemoteDevice(address);
        AvrcpControllerStateMachine stateMachine = getStateMachine(device);
        if (stateMachine != null) {
            stateMachine.sendMessage(
                    AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, playbackState);
        }
    }
AvrcpControllerStateMachine.java中Connected内部类的processMessage方法中,调BluetoothMediaBrowserService类的方法将播放状态通知到UI应用,如果是播放状态,要申请音频焦点。
case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
                    mAddressedPlayer.setPlayStatus(msg.arg1);
                    if (!isActive()) {
                        sendMessage(MSG_AVRCP_PASSTHRU,
                                AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);
                        return true;
                    }

                    PlaybackStateCompat playbackState = mAddressedPlayer.getPlaybackState();
                    // 将播放状态通过mediasession通知给UI
                    BluetoothMediaBrowserService.notifyChanged(playbackState);

                    // 获取当前的音频焦点状态
                    int focusState = AudioManager.ERROR;
                    A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService();
                    if (a2dpSinkService != null) {
                        focusState = a2dpSinkService.getFocusState();
                    }

                    if (focusState == AudioManager.ERROR) {
                        sendMessage(MSG_AVRCP_PASSTHRU,
                                AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);
                        return true;
                    }

                    if (playbackState.getState() == PlaybackStateCompat.STATE_PLAYING
                            && focusState == AudioManager.AUDIOFOCUS_NONE) {
                        // 申请音频焦点
                        if (shouldRequestFocus()) {
                            requestAudioFocus();
                        } else {
                            sendMessage(MSG_AVRCP_PASSTHRU,
                                    AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);
                        }
                    }
                    return true;

BluetoothMediaBrowserService是MediaSession框架的一个服务,UI端通过MediaSession框架绑定该服务就可以监听蓝牙音频的播放状态,控制蓝牙音频的播放。

BluetoothMediaBrowserService的notifyChanged方法中通过MediaSession框架将播放状态通知到UI端。

static synchronized void notifyChanged(PlaybackStateCompat playbackState) {
        Log.d(TAG, "notifyChanged PlaybackState" + playbackState);
        if (sBluetoothMediaBrowserService != null) {
            sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);
        } else {
            Log.w(TAG, "notifyChanged Unavailable");
        }
    }

二、音频焦点申请

上面播放状态处理的逻辑中会调到A2dpSinkService的requestAudioFocus方法

 /**
     * Request audio focus such that the designated device can stream audio
     */
    public void requestAudioFocus(BluetoothDevice device, boolean request) {
        synchronized (mStreamHandlerLock) {
            if (mA2dpSinkStreamHandler == null) return;
            mA2dpSinkStreamHandler.requestAudioFocus(request);
        }
    }
A2dpSinkStreamHandler
private synchronized int requestAudioFocus() {
        if (DBG) Log.d(TAG, "requestAudioFocus()");
        // Bluetooth A2DP may carry Music, Audio Books, Navigation, or other sounds so mark content
        // type unknown.
        AudioAttributes streamAttributes =
                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA)
                        .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
                        .build();
        // Bluetooth ducking is handled at the native layer at the request of AudioManager.
        AudioFocusRequest focusRequest =
                new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).setAudioAttributes(
                        streamAttributes)
                        .setOnAudioFocusChangeListener(mAudioFocusListener, this)
                        .build();
        int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest);
        // If the request is granted begin streaming immediately and schedule an upgrade.
        if (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            startFluorideStreaming();
            mAudioFocus = AudioManager.AUDIOFOCUS_GAIN;
        }
        return focusRequestStatus;
    }

三、播放控制

UI通过MediaSession框架绑定到BluetoothMediaBrowserService服务,BluetoothMediaBrowserService中的MediaSessionCompat对象是关键,MediaSessionCompat.CallBack是专门接收MediaSession客户端的指令并处理的。AvrcpControllerStateMachine的setActive方法中,会将MediaSessionCompat.CallBack对象传给BluetoothMediaBrowserService的MediaSessionCompat。

BluetoothMediaBrowserService.addressedPlayerChanged(mSessionCallbacks);
MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() {
        @Override
        public void onPlay() {
            logD("onPlay");
            requestAudioFocus();
            sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PLAY);
        }

        @Override
        public void onPause() {
            logD("onPause");
            sendMessage(MSG_AVRCP_PASSTHRU, AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE);
        }
    };
public boolean processMessage(Message msg) {
            logD(STATE_TAG + " processMessage " + msg.what);
            switch (msg.what) {
                case MSG_AVRCP_PASSTHRU:
                    passThru(msg.arg1);
                    return true;
            }
}

而后调AvrcpControllerService类的sendPassThroughCommandNative方法将指令通过JNI接口发给CPP层。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2023-12-09 19:22:01       75 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2023-12-09 19:22:01       80 阅读
  3. 在Django里面运行非项目文件

    2023-12-09 19:22:01       64 阅读
  4. Python语言-面向对象

    2023-12-09 19:22:01       75 阅读

热门阅读

  1. 装箱 Box 数据类型

    2023-12-09 19:22:01       53 阅读
  2. Linux 常用命令

    2023-12-09 19:22:01       61 阅读
  3. 从视频中截取指定帧图片

    2023-12-09 19:22:01       53 阅读
  4. Qt 编译fcitx-qt5 插件支持中文输入法

    2023-12-09 19:22:01       49 阅读
  5. 考研真题数据结构

    2023-12-09 19:22:01       44 阅读
  6. 第19章 正则表达式 - C++

    2023-12-09 19:22:01       55 阅读
  7. 关于锁的粒度问题——面试

    2023-12-09 19:22:01       56 阅读
  8. python 学习笔记20 批量修改页眉页脚

    2023-12-09 19:22:01       51 阅读
  9. 死锁产生的原因和预防

    2023-12-09 19:22:01       55 阅读