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,其代码为:
3.2.1 finishSetDataSource_l
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(解析器)。其代码如下:
当然对于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,其代码如下:
由此可见,这些sniff是根据文件开始的内容来识别各种file container. 比如wav文件通过其头中的RIFF或WAVE字符串来识别。注:在创建player时,是根据url中的相关信息来判断的,而不是文件的内容来判断。
3.2.1.3 AwesomePlayer::setDataSource_l(extractor)
主要逻辑代码如下(当然此extractor实质为MPEG2TSExtractor对象):
先看看extractor->getTrack做了些什么?
它以MPEG2TSExtractor和AnotherPacketSource做为参数创建了一个MPEG2TSSource对象返回,然后AwesomePlayer把它保存在mVideoTrack或mAudioTrack中。
3.2.2 initVideoDecoder
主要代码如下:
它主要做了两件事,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中
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的准备工作已经完成。其架构如下图所示: