音视频实践学习

  • android全平台编译ffmpeg以及x264与fdk-aac实践
  • ubuntu下使用nginx和nginx-rtmp-module配置直播推流服务器
  • android全平台编译ffmpeg合并为单个库实践
  • android-studio使用cmake编译ffmpeg实践
  • android全平台下基于ffmpeg解码MP4视频文件为YUV文件
  • android全平台编译ffmpeg支持命令行实践
  • android全平台基于ffmpeg解码本地MP4视频推流到RTMP服务器
  • android平台下音频编码之编译LAME库转码PCM为MP3
  • ubuntu平台下编译vlc-android视频播放器实践
  • 图解YU12、I420、YV12、NV12、NV21、YUV420P、YUV420SP、YUV422P、YUV444P的区别
  • 图解RGB565、RGB555、RGB16、RGB24、RGB32、ARGB32等格式的区别
  • YUV420P、YUV420SP、NV12、NV21和RGB互相转换并存储为JPEG以及PNG图片
  • android全平台编译libyuv库实现YUV和RGB的转换
  • android平台下基于ffmpeg对相机采集的NV21数据编码为MP4视频文件
  • android平台下基于ffmpeg采集Camera数据编码成H.264推流到RTMP服务器
  • android平台下基于ffmpeg和ANativeWindow实现简单的视频播放器
  • android平台下基于ffmpeg实现对相机预览截图的功能(NV21数据编码为JPEG文件)
  • android平台下基于ffmpeg的swscale模块实现对YUV和RGB数据进行转换
  • android平台下基于MediaRecorder和AudioRecord实现录制AAC、PCM音频数据

概述

android sdk中提供了两种方式来实现音频的采集:MediaRecorderAudioRecord,其中的MediaRecorder处于更上层,它可以对音频录制的数据编码成AMR,MP3等格式,并存储为文件,而AudioRecord则更灵活,因为它可以录制最原始的PCM流数据,这个在直播中很常见。

录制接口

使用不同的方式处理音频,API肯定有所不同,这里暂时先抽象一个接口:

public interface IAudioRecorder {    interface RECORD_STATE {        int STATE_RECORDING = 0;        int STATE_SUCCESS = 1;        int STATE_ERROR = 2;    }    /**     * 初始化     */    void initRecorder();    /**     * 开始录制音频     */    int recordStart();    /**     * 停止录制音频     */    void recordStop();}

基于MediaRecorder

系统都已经给你提供好了的轮子,我们只需要设置几个参数就可以了:

public class MediaRecordRecorder implements IAudioRecorder {    /**     * 基于MediaRecorder录制音频     */    private MediaRecorder mMediaRecorder;    /**     * 是否正在录制     */    private boolean isRecord = false;    private String filePath;    public MediaRecordRecorder(String filePath){        this.filePath = filePath;    }    @Override    public void initRecorder() {        //实例化MediaRecorder对象        mMediaRecorder = new MediaRecorder();        //从麦克风采集声音数据        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);        //设置输出格式为MP4        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);        //设置采样频率44100 频率越高,音质越好,文件越大        mMediaRecorder.setAudioSamplingRate(44100);        //设置声音数据编码格式,音频通用格式是AAC        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);        //设置编码频率        mMediaRecorder.setAudioEncodingBitRate(96000);        //设置输出文件        mMediaRecorder.setOutputFile(filePath);    }    @Override    public int recordStart() {        //判断是否有外部存储设备sdcard        if (isRecord) {            return RECORD_STATE.STATE_RECORDING;        } else {            if (mMediaRecorder != null) {                try {                    mMediaRecorder.prepare();                    mMediaRecorder.start();                    // 让录制状态为true                    isRecord = true;                    return RECORD_STATE.STATE_SUCCESS;                } catch (IOException e) {                    e.printStackTrace();                }            }            return RECORD_STATE.STATE_ERROR;        }    }    @Override    public void recordStop() {        if (mMediaRecorder != null) {            isRecord = false;            mMediaRecorder.stop();            mMediaRecorder.release();            mMediaRecorder = null;        }    }}

笔者这里只是作演示操作,因此部分参数直接硬编码,实际过程最好以参数的形式对外提供

基于AudioRecord

这种方式需要我们自己在线程中,循环读取音频数据:

public class AudioRecordRecorder implements IAudioRecorder {    private static final String TAG = "AudioRecordRecorder";    private int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;    /**     * 音频采样率     */    public static int SAMPLE_RATE = 44100;    /**     * 单声道     */    public final static int CHANNEL = AudioFormat.CHANNEL_IN_STEREO;    /**     * 16比特     */    public final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;    /**     * 音频录制实例     */    protected AudioRecord audioRecord;    /**     * 录制线程     */    private Thread recordThread;    /**     * 输出的文件路径     */    private String pcmPath;    /**     * 缓冲区大小     */    private int bufferSize = 0;    /**     * 是否正在录制     */    private boolean isRecording = false;    /**     * 回调原始的PCM数据     */    private OnAudioRecordListener mOnAudioRecordListener;    public AudioRecordRecorder(String filePath) {        this.pcmPath = filePath;    }    @Override    public void initRecorder() {        if (null != audioRecord) {            audioRecord.release();        }        try {            bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL, AUDIO_FORMAT);            audioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE, CHANNEL, AUDIO_FORMAT, bufferSize);        } catch (Exception e) {            e.printStackTrace();        }    }    @Override    public int recordStart() {        if (isRecording) {            return RECORD_STATE.STATE_RECORDING;        } else if (audioRecord != null && audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {            try {                audioRecord.startRecording();                isRecording = true;                recordThread = new Thread(new AudioRecordRunnable());                recordThread.start();                return RECORD_STATE.STATE_SUCCESS;            } catch (Exception e) {                e.printStackTrace();            }        }        return RECORD_STATE.STATE_ERROR;    }    /**     * 停止音频录制     */    @Override    public void recordStop() {        try {            if (audioRecord != null) {                isRecording = false;                try {                    if (recordThread != null) {                        recordThread.join();                        recordThread = null;                    }                } catch (InterruptedException e) {                    e.printStackTrace();                }                //释放资源                recordRelease();            }        } catch (Exception e) {            e.printStackTrace();        }    }    private void recordRelease() {        if (audioRecord != null) {            if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {                audioRecord.stop();            }            audioRecord.release();            audioRecord = null;        }    }    private class AudioRecordRunnable implements Runnable {        private FileOutputStream outputStream = null;        @Override        public void run() {            try {                if (!TextUtils.isEmpty(pcmPath)) {                    outputStream = new FileOutputStream(pcmPath);                }                byte[] audioBuffer = new byte[bufferSize];                while (isRecording && audioRecord != null) {                    int audioSampleSize = audioRecord.read(audioBuffer, 0, bufferSize);                    if (audioSampleSize > 0) {                        if (outputStream != null) {                            outputStream.write(audioBuffer);                        }                        if (mOnAudioRecordListener != null) {                            mOnAudioRecordListener.onAudioBuffer(audioBuffer, audioBuffer.length);                        }                    }                }            } catch (FileNotFoundException e) {                e.printStackTrace();            } catch (IOException e) {                e.printStackTrace();            } finally {                close(outputStream);                outputStream = null;            }        }    }    private void close(Closeable closeable) {        if (closeable != null) {            try {                closeable.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    public void setOnAudioRecordListener(OnAudioRecordListener onAudioRecordListener) {        this.mOnAudioRecordListener = onAudioRecordListener;    }    public interface OnAudioRecordListener {        void onAudioBuffer(byte[] buffer, int length);    }}

工程实践

新建工程ffmpeg-audio-encode,在应用的点击事件中处理如下:

/**     * 点击开始录制按钮     * @param view     */    public void onRecordStart(View view) {        File targetDir = getExternalFilesDir(null);        if (audioRecorder == null) {            audioRecorder = new MediaRecordRecorder(targetDir.getAbsolutePath()+ File.separator+"output.aac");            //audioRecorder = new AudioRecordRecorder(targetDir.getAbsolutePath()+ File.separator+"output.pcm");        }        audioRecorder.initRecorder();        audioRecorder.recordStart();        //更新按钮状态        mBtnStart.setEnabled(false);        mBtnStop.setEnabled(true);    }    /**     * 点击停止按钮     * @param view     */    public void onRecordStop(View view) {        if (audioRecorder != null) {            audioRecorder.recordStop();            audioRecorder = null;        }        //更新按钮状态        mBtnStart.setEnabled(true);        mBtnStop.setEnabled(false);    }

基于MediaRecorder方式我们可以直接得到output.aac文件,这个直接使用系统的音频播放器即可播放

android平台下基于MediaRecorder和AudioRecord实现录制AAC、PCM音频数据_第1张图片

基于AudioRecord方式我们可以直接得到output.ocm文件,需要导出到电脑上,使用ffplay播放器进行播放

ffplay -ar 44100 -channels 2 -f s16le -i output.pcm

android平台下基于MediaRecorder和AudioRecord实现录制AAC、PCM音频数据_第2张图片

项目地址:ffmpeg-audio-encode
https://github.com/byhook/ffmpeg4android

更多相关文章

  1. 详解Android中的SQLite数据库存储
  2. android基于tcpdump的数据包捕获完整解决方案
  3. 【最近读书】《Android平台开发之旅(第2版)》
  4. Android 判断用户2G/3G/4G移动数据网络
  5. android数据库 Android自带数据库SQLite 操作 一步一步 图示教程
  6. Android Studio中使用adb shell查看数据库
  7. 共享全局数据 android
  8. Android 数据Parcel序列化过程源码分析
  9. Android Studio 2.2 发布,速度、智能和 Android 平台支持三个方面

随机推荐

  1. Gradle:Basic Project
  2. Android(安卓)Studio升级3.2以后 Android
  3. Mono登录界面记住密码的控件
  4. Android(安卓)本地推送消息到通知栏 Noti
  5. Android(安卓)简单音乐播放器(破烂版,后续
  6. android 源码编译同步的小问题
  7. android 文本转语音
  8. 【android】时间选择器
  9. Android(安卓)上传文件到XP
  10. 解决Android(安卓)studio 启动报错java.l