Android AudioRecord obligeant un autre stream à la source audio MIC

Mise à jour 3: Je me suis associé à un autre développeur et il semble que nous ayons trouvé quelqu’un qui puisse le faire pour une grosse sum d’argent. Ils nous ont envoyé un test apk et cela semble fonctionner. Nous allons aller de l’avant et acheter la source. J’espère que nous n’allons pas nous faire arnaquer. Je met à jour une fois que je découvre

Mise à jour 2: continue de travailler dessus. Après des jours plus douloureux, je pense maintenant qu’il n’y a rien d’extraordinaire, mais ils utilisent simplement AudioFlinger ( Voir le lien ) du côté natif pour appeler AudioFlinger :: setParameters

Je cherche maintenant à trouver comment écrire un simple JNI pour appeler AudioFlinger :: setParameters avec audio_io_handle_t ioHandle, const Ssortingng8 & keyValuePairs

Je sais ce que keyValuePairs peut être mais pas un indice sur audio_io_handle_t

Mise à jour: je crois maintenant que d’autres applications pourraient utiliser l’audio QCOM avec CAF. Voir audio_extn_utils_send_audio_calibration au lien pour le même

et voice_get_incall_rec_snd_device au lien pour le même

Je n’ai aucune connaissance en C / ++. Comment puis-je savoir si je peux appeler ces méthodes du côté natal? Comme d’autres applications peuvent, il doit y avoir un moyen.


Cela fait plus de 40 jours que je lutte depuis au moins 5 à 6 heures par jour. Je ne suis pas sûr que cela soit permis par SO mais je suis heureux de faire un don pour la bonne réponse aussi.

J’ai une application d’enregistrement d’appel qui utilise la source audio VOICE_CALL. Bien que ASOP ne l’implémente pas, la plupart des fabricants ont implémenté VOICE_CALL et les applications utilisant la source audio VOICE_CALL ont bien fonctionné sur de nombreux appareils. C’est jusqu’à Android 6.

Google a changé ce comportement avec Android 6. L’ouverture de la source audio VOICE_CALL nécessite désormais android.permission.CAPTURE_AUDIO_OUTPUT, qui n’est accordée qu’aux applications système.

Cela arrête essentiellement l’enregistrement d’appel, ou il devrait avoir. Eh bien, il le fait pour le mien et plus de 200 autres applications d’enregistrement d’appels, à l’exception de 3 qui ont trouvé un moyen de contourner cette limitation.

J’ai essayé ces applications sur de nombreux téléphones différents avec Android 6 et j’ai découvert certaines caractéristiques dans la façon dont ils enregistrent.

Ils utilisent tous la classe AudioRecord Android et ouvrent la source audio MIC. Moi aussi; mais sur mon application, je ne reçois que de l’audio de MIC, pas de l’autre partie. Ce que j’ai découvert, c’est qu’ils me disent qu’ils émettent un appel système juste après ou avant de commencer l’enregistrement.

Jetez un coup d’œil au journal suivant, l’une des applications qui enregistrent avec succès VOICE_CALL, même s’il utilise MIC pour enregistrer. Il semble que l’appli soit en train de gérer, mixer / router / stream / fusionner la source audio VOICE_CALL dans 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) 

Comme vous pouvez le voir dans la première ligne, il commence par la source audio MIC input_source = 1; routing = -2147483644.

Ensuite, sur la deuxième ligne, il fait quelque chose et obtient android.permission.MODIFY_AUDIO_SETTINGS qui est une autorisation normale et mon application l’a aussi. Cela semble être la partie la plus importante et il semble que tous les 3 utilisent JNI pour faire ce qu’ils font pour déclencher le streaming / fusion de la source audio VOICE_CALL vers MIC et enregistrer avec l’API standard AudioRecorder

À la ligne suivante, vous voyez que le matériel audio commence à mixer VOICE_CALL (input_source = 4) même s’il a ouvert la source audio MIC (1).

J’ai supposé qu’ils utilisaient

 AudioManager.setParameters("key=value") 

et essayé de nombreuses variantes telles que

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

sans chance.

Ensuite, j’ai trouvé Android, NDK, le routage audio, forçant l’audio à travers le casque et j’ai pensé qu’ils pourraient être un peu mix / route / stream / fusion VOICE_CALL dans la session AudioRecord en cours et pour réaliser la même chose avec le code ci-dessous (encore une fois) sans chance.

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

De toute évidence, il me manque quelque chose qui rend l’enregistrement possible.

J’ai même proposé de payer pour obtenir cette information, tout a été refusé. Assez bien je l’ai dit. Je le publierai une fois / si je le trouve!

Avez-vous une idée de ce qu’ils pourraient faire?

Mon partenaire et moi avons pu acheter ce que nous recherchions. Nous étions sur le bon chemin, vous définissez keyValuePairs du côté natif.

Malheureusement, je ne peux pas publier la source en raison des ressortingctions de licence imposées par la société que nous avions écrite pour nous.

Vos résultats sont intéressants. J’ai travaillé sur quelques petits projets impliquant l’API AudioRecord . Espérons que ce qui suit aidera:

Supposons que vous souhaitiez configurer une instance AudioRecord pour pouvoir enregistrer en mono 16 bits à 16 kHz. Vous pouvez y parvenir en créant une classe avec quelques méthodes d’assistance, comme suit:

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

Je suppose que vous aurez besoin de garder les mêmes permissions en place, car les utilisateurs de Marshmallow sont les seuls qui nous accordent l’access à certaines permissions, sinon presque toutes les plus intéressantes.

Essayez d’implémenter la classe et dites-nous comment ça s’est passé, aussi je laisse quelques références supplémentaires au cas où vous auriez besoin de plus de recherches.

Bonne chance.

API AudioRecord

API AudioCaputre

API MediaRecorder