Android多媒体之MediaPlayerService

如同其它service一样,MediaPlayerService在Android架构中起着承上启下的作用。向上提供media相关的api,向下调用其它框架(opencore、stegefright)具体实现相关功能。

  涉及到代理到MediaPlayerService的IPC在前文已经分析过了,所以这里重点讨 论实现。如apk调用setDataSource,经过Binder IPC会调用到MediaPlayerService::Client::setDataSource。

以一个播放器应用的流程作为分析起点:
1. MediaPlayer mp = new MediaPlayer();
2. mp.setDataSource(”/sdcard/aaa.mp3”);
3. mp.prepare();
4. mp.start();
5. mp.stop();

MediaPlayer为MediaPlayerService的客户端,不做分析。直接看setDataSource:

status_t MediaPlayerService::Client::setDataSource(const sp<IStreamSource> &source)

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)

status_t MediaPlayerService::Client::setDataSource(const char *url, const KeyedVector<String8, String8> *headers)

setDataSource根据data源参数不同有三种形式:IStreamSource(rtsp)类型、文件(本地)类型、url(http)类型。

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length) {
    player_type playerType =MediaPlayerFactory::getPlayerType(this,
    fd, offset, length);
    sp<MediaPlayerBase> p = setDataSource_pre(playerType);
    setDataSource_post(p, p->setDataSource(fd, offset, length));
}

enum player_type {
    PV_PLAYER = 1,
    SONIVOX_PLAYER = 2,
    STAGEFRIGHT_PLAYER = 3,
    NU_PLAYER = 4,
    // argument to the 'test:' url in the setDataSource call.
    TEST_PLAYER = 5,
};

aaa.mp3属性文件源,首先由MediaPlayerFactory产生一个播放器,播放的类型为:STAGEFRIGHT_PLAYER。
所有类型都事先注册到sFactoryMap中,然后再取出来。

StagefrightPlayer::StagefrightPlayer()
    : mPlayer(new AwesomePlayer) {
    mPlayer->setListener(this);
}

StagefrightPlayer类型实质是AwesomePlayer播放器,AwesomePlayer框架后文再介绍。继续MediaPlayerService中的流程:

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)
{
    // create the right type of player
    sp<MediaPlayerBase> p = createPlayer(playerType);

    if (!p->hardwareOutput()) {
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid());
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
    }
}

可以看到playerType(AwesomePlayer)得到之后,就开始创建AwesomePlayer的实例,接着又创建一个音频输出AudioOutput实例,并设置音频接收器setAudioSink。最后设置新的数据源:
setDataSource_post(p, p->setDataSource(fd, offset, length));

这样播放器和数据源都设置好了,我们的aaa.mp3文件保存在FileSource类型里:
sp<DataSource> dataSource = new FileSource(fd, offset, length);
其中class FileSource : public DataSource

下边进入mp.prepare();
调用status_t MediaPlayerService::Client::prepareAsync()

status_t MediaPlayerService::Client::prepareAsync()
{
    sp<MediaPlayerBase> p = getPlayer();
    status_t ret = p->prepareAsync();
    if (ret == NO_ERROR) mAntagonizer->start();
}

这里的p为AwesomePlayer,准备工作又涉及到AwesomePlayer的播放框架,简单提一下,AwesomePlayer以事件驱动框架进行运作,所以会准备事件队列。

status_t AwesomePlayer::prepareAsync() {
    return prepareAsync_l();
}

status_t AwesomePlayer::prepareAsync_l() {
    mQueue.start();

    modifyFlags(PREPARING, SET);
    mAsyncPrepareEvent = new AwesomeEvent(
            this, &AwesomePlayer::onPrepareAsyncEvent);
    mQueue.postEvent(mAsyncPrepareEvent);
    return OK;
}

队列TimedEventQueue mQueue; 对事件的处理需要协同时间。

void TimedEventQueue::start() {
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    pthread_create(&mThread, &attr, ThreadWrapper, this);
    pthread_attr_destroy(&attr);
}

start设置线程属性,
PTHREAD_CREATE_JOINABLE这个属性表明新创建的线程必须使用pthread_join()等待线程结束,否则线程所占用的资源不会得到释放,会造成资源泄露。在线程创建并运行以后可以用pthread_detach()来设置。
然后产生一个onPrepareAsyncEvent事件,并post到mQueue中,

TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
        const sp<Event> &event, int64_t realtime_us) {
    Mutex::Autolock autoLock(mLock);

    event->setEventID(mNextEventID++);

    List<QueueItem>::iterator it = mQueue.begin();
    while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
        ++it;
    }

    QueueItem item;
    item.event = event;
    item.realtime_us = realtime_us;
    item.has_wakelock = false;

    if (it == mQueue.begin()) {
        mQueueHeadChangedCondition.signal();
    }

    if (realtime_us > ALooper::GetNowUs() + kWakelockMinDelay) {
        acquireWakeLock_l();
        item.has_wakelock = true;
    }
    mQueue.insert(it, item);

    mQueueNotEmptyCondition.signal();

    return event->eventID();

这个带时间的队列控制非常巧妙,while进行空转耗时,实质代码:
mQueue.insert(it, item);
mQueueNotEmptyCondition.signal();
加入事件,发送信号。

接下来是mp.start();

status_t MediaPlayerService::Client::start()
{
    ALOGV("[%d] start", mConnId);
    sp<MediaPlayerBase> p = getPlayer();
    if (p == 0) return UNKNOWN_ERROR;
    p->setLooping(mLoop);
    return p->start();
}

status_t AwesomePlayer::setLooping(bool shouldLoop) {
    Mutex::Autolock autoLock(mLock);
    modifyFlags(LOOPING, CLEAR);
    if (shouldLoop) {
        modifyFlags(LOOPING, SET);
    }
    return OK;
}

status_t StagefrightPlayer::start() {
    return mPlayer->play();
}

先是修改播放器的标志:
void AwesomePlayer::modifyFlags(unsigned value, FlagMode mode) {
然后调用play进行播放。

status_t AwesomePlayer::play() {
    modifyFlags(CACHE_UNDERRUN, CLEAR);
    return play_l();
}

status_t AwesomePlayer::play_l() {
    mMediaRenderingStartGeneration = ++mStartGeneration;   

    if (mDecryptHandle != NULL) {
        int64_t position;
        getPosition(&position);
        mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
                Playback::START, position / 1000);
    }

    if (mAudioSource != NULL) {
        if (mAudioPlayer == NULL) {
            createAudioPlayer_l();
        }   

    if (mTimeSource == NULL && mAudioPlayer == NULL) {
        mTimeSource = &mSystemTimeSource;
    }

    if (mVideoSource != NULL) {
        // Kick off video playback
        postVideoEvent_l();

        if (mAudioSource != NULL && mVideoSource != NULL) {
            postVideoLagEvent_l();
        }
    } 
    return OK;
}

这个play函数比较长,处理各种异常情况,如没有播放前准备、文件解密准备、无视频播放实例、无音频播放实例等等。当这些条件都满足后,就向队列送一个video事件postVideoEvent_l();同时还要对当前电池电量进行监控等。

最后一步mp.stop();最终调用StagefrightPlayer的pause。

status_t StagefrightPlayer::stop() {
    ALOGV("stop");

    return pause();  // what's the difference?
}
status_t StagefrightPlayer::pause() {
    ALOGV("pause");

    return mPlayer->pause();
}

status_t AwesomePlayer::pause_l(bool at_eos) {
    notifyListener_l(MEDIA_PAUSED);
    cancelPlayerEvents(true /* keepNotifications */);
    postAudioTearDownEvent(kOffloadPauseMaxUs);
    modifyFlags(PLAYING, CLEAR);

结束时取消播放事件,然后暂停音频播放,再清除播放标志。

这样一个mp3文件的播放控制流程在就分析完了,主要涉及到MediaPlayerService与stagefright框架、awesomeplayer,越来越接近核心的部分了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值