Android AudioRecord примушує інший потік до джерела звуку MIC


82

Оновлення 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 додатків для запису дзвінків, крім 3-х, хто знайшов спосіб обійти це обмеження.

Я пробував ці програми на багатьох різних телефонах з Android 6 і з'ясував певні характеристики в тому, як їм вдається записувати.

Усі вони використовують клас Android AudioRecord та відкрите джерело звуку MIC. Я - також; але в моєму додатку я отримую аудіо лише від MIC, а не від іншої сторони. Те, що я дізнався, говорить мені, що вони роблять якийсь системний дзвінок відразу після або перед початком запису.

Погляньте на наступний журнал із однієї з програм, яка успішно записує VOICE_CALL, хоча для запису використовується мікрофон. Схоже, додаток - це те, як вдається змішати / маршрут / потік / об’єднати джерело звуку 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, маршрутизацію звуку, примусове передавання звуку через гарнітуру і думав, що це може бути те, як змішувати / маршрутизувати / транслювати / об’єднувати 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();
    }

}

Очевидно, є щось, чого мені не вистачає, що робить запис можливим.

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

Чи маєте ви уявлення про те, що вони можуть робити?


не могли б ви зв’язати програми, які це успішно роблять?
ревстер


Останній ви опублікували двічі.
shmosel


1
ви пробували декомпілювати його, щоб можна було заглянути? (лише для дослідницьких цілей;))
Буда Гаврил

Відповіді:


9

Я та мій партнер змогли придбати те, що шукали. Ми були на правильному шляху, ви встановили keyValuePairs з рідної сторони.

На жаль, я не можу опублікувати джерело через ліцензійні обмеження від компанії, яку ми написали для нас


3
Помилка однієї людини - це особливість іншої людини.
Elwin Arens

7
ти маєш рацію nLL - це ACR, якщо він не може поділитися рішенням, чому він використовує цю спільноту stackoverflow? Я думаю, що це суперечить основному мотиву спільнот. Я обіцяю, що якщо я отримаю рішення, я негайно опублікую рішення, оскільки я вірю у відкритий код.
japanjot singh

2
@japanjotsingh дозволяє знаходити рішення разом
Петро,

3
@nLL Можливо, я звучу грубо, але брате, це дуже розчаровує всіх нас :-(
japanjot singh

2
CallRecLib - це бібліотека, яка забезпечує хакерство для запису телефонних розмов на Android 6 github.com/ViktorDegtyarev/CallRecLib
Віктор Дегтярьов

0

Ваші знахідки цікаві. Я працював над кількома невеликими проектами за участю AudioRecordAPI. Сподіваємось, допоможе наступне:

Скажімо, ви хочете встановити 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 - єдині, хто надає нам доступ до певних дозволів, якщо не майже до всіх крутих.

Спробуйте впровадити клас і повідомте нам, як він пройшов, також я залишаю додаткові посилання на випадок, якщо вам знадобляться додаткові дослідження.

Удачі.

API AudioRecord

API AudioCaputre

API MediaRecorder


1
Ні, на жаль, це не має нічого спільного з моїм запитанням. Я вже використовую клас AudioReocord і багато працював із ним.
nLL

Ви вирішили цю проблему? Мені дійсно потрібне робоче рішення :-(
Nobody8
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.