概述
本篇介绍播放器结构中的第一部分Stream+Demuxer.
Awesomeplayer中对应的数据结构主要有DataSource,MediaExtractor,MediaSource。
其中DataSource 主要负责提供原始数据,MediaSource负责提供demux后的数据(即实际的audio 或者 video 数据包)
而MediaExtractor则负责中间的过程,即将从DataSource得到的原始数据解析成解码器需要的es数据,并通过MediaSource的接口输出。
以ts为例,extractor在awesomeplayer中的位置图如下:
我们按照一个demux的基本结构结合ts的实例来分析整个结构
其中demuxer的部分我们按照ffmpeg demuxer的结构中的主要成员来分析
1 stream -- 功能
2 extractor 创建流程
3 extractor 结构介绍(ts为例)
3.1 demuxer -- read_probe
3.2 demuxer -- read_header
3.3 demuxer -- read_packet
3.4 demuxer -- read_seek
下面通过实际的代码来分析
1 stream -- 功能
stream的主要功能是,从外部介质(本地磁盘或者网络等)获取待播放的原始数据。
对应的数据结构为:DataSource
看下awesomeplayer中对应的代码
a构造函数
[html] view plaincopy
1. AwesomePlayer::AwesomePlayer(){
2. *************
3. DataSource::RegisterDefaultSniffers();
4. *************
5. }
看下RegisterDefaultSniffers实现
[html] view plaincopy
1. // static
2. void DataSource::RegisterSniffer(SnifferFunc func) {
3. Mutex::Autolock autoLock(gSnifferMutex);
4.
5. for (List
6. it != gSniffers.end(); ++it) {
7. if (*it == func) {
8. return;
9. }
10. }
11.
12. gSniffers.push_back(func);
13. }
void DataSource::RegisterDefaultSniffers() {
[html] view plaincopy
1. RegisterSniffer(SniffMPEG4);
2. RegisterSniffer(SniffFragmentedMP4);
3. RegisterSniffer(SniffMatroska);
4. RegisterSniffer(SniffOgg);
5. RegisterSniffer(SniffWAV);
6. RegisterSniffer(SniffFLAC);
7. RegisterSniffer(SniffAMR);
8. RegisterSniffer(SniffMPEG2TS);
9. RegisterSniffer(SniffMP3);
10. RegisterSniffer(SniffAAC);
11. RegisterSniffer(SniffMPEG2PS);
12. RegisterSniffer(SniffWVM);
13.
14. char value[PROPERTY_VALUE_MAX];
15. if (property_get("drm.service.enabled", value, NULL)
16. && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
17. RegisterSniffer(SniffDRM);
18. }
从代码可以看出RegisterDefaultSniffers的主要作用既是注册Sniffer函数
将所有的sniffer函数都挂在全局链表gSniffers中。
sniffer函数的主要作用就是用于探测文件的类型,每种类型的媒体文件都对应一个sniffer函数。
这里从代码可以看出原生的android播放器支持的格式还比较少
这里主要作用就是注册完成后 demuxer在read_probe阶段便可以通过调用sniffer函数来探测文件类型,具体等讲解probe的时候结合实际的例子(mpegts)分析
b setDataSource
[html] view plaincopy
1. status_t AwesomePlayer::setDataSource(
2. int fd, int64_t offset, int64_t length) {
3. Mutex::Autolock autoLock(mLock);
4.
5. reset_l();
6.
7. sp
8.
9. status_t err = dataSource->initCheck();
10.
11. if (err != OK) {
12. return err;
13. }
14.
15. mFileSource = dataSource;
16.
17. {
18. Mutex::Autolock autoLock(mStatsLock);
19. mStats.mFd = fd;
20. mStats.mURI = String8();
21. }
22.
23. return setDataSource_l(dataSource);
24. }
代码中构造了FileSource对象赋值给mFileSource,这里FileSource继承自DataSource,提供stream功能。
下面列出filesource类的定义
[html] view plaincopy
1. class FileSource : public DataSource {
2. public:
3. FileSource(const char *filename);
4. FileSource(int fd, int64_t offset, int64_t length);
5.
6. virtual status_t initCheck() const;
7.
8. virtual ssize_t readAt(off64_t offset, void *data, size_t size);
9.
10. virtual status_t getSize(off64_t *size);
11.
12. virtual sp
13.
14. virtual void getDrmInfo(sp
15.
16. protected:
17. virtual ~FileSource();
18.
19. private:
20. int mFd;
21. int64_t mOffset;
22. int64_t mLength;
23. Mutex mLock;
24.
25. /*for DRM*/
26. sp
27. DrmManagerClient *mDrmManagerClient;
28. int64_t mDrmBufOffset;
29. int64_t mDrmBufSize;
30. unsigned char *mDrmBuf;
31.
32. ssize_t readAtDRM(off64_t offset, void *data, size_t size);
33.
34. FileSource(const FileSource &);
35. FileSource &operator=(const FileSource &);
36. };
这里filesource提供了readAt方法提供原始数据获取,由于其参数有offset,则支持随机存取,支持seek功能。其构造函数如下
[html] view plaincopy
1. FileSource::FileSource(const char *filename)
2. : mFd(-1),
3. mOffset(0),
4. mLength(-1),
5. mDecryptHandle(NULL),
6. mDrmManagerClient(NULL),
7. mDrmBufOffset(0),
8. mDrmBufSize(0),
9. mDrmBuf(NULL){
10.
11. mFd = open(filename, O_LARGEFILE | O_RDONLY);
12.
13. if (mFd >= 0) {
14. mLength = lseek64(mFd, 0, SEEK_END);
15. } else {
16. ALOGE("Failed to open file '%s'. (%s)", filename, strerror(errno));
17. }
18. }
构造函数的主要功能就是open给定的文件名,并将句柄存储在mFd中,后面读取数据可以直接使用Linux标准方法读取。
2 awesomeplayer中extractor 创建流程
在setDataSource的最后,会调用setDataSource_l(dataSource);将datasource和对应的extractor对应起来,这里看下流程
[html] view plaincopy
1. status_t AwesomePlayer::setDataSource_l(
2. const sp
3. sp
4.
5. if (extractor == NULL) {
6. return UNKNOWN_ERROR;
7. }
8.
9. if (extractor->getDrmFlag()) {
10. checkDrmStatus(dataSource);
11. }
12.
13. return setDataSource_l(extractor);
14. }
2.1 MediaExtractor::Create
这里通过MediaExtractor::Create创建extractor ,分段来看代码实现。这里还是忽略drm等实现
[html] view plaincopy
1. sp
2. const sp
3. sp
4.
5. String8 tmp;
6. if (mime == NULL) {
7. float confidence;
8. if (!source->sniff(&tmp, &confidence, &meta)) {
9. ALOGV("FAILED to autodetect media content.");
10.
11. return NULL;
12. }
13.
14. mime = tmp.string();
15. ALOGV("Autodetected media content as '%s' with confidence %.2f",
16. mime, confidence);
17. }
这里第一步是通过调用datasource的siniff函数探测文件类型。
看下sniff实现
[html] view plaincopy
1. bool DataSource::sniff(
2. String8 *mimeType, float *confidence, sp
3. *mimeType = "";
4. *confidence = 0.0f;
5. meta->clear();
6.
7. Mutex::Autolock autoLock(gSnifferMutex);
8. for (List
9. it != gSniffers.end(); ++it) {
10. String8 newMimeType;
11. float newConfidence;
12. sp
13. if ((*it)(this, &newMimeType, &newConfidence, &newMeta)) {
14. if (newConfidence > *confidence) {
15. *mimeType = newMimeType;
16. *confidence = newConfidence;
17. *meta = newMeta;
18. }
19. }
20. }
21.
22. return *confidence > 0.0;
23. }
主要是将gSniffers链表中的每种格式的函数调用一遍,选取最高的confidence作为选中的文件格式。后面会以ts为例讲解具体细节。
下面继续
[html] view plaincopy
1. MediaExtractor *ret = NULL;
2. if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
3. || !strcasecmp(mime, "audio/mp4")) {
4. int fragmented = 0;
5. if (meta != NULL && meta->findInt32("fragmented", &fragmented) && fragmented) {
6. ret = new FragmentedMP4Extractor(source);
7. } else {
8. ret = new MPEG4Extractor(source);
9. }
10. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
11. ret = new MP3Extractor(source, meta);
12. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
13. || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
14. ret = new AMRExtractor(source);
15. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_FLAC)) {
16. ret = new FLACExtractor(source);
17. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
18. ret = new WAVExtractor(source);
19. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
20. ret = new OggExtractor(source);
21. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
22. ret = new MatroskaExtractor(source);
23. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
24. ret = new MPEG2TSExtractor(source);
25. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WVM)) {
26. // Return now. WVExtractor should not have the DrmFlag set in the block below.
27. return new WVMExtractor(source);
28. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC_ADTS)) {
29. ret = new AACExtractor(source, meta);
30. } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2PS)) {
31. ret = new MPEG2PSExtractor(source);
32. }
33.
34. if (ret != NULL) {
35. if (isDrm) {
36. ret->setDrmFlag(true);
37. } else {
38. ret->setDrmFlag(false);
39. }
40. }
41.
42. return ret;
43. }
成功的通过sniff函数确定了文件的格式之后,就可以构造extractor对象了。
例如:如果文件格式是ts格式,则会调用ret = new MPEG2TSExtractor(source);
具体的构造函数就完成了文件头的解析获取了流信息。
2.2 setDataSource_l(extractor)
创建了extractor对象之后,setDataSource_l(extractor) 的主要作用就是使用上面得到的信息来构造播放器框架了。
这里只将重要的语句列出
setVideoSource(extractor->getTrack(i));
setAudioSource(extractor->getTrack(i));
addTextSource_l(i, extractor->getTrack(i));
主要是将文件中的各个流通过上面三个方法,存放在mVideoTrack、mAudioTrack,之后作为参数传递给解码器。便建立了解码器与extractor的关联
3 MediaExtractor的结构介绍(ts为例)
这里主要是以ts为例,按照一个demuxer的具体功能组件(仿照ffmpeg结构)来介绍,extractor是如何实现一个demuxer的功能。
ts相关的代码文件是:frameworks/media/libstagefrightplayer/mpeg2ts/MPEG2TSExtractor.cpp
下面是一张MPEG2TSExtractor 的总体结构图:
下面的很多介绍都会引用此图
解释下这张图:MPEG2TSExtractor是总入口,负责解析文件头信息,提供原始数据包。在功能上讲,MPEG2TSExtractor 是ATSParser的封装,而ATSParser负责实际的解析工作,
即:MPEG2TSExtractor 从FileSource中获取数据,提供给ATSParser 进行解析。而MPEG2TSExtractor 对外提供的各种接口及文件信息都是借由ATSParser 来完成的。
比如图中:getTrack接口提供的MediaSource也就是awesomeplayer中的mAudioTrack和mVideoTrack,是MPEG2TSSource结构,而MPEG2TSSource的工作由AnotherPacketSource(mSourceImpls)完成
而实际上AnotherPacketSource 是在ATSParser中的Stream中生成的,每个Stream对应一个实际的流。后面分析代码时可以依据上面解释来理解。
3.1 demuxer -- read_probe
这里的probe主要是对应datasource的sniffer函数。具体实现如下:
[html] view plaincopy
1. bool SniffMPEG2TS(
2. const sp
3. sp
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. }
基本思路很简单,对于ts文件每个ts包为188字节,每个包的同步字为0x47
这里主要通过datasource的readAt接口读取一个字节,判断是否是0x47,主要是间隔kTSPacketSize=188读取一个字节判断5次
如果全部通过则确定此文件为mpegts文件。设置*confidence = 0.1f;返回。
3.2 demuxer -- read_header
ffmpeg中的read_header函数主要作用是解析文件头信息,获取文件中的具体流信息及参数
这里主要是通过构造函数来完成,具体看下
[html] view plaincopy
1. MPEG2TSExtractor::MPEG2TSExtractor(const sp
2. : mDataSource(source),
3. mParser(new ATSParser),
4. mOffset(0) {
5. init();
6. }
这里将datasource参数存放在mDataSource中,并构造了ATSParser对象,这里可以这样认定:MPEG2TSExtractor是ATSParser的封装,具体的工作都是ATSParser完成的
看下其构造函数
[html] view plaincopy
1. ATSParser::ATSParser(uint32_t flags)
2. : mFlags(flags),
3. mAbsoluteTimeAnchorUs(-1ll),
4. mNumTSPacketsParsed(0),
5. mNumPCRs(0) {
6. mPSISections.add(0 /* PID */, new PSISection);
7. }
构造好ATSParser对象之后,调用了init()函数
[html] view plaincopy
1. void MPEG2TSExtractor::init() {
2. bool haveAudio = false;
3. bool haveVideo = false;
4. int numPacketsParsed = 0;
5.
6. while (feedMore() == OK) {
7. ATSParser::SourceType type;
8. if (haveAudio && haveVideo) {
9. break;
10. }
11. if (!haveVideo) {
12. sp
13. (AnotherPacketSource *)mParser->getSource(
14. ATSParser::VIDEO).get();
15.
16. if (impl != NULL) {
17. haveVideo = true;
18. mSourceImpls.push(impl);
19. }
20. }
21.
22. if (!haveAudio) {
23. sp
24. (AnotherPacketSource *)mParser->getSource(
25. ATSParser::AUDIO).get();
26.
27. if (impl != NULL) {
28. haveAudio = true;
29. mSourceImpls.push(impl);
30. }
31. }
32.
33. if (++numPacketsParsed > 10000) {
34. break;
35. }
36. }
37.
38. ALOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo);
39. }
这里feedMore便是解析文件头的具体实现,代码的后半部分主要是当解析成功即feedMore() == OK
feedmore作用有两个:解析头信息+缓冲数据
将audio video的source存放在栈mSourceImpls中。看下具体实现
[html] view plaincopy
1. status_t MPEG2TSExtractor::feedMore() {
2. Mutex::Autolock autoLock(mLock);
3.
4. uint8_t packet[kTSPacketSize];
5. ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
6.
7. if (n < (ssize_t)kTSPacketSize) {
8. return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
9. }
10.
11. mOffset += n;
12. return mParser->feedTSPacket(packet, kTSPacketSize);
13. }
从代码中可以看出mParser并不与datasource直接联系,而是mExtractor读取一个ts包,传递给mParser来分析
具体分析过程通过调用mParser->feedTSPacket方法完成,进入ATParser类方法中
[html] view plaincopy
1. status_t ATSParser::feedTSPacket(const void *data, size_t size) {
2. CHECK_EQ(size, kTSPacketSize);
3.
4. ABitReader br((const uint8_t *)data, kTSPacketSize);
5. return parseTS(&br);
6. }
[html] view plaincopy
1. status_t ATSParser::parseTS(ABitReader *br) {
2.
3. *****************
4.
5. if (adaptation_field_control == 1 || adaptation_field_control == 3) {
6. err = parsePID(
7. br, PID, continuity_counter, payload_unit_start_indicator);
8. }
9.
10. ++mNumTSPacketsParsed;
11.
12. return err;
13. }
省略部分无关代码
如果对ts文件格式不了解,网上有很多资料。这里解析ts头主要就是解析几个表,如PAT(pid==0) PMT(pid通过解析pat获取) 以及通过上面两步获取实际流pid号之后解析实际数据获取参数。
这里获取pid之后通过调用parsePID来处理。
[html] view plaincopy
1. status_t ATSParser::parsePID(
2. ABitReader *br, unsigned PID,
3. unsigned continuity_counter,
4. unsigned payload_unit_start_indicator) {
5.
6. if (PID == 0) {
7. parseProgramAssociationTable(§ionBits);
8. } else {
9.
10. bool handled = false;
11. for (size_t i = 0; i < mPrograms.size(); ++i) {
12. status_t err;
13. if (!mPrograms.editItemAt(i)->parsePSISection(
14. PID, §ionBits, &err)) {
15. continue;
16. }
17. section->clear();
18.
19. return OK;
20. }
21.
22. bool handled = false;
23. for (size_t i = 0; i < mPrograms.size(); ++i) {
24. status_t err;
25. if (mPrograms.editItemAt(i)->parsePID(
26. PID, continuity_counter, payload_unit_start_indicator,
27. br, &err)) {
28. if (err != OK) {
29. return err;
30. }
31.
32. handled = true;
33. break;
34. }
35. }
36.
37. if (!handled) {
38. ALOGV("PID 0x%04x not handled.", PID);
39. }
40.
41. return OK;
42. }
这里主要是根据pid的不同来分别处理,如parseProgramAssociationTable用来处理pat表,此时pid==0
如果不是,则调用mPrograms.editItemAt(i)->parsePSISection来处理pmt表
这里如果没有处理过pat表,则 mPrograms.size()==0 会进入下一循环直到找到pat为止
这里不再深入了,解析完pmt表,就知道了文件中有几个流,几路audio 几路video等信息都得到了
后面会调用每个流的parsePID -- >调用每个流的parse函数,后续调用顺序为
mStreams.editValueAt(index)->parse->flush();->parsePES->onPayloadData等
这里简单总结下:在ATSParser中有几个内嵌类,
Program类负责解析pmt表
Stream类负责解析每个流的具体信息
具体代码不再列举了,总归都是按照ts的标准来解析,若读者不熟悉ts强烈建议仔细阅读下ffmpeg或者ATSParser中的实现方式。
重点注释:
每个Stream代表一个流,可以是音频流或者是视频流,读包的操作主要就是通过Stream接口来完成的。当在extractor中的feedmore方法中读取一个ts包传递给mParser解析时,
如果是实际的数据包,则最终都会存储在Stream的buffer中,而stream中的成员mSource(AnotherPacketSource)则是最终传递上去的保存在MPEG2TSExtractor中保存在全局变量中mSourceImpl中
而实际的mAudioTrack mVideoTrack则是通过getTrack返回MPEG2TSSource,而MPEG2TSSource 封装了AnotherPacketSource ,也就和实际的stream关联起来,
最终可以通过MPEG2TSSource 读取保存在stream中的数据包
【说明】请仔细消化上面这段话,并结合代码及上面的图来理解
3.3 demuxer -- read_packet
之前分析过,在awesomeplayer中有如下语句
mAudioTrack = extractor->getTrack(*);
mVideoTrack = extractor->getTrack(*);
这里getTrack便是建立awesomeplayer与extractor连接的地方(参考上面的黑体部分)
这里分析下ts的具体实现
[html] view plaincopy
1. sp
2.
3.
4. ************
5.
6. return new MPEG2TSSource(this, mSourceImpls.editItemAt(index), seekable);
7. }
[html] view plaincopy
1. sp
2.
3.
4. ************
5.
6. return new MPEG2TSSource(this, mSourceImpls.editItemAt(index), seekable);
7. }
这里在MPEG2TSExtractor定义了一个新的继承自MediaSource的类MPEG2TSSource,返回给上层
其构造函数如下:
[html] view plaincopy
1. MPEG2TSSource::MPEG2TSSource(
2. const sp
3. const sp
4. bool seekable)
5. : mExtractor(extractor),
6. mImpl(impl),
7. mSeekable(seekable) {
8. }
[html] view plaincopy
1. MPEG2TSSource::MPEG2TSSource(
2. const sp
3. const sp
4. bool seekable)
5. : mExtractor(extractor),
6. mImpl(impl),
7. mSeekable(seekable) {
8. }
这里传入的参数mSourceImpls.editItemAt(index)是一个AnotherPacketSource对象(实际生成是在ATSParser中的Stream类中),主要负责缓存数据包,等上层解码器需要数据的时候会从此处读取
看下实际的读包方法:
在awesomeplayer中会调用mAudioTrack->read(*)方法,由于mAudioTrack == mVideoTrack == MPEG2TSSource ,因此调用的是MPEG2TSSource 的read方法
[html] view plaincopy
1. status_t MPEG2TSSource::read(
2. MediaBuffer **out, const ReadOptions *options) {
3. *out = NULL;
4.
5. int64_t seekTimeUs;
6. ReadOptions::SeekMode seekMode;
7. if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
8. mExtractor->seekTo(seekTimeUs);
9. }
10.
11. status_t finalResult;
12. while (!mImpl->hasBufferAvailable(&finalResult)) {
13. if (finalResult != OK) {
14. return ERROR_END_OF_STREAM;
15. }
16.
17. status_t err = mExtractor->feedMore();
18. if (err != OK) {
19. mImpl->signalEOS(err);
20. }
21. }
22.
23. return mImpl->read(out, options);
24. }
[html] view plaincopy
1. status_t MPEG2TSSource::read(
2. MediaBuffer **out, const ReadOptions *options) {
3. *out = NULL;
4.
5. int64_t seekTimeUs;
6. ReadOptions::SeekMode seekMode;
7. if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
8. mExtractor->seekTo(seekTimeUs);
9. }
10.
11. status_t finalResult;
12. while (!mImpl->hasBufferAvailable(&finalResult)) {
13. if (finalResult != OK) {
14. return ERROR_END_OF_STREAM;
15. }
16.
17. status_t err = mExtractor->feedMore();
18. if (err != OK) {
19. mImpl->signalEOS(err);
20. }
21. }
22.
23. return mImpl->read(out, options);
24. }
这里首先判断是否需要seek,若不需要。则通过mImpl->hasBufferAvailable看是否有缓存数据包,此处mImpl == mSourceImpls.editItemAt(index),(见上面构造函数参数列表)
如果没有则通过feedMore()读取包
最后通过 mImpl->read(out, options)返回给上层。
这里再解释下:首先每个流都对应一个MPEG2TSSource 对象,而每个MPEG2TSSource 对象都有一个AnotherPacketSource 对象,对应一个缓存列表。一开始extractor通过feedMore解析数据后将数据存储在各个AnotherPacketSource 的缓存列表中
举例来讲:如果某一时刻 audio 缓存类表为空,而video 缓存列表为 4 ,此时读取audio包会导致mImpl->hasBufferAvailable ==0,此时会通过mExtractor->feedMore 继续缓存数据,若下一包为video则挂在到video缓存(此处video包+1=5)
直到解析到一个audio包,则返回。此处的实现保证了不会因为包分布不合理导致解码阻塞等情况
看下feedMore实现
[html] view plaincopy
1. status_t MPEG2TSExtractor::feedMore() {
2. Mutex::Autolock autoLock(mLock);
3.
4. uint8_t packet[kTSPacketSize];
5. ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
6.
7. if (n < (ssize_t)kTSPacketSize) {
8. return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
9. }
10.
11. mOffset += n;
12. return mParser->feedTSPacket(packet, kTSPacketSize);
13. }
[html] view plaincopy
1. status_t MPEG2TSExtractor::feedMore() {
2. Mutex::Autolock autoLock(mLock);
3.
4. uint8_t packet[kTSPacketSize];
5. ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
6.
7. if (n < (ssize_t)kTSPacketSize) {
8. return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
9. }
10.
11. mOffset += n;
12. return mParser->feedTSPacket(packet, kTSPacketSize);
13. }
主要是读取一包,然后解析,调用顺序如下
feedMore->mParser->feedTSPacket->parseTS->ATSParser::parsePID->ATSParser::Program::parsePID->ATSParser::Stream::parse
在ATSParser::Stream::parse 最终会将数据包缓存起来(具体实现方式:将数据全部缓存起来,放在mBuffer中,当此包数据都完成时,即得到了一个完整的audio或者video包,调用flush解析好了存在AnotherPacketSource的缓存中。)
3.4 demuxer -- read_seek
当需要seek的时候,入口在awesomeplayer的seekto,代码如下
[html] view plaincopy
1. status_t AwesomePlayer::seekTo(int64_t timeUs) {
2. ATRACE_CALL();
3.
4. if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
5. Mutex::Autolock autoLock(mLock);
6. return seekTo_l(timeUs);
7. }
8.
9. return OK;
10. }
[html] view plaincopy
1. status_t AwesomePlayer::seekTo(int64_t timeUs) {
2. ATRACE_CALL();
3.
4. if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
5. Mutex::Autolock autoLock(mLock);
6. return seekTo_l(timeUs);
7. }
8.
9. return OK;
10. }
[html] view plaincopy
1. status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
2. *********************
3. mSeeking = SEEK;
4. mSeekNotificationSent = false;
5. mSeekTimeUs = timeUs;
6. modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR);
7. seekAudioIfNecessary_l();
8. if (!(mFlags & PLAYING)) {
9. ALOGV("seeking while paused, sending SEEK_COMPLETE notification"
10. " immediately.");
11. notifyListener_l(MEDIA_SEEK_COMPLETE);
12. mSeekNotificationSent = true;
13. if ((mFlags & PREPARED) && mVideoSource != NULL) {
14. modifyFlags(SEEK_PREVIEW, SET);
15. postVideoEvent_l();
16. }
17. }
18.
19. return OK;
20. }
[html] view plaincopy
1. status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
2. *********************
3. mSeeking = SEEK;
4. mSeekNotificationSent = false;
5. mSeekTimeUs = timeUs;
6. modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR);
7. seekAudioIfNecessary_l();
8. if (!(mFlags & PLAYING)) {
9. ALOGV("seeking while paused, sending SEEK_COMPLETE notification"
10. " immediately.");
11. notifyListener_l(MEDIA_SEEK_COMPLETE);
12. mSeekNotificationSent = true;
13. if ((mFlags & PREPARED) && mVideoSource != NULL) {
14. modifyFlags(SEEK_PREVIEW, SET);
15. postVideoEvent_l();
16. }
17. }
18.
19. return OK;
20. }
首先设置seek标志,mSeeking = SEEK,然后设置seek到的位置 mSeekTimeUs = timeUs;清空标志位。
调用seekAudioIfNecessary_l,看下实现
[html] view plaincopy
1. void AwesomePlayer::seekAudioIfNecessary_l() {
2. if (mSeeking != NO_SEEK && mVideoSource == NULL && mAudioPlayer != NULL) {
3. mAudioPlayer->seekTo(mSeekTimeUs);
4.
5. mWatchForAudioSeekComplete = true;
6. mWatchForAudioEOS = true;
7.
8. if (mDecryptHandle != NULL) {
9. mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
10. Playback::PAUSE, 0);
11. mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
12. Playback::START, mSeekTimeUs / 1000);
13. }
14. }
15. }
[html] view plaincopy
1. void AwesomePlayer::seekAudioIfNecessary_l() {
2. if (mSeeking != NO_SEEK && mVideoSource == NULL && mAudioPlayer != NULL) {
3. mAudioPlayer->seekTo(mSeekTimeUs);
4.
5. mWatchForAudioSeekComplete = true;
6. mWatchForAudioEOS = true;
7.
8. if (mDecryptHandle != NULL) {
9. mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
10. Playback::PAUSE, 0);
11. mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
12. Playback::START, mSeekTimeUs / 1000);
13. }
14. }
15. }
比较简单,调用mAudioPlayer->seekTo
[html] view plaincopy
1. status_t AudioPlayer::seekTo(int64_t time_us) {
2. Mutex::Autolock autoLock(mLock);
3.
4. mSeeking = true;
5. mPositionTimeRealUs = mPositionTimeMediaUs = -1;
6. mReachedEOS = false;
7. mSeekTimeUs = time_us;
8.
9. // Flush resets the number of played frames
10. mNumFramesPlayed = 0;
11. mNumFramesPlayedSysTimeUs = ALooper::GetNowUs();
12.
13. if (mAudioSink != NULL) {
14. mAudioSink->flush();
15. } else {
16. mAudioTrack->flush();
17. }
18.
19. return OK;
20. }
[html] view plaincopy
1. status_t AudioPlayer::seekTo(int64_t time_us) {
2. Mutex::Autolock autoLock(mLock);
3.
4. mSeeking = true;
5. mPositionTimeRealUs = mPositionTimeMediaUs = -1;
6. mReachedEOS = false;
7. mSeekTimeUs = time_us;
8.
9. // Flush resets the number of played frames
10. mNumFramesPlayed = 0;
11. mNumFramesPlayedSysTimeUs = ALooper::GetNowUs();
12.
13. if (mAudioSink != NULL) {
14. mAudioSink->flush();
15. } else {
16. mAudioTrack->flush();
17. }
18.
19. return OK;
20. }
从代码中看主要是设置标志位
mSeeking = true;
mSeekTimeUs = time_us;
然后清空audioplayer中的pcm数据,而设置好seek标志之后,在audioplayer的fill buffer中读取数据时便会设置要读取的时间的数据,具体代码如下
[html] view plaincopy
1. size_t AudioPlayer::fillBuffer(void *data, size_t size) {
2. *************
3. MediaSource::ReadOptions options;
4. options.setSeekTo(mSeekTimeUs);
5. err = mSource->read(&mInputBuffer, &options);
6. *************
7. }
[html] view plaincopy
1. size_t AudioPlayer::fillBuffer(void *data, size_t size) {
2. *************
3. MediaSource::ReadOptions options;
4. options.setSeekTo(mSeekTimeUs);
5. err = mSource->read(&mInputBuffer, &options);
6. *************
7. }
只列出了关键语句。
通过上面分析得出:当收到seek命令时,对于audio来讲就是清空audioplayer的pcm数据,设置下次要读取的位置MediaSource::ReadOptions options,其他工作由上层完成(指的是decoder,具体在分析decoder的时候介绍)
audio的seek完成之后,看下video的seek
具体代码在onVideoEvent中
[html] view plaincopy
1. void AwesomePlayer::onVideoEvent() {
2.
3. ********
4.
5. MediaSource::ReadOptions options;
6. if (mSeeking != NO_SEEK) {
7. options.setSeekTo(
8. mSeekTimeUs,
9. mSeeking == SEEK_VIDEO_ONLY
10. ? MediaSource::ReadOptions::SEEK_NEXT_SYNC
11. : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
12. }
13.
14. status_t err = mVideoSource->read(&mVideoBuffer, &options);
15.
16. ********
17.
18. }
[html] view plaincopy
1. void AwesomePlayer::onVideoEvent() {
2.
3. ********
4.
5. MediaSource::ReadOptions options;
6. if (mSeeking != NO_SEEK) {
7. options.setSeekTo(
8. mSeekTimeUs,
9. mSeeking == SEEK_VIDEO_ONLY
10. ? MediaSource::ReadOptions::SEEK_NEXT_SYNC
11. : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
12. }
13.
14. status_t err = mVideoSource->read(&mVideoBuffer, &options);
15.
16. ********
17.
18. }
这里的处理方式与audio类似,都是设置好seek后的位置 MediaSource::ReadOptions options ,然后再从mVideoSource读取数据时作为参数传递进去(decoder后面介绍)。
但这里可以想到,最终decoder都会设置MediaExtractor 读取位置来达到seek的效果,这里看些ts的case
这里在ts extractor中调用read方法读取数据代码如下:
[html] view plaincopy
1. status_t MPEG2TSSource::read(
2. MediaBuffer **out, const ReadOptions *options) {
3. *out = NULL;
4.
5. int64_t seekTimeUs;
6. ReadOptions::SeekMode seekMode;
7. if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
8. mExtractor->seekTo(seekTimeUs);
9. }
10.
11. status_t finalResult;
12. while (!mImpl->hasBufferAvailable(&finalResult)) {
13. if (finalResult != OK) {
14. return ERROR_END_OF_STREAM;
15. }
16.
17. status_t err = mExtractor->feedMore();
18. if (err != OK) {
19. mImpl->signalEOS(err);
20. }
21. }
22.
23. return mImpl->read(out, options);
24. }
[html] view plaincopy
1. status_t MPEG2TSSource::read(
2. MediaBuffer **out, const ReadOptions *options) {
3. *out = NULL;
4.
5. int64_t seekTimeUs;
6. ReadOptions::SeekMode seekMode;
7. if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) {
8. mExtractor->seekTo(seekTimeUs);
9. }
10.
11. status_t finalResult;
12. while (!mImpl->hasBufferAvailable(&finalResult)) {
13. if (finalResult != OK) {
14. return ERROR_END_OF_STREAM;
15. }
16.
17. status_t err = mExtractor->feedMore();
18. if (err != OK) {
19. mImpl->signalEOS(err);
20. }
21. }
22.
23. return mImpl->read(out, options);
24. }
可以看到option已经传递进来,继续跟进mImpl->read
[html] view plaincopy
1. status_t AnotherPacketSource::read(
2. MediaBuffer **out, const ReadOptions *) {
3. *out = NULL;
4.
5. Mutex::Autolock autoLock(mLock);
6. while (mEOSResult == OK && mBuffers.empty()) {
7. mCondition.wait(mLock);
8. }
9.
10. if (!mBuffers.empty()) {
11. const sp
12. mBuffers.erase(mBuffers.begin());
13.
14. int32_t discontinuity;
15. if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
16. if (wasFormatChange(discontinuity)) {
17. mFormat.clear();
18. }
19.
20. return INFO_DISCONTINUITY;
21. } else {
22. int64_t timeUs;
23. CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
24.
25. MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
26.
27. mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
28.
29. *out = mediaBuffer;
30. return OK;
31. }
32. }
33.
34. return mEOSResult;
35. }
[html] view plaincopy
1. status_t AnotherPacketSource::read(
2. MediaBuffer **out, const ReadOptions *) {
3. *out = NULL;
4.
5. Mutex::Autolock autoLock(mLock);
6. while (mEOSResult == OK && mBuffers.empty()) {
7. mCondition.wait(mLock);
8. }
9.
10. if (!mBuffers.empty()) {
11. const sp
12. mBuffers.erase(mBuffers.begin());
13.
14. int32_t discontinuity;
15. if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
16. if (wasFormatChange(discontinuity)) {
17. mFormat.clear();
18. }
19.
20. return INFO_DISCONTINUITY;
21. } else {
22. int64_t timeUs;
23. CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
24.
25. MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
26.
27. mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
28.
29. *out = mediaBuffer;
30. return OK;
31. }
32. }
33.
34. return mEOSResult;
35. }
从代码里看并没有使用传递进来的 ReadOptions ,预计seek动作实在decoder中解决的。等分析decoder的时候再详细看。
到此,extractor就分析完了