Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7893997
  • 博文数量: 701
  • 博客积分: 2150
  • 博客等级: 上尉
  • 技术积分: 13233
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-29 16:28
个人简介

天行健,君子以自强不息!

文章分类

全部博文(701)

文章存档

2019年(2)

2018年(12)

2017年(76)

2016年(120)

2015年(178)

2014年(129)

2013年(123)

2012年(61)

分类: 架构设计与优化

2013-04-01 18:23:48

本文的参考和借鉴:(文章在开篇就声明,它有些过时了)。
所以本文分析的FFmpeg源码为Version0.8.2

我们在网络上看到的“视频”通常都是一种三层数据封装结构:

        编码图像和声音形成的裸视频流(如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)

{

    /* *_register_all类函数是注册库中所有有效的文件格式和codec。它们只用在main中调用一次即可*/

    avcodec_register_all();
    av_register_all();               // This registers all file formats and codecs  

    

    /*分析命令行输入参数,进行相关的设置。*/

    init_opts();
    
    /* parse options */
    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);

    return ffmpeg_exit(0);
}

转码结束后的收尾工作。

2.     transcode函数(ffmpeg.c)

/*
 * The following code is the main loop of the file converter
 */
static int transcode(AVFormatContext **output_files,int nb_output_files,
                     InputFile *input_files, int nb_input_files,
                     StreamMap *stream_maps, int nb_stream_maps)
{
    /* 分配输出流的空间 */
    ost_table = av_mallocz(sizeof(OutputStream *) * nb_ostreams); 
z:是ffmpeg对“malloc”的简单封装,它确保了存储地址的对齐,但不保证存储器的内存泄漏,二次释放或其它的问题。

    /* 针对每个输出文件的流,找到对应输入流的索引,以得到正确的流映射 */
    for(k=0;k     {  ... }

    /* 对每个输出流,计算相应的编码参数 */
    for(i=0;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;
            }  
    }

    /* 打开每个编码器 open each encoder */
    for(i=0;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) 
            {
               ... 
            }
        }
    }


    /* open files and write file headers */ 
    for(i=0;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) 
        {
          ... 
        }

    }

    /* at the end of stream, we must flush the decoder buffers */
    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);
        }
    }

    /* write the trailer if needed and close file */
    for(i=0;i     {
        os = output_files[i];
        av_write_trailer(os);
    }

    /* close each encoder */
    for(i=0;i     {
        ost = ost_table[i];
        if (ost->encoding_needed) 
        {
            av_freep(&ost->st->codec->stats_in);
            avcodec_close(ost->st->codec);
        }
    }

    /* close each decoder */
    for (i = 0; i < nb_input_streams; i++) 
    {
        ist = &input_streams[i];
        if (ist->decoding_needed) {
            avcodec_close(ist->st->codec);
        }
    }

    return ret;
}

3.     output_packet函数(ffmpeg.c)

static int output_packet(InputStream *ist, int ist_index,
                         OutputStream **ost_table, int nb_ostreams,
                         const AVPacket *pkt)
{
    AVFrame picture;
    AVPacket avpkt;

    // 先进行解码
    while (avpkt.size > 0 || (!pkt && got_output)) 
    {  
        /* decode the packet if needed */
        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;
            case AVMEDIA_TYPE_VIDEO:                   
                /* XXX: allocate picture correctly */
                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             {
                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);
    }
    return 0;
}

至此,框架分析结束。

阅读(4570) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~