зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1125776: Part2. appendBuffer scanning the data before firing updateend. r=mattwoodrow
This commit is contained in:
Родитель
80732517a0
Коммит
a7911540c6
|
@ -205,7 +205,7 @@ MediaSourceDecoder::DurationChanged(double aOldDuration, double aNewDuration)
|
|||
}
|
||||
|
||||
void
|
||||
MediaSourceDecoder::SetDecodedDuration(int64_t aDuration)
|
||||
MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
|
||||
{
|
||||
// Only use the decoded duration if one wasn't already
|
||||
// set.
|
||||
|
@ -218,7 +218,7 @@ MediaSourceDecoder::SetDecodedDuration(int64_t aDuration)
|
|||
if (aDuration >= 0) {
|
||||
duration /= USECS_PER_S;
|
||||
}
|
||||
DoSetMediaSourceDuration(duration);
|
||||
SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -226,13 +226,6 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalActio
|
|||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
double oldDuration = mMediaSourceDuration;
|
||||
DoSetMediaSourceDuration(aDuration);
|
||||
ScheduleDurationChange(oldDuration, aDuration, aAction);
|
||||
}
|
||||
|
||||
void
|
||||
MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
|
||||
{
|
||||
if (aDuration >= 0) {
|
||||
mDecoderStateMachine->SetDuration(aDuration * USECS_PER_S);
|
||||
mMediaSourceDuration = aDuration;
|
||||
|
@ -243,6 +236,7 @@ MediaSourceDecoder::DoSetMediaSourceDuration(double aDuration)
|
|||
if (mReader) {
|
||||
mReader->SetMediaSourceDuration(mMediaSourceDuration);
|
||||
}
|
||||
ScheduleDurationChange(oldDuration, aDuration, aAction);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
void Ended();
|
||||
bool IsExpectingMoreData() MOZ_OVERRIDE;
|
||||
|
||||
void SetDecodedDuration(int64_t aDuration);
|
||||
void SetInitialDuration(int64_t aDuration);
|
||||
void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
|
||||
double GetMediaSourceDuration();
|
||||
void DurationChanged(double aOldDuration, double aNewDuration);
|
||||
|
|
|
@ -897,8 +897,6 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int64_t maxDuration = -1;
|
||||
|
||||
if (mAudioTrack) {
|
||||
MOZ_ASSERT(mAudioTrack->IsReady());
|
||||
mAudioReader = mAudioTrack->Decoders()[0]->GetReader();
|
||||
|
@ -907,9 +905,9 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
|||
MOZ_ASSERT(info.HasAudio());
|
||||
mInfo.mAudio = info.mAudio;
|
||||
mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
|
||||
maxDuration = std::max(maxDuration, mAudioReader->GetDecoder()->GetMediaDuration());
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p maxDuration=%lld",
|
||||
this, mAudioReader.get(), maxDuration);
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata audio reader=%p duration=%lld",
|
||||
this, mAudioReader.get(),
|
||||
mAudioReader->GetDecoder()->GetMediaDuration());
|
||||
}
|
||||
|
||||
if (mVideoTrack) {
|
||||
|
@ -920,17 +918,11 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
|
|||
MOZ_ASSERT(info.HasVideo());
|
||||
mInfo.mVideo = info.mVideo;
|
||||
mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
|
||||
maxDuration = std::max(maxDuration, mVideoReader->GetDecoder()->GetMediaDuration());
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p maxDuration=%lld",
|
||||
this, mVideoReader.get(), maxDuration);
|
||||
MSE_DEBUG("MediaSourceReader(%p)::ReadMetadata video reader=%p duration=%lld",
|
||||
this, mVideoReader.get(),
|
||||
mVideoReader->GetDecoder()->GetMediaDuration());
|
||||
}
|
||||
|
||||
if (!maxDuration) {
|
||||
// Treat a duration of 0 as infinity
|
||||
maxDuration = -1;
|
||||
}
|
||||
static_cast<MediaSourceDecoder*>(mDecoder)->SetDecodedDuration(maxDuration);
|
||||
|
||||
*aInfo = mInfo;
|
||||
*aTags = nullptr; // TODO: Handle metadata.
|
||||
|
||||
|
|
|
@ -269,6 +269,8 @@ SourceBuffer::RangeRemoval(double aStart, double aEnd)
|
|||
void
|
||||
SourceBuffer::DoRangeRemoval(double aStart, double aEnd)
|
||||
{
|
||||
MSE_DEBUG("SourceBuffer(%p)::DoRangeRemoval (updating:%d)",
|
||||
this, mUpdating);
|
||||
if (!mUpdating) {
|
||||
// abort was called in between.
|
||||
return;
|
||||
|
@ -397,9 +399,11 @@ SourceBuffer::AbortUpdating()
|
|||
void
|
||||
SourceBuffer::CheckEndTime()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// Check if we need to update mMediaSource duration
|
||||
double endTime = GetBufferedEnd();
|
||||
if (endTime > mMediaSource->Duration()) {
|
||||
double duration = mMediaSource->Duration();
|
||||
if (endTime > duration) {
|
||||
mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP);
|
||||
}
|
||||
}
|
||||
|
@ -437,21 +441,41 @@ SourceBuffer::AppendData(LargeDataBuffer* aData, double aTimestampOffset)
|
|||
|
||||
MOZ_ASSERT(mMediaSource);
|
||||
|
||||
if (aData->Length()) {
|
||||
if (!mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)) {
|
||||
AppendError(true);
|
||||
return;
|
||||
}
|
||||
if (!aData->Length()) {
|
||||
StopUpdating();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTrackBuffer->HasInitSegment()) {
|
||||
mMediaSource->QueueInitializationEvent();
|
||||
}
|
||||
mTrackBuffer->AppendData(aData, aTimestampOffset * USECS_PER_S)
|
||||
->Then(NS_GetCurrentThread(), __func__, this,
|
||||
&SourceBuffer::AppendDataCompletedWithSuccess,
|
||||
&SourceBuffer::AppendDataErrored);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendDataCompletedWithSuccess(bool aGotMedia)
|
||||
{
|
||||
if (!mUpdating) {
|
||||
// The buffer append algorithm has been interrupted by abort().
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTrackBuffer->HasInitSegment()) {
|
||||
mMediaSource->QueueInitializationEvent();
|
||||
}
|
||||
|
||||
if (aGotMedia) {
|
||||
CheckEndTime();
|
||||
}
|
||||
|
||||
StopUpdating();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendDataErrored(nsresult aError)
|
||||
{
|
||||
AppendError(true);
|
||||
}
|
||||
|
||||
void
|
||||
SourceBuffer::AppendError(bool aDecoderError)
|
||||
|
|
|
@ -153,6 +153,9 @@ private:
|
|||
uint32_t aLength,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void AppendDataCompletedWithSuccess(bool aValue);
|
||||
void AppendDataErrored(nsresult aError);
|
||||
|
||||
nsRefPtr<MediaSource> mMediaSource;
|
||||
|
||||
uint32_t mEvictionThreshold;
|
||||
|
|
|
@ -102,6 +102,11 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
size_t Length()
|
||||
{
|
||||
return mDecoders.Length();
|
||||
}
|
||||
|
||||
private:
|
||||
TrackBuffer* mOwner;
|
||||
nsAutoTArray<nsRefPtr<SourceBufferDecoder>,2> mDecoders;
|
||||
|
@ -144,20 +149,24 @@ TrackBuffer::ContinueShutdown()
|
|||
mShutdownPromise.Resolve(true, __func__);
|
||||
}
|
||||
|
||||
bool
|
||||
nsRefPtr<TrackBuffer::InitializationPromise>
|
||||
TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DecodersToInitialize decoders(this);
|
||||
nsRefPtr<InitializationPromise> p = mInitializationPromise.Ensure(__func__);
|
||||
|
||||
// TODO: Run more of the buffer append algorithm asynchronously.
|
||||
if (mParser->IsInitSegmentPresent(aData)) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
|
||||
if (!decoders.NewDecoder(aTimestampOffset)) {
|
||||
return false;
|
||||
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
|
||||
return p;
|
||||
}
|
||||
} else if (!mParser->HasInitData()) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
|
||||
return false;
|
||||
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
|
||||
return p;
|
||||
}
|
||||
|
||||
int64_t start = 0, end = 0;
|
||||
|
@ -175,7 +184,8 @@ TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
|
|||
// This data is earlier in the timeline than data we have already
|
||||
// processed, so we must create a new decoder to handle the decoding.
|
||||
if (!decoders.NewDecoder(aTimestampOffset)) {
|
||||
return false;
|
||||
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
|
||||
return p;
|
||||
}
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
|
||||
nsRefPtr<LargeDataBuffer> initData = mParser->InitData();
|
||||
|
@ -190,13 +200,22 @@ TrackBuffer::AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset)
|
|||
}
|
||||
|
||||
if (!AppendDataToCurrentResource(aData, end - start)) {
|
||||
return false;
|
||||
mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
|
||||
return p;
|
||||
}
|
||||
|
||||
if (decoders.Length()) {
|
||||
// TODO: the theory is that we should only ever have one decoder to
|
||||
// initialize in common use. We can't properly handle the condition where
|
||||
// the source buffer needs to wait on two decoders to initialize.
|
||||
return p;
|
||||
}
|
||||
// Tell our reader that we have more data to ensure that playback starts if
|
||||
// required when data is appended.
|
||||
mParentDecoder->GetReader()->MaybeNotifyHaveData();
|
||||
return true;
|
||||
|
||||
mInitializationPromise.Resolve(end - start > 0, __func__);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -410,6 +429,7 @@ bool
|
|||
TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
{
|
||||
if (NS_WARN_IF(!mTaskQueue)) {
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -418,8 +438,9 @@ TrackBuffer::QueueInitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
&TrackBuffer::InitializeDecoder,
|
||||
aDecoder);
|
||||
if (NS_FAILED(mTaskQueue->Dispatch(task))) {
|
||||
MSE_DEBUG("MediaSourceReader(%p): Failed to enqueue decoder initialization task", this);
|
||||
MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
|
||||
RemoveDecoder(aDecoder);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -441,6 +462,7 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
// important pieces of our state (like mTaskQueue) have also been torn down.
|
||||
if (mShutdown) {
|
||||
MSE_DEBUG("TrackBuffer(%p) was shut down. Aborting initialization.", this);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -460,6 +482,7 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
reader->SetIdle();
|
||||
if (mShutdown) {
|
||||
MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -475,6 +498,7 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
MSE_DEBUG("TrackBuffer(%p): Reader %p failed to initialize rv=%x audio=%d video=%d",
|
||||
this, reader, rv, mi.HasAudio(), mi.HasVideo());
|
||||
RemoveDecoder(aDecoder);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -487,18 +511,51 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
|
|||
this, reader, mi.mAudio.mRate, mi.mAudio.mChannels);
|
||||
}
|
||||
|
||||
if (!RegisterDecoder(aDecoder)) {
|
||||
// XXX: Need to signal error back to owning SourceBuffer.
|
||||
MSE_DEBUG("TrackBuffer(%p): Reader %p not activated", this, reader);
|
||||
RefPtr<nsIRunnable> task =
|
||||
NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
|
||||
&TrackBuffer::CompleteInitializeDecoder,
|
||||
aDecoder);
|
||||
if (NS_FAILED(NS_DispatchToMainThread(task))) {
|
||||
MSE_DEBUG("TrackBuffer(%p): Failed to enqueue decoder initialization task", this);
|
||||
RemoveDecoder(aDecoder);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
if (mShutdown) {
|
||||
MSE_DEBUG("TrackBuffer(%p) was shut down while reading metadata. Aborting initialization.", this);
|
||||
RemoveDecoder(aDecoder);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_ABORT, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!RegisterDecoder(aDecoder)) {
|
||||
MSE_DEBUG("TrackBuffer(%p): Reader %p not activated",
|
||||
this, aDecoder->GetReader());
|
||||
RemoveDecoder(aDecoder);
|
||||
mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t duration = aDecoder->GetMediaDuration();
|
||||
if (!duration) {
|
||||
// Treat a duration of 0 as infinity
|
||||
duration = -1;
|
||||
}
|
||||
mParentDecoder->SetInitialDuration(duration);
|
||||
|
||||
// Tell our reader that we have more data to ensure that playback starts if
|
||||
// required when data is appended.
|
||||
mParentDecoder->GetReader()->MaybeNotifyHaveData();
|
||||
|
||||
MSE_DEBUG("TrackBuffer(%p): Reader %p activated", this, reader);
|
||||
MSE_DEBUG("TrackBuffer(%p): Reader %p activated",
|
||||
this, aDecoder->GetReader());
|
||||
mInitializationPromise.ResolveIfExists(aDecoder->GetRealMediaDuration() > 0, __func__);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -33,6 +33,8 @@ class TrackBuffer MOZ_FINAL {
|
|||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TrackBuffer);
|
||||
|
||||
typedef MediaPromise<bool, nsresult, /* IsExclusive = */ false> InitializationPromise;
|
||||
|
||||
TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& aType);
|
||||
|
||||
nsRefPtr<ShutdownPromise> Shutdown();
|
||||
|
@ -40,7 +42,8 @@ public:
|
|||
// Append data to the current decoder. Also responsible for calling
|
||||
// NotifyDataArrived on the decoder to keep buffered range computation up
|
||||
// to date. Returns false if the append failed.
|
||||
bool AppendData(LargeDataBuffer* aData, int64_t aTimestampOffset /* microseconds */);
|
||||
nsRefPtr<InitializationPromise> AppendData(LargeDataBuffer* aData,
|
||||
int64_t aTimestampOffset /* microseconds */);
|
||||
|
||||
// Evicts data held in the current decoders SourceBufferResource from the
|
||||
// start of the buffer through to aPlaybackTime. aThreshold is used to
|
||||
|
@ -130,6 +133,11 @@ private:
|
|||
// Runs decoder initialization including calling ReadMetadata. Runs as an
|
||||
// event on the decode thread pool.
|
||||
void InitializeDecoder(SourceBufferDecoder* aDecoder);
|
||||
// Once decoder has been initialized, set mediasource duration if required
|
||||
// and resolve any pending InitializationPromise.
|
||||
// Setting the mediasource duration must be done on the main thread.
|
||||
// TODO: Why is that so?
|
||||
void CompleteInitializeDecoder(SourceBufferDecoder* aDecoder);
|
||||
|
||||
// Adds a successfully initialized decoder to mDecoders and (if it's the
|
||||
// first decoder initialized), initializes mHasAudio/mHasVideo. Called
|
||||
|
@ -193,6 +201,9 @@ private:
|
|||
MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
|
||||
bool mDecoderPerSegment;
|
||||
bool mShutdown;
|
||||
|
||||
MediaPromiseHolder<InitializationPromise> mInitializationPromise;
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
Загрузка…
Ссылка в новой задаче