• [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 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 感谢您的阅读。如果觉得有用的就请各位大神高抬贵手“推荐一下”吧!你的精神支持是博主强大的写作动力。 如果觉得我的博客有意思,欢迎点击首页左上角的“+加关注”按钮关注我!
  • 相关阅读:
    MySQL Online DDL导致全局锁表案例分析
    .NET Core教程--给API加一个服务端缓存啦
    任务队列和异步接口的正确打开方式(.NET Core版本)
    .NET Core中使用RabbitMQ正确方式
    .NET Core单元测试之搞死开发的覆盖率统计(coverlet + ReportGenerator )
    没有执行过rm -rf /*的开发不是好运维
    dotnet core在Task中使用依赖注入的Service/EFContext
    可能是全网首个支持阿里云Elasticsearch Xapck鉴权的Skywalking
    dpdk中QSBR具体实现
    C语言二级指针底层实现
  • 原文地址:https://www.cnblogs.com/zhaohu/p/7225613.html
Copyright © 2020-2023  润新知