zoukankan      html  css  js  c++  java
  • ffmpeg实战系列——003

    Talk is cheap,Show me the code!

    示例2、demuxing_decoding.c

    以下例子并不完整,只列出核心数据结构和代码

    static AVFormatContext *fmt_ctx = NULL;

    static AVCodecContext *video_dec_ctx = NULL, *audio_dec_ctx;

    static AVStream *video_stream = NULL, *audio_stream = NULL;

    static int video_stream_idx = -1, audio_stream_idx = -1;

    static AVFrame *frame = NULL;

    static AVPacket pkt;

    int main (int argc, char **argv)

    {

        av_register_all();

        /* open input file, and allocate format context */

        if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {

        }

        /* retrieve stream information */

        if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {

        }

        if (open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {

            video_stream = fmt_ctx->streams[video_stream_idx];

            }

        }

        if (open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx, AVMEDIA_TYPE_AUDIO) >= 0) {

            audio_stream = fmt_ctx->streams[audio_stream_idx];

        }

        frame = av_frame_alloc();

        av_init_packet(&pkt);

        while (av_read_frame(fmt_ctx, &pkt) >= 0) {

            AVPacket orig_pkt = pkt;

            do {

                ret = decode_packet(&got_frame, 0);

            } while (pkt.size > 0);

            av_packet_unref(&orig_pkt);

        }

    end:

        avcodec_free_context(&video_dec_ctx);

        avcodec_free_context(&audio_dec_ctx);

        avformat_close_input(&fmt_ctx);

        if (video_dst_file)

            fclose(video_dst_file);

        if (audio_dst_file)

            fclose(audio_dst_file);

        av_frame_free(&frame);

        av_free(video_dst_data[0]);

        return ret < 0;

    }

    static int open_codec_context(int *stream_idx,

                                  AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)

    {

        int ret, stream_index;

        AVStream *st;

        AVCodec *dec = NULL;

        AVDictionary *opts = NULL;

        ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0); //返回值是一个index,通过index可以拿到对应的avstream

        if (ret < 0) {

        } else {

            stream_index = ret;

            st = fmt_ctx->streams[stream_index];

            /* find decoder for the stream */

            dec = avcodec_find_decoder(st->codecpar->codec_id);

            /* Allocate a codec context for the decoder */

            *dec_ctx = avcodec_alloc_context3(dec);

            /* Copy codec parameters from input stream to output codec context */

            if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {

                fprintf(stderr, "Failed to copy %s codec parameters to decoder context ",

                        av_get_media_type_string(type));

                return ret;

            }

            /* Init the decoders, with or without reference counting */

            av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);

            if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {

                fprintf(stderr, "Failed to open %s codec ",

                        av_get_media_type_string(type));

                return ret;

            }

            *stream_idx = stream_index;

        }

        return 0;

    }

    下面详细分析下这个过程:

    1、avformat_open_input(&fmt_ctx, src_filename, NULL)

    avformat_open_input(&fmt_ctx, src_filename, NULL, NULL)

    int avformat_open_input(AVFormatContext **ps, const char *filename,

                            AVInputFormat *fmt, AVDictionary **options)

    {

        AVFormatContext *s = *ps;

        int i, ret = 0;

        AVDictionary *tmp = NULL;

        ID3v2ExtraMeta *id3v2_extra_meta = NULL;

        if (!s && !(s = avformat_alloc_context()))

            return AVERROR(ENOMEM);

        if ((ret = init_input(s, filename, &tmp)) < 0)

            goto fail;

        s->probe_score = ret;

        avio_skip(s->pb, s->skip_initial_bytes);

        /* Allocate private data. */

        if (s->iformat->priv_data_size > 0) {

            if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {

                ret = AVERROR(ENOMEM);

                goto fail;

            }

            if (s->iformat->priv_class) {

                *(const AVClass **) s->priv_data = s->iformat->priv_class;

                av_opt_set_defaults(s->priv_data);

                if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)

                    goto fail;

            }

        }

        if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)

            if ((ret = s->iformat->read_header(s)) < 0)

                goto fail;

        s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

        update_stream_avctx(s);

        for (i = 0; i < s->nb_streams; i++)

            s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;

    }

    在分析之前先了解几个重要的数据结构:

    ——AVFormatContext

    typedef struct AVFormatContext {

        const AVClass *av_class;

        struct AVInputFormat *iformat;

        struct AVOutputFormat *oformat;

        /**

         * - muxing: set by avformat_write_header()

         * - demuxing: set by avformat_open_input()

         */

        void *priv_data;             //最重要的成员

        /**

         * - demuxing: either set by the user before avformat_open_input() (then

         *             the user must close it manually) or set by avformat_open_input().

         * - muxing: set by the user before avformat_write_header(). T

         */

        AVIOContext *pb;            //最重要的成员

        unsigned int nb_streams;

        /**

         * A list of all streams in the file. New streams are created with

         * avformat_new_stream().

         *

         * - demuxing: streams are created by libavformat in avformat_open_input().

         *             If AVFMTCTX_NOHEADER is set in ctx_flags, then new streams may also

         *             appear in av_read_frame().

         * - muxing: streams are created by the user before avformat_write_header().

         *

         * Freed by libavformat in avformat_free_context().

         */

        AVStream **streams;

        /**

         * input or output filename

         *

         * - demuxing: set by avformat_open_input()

         * - muxing: may be set by the caller before avformat_write_header()

         */

        char filename[1024];

        int64_t duration;

        int64_t bit_rate;

        unsigned int packet_size;

        int max_delay;

        int64_t probesize;

        int64_t max_analyze_duration;

        const uint8_t *key;

        int keylen;

        unsigned int nb_programs;

        AVProgram **programs;

        enum AVCodecID video_codec_id;

        enum AVCodecID audio_codec_id;

        enum AVCodecID subtitle_codec_id;

        unsigned int max_index_size;

        unsigned int max_picture_buffer;

        unsigned int nb_chapters;

        AVChapter **chapters;

        AVDictionary *metadata;

        int64_t start_time_realtime;

        int fps_probe_size;

        int error_recognition;

        AVIOInterruptCB interrupt_callback;

        int64_t max_interleave_delta;

        int strict_std_compliance;

        int event_flags;

        int max_ts_probe;

        int avoid_negative_ts;

        int audio_preload;

        int max_chunk_duration;

        int max_chunk_size;

        int use_wallclock_as_timestamps;

        int avio_flags;

        enum AVDurationEstimationMethod duration_estimation_method;

        int64_t skip_initial_bytes;

        unsigned int correct_ts_overflow;

        int seek2any;

        int flush_packets;

        int probe_score;

        int format_probesize;

        char *codec_whitelist;

        char *format_whitelist;

        AVFormatInternal *internal;

        int io_repositioned;

        AVCodec *video_codec;

        AVCodec *audio_codec;

        AVCodec *subtitle_codec;

        AVCodec *data_codec;

        int metadata_header_padding;

        void *opaque;

        av_format_control_message control_message_cb;

        int64_t output_ts_offset;

        uint8_t *dump_separator;

        /**

         * Forced Data codec_id.

         * Demuxing: Set by user.

         */

        enum AVCodecID data_codec_id;

        char *protocol_whitelist;

        int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,

                       int flags, AVDictionary **options);

        void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);

        char *protocol_blacklist;

        int max_streams;

    } AVFormatContext;

    我们主要关注下面几个成员:

    struct AVInputFormat *iformat;

    void *priv_data;

    AVIOContext *pb;

    AVStream **streams;

    ——if ((ret = init_input(s, filename, &tmp)) < 0)

    if ((ret = init_input(s, filename, &tmp)) < 0)

    /* Open input file and probe the format if necessary. */

    static int init_input(AVFormatContext *s, const char *filename,

                          AVDictionary **options)

    {

        int ret;

        AVProbeData pd = { filename, NULL, 0 };

        int score = AVPROBE_SCORE_RETRY;

        if (s->pb) {

            s->flags |= AVFMT_FLAG_CUSTOM_IO;

            if (!s->iformat)

                return av_probe_input_buffer2(s->pb, &s->iformat, filename,

                                             s, 0, s->format_probesize);

            else if (s->iformat->flags & AVFMT_NOFILE)

                av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "

                                          "will be ignored with AVFMT_NOFILE format. ");

            return 0;

        }

        if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||

            (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))

            return score;

        if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)

            return ret;

        if (s->iformat)

            return 0;

        return av_probe_input_buffer2(s->pb, &s->iformat, filename,

                                     s, 0, s->format_probesize);

    }

    AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)

    {

        int score_ret;

        AVInputFormat *fmt = av_probe_input_format3(pd, is_opened, &score_ret);

        if (score_ret > *score_max) {

            *score_max = score_ret;

            return fmt;

        } else

            return NULL;

    }

    AVInputFormat *av_probe_input_format3(AVProbeData *pd, int is_opened,

                                          int *score_ret)

    {

        AVProbeData lpd = *pd;

        AVInputFormat *fmt1 = NULL, *fmt;

        int score, score_max = 0;

        const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];

        enum nodat {

            NO_ID3,

            ID3_ALMOST_GREATER_PROBE,

            ID3_GREATER_PROBE,

            ID3_GREATER_MAX_PROBE,

        } nodat = NO_ID3;

        if (!lpd.buf)

            lpd.buf = (unsigned char *) zerobuffer;

        if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {

            int id3len = ff_id3v2_tag_len(lpd.buf);

            if (lpd.buf_size > id3len + 16) {

                if (lpd.buf_size < 2LL*id3len + 16)

                    nodat = ID3_ALMOST_GREATER_PROBE;

                lpd.buf      += id3len;

                lpd.buf_size -= id3len;

            } else if (id3len >= PROBE_BUF_MAX) {

                nodat = ID3_GREATER_MAX_PROBE;

            } else

                nodat = ID3_GREATER_PROBE;

        }

        fmt = NULL;

        while ((fmt1 = av_iformat_next(fmt1))) {

            if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))

                continue;

            score = 0;

            if (fmt1->read_probe) {

                score = fmt1->read_probe(&lpd);

                if (score)

                    av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d ", fmt1->name, score, lpd.buf_size);

                if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {

                    switch (nodat) {

                    case NO_ID3:

                        score = FFMAX(score, 1);

                        break;

                    case ID3_GREATER_PROBE:

                    case ID3_ALMOST_GREATER_PROBE:

                        score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);

                        break;

                    case ID3_GREATER_MAX_PROBE:

                        score = FFMAX(score, AVPROBE_SCORE_EXTENSION);

                        break;

                    }

                }

            } else if (fmt1->extensions) {

                if (av_match_ext(lpd.filename, fmt1->extensions))

                    score = AVPROBE_SCORE_EXTENSION;

            }

            if (av_match_name(lpd.mime_type, fmt1->mime_type)) {

                if (AVPROBE_SCORE_MIME > score) {

                    av_log(NULL, AV_LOG_DEBUG, "Probing %s score:%d increased to %d due to MIME type ", fmt1->name, score, AVPROBE_SCORE_MIME);

                    score = AVPROBE_SCORE_MIME;

                }

            }

            if (score > score_max) {

                score_max = score;

                fmt       = fmt1;

            } else if (score == score_max)

                fmt = NULL;

        }

        if (nodat == ID3_GREATER_PROBE)

            score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);

        *score_ret = score_max;

        return fmt;

    }

    ——AVInputFormat

    typedef struct AVInputFormat {

        const char *name;

        const char *long_name;

        int flags;

        const char *extensions;

        const struct AVCodecTag * const *codec_tag;

        const AVClass *priv_class; ///< AVClass for the private context

        const char *mime_type;

        struct AVInputFormat *next;

        int raw_codec_id;

        int priv_data_size;

        int (*read_probe)(AVProbeData *);

        int (*read_header)(struct AVFormatContext *);

        int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);

        int (*read_close)(struct AVFormatContext *);

        int (*read_seek)(struct AVFormatContext *,

                         int stream_index, int64_t timestamp, int flags);

        int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index,

                                  int64_t *pos, int64_t pos_limit);

        int (*read_play)(struct AVFormatContext *);

        int (*read_pause)(struct AVFormatContext *);

        int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags);

        int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);

        int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);

        int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);

    } AVInputFormat;

    下面来看一种具体的AVInputFormat:

    AVInputFormat ff_mov_demuxer = {

        .name           = "mov,mp4,m4a,3gp,3g2,mj2",

        .long_name      = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),

        .priv_class     = &mov_class,

        .priv_data_size = sizeof(MOVContext),

        .extensions     = "mov,mp4,m4a,3gp,3g2,mj2",

        .read_probe     = mov_probe,

        .read_header    = mov_read_header,

        .read_packet    = mov_read_packet,

        .read_close     = mov_read_close,

        .read_seek      = mov_read_seek,

        .flags          = AVFMT_NO_BYTE_SEEK,

    };

    重点关注:MOVContext这个结构体,在mov_read_header时被初始化。只要你要用ff_mov_demuxer干活,必须要有MOVContext实例,所有的mov上下文信息都来自于MOVContext

    而实际上:AVFormatContext的priv_data成员就是一个MOVContext

        /* Allocate private data. */

        if (s->iformat->priv_data_size > 0) {

            if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {

                ret = AVERROR(ENOMEM);

                goto fail;

            }

            if (s->iformat->priv_class) {

                *(const AVClass **) s->priv_data = s->iformat->priv_class;

                av_opt_set_defaults(s->priv_data);

                if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)

                    goto fail;

            }

        }

    ——如果没有识别到具体的AVInputFormat,那么就需要打开这个文件读取一些数据进行进一步分析:一般都是走这个flow:

    ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options))

    先来看看这个接口是在哪里赋值的:

    AVFormatContext *avformat_alloc_context(void)

    {

        AVFormatContext *ic;

        ic = av_malloc(sizeof(AVFormatContext));

        if (!ic) return ic;

        avformat_get_context_defaults(ic);

        ic->internal = av_mallocz(sizeof(*ic->internal));

        if (!ic->internal) {

            avformat_free_context(ic);

            return NULL;

        }

        ic->internal->offset = AV_NOPTS_VALUE;

        ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

        ic->internal->shortest_end = AV_NOPTS_VALUE;

        return ic;

    }

    static void avformat_get_context_defaults(AVFormatContext *s)

    {

        memset(s, 0, sizeof(AVFormatContext));

        s->av_class = &av_format_context_class;

        s->io_open  = io_open_default;

        s->io_close = io_close_default;

        av_opt_set_defaults(s);

    }

    static int io_open_default(AVFormatContext *s, AVIOContext **pb,

                               const char *url, int flags, AVDictionary **options)

    {

    #if FF_API_OLD_OPEN_CALLBACKS

    FF_DISABLE_DEPRECATION_WARNINGS

        if (s->open_cb)

            return s->open_cb(s, pb, url, flags, &s->interrupt_callback, options);

    FF_ENABLE_DEPRECATION_WARNINGS

    #endif

        return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);

    }

    int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,

                             const AVIOInterruptCB *int_cb, AVDictionary **options,

                             const char *whitelist, const char *blacklist

                            )

    {

        URLContext *h;

        int err;

        err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);

        if (err < 0)

            return err;

        err = ffio_fdopen(s, h);

        if (err < 0) {

            ffurl_close(h);

            return err;

        }

        return 0;

    }

    这个两个也非常重要。开始吧:

    第一个API:先分配并初始化一个URLContext,这个非常重要

        URLContext *h;

        int err;

    err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);

    int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,

                             const AVIOInterruptCB *int_cb, AVDictionary **options,

                             const char *whitelist, const char* blacklist,

                             URLContext *parent)

    {

        int ret = ffurl_alloc(puc, filename, flags, int_cb);

        ret = ffurl_connect(*puc, options);

    }

    //这里就是先通过文件名找到URLProtocol,然后再用URLProtocol去分配和初始化URLContext

    int ffurl_alloc(URLContext **puc, const char *filename, int flags,

                    const AVIOInterruptCB *int_cb)

    {

        const URLProtocol *p = NULL;

        p = url_find_protocol(filename);

        if (p)

           return url_alloc_for_protocol(puc, p, filename, flags, int_cb);

        return AVERROR_PROTOCOL_NOT_FOUND;

    }

    查找的过程没什么花样,全局链表搜索:

    static const struct URLProtocol *url_find_protocol(const char *filename)

    {

        const URLProtocol **protocols;

        char proto_str[128], proto_nested[128], *ptr;

        protocols = ffurl_get_protocols(NULL, NULL); //获取到所有的白名单里面的协议//URLProtocol

        if (!protocols)

            return NULL;

        for (i = 0; protocols[i]; i++) {

                const URLProtocol *up = protocols[i];

            if (!strcmp(proto_str, up->name)) {

                av_freep(&protocols);

                return up;

            }

            if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&

                !strcmp(proto_nested, up->name)) {

                av_freep(&protocols);

                return up;

            }

        }

        av_freep(&protocols);

        return NULL;

    }

    我们这里举一个实际的例子:

    const URLProtocol ff_file_protocol = {

        .name                = "file",

        .url_open            = file_open,

        .url_read            = file_read,

        .url_write           = file_write,

        .url_seek            = file_seek,

        .url_close           = file_close,

        .url_get_file_handle = file_get_handle,

        .url_check           = file_check,

        .url_delete          = file_delete,

        .url_move            = file_move,

        .priv_data_size      = sizeof(FileContext),  //密切关注这个结构体

        .priv_data_class     = &file_class,

        .url_open_dir        = file_open_dir,

        .url_read_dir        = file_read_dir,

        .url_close_dir       = file_close_dir,

        .default_whitelist   = "file,crypto"

    };

    如果你需要调用 ff_file_protocol干活,那么FileContext是必须要提供的,所有的信息都来源于FileContext

    下面通过找到的协议分配URLContext:主要是做些初始化,这里最重点强调:URLContext有一个成员是FileContext通过这个成员uc->priv_data,才得以调用ff_file_protocol

    url_alloc_for_protocol(puc, p, filename, flags, int_cb);

    static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up,

                                      const char *filename, int flags,

                                      const AVIOInterruptCB *int_cb)

    {

        URLContext *uc;

        int err;

    #if CONFIG_NETWORK

        if (up->flags & URL_PROTOCOL_FLAG_NETWORK && !ff_network_init())

            return AVERROR(EIO);

    #endif

        if ((flags & AVIO_FLAG_READ) && !up->url_read) {

            av_log(NULL, AV_LOG_ERROR,

                   "Impossible to open the '%s' protocol for reading ", up->name);

            return AVERROR(EIO);

        }

        if ((flags & AVIO_FLAG_WRITE) && !up->url_write) {

            av_log(NULL, AV_LOG_ERROR,

                   "Impossible to open the '%s' protocol for writing ", up->name);

            return AVERROR(EIO);

        }

        uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);

        if (!uc) {

            err = AVERROR(ENOMEM);

            goto fail;

        }

        uc->av_class = &ffurl_context_class;

        uc->filename = (char *)&uc[1];

        strcpy(uc->filename, filename);

        uc->prot            = up;        //这里将URLProtocol赋值给URLContext的prot           

        uc->flags           = flags;

        uc->is_streamed     = 0; /* default = not streamed */

        uc->max_packet_size = 0; /* default: stream file */

        if (up->priv_data_size) {

            uc->priv_data = av_mallocz(up->priv_data_size); //最重要的数据结构

            if (!uc->priv_data) {

                err = AVERROR(ENOMEM);

                goto fail;

            }

            if (up->priv_data_class) {

                int proto_len= strlen(up->name);

                char *start = strchr(uc->filename, ',');

                *(const AVClass **)uc->priv_data = up->priv_data_class;

                av_opt_set_defaults(uc->priv_data);

                if(!strncmp(up->name, uc->filename, proto_len) && uc->filename + proto_len == start){

                    int ret= 0;

                    char *p= start;

                    char sep= *++p;

                    char *key, *val;

                    p++;

                    if (strcmp(up->name, "subfile"))

                        ret = AVERROR(EINVAL);

                    while(ret >= 0 && (key= strchr(p, sep)) && p<key && (val = strchr(key+1, sep))){

                        *val= *key= 0;

                        if (strcmp(p, "start") && strcmp(p, "end")) {

                            ret = AVERROR_OPTION_NOT_FOUND;

                        } else

                            ret= av_opt_set(uc->priv_data, p, key+1, 0);

                        if (ret == AVERROR_OPTION_NOT_FOUND)

                            av_log(uc, AV_LOG_ERROR, "Key '%s' not found. ", p);

                        *val= *key= sep;

                        p= val+1;

                    }

                    if(ret<0 || p!=key){

                        av_log(uc, AV_LOG_ERROR, "Error parsing options string %s ", start);

                        av_freep(&uc->priv_data);

                        av_freep(&uc);

                        err = AVERROR(EINVAL);

                        goto fail;

                    }

                    memmove(start, key+1, strlen(key));

                }

            }

        }

        if (int_cb)

            uc->interrupt_callback = *int_cb;

        *puc = uc;

        return 0;

    fail:

        *puc = NULL;

        if (uc)

            av_freep(&uc->priv_data);

        av_freep(&uc);

    #if CONFIG_NETWORK

        if (up->flags & URL_PROTOCOL_FLAG_NETWORK)

            ff_network_close();

    #endif

        return err;

    }

    第二个API分析:err = ffio_fdopen(s, h);

    其中:两个参数类型分别是:

    AVIOContext **s

    URLContext *h;

    这里主要是将AVIOContext和URLContext进行绑定,同时初始化AVIOContext

    int ffio_fdopen(AVIOContext **s, URLContext *h)

    {

    AVIOInternal *internal = NULL;   //其实就是一个URLContext,只是内部使用

    //这里插一句:

    typedef struct AVIOInternal {

        URLContext *h;

    } AVIOInternal;

        uint8_t *buffer = NULL;

        int buffer_size, max_packet_size;

        max_packet_size = h->max_packet_size;

        if (max_packet_size) {

            buffer_size = max_packet_size; /* no need to bufferize more than one packet */

        } else {

            buffer_size = IO_BUFFER_SIZE;

        }

        buffer = av_malloc(buffer_size);

        if (!buffer)

            return AVERROR(ENOMEM);

        internal = av_mallocz(sizeof(*internal));

        if (!internal)

            goto fail;

        internal->h = h;     //将URLContext赋值给AVIOInternal

    //分配AVIOContext,传入三个函数指针:

        *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE,

                                internal, io_read_packet, io_write_packet, io_seek);

        if (!*s)

            goto fail;

        (*s)->protocol_whitelist = av_strdup(h->protocol_whitelist);

        if (!(*s)->protocol_whitelist && h->protocol_whitelist) {

            avio_closep(s);

            goto fail;

        }

        (*s)->protocol_blacklist = av_strdup(h->protocol_blacklist);

        if (!(*s)->protocol_blacklist && h->protocol_blacklist) {

            avio_closep(s);

            goto fail;

        }

        (*s)->direct = h->flags & AVIO_FLAG_DIRECT;

        (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL;

        (*s)->max_packet_size = max_packet_size;

        if(h->prot) {

            (*s)->read_pause = io_read_pause;

            (*s)->read_seek  = io_read_seek;

            if (h->prot->url_read_seek)

                (*s)->seekable |= AVIO_SEEKABLE_TIME;

        }

        (*s)->short_seek_get = io_short_seek;

        (*s)->av_class = &ff_avio_class;

        return 0;

    fail:

        av_freep(&internal);

        av_freep(&buffer);

        return AVERROR(ENOMEM);

    }

    这里先别急,来看看这几个函数指针到底是什么鬼:

    io_read_packet

    io_write_packet,

    io_seek,

    io_read_pause,

    io_read_seek,

    io_short_seek

    揭开庐山真面目:

    static int io_read_packet(void *opaque, uint8_t *buf, int buf_size)

    {

        AVIOInternal *internal = opaque;

        return ffurl_read(internal->h, buf, buf_size);

    }

    int ffurl_read(URLContext *h, unsigned char *buf, int size)

    {

        if (!(h->flags & AVIO_FLAG_READ))

            return AVERROR(EIO);

        return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read); //好像看出什么来了,莫不是URLProtocol的url_read()函数?

    }

    static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf,

                                             int size, int size_min,

                                             int (*transfer_func)(URLContext *h,

                                                                  uint8_t *buf,

                                                                  int size))

    {

        int ret, len;

        int fast_retries = 5;

        int64_t wait_since = 0;

        len = 0;

        while (len < size_min) {

            if (ff_check_interrupt(&h->interrupt_callback))

                return AVERROR_EXIT;

            ret = transfer_func(h, buf + len, size - len);  //卧槽,居然真的是URLProtocol的函数

            if (ret == AVERROR(EINTR))

                continue;

            if (h->flags & AVIO_FLAG_NONBLOCK)

                return ret;

        return len;

    }

    假如是文件类型:

    const URLProtocol ff_file_protocol = {

        .name                = "file",

        .url_open            = file_open,

        .url_read            = file_read,

        .url_write           = file_write,

        .url_seek            = file_seek,

        .url_close           = file_close,

        .url_get_file_handle = file_get_handle,

        .url_check           = file_check,

        .url_delete          = file_delete,

        .url_move            = file_move,

        .priv_data_size      = sizeof(FileContext),

        .priv_data_class     = &file_class,

        .url_open_dir        = file_open_dir,

        .url_read_dir        = file_read_dir,

        .url_close_dir       = file_close_dir,

        .default_whitelist   = "file,crypto"

    };

    那就是最终调到file_read()函数。

    下面继续分析:密切关注这三个函数指针的去向哈,还有URLContext的去向

    //分配AVIOContext,传入三个函数指针:

        *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE,

                                internal, io_read_packet, io_write_packet, io_seek);

    AVIOContext *avio_alloc_context(

                      unsigned char *buffer,

                      int buffer_size,

                      int write_flag,

                      void *opaque,

                      int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),

                      int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),

                      int64_t (*seek)(void *opaque, int64_t offset, int whence))

    {

        AVIOContext *s = av_mallocz(sizeof(AVIOContext));

        if (!s)

            return NULL;

        ffio_init_context(s, buffer, buffer_size, write_flag, opaque,

                      read_packet, write_packet, seek);

        return s;

    }

    int ffio_init_context(AVIOContext *s,

                      unsigned char *buffer,

                      int buffer_size,

                      int write_flag,

                      void *opaque,

                      int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),

                      int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),

                      int64_t (*seek)(void *opaque, int64_t offset, int whence))

    {

        s->buffer      = buffer;

        s->orig_buffer_size =

        s->buffer_size = buffer_size;

        s->buf_ptr     = buffer;

        s->opaque      = opaque;           //URLContext对象到了这里,叼

        s->direct      = 0;

        url_resetbuf(s, write_flag ? AVIO_FLAG_WRITE : AVIO_FLAG_READ);

        s->write_packet    = write_packet;     //三个函数指针到了这里,叼

        s->read_packet     = read_packet;

        s->seek            = seek;

        s->pos             = 0;

        s->must_flush      = 0;

        s->eof_reached     = 0;

        s->error           = 0;

        s->seekable        = seek ? AVIO_SEEKABLE_NORMAL : 0;

        s->max_packet_size = 0;

        s->update_checksum = NULL;

        s->short_seek_threshold = SHORT_SEEK_THRESHOLD;

        if (!read_packet && !write_flag) {

            s->pos     = buffer_size;

            s->buf_end = s->buffer + buffer_size;

        }

        s->read_pause = NULL;

        s->read_seek  = NULL;

        s->write_data_type       = NULL;

        s->ignore_boundary_point = 0;

        s->current_type          = AVIO_DATA_MARKER_UNKNOWN;

        s->last_time             = AV_NOPTS_VALUE;

        s->short_seek_get        = NULL;

        return 0;

    }

    这里总结一下:AVIOContext里面最重要的两个成员:URLContext:s->opaque

    以及一堆函数指针:

                      int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),

                      int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),

                      int64_t (*seek)(void *opaque, int64_t offset, int whence))

    调用这些函数都必须通过URLContext才行,因为URLContext里面有这些函数调用需要的上下文:URLContext-〉priv_data

    例如file格式:

    const URLProtocol ff_file_protocol = {

        .name                = "file",

        .url_open            = file_open,

        .url_read            = file_read,

        .url_write           = file_write,

        .url_seek            = file_seek,

        .url_close           = file_close,

        .url_get_file_handle = file_get_handle,

        .url_check           = file_check,

        .url_delete          = file_delete,

        .url_move            = file_move,

        .priv_data_size      = sizeof(FileContext),

        .priv_data_class     = &file_class,

        .url_open_dir        = file_open_dir,

        .url_read_dir        = file_read_dir,

        .url_close_dir       = file_close_dir,

        .default_whitelist   = "file,crypto"

    };

    他就需要上下文:

    typedef struct FileContext {

        const AVClass *class;

        int fd;

        int trunc;

        int blocksize;

        int follow;

    #if HAVE_DIRENT_H

        DIR *dir;

    #endif

    } FileContext;

    到此,AVIO全部分析完毕,简直透彻简单:

    AVIOContext  —— URLContext——URLProcotol——具体某种协议格式的函数指针

    至此整个AVIO_open函数分析完毕,核心如下:

    int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,

                             const AVIOInterruptCB *int_cb, AVDictionary **options,

                             const char *whitelist, const char *blacklist

                            )

    {

        URLContext *h;

        int err;

    //根据filename获取URLProtocol,然后根据URLProcol初始化URLContext

        err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);

        if (err < 0)

            return err;

    //根据URLContext初始化AVIOContext,之后AVIOContext的所有函数其实都是调URLProtocol的函数干活,实际上AVIOContext是对URLProcol函数的封装,做了错误处理和缓冲

        err = ffio_fdopen(s, h); 

        if (err < 0) {

            ffurl_close(h);

            return err;

        }

        return 0;

    }

    好像前面漏了一点东西,现在补上去:第一个API其实还有connect的动作

    int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,

                             const AVIOInterruptCB *int_cb, AVDictionary **options,

                             const char *whitelist, const char* blacklist,

                             URLContext *parent)

    {

    int ret = ffurl_alloc(puc, filename, flags, int_cb);

    ret = ffurl_connect(*puc, options);

    }

    实际上很简单就是调用具体URLProtocol的open函数:

    int ffurl_connect(URLContext *uc, AVDictionary **options)

    {

        err =

            uc->prot->url_open2 ? uc->prot->url_open2(uc,

                                                      uc->filename,

                                                      uc->flags,

                                                      options) :

            uc->prot->url_open(uc, uc->filename, uc->flags);

    }

    到这里为止,其实init_input并没有走完,我们只是初始化了AVIOContext,但是并没有获得AVInputFormat,这个是具体的某种文件格式的Parser:

    /* Open input file and probe the format if necessary. */

    static int init_input(AVFormatContext *s, const char *filename,

                          AVDictionary **options)

    {

        int ret;

        AVProbeData pd = { filename, NULL, 0 };

        int score = AVPROBE_SCORE_RETRY;

        if (s->pb) {

            s->flags |= AVFMT_FLAG_CUSTOM_IO;

            if (!s->iformat)

                return av_probe_input_buffer2(s->pb, &s->iformat, filename,

                                             s, 0, s->format_probesize);

            else if (s->iformat->flags & AVFMT_NOFILE)

                av_log(s, AV_LOG_WARNING, "Custom AVIOContext makes no sense and "

                                          "will be ignored with AVFMT_NOFILE format. ");

            return 0;

        }

        if ((s->iformat && s->iformat->flags & AVFMT_NOFILE) ||

            (!s->iformat && (s->iformat = av_probe_input_format2(&pd, 0, &score))))

            return score;

    //初始化AVIOContext

        if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)

            return ret;

        if (s->iformat)

            return 0;

    //初始化AVInputFormat

        return av_probe_input_buffer2(s->pb, &s->iformat, filename,

                                     s, 0, s->format_probesize);

    }

    再来看看AVInputFormat的初始化过程:

    其实也是简单,通过probedata去获取得分最高的AVInputFormat:

    int av_probe_input_buffer2(AVIOContext *pb, AVInputFormat **fmt,

                              const char *filename, void *logctx,

                              unsigned int offset, unsigned int max_probe_size)

    {

        for (probe_size = PROBE_BUF_MIN; probe_size <= max_probe_size && !*fmt;

             probe_size = FFMIN(probe_size << 1,

                                FFMAX(max_probe_size, probe_size + 1))) {

            score = probe_size < max_probe_size ? AVPROBE_SCORE_RETRY : 0;

            /* Read probe data. */

            if ((ret = av_reallocp(&buf, probe_size + AVPROBE_PADDING_SIZE)) < 0)

            if ((ret = avio_read(pb, buf + buf_offset,

                                 probe_size - buf_offset)) < 0) {

                score = 0;

                ret   = 0;          /* error was end of file, nothing read */

            }

            buf_offset += ret;

            if (buf_offset < offset)

                continue;

            pd.buf_size = buf_offset - offset;

            pd.buf = &buf[offset];

            memset(pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE);

            /* Guess file format. */

            *fmt = av_probe_input_format2(&pd, 1, &score);

            if (*fmt) {

        return ret < 0 ? ret : score;

    }

    假如我们probe到的格式是ts:

    AVInputFormat ff_mpegts_demuxer = {

        .name           = "mpegts",

        .long_name      = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),

        .priv_data_size = sizeof(MpegTSContext),

        .read_probe     = mpegts_probe,

        .read_header    = mpegts_read_header,

        .read_packet    = mpegts_read_packet,

        .read_close     = mpegts_read_close,

        .read_timestamp = mpegts_get_dts,

        .flags          = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,

        .priv_class     = &mpegts_class,

    };

    至此init_input分析完毕:

    AVFormatContext两个成员都成功初始化:s->pb, &s->iformat,

    一个是AVIOContext类型,一个是AVInputFormat类型

    继续分析:avformat_open_input()函数

    ——ret = s->iformat->read_header(s):这个read_header()函数非常重要,不但初始化了该格式的上下文结构体如:MpegTSContext,同时还创建了该文件中包含的视频和音频流实体:

    AVStream

    换ts来分析:

    AVInputFormat ff_mpegtsraw_demuxer = {

        .name           = "mpegtsraw",

        .long_name      = NULL_IF_CONFIG_SMALL("raw MPEG-TS (MPEG-2 Transport Stream)"),

        .priv_data_size = sizeof(MpegTSContext),

        .read_header    = mpegts_read_header,

        .read_packet    = mpegts_raw_read_packet,

        .read_close     = mpegts_read_close,

        .read_timestamp = mpegts_get_dts,

        .flags          = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT,

        .priv_class     = &mpegtsraw_class,

    };

    //这个API超级重要,主要功能是初始化MpegTSContext数据结构,方便后面调用AVInputFormat ff_mpegts_demuxer的函数获取上下文信息

    static int mpegts_read_header(AVFormatContext *s)

    {

        MpegTSContext *ts = s->priv_data;    //AVFormatContext持有MpegTSContext指针,初始化的就是AVFormatContext里面的priv_data成员

        AVIOContext *pb   = s->pb;

        uint8_t buf[8 * 1024] = {0};

        int len;

        int64_t pos, probesize = s->probesize;

        if (ffio_ensure_seekback(pb, probesize) < 0)

            av_log(s, AV_LOG_WARNING, "Failed to allocate buffers for seekback ");

        /* read the first 8192 bytes to get packet size */

        pos = avio_tell(pb);

        len = avio_read(pb, buf, sizeof(buf));

        ts->raw_packet_size = get_packet_size(buf, len);

        if (ts->raw_packet_size <= 0) {

            av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS ");

            ts->raw_packet_size = TS_PACKET_SIZE;

        }

        ts->stream     = s;

        ts->auto_guess = 0;

        if (s->iformat == &ff_mpegts_demuxer) {

            /* normal demux */

            /* first do a scan to get all the services */

            seek_back(s, pb, pos);

    //这里会根据PMT创建具体的video、audio AVStream

            mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);

     

            mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);

            handle_packets(ts, probesize / ts->raw_packet_size);

            /* if could not find service, enable auto_guess */

            ts->auto_guess = 1;

            av_log(ts->stream, AV_LOG_TRACE, "tuning done ");

            s->ctx_flags |= AVFMTCTX_NOHEADER;

        }

        seek_back(s, pb, pos);

        return 0;

    }

    重点讲讲这里:因为这里非常重要。如果媒体格式有header,那么在read_header()创建AVStream,如果没有header,那么在read_packet()创建AVStream

    创建AVStream的函数是:

    AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c)

    {

    }

    看一下这个函数的说明:

    /**

     * Add a new stream to a media file.

     *

     * When demuxing, it is called by the demuxer in read_header(). If the

     * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also

     * be called in read_packet().

     *

     * When muxing, should be called by the user before avformat_write_header().

     *

     * User is required to call avcodec_close() and avformat_free_context() to

     * clean up the allocation by avformat_new_stream().

    AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c);

    ###############################################################################

    另外强调一点:AVInputFormat内部持有AVFormatContext指针,所以所有关于读写packet的函数,最终都是调用AVFormatContext的AVIOContext的读写函数,都是调用URLContext的URLProtocol的读写函数。

    另外:Aviobuf.c是对Avio.c的读写函数封装,添加了错误检测机制和缓冲机制。

    继续分析:avformat_open_input()剩余的部分

    接下来我们关注一下这个变量:struct AVFormatInternal

    在分配AVFormatContext的时候:

    AVFormatContext *avformat_alloc_context(void)

    {

        AVFormatContext *ic;

        ic = av_malloc(sizeof(AVFormatContext));

        if (!ic) return ic;

        avformat_get_context_defaults(ic);

        ic->internal = av_mallocz(sizeof(*ic->internal));

        if (!ic->internal) {

            avformat_free_context(ic);

            return NULL;

        }

        ic->internal->offset = AV_NOPTS_VALUE;

        ic->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

        ic->internal->shortest_end = AV_NOPTS_VALUE;

        return ic;

    }

    这个结构体在avformat_open_input内部还会做一些初始化:

        if (!s->metadata) {

            s->metadata = s->internal->id3v2_meta;

            s->internal->id3v2_meta = NULL;

        } else if (s->internal->id3v2_meta) {

            int level = AV_LOG_WARNING;

            if (s->error_recognition & AV_EF_COMPLIANT)

                level = AV_LOG_ERROR;

            av_log(s, level, "Discarding ID3 tags because more suitable tags were found. ");

            av_dict_free(&s->internal->id3v2_meta);

            if (s->error_recognition & AV_EF_EXPLODE)

                return AVERROR_INVALIDDATA;

        }

        ff_id3v2_free_extra_meta(&id3v2_extra_meta);

        if ((ret = avformat_queue_attached_pictures(s)) < 0)

            goto fail;

        if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)

            s->internal->data_offset = avio_tell(s->pb);

        s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;

    感觉没什么内容,先略过。

    继续分析:avformat_open_input()函数的最后一部分

        update_stream_avctx(s);

        for (i = 0; i < s->nb_streams; i++)

            s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;

        if (options) {

            av_dict_free(options);

            *options = tmp;

        }

        *ps = s;

    return 0;

    //个人认为这个函数在第一次avformat_open_input()的时候是不会执行的,因为s->nb_streams是0,以后打开时,s->nb_streams不为0,则会更新avstream的信息

    static int update_stream_avctx(AVFormatContext *s)

    {

        int i, ret;

        for (i = 0; i < s->nb_streams; i++) {

            AVStream *st = s->streams[i];

            if (!st->internal->need_context_update)

                continue;

            /* close parser, because it depends on the codec */

            if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {

                av_parser_close(st->parser);

                st->parser = NULL;

            }

            /* update internal codec context, for the parser */

            ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);

            if (ret < 0)

                return ret;

    #if FF_API_LAVF_AVCTX

    FF_DISABLE_DEPRECATION_WARNINGS

            /* update deprecated public codec context */

            ret = avcodec_parameters_to_context(st->codec, st->codecpar);

            if (ret < 0)

                return ret;

    FF_ENABLE_DEPRECATION_WARNINGS

    #endif

            st->internal->need_context_update = 0;

        }

        return 0;

    }

    至此:avformat_open_input()函数的全部过程分析完毕,收获很大阿。

    2、avformat_find_stream_info(fmt_ctx, NULL)

    这个函数貌似也挺厉害的样子,开搞:

    主要是对AVFormatContext的所有AVStream成员进行初始化,可以参考雷神的博客。我们这里只重点分析主要成员的初始化:

    int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)

    {

        AVStream *st;

        AVCodecContext *avctx;

      for (i = 0; i < ic->nb_streams; i++) {

            const AVCodec *codec;

            st = ic->streams[i];

            avctx = st->internal->avctx;

    //初始化AVStream的parser成员

            st->parser = av_parser_init(st->codecpar->codec_id);

            ret = avcodec_parameters_to_context(avctx, st->codecpar);

    //初始化AVCodec和AVCodecContext

            codec = find_probe_decoder(ic, st, st->codecpar->codec_id);

            // Try to just open decoders, in case this is enough to get parameters.

            if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) {

                if (codec && !avctx->codec)

                    if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0)

            }

       }

    //为了初始化AVCodecContext,有时候需要解码一些帧

                if (st->info->found_decoder == 1) {

                    do {

                        err = try_decode_frame(ic, st, &empty_pkt,

                                                (options && i < orig_nb_streams)

                                                ? &options[i] : NULL);

                    } while (err > 0 && !has_codec_parameters(st, NULL));

    // close codecs which were opened in try_decode_frame()

        for (i = 0; i < ic->nb_streams; i++) {

            st = ic->streams[i];

            avcodec_close(st->internal->avctx);

        }

    /* update the stream parameters from the internal codec contexts */

        for (i = 0; i < ic->nb_streams; i++) {

            st = ic->streams[i];

             ret = avcodec_parameters_to_context(st->codec, st->codecpar);

      }

    }

    至此整个函数分析完毕:主要工作就是初始化AVStream和AVCodecContext

    这里重点看一下AVStream的数据结构:

    typedef struct AVStream {

        int index;    /**< stream index in AVFormatContext */

        int id;

    #if FF_API_LAVF_AVCTX

        /**

         * @deprecated use the codecpar struct instead

         */

        attribute_deprecated

        AVCodecContext *codec;

    #endif

        void *priv_data;

        AVRational time_base;

        int64_t start_time;

        int64_t duration;

        int64_t nb_frames;                 ///< number of frames in this stream if known or 0

        AVRational sample_aspect_ratio;

        AVDictionary *metadata;

        AVRational avg_frame_rate;

        AVPacketSideData *side_data;

        int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */

        int64_t first_dts;

        int64_t cur_dts;

        int64_t last_IP_pts;

        int last_IP_duration;

        int codec_info_nb_frames;

        /* av_read_frame() support */

        enum AVStreamParseType need_parsing;

        struct AVCodecParserContext *parser;

        struct AVPacketList *last_in_packet_buffer;

        AVProbeData probe_data;

        int nb_index_entries;

        unsigned int index_entries_allocated_size;

        AVRational r_frame_rate;

        AVStreamInternal *internal;

        AVCodecParameters *codecpar;

    } AVStream;

    3、open_codec_context()

    这个函数是自己定义的,做得事情主要是初始化AVCodecContext和打开AVCodec

    static int open_codec_context(int *stream_idx,

                                  AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)

    {

        int ret, stream_index;

        AVStream *st;

        AVCodec *dec = NULL;

        AVDictionary *opts = NULL;

        ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);

        if (ret < 0) {

            fprintf(stderr, "Could not find %s stream in input file '%s' ",

                    av_get_media_type_string(type), src_filename);

            return ret;

        } else {

            stream_index = ret;

    //如果有多个streams,则通过av_find_best_stream找到最匹配stream的stream_index

            st = fmt_ctx->streams[stream_index]; 

            /* find decoder for the stream */

            dec = avcodec_find_decoder(st->codecpar->codec_id);

            if (!dec) {

                fprintf(stderr, "Failed to find %s codec ",

                        av_get_media_type_string(type));

                return AVERROR(EINVAL);

            }

            /* Allocate a codec context for the decoder */

            *dec_ctx = avcodec_alloc_context3(dec);

            if (!*dec_ctx) {

                fprintf(stderr, "Failed to allocate the %s codec context ",

                        av_get_media_type_string(type));

                return AVERROR(ENOMEM);

            }

            /* Copy codec parameters from input stream to output codec context */

            if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {

                fprintf(stderr, "Failed to copy %s codec parameters to decoder context ",

                        av_get_media_type_string(type));

                return ret;

            }

            /* Init the decoders, with or without reference counting */

            av_dict_set(&opts, "refcounted_frames", refcount ? "1" : "0", 0);

            if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {

                fprintf(stderr, "Failed to open %s codec ",

                        av_get_media_type_string(type));

                return ret;

            }

            *stream_idx = stream_index;

        }

        return 0;

    }

    4、decode_packet()

    这个函数也是个自己定义的函数,和之前分析的差不多:

    static int decode_packet(int *got_frame, int cached)

    {

        int ret = 0;

        int decoded = pkt.size;

        *got_frame = 0;

        if (pkt.stream_index == video_stream_idx) {

            /* decode video frame */

            ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);

            if (ret < 0) {

                fprintf(stderr, "Error decoding video frame (%s) ", av_err2str(ret));

                return ret;

            }

    }

    很简单,这里就不重复分析了。

    5、资源回收

    最后看一下资源释放:

        avcodec_free_context(&video_dec_ctx);  // AVCodecContext释放

        avcodec_free_context(&audio_dec_ctx);  // AVCodecContext释放

        avformat_close_input(&fmt_ctx);       // AVFormatContext释放

        av_frame_free(&frame);              // AVFrame释放

    至此整个demuxing再解码的详细过程分析完毕:demuxing_decoding.c

    附录:

    新版API只有以下几种格式能用:

        int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);

        int (*send_packet)(AVCodecContext *avctx, const AVPacket *avpkt);

        int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);

        int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);

    #define DEFINE_CRYSTALHD_DECODER(x, X)

        static const AVClass x##_crystalhd_class = {

            .class_name = #x "_crystalhd",

            .item_name = av_default_item_name,

            .option = options,

            .version = LIBAVUTIL_VERSION_INT,

        };

        AVCodec ff_##x##_crystalhd_decoder = {

            .name           = #x "_crystalhd",

            .long_name      = NULL_IF_CONFIG_SMALL("CrystalHD " #X " decoder"),

            .type           = AVMEDIA_TYPE_VIDEO,

            .id             = AV_CODEC_ID_##X,

            .priv_data_size = sizeof(CHDContext),

            .priv_class     = &x##_crystalhd_class,

            .init           = init,

            .close          = uninit,

            .send_packet    = crystalhd_decode_packet,

            .receive_frame  = crystalhd_receive_frame,

            .flush          = flush,

            .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING,

            .pix_fmts       = (const enum AVPixelFormat[]){AV_PIX_FMT_YUYV422, AV_PIX_FMT_NONE},

        };

    #if CONFIG_H264_CRYSTALHD_DECODER

    DEFINE_CRYSTALHD_DECODER(h264, H264)

    #endif

    #if CONFIG_MPEG2_CRYSTALHD_DECODER

    DEFINE_CRYSTALHD_DECODER(mpeg2, MPEG2VIDEO)

    #endif

    #if CONFIG_MPEG4_CRYSTALHD_DECODER

    DEFINE_CRYSTALHD_DECODER(mpeg4, MPEG4)

    #endif

    #if CONFIG_MSMPEG4_CRYSTALHD_DECODER

    DEFINE_CRYSTALHD_DECODER(msmpeg4, MSMPEG4V3)

    #endif

    #if CONFIG_VC1_CRYSTALHD_DECODER

    DEFINE_CRYSTALHD_DECODER(vc1, VC1)

    #endif

    #if CONFIG_WMV3_CRYSTALHD_DECODER

    DEFINE_CRYSTALHD_DECODER(wmv3, WMV3)

    #endif

  • 相关阅读:
    版本控制
    1121 Reverse the lights(“玲珑杯”线上赛 Round #15 河南专场)
    LightOJ 1055
    LightOJ 1053
    LightOJ 1052
    4512 吉哥系列故事——完美队形I(LCIS)
    ZOJ 2432-Greatest Common Increasing Subsequence
    病毒 (湖南省第八届大学生计算机程序设计竞赛)
    1328 台球碰撞 (湖南省第六届大学生计算机程序设计竞赛)
    zzuli 1332 内部收益率 (湖南省第六届大学生计算机程序设计竞赛)
  • 原文地址:https://www.cnblogs.com/stnlcd/p/7149919.html
Copyright © 2011-2022 走看看