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

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

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (LibVlcUtil.isJellyBeanMR1OrLater()) {

// Get the media router service (miracast)

mMediaRouter = (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE);

mMediaRouterCallback = new MediaRouter.SimpleCallback() {

@Override

public void onRoutePresentationDisplayChanged(

MediaRouter router, MediaRouter.RouteInfo info) {

Log.d(TAG, "onRoutePresentationDisplayChanged: info="

+ info);

removePresentation();

}

};

}

createPresentation();

然后用得到一个LibVLC的实例

try {

mLibVLC = Util.getLibVlcInstance();

} catch (LibVlcException e) {

Log.d(TAG, "LibVLC initialisation failed");

return;

}

LibVLC类是vlc的sdk的封装,有java层和jni层,负责调用vlc库的功能。是连接android端与vlc库的桥梁,要想使用vlc库 必须先得到一个LibVLC的实例,用来控制整个播放。

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

mSurface = (SurfaceView) findViewById(R.id.player_surface);

mSurfaceHolder = mSurface.getHolder();

mSurfaceFrame = (FrameLayout) findViewById(R.id.player_surface_frame);

String chroma = pref.getString("chroma_format", "");

if(LibVlcUtil.isGingerbreadOrLater() && chroma.equals("YV12")) {

mSurfaceHolder.setFormat(ImageFormat.YV12);

} else if (chroma.equals("RV16")) {

mSurfaceHolder.setFormat(PixelFormat.RGB_565);

} else {

mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);

}

mSurfaceHolder.addCallback(mSurfaceCallback);

先要设置surfaceView渲染的颜色格式,chroma的值存储在SharedPreferences中,可以通过设置改变。

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

mLibVLC.eventVideoPlayerActivityCreated(true);

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

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

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

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

mLocation = LibVLC.PathToURI(Environment.getExternalStorageDirectory().getPath() + "/Download/" + filename);

接着将MRL传给LibVLC

mLibVLC.setMediaList();

mLibVLC.getMediaList().add(new Media(mLibVLC, mLocation));

然后开始播放

mLibVLC.playIndex(savedIndexPosition);

如果有过播放记录的资源,就会跳到上次的播放位置

if(rTime > 0)

mLibVLC.setTime(rTime);

if(intentPosition > 0)

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对象

void Java_org_videolan_libvlc_LibVLC_nativeInit(JNIEnv *env, jobject thiz)

{

……

const char *argv[] = {

/* CPU intensive plugin, setting for slow devices */

enable_time_stretch ? "--audio-time-stretch" : "--no-audio-time-stretch",

/* avcodec speed settings for slow devices */

"--avcodec-fast", // non-spec-compliant speedup tricks

"--avcodec-skiploopfilter", deblockstr,

"--avcodec-skip-frame", enable_frame_skip ? "2" : "0",

"--avcodec-skip-idct", enable_frame_skip ? "2" : "0",

/* Remove me when UTF-8 is enforced by law */

"--subsdec-encoding", subsencodingstr,

/* XXX: why can't the default be fine ? #7792 */

(networkCaching > 0) ? networkCachingstr : "",

/* Android audio API is a mess */

use_opensles ? "--aout=opensles" : "--aout=android_audiotrack",

/* Android video API is a mess */

use_opengles2 ? "--vout=gles2" : "--vout=androidsurface",

"--androidsurface-chroma", chromastr != NULL && chromastr[0] != 0 ? chromastr : "RV32",

/* XXX: we can't recover from direct rendering failure */

(hardwareAcceleration == HW_ACCELERATION_FULL) ? "" : "--no-mediacodec-dr",

};

libvlc_instance_t *instance = libvlc_new(sizeof(argv) / sizeof(*argv), argv);

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

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

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

playMRL函数先建立播放器对象

libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)(intptr_t)instance);

此时用到的instance就是在初始化中建立的libvlc_instance_t对象的指针。

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

libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);

static const libvlc_event_type_t mp_events[] = {

libvlc_MediaPlayerPlaying,

libvlc_MediaPlayerPaused,

libvlc_MediaPlayerEndReached,

libvlc_MediaPlayerStopped,

libvlc_MediaPlayerVout,

libvlc_MediaPlayerPositionChanged,

libvlc_MediaPlayerEncounteredError

};

for(int i = 0; i < (sizeof(mp_events) / sizeof(*mp_events)); i++)

libvlc_event_attach(ev, mp_events[i], vlc_event_callback, myVm);

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

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

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

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

之后就该建立media对象了

libvlc_media_t* p_md = libvlc_media_new_location((libvlc_instance_t*)(intptr_t)instance, p_mrl);

这里的p_mrl就是前文说到的MRL,作为参数被传进来。

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

libvlc_event_manager_t *ev_media = libvlc_media_event_manager(p_md);

static const libvlc_event_type_t mp_media_events[] = {

libvlc_MediaParsedChanged

};

for(int i = 0; i < (sizeof(mp_media_events) / sizeof(*mp_media_events)); i++)

libvlc_event_attach(ev_media, mp_media_events[i], vlc_event_callback, myVm);

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

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

libvlc_media_player_set_media(mp, p_md);

libvlc_media_player_play(mp);

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

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

jfloat Java_org_videolan_libvlc_LibVLC_getRate(JNIEnv *env, jobject thiz) {

libvlc_media_player_t* mp = getMediaPlayer(env, thiz);

if(mp)

return libvlc_media_player_get_rate(mp);

else

return 1.00;

}

void Java_org_videolan_libvlc_LibVLC_setRate(JNIEnv *env, jobject thiz, jfloat rate) {

libvlc_media_player_t* mp = getMediaPlayer(env, thiz);

if(mp)

libvlc_media_player_set_rate(mp, rate);

}

jboolean Java_org_videolan_libvlc_LibVLC_isPlaying(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

return !!libvlc_media_player_is_playing(mp);

else

return 0;

}

jboolean Java_org_videolan_libvlc_LibVLC_isSeekable(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

return !!libvlc_media_player_is_seekable(mp);

return 0;

}

void Java_org_videolan_libvlc_LibVLC_play(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

libvlc_media_player_play(mp);

}

void Java_org_videolan_libvlc_LibVLC_pause(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

libvlc_media_player_pause(mp);

}

void Java_org_videolan_libvlc_LibVLC_stop(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

libvlc_media_player_stop(mp);

}

jint Java_org_videolan_libvlc_LibVLC_getVolume(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

return (jint) libvlc_audio_get_volume(mp);

return -1;

}

jint Java_org_videolan_libvlc_LibVLC_setVolume(JNIEnv *env, jobject thiz, jint volume)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

//Returns 0 if the volume was set, -1 if it was out of range or error

return (jint) libvlc_audio_set_volume(mp, (int) volume);

return -1;

}

jlong Java_org_videolan_libvlc_LibVLC_getTime(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

return libvlc_media_player_get_time(mp);

return -1;

}

void Java_org_videolan_libvlc_LibVLC_setTime(JNIEnv *env, jobject thiz, jlong time)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

libvlc_media_player_set_time(mp, time);

}

jfloat Java_org_videolan_libvlc_LibVLC_getPosition(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

return (jfloat) libvlc_media_player_get_position(mp);

return -1;

}

void Java_org_videolan_libvlc_LibVLC_setPosition(JNIEnv *env, jobject thiz, jfloat pos)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

libvlc_media_player_set_position(mp, pos);

}

jlong Java_org_videolan_libvlc_LibVLC_getLength(JNIEnv *env, jobject thiz)

{

libvlc_media_player_t *mp = getMediaPlayer(env, thiz);

if (mp)

return (jlong) libvlc_media_player_get_length(mp);

return -1;

}

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

vlc android 源码分析,Vlc-for-android源码分析相关推荐

  1. android 浏览器源码分析,从源码出发深入理解 Android Service

    原标题:从源码出发深入理解 Android Service 原文链接: 建议在浏览器上打开,删除了大量代码细节,:) 本文是 Android 系统学习系列文章中的第三章节的内容,介绍了 Android ...

  2. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  3. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  4. 【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 源码分析 | 创建 Protobuf 对象 )

    文章目录 一.Protobuf 源码分析 二.创建 Protobuf 对象 三.完整代码示例 四.参考资料 一.Protobuf 源码分析 Protobuf 源文件如下 : addressbook.p ...

  5. 友盟统计java代码_SFAnalytics 分析友盟统计源码,反编译 SDK,还有部分没有 出来 android 259万源代码下载- www.pudn.com...

    文件名称: SFAnalytics下载  收藏√  [ 5  4  3  2  1 ] 开发工具: Java 文件大小: 8023 KB 上传时间: 2016-06-05 下载次数: 0 提 供 者: ...

  6. Android 系统(78)---《android framework常用api源码分析》之 app应用安装流程

    <android framework常用api源码分析>之 app应用安装流程 <android framework常用api源码分析>android生态在中国已经发展非常庞大 ...

  7. 【Android 10 源码】healthd 模块 HAL 2.0 分析

    Android 9 引入了从 health@1.0 HAL 升级的主要版本 android.hardware.health HAL 2.0.这一新 HAL 具有以下优势: 框架代码和供应商代码之间的区 ...

  8. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  9. Android SQLite多线程读写和线程同步源码分析

    没啥诀窍,只需保证几个线程都是用的一个SQLiteDataBase对象就行了. 如果我们非要在不同线程中用两个或更多的SQLiteDataBase对象呢,当然这些SQLiteDataBase对象所操作 ...

  10. Android 9 (P)之init进程启动源码分析指南之三

          Android 9 (P)之init进程启动源码分析指南之三 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 An ...

最新文章

  1. python扫雷 广度优先_Leetcode之广度优先搜索(BFS)专题-529. 扫雷游戏(Minesweeper)...
  2. 统计学习三要素 模型+策略+算法
  3. Windows server 2016 添加盘符为D盘的数据盘
  4. Hadoop最常用的工具(SQL on Hadoop):Hive
  5. android onlescan 参数,Android BLE:从iOS外设广告时,在onLeScan()回调中检索服务UUID
  6. 漫步最优化四十三——拟牛顿法
  7. Linux内核 eBPF基础:BCC (BPF Compiler Collection)
  8. 严防ARP病毒的六个步骤
  9. Loadrunner脚本编程(4)-数据类型操作和字符串操作
  10. CSP202012-2 期末预测之最佳阈值(100分)【序列处理】
  11. pgsql merge方法
  12. ubuntu11.04下安装TCL及TK
  13. matplotlib——饼状图pie函数
  14. 路由器当交换机用的设置方法
  15. iOS项目开发中的知识点与问题收集整理①
  16. 可数集合与不可数集合的理解(准大学生初学者)
  17. Quartus II 的下载及安装
  18. 解决Zxing解析本地图片二维码,识别率低,识别不出来的问题!
  19. 浙江省数字化改革总体方案(附下载)
  20. IBMMQ-安装IBMMQ服务端7.5(windows)

热门文章

  1. C# WebForm 屏蔽输入框的验证
  2. 编程小游戏:模仿掷骰子,猜大小!
  3. Spring MVC Controler层获取errors验证信息 同时返回
  4. Python使用MSS截屏
  5. QQ群消息监听并将消息存储到SQLite数据库中
  6. 编写transformers的自定义pytorch训练循环(Dataset和DataLoader解析和实例代码)
  7. java 文件结束符 eof_文件结束符EOF .
  8. 近10年来愚人节经典假新闻
  9. Radiology第18期:MRI检测致痫区的图像处理研究进展
  10. 电子产品量产工具-软件架构-显示系统