Android添加蓝牙音响功能
\system\media\audio\include\system
audio.h
添加AUDIO_SOURCE_BLUETOOTH_A2DP
typedef enum {
AUDIO_SOURCE_DEFAULT = 0,
AUDIO_SOURCE_MIC = 1,
AUDIO_SOURCE_VOICE_UPLINK = 2,
AUDIO_SOURCE_VOICE_DOWNLINK = 3,
AUDIO_SOURCE_VOICE_CALL = 4,
AUDIO_SOURCE_CAMCORDER = 5,
AUDIO_SOURCE_VOICE_RECOGNITION = 6,
AUDIO_SOURCE_VOICE_COMMUNICATION = 7,
AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */
/* An example of remote presentation is Wifi Display */
/* where a dongle attached to a TV can be used to */
/* play the mix captured by this audio source. */
AUDIO_SOURCE_HDMIRX = 9,
AUDIO_SOURCE_BLUETOOTH_A2DP = 10,
AUDIO_SOURCE_CNT,
AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1,
AUDIO_SOURCE_FM_TUNER = 1998,
AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for
for background software hotword detection.
Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION.
Used only internally to the framework. Not exposed
at the audio HAL. */
} audio_source_t;
\system\bt
Android.mk
ifeq ($(BLUETOOTH_A2DP_SINK),true)
bdroid_CFLAGS += -DA2DP_SINK
endif
audio_a2dp_hw\Android.mk
ifeq ($(BLUETOOTH_A2DP_SINK),true)
LOCAL_CFLAGS += -DA2DP_SINK
endif
include\bt_target.h
#ifndef BTA_AV_SINK_INCLUDED
#define BTA_AV_SINK_INCLUDED TRUE
#endif
\hardware\libhardware_legacy\audio
Android.mk
ifeq ($(BLUETOOTH_A2DP_SINK),true)
LOCAL_CFLAGS += -DBLUETOOTH_A2DP_SINK
endif
AudioPolicyManagerBase.cpp
audio_devices_t AudioPolicyManagerBase::getDeviceForInputSource(int inputSource)
{
uint32_t device = AUDIO_DEVICE_NONE;
switch (inputSource) {
case AUDIO_SOURCE_VOICE_UPLINK:
if (mAvailableInputDevices & AUDIO_DEVICE_IN_VOICE_CALL) {
device = AUDIO_DEVICE_IN_VOICE_CALL;
break;
}
// FALL THROUGH
case AUDIO_SOURCE_DEFAULT:
case AUDIO_SOURCE_MIC:
if (mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
break;
}
// FALL THROUGH
case AUDIO_SOURCE_VOICE_RECOGNITION:
case AUDIO_SOURCE_HOTWORD:
case AUDIO_SOURCE_VOICE_COMMUNICATION:
if (mForceUse[AudioSystem::FOR_RECORD] == AudioSystem::FORCE_BT_SCO &&
mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) {
device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
} else if (mAvailableInputDevices & AUDIO_DEVICE_IN_WIRED_HEADSET) {
device = AUDIO_DEVICE_IN_WIRED_HEADSET;
} else if (mAvailableInputDevices & AUDIO_DEVICE_IN_USB_DEVICE) {
device = AUDIO_DEVICE_IN_USB_DEVICE;
} else if (mAvailableInputDevices & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_CAMCORDER:
if (mAvailableInputDevices & AUDIO_DEVICE_IN_BACK_MIC) {
device = AUDIO_DEVICE_IN_BACK_MIC;
} else if (mAvailableInputDevices & AUDIO_DEVICE_IN_BUILTIN_MIC) {
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
}
break;
case AUDIO_SOURCE_VOICE_DOWNLINK:
case AUDIO_SOURCE_VOICE_CALL:
if (mAvailableInputDevices & AUDIO_DEVICE_IN_VOICE_CALL) {
device = AUDIO_DEVICE_IN_VOICE_CALL;
}
break;
case AUDIO_SOURCE_REMOTE_SUBMIX:
if (mAvailableInputDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
}
break;
case AUDIO_SOURCE_HDMIRX:
if (mAvailableInputDevices & AUDIO_DEVICE_IN_AUX_DIGITAL) {
device = AUDIO_DEVICE_IN_AUX_DIGITAL;
}
break;
//add begin
case AUDIO_SOURCE_BLUETOOTH_A2DP:
if (mAvailableInputDevices & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
}
break;
//add end
default:
ALOGW("getDeviceForInputSource() invalid input source %d", inputSource);
break;
}
ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device);
return device;
}
\packages\apps\Bluetooth\src\com\android\bluetooth\a2dp
A2dpSinkStateMachine.java
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder.AudioSource;
final class A2dpSinkStateMachine extends StateMachine {
private AudioRecord recorder;
private AudioTrack player;
private int recorder_buf_size;
private int player_buf_size;
private boolean mThreadExitFlag = false;
private boolean isPlaying = false;
通过AudioRecord和AudioTrack播放蓝牙音频
private A2dpSinkStateMachine(A2dpSinkService svc, Context context) {
super("A2dpSinkStateMachine");
recorder_buf_size = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
player_buf_size = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
}
private void cleanAudioTrack()
{
audioPause();
mThreadExitFlag = true;
if (recorder != null) {
recorder.release();
recorder = null;
}
if (player != null) {
player.release();
player = null;
}
}
private void initAudioTrack()
{
if (recorder == null)
{
recorder = new AudioRecord(AudioSource.BLUETOOTH_A2DP, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, recorder_buf_size);
}
if (player == null)
{
player = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, player_buf_size, AudioTrack.MODE_STREAM);
}
}
private void audioPlay()
{
initAudioTrack();
if (isPlaying == false)
{
isPlaying = true;
mThreadExitFlag = false;
new RecordThread().start();
}
}
private void audioPause()
{
if (isPlaying == true)
{
isPlaying = false;
mThreadExitFlag = true;
recorder.stop();
player.stop();
}
}
private class Disconnected extends State {
@Override
public void enter() {
log("Enter Disconnected: " + getCurrentMessage().what);
if (isA2dpSinkEnabled()) {
cleanAudioTrack();
}
}
private void processAudioStateEvent(int state, BluetoothDevice device) {
if (!mCurrentDevice.equals(device)) {
loge("Audio State Device:" + device + "is different from ConnectedDevice:" +
mCurrentDevice);
return;
}
switch (state) {
case AUDIO_STATE_STARTED:
if (isA2dpSinkEnabled()) {
audioPlay();
}
broadcastAudioState(device, BluetoothA2dpSink.STATE_PLAYING,
BluetoothA2dpSink.STATE_NOT_PLAYING);
break;
case AUDIO_STATE_REMOTE_SUSPEND:
case AUDIO_STATE_STOPPED:
if (isA2dpSinkEnabled()) {
audioPause();
}
broadcastAudioState(device, BluetoothA2dpSink.STATE_NOT_PLAYING,
BluetoothA2dpSink.STATE_PLAYING);
break;
default:
loge("Audio State Device: " + device + " bad state: " + state);
break;
}
}
}
private static boolean isA2dpSinkEnabled()
{
ParcelUuid[] uuids = BluetoothAdapter.getDefaultAdapter().getUuids();
return BluetoothUuid.isUuidPresent(uuids,BluetoothUuid.AudioSink);
}
class RecordThread extends Thread{
@Override
public void run() {
byte[] buffer = new byte[recorder_buf_size];
recorder.startRecording();
player.play();
while(true) {
if (mThreadExitFlag == true) {
break;
}
try {
int res = recorder.read(buffer, 0, recorder_buf_size);
if (res>0) {
byte[] tmpBuf = new byte[res];
System.arraycopy(buffer, 0, tmpBuf, 0, res);
player.write(tmpBuf, 0, tmpBuf.length);
}
} catch (Exception e) {
e.printStackTrace();
break;
}
}
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
关于在哪取蓝牙过来的音频数据,怎么能让蓝牙进入音频数据过来的模式,这部分android 已经做好了。我们只需要配置一下 packages/apps/Bluetooth/res/values/config.xml 里
<resources>
<bool name="profile_supported_rtkbt">true</bool>
<bool name="profile_supported_a2dp">true</bool>
<bool name="profile_supported_a2dp_sink">true</bool>
\frameworks\base
api\sytem-current.txt
api\current.txt
public class AudioManager {
field public static final int DEVICE_IN_BLUETOOTH_A2DP = -2147352576; // 0x80020000
public final class MediaRecorder.AudioSource {
field public static final int BLUETOOTH_A2DP = 10; // 0xa
\media\java\android\media
AudioAttributes.java
@SystemApi
public Builder setCapturePreset(int preset) {
switch (preset) {
case MediaRecorder.AudioSource.DEFAULT:
case MediaRecorder.AudioSource.MIC:
case MediaRecorder.AudioSource.CAMCORDER:
case MediaRecorder.AudioSource.VOICE_RECOGNITION:
case MediaRecorder.AudioSource.VOICE_COMMUNICATION:
case MediaRecorder.AudioSource.BLUETOOTH_A2DP:
mSource = preset;
break;
default:
Log.e(TAG, "Invalid capture preset " + preset + " for AudioAttributes");
}
return this;
AudioManager.java
public boolean isBluetoothA2dpOn() {
/*
if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"")
== AudioSystem.DEVICE_STATE_UNAVAILABLE) {
return false;
} else {
return true;
}
*/
if (AudioSystem.isA2dpSinkEnabled())
{
if (AudioSystem.getDeviceConnectionState(DEVICE_IN_BLUETOOTH_A2DP,"")
== AudioSystem.DEVICE_STATE_UNAVAILABLE) {
return false;
} else {
return true;
}
} else {
if (AudioSystem.getDeviceConnectionState(DEVICE_OUT_BLUETOOTH_A2DP,"")
== AudioSystem.DEVICE_STATE_UNAVAILABLE) {
return false;
} else {
return true;
}
}
}
public static final int DEVICE_OUT_BLUETOOTH_A2DP = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
public static final int DEVICE_IN_BLUETOOTH_A2DP = AudioSystem.DEVICE_IN_BLUETOOTH_A2DP;
AudioSystem.java
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothUuid;
import android.os.ParcelUuid;
import android.os.SystemProperties;
public static boolean isA2dpSinkEnabled()
{
String property = SystemProperties.get("device.bluetooth.ctrl");
if(property.equals("true")){
ParcelUuid[] uuids = BluetoothAdapter.getDefaultAdapter().getUuids();
for (ParcelUuid uuid: uuids) {
Log.e(TAG, "UUID: " + uuid.getUuid().toString());
}
return BluetoothUuid.isUuidPresent(uuids,BluetoothUuid.AudioSink);
}else{
return false;
}
}
MediaRecorder.java
public static final int BLUETOOTH_A2DP = 10;
public static final int getAudioSourceMax() {
//return AudioSource.REMOTE_SUBMIX;
if (AudioSystem.isA2dpSinkEnabled()) {
return AudioSource.BLUETOOTH_A2DP;
} else {
return AudioSource.REMOTE_SUBMIX;
}
}
services/core/java/com/android/server/audio/AudioService.java
public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
// address is not used for now, but may be used when multiple a2dp devices are supported
synchronized (mA2dpAvrcpLock) {
mAvrcpAbsVolSupported = support;
/*
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_MUSIC], 0);
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_RING], 0);
*/
if (AudioSystem.isA2dpSinkEnabled()) {
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_MUSIC], 0);
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_RING], 0);
} else {
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_MUSIC], 0);
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
mStreamStates[AudioSystem.STREAM_RING], 0);
}
}
}
\frameworks\av\services\audiopolicy
enginedefault\src\Engine.cpp
audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const
{
const DeviceVector &availableOutputDevices = mApmObserver->getAvailableOutputDevices();
const DeviceVector &availableInputDevices = mApmObserver->getAvailableInputDevices();
const SwAudioOutputCollection &outputs = mApmObserver->getOutputs();
audio_devices_t availableDeviceTypes = availableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN;
uint32_t device = AUDIO_DEVICE_NONE;
switch (inputSource) {
case AUDIO_SOURCE_VOICE_UPLINK:
if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) {
device = AUDIO_DEVICE_IN_VOICE_CALL;
break;
}
break;
case AUDIO_SOURCE_DEFAULT:
device = AUDIO_DEVICE_IN_BUILTIN_MIC;
break;
//begin
case AUDIO_SOURCE_BLUETOOTH_A2DP:
if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
}
break;
audio_policy.conf
修改audio_policy.conf配置
a2dp {
outputs {
a2dp {
sampling_rates 44100
channel_masks AUDIO_CHANNEL_OUT_STEREO
formats AUDIO_FORMAT_PCM_16_BIT
devices AUDIO_DEVICE_OUT_ALL_A2DP
}
}
inputs {
a2dp {
sampling_rates 44100
channel_masks AUDIO_CHANNEL_IN_STEREO
formats AUDIO_FORMAT_PCM_16_BIT
devices AUDIO_DEVICE_IN_BLUETOOTH_A2DP
}
}
}