zoukankan      html  css  js  c++  java
  • [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 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 感谢您的阅读。如果觉得有用的就请各位大神高抬贵手“推荐一下”吧!你的精神支持是博主强大的写作动力。 如果觉得我的博客有意思,欢迎点击首页左上角的“+加关注”按钮关注我!
  • 相关阅读:
    Azure HPC Pack Cluster添加辅助节点
    Azure HPC Pack 辅助节点模板配置
    Azure HPC Pack配置管理系列(PART6)
    Windows HPC Pack 2012 R2配置
    Azure HPC Pack 节点提升成域控制器
    Azure HPC Pack VM 节点创建和配置
    Azure HPC Pack 部署必要条件准备
    Azure HPC Pack 基础拓扑概述
    Azure VM 性能计数器配置
    Maven私仓配置
  • 原文地址:https://www.cnblogs.com/zhaohu/p/7225613.html
Copyright © 2011-2022 走看看