本文是蓝牙音频相关的第3篇文章,查阅其他内容,请点击《Android蓝牙开发系列文章-策划篇》。

目前a2dp相关的内容有:

《Android蓝牙开发系列文章-AudioTrack播放PCM音频》

《Android蓝牙开发系列文章-蓝牙音箱连接》

我们通常直接手机听歌或者接个蓝牙音箱来听歌,其实你的手机也可以变成一个蓝牙音箱来使用。

怎么来做这个转变呢?让我一起学起来吧~

本文主要内容包括:1.什么是a2dp sink? 2.如何实现a2dp和a2dp sink状态的切换?

目录

1.什么是a2dp sink?

2.如何实现a2dp和a2dp sink状态的切换?

2.1将设备暂停a2dp

2.2启动a2dp sink


1.什么是a2dp sink?

两个蓝牙设备连接之后,一个设备提供音频数据,另外一个设备出声音,我们将提供数据的一端称为a2dp source(源端),发出声音的一端称为a2dp sink(目的端)。

也就是说,a2dp与a2dp sink是两个相对的状态,一个蓝牙设备一个时刻只能处于其中的一种状态。

现在一些智能设备可以实现a2dp与a2dp sink状态之间的切换(但一个时刻只能是其中一个状态)。

与a2dp/a2dp sink协议紧密关联的一个协议之avrcp(Audio/Video Remote Control Profile)协议,该协议实现了从sink端到souce端的反向控制,例如,暂停、停止、启动重放、音量控制及其它类型的远程控制操作。

2.如何实现a2dp和a2dp sink状态的切换?

在实际动手完成两个状态切换前,需要明确你的设备是否支持这两种状态。对于耳机、音箱这类设备而言,即使将它砸了,它也很难切到a2dp source状态,因为它一出生就决定了自己没有这个状态的使用场景,不信你可以砸了试一下,O(∩_∩)O哈哈~

而对于通常作为音乐播放端的某些手机和智能电视是可能切到sink状态的。

怎么来判断自己的手机或者电视是否支持a2dp sink呢?大体可以按照如下步骤确定,但是不是说这个步骤都满足了,你的设备肯定是支持a2dp sink的,因为我们看不到设备的源码,甚至我们连root权限都没有。

  1. 确认设备是否自带sink功能的使用场景(一般来说,如果没有自带使用场景,sink功能应该要默认关闭,不然就是给自己的产品挖了一个坑)
  2. 串口执行:dumpsys package com.android.bluetooth,看一下是否支持A2dpSinkService
  3. 去跟蓝牙芯片方案商确认是否支持sink功能
  4. 参考本文的代码,写一个demo来试一下

下面来讲一下我们的demo实现:

2.1将设备暂停a2dp

在停掉a2dp前,我们需要将处于连接状态的设备进行断开。

为了让我们的设备可以被其他设备扫描到和可被连接,我们需要将设备的蓝牙扫描模式设置成可被发现、可被连接。

如上提到的两点也是很重要的,我的demo里面没有实现它,是通过在设置中进行操作来替代了代码实现。

停掉a2d的动作对于应用层来说就是停掉a2dp service,这个动作会触发一些系列的a2dp状态清理动作,这个动作是从上而下的一整套,不仅仅是设计Host端,也涉及Controller。

    public void stopSource() {Intent intent = new Intent();intent.setAction("com.android.bluetooth/.a2dp.A2dpService");intent.setPackage("com.android.bluetooth");intent.putExtra("action", "com.android.bluetooth.btservice.action.STATE_CHANGED");intent.putExtra(BluetoothAdapter.EXTRA_STATE, 10);startService(intent);}

你也许会问:你怎么知道这样写就可以启动A2dpService的?

如果,你研究过蓝牙开关流程的话,你应该看到过各种profile的启动流程,也就是说以上这段代码就是从源码中摘出来的,

Android源码如下:

@SuppressWarnings("rawtypes")private void setGattProfileServiceState(Class[] services, int state) {if (state != BluetoothAdapter.STATE_ON && state != BluetoothAdapter.STATE_OFF) {Log.w(TAG,"setGattProfileServiceState(): invalid state...Leaving...");return;}int expectedCurrentState= BluetoothAdapter.STATE_OFF;int pendingState = BluetoothAdapter.STATE_TURNING_ON;if (state == BluetoothAdapter.STATE_OFF) {expectedCurrentState= BluetoothAdapter.STATE_ON;pendingState = BluetoothAdapter.STATE_TURNING_OFF;}for (int i=0; i <services.length;i++) {String serviceName = services[i].getName();String simpleName = services[i].getSimpleName();if (simpleName.equals("GattService")) {Integer serviceState = mProfileServicesState.get(serviceName);if(serviceState != null && serviceState != expectedCurrentState) {debugLog("setProfileServiceState() - Unable to "+ (state == BluetoothAdapter.STATE_OFF ? "start" : "stop" )+ " service " + serviceName+ ". Invalid state: " + serviceState);continue;}debugLog("setProfileServiceState() - "+ (state == BluetoothAdapter.STATE_OFF ? "Stopping" : "Starting")+ " service " + serviceName);mProfileServicesState.put(serviceName,pendingState);Intent intent = new Intent(this,services[i]);intent.putExtra(EXTRA_ACTION,ACTION_SERVICE_STATE_CHANGED);intent.putExtra(BluetoothAdapter.EXTRA_STATE,state);startService(intent);return;}

2.2启动a2dp sink

直接上代码:

public void startSink() {Intent intent = new Intent();intent.setAction("com.android.bluetooth/.a2dp.A2dpSinkService");intent.setPackage("com.android.bluetooth");intent.putExtra("action", "com.android.bluetooth.btservice.action.STATE_CHANGED");intent.putExtra(BluetoothAdapter.EXTRA_STATE, 12);startService(intent);}

这么写的原因在上面已经说了哈~

这里要说一下,在不同的Android版本中,a2dp sink的路径有所改变,也就是intent action的写法会有所区别。可以针对具体设备就行分析,一个方法:dumpsys package com.android.bluetooth看一下,例如,我的设备显示长这个样子:

好了,本篇内容到这里就结束了~

欢迎留言讨论问题哈

如果想持续关注本博客内容,请扫描关注个人微信公众号,或者微信搜索:万物互联技术。

Android蓝牙开发系列文章-其实你的手机可以变成一个蓝牙音箱相关推荐

  1. Android蓝牙开发系列文章-玩转BLE开发(一)

    我们在<Android蓝牙开发系列文章-策划篇>中计划讲解一下蓝牙BLE,现在开始第一篇:Android蓝牙开发系列文章-玩转BLE开发(一).计划要写的BLE文章至少分四篇,其他三篇分别 ...

  2. Android蓝牙开发系列文章-蓝牙设备类型知多少?

    在写<Android蓝牙开发系列文章-蓝牙音箱连接>时,计划细化出一篇讲解蓝牙设备类型的文章,现在它来了~ 阅读其他内容,可以点击<Android蓝牙开发系列文章-策划篇>,或 ...

  3. Android蓝牙开发系列文章-蓝牙mesh(一)

    在<Android蓝牙开发系列文章-策划篇>中我们对蓝牙专题的内容进行整体规划,现在让我们一点点揭开他的面纱~ 本文是关于蓝牙mesh的首篇文章,由好友CSDN博主yk150915提供,在 ...

  4. Android蓝牙开发系列文章-蓝牙音箱连接

    经过一段时间的折腾,我的Android Studio终于可以正常工作了,期间遇到的坑记录在了文章<创建Android Studio 3.5第一个工程遇到的坑>. 我们在<Androi ...

  5. Android蓝牙开发系列文章-扫不到蓝牙设备,你的姿势对了吗?

    在写<Android蓝牙开发系列文章-蓝牙音箱连接>时,计划细化出两篇文章,分别是: 关于蓝牙设备类型分类的,这个已经完成了,阅读请点击<Android蓝牙开发系列文章-蓝牙设备类型 ...

  6. Qt for Bluetooth 蓝牙开发系列文章总纲

    文章目录 叙述 大纲 金典蓝牙开发篇 低功耗蓝牙开发篇 双十一,九折优惠券在线地址获取:购课优惠券 欢迎大家关注公众号,可获取pdf等相关demo下载资源 叙述   此套蓝牙开发主要针对win平台和a ...

  7. android 相机编程,Android相机开发系列

    Android Camera Develop Series 简介 Android相机开发系列文章循序渐进,教你从一个没有任何功能的相机APP开始,逐步完善实现一般相机APP的各种功能,甚至还能拿来做图 ...

  8. Windows Mobile 开发系列文章收藏 - Windows Mobile 6.x

    收集整理一些Windows Mobile 6.x开发相关文章, 文章及相关代码大部分搜集自网络,版权属于原作者! 智能手机      手机词汇      研发手机基本流程 WAP协议分析(1)     ...

  9. Android自定义控件开发系列(零)——基础原理篇

    在后边的文章中发现在说Android自定义时,有时候要重复解释很多东西,所以想想返回来增加一篇"基础原理篇",直接进入正题吧-- 首先的问题是:在Android项目开发中,什么时候 ...

最新文章

  1. 《Java 开发从入门到精通》—— 2.2 编写第一段Java程序
  2. ASP.NET Core MVC – Caching Tag Helpers
  3. SAP CRM OData模型里的addressable为true的含义
  4. python 投资组合_成功投资组合的提示
  5. Linux文件属性及如何修改文件属性
  6. JS收集:遍历CHECKBOX
  7. AI 是否会取代计算机程序员
  8. 摆脱了Excel重复做表,换个工具轻松实现报表自动化,涨薪三倍
  9. jquery.form.js实现将form提交转为ajax方式提交的使用方法
  10. AutoCAD工具栏中没有工具栏选项
  11. 用ROS来做无人测试平台系列之国外的一些RACECAR
  12. 大鹏教你python数据分析
  13. 防御DDoS攻击的十一种方法
  14. 解决表格的Drag a column header here to group by that column
  15. 浙大数据结构课后习题 练习一 7-1 Maximum Subsequence Sum (25 分)
  16. python 使用pip安装和更新包
  17. C++:将六个tif分色片写成bmp格式图片(8位深)
  18. 张家口北方学院计算机是专科,河北北方学院有哪些专科专业
  19. c#开发Windows服务程序及部署
  20. Android学习之如何集成极光IM功能(一)

热门文章

  1. NLP-最小编辑距离
  2. 通过filter过滤器对请求参数进行处理
  3. Protostuff序列化分析
  4. 量化投资学习——创业板盘中临时停牌规则
  5. 面向对象编程类的内聚性
  6. 2022 年的跨年文案,你会发什么?
  7. 瓜子二手车严选直卖店再下一城:长沙 直卖模式开启汽车新零售新时代
  8. 头插法和尾插法图文并茂
  9. 如何在 XMind 中绘制流程图?
  10. apply和call方法