Android AudioRecordは、MICオーディオソースに別のストリームを強制します


81

更新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 / ++の知識がありません。これらのメソッドをネイティブ側から呼び出すことができるかどうかを確認するにはどうすればよいですか?他のアプリでもできるので、方法が必要です。


私はこれに40日以上、少なくとも1日5〜6時間苦労してきました。SOで許可されているかどうかはわかりませんが、正解も喜んで寄付させていただきます。

VOICE_CALLオーディオソースを使用する通話録音アプリがあります。ASOPはそれを実装/義務付けていませんが、ほとんどのメーカーはVOICE_CALLを実装しており、VOICE_CALLオーディオソースを使用するアプリは多くのデバイスで正常に機能しました。それはAndroid6までです。

GoogleはAndroid6でこの動作を変更しました。VOICE_CALLオーディオソースを開くには、システムアプリケーションにのみ付与されるandroid.permission.CAPTURE_AUDIO_OUTPUTが必要になりました。

これは基本的に通話の録音を停止します。まあ、それはこの制限を回避する方法を見つけた3人を除いて私のものと200以上の他の通話録音アプリのためにあります。

私はAndroid6を搭載したさまざまな携帯電話でこれらのアプリを試してきましたが、記録方法に特定の特徴があることがわかりました。

それらはすべてAndroidAudioRecordクラスを使用し、MICオーディオソースを開きます。私も; しかし、私のアプリでは、MICからのみ音声を取得し、相手からは取得しません。私が見つけたのは、彼らが録音を開始した直後または開始する前に、ある種のシステムコールを発行しているということです。

MICを使用して録音しているにもかかわらず、VOICE_CALLを正常に録音したアプリの1つから、次のログを確認してください。アプリは、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で始まります。

次に、2行目で何かを実行し、android.permission.MODIFY_AUDIO_SETTINGSが付与されます。これは通常の権限であり、私のアプリにもそれがあります。これは最も重要な部分のようで、3つすべてがJNIを使​​用して、VOICE_CALLオーディオソースのMICへのストリーミング/マージをトリガーし、標準のAudioRecorderAPIで録音するために行っていることを実行しているようです。

次の行では、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();
    }

}

明らかに、録音を可能にする何かが欠けています。

私はこの情報を得るためにお金を払うことさえ申し出ました、すべて拒否しました。私が言ったのは十分に公平だ。一度公開します/見つけたら公開します!

彼らが何をしているのかについて何か考えがありますか?


それを成功させるアプリをリンクできますか?
roarster 2016年


最後の投稿を2回投稿しました。
shmosel 2016年


1
覗いてみることができるように逆コンパイルしようとしましたか?(調査目的のみ;))
Buda Gavril 2016年

回答:


9

私と私のパートナーは私たちが探していたものを購入することができました。私たちは正しい道を進んでいました、あなたはネイティブ側でkeyValuePairsを設定しました。

残念ながら、私たちが作成した会社からのライセンス制限のため、ソースを公開できません。


3
ある人のバグは別の人の特徴です。
Elwin Arens 2016年

7
あなたが正しいですnLLはACRです、彼がソリューションを共有できない場合、なぜ彼はこのスタックオーバーフローコミュニティを使用しているのですか?これはコミュニティの主な動機に完全に反していると思います。解決策が得られれば、オープンソースを信じているのですぐに解決策を投稿することを約束します。
japanjot singh 2017

2
@japanjotsinghは一緒に解決策を見つけることができます
ピーター

3
@nLL失礼に聞こえるかもしれませんが、私たち全員にとって非常にイライラしています:-(
japanjot singh 2017

2
CallRecLibは、Android6で電話での会話を録音するためのハックを提供するライブラリです。github.com/ ViktorDegtyarev / CallRecLib
Viktor Degtyarev

0

あなたの発見は興味深いものです。私はAudioRecordAPIを含むいくつかの小さなプロジェクトに取り組んできました。うまくいけば、以下が役立つでしょう:

AudioRecord16kHzで16ビットモノラルで録音できるようにインスタンスを設定するとします。これは、次のようないくつかのヘルパーメソッドを使用してクラスを作成することで実現できます。

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

マシュマロユーザーだけが、クールなユーザーのほとんどすべてではないにしても、特定のアクセス許可へのアクセスを許可しているので、同じアクセス許可を維持する必要があると思います。

クラスを実装してみて、それがどのように進んだかを知らせてください。また、さらに調査が必要な場合に備えて、いくつかの追加の参照を残しておきます。

幸運を。

AudioRecord API

AudioCaputre API

MediaRecorder API


1
いいえ、残念ながらこれは私の質問とは何の関係もありません。私はすでにAudioReocordクラスを使用しており、広範囲にわたって使用しています。
nLL 2016年

この問題を解決しましたか?私は本当に実用的な解決策が必要です:
Nobody8 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.