[ffmpeg_3.3.2]demuxing_decoding.c

分析ffmpeg3.3.2的example:

由于ffmpeg文档比较少,而且API变化表较大,所以个人首先从ffmpeg自带的demo开始分析,分析(demuxing_decoding.c)

1:首先入口函数main,注册所有解码器和打开输入流,最后解码每一个Packet,当解码完成后,需要刷新帧缓存,完成整个解码流程。

  1 int main (int argc, char **argv)
  2 {
  3     int ret = 0, got_frame;
  4 
  5     if (argc != 4 && argc != 5) {
  6         fprintf(stderr, "usage: %s [-refcount] input_file video_output_file audio_output_file
"
  7                 "API example program to show how to read frames from an input file.
"
  8                 "This program reads frames from a file, decodes them, and writes decoded
"
  9                 "video frames to a rawvideo file named video_output_file, and decoded
"
 10                 "audio frames to a rawaudio file named audio_output_file.

"
 11                 "If the -refcount option is specified, the program use the
"
 12                 "reference counting frame system which allows keeping a copy of
"
 13                 "the data for longer than one decode call.
"
 14                 "
", argv[0]);
 15         exit(1);
 16     }
 17     if (argc == 5 && !strcmp(argv[1], "-refcount")) {
 18         refcount = 1;
 19         argv++;
 20     }
 21     src_filename = argv[1];
 22     video_dst_filename = argv[2];
 23     audio_dst_filename = argv[3];
 24 
 25     /* register all formats and codecs */
 26     av_register_all();
 27 
 28     /* open input file, and allocate format context */
 29     if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {
 30         fprintf(stderr, "Could not open source file %s
", src_filename);
 31         exit(1);
 32     }
 33 
 34     /* retrieve stream information */
 35     if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
 36         fprintf(stderr, "Could not find stream information
");
 37         exit(1);
 38     }
 39 
 40     if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
 41         video_stream = fmt_ctx->streams[video_stream_idx];
 42 
 43         video_dst_file = fopen(video_dst_filename, "wb");
 44         if (!video_dst_file) {
 45             fprintf(stderr, "Could not open destination file %s
", video_dst_filename);
 46             ret = 1;
 47             goto end;
 48         }
 49 
 50         /* allocate image where the decoded image will be put */
 51         width = video_dec_ctx->width;
 52         height = video_dec_ctx->height;
 53         pix_fmt = video_dec_ctx->pix_fmt;
 54         ret = av_image_alloc(video_dst_data, video_dst_linesize,
 55                              width, height, pix_fmt, 1);
 56         if (ret < 0) {
 57             fprintf(stderr, "Could not allocate raw video buffer
");
 58             goto end;
 59         }
 60         video_dst_bufsize = ret;
 61     }
 62 
 63     if (open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) {
 64         audio_stream = fmt_ctx->streams[audio_stream_idx];
 65         audio_dst_file = fopen(audio_dst_filename, "wb");
 66         if (!audio_dst_file) {
 67             fprintf(stderr, "Could not open destination file %s
", audio_dst_filename);
 68             ret = 1;
 69             goto end;
 70         }
 71     }
 72 
 73     /* dump input information to stderr */
 74     av_dump_format(fmt_ctx, 0, src_filename, 0);
 75 
 76     if (!audio_stream && !video_stream) {
 77         fprintf(stderr, "Could not find audio or video stream in the input, aborting
");
 78         ret = 1;
 79         goto end;
 80     }
 81 
 82     frame = av_frame_alloc();
 83     if (!frame) {
 84         fprintf(stderr, "Could not allocate frame
");
 85         ret = AVERROR(ENOMEM);
 86         goto end;
 87     }
 88 
 89     /* initialize packet, set data to NULL, let the demuxer fill it */
 90     av_init_packet(&pkt);
 91     pkt.data = NULL;
 92     pkt.size = 0;
 93 
 94     if (video_stream)
 95         printf("Demuxing video from file '%s' into '%s'
", src_filename, video_dst_filename);
 96     if (audio_stream)
 97         printf("Demuxing audio from file '%s' into '%s'
", src_filename, audio_dst_filename);
 98 
 99     /* read frames from the file */
100     while (av_read_frame(fmt_ctx, &pkt) >= 0) {
101         AVPacket orig_pkt = pkt;
102         do {
103             ret = decode_packet(&got_frame, 0);
104             if (ret < 0)
105                 break;
106             pkt.data += ret;
107             pkt.size -= ret;
108         } while (pkt.size > 0);
109         av_packet_unref(&orig_pkt);
110     }
111 
112     /* flush cached frames */
113     pkt.data = NULL;
114     pkt.size = 0;
115     do {
116         decode_packet(&got_frame, 1);
117     } while (got_frame);
118 
119     printf("Demuxing succeeded.
");
120 
121     if (video_stream) {
122         printf("Play the output video file with the command:
"
123                "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s
",
124                av_get_pix_fmt_name(pix_fmt), width, height,
125                video_dst_filename);
126     }
127 
128     if (audio_stream) {
129         enum AVSampleFormat sfmt = audio_dec_ctx->sample_fmt;
130         int n_channels = audio_dec_ctx->channels;
131         const char *fmt;
132 
133         if (av_sample_fmt_is_planar(sfmt)) {
134             const char *packed = av_get_sample_fmt_name(sfmt);
135             printf("Warning: the sample format the decoder produced is planar "
136                    "(%s). This example will output the first channel only.
",
137                    packed ? packed : "?");
138             sfmt = av_get_packed_sample_fmt(sfmt);
139             n_channels = 1;
140         }
141 
142         if ((ret = get_format_from_sample_fmt(&fmt, sfmt)) < 0)
143             goto end;
144 
145         printf("Play the output audio file with the command:
"
146                "ffplay -f %s -ac %d -ar %d %s
",
147                fmt, n_channels, audio_dec_ctx->sample_rate,
148                audio_dst_filename);
149     }
150 
151 end:
152     avcodec_free_context(&video_dec_ctx);
153     avcodec_free_context(&audio_dec_ctx);
154     avformat_close_input(&fmt_ctx);
155     if (video_dst_file)
156         fclose(video_dst_file);
157     if (audio_dst_file)
158         fclose(audio_dst_file);
159     av_frame_free(&frame);
160     av_free(video_dst_data[0]);
161 
162     return ret < 0;
163 }
View Code

2:main函数中调用open_codec_context()函数中查找和打开音频、视屏轨道,编码器等等

 1 static int open_codec_context(int *stream_idx,
 2                               AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
 3 {
 4     int ret, stream_index;
 5     AVStream *st;
 6     AVCodec *dec = NULL;
 7     AVDictionary *opts = NULL;
 8 
 9     ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
10     if (ret < 0) {
11         fprintf(stderr, "Could not find %s stream in input file '%s'
",
12                 av_get_media_type_string(type), src_filename);
13         return ret;
14     } else {
15         stream_index = ret;
16         st = fmt_ctx->streams[stream_index];
17 
18         /* find decoder for the stream */
19         dec = avcodec_find_decoder(st->codecpar->codec_id);
20         if (!dec) {
21             fprintf(stderr, "Failed to find %s codec
",
22                     av_get_media_type_string(type));
23             return AVERROR(EINVAL);
24         }
25 
26         /* Allocate a codec context for the decoder */
27         *dec_ctx = avcodec_alloc_context3(dec);
28         if (!*dec_ctx) {
29             fprintf(stderr, "Failed to allocate the %s codec context
",
30                     av_get_media_type_string(type));
31             return AVERROR(ENOMEM);
32         }
33 
34         /* Copy codec parameters from input stream to output codec context */
35         if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
36             fprintf(stderr, "Failed to copy %s codec parameters to decoder context
",
37                     av_get_media_type_string(type));
38             return ret;
39         }
40 
41         /* Init the decoders, with or without reference counting */
42         av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);
43         if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
44             fprintf(stderr, "Failed to open %s codec
",
45                     av_get_media_type_string(type));
46             return ret;
47         }
48         *stream_idx = stream_index;
49     }
50 
51     return 0;
52 }
View Code

  这段代码和中的API有变化,需要注意。首先,使用av_find_best_stream(AVFormatContext* AVMEdiaType,-1,-1,NULL,0)找到Type对应的视屏或则音频流,然后获取对应的Stream轨道,然后根据Stream->AVCodecParam->AVCodeID来找到对应的解码器。由于在ffmpeg3.X的版本Stream->AVCodecContext已经被弃用了,需要使用Stream->AVCodecParam 进行转化,所以首先为解码器申请一个遍解码器上下文,因此使用avcodec_alloc_context3(AVCodec* )申请一个编解码上下文,然后使用avcodec_parameters_to_context(AVCodecContex*,AVCodecParam*)来获取一个编解码器上下文,最后设置打开解码器的参数,并且打开解码器avcodec_open2(AVCodecContext*,AVCodec*,AVDictionary*);返回到主函数main中。

  然后定义个packet,从输入中读取一个packet进行解码,直到将一个Packet解码完成后,才会读取下一个包,继续解码。

 1 av_init_packet(&pkt);
 2     pkt.data = NULL;
 3     pkt.size = 0;
 4 
 5     if (video_stream)
 6         printf("Demuxing video from file '%s' into '%s'
", src_filename, video_dst_filename);
 7     if (audio_stream)
 8         printf("Demuxing audio from file '%s' into '%s'
", src_filename, audio_dst_filename);
 9 
10     /* read frames from the file */
11     while (av_read_frame(fmt_ctx, &pkt) >= 0) {
12         AVPacket orig_pkt = pkt;
13         do {
14             ret = decode_packet(&got_frame, 0);
15             if (ret < 0)
16                 break;
17             pkt.data += ret;
18             pkt.size -= ret;
19         } while (pkt.size > 0);
20         av_packet_unref(&orig_pkt);
21     }
View Code

3:解码调用decode_packet(int *got_frame,int cached),返回解码的大小。以判断是否将当前包解码完成。

 1 static int decode_packet(int *got_frame, int cached)
 2 {
 3     int ret = 0;
 4     int decoded = pkt.size;
 5 
 6     *got_frame = 0;
 7 
 8     if (pkt.stream_index == video_stream_idx) {
 9         /* decode video frame */
10         ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
11         if (ret < 0) {
12             fprintf(stderr, "Error decoding video frame (%s)
", av_err2str(ret));
13             return ret;
14         }
15 
16         if (*got_frame) {
17 
18             if (frame->width != width || frame->height != height ||
19                 frame->format != pix_fmt) {
20                 /* To handle this change, one could call av_image_alloc again and
21                  * decode the following frames into another rawvideo file. */
22                 fprintf(stderr, "Error: Width, height and pixel format have to be "
23                         "constant in a rawvideo file, but the width, height or "
24                         "pixel format of the input video changed:
"
25                         "old: width = %d, height = %d, format = %s
"
26                         "new: width = %d, height = %d, format = %s
",
27                         width, height, av_get_pix_fmt_name(pix_fmt),
28                         frame->width, frame->height,
29                         av_get_pix_fmt_name(frame->format));
30                 return -1;
31             }
32 
33             printf("video_frame%s n:%d coded_n:%d
",
34                    cached ? "(cached)" : "",
35                    video_frame_count++, frame->coded_picture_number);
36 
37             /* copy decoded frame to destination buffer:
38              * this is required since rawvideo expects non aligned data */
39             av_image_copy(video_dst_data, video_dst_linesize,
40                           (const uint8_t **)(frame->data), frame->linesize,
41                           pix_fmt, width, height);
42 
43             /* write to rawvideo file */
44             fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);
45         }
46     } else if (pkt.stream_index == audio_stream_idx) {
47         /* decode audio frame */
48         ret = avcodec_decode_audio4(audio_dec_ctx, frame, got_frame, &pkt);
49         if (ret < 0) {
50             fprintf(stderr, "Error decoding audio frame (%s)
", av_err2str(ret));
51             return ret;
52         }
53         /* Some audio decoders decode only part of the packet, and have to be
54          * called again with the remainder of the packet data.
55          * Sample: fate-suite/lossless-audio/luckynight-partial.shn
56          * Also, some decoders might over-read the packet. */
57         decoded = FFMIN(ret, pkt.size);
58 
59         if (*got_frame) {
60             size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format);
61             printf("audio_frame%s n:%d nb_samples:%d pts:%s
",
62                    cached ? "(cached)" : "",
63                    audio_frame_count++, frame->nb_samples,
64                    av_ts2timestr(frame->pts, &audio_dec_ctx->time_base));
65 
66             /* Write the raw audio data samples of the first plane. This works
67              * fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,
68              * most audio decoders output planar audio, which uses a separate
69              * plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).
70              * In other words, this code will write only the first audio channel
71              * in these cases.
72              * You should use libswresample or libavfilter to convert the frame
73              * to packed data. */
74             fwrite(frame->extended_data[0], 1, unpadded_linesize, audio_dst_file);
75         }
76     }
77 
78     /* If we use frame reference counting, we own the data and need
79      * to de-reference it when we don't use it anymore */
80     if (*got_frame && refcount)
81         av_frame_unref(frame);
82 
83     return decoded;
84 }
View Code

  注意:在音频解码的时候,因为每一个音频Packet中可能含有多个Sample,所以需要多次解码,解码的大小可能会超过包的大小,需要使用

decoded = FFMIN(ret, pkt.size);来确认解码后的大小,解码出来的音频的大小为

size_t unpadded_linesize = frame->nb_samples * av_get_bytes_per_sample(frame->format);

4:当解码结束后,需要刷新仍在缓存在解码器上下文中的数据:

1 pkt.data = NULL;
2     pkt.size = 0;
3     do {
4         decode_packet(&got_frame, 1);
5     } while (got_frame);
View Code

5:在退出前应该释放所申请的空间

1 avcodec_free_context(&video_dec_ctx);
2     avcodec_free_context(&audio_dec_ctx);
3     avformat_close_input(&fmt_ctx);
4     if (video_dst_file)
5         fclose(video_dst_file);
6     if (audio_dst_file)
7         fclose(audio_dst_file);
8     av_frame_free(&frame);
9     av_free(video_dst_data[0]);
View Code
作者:长风 Email:844064492@qq.com QQ群:607717453 Git:https://github.com/zhaohu19910409Dz 开源项目:https://github.com/OriginMEK/MEK 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 感谢您的阅读。如果觉得有用的就请各位大神高抬贵手“推荐一下”吧!你的精神支持是博主强大的写作动力。 如果觉得我的博客有意思,欢迎点击首页左上角的“+加关注”按钮关注我!
原文地址:https://www.cnblogs.com/zhaohu/p/7225613.html