我们在网络上看到的“视频”通常都是一种三层数据封装结构:
编码图像和声音形成的裸视频流(如H.264, VP8, 等)和音频流(mp3, AAC等);
用容器格式将这些裸流封装形成文件(FLV,rmvb, mkv等)或流(mpeg2-TS等);
再利用传输协议(如RTSP, RTMP, HTTP, UDP等)将数据通过互联网,电视线路或无线网络等传输到用户端(电视机,PC,移动设备等)以收看。
FFmpeg的强大与复杂都体现在对这三层封装数据做了大量的支持和处理,从而使它能完成流或文件输入,解码显示,转码存储,封装推送等多项工作。
闲言少絮,进入正题。
对于C语言构建的面向过程编程的程序最基本的分析原则是:
去掉无关,分层推进。
FFmpeg最精简的伪码描述可如下图所示:
简而言之就是,打开文件,读取一帧数据 ,处理一帧数据,如此循环直到文件结束。
这个描述是简单而正确的。我们以转码一个文件来对应实际的代码进行分析。
1.main函数(ffmpeg.c)
int main(int argc, char **argv)
{
avcodec_register_all();
av_register_all();
// This registers all file formats andcodecs
init_opts();
parse_options(argc, argv, options, opt_output_file);
if(transcode(output_files,nb_output_files, input_files, nb_input_files,
stream_maps, nb_stream_maps) < 0)
ffmpeg_exit(1);
returnffmpeg_exit(0);
}
转码结束后的收尾工作。
2. transcode函数(ffmpeg.c)
static int transcode(AVFormatContext **output_files,intnb_output_files,
InputFile *input_files, int nb_input_files,
StreamMap *stream_maps, int nb_stream_maps)
{
ost_table =av_mallocz(sizeof(OutputStream *) * nb_ostreams);
//av_mallocz:是ffmpeg对“malloc”的简单封装,它确保了存储地址的对齐,但不保证存储器的内存泄漏,二次释放或其它的问题。
for(k=0;k<nb_output_files;k++)
{ ... }
for(i=0;i<nb_ostreams;i++)
{
if (!ost->enc)
ost->enc =avcodec_find_encoder(ost->st->codec->codec_id);
switch(codec->codec_type)
{
case AVMEDIA_TYPE_AUDIO:
ost->fifo= av_fifo_alloc(1024);
ost->reformat_pair =MAKE_SFMT_PAIR(AV_SAMPLE_FMT_NONE,AV_SAMPLE_FMT_NONE);
break;
case AVMEDIA_TYPE_VIDEO:
choose_pixel_fmt(ost->st,ost->enc);
break;
case AVMEDIA_TYPE_SUBTITLE:
ost->encoding_needed = 1;
ist->decoding_needed = 1;
break;
default:
abort();
break;
}
}
for(i=0;i<nb_ostreams;i++)
{
ost = ost_table[i];
if (ost->encoding_needed)
{
AVCodec *codec = ost->enc;
AVCodecContext *dec =input_streams[ost->source_index].st->codec;
if (avcodec_open2(ost->st->codec,codec, &ost->opts) <0)
{
...
}
}
}
for (i = 0;i < nb_input_streams; i++)
{
ist = &input_streams[i];
if (ist->decoding_needed)
{
if (avcodec_open2(ist->st->codec,codec, &ist->opts) <0)
{
...
}
}
}
for(i=0;i<nb_output_files;i++)
{
os = output_files[i];
if (avformat_write_header(os, &output_opts[i])< 0)
{
...
}
}
for(;received_sigterm == 0;)
{
is = input_files[file_index].ctx;
ret= av_read_frame(is, &pkt);
if (output_packet(ist, ist_index,ost_table, nb_ostreams,&pkt)< 0)
{
...
}
}
for (i = 0;i < nb_input_streams; i++)
{
ist = &input_streams[i];
if (ist->decoding_needed)
{
output_packet(ist, i, ost_table, nb_ostreams, NULL);
}
}
for(i=0;i<nb_output_files;i++)
{
os = output_files[i];
av_write_trailer(os);
}
for(i=0;i<nb_ostreams;i++)
{
ost = ost_table[i];
if (ost->encoding_needed)
{
av_freep(&ost->st->codec->stats_in);
avcodec_close(ost->st->codec);
}
}
for (i = 0;i < nb_input_streams; i++)
{
ist = &input_streams[i];
if (ist->decoding_needed) {
avcodec_close(ist->st->codec);
}
}
returnret;
}
3. output_packet函数(ffmpeg.c)
static int output_packet(InputStream *ist, int ist_index,
OutputStream **ost_table, int nb_ostreams,
const AVPacket *pkt)
{
AVFramepicture;
AVPacketavpkt;
//先进行解码
while(avpkt.size > 0 || (!pkt&& got_output))
{
if (ist->decoding_needed) {
switch(ist->st->codec->codec_type)
{
case AVMEDIA_TYPE_AUDIO:
ret = avcodec_decode_audio3(ist->st->codec,samples, &decoded_data_size,&avpkt);
break;
caseAVMEDIA_TYPE_VIDEO:
avcodec_get_frame_defaults(&picture);
ret = avcodec_decode_video2(ist->st->codec,&picture, &got_output,&avpkt);
break;
case AVMEDIA_TYPE_SUBTITLE:
ret =avcodec_decode_subtitle2(ist->st->codec,&subtitle,&got_output, &avpkt);
break;
default:
return -1;
}
}
if (start_time == 0 || ist->pts >=start_time)
for(i=0;i<nb_ostreams;i++)
{
if (ost->source_index == ist_index)
{
os = output_files[ost->file_index];
if (ost->encoding_needed)
{
av_assert0(ist->decoding_needed);
switch(ost->st->codec->codec_type)
{
case AVMEDIA_TYPE_AUDIO:
do_audio_out(os, ost, ist,decoded_data_buf, decoded_data_size);
break;
case AVMEDIA_TYPE_VIDEO:
do_video_out(os, ost, ist,&picture, &frame_size,same_quality? quality :ost->st->codec->global_quality);
if (vstats_filename &&frame_size)
do_video_stats(os, ost, frame_size);
break;
case AVMEDIA_TYPE_SUBTITLE:
do_subtitle_out(os, ost, ist, &subtitle,
pkt->pts);
break;
default:
abort();
}
}
}
}
av_free(buffer_to_free);
}
return0;
}
至此,框架分析结束