本文系作者自己学习之所用,文章内容仅出自作者拙劣之思考,问题之处烦请不吝指教。
MediaPlayer 能被用来控制音/视频文件或流媒体的回放。Android中以MediaPlayer类作为音视频播放的基础类,围绕着他开展了一系列的处理。学习一个新的模块,最简单的步骤就是找到一个典型的应用程序,通过它的实现,来分析整个模块的数据流和控制流。典型的MediaPlayer在Java处的接口包括视频播放类VideoView以及音频专用MediaPlayer类。
一、 一个简单的视频播放demo app
Android中实现视频的播放可以采用MediaPlayer+SurfaceView配合的方式,其实Android还为开发人员提供了另外一种更简单的播放视频媒体的方式,那就是VideoView。VideoView类,其实质是用MediaPlayer类来实现的,只是由于其是视频播放,不得不和Surfaceview挂上够,才将其独立出来。使得其有如下的结构:
1 public class VideoView extends SurfaceView
2 implements MediaPlayerControl,SubtitleController.Anchor {
3 private static final String TAG = "VideoView";
4 ......
5 }
在Android中,提供了VideoView组件用于播放视频文件。想要使用VideoView组件播放视频,首先需要在布局文件中创建该组件,然后在Activity中获取该组件,并应用其setVideoPath()方法或setVideoURI()方法加载要播放的视频,最后调用start()方法来播放视频。另外,VideoView组件还提供了stop()和pause()方法,用于停止或暂停视频的播放。
在APP中,VideoView的典型简单使用如下:
1 mMediaController =new MediaController(this);
2 mVideoView = (VideoView) findViewById(R.id.videoView);
3 mVideoView.setVideoPath("/sdcard/1080P24FPS.mp4"); // 设置档案路径
4 mVideoView.setMediaController(mMediaController); 设置播放器的控制器
5 mVideoView.start(); 开始播放
先看看效果就是下面这个样子,短短几行代码一个播放器就做好了,还可以进行暂停,快进,快退,进度条控制。
PS:VideoView还提供许多其他播放控制API,在此不做重点介绍,以上代码也仅仅是个人demo,难免有误,谨慎参考使用。
二、 VideoView中setVideoPath的处理
任何华丽的语言都不如source code来的简单直接,上代码:
1 /**
2 * Sets video path.
3 *
4 * @param path the path of the video.
5 */
6 void setVideoPath(String path) {
7 setVideoURI(Uri.parse(path));
8 }
9
10 11 * Sets video URI.
12 13 uri the URI of the video.
14 15 setVideoURI(Uri uri) {
16 setVideoURI(uri,null17 18
19 20 * Sets video URI using specific headers.
21 22 uri the URI of the video.
23 headers the headers for the URI request.
24 * Note that the cross domain redirection is allowed by default,but that can be
25 * changed with key/value pairs through the headers parameter with
26 * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
27 * to disallow or allow cross domain redirection.
28 29 void setVideoURI(Uri uri,Map<String,String> headers) {
30 mUri = uri;
31 mHeaders = headers;
32 mSeekWhenPrepared = 033 openVideo(); openVideo的处理,让最终的处理权交给了MediaPlayer
34 requestLayout();
35 invalidate();
36 }
经过setVideoPath(String path) --> setVideoURI(Uri uri) --> setVideoURI(Uri uri,1)"> headers) 的调用流程,程序最终来到了openVideo()这一函数中:
openVideo() {
......
3 mMediaPlayer = new MediaPlayer();
4
5 mMediaPlayer.setDataSource(mContext,mUri,mHeaders);
6 mMediaPlayer.setDisplay(mSurfaceHolder);
7
mMediaPlayer.prepareAsync();
9 .......
10 }
在上面的代码中可以清楚的看到,我们首先new 了一个MediaPlayer类的对象,然后去setDataSource,到这里VideoView::openVideo的处理让最终的处理权交给了MediaPlayer。接下来我们就进入MediaPlayer的世界.
三、 MediaPlayer的世界
3.1 new MediaPlayer对象过程
首先关注MediaPlayer对象的创建过程,这也是分析android源码的一个基本要求。依次通过Java --> JNI(libmedia_jni.so) -- > Frameworks(libmedia.so)的处理流程。
MediaPlayer.java 构造函数,这一部分在 Android MediaPlayer架构 -- 前言小知识点(一)也有分析
public MediaPlayer() {
super( AudioAttributes.Builder().build());
Looper looper;
if ((looper = Looper.myLooper()) != ) {
mEventHandler = new EventHandler(,looper);
} else if ((looper = Looper.getMainLooper()) != else {
mEventHandler = ;
}
mTimeProvider = new TimeProvider();
mOpenSubtitleSources = new Vector<InputStream>();
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
*/
native_setup(new WeakReference<MediaPlayer>());
}
可以看到,在使用VideoView中到创建MediaPlayer会经过:new VideoView——> new MediaPlayer ——>native_setup 这样一个典型的对象建立过程,并传递到JNI。
native_setup主要用于本地C++层的对象的建立,在JNI代码(frameworks\base\media\jni\android_media_MediaPlayer.cpp)中可以找到对应的native函数:
1 void
2 android_media_MediaPlayer_native_setup(JNIEnv *env,jobject thiz,jobject weak_this)
{
4 ALOGV("native_setup" 5 sp<MediaPlayer> mp = new MediaPlayer(); 实例化一个native MediaPlayer(frameworks\av\media\libmedia\mediaplayer.cpp)
if (mp == NULL) {
7 jniThrowException(env,"java/lang/RuntimeException","Out of memory" 8 return10
11 create new listener and give it to MediaPlayer
12 sp<JNIMediaPlayerListener> listener = JNIMediaPlayerListener(env,thiz,weak_this);
13 mp->setListener(listener);
14
Stow our new C++ MediaPlayer in an opaque field in the Java object.
16 setMediaPlayer(env,mp);
17 }
进入JNI做android_media_MediaPlayer_native_setup处理:sp<MediaPlayer> mp = new MediaPlayer() 这个native MediaPlayer会去和media service进行交互实现真正的播放功能,使得最终处理进入C++的世界。
3.2 setDataSource过程
MediaPlayer java class中提供了多种setDataSource方法来设置不同的URI播放流,在此我们以播放本地档案为例来介绍处理流程:
VideoView::setVideoURI() --> MediaPlayer::setDataSource(mContext,mHeaders); --> MediaPlayer::setDataSource(uri.toString()) --> MediaPlayer::setDataSource(path,null,null) --> MediaPlayer::setDataSource(fd) --> setDataSource(fd,0,0x7ffffffffffffffL) --> _setDataSource(fd,offset,length)
最后会调到 _setDataSource(fd,length),看这个方法被声明为 native method
1 native _setDataSource(MediaDataSource dataSource)
2 throws IllegalArgumentException,IllegalStateException;
在JNI层我们找到该方法对应的JNI method实现:
1 {"_setDataSource","(Ljava/io/FileDescriptor;JJ)V",(void *)android_media_MediaPlayer_setDataSourceFD},
android_media_MediaPlayer_setDataSourceFD()方法定义如下:
android_media_MediaPlayer_setDataSourceFD(JNIEnv * getMediaPlayer(env,thiz);
NULL ) {
jniThrowException(env,"java/lang/IllegalStateException";
}
if (fileDescriptor == NULL) {
jniThrowException(env,"java/lang/IllegalArgumentException";
}
int fd = jniGetFDFromFileDescriptor(env,fileDescriptor);
ALOGV("setDataSourceFD: fd %d" );
}
上面这段代码可以看到最终调用了status_t MediaPlayer::setDataSource(int fd,int64_t offset,int64_t length)
//*********************************************************************************************************************************************************************
MediaPlayer的C++代码位于/frameworks/av/media/libmedia/mediaplayer.cpp, 编译后形成一个libmedia.so。
下面来看这个API的处理,接下去都只分析framework层的C++的处理流
1 status_t MediaPlayer::setDataSource(int fd,int64_t offset,int64_t length)
3 ALOGV("setDataSource(%d,%" PRId64 )" 4 status_t err = UNKNOWN_ERROR;
5 const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
7 sp<IMediaPlayer> player(service->create(if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
9 (NO_ERROR != player->setDataSource(fd,length))) {
10 player.clear();
}
12 err = attachNewPlayer(player);
14 err;
15 }
典型的Binder C/S架构,获取MediaPlayerService的proxy,通过MediaPlayerService来创建一个player,然后对这个player调用setDataSource。
3.3 MediaPlayerService的工作
启动与获取
MediaPlayerService同其他的Binder Service一样,作为一个server对外提供服务,它是在mediaserver中启动的:
/frameworks/av/media/mediaserver/main_mediaserver.cpp
int main(int argc __unused,1)">char **argv __unused) signal(SIGPIPE,SIG_IGN); 5 sp<ProcessState> proc(ProcessState::self()); 6 sp<IServiceManager> sm(defaultServiceManager()); 7 ALOGI(ServiceManager: %p",sm.get()); InitializeIcuOrDie(); 9 MediaPlayerService::instantiate(); 启动MediaPlayerService ResourceManagerService::instantiate(); registerExtensions(); 12 ProcessState::self()->startThreadPool(); 13 IPCThreadState::self()->joinThreadPool(); 14 }
在/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中对instantiate()方法的定义:
MediaPlayerService::instantiate() { 2 defaultServiceManager()->addService( 3 String16(media.player"),1)"> MediaPlayerService()); 4 }
在上面这段代码中我们注册了一个名为“media.player"的Binder Service,也就是MediaPlayerService,之后就可以通过 binder = sm->getService(String16("media.player"));来请求这个服务了
Player的创建
获取MediaPlayerService后就要去create player: sp<IMediaPlayer> player(service->create(this create请求处理:
原文地址:https://www.cnblogs.com/roger-yu
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。