Update 3: Ich habe mich mit einem anderen Entwickler zusammengetan und wir scheinen jemanden gefunden zu haben, der dies für eine große Geldsumme tun kann. Sie haben uns eine Test-Apk geschickt und es scheint zu funktionieren. Wir werden fortfahren und die Quelle kaufen. Ich hoffe, wir werden nicht betrogen. Ich werde aktualisieren, sobald ich es herausgefunden habe
Update 2: Ich arbeite noch daran. Nach schmerzhafteren Tagen denke ich jetzt, dass nichts Besonderes los ist, aber sie verwenden einfach AudioFlinger ( siehe Link ) auf der nativen Seite, um AudioFlinger :: setParameters aufzurufen
Ich suche jetzt nach Möglichkeiten, wie ich eine einfache JNI schreiben kann, um AudioFlinger :: setParameters mit audio_io_handle_t ioHandle, const String8 & keyValuePairs aufzurufen
Ich weiß, was keyValuePairs sein kann , aber kein Hinweis auf audio_io_handle_t
Update: Ich glaube jetzt, dass andere Apps möglicherweise QCOM-Audio mit CAF verwenden. Siehe audio_extn_utils_send_audio_calibration unter dem Link dazu
und voice_get_incall_rec_snd_device unter link for same
Ich habe keine C / ++ Kenntnisse. Wie kann ich herausfinden, ob ich diese Methoden von der nativen Seite aus aufrufen kann? Da andere Apps das können, muss es einen Weg geben.
Ich habe über 40 Tage lang mindestens 5-6 Stunden am Tag damit zu kämpfen. Ich bin mir nicht sicher, ob dies von SO erlaubt ist, aber ich spende auch gerne für die richtige Antwort.
Ich habe eine Anrufaufzeichnungs-App, die die Audioquelle VOICE_CALL verwendet. Obwohl ASOP dies nicht implementiert / vorschreibt, haben die meisten Hersteller VOICE_CALL implementiert, und Apps, die die Audioquelle VOICE_CALL verwenden, funktionierten auf vielen Geräten einwandfrei. Das ist bis Android 6.
Google hat dieses Verhalten mit Android 6 geändert. Zum Öffnen der Audioquelle VOICE_CALL ist jetzt android.permission.CAPTURE_AUDIO_OUTPUT erforderlich, das nur Systemanwendungen gewährt wird.
Dies stoppt im Wesentlichen die Anrufaufzeichnung oder sollte es haben. Nun, es gilt für meine und mehr als 200 andere Anrufaufzeichnungs-Apps, abgesehen von 3, die einen Weg gefunden haben, diese Einschränkung zu umgehen.
Ich habe diese Apps auf vielen verschiedenen Handys mit Android 6 ausprobiert und bestimmte Eigenschaften bei der Aufzeichnung herausgefunden.
Sie alle verwenden die Android AudioRecord-Klasse und öffnen die MIC-Audioquelle. Ich auch; In meiner App erhalte ich jedoch nur Audio von MIC, nicht von der anderen Partei. Was ich herausgefunden habe, ist mir zu sagen, dass sie kurz nach oder vor Beginn der Aufnahme einen Systemaufruf ausgeben.
Sehen Sie sich das folgende Protokoll einer der Apps an, die VOICE_CALL erfolgreich aufzeichnen, obwohl MIC zum Aufzeichnen verwendet wird. Es sieht so aus, als ob die App das Mischen / Weiterleiten / Streamen / Zusammenführen von VOICE_CALL-Audioquellen in MIC verwaltet.
- 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)
Wie Sie in der ersten Zeile sehen können, beginnt es mit der MIC-Audioquelle input_source = 1; routing = -2147483644.
Dann macht es in der zweiten Zeile etwas und erhält android.permission.MODIFY_AUDIO_SETTINGS, was eine normale Erlaubnis ist und meine App hat es auch. Dies scheint der wichtigste Teil zu sein, und es sieht so aus, als würden alle drei JNI verwenden, um das Streaming / Zusammenführen der VOICE_CALL-Audioquelle zum MIC auszulösen und mit der Standard-AudioRecorder-API aufzunehmen
In der nächsten Zeile sehen Sie, dass die Audio-Hardware beginnt, VOICE_CALL (input_source = 4) zu mischen, obwohl sie die MIC (1) -Audioquelle geöffnet hat.
Ich habe angenommen, dass sie verwendet
AudioManager.setParameters("key=value")
und versuchte viele Variationen wie
AudioManager.setParameters("input_source=4;routing=-2147483584;format=1")
ohne Glück.
Dann habe ich Android, NDK, Audio-Routing gefunden, Audio durch das Headset erzwungen und dachte, es könnte sein, wie VOICE_CALL in die aktuelle AudioRecord-Sitzung gemischt / weitergeleitet / gestreamt / zusammengeführt wird, und habe (da ich keine C-Kenntnisse habe) versucht, die Reflation zu verwenden das gleiche mit dem folgenden Code (wieder) ohne Glück zu erreichen.
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();
}
}
Offensichtlich fehlt mir etwas, das die Aufnahme ermöglicht.
Ich habe sogar angeboten zu zahlen, um diese Informationen zu erhalten, alles abgelehnt. Fair genug habe ich gesagt. Ich werde es einmal veröffentlichen / wenn ich es finde!
Haben Sie eine Idee, was sie tun könnten?
Antworten:
Ich und mein Partner konnten kaufen, was wir suchten. Wir waren auf dem richtigen Weg, Sie haben keyValuePairs auf der nativen Seite festgelegt.
Leider kann ich die Quelle aufgrund der Lizenzbeschränkungen des Unternehmens, für das wir sie geschrieben haben, nicht veröffentlichen
quelle
Ihre Ergebnisse sind interessant. Ich habe an einigen kleinen Projekten mit der
AudioRecord
API gearbeitet. Hoffentlich hilft Folgendes:Angenommen, Sie möchten eine
AudioRecord
Instanz einrichten, damit Sie mit 16 kHz in 16-Bit-Mono aufnehmen können. Sie können dies erreichen, indem Sie eine Klasse mit einigen Hilfsmethoden wie den folgenden erstellen:Ich vermute, Sie müssen die gleichen Berechtigungen beibehalten, da Marshmallow- Benutzer die einzigen sind, die uns Zugriff auf bestimmte Berechtigungen gewähren, wenn nicht fast alle coolen.
Versuchen Sie, die Klasse zu implementieren, und lassen Sie uns wissen, wie es gelaufen ist. Außerdem hinterlasse ich einige zusätzliche Referenzen, falls Sie weitere Nachforschungen anstellen müssen.
Viel Glück.
AudioRecord API
AudioCaputre API
MediaRecorder API
quelle