FFmpeg SDK使用实例apiexample的分析

一、编译
1. 编译前的修改
1). apiexample.c  LINE 19
#include "avcodec.h"
改成:
#include "libavcodec/avcodec.h"

2). apiexample.c LINE 145
len = avcodec_decode_audio(c, (short *)outbuf, &out_size,
改成:
len = avcodec_decode_audio2(c, (short *)outbuf, &out_size,

2. 编译生成
# gcc apiexample.c -g -o apiexample -I/opt/.../include -L/opt/.../lib  
-lavformat -lavdevice -lavcodec  -lavutil -lavfilter -pthread -ldl -lswscale -lbz2 -lasound  -lz -lm

3. 运行
# apiexample xxx.mpg
输出文件存储在 /tmp/test%d.mpg

二、 主函数源码与分析
001 int main(int argc, char **argv){
002     const char *filename;

        /* must be called before using avcodec lib */
003     avcodec_init();

        /* register all the codecs (you can also register only the codec
           you wish to have smaller code */
004     avcodec_register_all();

005     if (argc <= 1) {
006       audio_encode_example("/tmp/test.mp2");
007       audio_decode_example("/tmp/test.sw", "/tmp/test.mp2");
008       video_encode_example("/tmp/test.mpg");
009       filename = "/tmp/test.mpg";
010     } else {
011       filename = argv[1];
012     }

        // audio_decode_example("/tmp/test.sw", filename);
013     video_decode_example("/tmp/test%d.pgm", filename);

014     return 0;
015 }


分析:
003: 初始化codec库
004: 注册编解码器
005: 命令行输入参数个数判断
006~009: 如果未输入参数,则先编码生成音频文件 /tmp/test.mp2, 再解码输出 /tmp/test.sw
                         并编码生成视频文件  /tmp/test.mpg
011: 如果有输入参数,则用这个参数作为要解码的文件名;
013: 视频解码,并生成输出文件 /tmp/test%d.pgm;
014: 主函数结束并返回;

三. video_decode_example()函数源码与分析
001 void video_decode_example(const char *outfilename, const char *filename){
002     AVCodec *codec;
003     AVCodecContext *c= NULL;
004     int frame, size, got_picture, len;
005     FILE *f;
006     AVFrame *picture;
007     uint8_t inbuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE], *inbuf_ptr;
008     char buf[1024];

        /* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
009     memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);

010     printf("Video decoding\n");

        /* find the mpeg1 video decoder */
011     codec = avcodec_find_decoder(CODEC_ID_MPEG1VIDEO);
012     if (!codec) {
            fprintf(stderr, "codec not found\n");
            exit(1);
        }

013     c= avcodec_alloc_context();
014     picture= avcodec_alloc_frame();

015     if(codec->capabilities&CODEC_CAP_TRUNCATED)
016         c->flags|= CODEC_FLAG_TRUNCATED; /* we dont send complete frames */

        /* for some codecs, such as msmpeg4 and mpeg4, width and height
           MUST be initialized there because these info are not available
           in the bitstream */

        /* open it */
017     if (avcodec_open(c, codec) < 0) {
            fprintf(stderr, "could not open codec\n");
            exit(1);
        }

       /* the codec gives us the frame size, in samples */
018     f = fopen(filename, "rb");
        if (!f) {
            fprintf(stderr, "could not open %s\n", filename);
            exit(1);
        }

019     frame = 0;
020     for(;;) {
021         size = fread(inbuf, 1, INBUF_SIZE, f);
022         if (size == 0)
                break;

            /* NOTE1: some codecs are stream based (mpegvideo, mpegaudio)
               and this is the only method to use them because you cannot
               know the compressed data size before analysing it.

               BUT some other codecs (msmpeg4, mpeg4) are inherently frame
               based, so you must call them with all the data for one
               frame exactly. You must also initialize 'width' and
               'height' before initializing them. */

            /* NOTE2: some codecs allow the raw parameters (frame size,
               sample rate) to be changed at any frame. We handle this, so
               you should also take care of it */

            /* here, we use a stream based decoder (mpeg1video), so we
               feed decoder and see if it could decode a frame */
023         inbuf_ptr = inbuf;
024         while (size > 0) {
025             len = avcodec_decode_video(c, picture, &got_picture,
                                       inbuf_ptr, size);
026             if (len < 0) {
                    fprintf(stderr, "Error while decoding frame %d\n", frame);
                    exit(1);
                }
027             if (got_picture) {
028                 printf("saving frame %3d\n", frame);
029                 fflush(stdout);

                    /* the picture is allocated by the decoder. no need to free it */
030                 snprintf(buf, sizeof(buf), outfilename, frame);
031                 pgm_save(picture->data[0], picture->linesize[0], c->width, c->height, buf);
032                 frame++;
033             }
034             size -= len;
035             inbuf_ptr += len;
036         }
037     }

        /* some codecs, such as MPEG, transmit the I and P frame with a
           latency of one frame. You must do the following to have a
           chance to get the last frame of the video */
038     len = avcodec_decode_video(c, picture, &got_picture, NULL, 0);
039     if (got_picture) {
040         printf("saving last frame %3d\n", frame);
041         fflush(stdout);
            /* the picture is allocated by the decoder. no need to free it */
042         snprintf(buf, sizeof(buf), outfilename, frame);
043         pgm_save(picture->data[0], picture->linesize[0],
                 c->width, c->height, buf);
044         frame++;
045     }

046     fclose(f);

047     avcodec_close(c);
048     av_free(c);
049     av_free(picture);
050     printf("\n");
051 }

分析:
002~008: 变量定义,最重要的是 AVCodec,AVCodecContext 这两个;
009: 将输入Buffer的尾部置零,以防止被破坏的mpeg流造成写越界;
011~012: 遍历编解码器链表,查找解码器,找到对应的功能函数;
013: 分配编解码器上下文的内存,清零后,部分参数赋初值;
014: 分配一个AVFrame , 并设置默认值;
015~016: 对CODEC处理流的能力进行标识;
#define CODEC_FLAG_TRUNCATED       0x00010000 /** Input bitstream might be truncated at a random
                                                  location instead of only at frame boundaries. */ 
017: 打开编解码器,分配具体编解码器使用的上下文,简单变量赋初值,
     并调用初始化函数初始化编解码器;
018: 打开输入文件;
019: 初始化帧序号,从零开始;
020: for循环,处理输入文件直到结尾;
021~023: 每次从输入文件读入 INBUF_SIZE 字节进行处理,遇到文件结束则结束处理;
     代码的注释部分是说:
       有的Codec是基于流的,如mpegvideo, mpegaudio, 
           因此,逐字节读入,分析后再处理是唯一的办法;
       有的Codec是基于帧的,如msmpeg4, mpeg4,
           所以需要逐帧准确读入才能正确处理,所以要初始化“宽和高”;
       还有的Codec允许帧大小,采样率可变,这种情况更要小心处理;
    
      本例是基于流的解码器--mpeg1video, 所以需要填充好解码器后再看是否能解码了;
024: 如果输入buffer中有数据,则一直处理这些数据;
025~026: 视频解码
027~033: 如果解码得到了一帧数据,
             则将这帧数据写到输出Buffer,将它的PGM数据按宽高比写到文件,帧序号加一;
034~035: 将输入buffer的指针向前推进;
038~045: 有的Codec, 如 MPEG, 会读进第二帧后再开始第一帧的处理,所以最后会留下一帧需要处理;
         这里就是处理最后一帧;
046~049: 处理完毕后的相应关闭和回收;

三、audio_decode_example()函数源码与分析
001 void audio_decode_example(const char *outfilename, const char *filename){
002     AVCodec *codec;
003     AVCodecContext *c= NULL;
004     int out_size, size, len;
005     FILE *f, *outfile;
006     uint8_t *outbuf;
007     uint8_t inbuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE], *inbuf_ptr;
           printf("Audio decoding\n");

        /* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */
         memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);

        /* find the mpeg audio decoder */
008     codec = avcodec_find_decoder(CODEC_ID_MP2);
009     if (!codec) {
            fprintf(stderr, "codec not found\n");
            exit(1);
        }

010     c= avcodec_alloc_context();

        /* open it */
011     if (avcodec_open(c, codec) < 0) {
            fprintf(stderr, "could not open codec\n");
            exit(1);
        }
012     outbuf = malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);

013     f = fopen(filename, "rb");
014     if (!f) {
            fprintf(stderr, "could not open %s\n", filename);
            exit(1);
        }
015     outfile = fopen(outfilename, "wb");
016     if (!outfile) {
            av_free(c);
            exit(1);
        }

        /* decode until eof */
017     inbuf_ptr = inbuf;
018     for(;;) {
019         size = fread(inbuf, 1, INBUF_SIZE, f);
020         if (size == 0)
021             break;

022         inbuf_ptr = inbuf;
023         while (size > 0) {
024             len = avcodec_decode_audio2(c, (short *)outbuf, &out_size, inbuf_ptr, size);
025             if (len < 0) {
                   fprintf(stderr, "Error while decoding\n");
                   exit(1);
                }
026             if (out_size > 0) {
                    /* if a frame has been decoded, output it */
027                 fwrite(outbuf, 1, out_size, outfile);
028             }
029             size -= len;
030             inbuf_ptr += len;
031         }
032     }

033     fclose(outfile);
034     fclose(f);
035     free(outbuf);
036     avcodec_close(c);
037     av_free(c);
038 }

分析:
和上面的视频解码示例类同,就不再做详解分析;

相关代码可以在这里下载:
http://download.csdn.net/detail/fireroll/7227225

你可能感兴趣的:(FFmpeg SDK使用实例apiexample的分析)