系列文章:
安卓上我们经常会使用MediaPlayer这个类去播放音频和视频,这篇笔记便从MediaPlayer着手,一层层分析安卓的音视频播放框架。
MediaPlayer的使用很简单,如果是想要在一个SurfaceView上播放,assets下的video.mp4视频,只需要下面的几行代码就能在手机上看到视频画面了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface); surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { MediaPlayer player = new MediaPlayer(); player.setDisplay(holder); //设置画面显示在哪
try { player.setDataSource(getAssets().openFd("video.mp4")); //设置视频源 player.prepare(); //准备视频数据 } catch (IOException e) { e.printStackTrace(); } player.start(); //开始播放 }
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override public void surfaceDestroyed(SurfaceHolder holder) {
} });
|
MediaPlayer的API和用法很简单,基本上只需要看熟谷歌官方给的这幅状态图,就能很方便的使用了。其实大部分情况下无非也就是setDataSource、prepare、start、pause、stop、reset、release这几个方法的调用:
具体的使用细节我这边就不去赘述了,感兴趣的可以参考下官方文档。
我们在应用里面调了MediaPlayer的方法,其实底层都会通过IPC机制调到MediaPlayerService。其实不仅是MediaPlayer,android.media包下的媒体播放接口像AudioTrack、SoundPool、MediaCodec都是会调到MediaPlayerService去做具体的编解码操作的,安卓的媒体播放是个典型的C/S架构,可以参考下官方文档的架构图:
使用C/S架构的好处就是可以比较方便的统一管理软硬件编解码资源.
整个框架除了java层的MediaPlayer之外还涉及三个关键so库:
- libmedia_jni.so 负责使用jni连接java层和native层,然后调用MediaPlayer类提供的接口
- libmedia.so 对上层提供了MediaPlayer类负责客户端与MediaPlayerService的IPC通讯
- libmediaplayerservice.so 负责统筹调度具体的编码器和解码器,它内部也实现了libmedia.so的IMediaPlayer类用于接收客户端通过IPC机制发送的指令
他们的依赖关系如下
代码细节
然后我们来追踪下具体的代码实现.
其实android.media.MediaPlayer这个java类只是native层的一个代理,具体的实现都是通过jni调用到libmedia_jni.so里面的c/c++代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class MediaPlayer extends PlayerBase implements SubtitleController.Listener { ... static { System.loadLibrary("media_jni"); native_init(); } ... private static native final void native_init(); ... private native void _setVideoSurface(Surface surface); ... private native void _prepare() throws IOException, IllegalStateException; ... private native void _start() throws IllegalStateException; ... public void setDataSource(FileDescriptor fd, long offset, long length) throws IOException, IllegalArgumentException, IllegalStateException { _setDataSource(fd, offset, length); } ... public void setDisplay(SurfaceHolder sh) { mSurfaceHolder = sh; Surface surface; if (sh != null) { surface = sh.getSurface(); } else { surface = null; } _setVideoSurface(surface); updateSurfaceScreenOn(); } ... public void prepare() throws IOException, IllegalStateException { _prepare(); scanInternalSubtitleTracks(); } ... public void start() throws IllegalStateException { baseStart(); stayAwake(true); _start(); } ... }
|
libmedia_jni.so的实现可以在/frameworks/base/media/jni/android_media_MediaPlayer.cpp里面找到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) { sp<MediaPlayer> mp = new MediaPlayer(); ... setMediaPlayer(env, thiz, mp); }
static void android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz) { sp<MediaPlayer> mp = getMediaPlayer(env, thiz); ... sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz); mp->setVideoSurfaceTexture(st);
process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." ); }
static void android_media_MediaPlayer_start(JNIEnv *env, jobject thiz) { sp<MediaPlayer> mp = getMediaPlayer(env, thiz); ... process_media_player_call( env, thiz, mp->start(), NULL, NULL ); } ...
|
而libmedia_jni.so内部也是依赖了MediaPlayer这个类去干活,它的代码可以在/frameworks/av/include/media/mediaplayer.h和/frameworks/av/media/libmedia/mediaplayer.cpp找到,而它编译之后打包在libmedia.so中
上面我们看到libmedia_jni.so里面调用了MediaPlayer的方法去干活,那MediaPlayer又是怎么干活的呢,看看具体代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| //mediaplayer.h sp<IMediaPlayer> mPlayer;
//mediaplayer.cpp status_t MediaPlayer::prepare() { ... status_t ret = prepareAsync_l(); ... }
status_t MediaPlayer::prepareAsync_l() { ... return mPlayer->prepareAsync(); ... }
|
这里又依赖了一个IMediaPlayer,让我们继续挖一挖这个IMediaPlayer又是什么来的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player) { ... mPlayer = player; ... }
status_t MediaPlayer::setDataSource( const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers) { ALOGV("setDataSource(%s)", url); status_t err = BAD_VALUE; if (url != NULL) { const sp<IMediaPlayerService> service(getMediaPlayerService()); if (service != 0) { sp<IMediaPlayer> player(service->create(this, mAudioSessionId)); if ((NO_ERROR != doSetRetransmitEndpoint(player)) || (NO_ERROR != player->setDataSource(httpService, url, headers))) { player.clear(); } err = attachNewPlayer(player); } } return err; }
// MediaPlayer继承IMediaDeathNotifier IMediaDeathNotifier::getMediaPlayerService() { Mutex::Autolock _l(sServiceLock); if (sMediaPlayerService == 0) { sp<IServiceManager> sm = defaultServiceManager(); sp<IBinder> binder; do { binder = sm->getService(String16("media.player")); if (binder != 0) { break; } usleep(500000); // 0.5 s } while (true);
if (sDeathNotifier == NULL) { sDeathNotifier = new DeathNotifier(); } binder->linkToDeath(sDeathNotifier); sMediaPlayerService = interface_cast<IMediaPlayerService>(binder); } return sMediaPlayerService; }
|
可以看到getMediaPlayerService方法实际是从ServiceManager里面获取了”media.player”这个服务,然后拿到了IMediaPlayerService的Binder代理,又去到了MediaPlayerService::create方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client, audio_session_t audioSessionId) { pid_t pid = IPCThreadState::self()->getCallingPid(); int32_t connId = android_atomic_inc(&mNextConnId);
sp<Client> c = new Client( this, pid, connId, client, audioSessionId, IPCThreadState::self()->getCallingUid());
ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid, IPCThreadState::self()->getCallingUid());
wp<Client> w = c; { Mutex::Autolock lock(mLock); mClients.add(w); } return c; }
|
MediaPlayerService会创建一个Client返回给客户端,客户端这个Client调用到MediaPlayerService的功能了。顺嘴说一句,Client是MediaPlayerService的一个内部类,它继承了BnMediaPlayerService,而BnMediaPlayer又继承了BnInterface<IMediaPlayer>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| //MediaPlayerService.h class MediaPlayerService : public BnMediaPlayerService { ... class Client : public BnMediaPlayer { ... } ... }
//IMediaPlayer.h class BnMediaPlayer: public BnInterface<IMediaPlayer> { public: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); };
|
查看MediaPlayerService的源码,可以知道在setDataSource的时候查找支持该源的播放器,然后创建出来使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| status_t MediaPlayerService::Client::setDataSource(const sp<IMediaHTTPService> &httpService, const char *url, const KeyedVector<String8, String8> *headers) { ... player_type playerType = MediaPlayerFactory::getPlayerType(this, url); sp<MediaPlayerBase> p = setDataSource_pre(playerType); ... setDataSource_post(p, p->setDataSource(httpService, url, headers)); ... }
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(player_type playerType) { ... sp<MediaPlayerBase> p = createPlayer(playerType); ... }
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType) { sp<MediaPlayerBase> p = mPlayer; ... p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid); ... return p; }
|
可以看到内部都是通过MediaPlayerFactory这个工厂去实现的,MediaPlayerFactory::registerBuiltinFactories方法注册了一些播放器,根据音视频源选择合适的播放器去播放。值得强调的是在sdk 23及以前的系统中会有StagefrightPlayer、NuPlayer两个播放器,sdk 24之后,真正工作的播放器就只有一个NuPlayer了。当然,各个厂家自己的提供的播放器也可以在这里注册,像小米盒子的ROM就导入过VLC框架的播放器。由于我司还有大量的安卓4.4的机器,所以我这里会把两个播放器都讲一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| // android sdk 23 void MediaPlayerFactory::registerBuiltinFactories() { Mutex::Autolock lock_(&sLock);
if (sInitComplete) return;
registerFactory_l(new StagefrightPlayerFactory(), STAGEFRIGHT_PLAYER); registerFactory_l(new NuPlayerFactory(), NU_PLAYER); registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);
sInitComplete = true; }
// android sdk 24 void MediaPlayerFactory::registerBuiltinFactories() { Mutex::Autolock lock_(&sLock);
if (sInitComplete) return;
registerFactory_l(new NuPlayerFactory(), NU_PLAYER); registerFactory_l(new TestPlayerFactory(), TEST_PLAYER);
sInitComplete = true; }
|
StagefrightPlayer实际上指的是AwesomePlayer,在早期的安卓系统使用AwesomePlayer去播放本地视频,用NuPlayer去播放流媒体。后来因为某些原因(具体原因我没有找到,只是说AwesomePlayer有问题)所以逐渐用弃用了AwesomePlayer,统一使用NuPlayer去播放。在某些过度版本的安卓系统开发者选项里面还可以选择NuPlayer代替AwesomePlayer,到后期都不用选了,只有一个NuPlayer可以用。
关于AwesomePlayer和NuPlayer的具体代码实现,我会在下篇文章继续解析.让我们继续讲这两个播放器都依赖的OpenMax框架.
OpenMax(OMX)框架
开放多媒体加速层(英语:Open Media Acceleration,缩写为OpenMAX),一个不需要授权、跨平台的软件抽象层,以C语言实现的软件界面,用来处理多媒体。它是由Khronos Group提出的标准,也由他们来维持,目标在于创造一个统一的界面,加速大量多媒体资料的处理。
也就是说OpenMax提供了具体的软硬件编解码能力,AwesomePlayer和NuPlayer依赖它,就能实现编解码功能.
OpenMax分成三层:
开发层(Development Layer,DL)
这一层定义了一些基础的音频、视频以及图像算法,比如音频信号处理的快速傅立叶变换、滤波器,图像处理的色域转换(RGB、YUV等)、视频处理的MPEG-4, H.264, MP3, AAC 和 JPEG编解码等.
DL层分为五个应用领域:
AC - 音频编解码器
IC - 图像编解码器
IP - 图像处理(通用图像处理功能)
SP - 信号处理(通用音频处理功能)
VC - 视频编解码器(H264和MP4组件)
它们都是一些比较算法层面的接口,由芯片原厂实现
整合层(Integration Layer,IL)
这一层整合了DL层的算法和功能,作为一个比较低层级的编解码器接口,也就是说实现了这一层的接口就实现了一个编解码器.它可以是软件的也可以是硬件的
应用层(Application Layer,AL)
AL层为多媒体中间件与应用层之间提供一个标准化的API接口,不同的系统都应有对应的实现,应用程序依赖这一层的接口进行编程,就能获得很好的跨平台特性.
完整框架图
到这里,整个音视频播放架构就很清晰了