本來這個(gè)過程我是不大想寫初始化的過程,覺得網(wǎng)上已經(jīng)有不少文章來分析了。但是在前面的整個(gè)分析過程中,暴露了自己對一些問題理解還不夠透徹,因此有必要做一次。
首先是java層:
private void initPlayer(IjkLibLoader libLoader) {
loadLibrariesOnce(libLoader);
initNativeOnce();
Looper looper;
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
/*
* Native setup requires a weak reference to our object. It's easier to
* create it here than in C++.
*/
native_setup(new WeakReference<IjkMediaPlayer>(this));
}
其實(shí)就2個(gè)事情,一個(gè)是loadLibrariesOnce,一個(gè)是initNativeOnce。前者的代碼就不貼了,就是loadLibrary3個(gè)so,分別是ijkffmpeg、ijksdl和ijkplayer。ffmpeg管協(xié)議和編解碼,sdl管渲染顯示,ijkplayer管理播放器。每次調(diào)用loadLibrary都會走到每個(gè)so的JNI_OnLoad函數(shù),也就是說這3個(gè)so的最開始初始化都在JNI_OnLoad這個(gè)函數(shù)內(nèi)處理?;仡^我們再看;后者的initNativeOnce里面實(shí)際上走的是native_init。這個(gè)對應(yīng)的是jni的函數(shù)IjkMediaPlayer_native_init。在ijkplayer_jni.c中:
static void
IjkMediaPlayer_native_init(JNIEnv *env)
{
MPTRACE("%s\n", __func__);
}
什么都沒干,對吧。
回來,看看JNI_OnLoad都干了什么:
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 );
// FindClass returns LocalReference
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);
return JNI_VERSION_1_4;
}
前面都是通用的一些做法,主要是注冊函數(shù)表,用來在java層能夠調(diào)用c層的函數(shù)。然后是ijkmp_global_init,這個(gè)最后會走到ffp_global_init:
void ffp_global_init()
{
if (g_ffmpeg_global_inited)
return;
/* register all codecs, demux and protocols */
avcodec_register_all();
#if CONFIG_AVDEVICE
avdevice_register_all();
#endif
#if CONFIG_AVFILTER
avfilter_register_all();
#endif
av_register_all();
ijkav_register_all();
avformat_network_init();
av_lockmgr_register(lockmgr);
av_log_set_callback(ffp_log_callback_brief);
av_init_packet(&flush_pkt);
flush_pkt.data = (uint8_t *)&flush_pkt;
g_ffmpeg_global_inited = true;
}
基本上以ffmpeg的初始化內(nèi)容居多,av開頭的應(yīng)該都是。注冊解碼器,然后協(xié)議的注冊。我們看ijkav_register_all:
void ijkav_register_all(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;
av_register_all();
/* protocols */
av_log(NULL, AV_LOG_INFO, "===== custom modules begin =====\n");
#ifdef __ANDROID__
IJK_REGISTER_PROTOCOL(ijkmediadatasource);
#endif
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);
av_log(NULL, AV_LOG_INFO, "===== custom modules end =====\n");
}
基本上都是為了支持網(wǎng)絡(luò)傳輸?shù)膮f(xié)議注冊。然后是avformat_network_init,ffmpeg的網(wǎng)絡(luò)初始化。最后到達(dá)ff_network_init,里面就是個(gè)WSAStartup。
回來看這么多協(xié)議的注冊,先看下這個(gè)宏:
#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)); \
}
URLProtocol結(jié)構(gòu)是個(gè)關(guān)鍵。那么這個(gè)結(jié)構(gòu)的填充靠什么呢?看宏的調(diào)用,找到extern后面的部分,搜索下,原來在不少文件里都有,例如ijkurlhook.c文件中:
URLProtocol ijkimp_ff_ijktcphook_protocol = {
.name = "ijktcphook",
.url_open2 = ijktcphook_open,
.url_read = ijkurlhook_read,
.url_write = ijkurlhook_write,
.url_close = ijkurlhook_close,
.priv_data_size = sizeof(Context),
.priv_data_class = &ijktcphook_context_class,
};
這里已經(jīng)規(guī)定了打開和寫入關(guān)閉等的函數(shù)。這下子與基礎(chǔ)協(xié)議對應(yīng)的各項(xiàng)操作算是找到了。我們來看看不一樣的live的處理:
#define IJK_REGISTER_DEMUXER(x) \
{ \
extern AVInputFormat ijkff_##x##_demuxer; \
ijkav_register_input_format(&ijkff_##x##_demuxer); \
}
然后會定位到ijklivehook.c文件中的
AVInputFormat ijkff_ijklivehook_demuxer = {
.name = "ijklivehook",
.long_name = "Live Hook Controller",
.flags = AVFMT_NOFILE | AVFMT_TS_DISCONT,
.priv_data_size = sizeof(Context),
.read_probe = ijklivehook_probe,
.read_header2 = ijklivehook_read_header,
.read_packet = ijklivehook_read_packet,
.read_close = ijklivehook_read_close,
.priv_class = &ijklivehook_class,
};
往下看,以ijklivehook_read_header為例,可以看到內(nèi)部有url的判斷,區(qū)分rtmp和rtsp,這下子清楚了吧。
簡單總結(jié)一下,就是通過URLProtocol這個(gè)結(jié)構(gòu)來規(guī)范化所有的協(xié)議,名稱和操作函數(shù)都在這里定義。
回到ffp_global_init,下面進(jìn)行到了av_init_packet。這里插一下一個(gè)數(shù)據(jù)結(jié)構(gòu)AVPacket。這個(gè)是存儲壓縮編碼數(shù)據(jù)相關(guān)信息的結(jié)構(gòu)體。
typedef struct AVPacket {
/**
* A reference to the reference-counted buffer where the packet data is
* stored.
* May be NULL, then the packet data is not reference-counted.
*/
AVBufferRef *buf;
/**
* Presentation timestamp in AVStream->time_base units; the time at which
* the decompressed packet will be presented to the user.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
* pts MUST be larger or equal to dts as presentation cannot happen before
* decompression, unless one wants to view hex dumps. Some formats misuse
* the terms dts and pts/cts to mean something different. Such timestamps
* must be converted to true pts/dts before they are stored in AVPacket.
*/
int64_t pts;
/**
* Decompression timestamp in AVStream->time_base units; the time at which
* the packet is decompressed.
* Can be AV_NOPTS_VALUE if it is not stored in the file.
*/
int64_t dts;
uint8_t *data;
int size;
int stream_index;
/**
* A combination of AV_PKT_FLAG values
*/
int flags;
/**
* Additional packet data that can be provided by the container.
* Packet can contain several types of side information.
*/
AVPacketSideData *side_data;
int side_data_elems;
/**
* Duration of this packet in AVStream->time_base units, 0 if unknown.
* Equals next_pts - this_pts in presentation order.
*/
int64_t duration;
int64_t pos; ///< byte position in stream, -1 if unknown
#if FF_API_CONVERGENCE_DURATION
/**
* @deprecated Same as the duration field, but as int64_t. This was required
* for Matroska subtitles, whose duration values could overflow when the
* duration field was still an int.
*/
attribute_deprecated
int64_t convergence_duration;
#endif
} AVPacket;
看到了什么嗎?pts,dts,data。顯示時(shí)間戳,解碼時(shí)間戳,數(shù)據(jù)。av_init_packet就是個(gè)簡單填充,不貼代碼了?;氐絁NI_OnLoad,然后進(jìn)行的是ijkmp_global_set_inject_callback。
設(shè)置了一個(gè)回調(diào),那么看看具體回調(diào)的約定吧:
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_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;
}
簡單找個(gè)函數(shù)看下:J4AC_IjkMediaPlayer__onNativeInvoke,在java層里找到了定義:
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
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";
/*
* @return true if invoke is handled
* @throws Exception on any error
*/
boolean onNativeInvoke(int what, Bundle args);
}
@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()");
OnNativeInvokeListener listener = player.mOnNativeInvokeListener;
if (listener != null && listener.onNativeInvoke(what, args))
return true;
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;
}
}
那么可以確定,這里是注冊的回調(diào),以便通知java層。好吧,回來繼續(xù)JNI_OnLoad,就差FFmpegApi_global_init了:
#define JNI_CLASS_FFMPEG_API "tv/danmaku/ijk/media/player/ffmpeg/FFmpegApi"
......
int FFmpegApi_global_init(JNIEnv *env)
{
int ret = 0;
IJK_FIND_JAVA_CLASS(env, g_clazz.clazz, JNI_CLASS_FFMPEG_API);
(*env)->RegisterNatives(env, g_clazz.clazz, g_methods, NELEM(g_methods));
return ret;
}
按照定義,找到這個(gè)類,只有一句話:
public class FFmpegApi {
public static native String av_base64_encode(byte in[]);
}
其實(shí)就是個(gè)base64的解碼,指向ffmpeg的c函數(shù),這里進(jìn)行了注冊。
總結(jié)起來就是各種初始化,協(xié)議的、解碼器的、網(wǎng)絡(luò)的、回調(diào)上層的。
這回接著來。java層的initPlayer函數(shù)中,最后還有native_setup的調(diào)用,走的是c層的IjkMediaPlayer_native_setup。來看看他干了什么吧:
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_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));
LABEL_RETURN:
ijkmp_dec_ref_p(&mp);
}
首先創(chuàng)建播放器IjkMediaPlayer,傳入一個(gè)message_loop。繼續(xù)看ijkmp_android_create:
IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*))
{
IjkMediaPlayer *mp = ijkmp_create(msg_loop);
if (!mp)
goto fail;
mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();
if (!mp->ffplayer->vout)
goto fail;
mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer);
if (!mp->ffplayer->pipeline)
goto fail;
ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout);
return mp;
fail:
ijkmp_dec_ref_p(&mp);
return NULL;
}
好吧,往下繼續(xù)看ijkmp_create:
IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void*))
{
IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
if (!mp)
goto fail;
mp->ffplayer = ffp_create();
if (!mp->ffplayer)
goto fail;
mp->msg_loop = msg_loop;
ijkmp_inc_ref(mp);
pthread_mutex_init(&mp->mutex, NULL);
return mp;
fail:
ijkmp_destroy_p(&mp);
return NULL;
}
一上來為結(jié)構(gòu)體IjkMediaPlayer分配空間,然后填充里面的內(nèi)容,例如ffplayer和msg_loop。結(jié)構(gòu)體如下:
struct IjkMediaPlayer {
volatile int ref_count;
pthread_mutex_t mutex;
FFPlayer *ffplayer;
int (*msg_loop)(void*);
SDL_Thread *msg_thread;
SDL_Thread _msg_thread;
int mp_state;
char *data_source;
void *weak_thiz;
int restart;
int restart_from_beginning;
int seek_req;
long seek_msec;
};
ffmpeg的player和sdl,以及msg_loop,其余是狀態(tài)及seek的內(nèi)容。
回來到ijkmp_create??催@里將這個(gè)函數(shù)指針給了mp的msg_loop。然后是ijkmp_inc_ref,這里設(shè)置了mp的引用計(jì)數(shù)加1。好吧,先回來看下這個(gè)loop是什么東西,回到最初的IjkMediaPlayer_native_setup。
static int message_loop(void *arg)
{
MPTRACE("%s\n", __func__);
JNIEnv *env = NULL;
(*g_jvm)->AttachCurrentThread(g_jvm, &env, NULL );
IjkMediaPlayer *mp = (IjkMediaPlayer*) arg;
JNI_CHECK_GOTO(mp, env, NULL, "mpjni: native_message_loop: null mp", LABEL_RETURN);
message_loop_n(env, mp);
LABEL_RETURN:
ijkmp_dec_ref_p(&mp);
(*g_jvm)->DetachCurrentThread(g_jvm);
MPTRACE("message_loop exit");
return 0;
}
AttachCurrentThread為了獲取JNIEnv,然后關(guān)鍵點(diǎn)是message_loop_n:
static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp)
{
jobject weak_thiz = (jobject) ijkmp_get_weak_thiz(mp);
JNI_CHECK_GOTO(weak_thiz, env, NULL, "mpjni: message_loop_n: null weak_thiz", LABEL_RETURN);
while (1) {
AVMessage msg;
int retval = ijkmp_get_msg(mp, &msg, 1);
if (retval < 0)
break;
// block-get should never return 0
assert(retval > 0);
switch (msg.what) {
case FFP_MSG_FLUSH:
MPTRACE("FFP_MSG_FLUSH:\n");
post_event(env, weak_thiz, MEDIA_NOP, 0, 0);
break;
case FFP_MSG_ERROR:
MPTRACE("FFP_MSG_ERROR: %d\n", msg.arg1);
post_event(env, weak_thiz, MEDIA_ERROR, MEDIA_ERROR_IJK_PLAYER, msg.arg1);
break;
case FFP_MSG_PREPARED:
MPTRACE("FFP_MSG_PREPARED:\n");
post_event(env, weak_thiz, MEDIA_PREPARED, 0, 0);
break;
case FFP_MSG_COMPLETED:
MPTRACE("FFP_MSG_COMPLETED:\n");
post_event(env, weak_thiz, MEDIA_PLAYBACK_COMPLETE, 0, 0);
break;
case FFP_MSG_VIDEO_SIZE_CHANGED:
MPTRACE("FFP_MSG_VIDEO_SIZE_CHANGED: %d, %d\n", msg.arg1, msg.arg2);
post_event(env, weak_thiz, MEDIA_SET_VIDEO_SIZE, msg.arg1, msg.arg2);
break;
case FFP_MSG_SAR_CHANGED:
MPTRACE("FFP_MSG_SAR_CHANGED: %d, %d\n", msg.arg1, msg.arg2);
post_event(env, weak_thiz, MEDIA_SET_VIDEO_SAR, msg.arg1, msg.arg2);
break;
case FFP_MSG_VIDEO_RENDERING_START:
MPTRACE("FFP_MSG_VIDEO_RENDERING_START:\n");
post_event(env, weak_thiz, MEDIA_INFO, MEDIA_INFO_VIDEO_RENDERING_START, 0);
break;
case FFP_MSG_AUDIO_RENDERING_START:
MPTRACE("FFP_MSG_AUDIO_RENDERING_START:\n");
post_event(env, weak_thiz, MEDIA_INFO, MEDIA_INFO_AUDIO_RENDERING_START, 0);
break;
case FFP_MSG_VIDEO_ROTATION_CHANGED:
MPTRACE("FFP_MSG_VIDEO_ROTATION_CHANGED: %d\n", msg.arg1);
post_event(env, weak_thiz, MEDIA_INFO, MEDIA_INFO_VIDEO_ROTATION_CHANGED, msg.arg1);
break;
case FFP_MSG_BUFFERING_START:
MPTRACE("FFP_MSG_BUFFERING_START:\n");
post_event(env, weak_thiz, MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0);
break;
case FFP_MSG_BUFFERING_END:
MPTRACE("FFP_MSG_BUFFERING_END:\n");
post_event(env, weak_thiz, MEDIA_INFO, MEDIA_INFO_BUFFERING_END, 0);
break;
case FFP_MSG_BUFFERING_UPDATE:
// MPTRACE("FFP_MSG_BUFFERING_UPDATE: %d, %d", msg.arg1, msg.arg2);
post_event(env, weak_thiz, MEDIA_BUFFERING_UPDATE, msg.arg1, msg.arg2);
break;
case FFP_MSG_BUFFERING_BYTES_UPDATE:
break;
case FFP_MSG_BUFFERING_TIME_UPDATE:
break;
case FFP_MSG_SEEK_COMPLETE:
MPTRACE("FFP_MSG_SEEK_COMPLETE:\n");
post_event(env, weak_thiz, MEDIA_SEEK_COMPLETE, 0, 0);
break;
case FFP_MSG_PLAYBACK_STATE_CHANGED:
break;
case FFP_MSG_TIMED_TEXT:
if (msg.obj) {
jstring text = (*env)->NewStringUTF(env, (char *)msg.obj);
post_event2(env, weak_thiz, MEDIA_TIMED_TEXT, 0, 0, text);
J4A_DeleteLocalRef__p(env, &text);
}
else {
post_event2(env, weak_thiz, MEDIA_TIMED_TEXT, 0, 0, NULL);
}
break;
default:
ALOGE("unknown FFP_MSG_xxx(%d)\n", msg.what);
break;
}
msg_free_res(&msg);
}
LABEL_RETURN:
;
}
這明顯是個(gè)事件處理loop,關(guān)鍵是post_event,里面就一句話:J4AC_IjkMediaPlayer__postEventFromNative(env, weak_this, what, arg1, arg2, NULL);最后追到J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer__postEventFromNative,里面是(*env)->CallStaticVoidMethod(env, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id, class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative, weakThiz, what, arg1, arg2, obj);調(diào)用java層的函數(shù),再看下去:J4A_loadClass__J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer里面:
class_id = class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.id;
name = "postEventFromNative";
sign = "(Ljava/lang/Object;IIILjava/lang/Object;)V";
class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative = J4A_GetStaticMethodID__catchAll(env, class_id, name, sign);
if (class_J4AC_tv_danmaku_ijk_media_player_IjkMediaPlayer.method_postEventFromNative == NULL)
goto fail;
不用說了吧,走的是java層的函數(shù)postEventFromNative。
@CalledByNative
private static void postEventFromNative(Object weakThiz, int what,
int arg1, int arg2, Object obj) {
if (weakThiz == null)
return;
@SuppressWarnings("rawtypes")
IjkMediaPlayer mp = (IjkMediaPlayer) ((WeakReference) weakThiz).get();
if (mp == null) {
return;
}
if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
// this acquires the wakelock if needed, and sets the client side
// state
mp.start();
}
if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
mp.mEventHandler.sendMessage(m);
}
}
這里把弱引用的IjkMediaPlayer取出來了,然后調(diào)用了mp.mEventHandler.sendMessage(m);那么這個(gè)IjkMediaPlayer是哪里來的呢?答案就在IjkMediaPlayer_native_setup,也就是java層的native_setup函數(shù)中傳遞進(jìn)去的,在最初的initPlayer函數(shù)中調(diào)用的。那么java層的postEventFromNative里面的mp.mEventHandler是什么呢?就是initPlayer里面創(chuàng)建的looper。這下子串起來了吧,java層建立的IjkMediaPlayer,并填充eventhandler,c層在觸發(fā)特定的一些動作(例如打開直播等),會調(diào)用java層的函數(shù)向looper里面發(fā)送message,于是java層就收到了內(nèi)容,可以進(jìn)行相關(guān)處理了。整個(gè)過程是個(gè)異步的過程,并不阻塞。至于ffmpeg的東西,都是在c層創(chuàng)建并填充的。