1. 程式人生 > >[ffmpeg_3.3.2]demuxing_decoding.c

[ffmpeg_3.3.2]demuxing_decoding.c

pin change 解碼器 ted reg system pro fopen param

分析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\n" 7 "API example program to show how to read frames from an input file.\n" 8 "This program reads frames from a file, decodes them, and writes decoded\n" 9 "video frames to a rawvideo file named video_output_file, and decoded\n
" 10 "audio frames to a rawaudio file named audio_output_file.\n\n" 11 "If the -refcount option is specified, the program use the\n" 12 "reference counting frame system which allows keeping a copy of\n" 13 "the data for longer than one decode call.\n
" 14 "\n", 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\n", 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\n"); 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\n", 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\n"); 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\n", 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\n"); 78 ret = 1; 79 goto end; 80 } 81 82 frame = av_frame_alloc(); 83 if (!frame) { 84 fprintf(stderr, "Could not allocate frame\n"); 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‘\n", src_filename, video_dst_filename); 96 if (audio_stream) 97 printf("Demuxing audio from file ‘%s‘ into ‘%s‘\n", 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.\n"); 120 121 if (video_stream) { 122 printf("Play the output video file with the command:\n" 123 "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", 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.\n", 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:\n" 146 "ffplay -f %s -ac %d -ar %d %s\n", 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‘\n",
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\n",
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\n",
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\n",
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\n",
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‘\n", src_filename, video_dst_filename);
 7     if (audio_stream)
 8         printf("Demuxing audio from file ‘%s‘ into ‘%s‘\n", 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)\n", 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:\n"
25                         "old: width = %d, height = %d, format = %s\n"
26                         "new: width = %d, height = %d, format = %s\n",
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\n",
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)\n", 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\n",
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

[ffmpeg_3.3.2]demuxing_decoding.c