Android MediaPlayer库源码解读(1):setDataSource()

前言

MediaPlayer是Android音视频子系统中重要的一个子模块库,其功能丰富,且与Audio库联系紧密,是Android系统音视频子系统学习的第一站。源码解读需要具备相关Java、JNI、C++、Binder等知识,较为复杂难懂,简单记录追踪流程。

java层

【frameworks/base/media/java/android/media/MediaPlayer.java】
**setDataSource()**在MediaPlayer.java中用户可直接调用的方法有四个:

  • public void setDataSource(String path)
  • public void setDataSource(FileDescriptor fd)
  • public void setDataSource(FileDescriptor fd, long offset, long length)
  • public void setDataSource(MediaDataSource dataSource)

下面就第一个setDataSource(String path)进行具体分析,其字符串可以是本地文件路径也可以是网络url。

    public void setDataSource(String path) //传参1,本地文件地址或网络地址
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        setDataSource(path, null, null);  //调用1-1 重载函数
    }

可以看到,上面的代码调用了内部函数setDataSource(String path, Map<String, String> headers, List cookies)。

    @UnsupportedAppUsage //被调用1-1
    private void setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
    {	
		//声明并初始化键值对数组
        String[] keys = null;
        String[] values = null;
		
		//赋值
        if (headers != null) {
            keys = new String[headers.size()];
            values = new String[headers.size()];

            int i = 0;
			//遍历传入的headers这个map集合的键值对数组中
            for (Map.Entry<String, String> entry: headers.entrySet()) {
                keys[i] = entry.getKey();
                values[i] = entry.getValue();
                ++i;
            }
        }
		//调用1-2
        setDataSource(path, keys, values, cookies);
    }

然后可以看到其继续调用了内部函数setDataSource(String path, String[] keys, String[] values, List cookies)。

 @UnsupportedAppUsage  //被调用1-2
    private void setDataSource(String path, String[] keys, String[] values,
            List<HttpCookie> cookies)
            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
        final Uri uri = Uri.parse(path);  //使用Uri解析文件路径
        final String scheme = uri.getScheme();//如果本地路径,函数返回值为file,如果是网络地址,返回值为"http"
        if ("file".equals(scheme)) { 	
            path = uri.getPath();		//本地路径,直接赋值给path
        } else if (scheme != null) {   
            // handle non-file sources
            nativeSetDataSource(		//如果是网络地址,调用本地方法nativeSetDataSource
                MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
                path,
                keys,
                values);
            return;
        }
		//后面的处理path为本地文件的情况
        final File file = new File(path);	//传入本地path
        try (FileInputStream is = new FileInputStream(file)) {
            setDataSource(is.getFD());  //调用1-3
        }
    }

可以看到其后续又调用了内部类setDataSource(FileDescriptor fd) 。

    public void setDataSource(FileDescriptor fd) //被调用1-3
           throws IOException, IllegalArgumentException, IllegalStateException {
       // intentionally less than LONG_MAX
       setDataSource(fd, 0, 0x7ffffffffffffffL); //调用1-4 传入(文件描述符、偏移量0,预先设置的最大播放位数)
   }

随后,其调用了内部函数setDataSource(FileDescriptor fd, long offset, long length)。

    public void setDataSource(FileDescriptor fd, long offset, long length) //被调用1-4
            throws IOException, IllegalArgumentException, IllegalStateException {
        _setDataSource(fd, offset, length); //调用1-5 native方法
    }

紧接着其调用了内部函数_setDataSource(FileDescriptor fd, long offset, long length),其是native方法,至此进入JNI层。不了解的同学可以先行学习jni。

	//被调用1-5
    private native void _setDataSource(FileDescriptor fd, long offset, long length) //进入jni
            throws IOException, IllegalArgumentException, IllegalStateException;

JNI层

JNI是Android中Java层与c++层之间的胶水层,负责JNI与C++的通信,其代码位于:【frameworks/base/media/jni/android_media_MediaPlayer.cpp】。
首先,android_media_MediaPlayer.cpp中会对_setDataSource函数进行一次换名。

static const JNINativeMethod gMethods[] = {   // 函数映射,对函数换名
    {
        "nativeSetDataSource",
        "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
        "[Ljava/lang/String;)V",
        (void *)android_media_MediaPlayer_setDataSourceAndHeaders
    },

    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD}, //换名3
    ...

经此,_setDataSource 函数换民为android_media_MediaPlayer_setDataSourceFD函数。

static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length) //衔接_setDataSource
{	
	//调用getMediaPlayer获取上层java环境中的MediaPlayer对象指针
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz); //创建MediaPlayer对象
	//判断指针非空
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
	//判断传入的文件描述符非空
    if (fileDescriptor == NULL) {
        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
        return;
    }
    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    ALOGV("setDataSourceFD: fd %d", fd);
    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
	//1-6 调用native层的 setDataSource(int fd, int64_t offset, int64_t length)  
}

通过 mp->setDataSource可见,其最后调用了native层的函数setDataSource(int fd, int64_t offset, int64_t length)。

native层

//被jni android_media_MediaPlayer_setDataSourceFD 调用  被调用1-6
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
    //用于记录framework APIs返回成功与否
	ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);
    status_t err = UNKNOWN_ERROR;
	//此处开始涉及Binder通信
	//getMediaPlayerService()函数定义域framework/av/media/libmedia/IMediaDeathNotifier.cpp中
	//getMediaPlayerService()返回的是MediaPlayerService服务在客户端的代理对象BpMediaPlayerService
    const sp<IMediaPlayerService> (getMediaPlayerService()); //调用服务端
    if (service != 0) {
		//调用IMediaPlayerService中的create()函数查询服务
		//并返回一个调用IMediaPlayer对象
        sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
		
        if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
            (NO_ERROR != player->setDataSource(fd, offset, length))) {  //1-7调用服务端的setDataSource(int fd, int64_t offset, int64_t length)方法
            player.clear();
        }
		//重置一些变量,更新MediaPlayer对象
        err = attachNewPlayer(player);
    }
    return err;
}

从const sp (getMediaPlayerService());
这句中可以得知,其设计Binder通信,IMediaPlayerService是Binder驱动层,IMediaPlayerService.h和IMediaPlayerService.cpp中包含一些接口定义。不了解Binder通信机制的可以先行学习Binder,此处我们再去服务端MediaPlayerService.cpp中去看status_t setDataSource(int fd, int64_t offset, int64_t length)函数。

//被调用1-7
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{
    ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld",
            fd, nameForFd(fd).c_str(), (long long) offset, (long long) length);
	//stat结构体主要用来描述系统文件中文件的属性结构
	//列如, 修改时间,文件用户标识等
    struct stat sb;
	//fstat获取文件相关信息保持在stat结构体中,为0表示返回成功
    int ret = fstat(fd, &sb);
    if (ret != 0) {
        ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
        return UNKNOWN_ERROR;
    }

    ALOGV("st_dev  = %llu", static_cast<unsigned long long>(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 = %llu", static_cast<unsigned long long>(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 = %lld", (long long)length);
    }
	//调用getPlayerType函数,通过评分机制,获取最优播放器类型
	//MediaPlayerFactory.h
    player_type playerType = MediaPlayerFactory::getPlayerType(this,
                                                               fd,
                                                               offset,
    //创建播放器                                                           length);
    sp<MediaPlayerBase> p = setDataSource_pre(playerType); //调用1-8
    if (p == NULL) {
        return NO_INIT;
    }

    // now set data source
	//在创建的播放器中,设置播放器的数据源
    return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
}

可以看到服务端实现的setDataSource()函数流程也比较清楚,在调用MediaPlayerFact::getPlayerType()处,使用评分机制选择最优的播放器类型,MediaPlayerFact.h中定义。此外还调用了内部函数setDataSource_pre( player_type playerType)

//被调用1-8
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;
    }

	//...........
	//...........
	//...........
	
    Mutex::Autolock lock(mLock);

    mDeathNotifiers.clear();
    mDeathNotifiers.swap(deathNotifiers);
    mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);
	
	//NU_PLAYER播放器的实现在Nuplayer.cpp中 setAudioSink
	
    if (!p->hardwareOutput()) {
        mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),
                mPid, mAudioAttributes, mAudioDeviceUpdatedListener);
        static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
    }

    return p;
}

其中createPlayer(playerType)函数调用类内函数createPlayer(player_type playerType)

//被调用
sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{
    // determine if we have the right player type
    sp<MediaPlayerBase> p = getPlayer();
    if ((p != NULL) && (p->playerType() != playerType)) {
        ALOGV("delete player");
        p.clear();
    }
    if (p == NULL) {
		//根据播放器类型创建播放器,并设置监听和pid号
        p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);
    }
	//设置uid
    if (p != NULL) {
        p->setUID(mUid);
    }

    return p;
}

其中调用了MediaPlayerFactory中的createPlayer去创建播放器
【./frameworks av media libmediaplayerservice MediaPlayerFactory.cpp】

 virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {
        ALOGV(" create NuPlayer");
        //创建播放器
        return new NuPlayerDriver(pid);
    }

这里调用的是NuPlayerDriver的构造函数去创建播放器。
【.frameworks av media libmediaplayerservice nuplayer NuPlayerDriver.cpp】

NuPlayerDriver::NuPlayerDriver(pid_t pid)
    : mState(STATE_IDLE),
      mIsAsyncPrepare(false),
      mAsyncResult(UNKNOWN_ERROR),
      mSetSurfaceInProgress(false),
      mDurationUs(-1),
      mPositionUs(-1),
      mSeekInProgress(false),
      mPlayingTimeUs(0),
      mRebufferingTimeUs(0),
      mRebufferingEvents(0),
      mRebufferingAtExit(false),
      mLooper(new ALooper),
      mMediaClock(new MediaClock),
      mPlayer(new NuPlayer(pid, mMediaClock)),
      mPlayerFlags(0),
      mAnalyticsItem(NULL),
      mClientUid(-1),
      mAtEOS(false),
      mLooping(false),
      mAutoLoop(false) {
    ALOGD("NuPlayerDriver(%p) created, clientPid(%d)", this, pid);
    mLooper->setName("NuPlayerDriver Looper");

    mMediaClock->init();

    // set up an analytics record
    mAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);

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

    mLooper->registerHandler(mPlayer);

    mPlayer->init(this);
}

在构造函数的初始化列表中调用了NuPlayer的构造函数去初始化NuPlayer对象,这就是实际的播放器对象。在NuPlayer的构造函数也会以初始化列表的方式完成一些变量的初始化赋值操作。具体的setDataSource也是在Nuplay中实现的。

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);
    }

    return mAsyncResult;
}

再通过setDataSourceAsync(httpService, url, headers),去到NuPlayer

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);
	
	//重置Source数据
    sp<GenericSource> source =
            new GenericSource(notify, mUIDValid, mUID, mMediaClock);

    ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",
            fd, (long long)offset, (long long)length, source.get());
	
	//调用GenericSource中的setDataSource方法
    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();
    mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
}

调用了GenericSource的setDataSource方法。
【./frameworks av media libmediaplayerservice nuplayer GenericSource.cpp】

status_t NuPlayer::GenericSource::setDataSource(
        int fd, int64_t offset, int64_t length) {
    Mutex::Autolock _l(mLock);
    ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);
	
	//重置相关变量
    resetDataSource();
	
	//dup函数是返回一个指向同一个文件的文件描述符
    mFd = dup(fd);
    mOffset = offset;
    mLength = length;

    // delay data source creation to prepareAsync() to avoid blocking
    // the calling thread in setDataSource for any significant time.
    return OK;
}

随后NuPlayer使用消息的形式:

void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
        case kWhatSetDataSource:
        {
            ALOGV("kWhatSetDataSource");

            CHECK(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;
            }

            CHECK(mDriver != NULL);
            sp<NuPlayerDriver> driver = mDriver.promote();
            if (driver != NULL) {
                driver->notifySetDataSourceCompleted(err);
            }
            break;
        }

mSource为GenericSource对象,Source设置完成则变更mState的状态为:

void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
    Mutex::Autolock autoLock(mLock);

    CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);

    mAsyncResult = err;
    //更改mState的状态为STATE_UNPREPARED 
    mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
    mCondition.broadcast();
}

至此,setDataSource()的流程结束,但播放器的状态还是STATE_UNPREPARED。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值