AwesomePlayer的准备工作

来源:互联网 发布:魏志香 知乎 编辑:程序博客网 时间:2024/05/02 01:22

1. 前提条件

本文以播放本地文件为例,且setDataSource时传入的是文件的url地址。

在Java中,若要播放一个本地文件,其代码如下:

MediaPlayer mp = new MediaPlayer();
mp.setDataSource(PATH_TO_FILE); ...... (1)
mp.prepareAsync(); ........................ (2)、(3)
当收到视频准备完毕,收到OnPreparedListener时
mp.start(); .......................... (4)

在AwesomePlayer中,则会看到相应的处理;

 

2. AwesomePlayer::setDataSource

为了能播放本地文件,需要通过AwesomePlayer::setDataSource来告诉AwesomePlayer播放url地址,AwesomePlayer也只是简单地把此url地址存入mUri和mStats.mURI中以备在prepare时使用。

 

3. AwesomePlayer::prepareAsync或AwesomePlayer::prepare

3.1 mQueue.start();

      表面看是启动一个队例,实质上是创建了一个线程,此程入口函数为:TimedEventQueue::ThreadWrapper。真正的线程处理函数为:TimedEventQueue::threadEntry, 从TimedEventQueue::mQueue队列中读取事件,然后调用event->fire处理此事件。TimedEventQueue中的每一个事件都带有触发此事件的绝对时间,到时间之后才执行此事件的fire.

     TimedEventQueue::Event的fire是一个纯虚函数,其实现由其派生类来实现,如在AwesomePlayer::prepareAsync_l中,创建了一个AwesomeEvent,然后通过mQueue.postEvent把事件发送到mQueue中,此时,fire函数为AwesomePlayer::onPrepareAsyncEvent.

3.2 AwesomePlayer::onPrepareAsyncEvent被执行

     根据上面的描述,把事件发送到队列之后,队列线程将读取此线程的事件,然后执行event的fire. 3.1中事件的fire函数为AwesomePlayer::onPrepareAsyncEvent,其代码为: 

[cpp] view plaincopy
  1. void AwesomePlayer::onPrepareAsyncEvent() {  
  2.     Mutex::Autolock autoLock(mLock);  
  3.     ....  
  4.     if (mUri.size() > 0) { //获取mAudioTrack和mVideoTrack  
  5.         status_t err = finishSetDataSource_l();    ---3.2.1   
  6.         ...  
  7.     }  
  8.     if (mVideoTrack != NULL && mVideoSource == NULL) { //获取mVideoSource  
  9.         status_t err = initVideoDecoder();             ---3.2.2  
  10.         ...  
  11.     }  
  12.     if (mAudioTrack != NULL && mAudioSource == NULL) { //获取mAudioSource  
  13.         status_t err = initAudioDecoder();              ---3.2.3  
  14.         ...  
  15.     }  
  16.     modifyFlags(PREPARING_CONNECTED, SET);  
  17.     if (isStreamingHTTP() || mRTSPController != NULL) {  
  18.         postBufferingEvent_l();  
  19.     } else {  
  20.         finishAsyncPrepare_l();  
  21.     }  
  22. }  


 3.2.1 finishSetDataSource_l

 

[cpp] view plaincopy
  1. {  
  2.   dataSource = DataSource::CreateFromURI(mUri.string(), ...); (3.2.1.1)  
  3.   sp<MediaExtractor> extractor =  
  4.                      MediaExtractor::Create(dataSource); ..... (3.2.1.2)  
  5.   
  6.   return setDataSource_l(extractor); ......................... (3.2.1.3)  
  7. }  

3.2.1.1 创建dataSource

a. 对于本地文件(http://,https://,rtsp://实现方式不一样)的实现方式如下:

dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);

根据url创建dataSource,它实际上new了一个FileSource。当new FileSource时,它打开此文件:

mFd = open(filename, O_LARGEFILE | O_RDONLY);

b. 对于http://和https://,则new一个ChromiumHTTPDataSource,

这些类之间的派生关系如下图所示:

3.2.1.2 创建一个MediaExtractor

       创建MediaExtractor::Create中创建真正的MediaExtractor,以下以MPEG2TSExtractor为例,它解析TS流,它也是一个空架子,它有传入的mDataSource给它读数据,并创建了一个mParser(ATSParser)来真正的数据解析。在此过程中产生的对象即拥有关系为:

       MPEG2TSExtractor->ATSParser->ATSParser::Program->ATSParser::Stream->AnotherPacketSource

 

       extractor = MediaExtractor::Create(dataSource);它解析source所指定的文件,并且根据其header来选择extractor(解析器)。其代码如下:

[cpp] view plaincopy
  1. sp<MediaExtractor> MediaExtractor::Create(  
  2.         const sp<DataSource> &source, const char *mime) {  
  3.     sp<AMessage> meta;  
  4.     String8 tmp;  
  5.     if (mime == NULL) {  
  6.         float confidence;  
  7.         if (!source->sniff(&tmp, &confidence, &meta)) {  
  8.             return NULL;  
  9.         }  
  10.   
  11.         mime = tmp.string();  
  12.     }  
  13.     ...  
  14.     MediaExtractor *ret = NULL;  
  15.     if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)  
  16.             || !strcasecmp(mime, "audio/mp4")) {  
  17.         ret = new MPEG4Extractor(source);  
  18.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {  
  19.         ret = new MP3Extractor(source, meta);  
  20.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)  
  21.             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {  
  22.         ret = new AMRExtractor(source);  
  23.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {  
  24.         ret = new FLACExtractor(source);  
  25.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {  
  26.         ret = new WAVExtractor(source);  
  27.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {  
  28.         ret = new OggExtractor(source);  
  29.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {  
  30.         ret = new MatroskaExtractor(source);  
  31.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {  
  32.         ret = new MPEG2TSExtractor(source);  
  33.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_AVI)) {  
  34.         ret = new AVIExtractor(source);  
  35.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {  
  36.         ret = new WVMExtractor(source);  
  37.     } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {  
  38.         ret = new AACExtractor(source);  
  39.     }  
  40.     if (ret != NULL) {  
  41.        if (isDrm) {  
  42.            ret->setDrmFlag(true);  
  43.        } else {  
  44.            ret->setDrmFlag(false);  
  45.        }  
  46.     }  
  47.     ...  
  48.     return ret;  
  49. }  

       当然对于TS流,它将创建一个MPEG2TSExtractor并返回。

       当执行new MPEG2TSExtractor(source)时:

       1) 把传入的FileSource对象保存在MEPG2TSExtractor的mDataSource成没变量中

       2) 创建一个ATSParser并保存在mParser中,它负责TS文件的解析,

       3) 在feedMore中,通过mDataSource->readAt从文件读取数据,把读取的数据作为mParser->feedTSPacket的参数,它将分析PAT表(ATSParser::parseProgramAssociationTable)而找到并创建对应的Program,并把Program存入ATSParser::mPrograms中。每个Program有一个唯一的program_number和programMapPID.

         扫盲一下,PAT中包含有所有PMT的PID,一个Program有一个对应的PMT,PMT中包含有Audio PID和Video PID.  

         ATSParser::Program::parseProgramMap中,它分析PMT表,并分别根据Audio和Video的PID,为他们分别创建一个Stream。然后把新创建的Stream保存在ATSParser::Program的mStreams成员变量中。

       ATSParser::Stream::Stream构造函数中,它根据媒体类型,创建一个类型为ElementaryStreamQueue的对象mQueue;并创建一个类型为ABuffer的对象mBuffer(mBuffer = new ABuffer(192 * 1024);)用于保存数据 。

       注:ATSParser::Stream::mSource<AnotherPacketSource>创建流程为:

MediaExtractor::Create->

  MPEG2TSExtractor::MPEG2TSExtractor->

    MPEG2TSExtractor::init->

     MPEG2TSExtractor::feedMore->

       ATSParser::feedTSPacket->

         ATSParser::parseTS->

           ATSParser::parsePID->

             ATSParser::parseProgramAssociationTable

             ATSParser::Program::parsePID->

               ATSParser::Program::parseProgramMap

               ATSParser::Stream::parse->

                 ATSParser::Stream::flush->

                   ATSParser::Stream::parsePES->

                     ATSParser::Stream::onPayloadData

 

       以上source->sniff函数在DataSource::sniff中实现,这些sniff函数是通过DataSource::RegisterSniffer来进行注册的,如MEPG2TS的sniff函数为:SniffMPEG2TS,其代码如下:
 

[cpp] view plaincopy
  1. bool SniffMPEG2TS(  
  2.         const sp<DataSource> &source, String8 *mimeType, float *confidence,  
  3.         sp<AMessage> *) {  
  4.     for (int i = 0; i < 5; ++i) {  
  5.         char header;  
  6.         if (source->readAt(kTSPacketSize * i, &header, 1) != 1  
  7.                 || header != 0x47) {  
  8.             return false;  
  9.         }  
  10.     }  
  11.   
  12.     *confidence = 0.1f;  
  13.     mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);  
  14.   
  15.     return true;  
  16. }  

        由此可见,这些sniff是根据文件开始的内容来识别各种file container. 比如wav文件通过其头中的RIFF或WAVE字符串来识别。注:在创建player时,是根据url中的相关信息来判断的,而不是文件的内容来判断

 

 3.2.1.3 AwesomePlayer::setDataSource_l(extractor)

       主要逻辑代码如下(当然此extractor实质为MPEG2TSExtractor对象):

[cpp] view plaincopy
  1. status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor)   
  2. {  
  3.    for (size_t i = 0; i < extractor->countTracks(); ++i) {  
  4.    ...  
  5.    if (!haveVideo && !strncasecmp(mime, "video/", 6))  
  6.       setVideoSource(extractor->getTrack(i));  
  7.    ...  
  8.    if(!haveAudio && !strncasecmp(mime, "audio/", 6))  
  9.       setAudioSource(extractor->getTrack(i));  
  10.    ...  
  11.    }  
  12. }  

    先看看extractor->getTrack做了些什么?

    它以MPEG2TSExtractor和AnotherPacketSource做为参数创建了一个MPEG2TSSource对象返回,然后AwesomePlayer把它保存在mVideoTrack或mAudioTrack中。

 3.2.2 initVideoDecoder

    主要代码如下:

[cpp] view plaincopy
  1. status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {  
  2.   
  3.     if (mDecryptHandle != NULL) {  
  4.         flags |= OMXCodec::kEnableGrallocUsageProtected;  
  5.     }  
  6.   
  7.     mVideoSource = OMXCodec::Create(                     //3.2.2.1  
  8.             mClient.interface(), mVideoTrack->getFormat(),  
  9.             false// createEncoder  
  10.             mVideoTrack,  
  11.             NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);  
  12.   
  13.     if (mVideoSource != NULL) {  
  14.         int64_t durationUs;  
  15.         if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {  
  16.             Mutex::Autolock autoLock(mMiscStateLock);  
  17.             if (mDurationUs < 0 || durationUs > mDurationUs) {  
  18.                 mDurationUs = durationUs;  
  19.             }  
  20.         }  
  21.   
  22.         status_t err = mVideoSource->start();     //3.2.2.2  
  23.   
  24.         if (err != OK) {  
  25.             mVideoSource.clear();  
  26.             return err;  
  27.         }  
  28.     }  
  29.   
  30.     return mVideoSource != NULL ? OK : UNKNOWN_ERROR;  
  31. }  

      它主要做了两件事,1)创建一个OMXCodec对象,2)调用OMXCodec的start方法。注mClient.interface()返回为一个OMX对象。其创建流程如下:

AwesomePlayer::AwesomePlayer->

   mClient.connect->

     OMXClient::connect(获取OMX对象,并保存在mOMX)->

       BpMediaPlayerService::getOMX->

         BnMediaPlayerService::onTransact(GET_OMX)->

            MediaPlayerService::getOMX


3.2.2.1 创建OMXCodec对象

      从上面的代码中可以看出,其mVideoTrack参数为一个MPEG2TSSource对象。

      1)从MPEG2TSSource的metadata中获取mime类型

      2)调用OMXCodec::findMatchingCodecs从kDecoderInfo中寻找可以解此mime媒体类型的codec名,并放在matchingCodecs变量中

      3)创建一个OMXCodecObserver对象

      4)调用OMX::allocateNode函数,以codec名和OMXCodecObserver对象为参数,创建一个OMXNodeInstance对象,并把其makeNodeID的返回值保存在node(node_id)中。

      5)以node,codec名,mime媒体类型,MPEG2TSSource对象为参数,创建一个OMXCodec对象,并把此OMXCodec对象保存在OMXCodecObserver::mTarget中

 

[cpp] view plaincopy
  1. OMXCodec::OMXCodec(  
  2.         const sp<IOMX> &omx, IOMX::node_id node,  
  3.         uint32_t quirks, uint32_t flags,  
  4.         bool isEncoder,  
  5.         const char *mime,  
  6.         const char *componentName,  
  7.         const sp<MediaSource> &source,  
  8.         const sp<ANativeWindow> &nativeWindow)  


      6)调用OMXCodec::configureCodec并以MEPG2TSSource的MetaData为参数,对此Codec进行配置。

 

 

3.2.2.2 调用OMXCodec::start方法

      1)它调用mSource->start,即调用MPEG2TSSource::start函数。

      2)它又调用Impl->start,即AnotherPacketSource::start,真遗憾,这其中什么都没有做。只是return OK;就完事了。

 

 

 3.2.3 initAudioDecoder

           其流程基本上与initVideoDecoder类似。创建一个OMXCodec保存在mAudioSource中。

     至此,AwesomePlayer的准备工作已经完成。其架构如下图所示:

0 0
原创粉丝点击