Bug 1195073: [MSE] P5. Detect out of order appends and recreate demuxer. r=gerald

The webm demuxer will only handle data where frames's a monotonically increasing.
This commit is contained in:
Jean-Yves Avenard 2015-08-19 15:31:30 +10:00
Родитель 3cd00ea187
Коммит e4dff83b7a
3 изменённых файлов: 87 добавлений и 1 удалений

Просмотреть файл

@ -97,6 +97,7 @@ TrackBuffersManager::TrackBuffersManager(dom::SourceBufferAttributes* aAttribute
, mAppendState(AppendState::WAITING_FOR_SEGMENT)
, mBufferFull(false)
, mFirstInitializationSegmentReceived(false)
, mNewSegmentStarted(false)
, mActiveTrack(false)
, mType(aType)
, mParser(ContainerParser::CreateForMIMEType(aType))
@ -655,10 +656,12 @@ TrackBuffersManager::SegmentParserLoop()
// This is a new initialization segment. Obsolete the old one.
RecreateParser(false);
}
mNewSegmentStarted = true;
continue;
}
if (mParser->IsMediaSegmentPresent(mInputBuffer)) {
SetAppendState(AppendState::PARSING_MEDIA_SEGMENT);
mNewSegmentStarted = true;
continue;
}
// We have neither an init segment nor a media segment, this is either
@ -669,7 +672,7 @@ TrackBuffersManager::SegmentParserLoop()
}
int64_t start, end;
mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
bool newData = mParser->ParseStartAndEndTimestamps(mInputBuffer, start, end);
mProcessedInput += mInputBuffer->Length();
// 5. If the append state equals PARSING_INIT_SEGMENT, then run the
@ -696,6 +699,22 @@ TrackBuffersManager::SegmentParserLoop()
NeedMoreData();
return;
}
// We can't feed some demuxers (WebMDemuxer) with data that do not have
// monotonizally increasing timestamps. So we check if we have a
// discontinuity from the previous segment parsed.
// If so, recreate a new demuxer to ensure that the demuxer is only fed
// monotonically increasing data.
if (newData) {
if (mNewSegmentStarted && mLastParsedEndTime.isSome() &&
start < mLastParsedEndTime.ref().ToMicroseconds()) {
ResetDemuxingState();
return;
}
mNewSegmentStarted = false;
mLastParsedEndTime = Some(TimeUnit::FromMicroseconds(end));
}
// 3. If the input buffer contains one or more complete coded frames, then run the coded frame processing algorithm.
nsRefPtr<TrackBuffersManager> self = this;
mProcessingRequest.Begin(CodedFrameProcessing()
@ -756,6 +775,7 @@ TrackBuffersManager::ShutdownDemuxers()
mAudioTracks.mDemuxer = nullptr;
}
mInputDemuxer = nullptr;
mLastParsedEndTime.reset();
}
void
@ -780,6 +800,58 @@ TrackBuffersManager::CreateDemuxerforMIMEType()
return;
}
// We reset the demuxer by creating a new one and initializing it.
void
TrackBuffersManager::ResetDemuxingState()
{
MOZ_ASSERT(mParser && mParser->HasInitData());
RecreateParser(true);
mCurrentInputBuffer = new SourceBufferResource(mType);
// The demuxer isn't initialized yet ; we don't want to notify it
// that data has been appended yet ; so we simply append the init segment
// to the resource.
mCurrentInputBuffer->AppendData(mParser->InitData());
CreateDemuxerforMIMEType();
if (!mInputDemuxer) {
RejectAppend(NS_ERROR_FAILURE, __func__);
return;
}
mDemuxerInitRequest.Begin(mInputDemuxer->Init()
->Then(GetTaskQueue(), __func__,
this,
&TrackBuffersManager::OnDemuxerResetDone,
&TrackBuffersManager::OnDemuxerInitFailed));
}
void
TrackBuffersManager::OnDemuxerResetDone(nsresult)
{
MOZ_ASSERT(OnTaskQueue());
MSE_DEBUG("mAbort:%d", static_cast<bool>(mAbort));
mDemuxerInitRequest.Complete();
if (mAbort) {
RejectAppend(NS_ERROR_ABORT, __func__);
return;
}
// Recreate track demuxers.
uint32_t numVideos = mInputDemuxer->GetNumberTracks(TrackInfo::kVideoTrack);
if (numVideos) {
// We currently only handle the first video track.
mVideoTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
MOZ_ASSERT(mVideoTracks.mDemuxer);
}
uint32_t numAudios = mInputDemuxer->GetNumberTracks(TrackInfo::kAudioTrack);
if (numAudios) {
// We currently only handle the first audio track.
mAudioTracks.mDemuxer = mInputDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0);
MOZ_ASSERT(mAudioTracks.mDemuxer);
}
SegmentParserLoop();
}
void
TrackBuffersManager::AppendDataToCurrentInputBuffer(MediaByteBuffer* aData)
{
@ -1056,6 +1128,14 @@ TrackBuffersManager::CodedFrameProcessing()
// The mediaRange is offset by the init segment position previously added.
uint32_t length =
mediaRange.mEnd - (mProcessedInput - mInputBuffer->Length());
if (!length) {
// We've completed our earlier media segment and no new data is to be
// processed. This happens with some containers that can't detect that a
// media segment is ending until a new one starts.
nsRefPtr<CodedFrameProcessingPromise> p = mProcessingPromise.Ensure(__func__);
CompleteCodedFrameProcessing();
return p;
}
nsRefPtr<MediaByteBuffer> segment = new MediaByteBuffer;
if (!segment->AppendElements(mInputBuffer->Elements(), length, fallible)) {
return CodedFrameProcessingPromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);

Просмотреть файл

@ -113,6 +113,7 @@ private:
void InitializationSegmentReceived();
void ShutdownDemuxers();
void CreateDemuxerforMIMEType();
void ResetDemuxingState();
void NeedMoreData();
void RejectAppend(nsresult aRejectValue, const char* aName);
// Will return a promise that will be resolved once all frames of the current
@ -151,6 +152,8 @@ private:
// TODO: Unused for now.
Atomic<bool> mBufferFull;
bool mFirstInitializationSegmentReceived;
// Set to true once a new segment is started.
bool mNewSegmentStarted;
bool mActiveTrack;
Maybe<media::TimeUnit> mGroupStartTimestamp;
media::TimeUnit mGroupEndTimestamp;
@ -171,9 +174,11 @@ private:
nsRefPtr<MediaDataDemuxer> mInputDemuxer;
// Length already processed in current media segment.
uint32_t mProcessedInput;
Maybe<media::TimeUnit> mLastParsedEndTime;
void OnDemuxerInitDone(nsresult);
void OnDemuxerInitFailed(DemuxerFailureReason aFailure);
void OnDemuxerResetDone(nsresult);
MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
bool mEncrypted;

Просмотреть файл

@ -208,6 +208,7 @@ void WebMBufferedParser::Append(const unsigned char* aBuffer, uint32_t aLength,
if (!mSkipBytes) {
if (mInitEndOffset < 0) {
mInitEndOffset = mCurrentOffset + (p - aBuffer);
mBlockEndOffset = mCurrentOffset + (p - aBuffer);
}
mState = READ_ELEMENT_ID;
}