ijkplayer添加私有协议(六)

目录

目的

私有协议的工作流程

协议注册过程

初始化AVIOFormat函数调用关系

私有协议实现过程

私有协议接口分析

 

调试过程记录


目的

       因为某些公司可能不想用通用的拉流协议(如http),会自定义一种新的协议比如起名ijkbuffersource://这种url来拉流播放。

私有协议的工作流程

协议注册过程

 
#define IJK_REGISTER_PROTOCOL(x)                                        \
    {                                                                   \
        extern URLProtocol ijkimp_ff_##x##_protocol;                        \
        int ijkav_register_##x##_protocol(URLProtocol *protocol, int protocol_size);\
        ijkav_register_##x##_protocol(&ijkimp_ff_##x##_protocol, sizeof(URLProtocol));  \
    }

我们以ijkbuffersource为例,则它等效于 

ijkav_register_ijkbuffersource_protocol(&ijkimp_ff_ijkbuffersource_protocol, sizeof(URLProtocol));

ijkimp_ff_ijkbuffersource_protocol 实现私有协议的open, read, write,close,init等方法

 


URLProtocol ijkimp_ff_ijkbuffersource_protocol = {
        .name                = "ijkbuffersource",
        .url_open2           = ijkbuffer_open,
        .url_read            = ijkbuffer_read,
        .url_seek            = ijkbuffer_seek,
        .url_close           = ijkbuffer_close,
        .priv_data_size      = sizeof(Context),
        .priv_data_class     = &ijkbuffersource_context_class,
};
IJK_REGISTER_DEMUXER解封装分析
#define IJK_REGISTER_DEMUXER(x)                                         \
    {                                                                   \
        extern AVInputFormat ijkff_##x##_demuxer;                       \
        ijkav_register_input_format(&ijkff_##x##_demuxer);              \
    }

从上面这段代码我们可以看出,真正注册的函数是av_register_input_format(&ff_aac_demuxer),那我就看看这个和函数的作用,查看一下av_register_input_format()的代码:

void av_register_input_format(AVInputFormat *format)
{
    AVInputFormat **p = last_iformat;

    // Note, format could be added after the first 2 checks but that implies that *p is no longer NULL
    while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
        p = &(*p)->next;

    if (!format->next)
        last_iformat = &format->next;
}

这段代码是比较容易理解的,首先先提一点,first_iformat是个什么东东呢?其实它是Input Format链表的头部地址,是一个全局静态变量,定义如下:

/** head of registered input format linked list */
static AVInputFormat *first_iformat = NULL;

由此我们可以分析出av_register_input_format()的含义,一句话概括就是:遍历链表并把当前的Input Format加到链表的尾部。
至此REGISTER_DEMUXER  (X)分析完毕。
 

初始化AVIOFormat函数调用关系

 

私有协议实现过程

step1, 在实现私有协议的open, read, write,close,init等方法。及URLProtocol结构体对象如ijkimp_ff_ijkbuffersource_protocol 于单个.c文件中,如buffersource.c

static const AVClass ijkbuffersource_context_class = {
        .class_name = "IjkBufferSource",
        .item_name  = av_default_item_name,
        .option     = options,
        .version    = LIBAVUTIL_VERSION_INT,
};

URLProtocol ijkimp_ff_ijkbuffersource_protocol = {
        .name                = "ijkbuffersource",
        .url_open2           = ijkbuffer_open,
        .url_read            = ijkbuffer_read,
        .url_seek            = ijkbuffer_seek,
        .url_close           = ijkbuffer_close,
        .priv_data_size      = sizeof(Context),
        .priv_data_class     = &ijkbuffersource_context_class,
};

其中ijkbuffer_open,ijkbuffer_read, ijkbuffer_seek, ijkbuffer_close为要实现的私有协议内容

step2, 将实现好的私有协议文件(buffersource.c文件)加入libavformat目录

step3, 修改ffmpeg/libavformat/protocols.c, 添加自定义网络协议URLProtocol全局变量声明

ffbuild\config.mak(这文件是自动生成,不用修改)

CONFIG_IJKBUFFERSOURCE_PROTOCOL=yes

libavformat\protocols.c

extern const URLProtocol ff_ijkbuffersource_protocol;

libavformat/ijkutils.c

IJK_DUMMY_PROTOCOL(ijkbuffersource);

libavformat\protocol_list.c(这文件是自动生成,不用修改)

&ff_ijkbuffersource_protocol,

 libavformat\Makefile

OBJS-$(CONFIG_IJKBUFFERSOURCE_PROTOCOL)             += ijkbuffersource.o

私有协议接口分析

typedef struct Context {
    AVClass        *class;

    /* options */
    int64_t         logical_pos;//当前读取的指针位置
    int64_t         logical_size;//总文件大小

    int64_t         media_data_source_ptr;
    jobject         media_data_source;//id
    jbyteArray      jbuffer;//data buffer
    int             jbuffer_capacity;//一次读取的byte,32768
} Context;
/*
 * 在 ijkmds_open  中,只需要把 intptr_t 的变量转换成 jobject 就行,不必实际去打开某个文件。 ijkmds_read 、 ijkmds_seek
 * 函数通过 J4A 去调用  IMediaDataSource 接口的 readAt 方法。 readAt 方法多了 pos 参数,所以 read 和 seek 都可以通过 readAt
 * 实现, ijkmds_close  调用 IMediaDataSource 接口的 close 方法,并释放 NDK 环境中的 jobject 全局引用。
 */
static int ijkmds_open(URLContext *h, const char *arg, int flags, AVDictionary **options)
{
    Context *c = h->priv_data;
    JNIEnv *env = NULL;
    jobject media_data_source = NULL;
    char *final = NULL;
    av_log(h, AV_LOG_ERROR, "Rapid ijkmds_open arg = %s", &arg);//"ijkmediadatasource:10598"
    av_strstart(arg, "ijkmediadatasource:", &arg);//10598,这个值会变化

    media_data_source = (jobject) (intptr_t) strtoll(arg, &final, 10);//10598
    if (!media_data_source)
        return AVERROR(EINVAL);
    //SDL_JNI_SetupThreadEnv(&env)这个是干嘛用的?创建线程,得到env
    //通过SDL库创建一个线程,该函数允许传入函数指针,使得该函数在创建的线程中执行
    if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) {
        av_log(h, AV_LOG_ERROR, "%s: SDL_JNI_SetupThreadEnv: failed", __func__);
        return AVERROR(EINVAL);
    }
//直播不能读取长度,为什么要获取size?因为回调里会先加载所有buffer,然后通过offset读取偏移的buffer
    c->logical_size = J4AC_IMediaDataSource__getSize(env, media_data_source);//如果直播,可以设置为getSize为-1
    if (J4A_ExceptionCheck__catchAll(env)) {
        return AVERROR(EINVAL);
    } else if (c->logical_size < 0) {
        h->is_streamed = 1;//直播流 ==1,要不会播放不出来,uc->is_streamed     = 0; /* default = not streamed */
        c->logical_size = -1;
    }

    c->media_data_source = (*env)->NewGlobalRef(env, media_data_source);
    if (J4A_ExceptionCheck__catchAll(env) || !c->media_data_source) {
        return AVERROR(ENOMEM);
    }

    return 0;
}

 

调试过程记录

在ijkbds_seek添加

    c->buffer = jbuffer_grow(h, 0);
    ret = ijk_mediasource_readAt(c->logical_pos, c->buffer, 0, 0);
    if (ret < 0)
        return AVERROR_EOF;
    else if (ret == 0)
        return AVERROR(EAGAIN);

会出现两种情况

写缓冲buffer为512K,播放阿里云视频会播放不了,而MFC的影片可以播放正常

而去掉ijkbds_seek这部分代码,阿里云播放正常,seek也正常,但是mfc的影片播放不了

 

就会出现

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值