diff --git a/dom/media/MediaDecoderReaderWrapper.cpp b/dom/media/MediaDecoderReaderWrapper.cpp new file mode 100644 index 000000000000..a083a92de77d --- /dev/null +++ b/dom/media/MediaDecoderReaderWrapper.cpp @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MozPromise.h" +#include "MediaDecoderReaderWrapper.h" + +namespace mozilla { + +extern LazyLogModule gMediaDecoderLog; + +#undef LOG +#define LOG(...) \ + MOZ_LOG(gMediaDecoderLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) + +// StartTimeRendezvous is a helper class that quarantines the first sample +// until it gets a sample from both channels, such that we can be guaranteed +// to know the start time by the time On{Audio,Video}Decoded is called on MDSM. +class StartTimeRendezvous { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StartTimeRendezvous); + typedef MediaDecoderReader::AudioDataPromise AudioDataPromise; + typedef MediaDecoderReader::VideoDataPromise VideoDataPromise; + +public: + StartTimeRendezvous(AbstractThread* aOwnerThread, + bool aHasAudio, + bool aHasVideo, + bool aForceZeroStartTime) + : mOwnerThread(aOwnerThread) + { + if (aForceZeroStartTime) { + mAudioStartTime.emplace(0); + mVideoStartTime.emplace(0); + return; + } + if (!aHasAudio) { + mAudioStartTime.emplace(INT64_MAX); + } + if (!aHasVideo) { + mVideoStartTime.emplace(INT64_MAX); + } + } + + void Destroy() + { + mAudioStartTime = Some(mAudioStartTime.refOr(INT64_MAX)); + mVideoStartTime = Some(mVideoStartTime.refOr(INT64_MAX)); + mHaveStartTimePromise.RejectIfExists(false, __func__); + } + + RefPtr AwaitStartTime() + { + if (HaveStartTime()) { + return HaveStartTimePromise::CreateAndResolve(true, __func__); + } + return mHaveStartTimePromise.Ensure(__func__); + } + + template + struct PromiseSampleType { + typedef typename PromiseType::ResolveValueType::element_type Type; + }; + + template + RefPtr + ProcessFirstSample(typename PromiseSampleType::Type* aData) + { + typedef typename PromiseSampleType::Type DataType; + typedef typename PromiseType::Private PromisePrivate; + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + + MaybeSetChannelStartTime(aData->mTime); + + RefPtr p = new PromisePrivate(__func__); + RefPtr data = aData; + RefPtr self = this; + AwaitStartTime()->Then( + mOwnerThread, __func__, + [p, data, self] () { + MOZ_ASSERT(self->mOwnerThread->IsCurrentThreadIn()); + p->Resolve(data, __func__); + }, + [p] () { + p->Reject(MediaDecoderReader::CANCELED, __func__); + }); + + return p.forget(); + } + + template + void FirstSampleRejected(MediaDecoderReader::NotDecodedReason aReason) + { + MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); + if (aReason == MediaDecoderReader::DECODE_ERROR) { + mHaveStartTimePromise.RejectIfExists(false, __func__); + } else if (aReason == MediaDecoderReader::END_OF_STREAM) { + LOG("StartTimeRendezvous=%p SampleType(%d) Has no samples.", + this, SampleType); + MaybeSetChannelStartTime(INT64_MAX); + } + } + + bool HaveStartTime() const + { + return mAudioStartTime.isSome() && mVideoStartTime.isSome(); + } + + int64_t StartTime() const + { + int64_t time = std::min(mAudioStartTime.ref(), mVideoStartTime.ref()); + return time == INT64_MAX ? 0 : time; + } + +private: + ~StartTimeRendezvous() {} + + template + void MaybeSetChannelStartTime(int64_t aStartTime) + { + if (ChannelStartTime(SampleType).isSome()) { + // If we're initialized with aForceZeroStartTime=true, the channel start + // times are already set. + return; + } + + LOG("StartTimeRendezvous=%p Setting SampleType(%d) start time to %lld", + this, SampleType, aStartTime); + + ChannelStartTime(SampleType).emplace(aStartTime); + if (HaveStartTime()) { + mHaveStartTimePromise.ResolveIfExists(true, __func__); + } + } + + Maybe& ChannelStartTime(MediaData::Type aType) + { + return aType == MediaData::AUDIO_DATA ? mAudioStartTime : mVideoStartTime; + } + + MozPromiseHolder mHaveStartTimePromise; + RefPtr mOwnerThread; + Maybe mAudioStartTime; + Maybe mVideoStartTime; +}; + +} // namespace mozilla diff --git a/dom/media/MediaDecoderReaderWrapper.h b/dom/media/MediaDecoderReaderWrapper.h new file mode 100644 index 000000000000..73fba4755071 --- /dev/null +++ b/dom/media/MediaDecoderReaderWrapper.h @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef MediaDecoderReaderWrapper_h_ +#define MediaDecoderReaderWrapper_h_ + +#include "mozilla/AbstractThread.h" +#include "mozilla/RefPtr.h" +#include "nsISupportsImpl.h" + +#include "MediaDecoderReader.h" + +namespace mozilla { + +class StartTimeRendezvous; + +typedef MozPromise HaveStartTimePromise; + +} // namespace mozilla + +#endif // MediaDecoderReaderWrapper_h_ diff --git a/dom/media/MediaDecoderStateMachine.h b/dom/media/MediaDecoderStateMachine.h index ef8f22bb3cf0..31bffca1c145 100644 --- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -694,133 +694,6 @@ private: // Used to dispatch another round schedule with specific target time. DelayedScheduler mDelayedScheduler; - // StartTimeRendezvous is a helper class that quarantines the first sample - // until it gets a sample from both channels, such that we can be guaranteed - // to know the start time by the time On{Audio,Video}Decoded is called. - class StartTimeRendezvous { - public: - typedef MediaDecoderReader::AudioDataPromise AudioDataPromise; - typedef MediaDecoderReader::VideoDataPromise VideoDataPromise; - typedef MozPromise HaveStartTimePromise; - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StartTimeRendezvous); - StartTimeRendezvous(AbstractThread* aOwnerThread, bool aHasAudio, bool aHasVideo, - bool aForceZeroStartTime) - : mOwnerThread(aOwnerThread) - { - if (aForceZeroStartTime) { - mAudioStartTime.emplace(0); - mVideoStartTime.emplace(0); - return; - } - - if (!aHasAudio) { - mAudioStartTime.emplace(INT64_MAX); - } - - if (!aHasVideo) { - mVideoStartTime.emplace(INT64_MAX); - } - } - - void Destroy() - { - mAudioStartTime = Some(mAudioStartTime.refOr(INT64_MAX)); - mVideoStartTime = Some(mVideoStartTime.refOr(INT64_MAX)); - mHaveStartTimePromise.RejectIfExists(false, __func__); - } - - RefPtr AwaitStartTime() - { - if (HaveStartTime()) { - return HaveStartTimePromise::CreateAndResolve(true, __func__); - } - return mHaveStartTimePromise.Ensure(__func__); - } - - template - struct PromiseSampleType { - typedef typename PromiseType::ResolveValueType::element_type Type; - }; - - template - RefPtr ProcessFirstSample(typename PromiseSampleType::Type* aData) - { - typedef typename PromiseSampleType::Type DataType; - typedef typename PromiseType::Private PromisePrivate; - MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); - - MaybeSetChannelStartTime(aData->mTime); - - RefPtr p = new PromisePrivate(__func__); - RefPtr data = aData; - RefPtr self = this; - AwaitStartTime()->Then(mOwnerThread, __func__, - [p, data, self] () -> void { - MOZ_ASSERT(self->mOwnerThread->IsCurrentThreadIn()); - p->Resolve(data, __func__); - }, - [p] () -> void { p->Reject(MediaDecoderReader::CANCELED, __func__); }); - - return p.forget(); - } - - template - void FirstSampleRejected(MediaDecoderReader::NotDecodedReason aReason) - { - MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); - if (aReason == MediaDecoderReader::DECODE_ERROR) { - mHaveStartTimePromise.RejectIfExists(false, __func__); - } else if (aReason == MediaDecoderReader::END_OF_STREAM) { - MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, - ("StartTimeRendezvous=%p SampleType(%d) Has no samples.", this, SampleType)); - MaybeSetChannelStartTime(INT64_MAX); - } - } - - bool HaveStartTime() { return mAudioStartTime.isSome() && mVideoStartTime.isSome(); } - int64_t StartTime() - { - int64_t time = std::min(mAudioStartTime.ref(), mVideoStartTime.ref()); - return time == INT64_MAX ? 0 : time; - } - private: - virtual ~StartTimeRendezvous() {} - - template - void MaybeSetChannelStartTime(int64_t aStartTime) - { - if (ChannelStartTime(SampleType).isSome()) { - // If we're initialized with aForceZeroStartTime=true, the channel start - // times are already set. - return; - } - - MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, - ("StartTimeRendezvous=%p Setting SampleType(%d) start time to %lld", - this, SampleType, aStartTime)); - - ChannelStartTime(SampleType).emplace(aStartTime); - if (HaveStartTime()) { - mHaveStartTimePromise.ResolveIfExists(true, __func__); - } - } - - Maybe& ChannelStartTime(MediaData::Type aType) - { - return aType == MediaData::AUDIO_DATA ? mAudioStartTime : mVideoStartTime; - } - - MozPromiseHolder mHaveStartTimePromise; - RefPtr mOwnerThread; - Maybe mAudioStartTime; - Maybe mVideoStartTime; - }; - RefPtr mStartTimeRendezvous; - - bool HaveStartTime() { return mStartTimeRendezvous && mStartTimeRendezvous->HaveStartTime(); } - int64_t StartTime() { return mStartTimeRendezvous->StartTime(); } - // Queue of audio frames. This queue is threadsafe, and is accessed from // the audio, decoder, state machine, and main threads. MediaQueue mAudioQueue; diff --git a/dom/media/moz.build b/dom/media/moz.build index 0d6bc0aa99fd..f5a0b2fa9834 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -214,6 +214,7 @@ UNIFIED_SOURCES += [ 'MediaData.cpp', 'MediaDecoder.cpp', 'MediaDecoderReader.cpp', + 'MediaDecoderReaderWrapper.cpp', 'MediaDecoderStateMachine.cpp', 'MediaDeviceInfo.cpp', 'MediaDevices.cpp',