Android AudioRecord forçating another stream to MIC audio source
mise à jour 3: j'ai formé un partenariat avec un autre développeur et il semble que nous ayons trouvé quelqu'un qui puisse faire cela pour une grosse somme d'argent. Ils nous ont envoyé un test apk et il semble fonctionner. Nous allons aller de l'avant et d'acheter à la source. J'espère que nous n'allons pas être victime d'une arnaque. Je mettrai à jour une fois que j'aurai trouvé
mise à Jour 2: Encore à travailler dessus. Après des jours plus douloureux je pense maintenant qu'il n'y a rien de fantaisiste qui se passe mais ils sont tout simplement en utilisant AudioFlinger ( voir le lien ) du côté natif pour appeler AudioFlinger:: setParameters
je cherche maintenant à trouver comment je peux écrire un JNI simple pour appeler AudioFlinger:: setParameters avec audio_io_handle_t ioHandle, const String8& keyvalupairs
je sais ce que peut keyValuePairs , mais pas un indice au sujet de audio_io_handle_t
mise à Jour: je crois maintenant que d'autres applications peuvent être à l'aide de QCOM audio avec la CAF. Voir audio_extn_utils_send_audio_calibration à lien pour le même
et voice_get_incall_rec_snd_device à lien pour la même
Je n'ai pas de connaissances c/++. Comment puis-je savoir si je peux appeler ces méthodes du côté des natifs? Depuis d'autres applications peut, il doit y avoir un moyen.
je me suis battu avec cela plus de 40 jours pendant au moins 5-6 heures par jour. Je ne suis pas sûr que cela soit permis, 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 de VOICE_CALL. Bien que ASOP ne le mette pas en œuvre / ne le rende pas obligatoire, la plupart des fabricants ont mis en œuvre VOICE_CALL et les applications qui utilisent VOICE_CALL audio source fonctionnaient très bien sur de nombreux appareils. C'est jusqu'à Android 6.
Google a changé ce comportement avec Android 6. L'ouverture de VOICE_CALL audio source nécessite maintenant android.autorisation.CAPTURE_AUDIO_OUTPUT qui n'est accordé qu'aux applications système.
cela arrête essentiellement l'enregistrement des appels, ou il aurait dû. Eh bien, il le fait pour la mienne 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 Android, 6 et découvert certaines caractéristiques dans la façon dont ils gèrent à enregistrer.
ils utilisent tous Android AudioRecord classe et open MIC source audio. Je le fais aussi; mais sur mon application, je n'ai que l'audio de MIC, pas l'autre partie. Ce que j'ai découvert, c'est qu'ils émettent une sorte d'appel système juste après ou avant le début de l'enregistrement.
regardez la forme log suivante, une des applications qui enregistre avec succès VOICE_CALL, même si elle utilise MIC enregistrer. Il ressemble à l'application est une certaine façon de gérer le mix / route/stream / merge VOICE_CALL source audio en 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 avec Mic audio source input_source=1;routing=-2147483644.
puis, sur la deuxième ligne il fait quelque chose et obtenir accordé android.autorisation.MODIFY_AUDIO_SETTINGS qui est une permission 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 la diffusion en continu / fusion de la source audio VOICE_CALL vers MIC et enregistrer avec l'API AudioRecorder standart
sur la ligne suivante vous voyez que le matériel audio commence à mélanger VOICE_CALL (input_source=4) même s'ils ont ouvert MIC(1) source audio.
je suppose qu'ils ont utilisé
AudioManager.setParameters("key=value")
et essayé de nombreuses variantes telles que
AudioManager.setParameters("input_source=4;routing=-2147483584;format=1")
sans un peu de chance.
puis, j'ai trouvé Android, NDK, audio routing, forçant l'audio à travers le casque d'écoute et pensé qu'ils pourraient être certains comment mix/route/stream/merge VOICE_CALL dans la session AudioRecord actuelle et (depuis n'ont pas de connaissances C) essayé d'utiliser la réflation pour atteindre la même chose avec le code ci-dessous (encore) 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();
}
}
évidemment il y a quelque chose que je manque qui rend l'enregistrement possible.
j'ai même offert de payer pour obtenir cette information, tout a refusé. Juste assez, j'ai dit. J'ai l'intention de publier, une fois/si je la trouve!
avez-vous une idée de ce qu'ils font?
2 réponses
mon partenaire et moi avons pu acheter ce que nous cherchions. Nous étions sur le droit chemin, vous avez mis keyValuePairs sur le côté natif.
malheureusement, je ne peux pas publier la source en raison des restrictions de licence de la société nous l'avons fait écrit pour nous
Vos résultats sont intéressants. J'ai travaillé sur quelques petits projets impliquant L'API AudioRecord
. Avec un peu de chance, ce qui suit aidera:
dites que vous voulez configurer une instance AudioRecord
pour que vous puissiez enregistrer en mono 16 bits à 16kHz. Vous pouvez y arriver en créant une classe avec quelques méthodes d'aide, comme:
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, depuis Les utilisateurs de Marshmallow sont les seuls qui nous donnent accès à certaines autorisations si ce n'est presque tous les frais.
essayez de mettre en œuvre la classe et laissez-nous savoir comment cela s'est passé, aussi je laisse quelques références supplémentaires au cas où vous avez besoin de plus de recherche.
bonne chance.