AVIOContext 再学习

这个目前阶段用的不多,暂时不要花费太多精力。

url 的格式不同,使用的传输层协议也不同。这块看代码还没看到自己想的这样。

目前看的信息是:avformatContext 的 io_open 回调函数 在默认情况下叫 io_open_default,在解复用的 avformat_open_input 方法中一定会调用。那么我们如果不使用这个默认的 io_open_default,使用自己写的回调函数,会怎么样呢? 

还看到的信息:avformatContext的pb (    AVIOContext *pb;)是通过 avio_alloc_context方法创建的,那么我们如果自己创建,也可以使用avio_alloc_context创建。

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))

avio_alloc_context 函数说明

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)
				);
				
opaque是 read_packet / write_packet 的第⼀个参数,指向⽤户数据。
buffer和buffer_size是 read_packet / write_packet 的第⼆个和第三个参数,是供FFmpeg使⽤
的数据区。
buffer ⽤作FFmpeg输⼊时,由⽤户负责向 buffer 中填充数据,FFmpeg取⾛数据。
buffer ⽤作FFmpeg输出时,由FFmpeg负责向 buffer 中填充数据,⽤户取⾛数据。
write_flag是缓冲区读写标志,读写的主语是指FFmpeg。
write_flag 为1时, buffer ⽤于写,即作为FFmpeg输出。
write_flag 为0时, buffer ⽤于读,即作为FFmpeg输⼊。
read_packet和write_packet是函数指针,指向⽤户编写的回调函数。
seek也是函数指针,需要⽀持seek时使⽤。 可以类⽐fseek的机制

需要和如下的结合起来学习,

AVFormatContext 再分析零-CSDN博客

AVIOContext  是 avformatContext 结构体中一个成员变量

是传输层的

中文翻译:

*-demuxing:

要么由用户在avformat_open_input()之前设置(然后用户必须手动关闭它),

要么由avformat_opend_input()设置。这里应该想要表达的是如果不设置,那么avformat_opend_input方法内部会自动查找。


*-muxing:由用户在avformat_write_header()之前设置。调用者必须负责关闭/释放IO上下文。
*如果在中设置了AVFMT_NOFILE标志,则不要设置此字段iform/oform.flags。在这种情况下,(解)复用器将以其他方式处理I/O,此字段将为NULL。

    /**
     * I/O context.
     *
     * - 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(). The caller must
     *           take care of closing / freeing the IO context.
     *
     * Do NOT set this field if AVFMT_NOFILE flag is set in
     * iformat/oformat.flags. In such a case, the (de)muxer will handle
     * I/O in some other way and this field will be NULL.
     */
    AVIOContext *pb;

AVIOContext 自己是一个结构体

/**
 * Bytestream IO Context.
 * New fields can be added to the end with minor version bumps.
 * Removal, reordering and changes to existing fields require a major
 * version bump.
 * sizeof(AVIOContext) must not be used outside libav*.
 *
 * @note None of the function pointers in AVIOContext should be called
 *       directly, they should only be set by the client application
 *       when implementing custom I/O. Normally these are set to the
 *       function pointers specified in avio_alloc_context()
 */
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;

    /*
     * The following shows the relationship between buffer, buf_ptr,
     * buf_ptr_max, 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                 |
     *                             |--------------------------------------|
     *                             |                                      |
     *
     *                                                buf_ptr_max
     *                          buffer                 (buf_ptr)       buf_end
     *                             +-----------------------+--------------+
     *                             |/ / / / / / / / / / / /|              |
     *  write buffer:              | / / to be flushed / / |              |
     *                             |/ / / / / / / / / / / /|              |
     *                             +-----------------------+--------------+
     *                               buf_ptr can be in this
     *                               due to a backward seek
     *
     *                            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 eof_reached;        /**< true if was unable to read due to error or eof */
    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);

    int64_t written;

    /**
     * Maximum reached position before a backward seek in the write buffer,
     * used keeping track of already written data for a later flush.
     */
    unsigned char *buf_ptr_max;

    /**
     * Try to buffer at least this amount of data before flushing it
     */
    int min_packet_size;
} AVIOContext;

ffmpeg 是如何通过参数url 找到具体的 AVIOContext 的?

在解复用一个本地的mp4文件的时候,我们 是这样调用的,传递的 url的值是 "./120/400_300_25.mp4"

    const char* url_400 = "./120/400_300_25.mp4";

    int ret = avformat_open_input(&avformatContext, url_400, nullptr, nullptr);

调用
    AVFormatContext* avformatContext = nullptr;
    const char* url_400 = "./120/400_300_25.mp4";
    const char* url_1080 = "./120/1920_1080_25.mp4";
    int ret = avformat_open_input(&avformatContext, url_400, nullptr, nullptr);
    
    
1. avformat_open_input 方法内部会重新创建 avformat_alloc_context,创建 AVFormatContext *s = avformat_alloc_context()  

1.1 avformat_alloc_context内部调用 avformat_get_context_defaults
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; //注意这里,设定了 AVFormatContext 的 io_open 回调函数
    s->io_close = io_close_default;

    av_opt_set_defaults(s);
}

1.2 然后调用
    if ((ret = init_input(s, filename, &tmp)) < 0)
    
    	------>		if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0)
    	------> 	也就是调用 io_open_default方法,传递参数为 AVFormatContext,AVFormatContext->pb的地址,后面在 
						------->	调用 return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist);
								---------> 和当前关系不大    err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
								--------->		 err = ffio_fdopen(s, h);int ffio_fdopen(AVIOContext **s, URLContext *h)
											------>			ffio_fdopen内部做了 avio_alloc_context,创建了AVIOContext *s,也就是 avformatContext的 pb,
										

io_open --- A callback for opening new IO streams.


从这里可以看出几点:
0. 当avformat_open_input方法中传递的 avformatContext是nullptr,  则:AVFormatContext->io_open = io_open_default
1.即使我们使用 AVFormatContext *avformat_alloc_context(void)创建了avfromatContext,内部也会   AVFormatContext->io_open = io_open_default
1. io_open 回调函数,在avformat_open_input方法中会调用,也就是说,默认会调用 io_open_default 函数
2. 那么我们可以自己定义 AVFormatContext->io_open的回调函数吗?那要看 io_open_default  函数的功能是啥?
在Qt QML中使用FFmpeg需要使用FFmpeg库的C++绑定。可以使用Qt提供的QProcess类执行FFmpeg命令,也可以使用Qt提供的QIODevice类和FFmpeg的AVIOContext结合使用,更加灵活。 以下是一些使用FFmpeg的示例代码: 1. 使用QProcess执行FFmpeg命令: ``` QProcess process; QStringList args; args << "-i" << inputFile << "-c:v" << "copy" << "-c:a" << "copy" << outputFile; process.start("ffmpeg", args); process.waitForFinished(); ``` 2. 使用QIODevice和AVIOContext实现更加灵活的操作: ``` // 创建AVIOContext AVIOContext *avio = NULL; uint8_t *avioBuffer = NULL; int avioBufferSize = 4096; avioBuffer = (uint8_t*)av_malloc(avioBufferSize); if (!avioBuffer) { return; } avio = avio_alloc_context(avioBuffer, avioBufferSize, 0, this, &read_packet, NULL, &seek); if (!avio) { av_free(avioBuffer); return; } // 打开输入文件 AVFormatContext *fmtCtx = NULL; int ret = avformat_open_input(&fmtCtx, inputFile.toUtf8().data(), NULL, NULL); if (ret < 0) { av_free(avioBuffer); avformat_close_input(&fmtCtx); return; } // 设置AVIOContext fmtCtx->pb = avio; // 构造输出文件AVFormatContext AVFormatContext *outFmtCtx = NULL; ret = avformat_alloc_output_context2(&outFmtCtx, NULL, NULL, outputFile.toUtf8().data()); if (ret < 0) { av_free(avioBuffer); avformat_close_input(&fmtCtx); return; } // 构造输出文件AVStream AVStream *inStream = fmtCtx->streams[streamIndex]; AVStream *outStream = avformat_new_stream(outFmtCtx, inStream->codec->codec); if (!outStream) { av_free(avioBuffer); avformat_close_input(&fmtCtx); avformat_free_context(outFmtCtx); return; } // 复制AVStream参数 ret = avcodec_parameters_copy(outStream->codecpar, inStream->codecpar); if (ret < 0) { av_free(avioBuffer); avformat_close_input(&fmtCtx); avformat_free_context(outFmtCtx); return; } // 写入文件头 ret = avformat_write_header(outFmtCtx, NULL); if (ret < 0) { av_free(avioBuffer); avformat_close_input(&fmtCtx); avformat_free_context(outFmtCtx); return; } // 循环读取AVPacket并写入输出文件 AVPacket pkt; while (1) { ret = av_read_frame(fmtCtx, &pkt); if (ret < 0) { break; } // 修改AVPacket的stream_index pkt.stream_index = outStream->index; // 写入AVPacket ret = av_interleaved_write_frame(outFmtCtx, &pkt); if (ret < 0) { break; } av_packet_unref(&pkt); } // 写入文件尾 av_write_trailer(outFmtCtx); // 释放资源 av_free(avioBuffer); avformat_close_input(&fmtCtx); avformat_free_context(outFmtCtx); ``` 需要注意的是,上述代码中使用了AVIOContext和AVFormatContext等FFmpeg底层API,需要对FFmpeg有一定的了解。建议先学习FFmpeg的基础知识再尝试在Qt QML中使用FFmpeg。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值