欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > FFmpeg源码:url_find_protocol函数分析

FFmpeg源码:url_find_protocol函数分析

2025/2/21 3:18:14 来源:https://blog.csdn.net/u014552102/article/details/145639767  浏览:    关键词:FFmpeg源码:url_find_protocol函数分析

一、url_find_protocol函数的定义

url_find_protocol函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/avio.c中:

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");elseav_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);if (!protocols)return NULL;for (i = 0; protocols[i]; i++) {const URLProtocol *up = protocols[i];if (!strcmp(proto_str, up->name)) {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);if (av_strstart(filename, "https:", NULL) || av_strstart(filename, "tls:", NULL))av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with ""openssl, gnutls or securetransport enabled.\n");return NULL;
}

该函数在avformat_open_input函数内部被调用。其作用是:根据形参filename查找并返回匹配的流媒体协议类型 ;如果没有匹配的协议但filename符合“file”协议的url格式,那就返回“file”协议类型(在FFmpeg源码中,“文件”也被当作一种协议类型);如果都不符合则返回NULL。

形参filename:输入型参数,包含流媒体协议的名称信息。比如RTP对应的filename为:rtp://XXX,UDP对应的filename为:udp://XXX。

返回值:匹配到的流媒体协议类型,为URLProtocol类型。该结构体声明在libavformat/url.h中:

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_whitelist()* or ffurl_connect() for those nested protocols.*/int     (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);int     (*url_accept)(URLContext *s, URLContext **c);int     (*url_handshake)(URLContext *c);/*** 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);int (*url_read_pause)(void *urlcontext, int pause);int64_t (*url_read_seek)(void *urlcontext, 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_get_short_seek)(URLContext *h);int (*url_shutdown)(URLContext *h, int flags);const AVClass *priv_data_class;int priv_data_size;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;

URLProtocol是FFmpeg协议解析器的注册结构体。每个流媒体协议都有一个与之对应的URLProtocol结构,其定义了协议的名称、流媒体url的打开、读取、写入等操作的函数指针。

比如RTP这种流媒体协议,其对应的URLProtocol结构为:

const URLProtocol ff_rtp_protocol = {.name                      = "rtp",.url_open                  = rtp_open,.url_read                  = rtp_read,.url_write                 = rtp_write,.url_close                 = rtp_close,.url_get_file_handle       = rtp_get_file_handle,.url_get_multi_file_handle = rtp_get_multi_file_handle,.priv_data_size            = sizeof(RTPContext),.flags                     = URL_PROTOCOL_FLAG_NETWORK,.priv_data_class           = &rtp_class,
};

“file”协议对应的URLProtocol结构为:

const 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,.url_delete          = file_delete,.url_move            = file_move,.priv_data_size      = sizeof(FileContext),.priv_data_class     = &file_class,.url_open_dir        = file_open_dir,.url_read_dir        = file_read_dir,.url_close_dir       = file_close_dir,.default_whitelist   = "file,crypto,data"
};

“rtmp”协议对应的URLProtocol结构为:

const URLProtocol ff_librtmp_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(LibRTMPContext),.priv_data_class     = &librtmp_class,.flags               = URL_PROTOCOL_FLAG_NETWORK,
};

二、url_find_protocol函数的内部实现分析

url_find_protocol函数中首先通过下面代码块将形参filename中的流媒体协议名称拷贝到数组proto_str中。如果没有匹配的协议但filename符合“file”协议的url格式,那就拷贝“file”,表示是“文件”协议类型:

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

拷贝流媒体协议名称到数组proto_nested中。关于av_strlcpy函数的用法可以参考:《FFmpeg源码:av_strlcpy函数分析》:

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

返回指向全局数组url_protocols开头的二级指针:

    protocols = ffurl_get_protocols(NULL, NULL);if (!protocols)return NULL;

ffurl_get_protocols函数的定义如下。可以看到其内部遍历了全局数组url_protocols:

const URLProtocol **ffurl_get_protocols(const char *whitelist,const char *blacklist)
{const URLProtocol **ret;int i, ret_idx = 0;ret = av_calloc(FF_ARRAY_ELEMS(url_protocols), sizeof(*ret));if (!ret)return NULL;for (i = 0; url_protocols[i]; i++) {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;
}

全局数组url_protocols定义在libavformat/protocol_list.c中,该数组中的每个元素都对应一种URLProtocol结构,也就是说数组中的每个元素都对应一种流媒体协议(RTP、FILE、RTMP等):

static const URLProtocol * const url_protocols[] = {&ff_async_protocol,&ff_cache_protocol,&ff_concat_protocol,&ff_concatf_protocol,&ff_crypto_protocol,&ff_data_protocol,&ff_fd_protocol,&ff_ffrtmphttp_protocol,&ff_file_protocol,&ff_ftp_protocol,&ff_gopher_protocol,&ff_hls_protocol,&ff_http_protocol,&ff_httpproxy_protocol,&ff_icecast_protocol,&ff_mmsh_protocol,&ff_mmst_protocol,&ff_md5_protocol,&ff_pipe_protocol,&ff_prompeg_protocol,&ff_rtmp_protocol,&ff_rtmpt_protocol,&ff_rtp_protocol,&ff_srtp_protocol,&ff_subfile_protocol,&ff_tee_protocol,&ff_tcp_protocol,&ff_udp_protocol,&ff_udplite_protocol,&ff_unix_protocol,NULL };

url_find_protocol函数中,通过遍历全局数组url_protocols,比较协议名称,查找出该流媒体协议匹配的URLProtocol结构。如果查找到了,那就赋值给指针up,作为url_find_protocol函数的返回值返回:

    for (i = 0; protocols[i]; i++) {const URLProtocol *up = protocols[i];if (!strcmp(proto_str, up->name)) {av_freep(&protocols);return up;}if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&!strcmp(proto_nested, up->name)) {av_freep(&protocols);return up;}}

如果查找不到匹配的流媒体协议,那就打印警报日志,返回NULL:

    av_freep(&protocols);if (av_strstart(filename, "https:", NULL) || av_strstart(filename, "tls:", NULL))av_log(NULL, AV_LOG_WARNING, "https protocol not found, recompile FFmpeg with ""openssl, gnutls or securetransport enabled.\n");return NULL;

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com