Android FM recording function

Recently, there is a demand that FM needs to listen to and recognize songs, and then I found out that Android natively provides this:

//MediaRecorder.java
/**
* Audio source for capturing broadcast radio tuner output.
* @hide
*/
@SystemApi
public static final int RADIO_TUNER = 1998;

​ Then I will try, what will happen if I do this:

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);

As a result, I found an error:

 //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

The place where the error is reported is here:

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;
    ...
}

That is to say, AUDIO_DEVICE_IN_FM_TUNER is not included in availableDeviceTypes.

So, I checked the configuration:

//audio_policy_configuration.xml
...
<attachedDevices>
   ...
   <item>Built-In Mic</item>
   <item>Built-In Back Mic</item>
   <item>FM Tuner</item>
   ...
...

It stands to reason that he is like this:

//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;
            }
        }
     ...
}

I added log to print

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);
        }
    }

The log is as follows:

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

Obviously added it. But I 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:
  ...

Just not! This is strange. I thought about it for a long time. . .

Then I thought, would it be deleted after it was added?

Searched in AudioPolicyManager (the place where mAavailableInputDevices is really used), as expected

two places:

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++;
    }
    ...
}

The corresponding InputDevice has not been loaded (the details will not be expanded here, there is a corresponding so, which will be loaded), and will be deleted.

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;
    ...
}

When the state is switched, it will also be deleted. . . fm will disconnect?

Then, I did not give up and searched for AUDIO_DEVICE_IN_FM_TUNER. Just found this:

//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);
    ...
}

There will really be a time to connect.

I have analyzed the mechanism of the native Android tunner in the previous article:

https://blog.csdn.net/bberdong/article/details/81865975

In fact, there are things related to the hal layer!

finally found out

//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);
    ...
}

​ Then, I happily opened our FM app. I found FM Tuner in Available input devices. Haha! Going back to the very beginning, you need to call nativeOpenTuner first, turn on the radio module, and my AudioRecord call will not report an error.

​ Then, after changing these two places, you can record normally:

@@ -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>

Of course, everything in front is based on hardware support. . .

Guess you like

Origin blog.csdn.net/bberdong/article/details/84820825
FM