• 基于FFmpeg的音频编码(PCM数据编码成AAC android)


    概述

    在Android上实现录音,并利用 FFmpeg将PCM数据编码成AAC。

    详细

    之前做的一个demo,Android录音获取pcm数据(音频原始数据),然后利用 FFmpeg将PCM数据编码成AAC。

    一、准备工作

    开发环境

    jdk1.8 

    Eclipse Luna Service Release 1 (4.4.1)

    运行环境:

    华为荣耀6(Android4.4)、华为p9(Android7.0)

    二、程序实现

    代码截图如下:

    QQ图片20170703214653.png

    该demo依赖v7包。

    FFAacEncoderDemo工程src目录

    • MainActivity.java是主界面,用来控制录音开始和结束。

    • FFAacEncoder.java时native方法,用来和jni通信。

    jni目录:

    • include目录下是FFmpeg的一些.h文件

    • libs目录下是FFmpeg编译的so

    • AacCoderc 音频编码

    • cn_vn_aacEncoder_jni_FFAacEncoderJni jni代码与java通信。

    1 录音(获取pcm数据)

    开始录音

    private void startRecord(){  
      
        Log.i(TAG, "startRecord mIsRecording="+mIsRecording);  
        if(!mIsRecording){  
            mIsRecording = true;  
            synchronized (mLock) {  
                mAudioRecordGetExit = false;  
            }  
            //初始化ffmpeg 编码器  
            mFFAacEncoderJni.start();  
            //创建录音线程、开始录音  
            mAudioRecordGetThread = new Thread(new AudioRecordGet());  
            mAudioRecordGetThread.start();  
        }  
    }

    关闭录音

    private void stoptRecord(){  
        if(mIsRecording){  
            synchronized (mLock) {  
                mAudioRecordGetExit = true;  
            }  
            mIsRecording = false;  
        }  
    }

    具体录音通过使用AudioRecord。

    private class AudioRecordGet implements Runnable{  
        private AudioRecord mAudioRecord;  
        private static final boolean PCM_DUMP_DEBUG = true;  
        private static final boolean AAC_DUMP_DEBUG = false;  
        private int mAudioSource = MediaRecorder.AudioSource.MIC;  
        //采样频率,采样频率越高,音质越好。44100 、22050、 8000、4000等  
        private int mSampleRateHz = 8000;  
        //MONO为单声道 ,STEREO为双声道  
        private int mChannelConfig = AudioFormat.CHANNEL_IN_MONO;  
        //编码格式和采样大小,pcm编码;支持的采样大小16bit和8bit,采样大小越大,信息越多,音质越好。  
        private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;  
        //该size设置为AudioRecord.getMinBufferSize(mSampleRateHz, mChannelConfig, mAudioFormat); 编码aac时会失败。  
        private int mBufferSizeInBytes = 2048;//AudioRecord.getMinBufferSize(mSampleRateHz, mChannelConfig, mAudioFormat);  
        private AudioPCMData mAudioPCMData;  
        public AudioRecordGet() {  
            Log.i(TAG, "AudioRecordGet ");  
            mAudioPCMData = new AudioPCMData(mBufferSizeInBytes);  
            mAudioRecord = new AudioRecord(mAudioSource,  
                    mSampleRateHz, mChannelConfig, mAudioFormat, mBufferSizeInBytes);  
            Log.i(TAG,"mBufferSizeInBytes="+mBufferSizeInBytes);  
        }  
        @Override  
        public void run() {  
            mAudioRecord.startRecording();  
            FileOutputStream outPCM = null;  
            try {  
                if (PCM_DUMP_DEBUG) {  
                    String File = "/sdcard/test.pcm";  
                    outPCM = new FileOutputStream(File);  
                }  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            for(;;){  
                synchronized (mLock) {  
                    if(mAudioRecordGetExit){  
                        break;  
                    }  
                }  
                //读取录音数据  
                int readSize = mAudioRecord.read(mAudioPCMData.mData, 0, mBufferSizeInBytes);  
                if (AudioRecord.ERROR_INVALID_OPERATION != readSize) {  
                    if (PCM_DUMP_DEBUG && null != outPCM) {  
                        try {  
                            outPCM.write(mAudioPCMData.mData, 0, readSize);  
                        } catch (Exception e) {  
                            e.printStackTrace();  
                        }  
                    }  
                    mAudioPCMData.mFrameSize = readSize;  
                    Log.i(TAG, "audio pcm size="+readSize);  
                    //设置pcm数据,进行aac编码  
                    mFFAacEncoderJni.setPcmData(mAudioPCMData.mData, readSize);  
                }  
            }  
            if(PCM_DUMP_DEBUG && null != outPCM){  
                try {  
                    outPCM.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            //停止录音、释放  
            mAudioRecord.stop();  
            mAudioRecord.release();  
            //停止音频编码  
            mFFAacEncoderJni.stop();  
            Log.i(TAG,"AudioRecordGet thread exit success");  
        }         
    }

    2 PCM编码成AAC

    1 初始化编码器

    public class FFAacEncoder {  
        private String TAG = "FFAacEncoder java";  
        //load .so  
        static{  
            System.loadLibrary("avcodec-57");  
            System.loadLibrary("avdevice-57");  
            System.loadLibrary("avfilter-6");  
            System.loadLibrary("avformat-57");  
            System.loadLibrary("avutil-55");  
            System.loadLibrary("postproc-54");  
            System.loadLibrary("swresample-2");  
            System.loadLibrary("swscale-4");  
            System.loadLibrary("aacEncoder");  
        }  
          
        private int mNativeContext = 0;  
        //初始化编码器  
        private native final void nativeStart();  
        //对pcm数据进行编码  
        private native final void nativeSetPcmData(byte[] pcm, int len);  
        //必要的清理  
        private native final void nativeStop();  
          
        public void start(){  
            nativeStart();  
        }  
          
        public void setPcmData(byte[] pcm, int len){  
            nativeSetPcmData(pcm, len);  
        }  
          
        public void stop(){  
            nativeStop();  
        }  
    }

    调用nativeStart方法。

    2 音频编码

    //设置pcm数据,进行aac编码  
    mFFAacEncoderJni.setPcmData(mAudioPCMData.mData, readSize);

    调用nativeSetPcmData

    C++层代码,通过编码获取的AAC原始数据不同播放(存储在/sdcard/test.aac文件中,不能播放),

    需要添加adts header(不懂的可以了解一下AAC格式),这样才可以正常播放。/sdcard/adts.aac该文件添加了header,可以正常播放。

    三、运行效果

    运行效果:

    Screenshot_2017-07-03-22-04-41.png

    运行结果将生成文件 /sdcard/test.aac

    四、其他补充

    博客地址:http://blog.csdn.net/vnanyesheshou/article/details/54560684

    注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

  • 相关阅读:
    scss 随机宽度函数
    github.com 连不了 Connection refused ssh.github.com port 443 22
    vscode tab size 缩进数设置 4 却总是自动重置为 2 的问题
    npm link 无效的原因?执行命令打开的是编辑器的问题?
    OpenCV图像特征提取与描述
    pandas对时间序列中缺失值进行线性插值
    图文解析:梯度下降原理与实现
    kindle dx 越狱 汉化 做电脑显示器 安装kindlePdfViewer 全搞定
    接口的幂等性如何设计?
    Spring大事务到底如何优化?
  • 原文地址:https://www.cnblogs.com/demodashi/p/8481544.html
Copyright © 2020-2023  润新知