当前位置:网站首页>Ijkplayer code walkthrough player network video data reading process details

Ijkplayer code walkthrough player network video data reading process details

2022-06-13 06:28:00 That's right

This part of the network reads video data code and opens another blog post for analysis .

Concept definition :
PTS:Presentation Time Stamp.PTS It is mainly used to measure when the decoded video frame is displayed
DTS:Decode Time Stamp.DTS It is mainly used to identify the data read into memory bit When does the stream begin to be sent to the decoder for decoding
That is to say pts Reflect when the frame starts to display ,dts Reflect when the data stream starts decoding .

This section is mainly for day reading ijkplayer Data input stream , We need to look at the relevant data structures first , as follows :

typedef struct AVIOContext {
    
    /** * A class for private options. * * If this AVIOContext is created by avio_open2(), av_class is set and * passes the options down to protocols. * * If this AVIOContext is manually allocated, then av_class may be set by * the caller. * * warning -- this field can be NULL, be sure to not pass this AVIOContext * to any av_opt_* functions in that case. */
    const AVClass *av_class;                    ///>  this  AVCLass  It can point to abstract protocol bodies ,

                                                /*  Such as  static const AVClass av_format_context_class = { .class_name = "AVFormatContext", .item_name = format_to_name, .option = avformat_options, .version = LIBAVUTIL_VERSION_INT, .child_next = format_child_next, .child_class_next = format_child_class_next, .category = AV_CLASS_CATEGORY_MUXER, .get_category = get_category, }; */

    /* * The following shows the relationship between buffer, buf_ptr, buf_end, buf_size, * and pos, when reading and when writing (since AVIOContext is used for both): * ********************************************************************************** * READING ********************************************************************************** * * | buffer_size | * |---------------------------------------| * | | * * buffer buf_ptr buf_end * +---------------+-----------------------+ * |/ / / / / / / /|/ / / / / / /| | * read buffer: |/ / consumed / | to be read /| | * |/ / / / / / / /|/ / / / / / /| | * +---------------+-----------------------+ * * pos * +-------------------------------------------+-----------------+ * input file: | | | * +-------------------------------------------+-----------------+ * * ********************************************************************************** * WRITING ********************************************************************************** * * | buffer_size | * |-------------------------------| * | | * * buffer buf_ptr buf_end * +-------------------+-----------+ * |/ / / / / / / / / /| | * write buffer: | / to be flushed / | | * |/ / / / / / / / / /| | * +-------------------+-----------+ * * pos * +--------------------------+-----------------------------------+ * output file: | | | * +--------------------------+-----------------------------------+ * */
    unsigned char *buffer;  /**< Start of the buffer. */
    int buffer_size;        /**< Maximum buffer size */
    unsigned char *buf_ptr; /**< Current position in the buffer */
    unsigned char *buf_end; /**< End of the data, may be less than buffer+buffer_size if the read function returned less data than requested, e.g. for streams where no more data has been received yet. */
    void *opaque;           /**< A private pointer, passed to the read/write/seek/... functions. */
    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);
    int64_t pos;            /**< position in the file of the current buffer */
    int must_flush;         /**< true if the next seek should flush */
    int eof_reached;        /**< true if eof reached */
    int write_flag;         /**< true if open for writing */
    int max_packet_size;
    unsigned long checksum;
    unsigned char *checksum_ptr;
    unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
    int error;              /**< contains the error code or 0 if no error happened */
    /** * Pause or resume playback for network streaming protocols - e.g. MMS. */
    int (*read_pause)(void *opaque, int pause);
    /** * Seek to a given timestamp in stream with the specified stream_index. * Needed for some network streaming protocols which don't support seeking * to byte position. */
    int64_t (*read_seek)(void *opaque, int stream_index,
                         int64_t timestamp, int flags);
    /** * A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable. */
    int seekable;

    /** * max filesize, used to limit allocations * This field is internal to libavformat and access from outside is not allowed. */
    int64_t maxsize;

    /** * avio_read and avio_write should if possible be satisfied directly * instead of going through a buffer, and avio_seek will always * call the underlying seek function directly. */
    int direct;

    /** * Bytes read statistic * This field is internal to libavformat and access from outside is not allowed. */
    int64_t bytes_read;

    /** * seek statistic * This field is internal to libavformat and access from outside is not allowed. */
    int seek_count;

    /** * writeout statistic * This field is internal to libavformat and access from outside is not allowed. */
    int writeout_count;

    /** * Original buffer size * used internally after probing and ensure seekback to reset the buffer size * This field is internal to libavformat and access from outside is not allowed. */
    int orig_buffer_size;

    /** * Threshold to favor readahead over seek. * This is current internal only, do not use from outside. */
    int short_seek_threshold;

    /** * ',' separated list of allowed protocols. */
    const char *protocol_whitelist;

    /** * ',' separated list of disallowed protocols. */
    const char *protocol_blacklist;

    /** * A callback that is used instead of write_packet. */
    int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,
                           enum AVIODataMarkerType type, int64_t time);
    /** * If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT, * but ignore them and treat them as AVIO_DATA_MARKER_UNKNOWN (to avoid needlessly * small chunks of data returned from the callback). */
    int ignore_boundary_point;

    /** * Internal, not meant to be used from outside of AVIOContext. */
    enum AVIODataMarkerType current_type;
    int64_t last_time;

    /** * A callback that is used instead of short_seek_threshold. * This is current internal only, do not use from outside. */
    int (*short_seek_get)(void *opaque);
} AVIOContext;

this AVIOContext The structure is AVFormatContext Variable name defined in pb , The following walk through code will use .

Let's review , In the read thread read_thread() , When entering LOOP Pre body call avformat_open_input() function ,
This function is based on url filename Find out whether the file is a local file or a network file , If it is a network file, it needs to be in the supported protocol
in , Find the corresponding protocol method .
Use this communication protocol method to obtain the first frame data of audio and video input , Find the corresponding audio and video decoder according to the data format , And initialization
Relevant parameters of decoder , Enable the communication transmission protocol and decoder to work .

///>  Read the format information of video data source , Source path extra\ffmpeg\libavformat\utils.c
///>  Call this... In the read thread  avformat_open_input(&ic, is->filename, is->iformat, &ffp->format_opts); function 
int avformat_open_input(AVFormatContext **ps, const char *filename,
                        AVInputFormat *fmt, AVDictionary **options)
{
    
    AVFormatContext *s = *ps;
    int i, ret = 0;
    AVDictionary *tmp = NULL;
    AVDictionary *tmp2 = NULL;
    ID3v2ExtraMeta *id3v2_extra_meta = NULL;

    if (!s && !(s = avformat_alloc_context()))
        return AVERROR(ENOMEM);
    if (!s->av_class) {
    
        av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
        return AVERROR(EINVAL);
    }
    if (fmt)
        s->iformat = fmt;

    if (options)
        av_dict_copy(&tmp, *options, 0);                                    ///>  hold  option  Format configuration  tmp  in 

    if (s->pb) // must be before any goto fail
        s->flags |= AVFMT_FLAG_CUSTOM_IO;

    if ((ret = av_opt_set_dict(s, &tmp)) < 0)                               ///>  hold  tmp  The value is set to  s  in 
        goto fail;

    av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));///> is->filename  yes url Address information 
    if ((ret = init_input(s, filename, &tmp)) < 0)                         ///> 1.  adopt  filename  initialization  AVFormatContext  Of  input  Related parameters 
        goto fail;
    s->probe_score = ret;

    if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
       ///> s->protocol_whitelist  White list 
        s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
        if (!s->protocol_whitelist) {
    
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    }

    if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
       ///> s->protocol_blacklist  The blacklist 
        s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
        if (!s->protocol_blacklist) {
    
            ret = AVERROR(ENOMEM);
            goto fail;
        }
    }

    if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
    
        av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
        ret = AVERROR(EINVAL);
        goto fail;
    }

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

    /* Check filename in case an image number is expected. */
    if (s->iformat->flags & AVFMT_NEEDNUMBER) {
    
        if (!av_filename_number_test(filename)) {
               ///> filename  String specification serialization 
            ret = AVERROR(EINVAL);
            goto fail;
        }
    }

    s->duration = s->start_time = AV_NOPTS_VALUE;

    /* 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;
        }
    }

    /* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
    if (s->pb)                                                    ///>  Decoder matches 
        ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);


    if (!(s->flags&AVFMT_FLAG_PRIV_OPT)) {
    
        if (s->iformat->read_header2) {
                             
            if (options)
                av_dict_copy(&tmp2, *options, 0);

            if ((ret = s->iformat->read_header2(s, &tmp2)) < 0)   ///> 2.  Call the input format  read_header2()  function , Match encoding 
                goto fail;
        } else if (s->iformat->read_header 
                      && (ret = s->iformat->read_header(s)) < 0)  ///>  Call the input format  read_header()  function , Match encoding 
            goto fail;
    }

    if (!s->metadata) {
                                               ///>  Metadata type related configurations 
        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.\n");
        av_dict_free(&s->internal->id3v2_meta);
        if (s->error_recognition & AV_EF_EXPLODE)
            return AVERROR_INVALIDDATA;
    }

    if (id3v2_extra_meta) {
    
        if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
            !strcmp(s->iformat->name, "tta")) {
    
            if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
                goto fail;
            if ((ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0)
                goto fail;
        } else
            av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
    }
    ff_id3v2_free_extra_meta(&id3v2_extra_meta);

    if ((ret = avformat_queue_attached_pictures(s)) < 0)                ///> 3.  towards  avformat queue  Add pictures , Where does the data come from , Data is queued before this function .
        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;

    update_stream_avctx(s);                                            ///>  Update stream  av context  Content 

    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;
        av_dict_free(&tmp2);
    }
    *ps = s;
    return 0;

fail:
    ff_id3v2_free_extra_meta(&id3v2_extra_meta);
    av_dict_free(&tmp);
    av_dict_free(&tmp2);
    if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
        avio_closep(&s->pb);
    avformat_free_context(s);
    *ps = NULL;
    return ret;
}
///>  Configure the video format of the data source , This section gets video data 、 And classify the video data .!!!

///> 1.  Initialize the input stream 
/* 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->pb  yes  AVIOContext  Structure pointer 
        s->flags |= AVFMT_FLAG_CUSTOM_IO;
        if (!s->iformat)
            return av_probe_input_buffer2(s->pb, &s->iformat, filename,
                                         s, 0, s->format_probesize);                            ///> 2.  to  iformat  Construct initial state parameters 
        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.\n");
        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)   ///> 1.2.  This function is  io_open()  Open a file or network socket
        return ret;

    if (s->iformat)
        return 0;
    return av_probe_input_buffer2(s->pb, &s->iformat, filename,                                 ///> 1.3  Read the first frame of data stream 
                                 s, 0, s->format_probesize);
}

///> 1.2  This function is  io_open()  Open a file or network socket
///>  function  io_open  Is a pointer ,  We need to find the function to which we are pointing ; The function pointer is defined as follows :
struct AVFormatContext {
    
    struct AVInputFormat *iformat;
    struct AVOutputFormat *oformat;

    AVIOContext *pb;
    AVStream **streams;

    char filename[1024];

    AVDictionary *metadata;                         //> Metadata that applies to the whole file.

    AVIOInterruptCB interrupt_callback;             //> Custom interrupt callbacks for the I/O layer.

    AVCodec *video_codec;
    AVCodec *audio_codec;
    AVCodec *subtitle_codec;

    char *protocol_whitelist;

    int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url,
                   int flags, AVDictionary **options);              //> A callback for opening new IO streams.

    void (*io_close)(struct AVFormatContext *s, AVIOContext *pb);   //> A callback for closing the streams opened with AVFormatContext.io_open().

};
///>  Intercept the key content of the structure , Here's what we're focusing on  io_open  The assignment points to the concrete implementation function .

//  stay  ff_play.c  Medium  read_thread()  Function , Initialization called the function 
static int read_thread(void *arg)
{
    
    FFPlayer *ffp = arg;                        ///> arg = ffp  Player instance 
    
    ......

    ic = avformat_alloc_context();                                                    ///> 2.6.1  establish  avformat context  Content , Read a file or socket Of io_open The pointer 
    if (!ic) {
                                                                            ///  Point to  io_open_default() function , Source path  libavformat\option.c
        av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");
        ret = AVERROR(ENOMEM);
        goto fail;
    }

    .......
}

///>  This function is related to each hardware architecture  libavformt\option.c In file ,
AVFormatContext *avformat_alloc_context(void)
{
    
    AVFormatContext *ic;
    ic = av_malloc(sizeof(AVFormatContext));
    if (!ic) return ic;
    avformat_get_context_defaults(ic);                                              //>  stay  avformat_get_context_defaults(ic)  Function , Perform specific assignment operation 

    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;
}

///>  Initialize the function pointed to by the pointer 
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;                                                     //> io_open  Pointer to  io_open_default()  function 
    s->io_close = io_close_default;
    av_opt_set_defaults(s);
}

///>  Now let's see io_open_default() Function implementation .
///> ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options) Such parameters 
static int io_open_default(AVFormatContext *s, AVIOContext **pb,
                           const char *url, int flags, AVDictionary **options)
{
    
    int loglevel;

    if (!strcmp(url, s->filename) ||
        s->iformat && !strcmp(s->iformat->name, "image2") ||
        s->oformat && !strcmp(s->oformat->name, "image2")
    ) {
    
        loglevel = AV_LOG_DEBUG;
    } else
        loglevel = AV_LOG_INFO;

    av_log(s, loglevel, "Opening \'%s\' for %s\n", url, flags & AVIO_FLAG_WRITE ? "writing" : "reading");
                ///>  Call this function 
    return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
}


///> ffio_open_whitelist function , In the source file path  libavformt\aviobuf.c ,  Use in a function  URLContext  type , The structure is as follows .
typedef struct URLContext {
    
    const AVClass *av_class;    /**< information for av_log(). Set by url_open(). */
    const struct URLProtocol *prot;
    void *priv_data;
    char *filename;             /**< specified URL */
    int flags;
    int max_packet_size;        /**< if non zero, the stream is packetized with this max packet size */
    int is_streamed;            /**< true if streamed (no seek possible), default = false */
    int is_connected;
    AVIOInterruptCB interrupt_callback;
    int64_t rw_timeout;         /**< maximum time to wait for (network) read/write operation completion, in mcs */
    const char *protocol_whitelist;
    const char *protocol_blacklist;
    int min_packet_size;        /**< if non zero, the stream is packetized with this min packet size */
} URLContext;

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);  ///>  Call this function to match  filename  Protocol category in 
    if (err < 0)
        return err;
    err = ffio_fdopen(s, h);                                                                       ///>  call  ffio_fdopen  function 
    if (err < 0) {
    
        ffurl_close(h);
        return err;
    }
    return 0;
}

///> ffurl_open_whitelist function , Source file path  libavformt\avio.c
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)
{
    
    AVDictionary *tmp_opts = NULL;
    AVDictionaryEntry *e;
    int ret = ffurl_alloc(puc, filename, flags, int_cb);            ///>  by  puc  Request memory space and create objects , And match the protocol category 
    if (ret < 0)
        return ret;
    if (parent)
        av_opt_copy(*puc, parent);
    if (options &&
        (ret = av_opt_set_dict(*puc, options)) < 0)
        goto fail;
    if (options && (*puc)->prot->priv_data_class &&
        (ret = av_opt_set_dict((*puc)->priv_data, options)) < 0)
        goto fail;

    if (!options)
        options = &tmp_opts;

    av_assert0(!whitelist ||
               !(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
               !strcmp(whitelist, e->value));
    av_assert0(!blacklist ||
               !(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
               !strcmp(blacklist, e->value));

    if ((ret = av_dict_set(options, "protocol_whitelist", whitelist, 0)) < 0)
        goto fail;

    if ((ret = av_dict_set(options, "protocol_blacklist", blacklist, 0)) < 0)
        goto fail;

    if ((ret = av_opt_set_dict(*puc, options)) < 0)
        goto fail;

    ret = ffurl_connect(*puc, options);                         ///>  Call... In the current file  ffurl_connect  function 

    if (!ret)
        return 0;
fail:
    ffurl_close(*puc);
    *puc = NULL;
    return ret;
}

///> ffurl_connect  Implementation of function 

int ffurl_connect(URLContext *uc, AVDictionary **options)
{
    
    int err;
    AVDictionary *tmp_opts = NULL;
    AVDictionaryEntry *e;

    if (!options)
        options = &tmp_opts;

    // Check that URLContext was initialized correctly and lists are matching if set
    av_assert0(!(e=av_dict_get(*options, "protocol_whitelist", NULL, 0)) ||
               (uc->protocol_whitelist && !strcmp(uc->protocol_whitelist, e->value)));
    av_assert0(!(e=av_dict_get(*options, "protocol_blacklist", NULL, 0)) ||
               (uc->protocol_blacklist && !strcmp(uc->protocol_blacklist, e->value)));

    if (uc->protocol_whitelist && av_match_list(uc->prot->name, uc->protocol_whitelist, ',') <= 0) {
    
        av_log(uc, AV_LOG_ERROR, "Protocol '%s' not on whitelist '%s'!\n", uc->prot->name, uc->protocol_whitelist);
        return AVERROR(EINVAL);
    }

    if (uc->protocol_blacklist && av_match_list(uc->prot->name, uc->protocol_blacklist, ',') > 0) {
    
        av_log(uc, AV_LOG_ERROR, "Protocol '%s' on blacklist '%s'!\n", uc->prot->name, uc->protocol_blacklist);
        return AVERROR(EINVAL);
    }

    if (!uc->protocol_whitelist && uc->prot->default_whitelist) {
    
        av_log(uc, AV_LOG_DEBUG, "Setting default whitelist '%s'\n", uc->prot->default_whitelist);
        uc->protocol_whitelist = av_strdup(uc->prot->default_whitelist);
        if (!uc->protocol_whitelist) {
    
            return AVERROR(ENOMEM);
        }
    } else if (!uc->protocol_whitelist)
        av_log(uc, AV_LOG_DEBUG, "No default whitelist set\n"); // This should be an error once all declare a default whitelist

    if ((err = av_dict_set(options, "protocol_whitelist", uc->protocol_whitelist, 0)) < 0)
        return err;
    if ((err = av_dict_set(options, "protocol_blacklist", uc->protocol_blacklist, 0)) < 0)
        return err;

    err =
        uc->prot->url_open2 ? uc->prot->url_open2(uc,                               ///>  When establishing a network link , call  url_open2 
                                                  uc->filename,
                                                  uc->flags,
                                                  options) :
        uc->prot->url_open(uc, uc->filename, uc->flags);                            ///>  If open2 If it fails, call  url_open  function 

    av_dict_set(options, "protocol_whitelist", NULL, 0);
    av_dict_set(options, "protocol_blacklist", NULL, 0);

    if (err)
        return err;
    uc->is_connected = 1;
    /* We must be careful here as ffurl_seek() could be slow, * for example for http */
    if ((uc->flags & AVIO_FLAG_WRITE) || !strcmp(uc->prot->name, "file"))
        if (!uc->is_streamed && ffurl_seek(uc, 0, SEEK_SET) < 0)
            uc->is_streamed = 1;
    return 0;
}
///>  Support that we will soon be able to see  url_open  If the function is operated .
///>  There are still some twists and turns 、url_open It's a function pointer , The specific function pointed to needs to find the initialization process .

///>  The search process is as follows . 
///>  In the open  white_list  Function ,  by  puc  Request memory space and create objects , And match the protocol category 
int ffurl_alloc(URLContext **puc, const char *filename, int flags,
                const AVIOInterruptCB *int_cb)
{
    
    const URLProtocol *p = NULL;

    p = url_find_protocol(filename);                                          ///>  Call this function   according to filename  Find corresponding  URLProtocol  Support examples  
    if (p)
       return url_alloc_for_protocol(puc, p, filename, flags, int_cb);        ///>  Call this function   establish  URLContext  Type instance 

    *puc = NULL;
    if (av_strstart(filename, "https:", NULL))
        av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with "
                                     "openssl, gnutls "
                                     "or securetransport enabled.\n");
    return AVERROR_PROTOCOL_NOT_FOUND;
}

///>  according to filename  Find the corresponding  URLProtocol  example  
static const struct URLProtocol *url_find_protocol(const char *filename)
{
    
    const URLProtocol **protocols;
    char proto_str[128], proto_nested[128], *ptr;
    size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
    int i;

    if (filename[proto_len] != ':' &&
        (strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||
        is_dos_path(filename))
        strcpy(proto_str, "file");
    else
        av_strlcpy(proto_str, filename,
                   FFMIN(proto_len + 1, sizeof(proto_str)));

    av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
    if ((ptr = strchr(proto_nested, '+')))
        *ptr = '\0';

    protocols = ffurl_get_protocols(NULL, NULL);        ///>  obtain  protocols[]  Array pointer 
    if (!protocols)
        return NULL;
    for (i = 0; protocols[i]; i++) {
                        ///> protocols[i] = 0  when , Traverse everything .
            const URLProtocol *up = protocols[i];
        if (!strcmp(proto_str, up->name)) {
                 ///>  Whether this part of the matching protocol supports 
            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;
}
///>  In the function, just give  url_open  The function is assigned , But why don't we see the assignment process ?  We will be able to interface soon  ijkplayer  Communication protocol abstract rules .
///>  Let's take a look first  URLProtocol  Definition of structure .
typedef struct URLProtocol {
    
    const char *name;
    int     (*url_open)( URLContext *h, const char *url, int flags);        ///>  The function called in the above function  url_open
    /** * This callback is to be used by protocols which open further nested * protocols. options are then to be passed to ffurl_open()/ffurl_connect() * for those nested protocols. */                                                                     ///>  The function called in the above function  url_open2
    int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
    int     (*url_accept)(URLContext *s, URLContext **c);
    int     (*url_handshake)(URLContext *c);
    int     (*url_read)( URLContext *h, unsigned char *buf, int size);
    int     (*url_write)(URLContext *h, const unsigned char *buf, int size);
    int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
    int     (*url_close)(URLContext *h);
    int (*url_read_pause)(URLContext *h, int pause);
    int64_t (*url_read_seek)(URLContext *h, int stream_index,
                             int64_t timestamp, int flags);
    ///>  Here are   Encapsulation interface for file operation 
    int (*url_get_file_handle)(URLContext *h);
    int (*url_get_multi_file_handle)(URLContext *h, int **handles,
                                     int *numhandles);
    int (*url_get_short_seek)(URLContext *h);
    int (*url_shutdown)(URLContext *h, int flags);
    int priv_data_size;
    const AVClass *priv_data_class;
    int flags;
    int (*url_check)(URLContext *h, int mask);
    int (*url_open_dir)(URLContext *h);
    int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
    int (*url_close_dir)(URLContext *h);
    int (*url_delete)(URLContext *h);
    int (*url_move)(URLContext *h_src, URLContext *h_dst);
    const char *default_whitelist;
} URLProtocol;

We are reviewing the process :

  1. In the read thread read_thread() , When entering LOOP Pre body call avformat_open_input() function
  2. stay avformat_open_input Function , call init_input(s, filename, &tmp)) initialization “input” function
  3. call io_open() Function to open a file or network socket How to input , The pointer points to io_open_default() function
  4. io_open_default The function is called ffio_open_whitelist() Function to find the supported protocols
  5. Open whitelist function call ffurl_connect() -> url_open function , This function is a pointer again
  6. url_open The pointer assignment is in url_find_protocol() Function , But we don't see any specific assignment statements .

Now it's a little awkward , I still haven't seen the specific process of opening files or network links after running such a big circle ,
If we need to refactor ijkplayer How to add a private agreement ? We are currently in such a state ,
Want to ijkplayer Expand the contents of the agreement 、 Add control .

In this article, I want to put ijkplayer Communication of profile The design logic is sorted out , First review the previous article
In function url_find_protocol in ,

static const struct URLProtocol *url_find_protocol(const char *filename)
{
    
    const URLProtocol **protocols;
    
    protocols = ffurl_get_protocols(NULL, NULL);        ///>  obtain  protocols[]  Array pointer 
    

    return NULL;
}

function ffurl_get_protocols() The return value is a ijkplayer Supported communication protocol items ,
The source file path of this function ffmpeg-x86\libavformat\protocols.c In file .

///>  The beginning of the file declares a lot of external  const URLProtocol  object ,URLProtocol The structure definition has been listed above .
extern const URLProtocol ff_rtp_protocol;
extern const URLProtocol ff_sctp_protocol;
extern const URLProtocol ff_srtp_protocol;
extern const URLProtocol ff_subfile_protocol;
extern const URLProtocol ff_tee_protocol;
extern const URLProtocol ff_tcp_protocol;
extern const URLProtocol ff_tls_gnutls_protocol;
extern const URLProtocol ff_tls_schannel_protocol;
extern const URLProtocol ff_tls_securetransport_protocol;
extern const URLProtocol ff_tls_openssl_protocol;
extern const URLProtocol ff_udp_protocol;
extern const URLProtocol ff_udplite_protocol;
extern const URLProtocol ff_unix_protocol;
extern const URLProtocol ff_librtmp_protocol;
///>  That is to say, this definition occupies memory space 、 Are all instance contents .

///>  We looked at ffurl_get_protocols function ,
const URLProtocol **ffurl_get_protocols(const char *whitelist,
                                        const char *blacklist)
{
    
    const URLProtocol **ret;
    int i, ret_idx = 0;

    ret = av_mallocz_array(FF_ARRAY_ELEMS(url_protocols), sizeof(*ret));    ///> url_protocols[i]  Arrays are global variables , Definition not found yet . No idea has been found for this problem .
    if (!ret)
        return NULL;

    for (i = 0; url_protocols[i]; i++) {
                                        ///>  Traverse  url_protocols[]  Support protocol array , Match the supported protocols in the array 
        const URLProtocol *up = url_protocols[i];

        if (whitelist && *whitelist && !av_match_name(up->name, whitelist))
            continue;
        if (blacklist && *blacklist && av_match_name(up->name, blacklist))
            continue;

        ret[ret_idx++] = up;
    }

    return ret;
}

///>  This function is a pointer to the protocol items outside the list , All put in  ret  Array , So the function seen in the last article will  protocols  Traverse the array .
///>  This logic corresponds to , But the problem is  url_protocols[]  Where do the array contents come from ?? 

Search the entire project globally , Under the hardware architecture related folder , route ijkplayer/android/contrib/ffmpeg-x86/configure Script files , In this document
It has the following contents :

# generate the lists of enabled components
print_enabled_components(){
    
    file=$1
    struct_name=$2
    name=$3
    shift 3
    echo "static const $struct_name * const $name[] = {" > $TMPH
    for c in $*; do
        enabled $c && printf " &ff_%s,\n" $c >> $TMPH
    done
    echo " NULL };" >> $TMPH
    cp_if_changed $TMPH $file
}

print_enabled_components libavcodec/bsf_list.c AVBitStreamFilter bitstream_filters $BSF_LIST
print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST

That is, when compiling the source code , Will execute this script to generate libavformat/protocol_list.c file , The contents of this document are as follows :

[email protected]:~/ljbPlayer/ijkplayer/android/contrib/ffmpeg-x86$ cat libavformat/protocol_list.c 
static const URLProtocol * const url_protocols[] = {
    
    &ff_async_protocol,
    &ff_cache_protocol,
    &ff_data_protocol,
    &ff_ffrtmphttp_protocol,
    &ff_file_protocol,
    &ff_ftp_protocol,
    &ff_hls_protocol,
    &ff_http_protocol,
    &ff_httpproxy_protocol,
    &ff_ijkhttphook_protocol,
    &ff_ijklongurl_protocol,
    &ff_ijkmediadatasource_protocol,
    &ff_ijksegment_protocol,
    &ff_ijktcphook_protocol,
    &ff_ijkio_protocol,
    &ff_pipe_protocol,
    &ff_prompeg_protocol,
    &ff_rtmp_protocol,
    &ff_rtmpt_protocol,
    &ff_tee_protocol,
    &ff_tcp_protocol,
    &ff_udp_protocol,
    &ff_udplite_protocol,
    NULL };
[email protected]:~/ljbPlayer/ijkplayer/android/contrib/ffmpeg-x86$ 

thus , We read through three code walkthroughs , hold ijkplayer Data communication of profile The design logic has been sorted out ,
What is clear here is that it passes so Library mode , There are other entry ways in the project, such as cmd Way, etc .

We are summarizing :

  1. IJKFF_PLAYER The library generates hardware architecture related so library , This rule can also be seen from the results of the project documents .
  2. When extending the communication protocol 、 You only need to add your own protocol file to the corresponding hardware architecture , The matching protocol can succeed .
  3. Video format profile Support methods and communication profile The design idea roughly infers that the logic should be similar .
原网站

版权声明
本文为[That's right]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202270555068482.html