Android AudioRecord заставляет другой поток к источнику звука MIC
Обновление 3: я сотрудничал с другим разработчиком, и мы, кажется, нашли кого-то, кто может сделать это за большую сумму денег. Они прислали нам тестовый апк и похоже на работу. Мы пойдем дальше и купим источник. Я надеюсь, что мы не будем обмануты. Я обновлю, как только узнаю
Обновление 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.
Google изменил это поведение с помощью Android 6. Для открытия аудиоисточника VOICE_CALL теперь требуется android.permission.CAPTURE_AUDIO_OUTPUT, который предоставляется только системным приложениям.
По сути, это останавливает запись звонка, или это должно быть. Что ж, это подходит для моих и более 200 приложений для записи звонков, кроме 3-х, которые нашли способ обойти это ограничение.
Я пробовал эти приложения на разных телефонах с Android 6 и выяснил некоторые особенности их записи.
Все они используют класс Android AudioRecord и открывают источник звука 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;routing=-2147483644.
Затем, во второй строке, он что-то делает и получает разрешение android.permission.MODIFY_AUDIO_SETTINGS, что является нормальным разрешением, и у моего приложения оно тоже есть. Это, кажется, самая важная часть, и похоже, что все три используют JNI, чтобы делать то, что они когда-либо делают, чтобы инициировать потоковую передачу / слияние аудиоисточника VOICE_CALL в MIC и записывать с помощью стандартного API AudioRecorder.
На следующей строке вы видите, что аудиооборудование начинает микшировать 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();
}
}
Очевидно, что я что-то упускаю, что делает возможной запись.
Я даже предложил заплатить, чтобы получить эту информацию, все отказались. Достаточно справедливо, как я уже сказал. Я буду публиковать его один раз / если я найду это!
Есть ли у вас какие-либо идеи относительно того, что они могут делать?
2 ответа
Я и мой партнер смогли купить то, что искали. Мы были на правильном пути, вы установили keyValuePairs на нативной стороне.
К сожалению, я не могу опубликовать источник из-за лицензионных ограничений от компании, для которой мы его написали
Ваши выводы интересны. Я работал над несколькими небольшими проектами с участием AudioRecord
API. Надеюсь, поможет следующее:
Скажем, вы хотите настроить AudioRecord
например, чтобы вы могли записывать в 16-битном моно на 16 кГц. Вы можете достичь этого, создав класс с помощью нескольких вспомогательных методов, например:
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 являются единственными, которые предоставляют нам доступ к определенным разрешениям, если не почти ко всем интересным.
Попробуйте реализовать класс и дайте нам знать, как он прошел, также я оставляю несколько дополнительных ссылок на случай, если вам нужно больше исследований.
Удачи.