ffmpeg分析系列之二(文件协议)

URL协议结构:
URLProtocol,URLContextByteIOContext是FFMpeg操作文件(即I/O,包括网络数据流)的结构,这几个结构现实的功能类似于C++的多态继承吧,C++的多态是通过子类继承实现,而FFMpeg的“多态”是通过静态对像现实。这部分的代码非常值得C程序借鉴,我是说,如果你要在C里实现类似C++多态性的功能;比如当你要区分你老婆和情人之间的不同功能时。

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, unsigned char *buf, int size);
    int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);
    int (*url_close)(URLContext *h);
    struct URLProtocol *next
;   //指向下一个URLProtocol见备注1
    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);
} URLProtocol;



备注1:FFMpeg所有的Protol类型都用这个变量串成一个链表,表头为avio.c里的URLProtocol *first_protocol = NULL;

每个文件类似都有自己的一个URLProtocol静态对象,如libavformat/file.c里

 

URLProtocol file_protocol = {

    "file",

    file_open,

    file_read,

    file_write,

    file_seek,

    file_close,

    .url_get_file_handle = file_get_handle,

};


再通过av_register_protocol()将他们链接成链表。在FFMpeg中所有的URLProtocol对像值都在编译时确定。

typedef struct URLContext {

#if LIBAVFORMAT_VERSION_MAJOR >= 53

    const AVClass *av_class; ///< information for av_log(). Set by url_open().

#endif

    struct URLProtocol *prot;          //指向具体的I/0类型,在运行时通过文件URL确定,如是file类型时就是file_protocol          

    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;                       //指向具体的I/O句柄

    char *filename; /**< specified URL */

} URLContext;


不同于URLProtocol对象值在编译时确定,URLContext对象值是在运行过程中根据输入的I/O类型动态确定的。这一动一静组合起到了C++的多态继承一样的作用。URLContext像是基类,为大家共同所有,而URLProtocol像是子类部分。

typedef struct {

    unsigned char *buffer;

    int buffer_size;

    unsigned char *buf_ptr, *buf_end;

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

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

    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

    int (*read_pause)(void *opaque, int pause);

    int64_t (*read_seek)(void *opaque, int stream_index,

                         int64_t timestamp, int flags);

} ByteIOContext;


ByteIOContext是URLContext和URLProtocol 一个扩展,也是FFMpeg提供给用户的接口,URLContext和URLProtocol对用户是透明,我们所有的操作是通过ByteIOContext现实。这几个struct的相关的关键函数有:

int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,

                       AVInputFormat *fmt,

                       int buf_size,

                       AVFormatParameters *ap)

{

    int url_fopen(ByteIOContext **s, const char *filename, int flags)

    {

         url_open(URLContext **puc, const char *filename, int flags)

         {

               URLProtocol *up;

               //根据filename确定up

               url_open_protocol (URLContext **puc, struct URLProtocol *up, const char *filename, int flags)

               {

                    //初始化URLContext对像,并通过 up->url_open()将I/O打开将I/O fd赋值给URLContext的priv_data对像

               }

         }

         url_fdopen(ByteIOContext **s, URLContext *h)

         {

               //初始化ByteIOContext 对像

         }

    }

}



libavformat/file.c文件的file协议:

static int file_open(URLContext *h, const char *filename, int flags)
{
    int access;
    int fd;

    av_strstart(filename, "file:", &filename);

    if (flags & URL_RDWR) {
        access = O_CREAT | O_TRUNC | O_RDWR;
    } else if (flags & URL_WRONLY) {
        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;
}

static int file_read(URLContext *h, unsigned char *buf, int size)
{
    int fd = (intptr_t) h->priv_data;
    return read(fd, buf, size);
}

static int file_write(URLContext *h, unsigned char *buf, int size)
{
    int fd = (intptr_t) h->priv_data;
    return write(fd, buf, size);
}

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

static int file_get_handle(URLContext *h)
{
    return (intptr_t) h->priv_data;
}

URLProtocol file_protocol = {
    "file",
    file_open,
    file_read,
    file_write,
    file_seek,
    file_close,
    .url_get_file_handle = file_get_handle,
};


libavformat/allformats.c文件的av_register_all函数注册了file协议:

#define REGISTER_PROTOCOL(X,x) { \
    extern URLProtocol x##_protocol; \
    if(CONFIG_##X##_PROTOCOL) av_register_protocol(&x##_protocol); }


void av_register_all(void)
{
    /* 省略部分代码 */
    /* protocols */
    REGISTER_PROTOCOL (FILE, file);
    /* 省略部分代码 */
}


把注册协议函数也贴出来吧:

URLProtocol *first_protocol = NULL;


int av_register_protocol(URLProtocol *protocol)
{
    URLProtocol **p;
    p = &first_protocol;
    while (*!= NULL) p = &(*p)->next;
    *= protocol;
    protocol->next = NULL;
    return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值