之前做的直播设计到音视频编码、rtmp推流、rtmp流播放等内容,现在抽时间整理一下。首先说一下音频编码(pcm 编码得到aac数据)。

                                                                                                                                                                                     

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);}@Overridepublic 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 初始化编码器

//初始化ffmpeg 编码器mFFAacEncoderJni.start();

FFAacEncoder代码如下

public class FFAacEncoder {private String TAG = "FFAacEncoder java";//load .sostatic{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方法。

最终调的代码如下(初始化):

int AacCodec::start(){ALOGW("start");av_register_all();//avcodec_register_all();mAVCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);//查找AAC编码器if(!mAVCodec){ALOGE("encoder AV_CODEC_ID_AAC not found");return -1;}mAVCodecContext = avcodec_alloc_context3(mAVCodec);if(mAVCodecContext != NULL){mAVCodecContext->codec_id         = AV_CODEC_ID_AAC;mAVCodecContext->codec_type       = AVMEDIA_TYPE_AUDIO;mAVCodecContext->bit_rate         = 12200;mAVCodecContext->sample_fmt       = AV_SAMPLE_FMT_FLTP;mAVCodecContext->sample_rate      = 8000;mAVCodecContext->channel_layout   = AV_CH_LAYOUT_MONO;mAVCodecContext->channels         = av_get_channel_layout_nb_channels(mAVCodecContext->channel_layout);}else {ALOGE("avcodec_alloc_context3 fail");return -1;}ALOGW("start  3 channels %d",mAVCodecContext->channels);if(avcodec_open2(mAVCodecContext, mAVCodec, NULL) < 0){ALOGE("aac avcodec open fail");av_free(mAVCodecContext);mAVCodecContext = NULL;return -1;}mAVFrame = av_frame_alloc();if(!mAVFrame) {avcodec_close(mAVCodecContext);av_free(mAVCodecContext);mAVCodecContext = NULL;return -1;}mAVFrame->nb_samples = mAVCodecContext->frame_size;mAVFrame->format = mAVCodecContext->sample_fmt;mAVFrame->channel_layout = mAVCodecContext->channel_layout;mBufferSize = av_samples_get_buffer_size(NULL, mAVCodecContext->channels, mAVCodecContext->frame_size, mAVCodecContext->sample_fmt, 0);if(mBufferSize < 0){ALOGE("av_samples_get_buffer_size fail");av_frame_free(&mAVFrame);mAVFrame = NULL;avcodec_close(mAVCodecContext);av_free(mAVCodecContext);mAVCodecContext = NULL;return -1;}mEncoderData = (uint8_t *)av_malloc(mBufferSize);if(!mEncoderData){ALOGE("av_malloc fail");av_frame_free(&mAVFrame);mAVFrame = NULL;avcodec_close(mAVCodecContext);av_free(mAVCodecContext);mAVCodecContext = NULL;return -1;}avcodec_fill_audio_frame(mAVFrame, mAVCodecContext->channels, mAVCodecContext->sample_fmt, (const uint8_t*)mEncoderData, mBufferSize, 0);if(DUMP_DEBUG){//存AAC原始音频数据mAVFormatContext = avformat_alloc_context();mAVOUtputFormat = av_guess_format(NULL, outFile, NULL);mAVFormatContext->oformat = mAVOUtputFormat;//Open output URLif (avio_open(&mAVFormatContext->pb, outFile, AVIO_FLAG_READ_WRITE) < 0){printf("Failed to open output file!\n");return -1;}mAVStream = avformat_new_stream(mAVFormatContext, 0);if (!mAVStream){return -1;}av_dump_format(mAVFormatContext, 0, outFile, 1);//Write Headeravformat_write_header(mAVFormatContext, NULL);}return 0;}

2 音频编码
//设置pcm数据,进行aac编码mFFAacEncoderJni.setPcmData(mAudioPCMData.mData, readSize);
调用nativeSetPcmData

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

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

int AacCodec::encode_pcm_data(void* pIn, int frameSize){int encode_ret = -1;int got_packet_ptr = 0;AVPacket pkt;av_init_packet(&pkt);pkt.data = NULL;pkt.size = 0;if(mAVCodecContext && mAVFrame){short2float((int16_t *)pIn, mEncoderData, frameSize/2);mAVFrame->data[0] = mEncoderData;mAVFrame->pts = 0;//音频编码encode_ret = avcodec_encode_audio2(mAVCodecContext, &pkt, mAVFrame, &got_packet_ptr);ALOGW("encode_pcm_data ----encode_ret:%d,got_packet_ptr:%d",encode_ret,got_packet_ptr);if(encode_ret < 0){ALOGE("Failed to encode!\n");return encode_ret;}ALOGW("encode_pcm_data ----size =%d",pkt.size);pkt.stream_index = mAVStream->index;void *adts;if(DUMP_DEBUG && pkt.size > 0){if(mADTSFile){void *adts = malloc(ADTS_HEADER_LENGTH);                                //添加adts header 可以正常播放。                               addADTSheader((uint8_t *)adts, pkt.size+ADTS_HEADER_LENGTH);fwrite(adts, 1, ADTS_HEADER_LENGTH, mADTSFile);fwrite(pkt.data, 1, pkt.size, mADTSFile);free(adts);}//无adts header,aac原始数据,不能播放mAVOUtputFormat->write_packet(mAVFormatContext, &pkt);}av_free_packet(&pkt);}return encode_ret;}

添加adts header 代码。这里要设置采样率下标(不同采样率对应不同下标,这个可以查一下),声道数。

void AacCodec::addADTSheader(uint8_t * in, int packet_size){int sampling_frequency_index = 11; //采样率下标,11表示8000int channel_configuration = mAVCodecContext->channels; //声道数in[0] = 0xFF;in[1] = 0xF9;in[2] = 0x40 | (sampling_frequency_index << 2) | (channel_configuration >> 2);//0x6c;in[3] = (channel_configuration & 0x3) << 6;in[3] |= (packet_size & 0x1800) >> 11;in[4] = (packet_size & 0x1FF8) >> 3;in[5] = ((((unsigned char)packet_size) & 0x07) << 5) | (0xff >> 3);in[6] = 0xFC;}

                                                                                                                                                                                                                                             

3 注意

我这里录音

采样率为8000HZ、声道为单声道,mBufferSizeInBytes 使用AudioRecord.getMinBufferSize(mSampleRateHz, mChannelConfig, mAudioFormat); 编码时会失败,使用2048 和4096都没有问题。


如果采样率要用16000HZ、声道为双声道,则要修改的地方有

private int mSampleRateHz = 16000;//MONO为单声道 ,STEREO为双声道private int mChannelConfig = AudioFormat.CHANNEL_IN_STEREO;private int mBufferSizeInBytes = 4096;

mAVCodecContext->sample_rate      = 16000;mAVCodecContext->channel_layout   = AV_CH_LAYOUT_STEREO;

int sampling_frequency_index = 8;



demo下载地址:http://www.demodashi.com/demo/10512.html


更多相关文章

  1. emoji表情在web html上显示
  2. Android(安卓)String.xml文件中转义字符的使用
  3. [多媒体]android MediaRecoder 实现录像机
  4. Android录音时指针摆动的实现(附源码)
  5. 庆祝一下,Android视频采集+H264编码成功
  6. Android(安卓)FFmpeg系列——2 播放音频
  7. Android麦克风录音的实现
  8. [置顶] Android(安卓)编码规范 | 代码风格指南
  9. android studio 中的编码问题

随机推荐

  1. Android自动化测试Espresso+UIAutomator
  2. Android_notifyDataSetChanged()
  3. Android(安卓)中文API (91) —— GestureDe
  4. Android使用Retrofit技术仿微信图片上传,
  5. 关于android:screenOrientation="portrai
  6. 在android 2.3.x中修改actionbarsherlock
  7. Android(安卓)HandlerThread分析
  8. Android(安卓)中加载延时加载动画
  9. 文件下载cordovaFileTransfer:cordova.fil
  10. android:persistent="true"相关--保证不