Vlc-for-android源码分析

  • http://www.it165.net/pro/html/201404/11895.html

    先从AndroidManifest.xml开始分析,从接收的data类型可以看出用来播放音视频的activity是VideoPlayerActivity,AudioService是用于支持音乐后台播放的service,其他activity都是和界面有关的activity。这里主要分析和视频播放有关的VideoPlayerActivity,从intent-filter可以看出的能够接受的播放格式,data的scheme中有http说明支持通过网络地址播放。播放器的主要播放工作都在 VideoPlayerActivity类完成。

    VideoPlayerActivity在onCreate中先处理一下多屏的情况,从onCreate的注解中可以看出这是4.1及以上才启用的功能。

    01. @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    02. protected void onCreate(Bundle savedInstanceState) {
    03. super.onCreate(savedInstanceState);
    04.  
    05.  
    06. if (LibVlcUtil.isJellyBeanMR1OrLater()) {
    07. // Get the media router service (miracast)
    08. mMediaRouter = (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE);
    09. mMediaRouterCallback = new MediaRouter.SimpleCallback() {
    10. @Override
    11. public void onRoutePresentationDisplayChanged(
    12. MediaRouter router, MediaRouter.RouteInfo info) {
    13. Log.d(TAG, "onRoutePresentationDisplayChanged: info="
    14. + info);
    15. removePresentation();
    16. }
    17. };
    18. }
    19.  
    20.  
    21. createPresentation();

     

    然后用得到一个LibVLC的实例

    1. try {
    2. mLibVLC = Util.getLibVlcInstance();
    3. catch (LibVlcException e) {
    4. Log.d(TAG, "LibVLC initialisation failed");
    5. return;
    6. }
    LibVLC类是vlc的sdk的封装,有java层和jni层,负责调用vlc库的功能。是连接android端与vlc库的桥梁,要想使用vlc库 必须先得到一个LibVLC的实例,用来控制整个播放。

     

    Vlcfor android的整个播放都是在surfaceView上渲染的,所以在播放前必须初始化一个surfaceView然后把surfaceView交给vlc来渲染。

     

    01. mSurface = (SurfaceView) findViewById(R.id.player_surface);
    02. mSurfaceHolder = mSurface.getHolder();
    03. mSurfaceFrame = (FrameLayout) findViewById(R.id.player_surface_frame);
    04. String chroma = pref.getString("chroma_format""");
    05. if(LibVlcUtil.isGingerbreadOrLater() && chroma.equals("YV12")) {
    06. mSurfaceHolder.setFormat(ImageFormat.YV12);
    07. else if (chroma.equals("RV16")) {
    08. mSurfaceHolder.setFormat(PixelFormat.RGB_565);
    09. else {
    10. mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);
    11. }
    12. mSurfaceHolder.addCallback(mSurfaceCallback);
    先要设置surfaceView渲染的颜色格式,chroma的值存储在SharedPreferences中,可以通过设置改变。

     

    然后为surfaceView注册一个回调函数,每当surfaceView发生变动时,surfaceView的控制端也就是负责渲染的vlc库会使用这个回调拿到surfaceView。

     

    1. mLibVLC.eventVideoPlayerActivityCreated(true);

    然后通知vlc库surfaceView已经设置完成,可以使用了。

    onCreate方法中主要的工作就是这些,其他的就是一些细枝末节,不用做过多了解。

    在VideoPlayerActivity的load方法中从activity传入的intent中分析播放资源的类型参数以及路径,将路径转换成LibVLC可识别的格式,然后和数据库对比一下,如果发现以前播放过,可以接着上次的进度播放。

    其中主要针对不同的资源类型,如文件、网络地址、甚至还有邮件等做了区分处理,将数据转换为vlc库可识别的形式,叫做MRL(media resource location)

     

    1. mLocation = LibVLC.PathToURI(Environment.getExternalStorageDirectory().getPath() + "/Download/" + filename);
    接着将MRL传给LibVLC

     

     

    1. mLibVLC.setMediaList();
    2. mLibVLC.getMediaList().add(new Media(mLibVLC, mLocation));

    然后开始播放

     

     

    1. mLibVLC.playIndex(savedIndexPosition);
    如果有过播放记录的资源,就会跳到上次的播放位置

     

     

    1. if(rTime > 0)
    2. mLibVLC.setTime(rTime);
    3.  
    4. if(intentPosition > 0)
    5. mLibVLC.setTime(intentPosition);

    onResume方法中向handler发送一条AUDIO_SERVICE_CONNECTION_SUCCESS的Message后,VideoPlayerHandler会调用load来开始载入资源并播放。

    VideoPlayerActivity还有play、pause、seek方法来控制播放、暂停、和跳转播放进度。可以根据用户的触摸来控制视频的播放。

    所有的播放控制最终都会调用到LibVLC中的方法,前面就说过,LibVLC是android层控制VLC库的核心,一切与播放有关的操作都要通过LibVLC来转调,LibVLC通过java与jni结合为VLC库的c语言接口提供了一个java接口,通过这个java接口,开发人员可以不用去了解VLC库的c接口来调用VLC库的功能。

    LibVLC类分为两层,java层在LibVLC类中存储了数据,jni层在Libvlcjni.c中实现功能。Jni层中的初始化函数负责从java层获得存储的数据然后把数据转换成命令行参数的方式建立libvlc_instance_t对象

     

    01. void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)
    02. {
    03. ……
    04. const char *argv[] = {
    05. /* CPU intensive plugin, setting for slow devices */
    06. enable_time_stretch ? "--audio-time-stretch" "--no-audio-time-stretch",
    07.  
    08. /* avcodec speed settings for slow devices */
    09. "--avcodec-fast"// non-spec-compliant speedup tricks
    10. "--avcodec-skiploopfilter", deblockstr,
    11. "--avcodec-skip-frame", enable_frame_skip ? "2" "0",
    12. "--avcodec-skip-idct", enable_frame_skip ? "2" "0",
    13.  
    14. /* Remove me when UTF-8 is enforced by law */
    15. "--subsdec-encoding", subsencodingstr,
    16.  
    17. /* XXX: why can't the default be fine ? #7792 */
    18. (networkCaching > 0) ? networkCachingstr : "",
    19.  
    20. /* <a href="http://www.it165.net/pro/ydad/" target="_blank" class="keylink">Android</a> audio API is a mess */
    21. use_opensles ? "--aout=opensles" "--aout=android_audiotrack",
    22.  
    23. /* Android video API is a mess */
    24. use_opengles2 ? "--vout=gles2" "--vout=androidsurface",
    25. "--androidsurface-chroma", chromastr != NULL && chromastr[0] != 0 ? chromastr : "RV32",
    26. /* XXX: we can't recover from direct rendering failure */
    27. (hardwareAcceleration == HW_ACCELERATION_FULL) ? "" "--no-mediacodec-dr",
    28. };
    29. libvlc_instance_t *instance = libvlc_new(sizeof(argv) / sizeof(*argv), argv);

     

    同时要把这个指针存在java层,以供以后使用。

     

    1. setLong(env, thiz, "mLibVlcInstance", (jlong)(intptr_t) instance);

    每次新播放一个资源最后都会调用playMRL函数来初始化播放器以及资源,VideoPlayerActivity中调用的LibVLC类的playIndex方法最后也会调用这个函数。

     

    playMRL函数先建立播放器对象

     

    1. libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)(intptr_t)instance);
    此时用到的instance就是在初始化中建立的libvlc_instance_t对象的指针。

     

    然后向播放器对象注册事件回调函数

     

    01. libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
    02. static const libvlc_event_type_t mp_events[] = {
    03. libvlc_MediaPlayerPlaying,
    04. libvlc_MediaPlayerPaused,
    05. libvlc_MediaPlayerEndReached,
    06. libvlc_MediaPlayerStopped,
    07. libvlc_MediaPlayerVout,
    08. libvlc_MediaPlayerPositionChanged,
    09. libvlc_MediaPlayerEncounteredError
    10. };
    11. for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)
    12. libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

    vlc使用回调的方式来通知调用者状态的改变,因为有时控制命令发出后并不是能立刻得到响应或者会发生错误,所以要通过回调的方式来通知调用者。

     

    每个事件都需要单独注册,所以这里用了for循环注册。这里的回调函数只是在java层打印了LOG,自己开发的时候如果有需要,可以在回调函数中进行业务逻辑。
     

    1. setLong(env, thiz, "mInternalMediaPlayerInstance", (jlong)(intptr_t)mp);

    同样将指针存在java层,以供以后使用。

     

    之后就该建立media对象了

     

    1. libvlc_media_t* p_md = libvlc_media_new_location((libvlc_instance_t*)(intptr_t)instance, p_mrl);
    这里的p_mrl就是前文说到的MRL,作为参数被传进来。

     

    而media对象同样可以注册事件回调


    1. libvlc_event_manager_t *ev_media = libvlc_media_event_manager(p_md);
    2. static const libvlc_event_type_t mp_media_events[] = {
    3. libvlc_MediaParsedChanged
    4. };
    5. for(int i = 0; i < (sizeof(mp_media_events) / sizeof(*mp_media_events)); i++)
    6. libvlc_event_attach(ev_media, mp_media_events[i], vlc_event_callback, myVm);

     

    所有的回调事件的类型定义在libvlc_events.h中。

    最后将media对象关联到播放器对象上,然后开始播放

     

    1. libvlc_media_player_set_media(mp, p_md);
    2. libvlc_media_player_play(mp);
    这两个函数是定义在Media_player.c中的函数,是vlc库中的函数,至此LibVLC类的jni已经完成了他的使命:将java层的调用转发到VLC库中去。

     

    而jni层中剩下的有关播放控制的函数也是如此

     

    001. jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {
    002. libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    003. if(mp)
    004. return libvlc_media_player_get_rate(mp);
    005. else
    006. return 1.00;
    007. }
    008.  
    009. void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {
    010. libvlc_media_player_t* mp = getMediaPlayer(env, thiz);
    011. if(mp)
    012. libvlc_media_player_set_rate(mp, rate);
    013. }
    014.  
    015. jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)
    016. {
    017. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    018. if (mp)
    019. return !!libvlc_media_player_is_playing(mp);
    020. else
    021. return 0;
    022. }
    023.  
    024. jboolean Java_org_videolan_libvlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)
    025. {
    026. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    027. if (mp)
    028. return !!libvlc_media_player_is_seekable(mp);
    029. return 0;
    030. }
    031.  
    032. void Java_org_videolan_libvlc_LibVLC_play(JNIEnv *env, jobject thiz)
    033. {
    034. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    035. if (mp)
    036. libvlc_media_player_play(mp);
    037. }
    038.  
    039. void Java_org_videolan_libvlc_LibVLC_pause(JNIEnv *env, jobject thiz)
    040. {
    041. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    042. if (mp)
    043. libvlc_media_player_pause(mp);
    044. }
    045.  
    046. void Java_org_videolan_libvlc_LibVLC_stop(JNIEnv *env, jobject thiz)
    047. {
    048. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    049. if (mp)
    050. libvlc_media_player_stop(mp);
    051. }
    052.  
    053. jint Java_org_videolan_libvlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)
    054. {
    055. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    056. if (mp)
    057. return (jint) libvlc_audio_get_volume(mp);
    058. return -1;
    059. }
    060.  
    061. jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)
    062. {
    063. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    064. if (mp)
    065. //Returns 0 if the volume was set, -1 if it was out of range or error
    066. return (jint) libvlc_audio_set_volume(mp, (int) volume);
    067. return -1;
    068. }
    069.  
    070. jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)
    071. {
    072. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    073. if (mp)
    074. return libvlc_media_player_get_time(mp);
    075. return -1;
    076. }
    077.  
    078. void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)
    079. {
    080. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    081. if (mp)
    082. libvlc_media_player_set_time(mp, time);
    083. }
    084.  
    085. jfloat Java_org_videolan_libvlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)
    086. {
    087. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    088. if (mp)
    089. return (jfloat) libvlc_media_player_get_position(mp);
    090. return -1;
    091. }
    092.  
    093. void Java_org_videolan_libvlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)
    094. {
    095. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    096. if (mp)
    097. libvlc_media_player_set_position(mp, pos);
    098. }
    099.  
    100. jlong Java_org_videolan_libvlc_LibVLC_getLength(JNIEnv *env, jobject thiz)
    101. {
    102. libvlc_media_player_t *mp = getMediaPlayer(env, thiz);
    103. if (mp)
    104. return (jlong) libvlc_media_player_get_length(mp);
    105. return -1;
    106. }

    可以看出,他们都是转而调用了VLC库中的函数,将上层的java调用转换成VLC库中的c调用。这就是LibVLC的主要的功能,只要调用libVLC类java层的play、pause、等方法就会在这里被转换成相应的函数调用,在android和vlc这件架起了一座桥梁。可见LibVLC类是粘合android和vlc的一个中间层,如果要基于vlc-for-android进行二次开发,可以直接在LibVLC类的基础上进行,不需要再去了解VLC库的调用方式,大大降低了学习成本。

     

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值