最近有个需求,FM需要做听歌识曲,然后我就发现,Andoid原生就提供了一个这个:
//MediaRecorder.java
/**
* Audio source for capturing broadcast radio tuner output.
* @hide
*/
@SystemApi
public static final int RADIO_TUNER = 1998;
那我就要试试,这么操作,会怎么样:
int channelConfiguration = AudioFormat.CHANNEL_IN_STEREO;
int audioEncodingBits = AudioFormat.ENCODING_PCM_16BIT;
int sampleRateInHz = 8000;
int audioSource = MediaRecorder.AudioSource.RADIO_TUNER;
int recordBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfiguration, audioEncodingBits);
mAudioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfiguration, audioEncodingBits,
recordBufferSize);
结果,我发现报错了:
//1998就是RADIO_TUNER
AudioRecord: set(): inputSource 1998, sampleRate 8000, format 0x1, channelMask 0xc, frameCount 320, notificationFrames 0, sessionId 0, transferType 0, flags 0, opPackageName com.chj.voicerecognize.captureservice uid -1, pid -1
10-19 09:05:27.160 2162 2162 V AudioRecord: Building AudioRecord with attributes: source=1998 flags=0x0 tags=[]
10-19 09:05:27.160 2162 2162 V AudioRecord: set(): mSessionId 33
10-19 09:05:27.163 779 1008 V APM_AudioPolicyManager: getInputForAttr() source 1998, samplingRate 8000, format 1, channelMask c,session 33, flags 0
10-19 09:05:27.163 779 1008 V APM::AudioPolicyEngine: getDeviceForInputSource() no device found for source 1998
10-19 09:05:27.163 779 1008 E APM::AudioPolicyEngine: getDeviceForInputSource() no default device defined
10-19 09:05:27.163 779 1008 V APM::AudioPolicyEngine: getDeviceForInputSource()input source 1998, device 00000000
10-19 09:05:27.163 779 1008 W APM_AudioPolicyManager: getInputForAttr() could not find device for source 1998
报错的地方在这里:
Engine.cpp
audio_devices_t Engine::getDeviceForInputSource(audio_source_t inputSource) const
{
...
case AUDIO_SOURCE_FM_TUNER:
if (availableDeviceTypes & AUDIO_DEVICE_IN_FM_TUNER) {
//没走到这里
device = AUDIO_DEVICE_IN_FM_TUNER;
}
break;
...
}
也就是说availableDeviceTypes里没有包含AUDIO_DEVICE_IN_FM_TUNER。
于是,我查看了配置:
//audio_policy_configuration.xml
...
<attachedDevices>
...
<item>Built-In Micitem>
<item>Built-In Back Micitem>
<item>FM Tuneritem>
...
...
按理说,他是这样的:
//Serializer.cpp
status_t ModuleTraits::deserialize(xmlDocPtr doc, const xmlNode *root, PtrElement &module,
PtrSerializingCtx ctx)
{
...
while (children != NULL) {
if (!xmlStrcmp(children->name, (const xmlChar *)childAttachedDevicesTag)) {
ALOGV("%s: %s %s found", __FUNCTION__, tag, childAttachedDevicesTag);
const xmlNode *child = children->xmlChildrenNode;
while (child != NULL) {
if (!xmlStrcmp(child->name, (const xmlChar *)childAttachedDeviceTag)) {
xmlChar *attachedDevice = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
if (attachedDevice != NULL) {
ALOGV("ZDQ %s: %s %s=%s", __FUNCTION__, tag, childAttachedDeviceTag,
(const char*)attachedDevice);
sp<DeviceDescriptor> device =
module->getDeclaredDevices().getDeviceFromTagName(String8((const char*)attachedDevice));
//就是这里
ctx->addAvailableDevice(device);
xmlFree(attachedDevice);
}
}
child = child->next;
}
}
...
}
我加了log打印
void addAvailableDevice(const sp<DeviceDescriptor> &availableDevice)
{
ALOGV("ZDQ addAvailableDevice %0x",availableDevice->type());
if (audio_is_output_device(availableDevice->type())) {
ALOGV("is output device");
mAvailableOutputDevices.add(availableDevice);
} else if (audio_is_input_device(availableDevice->type())) {
ALOGV("is in device");
mAvailableInputDevices.add(availableDevice);
}
}
log如下:
01-01 11:57:33.145 779 779 V APM::Serializer: ZDQ deserialize: module item=Built-In Mic
01-01 11:57:33.145 779 779 V APM::Serializer: ZDQ addAvailableDevice 80000004
01-01 11:57:33.145 779 779 V APM::Serializer: is in device
01-01 11:57:33.145 779 779 V APM::Serializer: ZDQ deserialize: module item=Built-In Back Mic
01-01 11:57:33.145 779 779 V APM::Serializer: ZDQ addAvailableDevice 80000080
01-01 11:57:33.145 779 779 V APM::Serializer: is in device
01-01 11:57:33.145 779 779 V APM::Serializer: ZDQ deserialize: module item=FM Tuner
01-01 11:57:33.145 779 779 V APM::Serializer: ZDQ addAvailableDevice 80002000
01-01 11:57:33.145 779 779 V APM::Serializer: is in device
明明加进去了呀。可是我dumpsys media.audio_policy
- Available input devices:
Device 1:
- id: 18
- tag name: Built-In Mic
- type: AUDIO_DEVICE_IN_BUILTIN_MIC
- Profiles:
...
Device 2:
- id: 19
- tag name: Built-In Back Mic
- type: AUDIO_DEVICE_IN_BACK_MIC
- Profiles:
Profile 0:
...
就是没有!这可就奇怪了。想了好久好久。。。
后来想到,会不会加进去之后,又被删掉了?
在AudioPolicyManager(真正使用mAvailableInputDevices的地方)搜索了下,果不其然
两个地方:
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
{
...
for (size_t i = 0; i < mAvailableInputDevices.size();) {
if (!mAvailableInputDevices[i]->isAttached()) {
ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->type());
mAvailableInputDevices.remove(mAvailableInputDevices[i]);
continue;
}
// The device is now validated and can be appended to the available devices of the engine
mEngine->setDeviceConnectionState(mAvailableInputDevices[i], AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
i++;
}
...
}
对应的InputDevice没有被加载(细节这里就不展开说了,对应有个so,会被load),会被删掉。
status_t AudioPolicyManager::setDeviceConnectionStateInt()
{
...
// handle input device disconnection
case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: {
if (index < 0) {
return INVALID_OPERATION;
}
// Set Disconnect to HALs
broadcastDeviceConnectionState(device, state, devDesc->mAddress);
checkInputsForDevice(devDesc, state, inputs, devDesc->mAddress);
mAvailableInputDevices.remove(devDesc);
mEngine->setDeviceConnectionState(devDesc, state);
} break;
...
}
状态切换的时候,也会被删掉。。。fm会disconnect?
然后,我不死心,搜索了下AUDIO_DEVICE_IN_FM_TUNER。竟然发现了这个:
//frameworks/base/services/core/jni/BroadcastRadio/Tuner.cpp
static void notifyAudioService(TunerContext& ctx, bool connected) {
...
AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_FM_TUNER,
connected ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE : AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
nullptr, kAudioDeviceName);
...
}
还真的会有connect的时候。
我有在之前的文章分析过原生Android tunner的机制:
https://blog.csdn.net/bberdong/article/details/81865975
其实hal层相关的东西都是有的!
最终发现
//frameworks/base/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jint moduleId,jobject bandConfig, bool withAudio, jobject callback) {
...
Tuner::assignHalInterfaces(env, tuner, module, halTuner);
...
}
//frameworks/base/services/core/jni/BroadcastRadio/Tuner.cpp
void assignHalInterfaces(/*参数省略*/)
{
...
notifyAudioService(ctx, true);
...
}
然后,我愉快地打开我们的FM app.发现Available input devices中有FM Tuner了。哈哈!回到最开始的地方,需要先调用nativeOpenTuner,打开收音机模块,我的AudioRecord调用就不会报错了。
然后,改下这两个地方,就能正常录到:
@@ -355,7 +355,7 @@ static int pcm_device_table[AUDIO_USECASE_MAX][2] = {
MULTIMEDIA2_PCM_DEVICE},
[USECASE_AUDIO_LINE_IN_PASSTHROUGH] = {-1, -1},
[USECASE_AUDIO_HDMI_IN_PASSTHROUGH] = {-1, -1},
-
+ [USECASE_AUDIO_RECORD_FM_VIRTUAL] = {AUDIO_RECORD_PCM_DEVICE,AUDIO_RECORD_PCM_DEVICE},
};
//这里改成自己的配置。比如说我的fm-loopback
<path name="fm-virtual-record capture-fm">
- <ctl name="QUAT_TDM_TX_0 Channels" value="One" />
- <ctl name="MultiMedia2 Mixer QUAT_TDM_TX_0" value="1" />
+ <!--<ctl name="QUAT_TDM_TX_0 Channels" value="One" />
+ <ctl name="MultiMedia2 Mixer QUAT_TDM_TX_0" value="1" />-->
+ <path name="fm-loopback"/>
</path>
当然,前面的一切,都是基于硬件要支持。。。