MP4 seek状态 sample读取流程
来源:互联网 发布:js中邮箱的正则表达式 编辑:程序博客网 时间:2024/05/17 22:05
前面一篇博客详细剖析了正常情况下,按照sample的顺序从前往后读取sample数据的流程,最重要的过程在于对那几张表的充分利用,将前面那篇博客的内容搞明白后,接下来的内容其实也很简单,这篇博客主要跟踪,在seek状态下的,sample内容的读取,即任意时间点对应的sampleIndex的确定,这里还需要确定的是关键帧的sampleIndex,有了sampleIndex之后,一切就和前面的博客流程一致了,所以这篇博客的重点在于蓝色部分。
MediaBuffer **out, const ReadOptions *options) { Mutex::Autolock autoLock(mLock); int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode)) { seek标志的判断 uint32_t findFlags = 0; switch (mode) { case ReadOptions::SEEK_PREVIOUS_SYNC: findFlags = SampleTable::kFlagBefore; break; case ReadOptions::SEEK_NEXT_SYNC: findFlags = SampleTable::kFlagAfter; break; case ReadOptions::SEEK_CLOSEST_SYNC: case ReadOptions::SEEK_CLOSEST: findFlags = SampleTable::kFlagClosest; break; default: CHECK(!"Should not be here."); break; }
uint32_t sampleIndex; status_t err = mSampleTable->findSampleAtTime( seekTimeUs * mTimescale / 1000000, &sampleIndex, findFlags); ---- ------------------- 注释1
if (mode == ReadOptions::SEEK_CLOSEST) { // We found the closest sample already, now we want the sync // sample preceding it (or the sample itself of course), even // if the subsequent sync sample is closer. findFlags = SampleTable::kFlagBefore; }
uint32_t syncSampleIndex; if (err == OK) { err = mSampleTable->findSyncSampleNear( sampleIndex, &syncSampleIndex, findFlags);------------------- 注释2 }
uint32_t sampleTime; if (err == OK) { err = mSampleTable->getMetaDataForSample( sampleIndex, NULL, NULL, &sampleTime); } if (mode == ReadOptions::SEEK_CLOSEST) { targetSampleTimeUs = (sampleTime * 1000000ll) / mTimescale; }
uint32_t syncSampleTime; CHECK_EQ(OK, mSampleTable->getMetaDataForSample( syncSampleIndex, NULL, NULL, &syncSampleTime)); ALOGI("seek to time %lld us => sample at time %lld us, " "sync sample at time %lld us", seekTimeUs, sampleTime * 1000000ll / mTimescale, syncSampleTime * 1000000ll / mTimescale);
mCurrentSampleIndex = syncSampleIndex; ------------注释3:好了得到了关键帧的sampleIndex,赋值为当前需要读取的sampleIndex。 if (mBuffer != NULL) { mBuffer->release(); mBuffer = NULL; } // fall through } 函数后面的内容是上一篇博客讲解的,通过mCurrentSampleIndex获取offset,size和cts,略过。
uint32_t req_time, uint32_t *sample_index, uint32_t flags) { buildSampleEntriesTable(); 通过time找sampleIndex会调用该函数初始化一下,可以跳到下面看这个函数的解析 使用二分查找在有序的数组mSampleTimeEntries中查找元素对应的mCompositionTime和seekTimeUs最接近的那个sampleIndex uint32_t left = 0; uint32_t right = mNumSampleSizes; while (left < right) { uint32_t center = (left + right) / 2; uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime; if (req_time < centerTime) { right = center; } else if (req_time > centerTime) { left = center + 1; } else { left = center; break; } }
if (left == mNumSampleSizes) { if (flags == kFlagAfter) { return ERROR_OUT_OF_RANGE; } --left; } uint32_t closestIndex = left; 根据不同的规则,对sampleIndex进行微调,找到最接近的sample switch (flags) { case kFlagBefore: { while (closestIndex > 0 && mSampleTimeEntries[closestIndex].mCompositionTime > req_time) { --closestIndex; } break; }
case kFlagAfter: { while (closestIndex + 1 < mNumSampleSizes && mSampleTimeEntries[closestIndex].mCompositionTime < req_time) { ++closestIndex; } break; } default: { CHECK(flags == kFlagClosest); if (closestIndex > 0) { // Check left neighbour and pick closest. uint32_t absdiff1 = abs_difference( mSampleTimeEntries[closestIndex].mCompositionTime, req_time); uint32_t absdiff2 = abs_difference( mSampleTimeEntries[closestIndex - 1].mCompositionTime, req_time); if (absdiff1 > absdiff2) { closestIndex = closestIndex - 1; } } break; } } *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex; return OK;
Mutex::Autolock autoLock(mLock); if (mSampleTimeEntries != NULL) { return; } 这个if功能类似于类中的singleton,只会初始化一次,初始化之后在进来就直接return了,提高了效率 mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes]; 建立一个数组,数组大小为sample的个数,数组的内容为一个结构体,结构体的定义如下: ****************************************** struct SampleTimeEntry { uint32_t mSampleIndex; sample的索引号 uint32_t mCompositionTime; 该sample对应的时间戳 }; ****************************************** uint32_t sampleIndex = 0; uint32_t sampleTime = 0; 通过stts表,将每一个sample的时间戳记录下来,保存在数组mSampleTimeEntries中,具体做法是如下:第一个for循环遍历所有stts表的记录,第二个for循环遍历stts表中一个记录中包含的所有的sample,每个sample的时间戳计算公式:mCompositionTime = 前面所有sample的时间和 + 当前sample的composition time(查ctts表得到) for (uint32_t i = 0; i < mTimeToSampleCount; ++i) { uint32_t n = mTimeToSample[2 * i]; uint32_t delta = mTimeToSample[2 * i + 1]; for (uint32_t j = 0; j < n; ++j) { if (sampleIndex < mNumSampleSizes) { // Technically this should always be the case if the file // is well-formed, but you know... there's (gasp) malformed // content out there. mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex; uint32_t compTimeDelta = mCompositionDeltaLookup->getCompositionTimeOffset( sampleIndex); mSampleTimeEntries[sampleIndex].mCompositionTime = sampleTime + compTimeDelta; } ++sampleIndex; sampleTime += delta; } } qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry), CompareIncreasingTime); 按照升序排列所有的记录,后面的二分查找需要在一个有序的数组内进行。
uint32_t start_sample_index, uint32_t *sample_index, uint32_t flags) { Mutex::Autolock autoLock(mLock); *sample_index = 0; 没有stsc表的情况,默认所有的sample都是关键帧 if (mSyncSampleOffset < 0) { // All samples are sync-samples. *sample_index = start_sample_index; return OK; } 如果没有关键帧,则使用第一个sample if (mNumSyncSamples == 0) { *sample_index = 0; return OK; }
又是二分查找法,查找关键帧数组中和制定的上面找出来的sampleIndex最近的syncSampleIndex uint32_t left = 0; uint32_t right = mNumSyncSamples; while (left < right) { uint32_t center = left + (right - left) / 2; uint32_t x = mSyncSamples[center]; if (start_sample_index < x) { right = center; } else if (start_sample_index > x) { left = center + 1; } else { left = center; break; } } if (left == mNumSyncSamples) { if (flags == kFlagAfter) { ALOGE("tried to find a sync frame after the last one: %d", left); return ERROR_OUT_OF_RANGE; } left = left - 1; } // Now ssi[left] is the sync sample index just before (or at) // start_sample_index. // Also start_sample_index < ssi[left + 1], if left + 1 < mNumSyncSamples. uint32_t x = mSyncSamples[left]; if (left + 1 < mNumSyncSamples) { uint32_t y = mSyncSamples[left + 1]; // our sample lies between sync samples x and y. status_t err = mSampleIterator->seekTo(start_sample_index); if (err != OK) { return err; } uint32_t sample_time = mSampleIterator->getSampleTime(); err = mSampleIterator->seekTo(x); if (err != OK) { return err; } uint32_t x_time = mSampleIterator->getSampleTime(); err = mSampleIterator->seekTo(y); if (err != OK) { return err; } uint32_t y_time = mSampleIterator->getSampleTime(); if (abs_difference(x_time, sample_time) > abs_difference(y_time, sample_time)) { // Pick the sync sample closest (timewise) to the start-sample. x = y; ++left; } } switch (flags) { case kFlagBefore: { if (x > start_sample_index) { CHECK(left > 0); x = mSyncSamples[left - 1]; if (x > start_sample_index) { // The table of sync sample indices was not sorted // properly. return ERROR_MALFORMED; } } break; } case kFlagAfter: { if (x < start_sample_index) { if (left + 1 >= mNumSyncSamples) { return ERROR_OUT_OF_RANGE; } x = mSyncSamples[left + 1]; if (x < start_sample_index) { // The table of sync sample indices was not sorted // properly. return ERROR_MALFORMED; } } break; } default: break; } 上面一段是微调,找到最接近的syncSampleIndex *sample_index = x; return OK;
当我们在播放界面上做了一个拖动进度条的操作,放手的那一刻我们最终给代码传递了一个值:我们拖动到的视频的播放的时刻,这里以代码中的seekTimeUs表示,得到这个值之后,我们大致的流程就是通过这个值,获取对应的sampleIndex,得到sampleIndex之后我们找离这个sampleIndex附近的某一关键帧的syncSampleIndex(附近的判定原则有三种,向前,向后,前后最近,对应三个case),得到syncSampleIndex之后,按照前一篇博客的内容,得到这个sampleIndex对应的offset和size,cts等信息,就可以送到解码器去播放。
下面就从代码上跟一下整个流程,仍然以MP4文件为例:
我们知道MP4文件的读取最后都会进入到MPEG4Extractor类的read函数中,在这个函数中首先就需要判断标志位,是否是seek的模式
status_t MPEG4Source::read(
#if 0
#endif
}
------------------------------注释1:通过时间戳找其对应的sampleIndex
status_t SampleTable::findSampleAtTime(
}
void SampleTable::buildSampleEntriesTable() {
}
通过上面的两个函数,以及二分查找,找到了和seekTimeUs最接近的一个sample的sampleIndex,下面需要找这个sampleIndex最接近的关键帧对应的syncSampleIndex
-----------------------------注释2:通过sampleIndex找syncSampleIndex
status_t SampleTable::findSyncSampleNear(
}
到此,找到了相对于seekTimeUs最近的一个关键帧对应的syncSampleIndex,将这个值赋值给mCurrentSampleIndex,然后去解析offset和size。
Over.
0 0
- MP4 seek状态 sample读取流程
- MP4 seek状态 sample读取流程
- MP4文件sample读取流程
- MP4文件sample读取流程
- MP4文件sample读取流程
- mp4 seek
- MP4文件Sample获取
- MP4文件Sample获取
- mp4 查找sample偏移
- MP4文件点播seek原理
- MP4文件点播seek原理
- 获取mp4文件信息6 - 查找sample
- 服务器mp4读取配置
- OPenCv读取MP4视频
- 声音文件Sample数据读取
- thumbnail的处理流程和读取缩略图thumbnail所在sample的index的方法
- nginx搭建支持flv mp4 seek 实现拖拽
- ts, mp4文件快进快退(seek)原理
- Unity Mecanim动画的实现(一):基本程序
- hls流媒体:ts流格式介绍
- Linux中的几个问题
- android属性之noHistory
- Redis学习03-对String类型的操作
- MP4 seek状态 sample读取流程
- 【一步一步学习VBA】WORD 中创建表格并插入文本
- Matlab 使用squeeze函数提取多维矩阵的某一维
- android异步任务 访问网络 加载图片 解决方案大集合
- 元组VS列表
- 拓扑排序->POJ2585
- # 记录每日点滴
- Windows下还原Oracle数据库常用命令
- [php学习十八]JQuery练习3-综合