Android nimmt Audio auf und spielt es mit ijkplayer ab

1. Verwenden Sie MediaRecorder zum Aufnehmen

1.1 Starten Sie die Aufnahme

private MediaRecorder mMediaRecorder;
private File mTempFile;
public void startRecordAudio(Context context) {
    
    
        
        //临时文件
        if (mTmpFile == null) {
    
    
            mTmpFile = SdcardUtils.getPublicFile(context, "record/voice.aac");
        }

        Log.i("tmpFile path", mTempFile.getPath());
        final File file = mTempFile;
        if (file.exists()) {
    
    
            file.delete();
        }
        MediaRecorder recorder = mMediaRecorder;
        if (recorder == null) {
    
    
            recorder = new MediaRecorder();
            mMediaRecorder = recorder;
            
            //设置输入源
            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            
            //设置音频输出格式/编码格式
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    
    
                recorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS);
            } else {
    
    
                recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            }
            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            
            //设置音频输出路径
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
                recorder.setOutputFile(file);
            } else {
    
    
                recorder.setOutputFile(file.getAbsolutePath());
            }

            try {
    
    
                //准备录制
                recorder.prepare();

                //开始录制音频
                recorder.start();
                
                requestAudioFocus();
            } catch (Exception e) {
    
    
                e.printStackTrace();
                Log.e(TAG, e.toString());
            }
        }
    }

1.2 Aufnahme beenden

public File stopRecordAudio() {
    
    
        final MediaRecorder recorder = mMediaRecorder;
        if (recorder != null) {
    
    
            try {
    
    
                recorder.stop();
                recorder.release();
                mMediaRecorder = null;
            } catch (Exception e) {
    
    
                e.printStackTrace();
                Log.e(TAG, e.toString());
                return null;
            } finally {
    
    
                abandonAudioFocus();
            }
        }

        File file = mTmpFile;
        if (file != null && file.exists() && file.length() > 0) {
    
    
            return file;
        } else {
    
    
            return null;
        }
    }

2. Verwenden Sie AudioRecorder zum Aufnehmen

Wenn Sie AudioRecorder verwenden, müssen Sie die relevanten Kenntnisse über Abtastrate, Kanalkonfiguration und PCM-Audioformatdaten verstehen;

  1. PCM: Die ursprünglichen Audiodaten (AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_FLOAT usw.); unterschiedliche PCMs repräsentieren unterschiedliche Bittiefen
  2. Abtastrate: Wie stark das Aufnahmegerät das analoge Signal pro Zeiteinheit abtastet Je höher die Abtastfrequenz, desto realistischer und natürlicher die Wellenform der mechanischen Welle. Häufig verwendet werden 16000 (1,6 kHz), 44100 (44,1 kHz) usw.
  3. Kanal: Mono-Eingangskanal, Ausgangskanal usw., zugehörige Werte sind (AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO usw.)
//根据采样率+音频格式+频道得到录音缓存大小
int minBufferSize = AudioRecord.getMinBufferSize(16000,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);

Für die Initialisierung von AudioRecord werden außerdem die Abtastrate, das ursprüngliche PCM-Audioformat und der Kanal sowie die Größe des Aufnahmepuffers und das Aufnahmegerät wie folgt benötigt:

//MediaRecorder.AudioSource.MIC是麦克风录音设备,
//minBufferSize是录音缓存大小
new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize);

AudioRecorder startet die Aufnahmemethode

recorder.startRecording();

Öffnen Sie den Unterthread und erhalten Sie die Aufzeichnungsdaten über die Lesemethode

while (isRecording && !recordingAudioThread.isInterrupted()) {
    
    
    //获取录音数据
    read = mAudioRecorder.read(data, 0, data.length);
    if (AudioRecord.ERROR_INVALID_OPERATION != read) {
    
    
    try {
    
    
        fos.write(data);
        Log.i("audioRecord", "写录音数据->" + read);
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

2.1 Aufnahme starten (vollständiger Code)

private AudioRecord mAudioRecorder;
private File mTempFile;
private boolean isRecording;
private Thread recordingAudioThread;

public void startRecordAudio(Context context) {
    
    
        //临时路径
        if (mTmpFile == null) {
    
    
            mTmpFile = SdcardUtils.getPublicFile(context, "record/voice.pcm");
        }

        Log.i("tmpFile path", mTmpFile.getPath());
        final File file = mTmpFile;
        if (file.exists()) {
    
    
            file.delete();
        }

        AudioRecord recorder = mAudioRecorder;
        if (recorder == null) {
    
    
            //16000是采样率,常用采样率有16000(1.6KHz),441000(44.1KHz)
            int minBufferSize = AudioRecord.getMinBufferSize(16000,
                    AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT);

            recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, AudioFormat.CHANNEL_IN_MONO,
                    AudioFormat.ENCODING_PCM_16BIT, minBufferSize);

            mAudioRecorder = recorder;

            try {
    
    
                //开始录制音频
                isRecording = true;
                recorder.startRecording();

                recordingAudioThread = new Thread(() -> {
    
    
                    try {
    
    
                        file.createNewFile();
                    } catch (IOException e) {
    
    
                        e.printStackTrace();
                    }
                    FileOutputStream fos = null;
                    try {
    
    
                        fos = new FileOutputStream(file);
                    } catch (FileNotFoundException e) {
    
    
                        e.printStackTrace();
                    }
                    if (fos != null) {
    
    
                        byte[] data = new byte[minBufferSize];
                        int read;

                        while (isRecording && !recordingAudioThread.isInterrupted()) {
    
    
                            read = mAudioRecorder.read(data, 0, data.length);
                            if (AudioRecord.ERROR_INVALID_OPERATION != read) {
    
    
                                try {
    
    
                                    fos.write(data);
                                    Log.i("audioRecord", "录音数据:" + read);
                                } catch (IOException e) {
    
    
                                    e.printStackTrace();
                                }
                            }
                        }

                        try {
    
    
                            fos.close();
                        } catch (IOException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                });
                recordingAudioThread.start();

                requestAudioFocus();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
    }

2.2 Aufnahme beenden

public File stopRecordAudio() {
    
    
    isRecording = false;

    final AudioRecord audioRecord = mAudioRecorder;
    if (audioRecord != null) {
    
    
        audioRecord.stop();
        audioRecord.release();
        mAudioRecorder = null;
        recordingAudioThread.interrupt();
        recordingAudioThread = null;
    }

    File file = mTmpFile;
    if (file != null && file.exists() && file.length() > 0) {
    
    
        return file;
    } else {
    
    
        return null;
    }
}

3. PCM-Format-Transkodierung AAC

Diese Transcodierung ist zu schwierig, siehe Artikel: Android pcm is encoded as aac
, aber es gibt einen Fehler im Code in diesem Artikel, wenn die Abtastrate 44,1 kHz beträgt, kann es in AAC konvertieren und normal abspielen, aber wenn die Abtastrate ist 1,6 KHz, Nach der Konvertierung in AAC ist der wiedergegebene Ton extrem scharf. Nach langem Einstellen habe ich festgestellt, dass der Wert von freqIdx in der addADTStoPacket-Methode auf 4 geschrieben wurde.

Lesen Sie noch einmal den Artikel: Pcm to AAc , der Fehler ist behoben

package com.example.recordvoice.utils;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AacEncoder {
    
    
    ...
    private int sampleRateType;


    public void init(int sampleRate, int inChannel,
                     int channelCount, int sampleFormat,
                     String srcPath, String dstPath,
                     IHanlderCallback callback) {
    
    

        ...
        sampleRateType = ADTSUtils.getSampleRateType(mSampleRate);
        ...
    }

    ......
    ......
    ......

    private void addADTStoPacket(byte[] packet, int packetLen) {
    
    
        ....
        int freqIdx = sampleRateType;
        ....
    }

    static class ADTSUtils {
    
    
        private static Map<String, Integer> SAMPLE_RATE_TYPE;

        static {
    
    
            SAMPLE_RATE_TYPE = new HashMap<>();
            SAMPLE_RATE_TYPE.put("96000", 0);
            SAMPLE_RATE_TYPE.put("88200", 1);
            SAMPLE_RATE_TYPE.put("64000", 2);
            SAMPLE_RATE_TYPE.put("48000", 3);
            SAMPLE_RATE_TYPE.put("44100", 4);
            SAMPLE_RATE_TYPE.put("32000", 5);
            SAMPLE_RATE_TYPE.put("24000", 6);
            SAMPLE_RATE_TYPE.put("22050", 7);
            SAMPLE_RATE_TYPE.put("16000", 8);
            SAMPLE_RATE_TYPE.put("12000", 9);
            SAMPLE_RATE_TYPE.put("11025", 10);
            SAMPLE_RATE_TYPE.put("8000", 11);
            SAMPLE_RATE_TYPE.put("7350", 12);
        }

        public static int getSampleRateType(int sampleRate) {
    
    
            return SAMPLE_RATE_TYPE.get(sampleRate + "");
        }
    }
}

4. Audiofokus

4.1 Die Bedeutung von Audiofokus

Wenn zwei oder mehr Audios gleichzeitig auf demselben Audioausgabegerät abgespielt werden, werden die Klänge zusammengemischt.Um zu verhindern, dass alle Musikanwendungen gleichzeitig abgespielt werden, gibt es das Konzept des "Audiofokus". dass immer nur eine abgespielt werden kann. Die App erhält den Audio-Fokus

4.2 Audio-Fokus-Akquisition

private boolean mAudioFocus = false;
private AudioFocusRequest mAudioFocusRequest;
private AbsOnAudioFocusChangeListener mOnAudioFocusChangeListener;
private android.media.AudioManager mAM;

    abstract static class AbsOnAudioFocusChangeListener implements android.media.AudioManager.OnAudioFocusChangeListener {
    
    
        boolean isEnabled = true;

        @Override
        public final void onAudioFocusChange(int focusChange) {
    
    
            if (isEnabled) {
    
    
                onChane(focusChange);
            }
        }

        abstract void onChane(int focusChane);

    }

    private synchronized void requestAudioFocus() {
    
    
        android.media.AudioManager am = mAM;

        mOnAudioFocusChangeListener = new AbsOnAudioFocusChangeListener() {
    
    
            @Override
            void onChane(int focusChane) {
    
    
                Log.i(TAG, "focusChane:" + focusChane);

                synchronized (AudioManager.this) {
    
    
                    switch (focusChane) {
    
    
                        case AUDIOFOCUS_LOSS:
                        case AUDIOFOCUS_LOSS_TRANSIENT:
                        case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                            if (mAudioFocus) {
    
    
                                stopPlay(true, true);
                            } else {
    
    
                                stopPlay(false, true);
                            }
                            break;
                        case AUDIOFOCUS_GAIN:
                            mAudioFocus = true;
                            break;
                    }


                }

            }

        };

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
            mAudioFocusRequest = new AudioFocusRequest.Builder(AUDIOFOCUS_GAIN)
                    .setOnAudioFocusChangeListener(mOnAudioFocusChangeListener).build();
            am.requestAudioFocus(mAudioFocusRequest);
        } else {
    
    
            am.requestAudioFocus(mOnAudioFocusChangeListener, AudioStream.MODE_NORMAL, AUDIOFOCUS_GAIN);
        }

        mAudioFocus = true;
    }

4.3 Audiofokus aufgeben

    private synchronized void abandonAudioFocus() {
    
    
        android.media.AudioManager am = mAM;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
    
            if (mAudioFocusRequest != null) {
    
    
                am.abandonAudioFocusRequest(mAudioFocusRequest);
            }
        } else {
    
    
            if (mOnAudioFocusChangeListener != null) {
    
    
                am.abandonAudioFocus(mOnAudioFocusChangeListener);
            }
        }

        mAudioFocus = false;

    }

5. IjkPlayer

5.1 Einführung in IjkPlayer

IjkPlayer ist eine Reihe von Videoplayer-Frameworks, die von BiliBili basierend auf ffmpeg gepackt wurden, sodass sowohl das von ffmpeg unterstützte Streaming-Media-Format als auch das Videoformat ijk unterstützt werden; Android- und IOS-Open-Source-
Adressen werden unterstützt

5.2. Einführung von IjkPlayer

# required, enough for most devices.
# 常用
implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'

# Other ABIs: optional
# 其他cpu架构,现在Android上架必要有64位的架构,所以arm64现在已成为必须
implementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'

# ExoPlayer as IMediaPlayer: optional, experimental
# Exo播放器,引入这个才可获得IjkMediaPlayer对象
implementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'

Entsprechend der tatsächlichen Situation muss mein Projekt nur Folgendes einführen:

implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'
implementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'

5.3 Verwendung von IjkMediaPlayer

5.3.1 Initialisierung

IjkMediaPlayer player = new IjkMediaPlayer();

5.3.2 Wiedergabequelle konfigurieren

player.setDataSource(path);

Der Pfad kann eine lokale Adresse sein, es kann auch eine Online-Audioadresse sein, Sie können Eason Chan-The Lonely Brave verwenden , Sie können den rtmp-Stream auch direkt abspielen. Nach langem Suchen konnte ich den rtmp nicht finden Adresse eines Fernsehsenders, der in China senden kann, und schließlich diese verwendet: rtmp: / /media3.scctv.net/live/scctv_800

5.3.3, Wiedergabeabschlussüberwachung

player.setOnCompletionListener(OnCompletionListener listener)

Unabhängig davon, ob die Wiedergabe erfolgreich ist oder nicht, wird nach Abschluss des Wiedergabevorgangs oder Wiedergabe des Videos die Abschlussmethode zurückgerufen

5.3.4 Bereiten Sie sich auf die Überwachung vor

player.setOnPreparedListener(OnPreparedListener listener)

Nach dem Aufrufen von PrepareAsync() wird das Listening-Ereignis zurückgerufen, aber nachdem der Rückruf erfolgreich war, kann die Startmethode zum Abspielen ausgeführt werden

5.3.5. Spielen

//准备
player.prepareAsync();

player.setOnPreparedListener(iMediaPlayer -> {
    
    
    iMediaPlayer.start();

});

5.3.6 Hör auf zu spielen

Stop Playing ist eine Reihe von Combo-Schlägen

  1. Hör auf zu spielen
  2. zurücksetzen
  3. befreit
//停止播放
player.stop();
//重置状态
player.reset();
//释放相关资源
player.release();

6. Ergänzung

6.1、SdcardUtils

public class SdcardUtils {
    
    
    /**
     * 检查是否存在SD卡
     */
    public static boolean hasSdcard() {
    
    
        String state = Environment.getExternalStorageState();
        return state.equals(Environment.MEDIA_MOUNTED);
    }

    public static File getPublicFile(Context context, String child) {
    
    
        File file;
        if (hasSdcard()) {
    
    
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
    
    
                file = new File(Environment.getExternalStorageDirectory(), child);
            } else {
    
    
                file = new File(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC), child);
            }
        } else {
    
    
            file = new File(context.getFilesDir(), child);
        }

        mkdir(file.getParentFile());

        return file;
    }

    private static File mkdir(File dir) {
    
    
        if (!dir.exists()) {
    
    
            dir.mkdirs();
        }
        return dir;
    }
}

6.2, Referenz

Audio-Sampling-RateAndroid-
Android-Entwicklung: Verwenden Sie AudioRecord zum Aufnehmen, speichern Sie die Aufnahme als WAV-Datei und verwenden Sie AudioTrack zum
Speichern
der
Aufnahme
Audio-Fokus-Management

おすすめ

転載: blog.csdn.net/sinat_28884723/article/details/126977230