java fastcv - 다른 오디오 스트림을 MIC 오디오 소스로 강제 전송하는 Android AudioRecord




android-ndk qualcomm (3)

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

업데이트 2 : 아직 작업 중입니다. 더 고통 스러울 날이 지나면 나는 아무것도 할 생각이 없다고 생각하지만, AudioFlinger : setParameters 를 호출하기 위해 네이티브 측 AudioFlinger ( 링크 참조 )를 사용하고 있습니다.

나는 AudioNavigator를 호출하기 위해 간단한 JNI를 어떻게 작성하는지 찾고있다. setParameters with audio_io_handle_t ioHandle, const String8 & keyValuePairs

나는 keyValuePairsaudio_io_handle_t에 대한 단서가 아니라는 것을 안다.

업데이트 : 이제 다른 응용 프로그램이 QCOM 오디오를 CAF와 함께 사용하고 있다고 생각합니다. 동일한 링크를 보려면 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을 사용하는 다양한 휴대 전화에서 이러한 응용 프로그램을 시험해보고 기록 관리 방식에 특정 특징을 발견했습니다.

그들은 모두 안드로이드 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에 스트리밍 / 병합을 트리거하고 standart AudioRecorder API로 기록하는 작업을 수행하는 것처럼 보입니다.

다음 라인에서는 오디오 하드웨어가 MIC (1) 오디오 소스를 연 경우에도 VOICE_CALL (input_source = 4) 믹싱을 시작합니다.

나는 그들이 사용했다고 생각했다.

AudioManager.setParameters("key=value")

여러 가지 변형을 시도했습니다.

AudioManager.setParameters("input_source=4;routing=-2147483584;format=1")

행운도없이.

그런 다음 안드로이드, NDK, 오디오 라우팅을 찾았 으며 헤드셋을 통해 오디오를 보내고 현재 AudioRecord 세션에서 VOICE_CALL을 믹스 / 라우트 / 스트리밍 / 병합하는 방법이 될 수 있다고 생각하고 (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();
    }

}

분명히 내가 녹음 할 수없는 뭔가가있다.

나는이 정보를 얻기 위해 돈을 내겠다고 제안했다, 모두 거부했다. 공정하게 말해 줬어. 내가 그것을 발견하면 나는 그것을 한 번 출판 할 것이다!

그들이 무엇을하고 있는지에 대해 당신은 어떤 생각을 가지고 있습니까?


Answers

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

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


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

16kHz에서 16 비트 모노로 녹음 할 수 있도록 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


맛을 사용하는 경우에는주의하십시오 . 구성 요소 이름 바로 가기 표기법.

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED -n com.companyname.develop/com.companyname.notification.RescheduleLocalNotificationsAtBootReceiver




java android c android-ndk qualcomm