• MediaPlayer本地播放流程解析(一)


    应用场景:

    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
    	@Override
    	public void onCompletion(MediaPlayer mp) {
    		mediaPlayer.release();
    		mediaPlayer = null;
    	}
    });
    mediaPlayer.setDataSource(“abc.mp3”);
    mediaPlayer.prepare();
    mediaPlayer.start();
    

    一、setDataSource

    在MediaPlayer.java 中

    public void setDataSource(FileDescriptor fd, long offset, long length)
        throws IOException, IllegalArgumentException, IllegalStateException {
        disableProxyListener();
        setDataSource(fd, offset, length);
    }
    

    setDataSource终于调用了native函数:_setDataSource(fd,offset, length);

    我们直接跳到JNI层来看它的详细实现

    依据JNI相关的知识,在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);
        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." );
    }
    
    mp为MediaPlayer类型的对象,在JNI层创建,在MediaPlayer.cpp中。一起来看setDataSource的实现。

    status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
    {
        status_t err = UNKNOWN_ERROR;
        const sp<IMediaPlayerService>& service(getMediaPlayerService());
        if (service != 0) {
            sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
            if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
                (NO_ERROR != player->setDataSource(fd, offset, length))) {
                player.clear();
            }
            err = attachNewPlayer(player);
        }
        return err;
    }
    

    getMediaPlayerService()为一个典型的Binder机制向ServiceManager获取服务的方法,Binder这方面的知识能够參考http://blog.csdn.net/super_dc/article/details/37738123http://blog.csdn.net/super_dc/article/details/37764947

    service->create(this, mAudioSessionId),先看create方法在IMediaPlayerService.cpp中的实现:

    virtual sp<IMediaPlayer> create(
            const sp<IMediaPlayerClient>& client, int audioSessionId) {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
        data.writeStrongBinder(client->asBinder());
        data.writeInt32(audioSessionId);
    
        remote()->transact(CREATE, data, &reply);
        return interface_cast<IMediaPlayer>(reply.readStrongBinder());
    }
    
    这里仅仅是Binder客户端的实现。其终于实现会在MediaPlayerService.cpp中由服务端MediaPlayerService来实现。


    sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,
            int audioSessionId)
    {
        pid_t pid = IPCThreadState::self()->getCallingPid();
        int32_t connId = android_atomic_inc(&mNextConnId);
    
        sp<Client> 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());
        /* add by Gary. start {{----------------------------------- */
        c->setScreen(mScreen);
        /* add by Gary. end   -----------------------------------}} */
        c->setSubGate(mGlobalSubGate);  // 2012-03-12, add the global interfaces to control the subtitle gate
    
        wp<Client> w = c;
        {
            Mutex::Autolock lock(mLock);
            mClients.add(w);
        }
        return c;
    }
    

    综合上面两点,sp<IMediaPlayer>player(service->create(this, mAudioSessionId));中player实际上是一个Client类型对象的proxy。

    其详细实现都在Client中实现。

    player->setDataSource(fd, offset, length)就能够直接到MediaPlayerService.cpp中的Client类中来看其详细实现了。

    status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t 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;
        }
    
        if (offset >= sb.st_size) {
            ALOGE("offset error");
            ::close(fd);
            return UNKNOWN_ERROR;
        }
        if (offset + length > sb.st_size) {
            length = sb.st_size - offset;
            ALOGV("calculated length = %lld", length);
        }
        // 关键点1
        player_type playerType = MediaPlayerFactory::getPlayerType(this,
                                                                   fd,
                                                                   offset,
                                                                   length);
        // 关键点2
        sp<MediaPlayerBase> p = setDataSource_pre(playerType);
        if (p == NULL) {
            return NO_INIT;
        }
    
        // now set data source
        // 关键点3
        setDataSource_post(p, p->setDataSource(fd, offset, length));
        return mStatus;
    }
    
    这里有3个关键点。我们分别破解之,先看getPlayerType

    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;
    

    MediaPlayerFactory作为一个工厂类,各种mediaplayer向它注冊。并各自实现scoreFactory和createPlayer用来推断当前多媒体文件是否适合用此mediaplayer来播放和创建mediaplayer。在哪儿注冊mediaplayer呢?在MediaPlayerService的构造函数中,也就是说当向系统注冊MediaPlayerService服务时,就已经注冊了一些mediaplayer了。

    播放mp3文件时。会创建STAGEFRIGHT_PLAYER,这也是默认的播放器。

    以下就以STAGEFRIGHT_PLAYER来继续以下的流程。

    到眼下为止,我们知道playerType返回了STAGEFRIGHT_PLAYER。接着来看关键点2.

    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;
        }
    
        if (!p->hardwareOutput()) {
            mAudioOutput = new AudioOutput(mAudioSessionId);
            static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
        }
    
        return p;
    }
    

    依据playerType创建播放器,实际上就是创建StagefrightPlayer

    再看关键点3,p->setDataSource(fd,offset, length)实际上就是调用了StagefrightPlayer的setDataSource。

    看代码:

    StagefrightPlayer::StagefrightPlayer()
        : mPlayer(new AwesomePlayer) {
        ALOGV("StagefrightPlayer");
    
        mPlayer->setListener(this);
    }
    status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
        ALOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
        return mPlayer->setDataSource(dup(fd), offset, length);
    }
    
    由代码可知。StagefrightPlayer仅仅是AwesomePlayer的代理类,详细实现还在AwesomePlayer里面。

    status_t AwesomePlayer::setDataSource(
            int fd, int64_t offset, int64_t length) {
        Mutex::Autolock autoLock(mLock);
    
        reset_l();
    
        sp<DataSource> dataSource = new FileSource(fd, offset, length);
    
        status_t err = dataSource->initCheck();
    
        if (err != OK) {
            return err;
        }
    
        mFileSource = dataSource;
    
        {
            Mutex::Autolock autoLock(mStatsLock);
            mStats.mFd = fd;
            mStats.mURI = String8();
        }
    
        return setDataSource_l(dataSource);
    }
    
    FileSource类实现了数据读取,播放器调用dataSource->readAt来获取数据,另外,其基类DataSource提供了一些分离器例如以下。

    RegisterDefaultSniffers将在AwesomePlayer的构造函数中被调用。

    // static
    void DataSource::RegisterDefaultSniffers() {
        RegisterSniffer(SniffMPEG4);
        RegisterSniffer(SniffMatroska);
        RegisterSniffer(SniffOgg);
        RegisterSniffer(SniffWAV);
        RegisterSniffer(SniffFLAC);
        RegisterSniffer(SniffAMR);
        RegisterSniffer(SniffMPEG2TS);
        RegisterSniffer(SniffMP3);
        RegisterSniffer(SniffAAC);
        RegisterSniffer(SniffMPEG2PS);
        RegisterSniffer(SniffWVM);
    
        char value[PROPERTY_VALUE_MAX];
        if (property_get("drm.service.enabled", value, NULL)
                && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
            RegisterSniffer(SniffDRM);
        }
    }
    
    接着往下看setDataSource_l(dataSource)

    status_t AwesomePlayer::setDataSource_l(
            const sp<DataSource> &dataSource) {
    
        // 对于不同的文件格式会创建不同的MediaExtractor。MP3文件会创建MP3Extractor
        // 文件格式靠source->sniff(&tmp, &confidence, &meta)来区分。这个函数会遍历之前通过RegisterSniffer注冊的分离器,得到最合适的文件格式
        sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
    
        if (extractor == NULL) {
            return UNKNOWN_ERROR;
        }
    
        if (extractor->getDrmFlag()) {
            checkDrmStatus(dataSource);
        }
    
        return setDataSource_l(extractor);
    }
    
    status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
        // Attempt to approximate overall stream bitrate by summing all
        // tracks' individual bitrates, if not all of them advertise bitrate,
        // we have to fail.
    
        int64_t totalBitRate = 0;
    
        mExtractor = extractor;
        for (size_t i = 0; i < extractor->countTracks(); ++i) {
            sp<MetaData> meta = extractor->getTrackMetaData(i);
    
            int32_t bitrate;
            if (!meta->findInt32(kKeyBitRate, &bitrate)) {
                const char *mime;
                CHECK(meta->findCString(kKeyMIMEType, &mime));
                ALOGV("track of type '%s' does not publish bitrate", mime);
    
                totalBitRate = -1;
                break;
            }
    
            totalBitRate += bitrate;
        }
    
        mBitrate = totalBitRate;
    
        ALOGV("mBitrate = %lld bits/sec", mBitrate);
    
        {
            Mutex::Autolock autoLock(mStatsLock);
            mStats.mBitrate = mBitrate;
            mStats.mTracks.clear();
            mStats.mAudioTrackIndex = -1;
            mStats.mVideoTrackIndex = -1;
        }
    
        bool haveAudio = false;
        bool haveVideo = false;
        for (size_t i = 0; i < extractor->countTracks(); ++i) {
            sp<MetaData> meta = extractor->getTrackMetaData(i);
    
            const char *_mime;
            CHECK(meta->findCString(kKeyMIMEType, &_mime));
    
            String8 mime = String8(_mime);
    
            if (!haveVideo && !strncasecmp(mime.string(), "video/", 6)) {
                setVideoSource(extractor->getTrack(i));
                haveVideo = true;
    
                // Set the presentation/display size
                int32_t displayWidth, displayHeight;
                bool success = meta->findInt32(kKeyDisplayWidth, &displayWidth);
                if (success) {
                    success = meta->findInt32(kKeyDisplayHeight, &displayHeight);
                }
                if (success) {
                    mDisplayWidth = displayWidth;
                    mDisplayHeight = displayHeight;
                }
    
                {
                    Mutex::Autolock autoLock(mStatsLock);
                    mStats.mVideoTrackIndex = mStats.mTracks.size();
                    mStats.mTracks.push();
                    TrackStat *stat =
                        &mStats.mTracks.editItemAt(mStats.mVideoTrackIndex);
                    stat->mMIME = mime.string();
                }
            } else if (!haveAudio && !strncasecmp(mime.string(), "audio/", 6)) {
                setAudioSource(extractor->getTrack(i));
                haveAudio = true;
                mActiveAudioTrackIndex = i;
    
                {
                    Mutex::Autolock autoLock(mStatsLock);
                    mStats.mAudioTrackIndex = mStats.mTracks.size();
                    mStats.mTracks.push();
                    TrackStat *stat =
                        &mStats.mTracks.editItemAt(mStats.mAudioTrackIndex);
                    stat->mMIME = mime.string();
                }
    
                if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_AUDIO_VORBIS)) {
                    // Only do this for vorbis audio, none of the other audio
                    // formats even support this ringtone specific hack and
                    // retrieving the metadata on some extractors may turn out
                    // to be very expensive.
                    sp<MetaData> fileMeta = extractor->getMetaData();
                    int32_t loop;
                    if (fileMeta != NULL
                            && fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
                        modifyFlags(AUTO_LOOPING, SET);
                    }
                }
            } else if (!strcasecmp(mime.string(), MEDIA_MIMETYPE_TEXT_3GPP)) {
                addTextSource_l(i, extractor->getTrack(i));
            }
        }
    
        if (!haveAudio && !haveVideo) {
            if (mWVMExtractor != NULL) {
                return mWVMExtractor->getError();
            } else {
                return UNKNOWN_ERROR;
            }
        }
    
        mExtractorFlags = extractor->flags();
    
        return OK;
    }
    
    MediaExtractor涉及到媒体文件格式的非常多内容,比方track的构成。有几种track等等。后面再做解说,这里我们播放的是MP3文件,所以countTracks的值为1。sp<MetaData> meta = extractor->getTrackMetaData(i)中meta的kKeyMIMEType值为"audio/",将会运行到setAudioSource(extractor->getTrack(i)),再看代码:

    sp<MediaSource> MP3Extractor::getTrack(size_t index) {
        if (mInitCheck != OK || index != 0) {
            return NULL;
        }
    	// 返回的是一个MP3Source对象
        return new MP3Source(
                mMeta, mDataSource, mFirstFramePos, mFixedHeader,
                mSeeker);
    }
    
    void AwesomePlayer::setAudioSource(sp<MediaSource> source) {
        CHECK(source != NULL);
    
        mAudioTrack = source;
    }
    
    至此,setdatasource就分析完毕,下一篇将分析prepare的实现过程。


  • 相关阅读:
    iOS中的NSTimer 和 Android 中的Timer
    正则表达式中*的使用小注意
    NSUrlConnection 和 NSUrlRequest 的关系
    iOS 中的第三方库管理工具
    Android 向Application对象添加Activity监听
    Android dp px转化公式
    Android 返回桌面的Intent
    Spring+SpringMVC+Hibernate小案例(实现Spring对Hibernate的事务管理)
    Equinox OSGi应用嵌入Jersey框架搭建REST服务
    在OSGI容器Equinox中嵌入HttpServer
  • 原文地址:https://www.cnblogs.com/bhlsheji/p/5302756.html
Copyright © 2020-2023  润新知