ijkplayer source code analysis (1): initialization process

1. ijkplayer initialization process

This article is based on the A4ijkplayer project for ijkplayer source code analysis. This project is to change ijkplayer to CMake-based compilation, which can be imported into Android Studio to compile and run, which is convenient for code search, function jump, single-step debugging, call stack trace, etc.

The main work done by initialization is to create a player object: IjkMediaPlayer . It provides two forms of constructors, the difference is whether to pass the parameter IjkLibLoader. It is not passed by default, that is, the default System.loadLibrary(libName) is used, and the two constructors finally call the initPlayer() function to initialize the player. Four things are done in this function:

  • Load native dynamic libraries: loadLibrariesOnce() loads dynamic libraries, dynamically registers JNI functions, and other initialization operations

  • Initialize native resources: initNativeOnce() There is no operation in this method in the source code of ijkplayer

  • Create an EventHandler for event processing Create a handler for the java layer to receive and process message events from the native layer callback

  • Set up native resources: native_setup() creates a native IjkMediaPlayer instance, creates a message queue, specifies msg_loop, creates a video rendering object SDL_Vout, creates a platform-related IJKFF_Pipeline (including video decoding and audio output), etc.

public IjkMediaPlayer() {
    this(sLocalLibLoader);
}
public IjkMediaPlayer(IjkLibLoader libLoader) {
    initPlayer(libLoader);
}
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(new WeakReference<IjkMediaPlayer>(this));
}

2、loadLibrariesOnce()

This function is a static method, which is called only once in the life cycle of the whole process, and is used to load the natvie dynamic library that needs to be depended on. The ijkplayer source code is to load the three dynamic libraries ijkffmpeg, ijksdl, and ijkplayer. In order to facilitate debugging and follow-up of the ijkplayer source code, I changed it to CMake and compiled the source code of ijksdl and ijkplayer into the same dynamic library a4ijkplayer in the CMakeList , so the call is as follows:

private static volatile boolean mIsLibLoaded = false;
    public static void loadLibrariesOnce(IjkLibLoader libLoader) {
        synchronized (IjkMediaPlayer.class) {
            if (!mIsLibLoaded) {
                if (libLoader == null)
                    libLoader = sLocalLibLoader;
                libLoader.loadLibrary("ijkffmpeg");
                libLoader.loadLibrary("a4ijkplayer");
//                libLoader.loadLibrary("ijksdl");
//                libLoader.loadLibrary("ijkplayer");
                mIsLibLoaded = true;
            }
        }
    }

loadLibrary When loading a dynamic library, the JNI_OnLoad() method of each library JNI will be called, and JNI_UnLoad() will be called when unloading.

2.1 Call libLoader.loadLibrary("ijksdl")

When the libijksdl.so dynamic library is loaded, the JNI_OnLoad() method in the ijkmedia/ijksdl/android/ijksdl_android_jni.c file will be called. Note: I compiled the source code of ijksdl and ijkplayer into the same dynamic library a4ijkplayer in the A4ijkplayer project, and will not call libLoader.loadLibrary("ijksdl"), so the JNI_OnLoad() method in ijksdl_android_jni.c will not be called. To solve this problem, change its name to SDL_JNI_OnLoad(), and then call the renamed SDL_JNI_OnLoad() in the JNI_OnLoad() method of the ijkplayer_jni.c file to ensure that the relevant methods in the original JNI_OnLoad() of the SDL library can be called. For relevant changes see: this commit

JNIEXPORT jint JNICALL SDL_JNI_OnLoad(JavaVM *vm, void *reserved)
{
    int retval;
    JNIEnv* env = NULL;
    g_jvm = vm;
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    retval = J4A_LoadAll__catchAll(env);
    JNI_CHECK_RET(retval == 0, env, NULL, NULL, -1);
    return JNI_VERSION_1_4;
}

2.2 Call libLoader.loadLibrary("a4ijkplayer")

The original code of Ijkplayer calls libLoader.loadLibrary("ijkplayer"), and the A4ijkplayer project loads the combined liba4ijkplayer.so , and then calls the JNI_OnLoad() method in the ijkmedia/ijkplayer/android/ijkplayer_jni.c file, as follows:

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);
    // 因为把 sdl 和 ijkplayer 和成一个 so 了,不会调用 sdl 原本的 JNI_OnLoad,所以需在这里主动调用
    jint result = SDL_JNI_OnLoad(vm, reserved);
    if (result < 0) {
        return result;
    }
    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;
}

This is mainly to dynamically register JNI functions, map and associate Java and jni related functions (RegisterNatives), and other initialization operations.

static JNINativeMethod g_methods[] = {
    // ...
    { "_prepareAsync",          "()V",      (void *) IjkMediaPlayer_prepareAsync },
    { "_start",                 "()V",      (void *) IjkMediaPlayer_start },
    // ...
    { "native_init",            "()V",      (void *) IjkMediaPlayer_native_init },
    { "native_setup",           "(Ljava/lang/Object;)V", (void *) IjkMediaPlayer_native_setup },
    { "native_finalize",        "()V",      (void *) IjkMediaPlayer_native_finalize },
    // ...
};

3、initNativeOnce()

This method is also a static method, calling the native_init() method.

private static volatile boolean mIsNativeInitialized = false;
private static void initNativeOnce() {
    synchronized (IjkMediaPlayer.class) {
        if (!mIsNativeInitialized) {
            native_init();
            mIsNativeInitialized = true;
        }
    }
}

Finally, the IjkMediaPlayer_native_init() method in the underlying ijkmedia/ijkplayer/android/ijkplayer_jni.c file is called . There is no operation in this method in the ijkplayer source code. If you need to modify the source code, you need to do some global initialization, which can be executed in this method.

// static JNINativeMethod g_methods[] = {...} 数组中
{ "native_init",            "()V",      (void *) IjkMediaPlayer_native_init },
static void
IjkMediaPlayer_native_init(JNIEnv *env)
{
    MPTRACE("%s\n", __func__);
}

4. Create EventHandler

Create a handler for the java layer to receive messages from the native layer callback, such as: resources are ready, playback is complete, seek is complete, error callbacks, etc. By default, the message processing is in the calling thread of the IjkMediaPlayer constructor. If the looper of the current thread is not obtained, the message processing is in the main thread.

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;
}

When the bottom layer notifies an event, it will call back to the postEventFromNative() function of the java layer, and then send a message to the processing thread through the EventHandler to avoid blocking the execution of the bottom layer. As follows (in IjkMediaPlayer.java):

@CalledByNative
private static void postEventFromNative(Object weakThiz, int what,
        int arg1, int arg2, Object obj) {
    // ...
    if (mp.mEventHandler != null) {
        Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
        mp.mEventHandler.sendMessage(m);
    }
}

Then handle the event in handleMessage() of EventHandler, as follows:

private static class EventHandler extends Handler {
    // ...
    @Override
    public void handleMessage(Message msg) {
        // ...
        switch (msg.what) {
        case MEDIA_PREPARED:
            player.notifyOnPrepared();
            return;
        case MEDIA_PLAYBACK_COMPLETE:
            player.stayAwake(false);
            player.notifyOnCompletion();
            return;
        // ...
    }
}

Then call back according to the listener set in the business layer, as follows: (in AbstractMediaPlayer.java)

protected final void notifyOnPrepared() {
    if (mOnPreparedListener != null)
        mOnPreparedListener.onPrepared(this);
}
protected final void notifyOnCompletion() {
    if (mOnCompletionListener != null)
        mOnCompletionListener.onCompletion(this);
}

4.1 native_setup()

Finally, the IjkMediaPlayer_native_setup() method in the underlying ijkmedia/ijkplayer/android/ijkplayer_jni.c file is called .

// static JNINativeMethod g_methods[] = {...} 数组中
{ "native_setup",           "(Ljava/lang/Object;)V", (void *) 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);
}

4.1.1 ijkmp_android_create()

You can view it in Android Studio through my A4ijkplayer project. This method is in the ijkmedia/ijkplayer/android/ijkplayer_android.c file.

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;
}

As above, ijkmp_android_create() does four things in total:

  • ijkmp_create() : Create an instance of the IjkMediaPlayer structure

  • SDL_VoutAndroid_CreateForAndroidSurface() : Create a video image rendering object SDL_Vout

  • ffpipeline_create_from_android() : Create a platform-dependent IJKFF_Pipeline, including video decoding and audio output

  • ffpipeline_set_vout() : associate pipeline with video output

[Learning address]: FFmpeg/WebRTC/RTMP/NDK/Android audio and video streaming media advanced development

[Article Benefits]: Receive more audio and video learning packages, Dachang interview questions, technical videos and learning roadmaps for free. The materials include (C/C++, Linux, FFmpeg webRTC rtmp hls rtsp ffplay srs, etc.) Click 1079654574 to join the group to receive it~

4.1.2 ijkmp_create()

This function creates an instance of the IjkMediaPlayer structure of the native layer, and its structure is defined in: ijkmedia/ijkplayer/ijkplayer_internal.h .

IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void*))
{
    IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
    // .....
    mp->ffplayer = ffp_create();
    // .....
    mp->msg_loop = msg_loop;
    // .....
    return mp;
}

And create FFplayer through ffp_create() and specify message event processing function msg_loop. As follows: message_loop() binds the native thread to the Java thread (in SDL_JNI_SetupThreadEnv), which is used to obtain the JNIEnv object, so that the Java layer method can be called from the bottom layer.

It internally calls the message_loop_n() method, and message_loop_n() calls ijkmp_get_msg() cyclically to obtain the current event, and passes the event to the Java layer through the post_event() method.

ijkmp_get_msg() gets the message event from the message queue. If the queue is empty and there is no event, it will block, and return -1 to indicate that the queue has exited. post_event() will reversely call the postEventFromNative() method of the Java layer IjkMediaPlayer object through JNI, and finally call the handleMessage() method of EventHandler.

static int message_loop(void *arg)
{
    // ...
    JNIEnv *env = NULL;
    if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) {
        return -1;
    }
    IjkMediaPlayer *mp = (IjkMediaPlayer*) arg;
    message_loop_n(env, mp);
    // ...
    return 0;
}
static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp)
{
    jobject weak_thiz = (jobject) ijkmp_get_weak_thiz(mp);
    while (1) {
        AVMessage msg;
        int retval = ijkmp_get_msg(mp, &msg, 1);
        if (retval < 0)
            break;
        switch (msg.what) {
            // ...
            case FFP_MSG_ERROR:
                post_event(env, weak_thiz, MEDIA_ERROR, MEDIA_ERROR_IJK_PLAYER, msg.arg1);
                break;
            case FFP_MSG_PREPARED:
                post_event(env, weak_thiz, MEDIA_PREPARED, 0, 0);
                break;
            // ...
        }
    }
}
inline static void post_event(JNIEnv *env, jobject weak_this, int what, int arg1, int arg2)
{
    J4AC_IjkMediaPlayer__postEventFromNative(env, weak_this, what, arg1, arg2, NULL);
}

4.1.3 SDL_VoutAndroid_CreateForAndroidSurface()

This function is used to create the Android platform video image rendering object SDL_Vout, as follows:

SDL_Vout *SDL_VoutAndroid_CreateForAndroidSurface()
{
    return SDL_VoutAndroid_CreateForANativeWindow();
}
SDL_Vout *SDL_VoutAndroid_CreateForANativeWindow()
{
    SDL_Vout *vout = SDL_Vout_CreateInternal(sizeof(SDL_Vout_Opaque));
    if (!vout)
        return NULL;
    SDL_Vout_Opaque *opaque = vout->opaque;
    opaque->native_window = NULL;
    if (ISDL_Array__init(&opaque->overlay_manager, 32))
        goto fail;
    if (ISDL_Array__init(&opaque->overlay_pool, 32))
        goto fail;
    opaque->egl = IJK_EGL_create();
    if (!opaque->egl)
        goto fail;
    vout->opaque_class    = &g_nativewindow_class;
    vout->create_overlay  = func_create_overlay;
    vout->free_l          = func_free_l;
    vout->display_overlay = func_display_overlay;
    return vout;
fail:
    func_free_l(vout);
    return NULL;
}

This is just to create the SDL_Vout structure, and related memory and function pointers. GL and Surface-related operations call the function pointers assigned here or assign the member variables of SDL_Vout here in the subsequent process. For example, the above function makes opaque->native_window empty, and the real assignment of native_window is to call SDL_VoutAndroid_SetNativeWindow_l() to associate the upper Surface with opaque->native_window when the Java layer calls setSurface to set the window to be displayed.

The func_create_overlay() function is as follows, here just assign the function pointer to vout->create_overlay, the actual call is called during rendering. Call vout->create_overlay through the SDL_Vout_CreateOverlay() function in ff_ffplay.c to transfer to the func_create_overlay() function.

static SDL_VoutOverlay *func_create_overlay(int width, int height, int frame_format, SDL_Vout *vout)
{
    SDL_LockMutex(vout->mutex);
    SDL_VoutOverlay *overlay = func_create_overlay_l(width, height, frame_format, vout);
    SDL_UnlockMutex(vout->mutex);
    return overlay;
}
static SDL_VoutOverlay *func_create_overlay_l(int width, int height, int frame_format, SDL_Vout *vout)
{
    switch (frame_format) {
    case IJK_AV_PIX_FMT__ANDROID_MEDIACODEC:
        // 硬解码
        return SDL_VoutAMediaCodec_CreateOverlay(width, height, vout);
    default:
        // 软解码
        return SDL_VoutFFmpeg_CreateOverlay(width, height, frame_format, vout);
    }
}

The func_display_overlay() function is as follows, here just assign the function pointer to vout->display_overlay, the real call is in ff_ffplay.c video_image_display2() calls vout->display_overlay through the SDL_VoutDisplayYUVOverlay() function to transfer to the func_display_overlay() function.

4.1.4 ffpipeline_create_from_android()

This function is used to create a platform-related IJKFF_Pipeline, including video decoding and audio output, here just set the function pointer for the pipeline, as follows:

IJKFF_Pipeline *ffpipeline_create_from_android(FFPlayer *ffp)
{
    ALOGD("ffpipeline_create_from_android()\n");
    IJKFF_Pipeline *pipeline = ffpipeline_alloc(&g_pipeline_class, sizeof(IJKFF_Pipeline_Opaque));
    if (!pipeline)
        return pipeline;
    IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
    opaque->ffp                   = ffp;
    opaque->surface_mutex         = SDL_CreateMutex();
    opaque->left_volume           = 1.0f;
    opaque->right_volume          = 1.0f;
    if (!opaque->surface_mutex) {
        ALOGE("ffpipeline-android:create SDL_CreateMutex failed\n");
        goto fail;
    }
    pipeline->func_destroy              = func_destroy;
    pipeline->func_open_video_decoder   = func_open_video_decoder;
    pipeline->func_open_audio_output    = func_open_audio_output;
    pipeline->func_init_video_decoder   = func_init_video_decoder;
    pipeline->func_config_video_decoder = func_config_video_decoder;
    return pipeline;
fail:
    ffpipeline_free_p(&pipeline);
    return NULL;
}

func_open_video_decoder : The function used to create a video decoder, call this function in the subsequent process, and create hard or soft decoding according to the parameters. Regardless of whether hard decoding or soft decoding returns IJKFF_Pipenode, it is said to be an abstraction of the decoder.

static IJKFF_Pipenode *func_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
    IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
    IJKFF_Pipenode        *node = NULL;
    if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2)
        // 视频硬解码
        node = ffpipenode_create_video_decoder_from_android_mediacodec(ffp, pipeline, opaque->weak_vout);
    if (!node) {
        // 视频软解码
        node = ffpipenode_create_video_decoder_from_ffplay(ffp);
    }
    return node;
}

The actual call to initialize the video decoder is in the prepareAsync() phase, before starting the video decoding thread, as follows:

static int stream_component_open(FFPlayer *ffp, int stream_index)
{
  //...
  case AVMEDIA_TYPE_VIDEO:
    // 初始化视频解码器
        decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);
    ffp->node_vdec = ffpipeline_open_video_decoder(ffp->pipeline, ffp);
      // 开始视频解码线程
    if ((ret = decoder_start(&is->viddec, video_thread, ffp, "ff_video_dec")) < 0)
        goto out;
  //...
}
IJKFF_Pipenode* ffpipeline_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
    return pipeline->func_open_video_decoder(pipeline, ffp);
}

func_open_audio_output : The function used to create audio output, call this function in the subsequent process, create AudioTrack or OpenSL according to the parameters to play audio.

static SDL_Aout *func_open_audio_output(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
    SDL_Aout *aout = NULL;
    if (ffp->opensles) {
        // 创建 OpenSLES
        aout = SDL_AoutAndroid_CreateForOpenSLES();
    } else {
        // 创建 AudioTrack
        aout = SDL_AoutAndroid_CreateForAudioTrack();
    }
    if (aout)
        SDL_AoutSetStereoVolume(aout, pipeline->opaque->left_volume, pipeline->opaque->right_volume);
    return aout;
}

func_init_video_decoder : The function used to initialize the video decoder, only hard decoding is valid.

static IJKFF_Pipenode *func_init_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
    IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
    IJKFF_Pipenode        *node = NULL;
    if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2)
        node = ffpipenode_init_decoder_from_android_mediacodec(ffp, pipeline, opaque->weak_vout);
    return node;
}
func_config_video_decoder :用于创建配置视频解码器的函数,只有硬解码才有效。

static int func_config_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
    IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
    int                       ret = NULL;
    if (ffp->node_vdec) {
        ret = ffpipenode_config_from_android_mediacodec(ffp, pipeline, opaque->weak_vout, ffp->node_vdec);
    }
    return ret;
}

4.1.5 ffpipeline_set_vout()

This function is used to associate the pipeline with the video output as follows:

void ffpipeline_set_vout(IJKFF_Pipeline* pipeline, SDL_Vout *vout)
{
    if (!check_ffpipeline(pipeline, __func__))
        return;
    IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
    opaque->weak_vout = vout;
}

5、jni_set_media_player()

Save the IjkMediaPlayer object of the native layer to the Java layer, that is, save the IjkMediaPlayer pointer of the native layer to the mNativeMediaPlayer variable of the IjkMediaPlayer of the Java layer.

static IjkMediaPlayer *jni_set_media_player(JNIEnv* env, jobject thiz, IjkMediaPlayer *mp)
{
    // ...
    IjkMediaPlayer *old = (IjkMediaPlayer*) (intptr_t) J4AC_IjkMediaPlayer__mNativeMediaPlayer__get__catchAll(env, thiz);
    if (mp) {
        ijkmp_inc_ref(mp);
    }
    J4AC_IjkMediaPlayer__mNativeMediaPlayer__set__catchAll(env, thiz, (intptr_t) mp);
    // ...
    return old;
}
public final class IjkMediaPlayer extends AbstractMediaPlayer {
    @AccessedByNative
    private long mNativeMediaPlayer;
}

6、ijkmp_set_weak_thiz()

Save the weak reference of the Java layer IjkMediaPlayer object to the native layer IjkMediaPlayer (mp->weak_thiz), as follows:

void *ijkmp_set_weak_thiz(IjkMediaPlayer *mp, void *weak_thiz)
{
    void *prev_weak_thiz = mp->weak_thiz;
    mp->weak_thiz = weak_thiz;
    return prev_weak_thiz;
}
private void initPlayer(IjkLibLoader libLoader) {
    // ...
    native_setup(new WeakReference<IjkMediaPlayer>(this));
}

7、ijkmp_set_inject_opaque()

void *ijkmp_set_inject_opaque(IjkMediaPlayer *mp, void *opaque)
{
    void *prev_weak_thiz = ffp_set_inject_opaque(mp->ffplayer, opaque);
    return prev_weak_thiz;
}
void *ffp_set_inject_opaque(FFPlayer *ffp, void *opaque)
{
    if (!ffp)
        return NULL;
    void *prev_weak_thiz = ffp->inject_opaque;
    ffp->inject_opaque = opaque;
    av_application_closep(&ffp->app_ctx);
    av_application_open(&ffp->app_ctx, ffp);
    ffp_set_option_int(ffp, FFP_OPT_CATEGORY_FORMAT, "ijkapplication", (int64_t)(intptr_t)ffp->app_ctx);
    ffp->app_ctx->func_on_app_event = app_func_event;
    return prev_weak_thiz;
}

8、ijkmp_set_ijkio_inject_opaque()

Create io manager to manage io-related operations and events, such as cache size.

void *ijkmp_set_ijkio_inject_opaque(IjkMediaPlayer *mp, void *opaque)
{
    void *prev_weak_thiz = ffp_set_ijkio_inject_opaque(mp->ffplayer, opaque);
    return prev_weak_thiz;
}
void *ffp_set_ijkio_inject_opaque(FFPlayer *ffp, void *opaque)
{
    if (!ffp)
        return NULL;
    void *prev_weak_thiz = ffp->ijkio_inject_opaque;
    ffp->ijkio_inject_opaque = opaque;
    ijkio_manager_destroyp(&ffp->ijkio_manager_ctx);
    ijkio_manager_create(&ffp->ijkio_manager_ctx, ffp);
    ijkio_manager_set_callback(ffp->ijkio_manager_ctx, ijkio_app_func_event);
    ffp_set_option_int(ffp, FFP_OPT_CATEGORY_FORMAT, "ijkiomanager", (int64_t)(intptr_t)ffp->ijkio_manager_ctx);
    return prev_weak_thiz;
}

9、ijkmp_android_set_mediacodec_select_callback()

Set the callback triggered when hard decoding is selected.

void ijkmp_android_set_mediacodec_select_callback(IjkMediaPlayer *mp, bool (*callback)(void *opaque, ijkmp_mediacodecinfo_context *mcc), void *opaque)
{
    if (mp && mp->ffplayer && mp->ffplayer->pipeline) {
        ffpipeline_set_mediacodec_select_callback(mp->ffplayer->pipeline, callback, opaque);
    }
    // ...
}
void ffpipeline_set_mediacodec_select_callback(IJKFF_Pipeline* pipeline, bool (*callback)(void *opaque, ijkmp_mediacodecinfo_context *mcc), void *opaque)
{
    // ...
    pipeline->opaque->mediacodec_select_callback        = callback;
    pipeline->opaque->mediacodec_select_callback_opaque = opaque;
}

10、Github : A4ijkplayer

11. Reference link:

www.jianshu.com/p/daf0a61cc…

anacz.blog.csdn.net/article/det…

Original link: ijkplayer source code analysis (1): initialization process - Nuggets

Guess you like

Origin blog.csdn.net/irainsa/article/details/130173079