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

  • 相关阅读:
    Delphi公用函数单元
    Delphi XE5 for Android (十一)
    Delphi XE5 for Android (十)
    Delphi XE5 for Android (九)
    Delphi XE5 for Android (八)
    Delphi XE5 for Android (七)
    Delphi XE5 for Android (五)
    Delphi XE5 for Android (四)
    Delphi XE5 for Android (三)
    Delphi XE5 for Android (二)
  • 原文地址:https://www.cnblogs.com/stnlcd/p/7149919.html
Copyright © 2011-2022 走看看