【十一】【vlc-anroid】视频图像展示window模块组件实现分析

有第十章中分析,在android设备上,通过模块组件搜索可知android设备上加载window如下:

window.c (vlc\modules\video_output\android) line 54 :     set_capability("vout window", 10)

1、其模块初始化分析:

// [vlc/modules/video_output/android/window.c]
vlc_module_begin()
    set_shortname(N_("Android Window"))
    set_description(N_("Android native window"))
    set_category(CAT_VIDEO)
    set_subcategory(SUBCAT_VIDEO_VOUT)
    set_capability("vout window", 10)
    set_callbacks(Open, Close)
vlc_module_end()

// [vlc/modules/video_output/android/window.c]
/**
 * Create an Android native window.
 */
static int Open(vout_window_t *wnd, const vout_window_cfg_t *cfg)
{
    
    
    // 检查设置的window类型
    if (cfg->type != VOUT_WINDOW_TYPE_INVALID
     && cfg->type != VOUT_WINDOW_TYPE_ANDROID_NATIVE)
        return VLC_EGENERIC;

    vout_window_sys_t *p_sys = malloc(sizeof (*p_sys));
    if (p_sys == NULL)
        return VLC_ENOMEM;
    wnd->sys = p_sys;

    // 创建【AWindowHandler】android window handler处理者对象,
    // 并赋值控制window大小、鼠标坐标事件的方法指针【android设备中可不用】
    // 见1.1小节分析
    p_sys->p_awh = AWindowHandler_new(wnd,
        &(awh_events_t) {
    
     OnNewWindowSize, OnNewMouseCoords });
    if (!p_sys->p_awh)
        goto error;

    // 初始化window类型、保存【AWindowHandler】对象
    wnd->type = VOUT_WINDOW_TYPE_ANDROID_NATIVE;
    wnd->handle.anativewindow = p_sys->p_awh;
    // window控制方法指针赋值
    // 但是主要:该方法在android native window实现中是空实现!
    // 即此模块不需要改功能
    wnd->control = Control;

    return VLC_SUCCESS;

error:
    Close(wnd);
    return VLC_EGENERIC;
}

1.1、AWindowHandler_new实现分析:

// 【vlc/modules/video_output/android/utils.c】
AWindowHandler *
AWindowHandler_new(vout_window_t *wnd, awh_events_t *p_events)
{
    
    
#define AWINDOW_REGISTER_FLAGS_SUCCESS 0x1
#define AWINDOW_REGISTER_FLAGS_HAS_VIDEO_LAYOUT_LISTENER 0x2

    AWindowHandler *p_awh;
    JNIEnv *p_env;
    // 初始化播放器时保存的对应对象地址值
    // 后续分析 TODO
    JavaVM *p_jvm = var_InheritAddress(wnd, "android-jvm");
    jobject jobj = var_InheritAddress(wnd, "drawable-androidwindow");

    if (!p_jvm || !jobj)
    {
    
    
        msg_Err(wnd, "libvlc_media_player options not set");
        return NULL;
    }

    // 获取缓存的jvm环境信息对象
    p_env = android_getEnvCommon(NULL, p_jvm, "AWindowHandler");
    if (!p_env)
    {
    
    
        msg_Err(wnd, "can't get JNIEnv");
        return NULL;
    }

    // 初始化JNI层jfilds结构体对应java层的【AndroidNativeWindow】和【SurfaceTexture】对象转换
    // 及其相关方法赋值、注册java回调jni方法事件等
    if (InitJNIFields(p_env, VLC_OBJECT(wnd), jobj) != VLC_SUCCESS)
    {
    
    
        msg_Err(wnd, "InitJNIFields failed");
        return NULL;
    }
    msg_Dbg(wnd, "InitJNIFields success");

    p_awh = calloc(1, sizeof(AWindowHandler));
    if (!p_awh)
        return NULL;

    p_awh->p_jvm = p_jvm;
    p_awh->jobj = (*p_env)->NewGlobalRef(p_env, jobj);

    p_awh->wnd = wnd;
    // 窗口大小控制方法赋值给cb,对应接收来自JAVA端的对应方法的调用
    p_awh->event.cb = *p_events;

    jfloatArray jarray = (*p_env)->NewFloatArray(p_env, 16);
    if ((*p_env)->ExceptionCheck(p_env))
    {
    
    
        (*p_env)->ExceptionClear(p_env);
        free(p_awh);
        return NULL;
    }
    p_awh->stex.jtransform_mtx_array = (*p_env)->NewGlobalRef(p_env, jarray);
    (*p_env)->DeleteLocalRef(p_env, jarray);
    p_awh->stex.jtransform_mtx = NULL;

    // 此方法为宏定义,实现为:调用了jvm中的jfields.AndroidNativeWindow.registerNative方法,
    // 其对应的是java层该对象的该方法,即将native层的【AWindowHandler】对象指针值传递给java层保存,用于对应关系
    const jint flags = JNI_ANWCALL(CallIntMethod, registerNative,
                                   (jlong)(intptr_t)p_awh);
    if ((flags & AWINDOW_REGISTER_FLAGS_SUCCESS) == 0)
    {
    
    
        msg_Err(wnd, "AWindow already registered");
        (*p_env)->DeleteGlobalRef(p_env, p_awh->jobj);
        (*p_env)->DeleteGlobalRef(p_env, p_awh->stex.jtransform_mtx_array);
        free(p_awh);
        return NULL;
    }
    // 加载android native window模块功能,见该小节下面分析
    LoadNativeWindowAPI(p_awh);

    // 根据java层到反馈flags值,判断java层是否组成了视频布局变化监听事件
    p_awh->b_has_video_layout_listener =
        flags & AWINDOW_REGISTER_FLAGS_HAS_VIDEO_LAYOUT_LISTENER;

    return p_awh;
}

// 【vlc/modules/video_output/android/utils.c】
static void
LoadNativeWindowAPI(AWindowHandler *p_awh)
{
    
    
    // 首先加载判断是否有android so库,若无则加载android native Surface对象
    void *p_library = dlopen("libandroid.so", RTLD_NOW);
    if (!p_library)
    {
    
    
        // 目前只分析该情况
        // 见该小节下面分析
        LoadNativeSurfaceAPI(p_awh);
        return;
    }

    p_awh->pf_winFromSurface = dlsym(p_library, "ANativeWindow_fromSurface");
    p_awh->pf_winRelease = dlsym(p_library, "ANativeWindow_release");
    p_awh->anw_api.winLock = dlsym(p_library, "ANativeWindow_lock");
    p_awh->anw_api.unlockAndPost = dlsym(p_library, "ANativeWindow_unlockAndPost");
    p_awh->anw_api.setBuffersGeometry = dlsym(p_library, "ANativeWindow_setBuffersGeometry");

    if (p_awh->pf_winFromSurface && p_awh->pf_winRelease
     && p_awh->anw_api.winLock && p_awh->anw_api.unlockAndPost
     && p_awh->anw_api.setBuffersGeometry)
    {
    
    
        p_awh->p_anw_dl = p_library;
    }
    else
    {
    
    
        dlclose(p_library);
        LoadNativeSurfaceAPI(p_awh);
    }
}

// 【vlc/modules/video_output/android/utils.c】
static void
LoadNativeSurfaceAPI(AWindowHandler *p_awh)
{
    
    
    // android native Surface创建等操作相关的方法指针赋值
    
    // 见第2小节分析,正常情况下加载的是【"libsurfaceflinger_client.so"】中的Surface对象
    p_awh->pf_winFromSurface = NativeSurface_fromSurface;
    
    p_awh->pf_winRelease = NativeSurface_release;
    
    // 见第3小节分析
    p_awh->anw_api.winLock = NativeSurface_lock;
    
    p_awh->anw_api.unlockAndPost = NativeSurface_unlockAndPost;
    p_awh->anw_api.setBuffersGeometry = NULL;
}

2、NativeSurface_fromSurface实现分析:

// 【vlc/modules/video_output/android/utils.c】
// 创建android native Surface 对象
static ANativeWindow*
NativeSurface_fromSurface(JNIEnv *p_env, jobject jsurf)
{
    
    
    void *p_surface_handle;
    NativeSurface *p_ns;

     // 从以下顺序的三个so库中尝试获取android native window对象,
    // 最先加载Surfaceflinger模块的ANativeWindow对象句柄
    static const char *libs[] = {
    
    
        "libsurfaceflinger_client.so",
        "libgui.so",
        "libui.so"
    };
    // 备注:从第十章后续分析【WindowHandler_NewSurfaceEnv】可知,
    // jsurf参数为java层【android.view.Surface】对象对应的JNI层对象实例。
    // 此处返回的是:对应的是native层Surface的对象指针
    // 见本小节下面的分析
    p_surface_handle = NativeSurface_getHandle(p_env, jsurf);
    if (!p_surface_handle)
        return NULL;
    p_ns = malloc(sizeof(NativeSurface));
    if (!p_ns)
        return NULL;
    // 保存native surface对象指针
    p_ns->p_surface_handle = p_surface_handle;

    for (size_t i = 0; i < sizeof(libs) / sizeof(*libs); i++)
    {
    
    
        // 按so库顺序,先加载Surfaceflinger so模块的ANativeWindow对象及其方法句柄指针
        // 见本小节下面的分析
        void *p_dl_handle = NativeSurface_Load(libs[i], p_ns);
        if (p_dl_handle)
        {
    
    
            p_ns->p_dl_handle = p_dl_handle;
            // 然后将其强转为【ANativeWindow】对象指针
            return (ANativeWindow*)p_ns;
        }
    }
    free(p_ns);
    return NULL;
}

// 【vlc/modules/video_output/android/utils.c】
static void *
NativeSurface_getHandle(JNIEnv *p_env, jobject jsurf)
{
    
    
    jclass clz;
    jfieldID fid;
    intptr_t p_surface_handle = 0;

    // 获取JVM中【android.view.Surface】该对象的class实例
    clz = (*p_env)->GetObjectClass(p_env, jsurf);
    if ((*p_env)->ExceptionCheck(p_env))
    {
    
    
        (*p_env)->ExceptionClear(p_env);
        return NULL;
    }
    // 获取该变量在该对象中的值【Int值,其实对应的是native层Surface的对象指针值】
    fid = (*p_env)->GetFieldID(p_env, clz, "mSurface", "I");
    if (fid == NULL)
    {
    
    
        if ((*p_env)->ExceptionCheck(p_env))
            (*p_env)->ExceptionClear(p_env);
        fid = (*p_env)->GetFieldID(p_env, clz, "mNativeSurface", "I");
        if (fid == NULL)
        {
    
    
            if ((*p_env)->ExceptionCheck(p_env))
                (*p_env)->ExceptionClear(p_env);
        }
    }
    if (fid != NULL)
        // 获取native层surface的指针int值
        p_surface_handle = (intptr_t)(*p_env)->GetIntField(p_env, jsurf, fid);
    (*p_env)->DeleteLocalRef(p_env, clz);

    return (void *)p_surface_handle;
}

// 【vlc/modules/video_output/android/utils.c】
// 该方法实现:加载对应so库中的ANativeWindow对象及其方法句柄指针
static inline void *
NativeSurface_Load(const char *psz_lib, NativeSurface *p_ns)
{
    
    
    void *p_lib = dlopen(psz_lib, RTLD_NOW);
    if (!p_lib)
        return NULL;

    p_ns->pf_lock = (AndroidSurface_lock)(dlsym(p_lib, ANDROID_SYM_S_LOCK));
    p_ns->pf_lock2 = (AndroidSurface_lock2)(dlsym(p_lib, ANDROID_SYM_S_LOCK2));
    p_ns->pf_unlockAndPost =
        (AndroidSurface_unlockAndPost)(dlsym(p_lib, ANDROID_SYM_S_UNLOCK));

    if ((p_ns->pf_lock || p_ns->pf_lock2) && p_ns->pf_unlockAndPost)
        return p_lib;

    dlclose(p_lib);
    return NULL;
}

3、NativeSurface_lock实现分析:

// 【vlc/modules/video_output/android/utils.c】
static int32_t
NativeSurface_lock(ANativeWindow *p_anw, ANativeWindow_Buffer *p_anb,
                   ARect *p_rect)
{
    
    
    (void) p_rect;
    NativeSurface *p_ns = (NativeSurface *)p_anw;
    struct {
    
    
        uint32_t    w;
        uint32_t    h;
        uint32_t    s;
        uint32_t    usage;
        uint32_t    format;
        uint32_t*   bits;
        uint32_t    reserved[2];
    } info = {
    
     0 };

    // 此处可知直接调用的android Surface的lock/lock2方法请求lock图像
    if (p_ns->pf_lock)
        p_ns->pf_lock(p_ns->p_surface_handle, &info, 1);
    else
        p_ns->pf_lock2(p_ns->p_surface_handle, &info, NULL);

    if (!info.w || !info.h) {
    
    
        p_ns->pf_unlockAndPost(p_ns->p_surface_handle);
        return -1;
    }

    // 获取到图像格式及其数据信息
    if (p_anb) {
    
    
        p_anb->bits = info.bits;
        p_anb->width = info.w;
        p_anb->height = info.h;
        p_anb->stride = info.s;
        p_anb->format = info.format;
    }
    return 0;
}

本章节结束,其他源码分析请查看后续章节分析

猜你喜欢

转载自blog.csdn.net/u012430727/article/details/110940752