Android AudioRecord 强制将另一个流传输到 MIC 音频源

2022-08-31 12:26:05

更新 3:我与另一位开发人员合作,我们似乎找到了一个可以花大笔钱做到这一点的人。他们给我们发送了一个测试apk,它似乎有效。我们将继续购买源头。我希望我们不会被骗。一旦我发现,我会更新

更新 2:仍在努力。经过更痛苦的日子,我现在认为没有什么花哨的事情发生,但他们只是在原生端使用AudioFlinger(参见链接)来调用AudioFlinger::setParameters。

我现在正在寻找如何编写一个简单的JNI来调用AudioFlinger::setParameters,audio_io_handle_t ioHandle,const String8& keyValuePairs

我知道keyValuePairs可以是什么,但不是关于audio_io_handle_t

更新:我现在相信其他应用程序可能正在将QCOM音频与CAF一起使用。请参阅链接中的audio_extn_utils_send_audio_calibration以获取相同内容

和链接voice_get_incall_rec_snd_device相同

我没有 C/++ 知识。如何确定是否可以从本机端调用这些方法?既然其他应用程序可以,那么必须有办法。


我已经为此挣扎了40多天,每天至少5-6个小时。我不确定SO是否允许,但我也很乐意为正确答案捐款。

我有一个使用VOICE_CALL音频源的通话录音应用程序。尽管 ASOP 没有实施/强制实施它,但大多数制造商已经实施了VOICE_CALL,并且使用VOICE_CALL音频源的应用在许多设备上都能正常工作。直到Android 6。

谷歌在Android 6中改变了这种行为。现在,打开音频源VOICE_CALL需要android.permission.CAPTURE_AUDIO_OUTPUT这仅授予系统应用程序。

这基本上停止了通话录音,或者它应该停止。好吧,对于我和200多个其他通话录音应用程序,除了3个已经找到绕过此限制的方法之外,它确实如此。

我一直在使用Android 6的许多不同手机上尝试这些应用程序,并在它们设法记录的方式中发现了某些特征。

他们都使用Android AudioRecord类和开放式MIC音频源。我也是。但是在我的应用程序上,我只从MIC获得音频,而不是另一方。我发现的是告诉我,他们在开始录制之后或之前发出某种系统调用。

查看以下日志表单,其中一个应用程序成功记录VOICE_CALL,即使它使用MIC进行记录。看起来应用程序是管理混合/路由/流/合并VOICE_CALL音频源到MIC的一些方法。

- D/audio_hw_primary: in_set_parameters: enter: kvpairs=input_source=1;routing=-2147483644
- D/PermissionCache: checking android.permission.MODIFY_AUDIO_SETTINGS for uid=10286 => granted (432 us)
- D/audio_hw_primary: in_set_parameters: enter: kvpairs=input_source=4;routing=-2147483584;format=1
- D/audio_hw_primary: select_devices: out_snd_device(0: ) in_snd_device(283: voice-dmic-ef)
- D/hardware_info: hw_info_append_hw_type : device_name = voice-dmic-ef
- D/voice: voice_get_incall_rec_snd_device: in_snd_device(283: voice-dmic-ef) incall_record_device(283: voice-dmic-ef)

正如您在第一行中看到的那样,它以MIC音频源input_source= 1;路由= - 2147483644开始。

然后,在第二行上,它做了一些事情,并被授予android.permission.MODIFY_AUDIO_SETTINGS这是正常的权限,我的应用程序也有它。这似乎是最重要的部分,看起来所有3个都在使用JNI来触发VOICE_CALL音频源到MIC的流式传输/合并,并使用标准AudioRecorder API进行录制。

在下一行中,您会看到音频硬件开始混合VOICE_CALL (input_source=4),即使它们已打开 MIC(1) 音频源。

我以为他们使用

AudioManager.setParameters("key=value")

并尝试了许多变体,例如

AudioManager.setParameters("input_source=4;routing=-2147483584;format=1")

没有任何运气。

然后,我发现Android,NDK,音频路由,强制音频通过耳机,并认为它们可能是混合/路由/流/合并VOICE_CALL当前AudioRecord会话的方式,并且(因为没有C知识)尝试使用通货再膨胀来实现与以下代码(再次)相同的事情,但没有运气。

private static void setForceUseOn() {

/*
setForceUse(int usage, int config);

----usage for setForceUse, must match AudioSystem::force_use
public static final int FOR_COMMUNICATION = 0;
public static final int FOR_MEDIA = 1;
public static final int FOR_RECORD = 2;
public static final int FOR_DOCK = 3;
public static final int FOR_SYSTEM = 4;
public static final int FOR_HDMI_SYSTEM_AUDIO = 5;

----device categories config for setForceUse, must match AudioSystem::forced_config
public static final int FORCE_NONE = 0;
public static final int FORCE_SPEAKER = 1;
public static final int FORCE_HEADPHONES = 2;
public static final int FORCE_BT_SCO = 3;
public static final int FORCE_BT_A2DP = 4;
public static final int FORCE_WIRED_ACCESSORY = 5;
public static final int FORCE_BT_CAR_DOCK = 6;
public static final int FORCE_BT_DESK_DOCK = 7;
public static final int FORCE_ANALOG_DOCK = 8;
public static final int FORCE_DIGITAL_DOCK = 9;
public static final int FORCE_NO_BT_A2DP = 10;
public static final int FORCE_SYSTEM_ENFORCED = 11;
public static final int FORCE_HDMI_SYSTEM_AUDIO_ENFORCED = 12;
public static final int FORCE_DEFAULT = FORCE_NONE;


 */

    try {
        Class audioSystemClass = Class.forName("android.media.AudioSystem");
        Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class);
        setForceUse.invoke(null, 0, 0);      // setForceUse(FOR_RECORD, FORCE_NONE)


    } catch (Exception e) {
        e.printStackTrace();
    }

}

显然,我缺少一些东西使录音成为可能。

我甚至提出付钱获取这些信息,但都被拒绝了。我说过很公平。我将发布一次/如果我找到它!

你知道他们可能在做什么吗?


答案 1

我和我的搭档能够购买我们正在寻找的东西。我们走在正确的道路上,你在原生端设置了keyValuePairs。

不幸的是,我无法发布源代码,因为我们为我们编写的公司的许可限制


答案 2

你的发现很有趣。我参与过一些涉及API的小项目。希望以下内容会有所帮助:AudioRecord

假设您要设置一个实例,以便可以在 16kHz 下以 16 位单声道进行录制。可以通过使用一些帮助器方法创建类来实现此目的,如下所示:AudioRecord

public class AudioRecordTool {
        private final int minBufferSize;
        private boolean doRecord = false;
        private final AudioRecord audioRecord;

        public AudioRecordTool() {
            minBufferSize = AudioTrack.getMinBufferSize(16000,
                    AudioFormat.CHANNEL_OUT_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);
            audioRecord = new AudioRecord(
                    MediaRecorder.AudioSource.VOICE_COMMUNICATION,
                    16000,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    minBufferSize * 2);
        }

        public void writeAudioToStream(OutputStream audioStream) {
            doRecord = true; //Will dictate if we are recording.
            audioRecord.startRecording();
            byte[] buffer = new byte[minBufferSize * 2];
            while (doRecord) {
                int bytesWritten = audioRecord.read(buffer, 0, buffer.length);
                try {
                    audioStream.write(buffer, 0, bytesWritten);
                } catch (IOException e) {
                    //You can log if you like, or simply ignore it
                   stopRecording();
                }
            }
            //cleanup
            audioRecord.stop();
            audioRecord.release();
        }

        public void stopRecording() {
            doRecord = false;
        }
    }

我猜你需要保持相同的权限,因为棉花糖用户是唯一授予我们访问某些权限的人,如果不是几乎所有很酷的权限。

尝试实现该类,并让我们知道它是如何进行的,我也留下了一些额外的参考资料,以防您需要更多研究。

祝你好运。

AudioRecord API

AudioCaputre API

MediaRecorder API


推荐