Android AudioRecord 强制另一个流到 MIC 音频源
Android AudioRecord forcing another stream to MIC audio source
更新 3: 我与另一位开发人员合作,我们似乎找到了可以花一大笔钱做这件事的人。他们给我们发了一个测试 apk,它似乎可以工作。我们将继续购买源代码。我希望我们不会被骗。一旦发现我会更新
更新 2: 仍在努力。经过更痛苦的日子后,我现在认为没有什么特别的,但他们只是在本机端使用 AudioFlinger (See the link) 来调用 AudioFlinger::setParameters
我现在正在寻找如何编写一个简单的 JNI 来调用 AudioFlinger::setParameters
audio_io_handle_t ioHandle, const String8& keyValuePairs
我知道 keyValuePairs 是什么,但不知道 audio_io_handle_t
更新: 我现在相信其他应用程序可能正在使用带有 CAF 的 QCOM 音频。请参阅 link for same
处的 audio_extn_utils_send_audio_calibration
和 voice_get_incall_rec_snd_device 在 link for same
我没有 C/++ 知识。我怎样才能知道我是否可以从本地调用这些方法?
既然其他应用可以,那肯定有办法。
我已经为此苦苦挣扎了 40 多天,每天至少有 5-6 个小时。我不确定 SO 是否允许,但我也很乐意为正确答案捐款。
我有一个使用 VOICE_CALL 音频源的通话录音应用程序。虽然 ASOP 没有 implement/mandate 它,但大多数制造商已经实施 VOICE_CALL 并且使用 VOICE_CALL 音频源的应用程序在许多设备上都能正常工作。直到 Android 6.
Google 使用 Android 更改了此行为 6. 打开 VOICE_CALL 音频源现在需要 android.permission.CAPTURE_AUDIO_OUTPUT,这仅授予系统应用程序。
这基本上停止了通话录音,或者它应该停止。好吧,它适用于我的和 200 多个其他通话录音应用程序,除了 3 个找到了绕过此限制的方法。
我一直在 Android6 的许多不同手机上试用这些应用程序,发现它们记录方式的某些特征。
他们都使用AndroidAudioRecordclass并打开MIC音源。我也做;但是在我的应用程序上,我只能从 MIC 获取音频,而不是对方。我发现的是,他们正在开始录制之前或之前发出某种系统调用。
查看以下记录成功记录的应用之一的日志 VOICE_CALL,即使它使用 MIC 记录。看起来应用程序是如何管理 mix/route/stream/merge 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;routing=-2147483644。
然后,在第二行它做了一些事情并获得了 android.permission.MODIFY_AUDIO_SETTINGS 的授予,这是正常的权限,我的应用程序也有。这似乎是最重要的部分,看起来所有 3 个都在使用 JNI 做他们所做的一切来触发 streaming/merging of 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, Audio routing, forcing audio through the headset 并认为它们可能是 mix/route/stream/merge VOICE_CALL 进入当前 AudioRecord 会话的某种方式并且(因为没有 C 知识)尝试使用 reflation运气不好用下面的代码(再次)实现同样的事情。
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();
}
}
显然,我缺少一些使录音成为可能的东西。
我什至提出付费获取这些信息,都被拒绝了。我说的很公平。我会发布它 once/if 我找到了!
你知道他们可能在做什么吗?
你的发现很有趣。我参与了一些涉及 AudioRecord
API 的小项目。希望以下内容对您有所帮助:
假设您想要设置一个 AudioRecord
实例,以便您可以以 16kHz 的频率录制 16 位单声道。您可以通过使用一些辅助方法创建 class 来实现此目的,如下所示:
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;
}
}
我猜您需要保留相同的权限,因为 Marshmallow 用户是唯一授予我们某些 权限 如果不是几乎所有的很酷的话。
尝试实施 class 并让我们知道它的进展情况,另外我会留下一些额外的参考资料,以备您需要更多研究时使用。
祝你好运。
我和我的搭档能够买到我们想要的东西。我们走在正确的道路上,您在本机端设置了 keyValuePairs。
不幸的是,由于我们为我们编写的公司的许可限制,我无法发布源代码
更新 3: 我与另一位开发人员合作,我们似乎找到了可以花一大笔钱做这件事的人。他们给我们发了一个测试 apk,它似乎可以工作。我们将继续购买源代码。我希望我们不会被骗。一旦发现我会更新
更新 2: 仍在努力。经过更痛苦的日子后,我现在认为没有什么特别的,但他们只是在本机端使用 AudioFlinger (See the link) 来调用 AudioFlinger::setParameters
我现在正在寻找如何编写一个简单的 JNI 来调用 AudioFlinger::setParameters audio_io_handle_t ioHandle, const String8& keyValuePairs
我知道 keyValuePairs 是什么,但不知道 audio_io_handle_t
更新: 我现在相信其他应用程序可能正在使用带有 CAF 的 QCOM 音频。请参阅 link for same
处的 audio_extn_utils_send_audio_calibration和 voice_get_incall_rec_snd_device 在 link for same
我没有 C/++ 知识。我怎样才能知道我是否可以从本地调用这些方法? 既然其他应用可以,那肯定有办法。
我已经为此苦苦挣扎了 40 多天,每天至少有 5-6 个小时。我不确定 SO 是否允许,但我也很乐意为正确答案捐款。
我有一个使用 VOICE_CALL 音频源的通话录音应用程序。虽然 ASOP 没有 implement/mandate 它,但大多数制造商已经实施 VOICE_CALL 并且使用 VOICE_CALL 音频源的应用程序在许多设备上都能正常工作。直到 Android 6.
Google 使用 Android 更改了此行为 6. 打开 VOICE_CALL 音频源现在需要 android.permission.CAPTURE_AUDIO_OUTPUT,这仅授予系统应用程序。
这基本上停止了通话录音,或者它应该停止。好吧,它适用于我的和 200 多个其他通话录音应用程序,除了 3 个找到了绕过此限制的方法。
我一直在 Android6 的许多不同手机上试用这些应用程序,发现它们记录方式的某些特征。
他们都使用AndroidAudioRecordclass并打开MIC音源。我也做;但是在我的应用程序上,我只能从 MIC 获取音频,而不是对方。我发现的是,他们正在开始录制之前或之前发出某种系统调用。
查看以下记录成功记录的应用之一的日志 VOICE_CALL,即使它使用 MIC 记录。看起来应用程序是如何管理 mix/route/stream/merge 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;routing=-2147483644。
然后,在第二行它做了一些事情并获得了 android.permission.MODIFY_AUDIO_SETTINGS 的授予,这是正常的权限,我的应用程序也有。这似乎是最重要的部分,看起来所有 3 个都在使用 JNI 做他们所做的一切来触发 streaming/merging of 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, Audio routing, forcing audio through the headset 并认为它们可能是 mix/route/stream/merge VOICE_CALL 进入当前 AudioRecord 会话的某种方式并且(因为没有 C 知识)尝试使用 reflation运气不好用下面的代码(再次)实现同样的事情。
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();
}
}
显然,我缺少一些使录音成为可能的东西。
我什至提出付费获取这些信息,都被拒绝了。我说的很公平。我会发布它 once/if 我找到了!
你知道他们可能在做什么吗?
你的发现很有趣。我参与了一些涉及 AudioRecord
API 的小项目。希望以下内容对您有所帮助:
假设您想要设置一个 AudioRecord
实例,以便您可以以 16kHz 的频率录制 16 位单声道。您可以通过使用一些辅助方法创建 class 来实现此目的,如下所示:
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;
}
}
我猜您需要保留相同的权限,因为 Marshmallow 用户是唯一授予我们某些 权限 如果不是几乎所有的很酷的话。
尝试实施 class 并让我们知道它的进展情况,另外我会留下一些额外的参考资料,以备您需要更多研究时使用。
祝你好运。
我和我的搭档能够买到我们想要的东西。我们走在正确的道路上,您在本机端设置了 keyValuePairs。
不幸的是,由于我们为我们编写的公司的许可限制,我无法发布源代码