系列文章:
安卓上我们经常会使用MediaPlayer这个类去播放音频和视频,这篇笔记便从MediaPlayer着手,一层层分析安卓的音视频播放框架。
MediaPlayer
MediaPlayer的使用很简单,如果是想要在一个SurfaceView上播放,assets下的video.mp4视频,只需要下面的几行代码就能在手机上看到视频画面了:
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这几个方法的调用:
具体的使用细节我这边就不去赘述了,感兴趣的可以参考下官方文档。
安卓Media框架
我们在应用里面调了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++代码:
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里面找到:
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又是怎么干活的呢,看看具体代码:
//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又是什么来的:
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方法:
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>
//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的工作原理
查看MediaPlayerService的源码,可以知道在setDataSource的时候查找支持该源的播放器,然后创建出来使用:
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的机器,所以我这里会把两个播放器都讲一下。
// 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接口,不同的系统都应有对应的实现,应用程序依赖这一层的接口进行编程,就能获得很好的跨平台特性.
完整框架图
到这里,整个音视频播放架构就很清晰了