您的位置:首页 > 移动开发 > Android开发

VLC4Android源码分析

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

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

view
sourceprint?

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的实例

view
sourceprint?

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来渲染。

view
sourceprint?

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。

view
sourceprint?

1.
mLibVLC.eventVideoPlayerActivityCreated(
true
);


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

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

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

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

view
sourceprint?

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


接着将MRL传给LibVLC

view
sourceprint?

1.
mLibVLC.setMediaList();


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


然后开始播放

view
sourceprint?

1.
mLibVLC.playIndex(savedIndexPosition);


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

view
sourceprint?

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

view
sourceprint?

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层,以供以后使用。

view
sourceprint?

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


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

playMRL函数先建立播放器对象

view
sourceprint?

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


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

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

view
sourceprint?

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,自己开发的时候如果有需要,可以在回调函数中进行业务逻辑。

view
sourceprint?

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


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

之后就该建立media对象了

view
sourceprint?

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


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

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

view
sourceprint?

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对象关联到播放器对象上,然后开始播放

view
sourceprint?

1.
libvlc_media_player_set_media(mp,
p_md);


2.
libvlc_media_player_play(mp);


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

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

view
sourceprint?

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库的调用方式,大大降低了学习成本。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: