赞
踩
FFmpeg实现http、https这些标准协议,但是要播放加密视频怎么办呢?ijkplayer在FFmpeg的libavformat模块进行扩展ijkio、ijklongurl、ijktcphook、ijkhttphook,我们也可以在这个基础上,自定义协议来进行解密播放。主要基于URLProtocol和AVClass进行扩展,实现protocol对应的方法。
URLProtocol的结构体如下:
- typedef struct URLProtocol {
- const char *name;
- int (*url_open)(URLContext *h, const char *url, int flags);
- int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
- int (*url_accept)(URLContext *s, URLContext **c);
- int (*url_handshake)(URLContext *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 priv_data_size;
- const AVClass *priv_data_class;
- const char *default_whitelist;
- } URLProtocol;
FFmpeg实现的标准协议有包括http、https、hls、tcp、rtmp等,如下图所示:
新建一个源文件ijkdecrypt.c放在libavformat,实现ijkdecrypt_open、ijkdecrypt_read、ijkdecrypt_seek、ijkdecrypt_close等方法,然后把方法注册到URLProtocol:
- static int ijkdecrypt_open(URLContext *h) {
- return decrypt_open(h);
- }
- static int ijkdecrypt_read(URLContext *h, unsigned char *buf, int size) {
- return decrypt_read(h, buf, size);
- }
- static int64_t decrypt_seek(URLContext *h, int64_t offset, int whence) {
- return decrypt_seek(h, offset, whence);
- }
- static int decrypt_close(URLContext *h) {
- return decrypt_close(h);
- }
-
- static const AVClass ijkio_context_class = {
- .class_name = "IjkDecrypt",
- .item_name = av_default_item_name,
- .option = options,
- .version = LIBAVUTIL_VERSION_INT,
- };
-
- URLProtocol ijkimp_ff_ijkio_protocol = {
- .name = "ijkdecrypt",
- .url_open2 = ijkdecrypt_open,
- .url_read = ijkdecrypt_read,
- .url_seek = ijkdecrypt_seek,
- .url_close = ijkdecrypt_close,
- .priv_data_size = sizeof(Context),
- .priv_data_class = &ijkio_context_class,
- };
修该libavformat/protocols.c,添加自定义协议并声明为全局变量:
extern const URLProtocol ff_ijkdecrypt_protocol;
在ffbuild/config.mak会自动生成CONFIG_FF_IJKDECRYPT_PROTOCOL
在libavformat/makefile添加依赖文件:
OBJS-$(CONFIG_FF_IJKDECRYPT_PROTOCOL) += ijkdecrypt.o
在libavformat/ijkutils.c添加dummy的ijkdecrypt:
IJK_DUMMY_PROTOCOL(ijkdecrypt);
dummy过程是生成AVClass和URLProtocol:
- #define IJK_DUMMY_PROTOCOL(x) \
- IJK_FF_PROTOCOL(x); \
- static const AVClass ijk_##x##_context_class = { \
- .class_name = #x, \
- .item_name = av_default_item_name, \
- .version = LIBAVUTIL_VERSION_INT, \
- }; \
- \
- URLProtocol ff_##x##_protocol = { \
- .name = #x, \
- .url_open2 = ijkdummy_open, \
- .priv_data_size = 1, \
- .priv_data_class = &ijk_##x##_context_class, \
- };
在allformats.c调用ijkav_register_all进行注册自定义协议:
- void ijkav_register_all(void)
- {
- av_register_all();
- /* protocols */
- #ifdef __ANDROID__
- IJK_REGISTER_PROTOCOL(ijkmediadatasource);
- #endif
- IJK_REGISTER_PROTOCOL(ijkio);
- IJK_REGISTER_PROTOCOL(async);
- IJK_REGISTER_PROTOCOL(ijklongurl);
- IJK_REGISTER_PROTOCOL(ijktcphook);
- IJK_REGISTER_PROTOCOL(ijkhttphook);
- IJK_REGISTER_PROTOCOL(ijksegment);
- /* demuxers */
- IJK_REGISTER_DEMUXER(ijklivehook);
- IJK_REGISTER_DEMUXER(ijklas);
- }
IJK_REGISTER_PROTOCOL()对应的宏定义:
- #define IJK_REGISTER_PROTOCOL(x)
- { \
- extern URLProtocol ijkmp_ff_##x##_protocol; \
- int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size); \
- ijkav_register_##x##_protocol(&ijkimp_ff_##x##_protocol, sizeof(URLProtocol));\
- }
其中ijkav_register_##x##_protocol的宏定义如下:
- int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size)
- {
- if (protocol_size != sizeof(URLProtocol)) {
- av_log(NULL, AV_LOG_ERROR, "ABI mismatch.\n");
- return -1;
- }
- memcpy(&ff_##x##_protocol, protocol, protocol_size);
- return 0;
- }
首先在ijkioprotocol.c声明自定义协议:
extern IjkURLProtocol ijkio_decrypt_protocol;
然后对scheme进行拦截,把自定义协议赋值给IjkURLProtocol:
- int ijkio_alloc_url(IjkURLContext **ph, const char *url) {
- if (!ph) {
- return -1;
- }
- IjkURLContext *h = NULL;
- if (!strncmp(url, "httphook:", strlen("httphook:"))) {
- h = (IjkURLContext *)calloc(1, sizeof(IjkURLContext));
- h->prot = &ijkio_httphook_protocol;
- h->priv_data = calloc(1, ijkio_httphook_protocol.priv_data_size);
- } else if (!strncmp(url, "decrypt:", strlen("decrypt:"))) {
- h = (IjkURLContext *)calloc(1, sizeof(IjkURLContext));
- h->prot = &ijkio_decrypt_protocol;
- h->priv_data = calloc(1, ijkio_decrypt_protocol.priv_data_size);
- } else {
- return -1;
- }
- *ph = h;
- return 0;
- }
在avformat_open_input时,会初始化input、打开avio、根据scheme查找对应协议,完整调用路径为init_input->avio_open2->ffurl_open->ffurl_alloc->url_find_protocol。在avio.c的查找协议过程为:
- static const struct URLProtocol *url_find_protocol(const char *filename) {
- ......
- 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);
- return NULL;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。