安卓音视频播放-总体架构

系列文章:

  • 安卓音视频播放 - 总体架构
  • 安卓音视频播放 - AwesomePlayer
  • 安卓音视频播放 - NuPlayer

安卓上我们经常会使用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这几个方法的调用:

安卓音视频播放-总体架构_第1张图片
1.jpeg

具体的使用细节我这边就不去赘述了,感兴趣的可以参考下官方文档。

安卓Media框架

我们在应用里面调了MediaPlayer的方法,其实底层都会通过IPC机制调到MediaPlayerService。其实不仅是MediaPlayer,android.media包下的媒体播放接口像AudioTrack、SoundPool、MediaCodec都是会调到MediaPlayerService去做具体的编解码操作的,安卓的媒体播放是个典型的C/S架构,可以参考下官方文档的架构图:

安卓音视频播放-总体架构_第2张图片
2.png

使用C/S架构的好处就是可以比较方便的统一管理软硬件编解码资源.

整个框架除了java层的MediaPlayer之外还涉及三个关键so库:

  • libmedia_jni.so 负责使用jni连接java层和native层,然后调用MediaPlayer类提供的接口
  • libmedia.so 对上层提供了MediaPlayer类负责客户端与MediaPlayerService的IPC通讯
  • libmediaplayerservice.so 负责统筹调度具体的编码器和解码器,它内部也实现了libmedia.so的IMediaPlayer类用于接收客户端通过IPC机制发送的指令

他们的依赖关系如下

安卓音视频播放-总体架构_第3张图片
3.png

代码细节

然后我们来追踪下具体的代码实现.

其实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 mp = new MediaPlayer();
    ...
    setMediaPlayer(env, thiz, mp);
}

static void
android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
{
    sp mp = getMediaPlayer(env, thiz);
    ...
    sp 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 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 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& player)
{
    ...
    mPlayer = player;
    ...
}

status_t MediaPlayer::setDataSource(
        const sp &httpService,
        const char *url, const KeyedVector *headers)
{
    ALOGV("setDataSource(%s)", url);
    status_t err = BAD_VALUE;
    if (url != NULL) {
        const sp service(getMediaPlayerService());
        if (service != 0) {
            sp 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 sm = defaultServiceManager();
        sp 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(binder);
    }
    return sMediaPlayerService;
}

可以看到getMediaPlayerService方法实际是从ServiceManager里面获取了"media.player"这个服务,然后拿到了IMediaPlayerService的Binder代理,又去到了MediaPlayerService::create方法:

sp MediaPlayerService::create(const sp& client,
        audio_session_t audioSessionId)
{
    pid_t pid = IPCThreadState::self()->getCallingPid();
    int32_t connId = android_atomic_inc(&mNextConnId);

    sp 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 w = c;
    {
        Mutex::Autolock lock(mLock);
        mClients.add(w);
    }
    return c;
}

MediaPlayerService会创建一个Client返回给客户端,客户端这个Client调用到MediaPlayerService的功能了。顺嘴说一句,Client是MediaPlayerService的一个内部类,它继承了BnMediaPlayerService,而BnMediaPlayer又继承了BnInterface

//MediaPlayerService.h
class MediaPlayerService : public BnMediaPlayerService {
    ...
    class Client : public BnMediaPlayer {
        ...
    }
    ...
}

//IMediaPlayer.h
class BnMediaPlayer: public BnInterface
{
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 &httpService, const char *url, const KeyedVector *headers)
{
    ...
    player_type playerType = MediaPlayerFactory::getPlayerType(this, url);
    sp p = setDataSource_pre(playerType);
    ...
    setDataSource_post(p, p->setDataSource(httpService, url, headers));
    ...
}

sp MediaPlayerService::Client::setDataSource_pre(player_type playerType) {
    ...
    sp p = createPlayer(playerType);
    ...
}

sp MediaPlayerService::Client::createPlayer(player_type playerType)
{
    sp 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接口,不同的系统都应有对应的实现,应用程序依赖这一层的接口进行编程,就能获得很好的跨平台特性.

完整框架图

到这里,整个音视频播放架构就很清晰了

安卓音视频播放-总体架构_第4张图片
4.png

你可能感兴趣的:(安卓音视频播放-总体架构)