java - fastcv




다른 스트림을 MIC 오디오 소스로 강제 전송하는 Android AudioRecord (2)

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

업데이트 2 : 여전히 작업 중입니다. 더 고통스러운 날들에 이제는 멋진 일이 없다고 생각하지만 AudioFlinger :: setParameters 를 호출하기 위해 기본적으로 AudioFlinger ( 링크 참조 )를 사용하고 있습니다.

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

keyValuePairs 가 무엇인지 알 수 있지만 audio_io_handle_t에 대한 실마리는 아닙니다.

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

동일한 링크 에서 voice_get_incall_rec_snd_device

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

나는 하루에 적어도 5-6 시간 동안 40 일 동안이 문제로 어려움을 겪고 있습니다. SO가 허용하는지 확실하지 않지만 정답도 기꺼이 기부 해드립니다.

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

Google은 Android 6에서이 동작을 변경했습니다. VOICE_CALL 오디오 소스를 열려면 이제 시스템 응용 프로그램에만 부여되는 android.permission.CAPTURE_AUDIO_OUTPUT이 필요합니다.

이것은 본질적으로 통화 녹음을 중지하거나 가지고 있어야합니다. 글쎄,이 제한을 극복 할 수있는 방법을 찾은 3 명을 제외하고는 내 전화 및 200 개 이상의 다른 통화 녹음 앱에 적용됩니다.

나는 안드로이드 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();
    }

}

분명히 내가 누락 된 것이 기록을 가능하게합니다.

나는 심지어이 정보를 얻기 위해 돈을 내겠다고 제안했다. 내가 말한 것으로 충분합니다. 한 번 / 발견하면 게시하겠습니다!

그들이 무엇을하고 있는지에 대한 아이디어가 있습니까?


나와 내 파트너는 우리가 찾고 있던 것을 구입할 수있었습니다. 우리는 올바른 길을 가고있었습니다.

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


당신의 발견은 흥미 롭습니다. AudioRecord API와 관련된 몇 가지 작은 프로젝트를 수행 AudioRecord . 다음이 도움이되기를 바랍니다.

16kHz에서 16kHz 모노로 녹음 할 수 있도록 AudioRecord 인스턴스를 설정한다고 가정 AudioRecord . 다음과 같은 몇 가지 도우미 메서드를 사용하여 클래스를 만들어이 작업을 수행 할 수 있습니다.

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





qualcomm