【三】vlc android native层播放流程源码分析

播放(控制)器组件:
org.videolan.libvlc.MediaPlayer
媒体数据实体类:
org.videolan.medialibrary.interfaces.media.MediaWrapper
播放布局组件:
org.videolan.libvlc.util.VLCVideoLayout

播放器设置:
val media = mediaFactory.getFromUri(VLCInstance.getInstance(service), uri)
1、设置播放开始时间点字段:
media.addOption(":start-time=${start/1000L}")
2、设置禁止音频输出字段:
media.addOption(":no-audio")
media.addOption(":no-spu")
3、设置是否硬解字段:
media.addOption(":codec=mediacodec_ndk,mediacodec_jni,none")

lib vlc so库加载:
VLCOptions配置lib vlc so库的配置条件,通过LibVlC对象内部进行加载【LibVLC是在初始化MediaPlayer对象时创建的】

额外优化处理:
1、MediaPlayer:Audio外部设备插入和拔出的兼容处理
2、VLCUtil:检查安卓设备CPU频率及其架构ABI和VLC so库的运行匹配兼容性处理
3、网络缓存时间长度字段:"–network-caching=$networkCaching" 【VLCOptions】
4、视频渲染方式设置字段:
if (opengl == 1) options.add("–vout=gles2,none") // enable OpenGL
else if (opengl == 0) options.add("–vout=android_display,none")
5、系统属性通过反射获取
6、LibVLC中检查当前安卓设备是否有硬解码组件并使用

点击播放调用链:
VideoGridFragment点击Item -->VideosViewModel.playVideo()–>MediaUtils.openMedia()–>PlaybackService.load()–>PlaylistManager.load()&playIndex()–>VideoPlayerActivity.startOpened()&onStart()&startPlayback()&loadMedia()–>PlaybackServie.playindex()–>PlayListManager.playIndex()–>PlayerController.startPlayback()–>MediaPlayer.play()&nativePlay()进入JNI native层播放器控制

java层比较好自行分析,因此不展开分析。

JNI层播放控制流程:
一、在LibVlC对象创建时创建native层对象:
1、调用jni层:lib/vlc/libvlcjni.c中,定义如下:

void Java_org_videolan_libvlc_LibVLC_nativeNew(JNIEnv *env, jobject thiz,
                                               jobjectArray jstringArray,
                                               jstring jhomePath)
{
   
   
    vlcjni_object *p_obj = NULL;
    libvlc_instance_t *p_libvlc = NULL;
    jstring *strings = NULL;
    const char **argv = NULL;
    int argc = 0;

    if (jhomePath)
    {
   
   
        const char *psz_home = (*env)->GetStringUTFChars(env, jhomePath, 0);
        if (psz_home)
        {
   
   
//  设置环境变量
            setenv("HOME", psz_home, 1);
            (*env)->ReleaseStringUTFChars(env, jhomePath, psz_home);
        }
    }
//  设置环境变量
    setenv("VLC_DATA_PATH", "/system/usr/share", 1);

    if (jstringArray)
    {
   
   
        argc = (*env)->GetArrayLength(env, jstringArray);

        argv = malloc(argc * sizeof(const char *));
        strings = malloc(argc * sizeof(jstring));
        if (!argv || !strings)
        {
   
   
//  内存分配失败,走失败流程
            argc = 0;
            goto error;
        }
        for (int i = 0; i < argc; ++i)
        {
   
   
            strings[i] = (*env)->GetObjectArrayElement(env, jstringArray, i);
            if (!strings[i])
            {
   
   
                argc = i;
                goto error;
            }
            argv[i] = (*env)->GetStringUTFChars(env, strings[i], 0);
            if (!argv)
            {
   
   
                argc = i;
                goto error;
            }
        }
    }
//  最终创建libvlc jni对象,参数为jstringArray内容即vlc的配置参数
    p_libvlc = libvlc_new(argc, argv);

error:
// 省略...... 
}

2、libvlc_new(argc, argv)实现如下:【位于vlc/lib/core.c文件中】

libvlc_instance_t * libvlc_new( int argc, const char *const *argv )
{
   
   
// 初始化vlc线程系统,使用安卓native库创建
    libvlc_threads_init ();

    libvlc_instance_t *p_new = malloc (sizeof (*p_new));
    if (unlikely(p_new == NULL))
        return NULL;

    const char *my_argv[argc + 2];
    my_argv[0] = "libvlc"; /* dummy arg0, skipped by getopt() et al */
    for( int i = 0; i < argc; i++ )
         my_argv[i + 1] = argv[i];
    my_argv[argc + 1] = NULL; /* C calling conventions require a NULL */

// 创建libvlc对象:空对象并初始化默认callback类型等
// 该部分内容不展开分析,主要进行libvlc对象初始化,涉及多平台设备定义的类和宏定义比较多,
// 基本使用C语言实现
    libvlc_int_t *p_libvlc_int = libvlc_InternalCreate();
    if (unlikely (p_libvlc_int == NULL))
        goto error;

//  对libvlc空对象进行赋值,参数为vlc播放器的配置参数指令解析并初始化VLC模块组件、
// 消息队列、输入的callback初始化【var.c】、播放列表结构包括节目获取方式和超时时间等。 
// 通过环境变量【"HOME"】路径以及【.config】后缀读取VLC配置文件信息
    if (libvlc_InternalInit( p_libvlc_int, argc + 1, my_argv ))
    {
   
   
        libvlc_InternalDestroy( p_libvlc_int );
        goto error;
    }

    p_new->p_libvlc_int = p_libvlc_int;
    p_new->vlm = NULL;
    p_new->ref_count = 1;
    p_new->p_callback_list = NULL;
    vlc_mutex_init(&p_new->instance_lock);
    return p_new;

error:
    free (p_new);
    libvlc_threads_deinit ();
    return NULL;
}

自此第一步根据配置参数创建除了libvlc相关初始化对象。

二、分析播放接口前先分析native层播放器的初始化流程:
1、由前面概述知道,java层播放控制器组件为【org.videolan.libvlc.MediaPlayer】,上一小节已分析native层libvlc对象的创建【当然是有java层LibVLC创建的】,则分析其对应构造函数:

    /**
     * Create an empty MediaPlayer
     *
     * @param ILibVLC a valid libVLC
     */
    public MediaPlayer(ILibVLC ILibVLC) {
   
   
        super(ILibVLC);
        nativeNewFromLibVlc(ILibVLC, mWindow);
    }
    // JNI java定义原型
    private native void nativeNewFromLibVlc(ILibVLC ILibVLC, AWindow window);

2、JNI层实现:位于【libvlc/jni/libvlc-mediaplayer.c】

void
Java_org_videolan_libvlc_MediaPlayer_nativeNewFromLibVlc(JNIEnv *env,
                                                         jobject thiz,
                                                         jobject libvlc,
                                                         jobject jwindow)
{
   
   
    // 内部实现通过JAVA虚拟机初始化【JNI_OnLoad()】时初始化的java层对象
    // 即从VLCObject的native层全局对象来获取其对应的[fields.VLCObject.mInstanceID]
    // 字段值,并强转为指针【vlcjni_object *】,
   // mInstanceID对应java层字段为【private long mInstance = 0;】,
   // 并且使用jweak来弱引用java层VLCObject的MediaPlayer对象
    vlcjni_object *p_obj = VLCJniObject_newFromJavaLibVlc(env, thiz, libvlc);
    if (!p_obj)
        return;
    
    // 创建播放器播放环境:选择并初始化音视频输出模块等
    /* Create a media player playing environment */
    p_obj->u.p_mp = libvlc_media_player_new(p_obj->p_libvlc);
    // 根据java层window创建native层此引用并缓存,以及播放控制回调事件注册等
    MediaPlayer_newCommon(env, thiz, p_obj, jwindow);
}

此处提一下播放控制事件回调java层的jni实现:

// 位于【libvlc/jni/libvlcjni-vlcobject.c】
static void
VLCJniObject_eventCallback(const libvlc_event_t *ev, void *data)
{
   
   
    vlcjni_object *p_obj = data;
    JNIEnv *env = NULL;

    assert(p_obj->p_libvlc);

    java_event jevent = {
   
    -1, 0, 0, 0.0, NULL };

    if (!(env = jni_get_env(THREAD_NAME)))
        return;

    if (!p_obj->p_owner->pf_event_cb(p_obj, ev, &jevent))
        return;

    jstring string = jevent.argc1 ? (*env)->NewStringUTF(env, jevent.argc1) : NULL;
    
    // weak对象为此前分析过的java层MediaPlayer对象的native层弱引用,
    // 调用其【dispatchEventFromNative】方法即完成将事件通知java层
    if (p_obj->p_owner->weak)
        (*env)->CallVoidMethod(env, p_obj->p_owner->weak,
                               fields.VLCObject.dispatchEventFromNativeID,
                               jevent.type, jevent.arg1, jevent.arg2,
                               jevent.argf1, string);
    if (string)
        (*env)->DeleteLocalRef(env, string);
}

// Java层定义: 位于【VLCObject.java】
private synchronized void dispatchEventFromNative(int eventType, long arg1, long arg2, float argf1, @Nullable String args1)

三、接着才开始分析nativePlay()播放接口流程:
1、JNI层实现:位于【libvlc/jni/libvlc-mediaplayer.c】

void
Java_org_videolan_libvlc_MediaPlayer_nativePlay(JNIEnv *env, jobject thiz)
{
   
   
    // 通过前面分析,此处获取是前面初始化中缓存的实例
    vlcjni_object *p_obj = VLCJniObject_getInstance(env, thiz);

    if (!p_obj)
        return;

    libvlc_media_player_play(p_obj->u.p_mp);
}

2、播放请求:【vlc/lib/media_player.c】

int libvlc_media_player_play( libvlc_media_player_t *p_mi )
{
   
   
    // 对输入锁加锁,以免多线程并发问题
    lock_input( p_mi );

    input_thread_t *p_input_thread = p_mi->input.p_thread;
    if( p_input_thread )
    {
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值