IjkMediaPlayer.java
public static void loadLibrariesOnce(IjkLibLoader libLoader) {
synchronized (IjkMediaPlayer.class) {
if (!mIsLibLoaded) {
if (libLoader == null)
libLoader = sLocalLibLoader;
libLoader.loadLibrary("ijkffmpeg");
libLoader.loadLibrary("ijksdl");
libLoader.loadLibrary("ijkplayer");
mIsLibLoaded = true;
}
}
}
libLoader.loadLibrary("ijkplayer");
我们知道动态注册最后走到了
ijkplayer/android/ijkplayer_jni.c
#define JNI_CLASS_IJKPLAYER "tv/danmaku/ijk/media/player/IjkMediaPlayer"
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
g_jvm = vm;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
默认方式初始化互斥锁
pthread_mutex_init(&g_clazz.mutex, NULL );
// 动态注册java层需要的函数
IJK_FIND_JAVA_CLASS(env, g_clazz.clazz, JNI_CLASS_IJKPLAYER);
(*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods) );
ijkmp_global_init();
ijkmp_global_set_inject_callback(inject_callback);
FFmpegApi_global_init(env); 注册一下ffmpeg的api给java层使用
return JNI_VERSION_1_4;
}
我们看下ijkmp_global_init
void ijkmp_global_init()
{
ffp_global_init();
}
void ffp_global_init()
{
if (g_ffmpeg_global_inited)
return;
ALOGD("ijkmediaplayer version : %s", ijkmp_version());
/* register all codecs, demux and protocols */
avcodec_register_all(); 注册解码器
#if CONFIG_AVDEVICE
avdevice_register_all(); 注册输入输出设备,默认CONFIG_AVDEVICE没有打开
#endif
#if CONFIG_AVFILTER
avfilter_register_all(); 注册滤镜,滤镜默认也是关闭
#endif
av_register_all(); 初始化libavformat,和所有的解封装、封装、协议。
ijkav_register_all(); 这个方法目前看起来无用,宏转化的函数是空函数
avformat_network_init(); 初始化网络
av_lockmgr_register(lockmgr);
//https://blog.csdn.net/sxc1989/article/details/54934062,注册一个锁管理,在avcodec_open2,avcodec_close时使用
av_log_set_callback(ffp_log_callback_brief); /日志自定义
av_init_packet(&flush_pkt); //初始化flush_pkt
flush_pkt.data = (uint8_t *)&flush_pkt; //flush_pkt的特点,就是自己数据是自身。具体用户可以看https://blog.csdn.net/qq_15255121/article/details/121927094
g_ffmpeg_global_inited = true; //定义已经初始化了,避免二次初始化
}
可以看到初始化就是把ffmpeg需要的注册。这里我们重点看ijkav_register_all
void ijkav_register_all(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;
av_register_all(); //通过上面的代码可以看到这个函数已经调用过了,这里多余。但是也没有影响,av_register_all内部做了初始化判断
/* protocols */
av_log(NULL, AV_LOG_INFO, "===== custom modules begin =====\n");
#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);
av_log(NULL, AV_LOG_INFO, "===== custom modules end =====\n");
}
#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)); \
}
就相当于
那么 IJK_REGISTER_PROTOCOL(ijkio);
就相当于与下面的代码
extern URLProtocol ijkimp_ff_ijkio_protocol;
int ijkav_register_ijkio_protocol(URLProtocol *protocol, int protocol_size); \
ijkav_register_ijkio_protocol(&ijkimp_ff_ijkio_protocol, sizeof(URLProtocol));;
这段代码应该是在ijk设计当中保留的,目前这些函数没有被定义。那么可以给我们启示,ijk的协议注册可以放在这里。
我们看下 ijkmp_global_set_inject_callback(inject_callback);
void ijkmp_global_set_inject_callback(ijk_inject_callback cb)
{
ffp_global_set_inject_callback(cb);
}
void ffp_global_set_inject_callback(ijk_inject_callback cb)
{
s_inject_callback = cb;
}
static int
inject_callback(void *opaque, int what, void *data, size_t data_size)
{
JNIEnv *env = NULL;
jobject jbundle = NULL;
int ret = -1;
SDL_JNI_SetupThreadEnv(&env);
jobject weak_thiz = (jobject) opaque;
if (weak_thiz == NULL )
goto fail;
switch (what) {
case AVAPP_CTRL_WILL_HTTP_OPEN:
case AVAPP_CTRL_WILL_LIVE_OPEN:
case AVAPP_CTRL_WILL_CONCAT_SEGMENT_OPEN: {
AVAppIOControl *real_data = (AVAppIOControl *)data;
real_data->is_handled = 0;
jbundle = J4AC_Bundle__Bundle__catchAll(env);
if (!jbundle) {
ALOGE("%s: J4AC_Bundle__Bundle__catchAll failed for case %d\n", __func__, what);
goto fail;
}
J4AC_Bundle__putString__withCString__catchAll(env, jbundle, "url", real_data->url);
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "segment_index", real_data->segment_index);
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "retry_counter", real_data->retry_counter);
real_data->is_handled = J4AC_IjkMediaPlayer__onNativeInvoke(env, weak_thiz, what, jbundle);
if (J4A_ExceptionCheck__catchAll(env)) {
goto fail;
}
J4AC_Bundle__getString__withCString__asCBuffer(env, jbundle, "url", real_data->url, sizeof(real_data->url));
if (J4A_ExceptionCheck__catchAll(env)) {
goto fail;
}
ret = 0;
break;
}
case AVAPP_EVENT_WILL_HTTP_OPEN:
case AVAPP_EVENT_DID_HTTP_OPEN:
case AVAPP_EVENT_WILL_HTTP_SEEK:
case AVAPP_EVENT_DID_HTTP_SEEK: {
AVAppHttpEvent *real_data = (AVAppHttpEvent *) data;
jbundle = J4AC_Bundle__Bundle__catchAll(env);
if (!jbundle) {
ALOGE("%s: J4AC_Bundle__Bundle__catchAll failed for case %d\n", __func__, what);
goto fail;
}
J4AC_Bundle__putString__withCString__catchAll(env, jbundle, "url", real_data->url);
J4AC_Bundle__putLong__withCString__catchAll(env, jbundle, "offset", real_data->offset);
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "error", real_data->error);
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "http_code", real_data->http_code);
J4AC_Bundle__putLong__withCString__catchAll(env, jbundle, "file_size", real_data->filesize);
J4AC_IjkMediaPlayer__onNativeInvoke(env, weak_thiz, what, jbundle);
if (J4A_ExceptionCheck__catchAll(env))
goto fail;
ret = 0;
break;
}
case AVAPP_CTRL_DID_TCP_OPEN:
case AVAPP_CTRL_WILL_TCP_OPEN: {
AVAppTcpIOControl *real_data = (AVAppTcpIOControl *)data;
jbundle = J4AC_Bundle__Bundle__catchAll(env);
if (!jbundle) {
ALOGE("%s: J4AC_Bundle__Bundle__catchAll failed for case %d\n", __func__, what);
goto fail;
}
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "error", real_data->error);
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "family", real_data->family);
J4AC_Bundle__putString__withCString__catchAll(env, jbundle, "ip", real_data->ip);
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "port", real_data->port);
J4AC_Bundle__putInt__withCString__catchAll(env, jbundle, "fd", real_data->fd);
J4AC_IjkMediaPlayer__onNativeInvoke(env, weak_thiz, what, jbundle);
if (J4A_ExceptionCheck__catchAll(env))
goto fail;
ret = 0;
break;
}
default: {
ret = 0;
}
}
fail:
SDL_JNI_DeleteLocalRefP(env, &jbundle);
return ret;
}
通过代码可以知道这个函数使用把网络相关的信息放入bundle,然后回调到java层。