Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7771161
  • 博文数量: 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)

分类: 架构设计与优化

2014-04-19 22:17:39

四、主函数代码
001 int main(int argc, char **argv)
002 {
003     const char *filename;
004     AVOutputFormat *fmt;
005     AVFormatContext *oc;
006     AVStream *audio_st, *video_st;
007     double audio_pts, video_pts;
008     int i;

        /* initialize libavcodec, and register all codecs and formats */
009     av_register_all();

010     if (argc != 2) {
011         printf("usage: %s output_file\n"
                  "API example program to output a media file with libavformat.\n"
                  "The output format is automatically guessed according to the file extension.\n"
                  "Raw images can also be output by using '%%d' in the filename\n"
                  "\n", argv[0]);
012         exit(1);
       }

013     filename = argv[1];

       /* auto detect the output format from the name. default is mpeg. */
014     fmt = guess_format(NULL, filename, NULL);
015     if (!fmt) {
016         printf("Could not deduce output format from file extension: using MPEG.\n");
017         fmt = guess_format("mpeg", NULL, NULL);
018     }
019     if (!fmt) {
           fprintf(stderr, "Could not find suitable output format\n");
           exit(1);
        }


        /* allocate the output media context */
020     oc = avformat_alloc_context();
021     if (!oc) {
022         fprintf(stderr, "Memory error\n");
023         exit(1);
024     }
025     oc->oformat = fmt;
026     snprintf(oc->filename, sizeof(oc->filename), "%s", filename);

        /* add the audio and video streams using the default format codecs
          and initialize the codecs */
027     video_st = NULL;
028     audio_st = NULL;
029     if (fmt->video_codec != CODEC_ID_NONE) {
030         video_st = add_video_stream(oc, fmt->video_codec);
031     }
032     if (fmt->audio_codec != CODEC_ID_NONE) {
033         audio_st = add_audio_stream(oc, fmt->audio_codec);
034     }
        /* set the output parameters (must be done even if no parameters). */
035     if (av_set_parameters(oc, NULL) < 0) {
036         fprintf(stderr, "Invalid output format parameters\n");
037         exit(1);
038     }

039     dump_format(oc, 0, filename, 1);

        /* now that all the parameters are set, we can open the audio and
           video codecs and allocate the necessary encode buffers */
040     if (video_st)
041         open_video(oc, video_st);
042     if (audio_st)
043         open_audio(oc, audio_st);

        /* open the output file, if needed */
044     if (!(fmt->flags & AVFMT_NOFILE)) {
045         if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) {
046             fprintf(stderr, "Could not open '%s'\n", filename);
047             exit(1);
048         }
049     }

        /* write the stream header, if any */
050     av_write_header(oc);

051     for(;;) {
            /* compute current audio and video time */
052         if (audio_st)
053             audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
054         else
055             audio_pts = 0.0;

056         if (video_st)
057             video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
058         else
059             video_pts = 0.0;

060         if ((!audio_st || audio_pts >= STREAM_DURATION) &&
061             (!video_st || video_pts >= STREAM_DURATION))
062             break;

            /* write interleaved audio and video frames */
063         if (!video_st || (video_st && audio_st && audio_pts < video_pts)) {
064             write_audio_frame(oc, audio_st);
065         } else {
066             write_video_frame(oc, video_st);
067         }
068     }

        /* write the trailer, if any.  the trailer must be written
         * before you close the CodecContexts open when you wrote the
         * header; otherwise write_trailer may try to use memory that
         * was freed on av_codec_close() */
069     av_write_trailer(oc);

        /* close each codec */
070     if (video_st)
071         close_video(oc, video_st);
072     if (audio_st)
073         close_audio(oc, audio_st);

        /* free the streams */
074     for(i = 0; i < oc->nb_streams; i++) {
075         av_freep(&oc->streams[i]->codec);
076         av_freep(&oc->streams[i]);
077     }

078     if (!(fmt->flags & AVFMT_NOFILE)) {
079         /* close the output file */
080         url_fclose(oc->pb);
081     }

082     /* free the stream */
083     av_free(oc);

084     return 0;
085 }

五、主函数流程分析
1. 该例子讲了如何输出一个libavformat库所支持格式的媒体文件。 
(1)av_register_all(),初始化libavcodec库,并注册所有的编解码器和格式。
(2)guess_format(),根据文件名来获取输出文件格式,默认为mpeg。
(3)av_alloc_format_context()分配输出媒体内容。
  ov->oformat = fmt;
  snprintf( oc->filename, sizeof(oc->filename), “%s”, filename );
(4)add_video_stream()使用默认格式的编解码器来增加一个视频流,并初始化编解码器。
  (4.1)av_new_stream()增加一个新的流到一个媒体文件。
  (4.2)初始化编解码器:
    c = st->codec;
    c->codec_id = codec_id;
    c->codec_type = CODEC_TYPE_VIDEO;
    c->bit_rate = 400000;
    c->width = 352;
    c->height = 288;
    c->time_base.den = STREAM_FRAME_RATE; //每秒25副图像
    c->time_base.num = 1;
    c->gop_size = 12;
    c->pix_fmt = STREAM_PIX_FMT; //默认格式为PIX_FMT_YUV420P
    …… ……
(5)av_set_parameters()设置输出参数,即使没有参数,该函数也必须被调用。
(6)dump_format()输出格式信息,用于调试。
(7)open_video()打开视频编解码器并分配必要的编码缓存。
  (7.1)avcodec_find_encoder()寻找c->codec_id指定的视频编码器。
  (7.2)avcodec_open()打开编码器。
  (7.3)分配视频输出缓存:
    video_outbuf_size = 200000;
    video_outbuf = av_malloc( video_outbuf_size );
  (7.4)picture = alloc_picture()分配原始图像。
    (7.4.1)avcodec_alloc_frame()分配一个AVFrame并设置默认值。
    (7.4.2)size = avpicture_get_size()计算对于给定的图片格式以及宽和高,所需占用多少内存。
    (7.4.3)picture_buf = av_malloc( size )分配所需内存。
    (7.4.4)avpicture_fill()填充AVPicture的域。
  (7.5)可选。如果输出格式不是YUV420P,那么临时的YUV420P格式的图像也是需要的,
           由此再转换为我们所需的格式,因此需要为临时的YUV420P图像分配缓存:
         tmp_picture = alloc_picture()


          说明:tmp_picture,picture,video_outbuf。
          如果输出格式为YUV420P,则直接通过avcodec_encode_video()函数,
              将picture缓存中的原始图像编码保存到video_outbuf缓存中;
          如果输出格式不是YUV420P,则需要先通过sws_scale()函数,将YUV420P格式转换为目标格式,
              此时tmp_picture缓存存放的是YUV420P格式的图像,而picture缓存为转换为目标格式后保存的图像,
              进而再将picture缓存中的图像编码保存到video_outbuf缓存中。
(8)url_fopen()打开输出文件,如果需要的话。
(9)av_write_header()写流动头部。
(10)LOOP循环{
    计算当前视频时间video_pts
    是否超时退出循环?
    write_video_frame()视频编码
  }
  (10.1)write_video_frame()
    如果图片不是YUV420P,则需要用sws_scale()函数先进行格式转换。
    若需要原始图像:
      av_init_packet()初始化一个包的选项域。
      av_write_frame()向输出媒体文件写一个包,该包会包含一个视频帧。
    若需要编码图像:
      avcodec_encode_video()编码一视频帧。
      av_init_packet()
      av_write_frame()
(11)close_video()关闭每个编解码器。
(12)av_write_trailer()写流的尾部。
(13)释放资源
  av_freep()释放AVFormatContext下的AVStream->AVCodecContext和AVStream:
    for( i = 0; i < oc->nb_streams; i++ ){
      av_freep( &oc->streams[i]->codec );
      av_freep( &oc->streams[i] );
    }
  url_fclose()关闭输出文件。
  av_free()释放AVFormatContext。


2. 流程图


原文《FFMpeg的output_example.c例子分析 》
阅读(3037) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~