Bug 1126465 - Introduce machinery to hold onto MediaPromise::Consumer references, and use it for MediaSourceReader subdecoders. r=mattwoodrow
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -247,33 +247,45 @@ protected:
nsRefPtr<ThisType> mThisVal;
ResolveMethodType mResolveMethod;
RejectMethodType mRejectMethod;
};
public:
template<typename TargetType, typename ThisType,
typename ResolveMethodType, typename RejectMethodType>
- void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal,
- ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+ already_AddRefed<Consumer> RefableThen(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal,
+ ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
{
MutexAutoLock lock(mMutex);
MOZ_RELEASE_ASSERT(!IsExclusive || !mHaveConsumer);
mHaveConsumer = true;
nsRefPtr<ThenValueBase> thenValue = new ThenValue<TargetType, ThisType, ResolveMethodType,
RejectMethodType>(aResponseTarget, aThisVal,
aResolveMethod, aRejectMethod,
aCallSite);
PROMISE_LOG("%s invoking Then() [this=%p, thenValue=%p, aThisVal=%p, isPending=%d]",
aCallSite, this, thenValue.get(), aThisVal, (int) IsPending());
if (!IsPending()) {
thenValue->Dispatch(this);
} else {
mThenValues.AppendElement(thenValue);
}
+
+ return thenValue.forget();
+ }
+
+ template<typename TargetType, typename ThisType,
+ typename ResolveMethodType, typename RejectMethodType>
+ void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal,
+ ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+ {
+ nsRefPtr<Consumer> c =
+ RefableThen(aResponseTarget, aCallSite, aThisVal, aResolveMethod, aRejectMethod);
+ return;
}
void ChainTo(already_AddRefed<MediaPromise> aChainedPromise, const char* aCallSite)
{
MutexAutoLock lock(mMutex);
MOZ_RELEASE_ASSERT(!IsExclusive || !mHaveConsumer);
mHaveConsumer = true;
nsRefPtr<MediaPromise> chainedPromise = aChainedPromise;
@@ -431,13 +443,42 @@ public:
}
}
private:
Monitor* mMonitor;
nsRefPtr<PromiseType> mPromise;
};
+/*
+ * Class to encapsulate a MediaPromise::Consumer reference. Use this as the member
+ * variable for a class waiting on a media promise.
+ */
+template<typename PromiseType>
+class MediaPromiseConsumerHolder
+{
+public:
+ MediaPromiseConsumerHolder() {}
+ ~MediaPromiseConsumerHolder() { MOZ_ASSERT(!mConsumer); }
+
+ void Begin(already_AddRefed<typename PromiseType::Consumer> aConsumer)
+ {
+ MOZ_RELEASE_ASSERT(!Exists());
+ mConsumer = aConsumer;
+ }
+
+ void Complete()
+ {
+ MOZ_RELEASE_ASSERT(Exists());
+ mConsumer = nullptr;
+ }
+
+ bool Exists() { return !!mConsumer; }
+
+private:
+ nsRefPtr<typename PromiseType::Consumer> mConsumer;
+};
+
#undef PROMISE_LOG
} // namespace mozilla
#endif
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -144,39 +144,43 @@ MediaSourceReader::RequestAudioData()
break;
}
return p;
}
void
MediaSourceReader::RequestAudioDataComplete(int64_t aTime)
{
- mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
- &MediaSourceReader::OnAudioDecoded,
- &MediaSourceReader::OnAudioNotDecoded);
+ mAudioRequest.Begin(mAudioReader->RequestAudioData()
+ ->RefableThen(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnAudioDecoded,
+ &MediaSourceReader::OnAudioNotDecoded));
}
void
MediaSourceReader::RequestAudioDataFailed(nsresult aResult)
{
mAudioPromise.Reject(DECODE_ERROR, __func__);
}
void
MediaSourceReader::OnAudioDecoded(AudioData* aSample)
{
+ mAudioRequest.Complete();
+
MSE_DEBUGV("MediaSourceReader(%p)::OnAudioDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
if (mDropAudioBeforeThreshold) {
if (aSample->mTime < mTimeThreshold) {
MSE_DEBUG("MediaSourceReader(%p)::OnAudioDecoded mTime=%lld < mTimeThreshold=%lld",
this, aSample->mTime, mTimeThreshold);
- mAudioReader->RequestAudioData()->Then(GetTaskQueue(), __func__, this,
- &MediaSourceReader::OnAudioDecoded,
- &MediaSourceReader::OnAudioNotDecoded);
+ mAudioRequest.Begin(mAudioReader->RequestAudioData()
+ ->RefableThen(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnAudioDecoded,
+ &MediaSourceReader::OnAudioNotDecoded));
return;
}
mDropAudioBeforeThreshold = false;
}
// Any OnAudioDecoded callbacks received while mAudioIsSeeking must be not
// update our last used timestamp, as these are emitted by the reader we're
// switching away from.
@@ -211,16 +215,18 @@ AdjustEndTime(int64_t* aEndTime, MediaDe
*aEndTime = std::max(*aEndTime, end);
}
}
}
void
MediaSourceReader::OnAudioNotDecoded(NotDecodedReason aReason)
{
+ mAudioRequest.Complete();
+
MSE_DEBUG("MediaSourceReader(%p)::OnAudioNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
if (aReason == DECODE_ERROR || aReason == CANCELED) {
mAudioPromise.Reject(aReason, __func__);
return;
}
// End of stream. Force switching past this stream to another reader by
// switching to the end of the buffered range.
@@ -275,51 +281,56 @@ MediaSourceReader::RequestVideoData(bool
break;
case READER_ERROR:
if (mLastVideoTime) {
CheckForWaitOrEndOfStream(MediaData::VIDEO_DATA, mLastVideoTime);
break;
}
// Fallback to using current reader.
default:
- mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
- ->Then(GetTaskQueue(), __func__, this,
- &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
+ mVideoRequest.Begin(mVideoReader->RequestVideoData(aSkipToNextKeyframe, aTimeThreshold)
+ ->RefableThen(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnVideoDecoded,
+ &MediaSourceReader::OnVideoNotDecoded));
break;
}
return p;
}
void
MediaSourceReader::RequestVideoDataComplete(int64_t aTime)
{
- mVideoReader->RequestVideoData(false, 0)
- ->Then(GetTaskQueue(), __func__, this,
- &MediaSourceReader::OnVideoDecoded, &MediaSourceReader::OnVideoNotDecoded);
+ mVideoRequest.Begin(mVideoReader->RequestVideoData(false, 0)
+ ->RefableThen(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnVideoDecoded,
+ &MediaSourceReader::OnVideoNotDecoded));
}
void
MediaSourceReader::RequestVideoDataFailed(nsresult aResult)
{
mVideoPromise.Reject(DECODE_ERROR, __func__);
}
void
MediaSourceReader::OnVideoDecoded(VideoData* aSample)
{
+ mVideoRequest.Complete();
+
MSE_DEBUGV("MediaSourceReader(%p)::OnVideoDecoded [mTime=%lld mDuration=%lld mDiscontinuity=%d]",
this, aSample->mTime, aSample->mDuration, aSample->mDiscontinuity);
if (mDropVideoBeforeThreshold) {
if (aSample->mTime < mTimeThreshold) {
MSE_DEBUG("MediaSourceReader(%p)::OnVideoDecoded mTime=%lld < mTimeThreshold=%lld",
this, aSample->mTime, mTimeThreshold);
- mVideoReader->RequestVideoData(false, 0)->Then(GetTaskQueue(), __func__, this,
- &MediaSourceReader::OnVideoDecoded,
- &MediaSourceReader::OnVideoNotDecoded);
+ mVideoRequest.Begin(mVideoReader->RequestVideoData(false, 0)
+ ->RefableThen(GetTaskQueue(), __func__, this,
+ &MediaSourceReader::OnVideoDecoded,
+ &MediaSourceReader::OnVideoNotDecoded));
return;
}
mDropVideoBeforeThreshold = false;
}
// Any OnVideoDecoded callbacks received while mVideoIsSeeking must be not
// update our last used timestamp, as these are emitted by the reader we're
// switching away from.
@@ -328,16 +339,18 @@ MediaSourceReader::OnVideoDecoded(VideoD
}
mVideoPromise.Resolve(aSample, __func__);
}
void
MediaSourceReader::OnVideoNotDecoded(NotDecodedReason aReason)
{
+ mVideoRequest.Complete();
+
MSE_DEBUG("MediaSourceReader(%p)::OnVideoNotDecoded aReason=%u IsEnded: %d", this, aReason, IsEnded());
if (aReason == DECODE_ERROR || aReason == CANCELED) {
mVideoPromise.Reject(aReason, __func__);
return;
}
// End of stream. Force switching past this stream to another reader by
// switching to the end of the buffered range.
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -182,16 +182,19 @@ private:
nsRefPtr<MediaDecoderReader> mVideoReader;
nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
nsTArray<nsRefPtr<TrackBuffer>> mShutdownTrackBuffers;
nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
nsRefPtr<TrackBuffer> mAudioTrack;
nsRefPtr<TrackBuffer> mVideoTrack;
+ MediaPromiseConsumerHolder<AudioDataPromise> mAudioRequest;
+ MediaPromiseConsumerHolder<VideoDataPromise> mVideoRequest;
+
MediaPromiseHolder<AudioDataPromise> mAudioPromise;
MediaPromiseHolder<VideoDataPromise> mVideoPromise;
MediaPromiseHolder<WaitForDataPromise> mAudioWaitPromise;
MediaPromiseHolder<WaitForDataPromise> mVideoWaitPromise;
MediaPromiseHolder<WaitForDataPromise>& WaitPromise(MediaData::Type aType)
{
return aType == MediaData::AUDIO_DATA ? mAudioWaitPromise : mVideoWaitPromise;