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. Я тоже; но в моем приложении я получаю только аудио от 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, который является нормальным разрешением, и у моего приложения тоже есть. Это, по-видимому, самая важная часть, и похоже, что все 3 используют 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, аудио-маршрутизацию, заставляя звук через гарнитуру, и подумал, что они могут быть такими, как 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();
    }

}

Очевидно, что мне что-то не хватает, что делает запись возможной.

Я даже предложил заплатить, чтобы получить эту информацию, все отказались. Достаточно справедливо, я сказал. Я буду публиковать его один раз/если найду его!

Есть ли у вас какие-либо идеи относительно того, что они могут делать?

Спасибо заранее.

Ответ 1

Я и мой партнер смогли купить то, что искали. Мы были на правильном пути, вы установили keyValuePairs на родной стороне.

К сожалению, я не могу опубликовать источник из-за лицензионных ограничений от компании, которую мы ее пишут для нас

Ответ 2

Ваши результаты интересны. Я работал над несколькими небольшими проектами с использованием API AudioRecord. Надеюсь, следующее поможет:

Предположим, вы хотите настроить экземпляр 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

API AudioCaputre

API MediaRecorder