Android AudioRecord가 다른 스트림을 MIC 오디오 소스로 강제
업데이트 3 : 저는 다른 개발자와 파트너 관계를 맺었으며 많은 돈을 지불하고이를 수행 할 수있는 사람을 찾은 것 같습니다. 그들은 우리에게 테스트 APK를 보냈고 작동하는 것 같습니다. 우리는 계속해서 소스를 구입할 것입니다. 우리가 사기를 당하지 않기를 바랍니다. 알아 내면 업데이트하겠습니다
업데이트 2 : 아직 작업 중입니다. 더 고통스러운 날이 지나면 이제는 멋진 일이 없다고 생각하지만 단순히 네이티브 측에서 AudioFlinger ( 링크 참조 )를 사용하여 AudioFlinger :: setParameters 를 호출하고 있습니다.
이제 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();
}
}
분명히 녹음을 가능하게하는 내가 놓친 것이 있습니다.
나는 심지어이 정보를 얻기 위해 비용을 지불하겠다고 제안했지만 모두 거절했습니다. 내가 말했듯이 충분히 공평합니다. 한 번 / 찾으면 게시하겠습니다!
그들이 무엇을하는지 알고 있습니까?
나와 내 파트너는 우리가 찾고있는 것을 구입할 수있었습니다. 우리는 올바른 길을 가고 있었고 네이티브 측에서 keyValuePairs를 설정했습니다.
불행히도 우리가 작성한 회사의 라이센스 제한으로 인해 소스를 게시 할 수 없습니다.
Your findings are interesting. I have worked on a few small projects involving the AudioRecord
API. Hopefully, the following will help:
Say you want to setup an AudioRecord
instance so that you can record in 16-bit mono at 16kHz. You can achieve this by creating a class with a few helper methods, like the following:
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;
}
}
I am guessing you will need to keep the same permissions in place, since Marshmallow users are the only ones that grant us access to certain permissions if not almost all of the cool ones.
Try implementing the class and let us know how it went, also i'm leaving some extra references in case you need more research.
Good luck.
'program tip' 카테고리의 다른 글
Delphi XML 데이터 바인딩 마법사를 사용할 때 필요한 태그가 없습니다. (0) | 2020.10.06 |
---|---|
Robolectric으로 사용자 지정보기 테스트 (0) | 2020.10.06 |
SBT 및 IntelliJ IDEA로 여러 상호 의존적 모듈을 관리하는 방법은 무엇입니까? (0) | 2020.10.06 |
CSS에서 텍스트와 선택 상자를 같은 너비로 정렬 하시겠습니까? (0) | 2020.10.06 |
재귀 생성자 호출이 잘못된 C # 코드를 컴파일하는 이유는 무엇입니까? (0) | 2020.10.06 |