123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 {
- typedef MediaDecoderReader::MediaDataPromise MediaDataPromise;
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StartTimeRendezvous);
- 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<HaveStartTimePromise> AwaitStartTime()
- {
- if (HaveStartTime()) {
- return HaveStartTimePromise::CreateAndResolve(true, __func__);
- }
- return mHaveStartTimePromise.Ensure(__func__);
- }
- template<MediaData::Type SampleType>
- RefPtr<MediaDataPromise>
- ProcessFirstSample(MediaData* aData)
- {
- typedef typename MediaDataPromise::Private PromisePrivate;
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- MaybeSetChannelStartTime<SampleType>(aData->mTime);
- RefPtr<PromisePrivate> p = new PromisePrivate(__func__);
- RefPtr<MediaData> data = aData;
- RefPtr<StartTimeRendezvous> self = this;
- AwaitStartTime()->Then(
- mOwnerThread, __func__,
- [p, data, self] () {
- MOZ_ASSERT(self->mOwnerThread->IsCurrentThreadIn());
- p->Resolve(data, __func__);
- },
- [p] () {
- p->Reject(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
- });
- return p.forget();
- }
- template<MediaData::Type SampleType>
- void FirstSampleRejected(const MediaResult& aError)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- if (aError == NS_ERROR_DOM_MEDIA_END_OF_STREAM) {
- LOG("StartTimeRendezvous=%p SampleType(%d) Has no samples.",
- this, SampleType);
- MaybeSetChannelStartTime<SampleType>(INT64_MAX);
- } else if (aError != NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA) {
- mHaveStartTimePromise.RejectIfExists(false, __func__);
- }
- }
- 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<MediaData::Type SampleType>
- 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<int64_t>& ChannelStartTime(MediaData::Type aType)
- {
- return aType == MediaData::AUDIO_DATA ? mAudioStartTime : mVideoStartTime;
- }
- MozPromiseHolder<HaveStartTimePromise> mHaveStartTimePromise;
- RefPtr<AbstractThread> mOwnerThread;
- Maybe<int64_t> mAudioStartTime;
- Maybe<int64_t> mVideoStartTime;
- };
- MediaDecoderReaderWrapper::MediaDecoderReaderWrapper(AbstractThread* aOwnerThread,
- MediaDecoderReader* aReader)
- : mForceZeroStartTime(aReader->ForceZeroStartTime())
- , mOwnerThread(aOwnerThread)
- , mReader(aReader)
- {}
- MediaDecoderReaderWrapper::~MediaDecoderReaderWrapper()
- {}
- media::TimeUnit
- MediaDecoderReaderWrapper::StartTime() const
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- MOZ_ASSERT(!mShutdown);
- return media::TimeUnit::FromMicroseconds(mStartTimeRendezvous->StartTime());
- }
- RefPtr<MediaDecoderReaderWrapper::MetadataPromise>
- MediaDecoderReaderWrapper::ReadMetadata()
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- MOZ_ASSERT(!mShutdown);
- return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
- &MediaDecoderReader::AsyncReadMetadata)
- ->Then(mOwnerThread, __func__, this,
- &MediaDecoderReaderWrapper::OnMetadataRead,
- &MediaDecoderReaderWrapper::OnMetadataNotRead)
- ->CompletionPromise();
- }
- RefPtr<HaveStartTimePromise>
- MediaDecoderReaderWrapper::AwaitStartTime()
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- MOZ_ASSERT(!mShutdown);
- return mStartTimeRendezvous->AwaitStartTime();
- }
- void
- MediaDecoderReaderWrapper::RequestAudioData()
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- MOZ_ASSERT(!mShutdown);
- auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
- &MediaDecoderReader::RequestAudioData);
- if (!mStartTimeRendezvous->HaveStartTime()) {
- p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(),
- &StartTimeRendezvous::ProcessFirstSample<MediaData::AUDIO_DATA>,
- &StartTimeRendezvous::FirstSampleRejected<MediaData::AUDIO_DATA>)
- ->CompletionPromise();
- }
- RefPtr<MediaDecoderReaderWrapper> self = this;
- mAudioDataRequest.Begin(p->Then(mOwnerThread, __func__,
- [self] (MediaData* aAudioSample) {
- self->mAudioDataRequest.Complete();
- aAudioSample->AdjustForStartTime(self->StartTime().ToMicroseconds());
- self->mAudioCallback.Notify(AsVariant(aAudioSample));
- },
- [self] (const MediaResult& aError) {
- self->mAudioDataRequest.Complete();
- self->mAudioCallback.Notify(AsVariant(aError));
- }));
- }
- void
- MediaDecoderReaderWrapper::RequestVideoData(bool aSkipToNextKeyframe,
- media::TimeUnit aTimeThreshold)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- MOZ_ASSERT(!mShutdown);
- // Time the video decode and send this value back to callbacks who accept
- // a TimeStamp as its second parameter.
- TimeStamp videoDecodeStartTime = TimeStamp::Now();
- if (aTimeThreshold.ToMicroseconds() > 0 &&
- mStartTimeRendezvous->HaveStartTime()) {
- aTimeThreshold += StartTime();
- }
- auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
- &MediaDecoderReader::RequestVideoData,
- aSkipToNextKeyframe, aTimeThreshold.ToMicroseconds());
- if (!mStartTimeRendezvous->HaveStartTime()) {
- p = p->Then(mOwnerThread, __func__, mStartTimeRendezvous.get(),
- &StartTimeRendezvous::ProcessFirstSample<MediaData::VIDEO_DATA>,
- &StartTimeRendezvous::FirstSampleRejected<MediaData::VIDEO_DATA>)
- ->CompletionPromise();
- }
- RefPtr<MediaDecoderReaderWrapper> self = this;
- mVideoDataRequest.Begin(p->Then(mOwnerThread, __func__,
- [self, videoDecodeStartTime] (MediaData* aVideoSample) {
- self->mVideoDataRequest.Complete();
- aVideoSample->AdjustForStartTime(self->StartTime().ToMicroseconds());
- self->mVideoCallback.Notify(AsVariant(MakeTuple(aVideoSample, videoDecodeStartTime)));
- },
- [self] (const MediaResult& aError) {
- self->mVideoDataRequest.Complete();
- self->mVideoCallback.Notify(AsVariant(aError));
- }));
- }
- bool
- MediaDecoderReaderWrapper::IsRequestingAudioData() const
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- return mAudioDataRequest.Exists();
- }
- bool
- MediaDecoderReaderWrapper::IsRequestingVideoData() const
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- return mVideoDataRequest.Exists();
- }
- bool
- MediaDecoderReaderWrapper::IsWaitingAudioData() const
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- return mAudioWaitRequest.Exists();
- }
- bool
- MediaDecoderReaderWrapper::IsWaitingVideoData() const
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- return mVideoWaitRequest.Exists();
- }
- RefPtr<MediaDecoderReader::SeekPromise>
- MediaDecoderReaderWrapper::Seek(SeekTarget aTarget, media::TimeUnit aEndTime)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- aTarget.SetTime(aTarget.GetTime() + StartTime());
- return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
- &MediaDecoderReader::Seek, aTarget,
- aEndTime.ToMicroseconds());
- }
- void
- MediaDecoderReaderWrapper::WaitForData(MediaData::Type aType)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- auto p = InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
- &MediaDecoderReader::WaitForData, aType);
- RefPtr<MediaDecoderReaderWrapper> self = this;
- WaitRequestRef(aType).Begin(p->Then(mOwnerThread, __func__,
- [self] (MediaData::Type aType) {
- self->WaitRequestRef(aType).Complete();
- self->WaitCallbackRef(aType).Notify(AsVariant(aType));
- },
- [self, aType] (WaitForDataRejectValue aRejection) {
- self->WaitRequestRef(aType).Complete();
- self->WaitCallbackRef(aType).Notify(AsVariant(aRejection));
- }));
- }
- MediaCallbackExc<WaitCallbackData>&
- MediaDecoderReaderWrapper::WaitCallbackRef(MediaData::Type aType)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- return aType == MediaData::AUDIO_DATA ? mAudioWaitCallback : mVideoWaitCallback;
- }
- MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise>&
- MediaDecoderReaderWrapper::WaitRequestRef(MediaData::Type aType)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- return aType == MediaData::AUDIO_DATA ? mAudioWaitRequest : mVideoWaitRequest;
- }
- RefPtr<MediaDecoderReaderWrapper::BufferedUpdatePromise>
- MediaDecoderReaderWrapper::UpdateBufferedWithPromise()
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
- &MediaDecoderReader::UpdateBufferedWithPromise);
- }
- void
- MediaDecoderReaderWrapper::ReleaseResources()
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- nsCOMPtr<nsIRunnable> r =
- NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseResources);
- mReader->OwnerThread()->Dispatch(r.forget());
- }
- void
- MediaDecoderReaderWrapper::SetIdle()
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- nsCOMPtr<nsIRunnable> r =
- NewRunnableMethod(mReader, &MediaDecoderReader::SetIdle);
- mReader->OwnerThread()->Dispatch(r.forget());
- }
- void
- MediaDecoderReaderWrapper::ResetDecode(TrackSet aTracks)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- if (aTracks.contains(TrackInfo::kAudioTrack)) {
- mAudioDataRequest.DisconnectIfExists();
- mAudioWaitRequest.DisconnectIfExists();
- }
- if (aTracks.contains(TrackInfo::kVideoTrack)) {
- mVideoDataRequest.DisconnectIfExists();
- mVideoWaitRequest.DisconnectIfExists();
- }
- nsCOMPtr<nsIRunnable> r =
- NewRunnableMethod<TrackSet>(mReader,
- &MediaDecoderReader::ResetDecode,
- aTracks);
- mReader->OwnerThread()->Dispatch(r.forget());
- }
- RefPtr<ShutdownPromise>
- MediaDecoderReaderWrapper::Shutdown()
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- MOZ_ASSERT(!mAudioDataRequest.Exists());
- MOZ_ASSERT(!mVideoDataRequest.Exists());
- mShutdown = true;
- if (mStartTimeRendezvous) {
- mStartTimeRendezvous->Destroy();
- mStartTimeRendezvous = nullptr;
- }
- return InvokeAsync(mReader->OwnerThread(), mReader.get(), __func__,
- &MediaDecoderReader::Shutdown);
- }
- void
- MediaDecoderReaderWrapper::OnMetadataRead(MetadataHolder* aMetadata)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- if (mShutdown) {
- return;
- }
- // Set up the start time rendezvous if it doesn't already exist (which is
- // generally the case, unless we're coming out of dormant mode).
- if (!mStartTimeRendezvous) {
- mStartTimeRendezvous = new StartTimeRendezvous(
- mOwnerThread, aMetadata->mInfo.HasAudio(),
- aMetadata->mInfo.HasVideo(), mForceZeroStartTime);
- RefPtr<MediaDecoderReaderWrapper> self = this;
- mStartTimeRendezvous->AwaitStartTime()->Then(
- mOwnerThread, __func__,
- [self] () {
- NS_ENSURE_TRUE_VOID(!self->mShutdown);
- self->mReader->DispatchSetStartTime(self->StartTime().ToMicroseconds());
- },
- [] () {
- NS_WARNING("Setting start time on reader failed");
- });
- }
- }
- void
- MediaDecoderReaderWrapper::SetVideoBlankDecode(bool aIsBlankDecode)
- {
- MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
- nsCOMPtr<nsIRunnable> r =
- NewRunnableMethod<bool>(mReader, &MediaDecoderReader::SetVideoBlankDecode,
- aIsBlankDecode);
- mReader->OwnerThread()->Dispatch(r.forget());
- }
- } // namespace mozilla
|