Android AudioRecord가 다른 스트림을 MIC 오디오 소스로 강제


81

업데이트 3 : 저는 다른 개발자와 파트너 관계를 맺고 있으며 많은 돈으로이 작업을 수행 할 수있는 사람을 찾은 것 같습니다. 그들은 우리에게 테스트 APK를 보냈고 작동하는 것 같습니다. 우리는 계속해서 소스를 구입할 것입니다. 우리가 사기 당하지 않기를 바랍니다. 알아 내면 업데이트하겠습니다

업데이트 2 : 아직 작업 중입니다. 더 고통스러운 날이 지나면 이제는 멋진 일이 없다고 생각하지만 그들은 단순히 AudioFlinger :: setParameters 를 호출하기 위해 네이티브 측에서 AudioFlinger ( 링크 참조 )를 사용하고 있습니다.

이제 audio_io_handle_t ioHandle, const String8 & keyValuePairs로 AudioFlinger :: setParameters를 호출하는 간단한 JNI를 작성하는 방법을 찾고 있습니다.

keyValuePairs 가 무엇인지 알고 있지만 audio_io_handle_t에 대한 단서는 아닙니다.

업데이트 : 이제 다른 앱이 CAF와 함께 QCOM 오디오를 사용할 수 있다고 생각합니다. 동일한 링크 에서 audio_extn_utils_send_audio_calibration을 참조하십시오.

그리고 같은 링크 에서 voice_get_incall_rec_snd_device

C / ++ 지식이 없습니다. 네이티브 측에서 이러한 메서드를 호출 할 수 있는지 어떻게 알 수 있습니까? 다른 앱도 가능하므로 방법이 있어야합니다.


나는 하루에 적어도 5-6 시간 동안 40 일 넘게이 문제로 고생하고 있습니다. 나는 그것이 허용되는지 확실하지 않지만 정답을 위해 기부하게되어 기쁩니다.

VOICE_CALL 오디오 소스를 사용하는 통화 녹음 앱이 있습니다. ASOP가이를 구현 / 의무하지는 않지만 대부분의 제조업체는 VOICE_CALL을 구현했으며 VOICE_CALL 오디오 소스를 사용하는 앱은 많은 장치에서 제대로 작동했습니다. Android 6까지입니다.

Google은 Android 6에서이 동작을 변경했습니다. 이제 VOICE_CALL 오디오 소스를 열려면 시스템 애플리케이션에만 부여되는 android.permission.CAPTURE_AUDIO_OUTPUT이 필요합니다.

이것은 본질적으로 통화 녹음을 중지하거나 있어야합니다. 이 제한을 극복하는 방법을 찾은 3 명을 제외하고는 저와 200 개 이상의 다른 통화 녹음 앱을위한 것입니다.

저는 Android 6을 사용하는 다양한 휴대폰에서 이러한 앱을 사용해 보았고 그들이 기록하는 방식에서 특정 특성을 발견했습니다.

그들은 모두 Android AudioRecord 클래스와 오픈 MIC 오디오 소스를 사용합니다. 나도 그래요 하지만 내 앱에서는 상대방이 아닌 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로 스트리밍 / 병합하고 표준 AudioRecorder API로 녹음하는 작업을 수행하는 것 같습니다.

다음 줄에서 오디오 하드웨어가 MIC (1) 오디오 소스를 열었음에도 VOICE_CALL (input_source = 4) 믹싱을 시작하는 것을 볼 수 있습니다.

나는 그들이 사용했다고 가정했다

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();
    }

}

분명히 녹음을 가능하게하는 내가 놓친 것이 있습니다.

나는 심지어이 정보를 얻기 위해 돈을 지불하겠다고 제안했지만 모두 거절했습니다. 내가 말한만큼 공평하다. 한 번 / 찾으면 게시하겠습니다!

그들이 무엇을하는지 알고 있나요?


성공적으로 수행하는 앱을 연결할 수 있습니까?
Roarster


마지막 하나를 두 번 게시했습니다.
shmosel


1
디 컴파일을 시도해 보았습니까? (연구 목적으로 만;))
Buda Gavril

답변:


9

나와 내 파트너는 우리가 찾고있는 것을 구입할 수있었습니다. 우리는 올바른 길을 가고 있었고 네이티브 측에서 keyValuePairs를 설정했습니다.

불행히도 우리를 위해 작성한 회사의 라이센스 제한으로 인해 소스를 게시 할 수 없습니다.


3
한 사람의 버그는 다른 사람의 기능입니다.
Elwin Arens

7
당신이 맞아요 nLL은 ACR입니다, 그가 해결책을 공유 할 수 없다면 왜 그가이 stackoverflow 커뮤니티를 사용하고 있습니까? 나는 이것이 커뮤니티의 주된 동기에 완전히 어긋나는 것이라고 생각하며, 내가 해결책을 얻을 수 있다면 나는 오픈 소스를 믿는 것처럼 즉시 해결책을 게시 할 것이라고 약속합니다.
japanjot singh

2
@japanjotsingh 함께 찾기 솔루션을 수 있습니다
피터

3
나는 무례 소리가 될 수 @nLL하지만 형제 매우 :-( 우리 모두에게 실망
싱 japanjot

2
CallRecLib은 Android 6에서 전화 대화를 녹음하기위한 해킹을 제공하는 라이브러리입니다. github.com/ViktorDegtyarev/CallRecLib
Viktor Degtyarev

0

당신의 발견은 흥미 롭습니다. 저는 AudioRecordAPI 와 관련된 몇 가지 작은 프로젝트에서 작업했습니다 . 다음이 도움이되기를 바랍니다.

AudioRecord16kHz에서 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


1
아니요, 불행히도 이것은 내 질문과 관련이 없습니다. 저는 이미 AudioReocord 클래스를 사용하고 있으며 광범위하게 작업했습니다.
nLL

이 문제를 해결 했습니까? 난 정말 작업 솔루션 :-( 필요
Nobody8
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.