ffmpeg对采集到摄像头视频和麦克风音频裸码封装

换个新工做,需求是将实时接收过来的音频和视频封装成mpegts格式,天然想到的是用ffmpeg进行编码,网上找了下这方面资料,无奈找了半天 没找到相应的资料,关于ffmpeg编译命令行的到是很是多,因此本身就研究总结下,参考ffmpeg中的例子,我的用ffmpeg时间很少,理解有限, 可能有错误之处,望指点一二。ide

输入参数:函数

输入视频流:h264,YUV420,分辨率1280*720,帧率25,码率4M编码

输入音频流:pcm,采样率8K,8位,单声道命令行

输出参数:code

音频:AAC,采样率44100,16位,双声道orm

视频:H264视频

mpegtsget

ffmpeg中对文件的输入和输出用一个结构体AVFormatContext来指定,其中AVInputFormat指定的是输入,AVOutputFormat指定的是输出,输出格式用函数av_guess_format来查找指定格式it

AVOutputFormat *fmt = av_guess_format("mpegts",NULL,NULL);io

AVFormatContex *oc = avformat_alloc_context();

oc->oformat= fmt;

其 中不管是AVInputFormat仍是AVOutputFormat,都包含一个结构体AVStream,这个结构体表示一个单一的流,音频流,或者视 频流,不管输入文件是用何种容器格式包装存储,他都将视频流和音频流分开放到这个AVStream中,这样咱们能够根据这两个流分别对音频或者视频进行处 理,能够对视频进行解码,或者编码,或者什么都不作,原样输出,也就是ffmpeg命令行中的copy。

其中建立输出流分两总状况:

1.音、视频编码:

AVCodec *codec = avcodec_find_encodec(CODEC_ID_AAC);//查找AAC编码器,视频就查找其余格式的编码器

AVStream *audio_st = avformat_new_stream(oc,codec);//其中就已经动态分配AVCodexContext,并将codec指定到AVCodexContext

2.音视频不编码

AVStream *video_st = avformat_new_stream(oc,NULL);//不指定编码器,这样实现就是ffmpeg命令行的copy,固然其余的参数赋值要根据AVInputFormat的AVStream的AVCodecContext来赋值。

完成上诉两个过程

都要进行下一步就是对AVStream中的AVCodexContext进行手动初始化

其 中共有的codec_id,bit_rate,codec_type,id等都须要赋值,其中id和avformat_new_stream初始化顺序有 关,若是先初始化的是视频,那么视频的id就是0,音频就是1,其中的视频AVStream中的AVCodecContext中的time_base这个 表示帧率

这样到后面的AVPacket中的stream_index就要根据这个id来指定当前packet是视频包仍是音频包

初始化完毕后须要判断是否对输出标志位进行设置

if(oc->oformat->flags & AVFMT_GLOBALHEADER)

{

    vodeo_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

}

若是进行编码的话

就须要进行打开编码器,不然就不须要

avcodec_open2

打开编码器的话,就须要一个结构体AVFrame

AVFrame这个存放一个解码后的原始数据,音频就是PCM,视频就是YUV

而AVPacket这个存放的是编码数据,音频多是AAC或者其余格式,视频就是H264等一桢数据,其中的flag表示是不是关键帧,这些在后续中都要手动设置的。

先说音频编码:

AVFrame有几个值须要赋值

其中的nb_samples要赋值输出流中AVStream的AVCodecContext的frame_size,表示一个AVFrame中包含多少个音频帧,虽然音频没有帧的概念,暂且这么理解吧

format要赋值成输出流AVStream的AVCodecContext的sample_fmt,指定AVFrame的音频格式,多是AV_SAMPLE_FMT_U8或者是AV_SAMPLE_S16,若是是U8还得须要音频重采样。

channel_layout这个不用说,声道数目,音频专用

这样咱们还须要一个缓冲区放入到AVFramleavede中,缓冲区的大小计算能够根据函数av_samples_get_buffer_size来计算

而后将此缓冲区赋值到AVFrame中,经过函数avcodec_fill_audio_frame

这样前期初始化完毕,接下来就处理就收到的视频帧和音频了

写文件第一帧时,必定要关键帧,而一些特殊容器如MP4或flv关键帧前还须要加pps和sps,若是想播放过程当中拖放视频,那每一个关键帧都得有sps和pps

avio_open,avformat_write_header

对于视频不须要编码,那就直接封装AVPacket

其 中的pts和dts若是没有B帧,值相等便可,而duration就按照90000除以帧率来赋值就能够,其中stream_index要和视频流 AVStream中的AVCodecContext的id要一致,若是是关键帧,就将flags设置AV_PKT_FLAG_KEY

而后直接av_interleaved_write_frame便可。

对 于音频能复杂些,毕竟涉及到编码,重采样,新版ffmpeg中对音频定义有两种格式,一种是平面的,一中是非平面的,目前我这个需求中是对非平面进行处 理,处理音频数据有一点要注意,音频数据必定要填满AVFrame的缓冲区,而后在进行编码。否者出来的效果就不受控制了。

项目中接收到的 音频数据是定长的,因此须要拼接到音频缓冲区大小,在写到音频缓冲区以前,先进行重采样处理,而后将重采样的数据拼接到AVFrame的音频缓冲区中,好 在ffmpeg中提供一个AVFifoBuffer,这个用起来很方便,这样每次收到数据写到av_fifo_generic_write中,另外一个判断 当前缓冲区是否达到AVFrame缓冲区大小便可,达到后进行编码,有一点说明的是每次编码前,AVFrame的pts要进行赋值,不然编码出的pts是 负值,就没有办法进行时间基数的转换了。通常来讲先定义一个int64_t lastpts = 0;编码前AVFrame->pts = lastpts;而后lastpts = lastpts + AVFrame->nb_samples;

进行avcodec_encode_audio2后,packet就是编码的数据,而后进行时间基的转换,用av_rescale_q函数时间。

最后写到av_interleaved_write_frame便可,因项目音频重采样有点问题,先不上代码了,后期再加上吧。