ffmpeg-URL(转)

ffmpeg中为方便对资源进行访问,定义了两个结构体,URLContext中是对具体资源文件进行操作的上下文,URLProtocol则是在将资源进行分类的基础上,对某一类资源操作的函数集,熟悉Linux设备驱动程序的话,很容易联想到file_operations结构体

typedef struct URLContext 
 {
     const AVClass *av_class;    /**< information for av_log(). Set by url_open(). */
     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 */
 } 
 URLContext;





typedef struct URLProtocol 
 {
     const char *name;
     int     (*url_open)( URLContext *h, const char *url, int flags);
     /**
      * 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.
      */
     int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);


     /**
      * Read data from the protocol.
      * If data is immediately available (even less than size), EOF is
      * reached or an error occurs (including EINTR), return immediately.
      * Otherwise:
      * In non-blocking mode, return AVERROR(EAGAIN) immediately.
      * In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
      * and return AVERROR(EAGAIN) on timeout.
      * Checking interrupt_callback, looping on EINTR and EAGAIN and until
      * enough data has been read is left to the calling function; see
      * retry_transfer_wrapper in avio.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);
     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 (*url_get_multi_file_handle)(URLContext *h, int **handles,
                                      int *numhandles);
     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);
 }
 URLProtocol;


在代码中搜索url_open,很容易能够找到ffmpeg中支持的几个协议:

URLProtocol ff_bluray_protocol = 
 {
     .name            = "bluray",
     .url_close       = bluray_close,
     .url_open        = bluray_open,
     .url_read        = bluray_read,
     .url_seek        = bluray_seek,
     .priv_data_size  = sizeof(BlurayContext),
     .priv_data_class = &bluray_context_class,
 };

URLProtocol ff_data_protocol = 
 {
     .name           = "data",
     .url_open       = data_open,
     .url_close      = data_close,
     .url_read       = data_read,
     .priv_data_size = sizeof(DataContext),
 };


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,
     .priv_data_size      = sizeof(FileContext),
     .priv_data_class     = &file_class,
 };


URLProtocol ff_pipe_protocol = 
 {
     .name                = "pipe",
     .url_open            = pipe_open,
     .url_read            = file_read,
     .url_write           = file_write,
     .url_get_file_handle = file_get_handle,
     .url_check           = file_check,
     .priv_data_size      = sizeof(FileContext),
     .priv_data_class     = &pipe_class,
 };


URLProtocol ff_ftp_protocol = 
 {
     .name                = "ftp",
     .url_open            = ftp_open,
     .url_read            = ftp_read,
     .url_write           = ftp_write,
     .url_seek            = ftp_seek,
     .url_close           = ftp_close,
     .url_get_file_handle = ftp_get_file_handle,
     .url_shutdown        = ftp_shutdown,
     .priv_data_size      = sizeof(FTPContext),
     .priv_data_class     = &ftp_context_class,
     .flags               = URL_PROTOCOL_FLAG_NETWORK,
 };

URLProtocol ff_hls_protocol = 
 {
     .name           = "hls",
     .url_open       = hls_open,
     .url_read       = hls_read,
     .url_close      = hls_close,
     .flags          = URL_PROTOCOL_FLAG_NESTED_SCHEME,
     .priv_data_size = sizeof(HLSContext),
 };


URLProtocol ff_httpproxy_protocol = 
 {
     .name                = "httpproxy",
     .url_open            = http_proxy_open,
     .url_read            = http_buf_read,
     .url_write           = http_proxy_write,
     .url_close           = http_proxy_close,
     .url_get_file_handle = http_get_file_handle,
     .priv_data_size      = sizeof(HTTPContext),
     .flags               = URL_PROTOCOL_FLAG_NETWORK,
 };


... 还有很多协议,不全部列出了,例如tcp, udp, ...

支持这些协议而定义的结构体在av_register_all()函数被调用时而注册,例如:

 REGISTER_PROTOCOL(FILE,             file);





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


ffurl_register_protocol(&ff_file_protocol);

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


注册到以static URLProtocol *first_protocol = NULL;为头的一个链表中,每一个协议结构体是链表中的一项;




以avio_open2为例:

int avio_open2(AVIOContext **s, const char *filename, int flags,
                const AVIOInterruptCB *int_cb, AVDictionary **options)
 {
     URLContext *h;
     int err;


     err = ffurl_open(&h, filename, flags, int_cb, options);
     if (err < 0)
         return err;
     
     err = ffio_fdopen(s, h);
     if (err < 0) 
     {
         ffurl_close(h);
         return err;
     }
     
     return 0;
 }


ffurl_open--->ffurl_alloc--->static struct URLProtocol *url_find_protocol(const char *filename)

url_find_protocol函数会根据filename判断协议,这个函数的定义没太看懂,但是其作用,无非是先判断具体的协议,然后根据判读出来的协议,调用

URLProtocol *ffurl_protocol_next(URLProtocol *prev)
 {
     return prev ? prev->next : first_protocol;
 }


函数,在之前注册的first_protocol链表上找到相应的协议结构体,然后返回给函数url_alloc_for_protocol,在这个函数中,则是根据判断的协议,生成URLContext结构体,

uc = av_mallocz(sizeof(URLContext) + strlen(filename) + 1);


*puc = uc;





在ffurl_open函数中,ffurl_alloc函数返回之后,URLContext **puc结构体就已经被设置完毕,然后在ffurl_open函数中,再调用ret = ffurl_connect(*puc, options);

int ffurl_connect(URLContext *uc, AVDictionary **options)
 {
     int err =
         uc->prot->url_open2 ? uc->prot->url_open2(uc,
                                                   uc->filename,
                                                   uc->flags,
                                                   options) :
         uc->prot->url_open(uc, uc->filename, uc->flags);
     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;
 }


prot是协议,在url_alloc_for_protocol函数中设置的:uc->prot            = up;

然后url_open2, url_open都是在协议结构体中定义的函数指针,以"file"协议为例:

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,
    .priv_data_size      = sizeof(FileContext),
    .priv_data_class     = &file_class,
};


url_open2是NULL,则调用url_open函数指针指向的函数file_open:

static int file_open(URLContext *h, const char *filename, int flags)
 {
     FileContext *c = h->priv_data;
     int access;
     int fd;
     struct stat st;


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


     if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
         access = O_CREAT | O_RDWR;
         if (c->trunc)
             access |= O_TRUNC;
     } else if (flags & AVIO_FLAG_WRITE) {
         access = O_CREAT | O_WRONLY;
         if (c->trunc)
             access |= O_TRUNC;
     } else {
         access = O_RDONLY;
     }
 #ifdef O_BINARY
     access |= O_BINARY;
 #endif
     fd = avpriv_open(filename, access, 0666);
     if (fd == -1)
         return AVERROR(errno);
     c->fd = fd;


     h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode);


     return 0;


}

http://blog.csdn.net/xiruanliuwei/article/details/24928237

原文地址:https://www.cnblogs.com/wainiwann/p/4223776.html