From 5e37f0ad2b8a319e0e5ff08060b580d066e3c3ee Mon Sep 17 00:00:00 2001 From: wenchao1024 <87457873+wenchao1024@users.noreply.github.com> Date: Thu, 6 Jan 2022 14:46:59 +0800 Subject: [PATCH] =?UTF-8?q?Create=20FFmpeg=20=E7=BB=93=E6=9E=84=E4=BD=93?= =?UTF-8?q?=E5=AD=A6=E4=B9=A0(=E4=B8=83)=EF=BC=9A=20AVIOContext=20?= =?UTF-8?q?=E5=88=86=E6=9E=90.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...构体学习(七): AVIOContext 分析.md | 295 ++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 FFmpeg 结构体学习(七): AVIOContext 分析.md diff --git a/FFmpeg 结构体学习(七): AVIOContext 分析.md b/FFmpeg 结构体学习(七): AVIOContext 分析.md new file mode 100644 index 0000000..247cdae --- /dev/null +++ b/FFmpeg 结构体学习(七): AVIOContext 分析.md @@ -0,0 +1,295 @@ +AVIOContext是FFMPEG管理输入输出数据的结构体。下面我们来分析一下该结构体里重要变量的含义和作用。 + +# 一、源码整理 + +首先我们先看一下结构体AVIOContext的定义的结构体源码(位于libavformat/avio.h): + + + +``` + /** + * 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 { + /** + * 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. + */ + AVClass *av_class; + 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; +} AVIOContext; +``` + + + +# 二、AVIOContext 重点字段 + +AVIOContext中有以下几个变量比较重要: + + + +``` +unsigned char *buffer:缓存开始位置 + +int buffer_size:缓存大小(默认32768) + +unsigned char *buf_ptr:当前指针读取到的位置 + +unsigned char *buf_end:缓存结束的位置 + +void *opaque:URLContext结构体 +``` + + + +在解码的情况下,buffer用于存储ffmpeg读入的数据。例如打开一个视频文件的时候,先把数据从硬盘读入buffer,然后在送给解码器用于解码。 + +其中opaque指向了URLContext。注意,这个结构体并不在FFMPEG提供的头文件中,而是在FFMPEG的源代码中。从FFMPEG源代码中翻出的定义如下所示: + + + +``` +typedef struct URLContext { + const AVClass *av_class; ///< information for av_log(). Set by url_open(). + struct URLProtocol *prot; + int flags; + int is_streamed; /**< true if streamed (no seek possible), default = false */ + int max_packet_size; /**< if non zero, the stream is packetized with this max packet size */ + void *priv_data; + char *filename; /**< specified URL */ + int is_connected; + AVIOInterruptCB interrupt_callback; +} URLContext; +``` + + + +URLContext结构体中还有一个结构体URLProtocol。注:每种协议(rtp,rtmp,file等)对应一个URLProtocol。这个结构体也不在FFMPEG提供的头文件中。从FFMPEG源代码中翻出其的定义: + + + +``` +typedef struct URLProtocol { + const char *name; + int (*url_open)(URLContext *h, const char *url, int flags); + 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); + struct URLProtocol *next; + int (*url_read_pause)(URLContext *h, int pause); + int64_t (*url_read_seek)(URLContext *h, int stream_index, + int64_t timestamp, int flags); + int (*url_get_file_handle)(URLContext *h); + int priv_data_size; + const AVClass *priv_data_class; + int flags; + int (*url_check)(URLContext *h, int mask); +} URLProtocol; +``` + + + +在这个结构体中,除了一些回调函数接口之外,有一个变量const char *name,该变量存储了协议的名称。每一种输入协议都对应这样一个结构体。 + +比如说,文件协议中代码如下(file.c): + + + +``` +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, +}; +``` + + + +libRTMP中代码如下(libRTMP.c): + + + +``` +URLProtocol ff_rtmp_protocol = { + .name = "rtmp", + .url_open = rtmp_open, + .url_read = rtmp_read, + .url_write = rtmp_write, + .url_close = rtmp_close, + .url_read_pause = rtmp_read_pause, + .url_read_seek = rtmp_read_seek, + .url_get_file_handle = rtmp_get_file_handle, + .priv_data_size = sizeof(RTMP), + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; +``` + + + +udp协议代码如下(udp.c): + + + +``` +URLProtocol ff_udp_protocol = { + .name = "udp", + .url_open = udp_open, + .url_read = udp_read, + .url_write = udp_write, + .url_close = udp_close, + .url_get_file_handle = udp_get_file_handle, + .priv_data_size = sizeof(UDPContext), + .flags = URL_PROTOCOL_FLAG_NETWORK, +}; +``` + + + +等号右边的函数是完成具体读写功能的函数。可以看一下file协议的几个函数(其实就是读文件,写文件这样的操作)(file.c): + + + +``` +/* standard file protocol */ + +static int file_read(URLContext *h, unsigned char *buf, int size) +{ + int fd = (intptr_t) h->priv_data; + int r = read(fd, buf, size); + return (-1 == r)?AVERROR(errno):r; +} + +static int file_write(URLContext *h, const unsigned char *buf, int size) +{ + int fd = (intptr_t) h->priv_data; + int r = write(fd, buf, size); + return (-1 == r)?AVERROR(errno):r; +} + +static int file_get_handle(URLContext *h) +{ + return (intptr_t) h->priv_data; +} + +static int file_check(URLContext *h, int mask) +{ + struct stat st; + int ret = stat(h->filename, &st); + if (ret < 0) + return AVERROR(errno); + + ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ : 0; + ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0; + + return ret; +} + +#if CONFIG_FILE_PROTOCOL + +static int file_open(URLContext *h, const char *filename, int flags) +{ + int access; + int fd; + + av_strstart(filename, "file:", &filename); + + if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) { + access = O_CREAT | O_TRUNC | O_RDWR; + } else if (flags & AVIO_FLAG_WRITE) { + access = O_CREAT | O_TRUNC | O_WRONLY; + } else { + access = O_RDONLY; + } +#ifdef O_BINARY + access |= O_BINARY; +#endif + fd = open(filename, access, 0666); + if (fd == -1) + return AVERROR(errno); + h->priv_data = (void *) (intptr_t) fd; + return 0; +} + +/* XXX: use llseek */ +static int64_t file_seek(URLContext *h, int64_t pos, int whence) +{ + int fd = (intptr_t) h->priv_data; + if (whence == AVSEEK_SIZE) { + struct stat st; + int ret = fstat(fd, &st); + return ret < 0 ? AVERROR(errno) : st.st_size; + } + return lseek(fd, pos, whence); +} + +static int file_close(URLContext *h) +{ + int fd = (intptr_t) h->priv_data; + return close(fd); +} +``` +