本文主要分析ijk中hook协议的实现流程和具体实现来进行分析。
1.ijkhttphook的使用
先看一下ijk中ijkhttphook的使用:
断网自动重新连接,url前接上ijkhttphook:,如
String url = "ijkhttphook:http://videocdn.eebbk.net/01cc6382b142217dad89516a19a4b299.mp4";
然后设置自定义的OnNativeInvokeListener接口。
((IjkMediaPlayer)mediaPlayer).setOnNativeInvokeListener(new IjkMediaPlayer.OnNativeInvokeListener() {
@Override
public boolean onNativeInvoke(int i, Bundle bundle) {
return true;
}
});
2.hook协议的实现方式
下面分析断网重连等实现方式:
先看一下ijkmediaplayer.java中 OnNativeInvokeListener接口的实现,如果没有自定义OnNativeListenr接口的话,从下面代码中我们可以看到则默认返回true。
private OnNativeInvokeListener mOnNativeInvokeListener;
public void setOnNativeInvokeListener(OnNativeInvokeListener listener) {
mOnNativeInvokeListener = listener;
}
public interface OnNativeInvokeListener {
int CTRL_WILL_TCP_OPEN = 0x20001; // NO ARGS
int CTRL_DID_TCP_OPEN = 0x20002; // ARG_ERROR, ARG_FAMILIY, ARG_IP, ARG_PORT, ARG_FD
int CTRL_WILL_HTTP_OPEN = 0x20003; // ARG_URL, ARG_SEGMENT_INDEX, ARG_RETRY_COUNTER
int CTRL_WILL_LIVE_OPEN = 0x20005; // ARG_URL, ARG_RETRY_COUNTER
int CTRL_WILL_CONCAT_RESOLVE_SEGMENT = 0x20007; // ARG_URL, ARG_SEGMENT_INDEX, ARG_RETRY_COUNTER
int EVENT_WILL_HTTP_OPEN = 0x1; // ARG_URL
int EVENT_DID_HTTP_OPEN = 0x2; // ARG_URL, ARG_ERROR, ARG_HTTP_CODE
int EVENT_WILL_HTTP_SEEK = 0x3; // ARG_URL, ARG_OFFSET
int EVENT_DID_HTTP_SEEK = 0x4; // ARG_URL, ARG_OFFSET, ARG_ERROR, ARG_HTTP_CODE, ARG_FILE_SIZE
String ARG_URL = "url";
String ARG_SEGMENT_INDEX = "segment_index";
String ARG_RETRY_COUNTER = "retry_counter";
String ARG_ERROR = "error";
String ARG_FAMILIY = "family";
String ARG_IP = "ip";
String ARG_PORT = "port";
String ARG_FD = "fd";
String ARG_OFFSET = "offset";
String ARG_HTTP_CODE = "http_code";
String ARG_FILE_SIZE = "file_size";
/*
* @return true if invoke is handled
* @throws Exception on any error
*/
boolean onNativeInvoke(int what, Bundle args);
}
//默认的实现方法,供native层调用
@CalledByNative
private static boolean onNativeInvoke(Object weakThiz, int what, Bundle args) {
DebugLog.ifmt(TAG, "onNativeInvoke %d", what);
if (weakThiz == null || !(weakThiz instanceof WeakReference<?>))
throw new IllegalStateException("<null weakThiz>.onNativeInvoke()");
@SuppressWarnings("unchecked")
WeakReference<IjkMediaPlayer> weakPlayer = (WeakReference<IjkMediaPlayer>) weakThiz;
IjkMediaPlayer player = weakPlayer.get();
if (player == null)
throw new IllegalStateException("<null weakPlayer>.onNativeInvoke()");
//如果实现了自己的mOnNativeInvokeListener
OnNativeInvokeListener listener = player.mOnNativeInvokeListener;
//如果自定义的onNativeInvokeListenr返回值是true的话,直接返回true
if (listener != null && listener.onNativeInvoke(what, args))
return true;
//如果没有实现自定义OnNativeListenr接口的话,则执行下面流程
switch (what) {
case OnNativeInvokeListener.CTRL_WILL_CONCAT_RESOLVE_SEGMENT: {
OnControlMessageListener onControlMessageListener = player.mOnControlMessageListener;
if (onControlMessageListener == null)
return false;
int segmentIndex = args.getInt(OnNativeInvokeListener.ARG_SEGMENT_INDEX, -1);
if (segmentIndex < 0)
throw new InvalidParameterException("onNativeInvoke(invalid segment index)");
String newUrl = onControlMessageListener.onControlResolveSegmentUrl(segmentIndex);
if (newUrl == null)
throw new RuntimeException(new IOException("onNativeInvoke() = <NULL newUrl>"));
args.putString(OnNativeInvokeListener.ARG_URL, newUrl);
return true;
}
default:
return false;
}
}
那么这里是从哪里开始初始化的呢?我们看下ijkplayer_jni.c文件下函数IjkMediaPlayer_native_setup
static void
IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
MPTRACE("%s\n", __func__);
IjkMediaPlayer *mp = ijkmp_android_create(message_loop);
JNI_CHECK_GOTO(mp, env, "java/lang/OutOfMemoryError", "mpjni: native_setup: ijkmp_create() failed", LABEL_RETURN);
jni_set_media_player(env, thiz, mp);
ijkmp_set_weak_thiz(mp, (*env)->NewGlobalRef(env, weak_this));
//注入
ijkmp_set_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
ijkmp_set_ijkio_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));
LABEL_RETURN:
ijkmp_dec_ref_p(&mp);
}
跟踪ijkmp_set_i