Android N Audio播放二:setDataSource窥探

来源:互联网 发布:怎么修改远程桌面端口 编辑:程序博客网 时间:2024/05/21 18:33

  在上一篇Android如何播放一首音乐中介绍了如何使用MediaPlayer来播放音乐文件。可以看到,步骤非常简单。

player.setDataSource(path);player.prepare();player.start();

  但如果你想更深入的了解Audio, 仅仅会调用这个几个API是远远不够的,没有考虑容器格式,文件的来源,协议,编解码等等。
  
  这篇文章就来介绍一下第一步的setDataSource到底都做了些什么。还是以MusicDemo为例,先将play方法中的prepare和start方法注释掉, 这样可以比较清楚的看到setDataSource的都干了些什么事。
  

    private void play(){        try {            String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/Music/Test.mp3";            player.setDataSource(path);            Log.d("Jaychou","MusicDemo setDataSource");            //player.prepare();            //Log.d("Jaychou","MusicDemo Prepare");            //player.start();        } catch (IOException e) {            e.printStackTrace();            Log.d("Jaychou","err when play");        }    }

先来一张流程图:
这里写图片描述

当点击PLAY_MUSIC的时候,打出如下日志:

02-10 15:45:13.538 V/MediaPlayer( 4748): setDataSource(59, 0, 576460752303423487)02-10 15:45:13.540 V/MediaPlayerService(  647): Client(4) constructor02-10 15:45:13.541 V/MediaPlayerService(  647): Create new client(4) from pid 4748, uid 10140, 02-10 15:45:13.545 V/MediaPlayerService(  647): setDataSource fd=8, offset=0, length=57646075230342348702-10 15:45:13.545 V/MediaPlayerService(  647): st_dev  = 2102-10 15:45:13.545 V/MediaPlayerService(  647): st_mode = 3318402-10 15:45:13.546 V/MediaPlayerService(  647): st_uid  = 002-10 15:45:13.546 V/MediaPlayerService(  647): st_gid  = 999702-10 15:45:13.546 V/MediaPlayerService(  647): st_size = 293271502-10 15:45:13.546 V/MediaPlayerService(  647): calculated length = 293271502-10 15:45:13.546 V/MediaPlayerService(  647): player type = 402-10 15:45:13.546 V/MediaPlayerFactory(  647):  create NuPlayer02-10 15:45:13.547 V/NuPlayerDriver(  647): NuPlayerDriver(0xf39ab140)02-10 15:45:13.551 V/AudioSink(  647): AudioOutput(57)02-10 15:45:13.552 V/NuPlayerDriver(  647): setDataSource(0xf39ab140) file(8)02-10 15:45:13.552 V/NuPlayer(  647): kWhatSetAudioSink02-10 15:45:13.552 V/NuPlayer(  647): kWhatSetDataSource02-10 15:45:13.553 I/ExtendedNuUtils(  647): printFileName fd(8) -> /storage/emulated/0/Music/Test.mp302-10 15:45:13.553 V/MediaPlayerService(  647):  setDataSource02-10 15:45:13.555 D/Jaychou ( 4748): MusicDemo setDataSource

结合流程图和Log, 我们来看一下setDataSource做的事情。

1、MusicDemo调用framework MediaPlayer.java的接口,继而通过JNI(JNI我这里没有画出来)再调用到Mediaplayer.cpp, 然后再通过Binder(IMediaPlayer)调到MediaPlayerService. 可以看到,MusicDemo, MediaPlayer.java, Mediaplayer.cpp, IMediaPlayer属于一个进程,pid是4748.
这里写图片描述

2、MediaPlayerService的setDataSource主要有三个动作:

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length){    ALOGV("setDataSource fd=%d, offset=%" PRId64 ", length=%" PRId64 "", fd, offset, length);    struct stat sb;    int ret = fstat(fd, &sb);    if (ret != 0) {        ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));        return UNKNOWN_ERROR;    }    ALOGV("st_dev  = %" PRIu64 "", static_cast<uint64_t>(sb.st_dev));    ALOGV("st_mode = %u", sb.st_mode);    ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));    ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));    ALOGV("st_size = %" PRId64 "", sb.st_size);    if (offset >= sb.st_size) {        ALOGE("offset error");        return UNKNOWN_ERROR;    }    if (offset + length > sb.st_size) {        length = sb.st_size - offset;        ALOGV("calculated length = %" PRId64 "\n", length);    }    player_type playerType = MediaPlayerFactory::getPlayerType(this,                                                               fd,                                                               offset,                                                               length);    sp<MediaPlayerBase> p = setDataSource_pre(playerType);    if (p == NULL) {        return NO_INIT;    }    // now set data source    setDataSource_post(p, p->setDataSource(fd, offset, length));    return mStatus;}

A: 获得播放器类型

MediaPlayerFactory::getPlayerType

从Log看是Nuplayer, Android N上android已经完全抛弃了Asomeplayer, 源代码里面已经看不到相关的代码了。

B: 创建播放器,设置AudioSink等。

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(        player_type playerType){    ALOGV("player type = %d", playerType);    // create the right type of player    sp<MediaPlayerBase> p = createPlayer(playerType);    if (p == NULL) {        return p;    }    sp<IServiceManager> sm = defaultServiceManager();    sp<IBinder> binder = sm->getService(String16("media.extractor"));    if (binder == NULL) {        ALOGE("Unable to connect to media extractor service");        return NULL;    }    mExtractorDeathListener = new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH);    binder->linkToDeath(mExtractorDeathListener);    binder = sm->getService(String16("media.codec"));    if (binder == NULL) {        ALOGE("Unable to connect to media codec service");        return NULL;    }    mCodecDeathListener = new ServiceDeathNotifier(binder, p, MEDIACODEC_PROCESS_DEATH);    binder->linkToDeath(mCodecDeathListener);    if (!p->hardwareOutput()) {        Mutex::Autolock l(mLock);        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),                mPid, mAudioAttributes);        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);    }    return p;}

C: 调用NuplayerDriver的setDataSource

p->setDataSource(fd, offset, length)

3、setDataSource是一个Blocking的方法, 在NuplayerDriver中可以看出,

status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {    ALOGV("setDataSource(%p) file(%d)", this, fd);    Mutex::Autolock autoLock(mLock);    if (mState != STATE_IDLE) {        return INVALID_OPERATION;    }    mState = STATE_SET_DATASOURCE_PENDING;    mPlayer->setDataSourceAsync(fd, offset, length);    while (mState == STATE_SET_DATASOURCE_PENDING) {        mCondition.wait(mLock);    }    AVNuUtils::get()->printFileName(fd);    return mAsyncResult;}

设置source之前状态是STATE_SET_DATASOURCE_PENDING, 当设置完成以后,NuplayerDriver的notifySetDataSourceCompleted会被调用, 状态更新成STATE_UNPREPARED。

4、NuplayerDriver调用的是Nuplayer的setDataSourceAsync, 这里通过构造GenericSource将数据源抽成了source。这个source就供下一步的解复用(demux)来使用。

void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);    sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);    sp<GenericSource> source =            new GenericSource(notify, mUIDValid, mUID);    status_t err = source->setDataSource(fd, offset, length);    if (err != OK) {        ALOGE("Failed to set data source!");        source = NULL;    }    msg->setObject("source", source);    msg->post();}

这样,整个setDataSource的流程就走完了。

这里写图片描述

0 0
原创粉丝点击