大家好,欢迎来到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