【蓝牙开发】Andorid蓝牙绝对音量

【蓝牙开发】Andorid蓝牙绝对音量首先看下两种角色在协议构成角度来说是没有太大的区别 现在来看下 AVRCPSpec 中关于 AVRCP 中 Controller CT 和 Target TG 的描述 Thecontrolle CT is

大家好,欢迎来到IT知识分享网。

蓝牙绝对音量相关基础知识
1. 什么是绝对音量

而蓝牙绝对音量(absolute volume)的概念是在AVRCP Sepc 1.4中引入的, 是为了替代蓝牙中的相对音量(relative volume)命令: volume up 和 volume down, 之前的相对音量是不考虑对方的Target device是处于最大音量或在最小音量.

2. AVRCP中的Controller和Target 两种角色的理解

在这里插入图片描述
首先看下两种角色在协议构成角度来说是没有太大的区别, 现在来看下AVRCP Spec中关于AVRCP中Controller(CT)和Target(TG)的描述.

  • The controller (CT) is a device that initiates a transaction by sending a command frame to a target. Examples for CT are a personal computer, a PDA, a mobile phone, a remote controller or an AV device (such as an in car system, headphone, player/recorder, timer, tuner, monitor etc.).
    将发起AVRC控制命令到对方的蓝牙设备称为CT
  • The target (TG) is a device that receives a command frame and accordingly generates a response frame. Examples for TG are an audio player/recorder, a video player/recorder, a TV, a tuner, an amplifier or a headphone.
    将接收AVRC控制命令并回复respond的蓝牙设备称为TG
3. 蓝牙绝对音量(Absolute)的定义

SetAbsoluteVolume命令用于设置绝对音量
命令格式如下:
在这里插入图片描述在这里插入图片描述这里以手机连接蓝牙音箱为例子, 在这个场景下手机为AVRCP CT,蓝牙音箱为AVRCP TG.
当在手机端进行音量调节时,手机端会发送SetAbsoluteVolume命令到音箱端进行音量的调节,
需要注意的是,当手机开启绝对音量的功能后,在手机端调节音量并不会对传输的音源进行音量处理而是通过TG进行音量的调节.



Notify Volume Change
RegisterNotification(Volume Changed Event)命令用于CT进行TG上本地音量的监听以及对于TG的音量同步.
命令格式定义如下:
在这里插入图片描述
下面可以看下AVRCP Spec中关于它的描述:
在这里插入图片描述
在CT和TG建立连接的时候,CT会注册RegisterNotification(Volume Changed)到TG, TG在接收到该注册请求后,会先返回一个临时的response(仅是获取当前TG的音量同步到CT). 之后只有在用户操作TG的本地音量后会再次同步一个RegisterNotification response(同步当前的TG音量). 而这才是完成一次Volume Change Notification.





这里需要注意的是, RegisterNotification(Volume Changed)的流程是不会因为CT发起了SetAbsoluteVolume命令打断的.

4. 什么情况下蓝牙设备需要支持绝对音量呢

Android 蓝牙中绝对音量的实现
手机连接音箱场景下音箱端绝对音量的实现
1. 基于Bluetooth Spec 讲述绝对音量的控制流程

手机端在收到response后,看到音箱端支持”(absolute)volume changed”, 于是发起了notification register – “registers for volume changed notifications”

然后音箱端需要立即回复一个临时的response(interim response),将当前音箱端(Target)的音量同步到手机(Controller)

手机端在收到该response后就可以及时的通过音量条显示,而手机端希望调大音量 会发出”SET_ABSOLUTE_VOLUME”命令到音箱端进行音量的设置

音箱端在接收到该命令后,会进行音量的设置 并返回真正设置的音量到手机端, 而手机端会基于该返回的音量值进行显示

2. 基于音箱端抓取到HCI日志分析绝对音量控制流程

待输出

3. 音箱端(Android: A2DP Sink + AVRCP Target)的绝对音量的相关代码分析

在system/bt(bluedroid)中主要是对手机发过来的registers for volume changed notifications 进行数据的解析.

// system/bt/btif/src/btif_rc.cc void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV* p_data) { 
     ... case BTA_AV_META_MSG_EVT: { 
     ... } else if (bt_rc_ctrl_callbacks != NULL) { 
     /* This is case of Sink + CT + TG(for abs vol)) */ switch (p_data->meta_msg.p_msg->hdr.opcode) { 
     case AVRC_OP_VENDOR: if ((p_data->meta_msg.code >= AVRC_RSP_NOT_IMPL) && (p_data->meta_msg.code <= AVRC_RSP_INTERIM)) { 
     /* Its a response */ handle_avk_rc_metamsg_rsp(&(p_data->meta_msg)); } else if (p_data->meta_msg.code <= AVRC_CMD_GEN_INQ) { 
     /* Its a command */ handle_avk_rc_metamsg_cmd(&(p_data->meta_msg)); } break; } // system/bt/btif/src/btif_rc.cc // handle_avk_rc_metamsg_cmd static void handle_avk_rc_metamsg_cmd(tBTA_AV_META_MSG* pmeta_msg) { 
     ... if (avrc_cmd.pdu == AVRC_PDU_REGISTER_NOTIFICATION) { 
     uint8_t event_id = avrc_cmd.reg_notif.event_id; BTIF_TRACE_EVENT("%s: Register notification event_id: %s", __func__, dump_rc_notification_event_id(event_id)); } else if (avrc_cmd.pdu == AVRC_PDU_SET_ABSOLUTE_VOLUME) { 
     BTIF_TRACE_EVENT("%s: Abs Volume Cmd Recvd", __func__); } btif_rc_ctrl_upstreams_rsp_cmd(avrc_cmd.pdu, &avrc_cmd, pmeta_msg->label, p_dev); } // 在btif_rc_ctrl_upstreams_rsp_cmd回调Bluetoth Process中往Bluedorid中注册的registernotification_absvol_cb static void btif_rc_ctrl_upstreams_rsp_cmd(uint8_t event, tAVRC_COMMAND* pavrc_cmd, uint8_t label, btif_rc_device_cb_t* p_dev) { 
     BTIF_TRACE_DEBUG("%s: pdu: %s: handle: 0x%x", __func__, dump_rc_pdu(pavrc_cmd->pdu), p_dev->rc_handle); switch (event) { 
     case AVRC_PDU_REGISTER_NOTIFICATION: if (pavrc_cmd->reg_notif.event_id == AVRC_EVT_VOLUME_CHANGE) { 
     // 最终会回调Bluetooth/jni/com_android_bluetooth_avrcp_controller.cpp 中定义的 do_in_jni_thread( FROM_HERE, base::Bind(bt_rc_ctrl_callbacks->registernotification_absvol_cb, p_dev->rc_addr, label)); } break; } } 

在system/bt(Bluedorid)处理解析”registers for volume changed notifications“ 后会回调到Bluetooth App中的相关代码, 并通过Bluetooth App中获取到当前系统音量后,返回interim response到手机端.

// /packages/apps/Bluetooth/jni/com_android_bluetooth_avrcp_controller.cpp static void btavrcp_register_notification_absvol_callback( const RawAddress& bd_addr, uint8_t label) { 
     ALOGI("%s", __func__); CallbackEnv sCallbackEnv(__func__); if (!sCallbackEnv.valid()) return; ScopedLocalRef<jbyteArray> addr( sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress))); if (!addr.get()) { 
     ALOGE("Fail to get new array "); return; } sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress), (jbyte*)&bd_addr.address); // jni 方式回调Java的API method_handleRegisterNotificationAbsVol - handleRegisterNotificationAbsVol sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleRegisterNotificationAbsVol, addr.get(), (jbyte)label); } // /packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java // Called by JNI when remote wants to receive absolute volume notifications. private synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) { 
     Log.d(TAG, "handleRegisterNotificationAbsVol "); BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (device != null && !device.equals(mConnectedDevice)) { 
     Log.e(TAG, "handleRegisterNotificationAbsVol device not found " + address); return; } Message msg = mAvrcpCtSm.obtainMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION, (int) label, 0); mAvrcpCtSm.sendMessage(msg); } // 发送MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION到ArcpController的状态机中进一步处理 // /packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java // Connected状态下进行处理 class Connected extends State { 
     public boolean processMessage(Message msg) { 
     A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); synchronized (mLock) { 
     switch (msg.what) { 
     case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION: { 
     mRemoteDevice.setNotificationLabel(msg.arg1); mRemoteDevice.setAbsVolNotificationRequested(true); int percentageVol = getVolumePercentage(); if (DBG) { 
     Log.d(TAG, " Sending Interim Response = " + percentageVol + " label " + msg.arg1); } // 通过JNI方式调用sendRegisterAbsVolRspNative AvrcpControllerService.sendRegisterAbsVolRspNative( mRemoteDevice.getBluetoothAddress(), NOTIFICATION_RSP_TYPE_INTERIM, percentageVol, mRemoteDevice.getNotificationLabel()); } break; ... } // /packages/apps/Bluetooth/jni/com_android_bluetooth_avrcp_controller.cpp static void sendRegisterAbsVolRspNative(JNIEnv* env, jobject object, ...{ 
     // system/bt中register_abs_vol_rs(实际函数: volume_change_notification_rsp) bt_status_t status = sBluetoothAvrcpInterface->register_abs_vol_rsp( rawAddress, (btrc_notification_type_t)rsp_type, (uint8_t)abs_vol, (uint8_t)label); } // system/bt/btif/src/btif_rc.cc /* * * Function send_register_abs_vol_rsp * * Description Rsp for Notification of Absolute Volume * * Returns void * / static bt_status_t volume_change_notification_rsp(... { 
     // 这里就比较好理解了 数据格式在AVRCP Sepc中有详细的描述 avrc_rsp.reg_notif.opcode = AVRC_OP_VENDOR; avrc_rsp.reg_notif.pdu = AVRC_PDU_REGISTER_NOTIFICATION; avrc_rsp.reg_notif.status = AVRC_STS_NO_ERROR; avrc_rsp.reg_notif.param.volume = abs_vol; avrc_rsp.reg_notif.event_id = AVRC_EVT_VOLUME_CHANGE; status = AVRC_BldResponse(p_dev->rc_handle, &avrc_rsp, &p_msg); if (status == AVRC_STS_NO_ERROR) { 
     BTIF_TRACE_DEBUG("%s: msgreq being sent out with label: %d", __func__, label); uint8_t* data_start = (uint8_t*)(p_msg + 1) + p_msg->offset; BTA_AvVendorRsp(p_dev->rc_handle, label, (rsp_type == BTRC_NOTIFICATION_TYPE_INTERIM) ? AVRC_RSP_INTERIM : AVRC_RSP_CHANGED, data_start, p_msg->len, 0); } 
// /packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java // BroadcastReceiver 监听系统音量的变化,在用户进行音量调节时, 这里监听到后会发送MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 
     @Override public void onReceive(Context context, Intent intent) { 
     String action = intent.getAction(); if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { 
     int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); if (streamType == AudioManager.STREAM_MUSIC) { 
     sendMessage(MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION); } } } }; //  class Connected extends State { 
     @Override public boolean processMessage(Message msg) { 
     if (DBG) Log.d(TAG, " HandleMessage: " + dumpMessageString(msg.what)); A2dpSinkService a2dpSinkService = A2dpSinkService.getA2dpSinkService(); synchronized (mLock) { 
     switch (msg.what) { 
     case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION: { 
     if (mRemoteDevice.getAbsVolNotificationRequested()) { 
     int percentageVol = getVolumePercentage(); if (percentageVol != mPreviousPercentageVol) { 
     // 通过JNI 方式 sendRegisterAbsVolRspNative // 最终会通过system/bt中的volume_change_notification_rsp // 这里大家会注意 这个接口不就是前面提到的Interim Response 是一样的吗? 是一样 这是由于这两个动作的数据格式和行为本质上是相同的 // 一个是当手机发起注册行为时需要立即回复 // 一个是在用户调节音量后进行回复 AvrcpControllerService.sendRegisterAbsVolRspNative(mRemoteDevice.getBluetoothAddress(), NOTIFICATION_RSP_TYPE_CHANGED, percentageVol,mRemoteDevice.getNotificationLabel()); } } } ... } 

下面继续看下当手机端发出“SET_ABSOLUTE_VOLUME”命令时,音箱端的处理流程.

// system/bt/btif/src/btif_rc.cc static void handle_avk_rc_metamsg_cmd(tBTA_AV_META_MSG* pmeta_msg) { 
     ... if (avrc_cmd.pdu == AVRC_PDU_REGISTER_NOTIFICATION) { 
     uint8_t event_id = avrc_cmd.reg_notif.event_id; BTIF_TRACE_EVENT("%s: Register notification event_id: %s", __func__, dump_rc_notification_event_id(event_id)); } else if (avrc_cmd.pdu == AVRC_PDU_SET_ABSOLUTE_VOLUME) { 
     BTIF_TRACE_EVENT("%s: Abs Volume Cmd Recvd", __func__); } btif_rc_ctrl_upstreams_rsp_cmd(avrc_cmd.pdu, &avrc_cmd, pmeta_msg->label, p_dev); ... } // 在btif_rc_ctrl_upstreams_rsp_cmd中通过JNI的方式 调用Bluetooth Precess中定义的API static void btif_rc_ctrl_upstreams_rsp_cmd(uint8_t event, tAVRC_COMMAND* pavrc_cmd, uint8_t label, btif_rc_device_cb_t* p_dev) { 
     BTIF_TRACE_DEBUG("%s: pdu: %s: handle: 0x%x", __func__, dump_rc_pdu(pavrc_cmd->pdu), p_dev->rc_handle); switch (event) { 
     case AVRC_PDU_SET_ABSOLUTE_VOLUME: // 而这里 bt_rc_ctrl_callbacks->setabsvol_cmd_cb 这里是怎么调用的Bluetooth Process中的相关api呢? //  do_in_jni_thread( FROM_HERE, base::Bind(bt_rc_ctrl_callbacks->setabsvol_cmd_cb, p_dev->rc_addr, pavrc_cmd->volume.volume, label)); break; ... } } // 下来先来看下 bt_rc_ctrl_callbacks是如何被初始化的 // 通过阅读源码可以看到 bt_rc_ctrl_callbacks是在 init_ctrl中进行赋值的 static bt_status_t init_ctrl(btrc_ctrl_callbacks_t* callbacks) { 
     if (bt_rc_ctrl_callbacks) return BT_STATUS_DONE; bt_rc_ctrl_callbacks = callbacks; } // 而init_ctrl是和bt_rc_ctrl_interface关联的也就是外部使用的初始化接口 static const btrc_ctrl_interface_t bt_rc_ctrl_interface = { 
     sizeof(bt_rc_ctrl_interface), init_ctrl, ... } // 进一步看 在Bluetooth Proceess中是如何调用init_ctrl 进行初始化的 // /packages/apps/Bluetooth/jni/com_android_bluetooth_avrcp_controller.cpp static void initNative(JNIEnv* env, jobject object) { 
     ... const bt_interface_t* btInf = getBluetoothInterface(); ... sBluetoothAvrcpInterface = (btrc_ctrl_interface_t*)btInf->get_profile_interface( BT_PROFILE_AV_RC_CTRL_ID); ... // static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = { 
     // btavrcp_set_abs_vol_cmd_callback // ... // 也就是 bt_rc_ctrl_callbacks->setabsvol_cmd_cb 对应的是 btavrcp_set_abs_vol_cmd_callback bt_status_t status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks); } // 继续看下 btavrcp_set_abs_vol_cmd_callback中是如何操作的. static void btavrcp_set_abs_vol_cmd_callback(const RawAddress& bd_addr,... { 
     ... // method_handleSetAbsVolume --> handleSetAbsVolume sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetAbsVolume, addr.get(), (jbyte)abs_vol, (jbyte)label); } // Called by JNI when remote wants to set absolute volume. private synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) { 
     Log.d(TAG, "handleSetAbsVolume "); BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); if (device != null && !device.equals(mConnectedDevice)) { 
     Log.e(TAG, "handleSetAbsVolume device not found " + address); return; } Message msg = mAvrcpCtSm.obtainMessage( AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, absVol, label); mAvrcpCtSm.sendMessage(msg); } // 在ArcpController的状态机中进行处理 // /packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java class Connected extends State { 
     @Override public boolean processMessage(Message msg) { 
     case MESSAGE_PROCESS_SET_ABS_VOL_CMD: mVolumeChangedNotificationsToIgnore++; removeMessages(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT); sendMessageDelayed(MESSAGE_INTERNAL_ABS_VOL_TIMEOUT, ABS_VOL_TIMEOUT_MILLIS); setAbsVolume(msg.arg1, msg.arg2); break; ... } private void setAbsVolume(int absVol, int label) { 
     // 对absVol进行处理 并通过mAudioManager.setStreamVolume调整系统音量后返回到remote device AvrcpControllerService.sendAbsVolRspNative(mRemoteDevice.getBluetoothAddress(), absVol, label); } // 通过JNI的方式调用com_android_bluetooth_avrcp_controller.cpp下的sendAbsVolRspNative // /packages/apps/Bluetooth/jni/com_android_bluetooth_avrcp_controller.cpp static void sendAbsVolRspNative(JNIEnv* env, jobject object, jbyteArray address,... { 
     } // 然后调用到system/bt下的set_volume_rsp // system/bt/btif/src/btif_rc.cc static bt_status_t set_volume_rsp(const RawAddress& bd_addr, uint8_t abs_vol, uint8_t label) { 
     avrc_rsp.volume.opcode = AVRC_OP_VENDOR; avrc_rsp.volume.pdu = AVRC_PDU_SET_ABSOLUTE_VOLUME; avrc_rsp.volume.status = AVRC_STS_NO_ERROR; avrc_rsp.volume.volume = abs_vol; // 基于Spec中规定的AVRC_PDU_SET_ABSOLUTE_VOLUME RSP格式 回复message status = AVRC_BldResponse(p_dev->rc_handle, &avrc_rsp, &p_msg); BTA_AvVendorRsp(p_dev->rc_handle, label, AVRC_RSP_ACCEPT, data_start, p_msg->len, 0); ... } 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/127252.html

(0)
上一篇 2025-09-12 16:45
下一篇 2025-09-12 17:10

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信