先从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.
}
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注册一个回调函数,每当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);
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函数先建立播放器对象
1.
libvlc_media_player_t *mp = libvlc_media_player_new((libvlc_instance_t*)(intptr_t)instance);
然后向播放器对象注册事件回调函数
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);
每个事件都需要单独注册,所以这里用了for循环注册。这里的回调函数只是在java层打印了LOG,自己开发的时候如果有需要,可以在回调函数中进行业务逻辑。
1.
setLong(env, thiz,
"mInternalMediaPlayerInstance"
, (jlong)(intptr_t)mp);
之后就该建立media对象了
1.
libvlc_media_t* p_md = libvlc_media_new_location((libvlc_instance_t*)(intptr_t)instance, p_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);
而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.
}