Android中间层分析2.【media】Android 音视频播放流程-4.MediaPlayer的setDataSource流程

2.3.MediaPlayer的setDataSource流程

上面已经讲了创建流程,已经讲本地的MediaPlayer创建好了,接下来就是给数据。比如给个"/sdcard/test.mp3"或者"/sdcard/test.mp4" 其实在上面来说流程是一样的,想想都知道应该是从:java-》jni-》本地Mediaplayer。

2.3.1JAVA层
[-->MediaPlayer.java]

    public void setDataSource(String path)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        setDataSource(path, null, null);
    }
    
    private void setDataSource(String path, String[] keys, String[] values)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
  ...

        final File file = new File(path);
        if (file.exists()) {
            FileInputStream is = new FileInputStream(file);
            FileDescriptor fd = is.getFD();
            setDataSource(fd);
            is.close();
        } 
    }
    public void setDataSource(FileDescriptor fd)
            throws IOException, IllegalArgumentException, IllegalStateException {
        setDataSource(fd, 0, 0x7ffffffffffffffL);
    }
    public void setDataSource(FileDescriptor fd, long offset, long length)
            throws IOException, IllegalArgumentException, IllegalStateException {
        _setDataSource(fd, offset, length);
    }
    private native void _setDataSource(FileDescriptor fd, long offset, long length)

setDataSource 将path 变成文件描述符fd,最后将fd通过native的_setDataSource设置到下面去。来看下JNI方法,在注册表中看到如下,调用的是android_media_MediaPlayer_setDataSourceFD方法。

2.3.2JNI层
[-->android_media_mediaplayer.cpp]
{"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
[-->android_media_mediaplayer.cpp]
static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
...
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
...
}
static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
{
...
        if ( opStatus == (status_t) INVALID_OPERATION ) {
            jniThrowException(env, "java/lang/IllegalStateException", NULL);
        } else if ( opStatus == (status_t) BAD_VALUE ) {
            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
            jniThrowException(env, "java/lang/SecurityException", NULL);
        } else if ( opStatus != (status_t) OK ) {
            if (strlen(message) > 230) {
               // if the message is too long, don't bother displaying the status code
               jniThrowException( env, exception, message);
            } else {
               char msg[256];
                // append the status code to the message
               sprintf(msg, "%s: status=0x%X", message, opStatus);
               jniThrowException( env, exception, msg);
   ...
}

拿出本地的MedaPlayer对象mp 调用本地的setDataSource,而process_media_player_call,这个方法是检测调用的返回值的,如果返回值有一些其他的异常则抛出异常。

2.3.3本地cpp层
IMediaDeathNotifier::getMediaPlayerService()
{
    ALOGV("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;
            }
            ALOGW("Media player service not published, waiting...");
            usleep(500000); // 0.5 s
        } while (true);

        if (sDeathNotifier == NULL) {
            sDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(sDeathNotifier);
        sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
    }
    ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");
    return sMediaPlayerService;
}

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
...
    const sp<IMediaPlayerService> service(getMediaPlayerService());
    ...
    player->setDataSource(fd, offset, length)...
        err = attachNewPlayer(player);
...
    }
    return err;
}
status_t MediaPlayer::attachNewPlayer(const sp<IMediaPlayer>& player)
{
...
        p = mPlayer;
        mPlayer = player;
...
    return err;
}

这里开始和MediaPlayerService打交道了,通过ServiceManager获取到MediaPlayerService,然后setDataSource,最后attachNewPlayer将其保持在变量中。注意在获取到MediaPlayerService 后建立了个DeathNotifier防止断开。

2.3.4 MediaPlayerService层
[-->MediaPlayerService.cpp]
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));
...
    return mStatus;
}
  1. 通过MediaPlayerFactory工程方法获取播放器的类型
  2. setDataSource_pre 创建播放器
  3. 调用创建的播放器设置DataSource
2.3.4.1 通过MediaPlayerFactory工程方法获取播放器的类型
[-->MediaPlayerFactory.cpp]
player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,
                                              int fd,
                                              int64_t offset,
                                              int64_t length) {
    GET_PLAYER_TYPE_IMPL(client, fd, offset, length);
}

通过一个宏定义;

#define GET_PLAYER_TYPE_IMPL(a...)                      \
    Mutex::Autolock lock_(&sLock);                      \
                                                        \
    player_type ret = STAGEFRIGHT_PLAYER;               \
    float bestScore = 0.0;                              \
                                                        \
    for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \
                                                        \
        IFactory* v = sFactoryMap.valueAt(i);           \
        float thisScore;                                \
        CHECK(v != NULL);                               \
        thisScore = v->scoreFactory(a, bestScore);      \
        if (thisScore > bestScore) {                    \
            ret = sFactoryMap.keyAt(i);                 \
            bestScore = thisScore;                      \
        }                                               \
    }                                                   \
                                                        \
    if (0.0 == bestScore) {                             \
        ret = getDefaultPlayerType();                   \
    }                                                   \
                                                        \
    return ret;
    
static player_type getDefaultPlayerType() {
    return NU_PLAYER;
}

这个宏定义和《启动:2.1.1 mediaserver进程的启动》中是对应的,将sFactoryMap中的player类型通过一些比较算法取出来,我们这边将使用最后的getDefaultPlayerType 返回的NU_PLAYER类型进行分析。

2.3.4.2 setDataSource_pre 创建播放器
sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
        player_type playerType)
{
...
    sp<MediaPlayerBase> p = createPlayer(playerType);
    if (p == NULL) {
        return p;
    }
...
...
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
...

    return p;
}
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
...
    if (p == NULL) {
        p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid);
    }
...
    return p;
}
sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(
...
    factory = sFactoryMap.valueFor(playerType);
    CHECK(NULL != factory);
    p = factory->createPlayer(pid);
...

    init_result = p->initCheck();
    if (init_result == NO_ERROR) {
        p->setNotifyCallback(cookie, notifyFunc);
    } 

    return p;
}

这里createPlayer 传进入的是NU_PLAYER 类型,并且最后sFactoryMap中取得是NuPlayerDriver—根据《启动:2.1.1 mediaserver进程的启动》
这里还需要注意两点,

  • 1.创建时还设置了(p->setNotifyCallback)notify,它是个函数,用于往上传递信息的。
  • 2.并且还创建了音频输出对象,static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput)这个将用于音频的输出
2.3.4.3 调用创建的播放器设置DataSource
[-->MediaPlayerFactory.cpp]
class NuPlayerFactory : public MediaPlayerFactory::IFactory {
...

    virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
        ALOGV(" create NuPlayer");
        return new NuPlayerDriver(pid);
    }
...
};

通过NuPlayerFactory 创建出来的是NuPlayerDriver,也就是上面的p ,然后p调用了setDataSource。

2.3.5 NuPlayer层
[-->NuPlayerDriver.cpp]
NuPlayerDriver::NuPlayerDriver(pid_t pid)
    : mState(STATE_IDLE),
...
      mLooper(new ALooper),
      mAutoLoop(false) {
...
    mLooper->setName("NuPlayerDriver Looper");

    mLooper->start(
            false, /* runOnCallingThread */
            true,  /* canCallJava */
            PRIORITY_AUDIO);

    mPlayer = new NuPlayer(pid);
    mLooper->registerHandler(mPlayer);

    mPlayer->setDriver(this);
...
}
status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
    ALOGV("setDataSource(%p) file(%d)", this, fd);
...

    mPlayer->setDataSourceAsync(fd, offset, length);

...

    return mAsyncResult;
}

最终会建立一个底层的Player 叫做Nuplayer ,并调用setDataSourceAsync。将上层的数据交给了Nu;这里需要注意的另一处,有个ALooper 这个loop是用于,消息传递的,这个细节将在后面的章节讲到。

2.3.5.1 最后的归属Nuplayer

在上一节中新建了个NuPlayer并将NuPlayerDriver的this传了进来。

[-->NuPlayer.cpp]
NuPlayer::NuPlayer(pid_t pid)
    : mUIDValid(false),
      mPID(pid),
...
	  mToneIsSpeech(0) {
...
  init_ext();
}

void NuPlayer::init_ext(){
    mVideoDecoder = NULL;//视频解码器变量
    mAudioDecoder = NULL;//音频解码器变量
    mRenderer = NULL;//
   ...
}

NuPlayer 构造中没有做什么事情。就一些变量的初始化。

[-->NuPlayer.cpp]
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
    sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
    sp<GenericSource> source =
            new GenericSource(notify, mUIDValid, mUID);
    status_t err = source->setDataSource(fd, offset, length);
    msg->post();
}
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatSetDataSource:
        {
 ...
        if(mSource == NULL) {
...
            status_t err = OK;
            sp<RefBase> obj;
            CHECK(msg->findObject("source", &obj));
            if (obj != NULL) {
                Mutex::Autolock autoLock(mSourceLock);
                mSource = static_cast<Source *>(obj.get());
            } else {
                err = UNKNOWN_ERROR;
            }
...
            break;
        }
}

生成一个GenericSource,并调用GenericSource的setDataSource,将fd 传下去。在msg->post()后消息处理将刚才创建的对象保存到mSource。中

[-->GenericSource.cpp]
NuPlayer::GenericSource::GenericSource(

    : Source(notify),
    {
    ...
    }
    
status_t NuPlayer::GenericSource::setDataSource(
        int fd, int64_t offset, int64_t length) {
...
    mFd = dup(fd);
    mOffset = offset;
    mLength = length;
...
    return OK;
}

GenericSource 的构造中没有做啥事情,setDataSource也就是将上面传下来的变量进行了保存。到此整个setDataSource就讲完了,难么回头想想都经历了什么。从上到下我们总结下经过的主要类

-->Mediaplayer.java
-->android_media_MediaPlayer.cpp
-->mediaplayer.cpp
-->MediaPlayerService.cpp
---->MediaPlayerFactory.cpp
-->NuPlayerDirvier.cpp
-->Nuplayer.cpp;
-->GenericSource.cpp

从流程上其实没什么太难的地方,就是将path 转成的fd 最后放到GenericSource中。但是其中间出现的一些技术要点需要好好记住的,
比如
1、关于创建的AudioOutPut ,它是用于音频输出的
2、关于传进去的notify函数,它是用于消息回调的
这些目前还用不到,但到后面的章节中是特别有用过的细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沈万三djh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值