Обновление 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.
Google изменил это поведение в Android 6. Для открытия источника звука VOICE_CALL теперь требуется android.permission.CAPTURE_AUDIO_OUTPUT, который предоставляется только системным приложениям.
По сути, это останавливает запись разговоров, или должна была. Что ж, это подходит для моего и более 200 других приложений для записи разговоров, кроме трех, которые нашли способ обойти это ограничение.
Я пробовал эти приложения на разных телефонах с Android 6 и обнаружил определенные характеристики в том, как им удается записывать.
Все они используют класс Android AudioRecord и открытый источник звука MIC. Я тоже; но в своем приложении я получаю звук только от микрофона, а не от другого абонента. Я узнал, что они выдают какой-то системный вызов сразу после или перед началом записи.
Взгляните на следующий журнал из одного из приложений, которые успешно записывают VOICE_CALL, даже если для записи используется MIC. Похоже, приложение каким-то образом управляет микшированием / маршрутизацией / потоковой передачей / объединением аудиоисточника VOICE_CALL в микрофон.
- 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, чтобы делать то, что они когда-либо делали, чтобы инициировать потоковую передачу / слияние источника звука VOICE_CALL с микрофоном и записывать с помощью стандартного 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();
}
}
Очевидно, мне не хватает чего-то, что делает запись возможной.
Я даже предлагал заплатить за получение этой информации, все отказались. Я сказал справедливо. Я опубликую его один раз / если найду!
Вы хоть представляете, что они могут делать?
Ответы:
Я и мой партнер смогли купить то, что искали. Мы были на правильном пути, вы установили 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 - единственные, кто предоставляет нам доступ к определенным разрешениям, если не почти ко всем классным.
Попробуйте реализовать класс и дайте нам знать, как он прошел, также я оставляю несколько дополнительных ссылок на случай, если вам понадобится больше исследований.
Удачи.
AudioRecord API
AudioCaputre API
MediaRecorder API
источник