diff options
Diffstat (limited to 'dom/media/platforms/wrappers/FuzzingWrapper.cpp')
-rw-r--r-- | dom/media/platforms/wrappers/FuzzingWrapper.cpp | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/dom/media/platforms/wrappers/FuzzingWrapper.cpp b/dom/media/platforms/wrappers/FuzzingWrapper.cpp new file mode 100644 index 000000000..7df020f46 --- /dev/null +++ b/dom/media/platforms/wrappers/FuzzingWrapper.cpp @@ -0,0 +1,328 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "FuzzingWrapper.h" + +mozilla::LogModule* GetFuzzingWrapperLog() { + static mozilla::LazyLogModule log("MediaFuzzingWrapper"); + return log; +} +#define DFW_LOGD(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Debug, ("DecoderFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) +#define DFW_LOGV(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Verbose, ("DecoderFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) +#define CFW_LOGD(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Debug, ("DecoderCallbackFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) +#define CFW_LOGV(arg, ...) MOZ_LOG(GetFuzzingWrapperLog(), mozilla::LogLevel::Verbose, ("DecoderCallbackFuzzingWrapper(%p)::%s: " arg, this, __func__, ##__VA_ARGS__)) + +namespace mozilla { + +DecoderFuzzingWrapper::DecoderFuzzingWrapper( + already_AddRefed<MediaDataDecoder> aDecoder, + already_AddRefed<DecoderCallbackFuzzingWrapper> aCallbackWrapper) + : mDecoder(aDecoder) + , mCallbackWrapper(aCallbackWrapper) +{ + DFW_LOGV("aDecoder=%p aCallbackWrapper=%p", mDecoder.get(), mCallbackWrapper.get()); +} + +DecoderFuzzingWrapper::~DecoderFuzzingWrapper() +{ + DFW_LOGV(""); +} + +RefPtr<MediaDataDecoder::InitPromise> +DecoderFuzzingWrapper::Init() +{ + DFW_LOGV(""); + MOZ_ASSERT(mDecoder); + return mDecoder->Init(); +} + +void +DecoderFuzzingWrapper::Input(MediaRawData* aData) +{ + DFW_LOGV("aData.mTime=%lld", aData->mTime); + MOZ_ASSERT(mDecoder); + mDecoder->Input(aData); +} + +void +DecoderFuzzingWrapper::Flush() +{ + DFW_LOGV("Calling mDecoder[%p]->Flush()", mDecoder.get()); + MOZ_ASSERT(mDecoder); + // Flush may output some frames (though unlikely). + // Flush may block a bit, it's ok if we output some frames in the meantime. + mDecoder->Flush(); + DFW_LOGV("mDecoder[%p]->Flush()", mDecoder.get()); + // Clear any delayed output we may have. + mCallbackWrapper->ClearDelayedOutput(); +} + +void +DecoderFuzzingWrapper::Drain() +{ + DFW_LOGV(""); + MOZ_ASSERT(mDecoder); + // Note: The decoder should callback DrainComplete(), we'll drain the + // delayed output (if any) then. + mDecoder->Drain(); +} + +void +DecoderFuzzingWrapper::Shutdown() +{ + DFW_LOGV(""); + MOZ_ASSERT(mDecoder); + // Both shutdowns below may block a bit. + mDecoder->Shutdown(); + mCallbackWrapper->Shutdown(); +} + +bool +DecoderFuzzingWrapper::IsHardwareAccelerated(nsACString& aFailureReason) const +{ + DFW_LOGV(""); + MOZ_ASSERT(mDecoder); + return mDecoder->IsHardwareAccelerated(aFailureReason); +} + +DecoderCallbackFuzzingWrapper::DecoderCallbackFuzzingWrapper(MediaDataDecoderCallback* aCallback) + : mCallback(aCallback) + , mDontDelayInputExhausted(false) + , mDraining(false) + , mTaskQueue(new TaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaFuzzingWrapper"), 1))) +{ + CFW_LOGV("aCallback=%p", aCallback); +} + +DecoderCallbackFuzzingWrapper::~DecoderCallbackFuzzingWrapper() +{ + CFW_LOGV(""); +} + +void +DecoderCallbackFuzzingWrapper::SetVideoOutputMinimumInterval( + TimeDuration aFrameOutputMinimumInterval) +{ + CFW_LOGD("aFrameOutputMinimumInterval=%fms", + aFrameOutputMinimumInterval.ToMilliseconds()); + mFrameOutputMinimumInterval = aFrameOutputMinimumInterval; +} + +void +DecoderCallbackFuzzingWrapper::SetDontDelayInputExhausted( + bool aDontDelayInputExhausted) +{ + CFW_LOGD("aDontDelayInputExhausted=%d", + aDontDelayInputExhausted); + mDontDelayInputExhausted = aDontDelayInputExhausted; +} + +void +DecoderCallbackFuzzingWrapper::Output(MediaData* aData) +{ + if (!mTaskQueue->IsCurrentThreadIn()) { + nsCOMPtr<nsIRunnable> task = + NewRunnableMethod<StorensRefPtrPassByPtr<MediaData>>( + this, &DecoderCallbackFuzzingWrapper::Output, aData); + mTaskQueue->Dispatch(task.forget()); + return; + } + CFW_LOGV("aData.mTime=%lld", aData->mTime); + MOZ_ASSERT(mCallback); + if (mFrameOutputMinimumInterval) { + if (!mPreviousOutput.IsNull()) { + if (!mDelayedOutput.empty()) { + // We already have some delayed frames, just add this one to the queue. + mDelayedOutput.push_back(MakePair<RefPtr<MediaData>, bool>(aData, false)); + CFW_LOGD("delaying output of sample@%lld, total queued:%d", + aData->mTime, int(mDelayedOutput.size())); + return; + } + if (TimeStamp::Now() < mPreviousOutput + mFrameOutputMinimumInterval) { + // Frame arriving too soon after the previous one, start queuing. + mDelayedOutput.push_back(MakePair<RefPtr<MediaData>, bool>(aData, false)); + CFW_LOGD("delaying output of sample@%lld, first queued", aData->mTime); + if (!mDelayedOutputTimer) { + mDelayedOutputTimer = new MediaTimer(); + } + ScheduleOutputDelayedFrame(); + return; + } + } + // If we're here, we're going to actually output a frame -> Record time. + mPreviousOutput = TimeStamp::Now(); + } + + // Passing the data straight through, no need to dispatch to another queue, + // callback should deal with that. + mCallback->Output(aData); +} + +void +DecoderCallbackFuzzingWrapper::Error(const MediaResult& aError) +{ + if (!mTaskQueue->IsCurrentThreadIn()) { + mTaskQueue->Dispatch(NewRunnableMethod<MediaResult>( + this, &DecoderCallbackFuzzingWrapper::Error, aError)); + return; + } + CFW_LOGV(""); + MOZ_ASSERT(mCallback); + ClearDelayedOutput(); + mCallback->Error(aError); +} + +void +DecoderCallbackFuzzingWrapper::InputExhausted() +{ + if (!mTaskQueue->IsCurrentThreadIn()) { + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::InputExhausted)); + return; + } + if (!mDontDelayInputExhausted && !mDelayedOutput.empty()) { + MediaDataAndInputExhausted& last = mDelayedOutput.back(); + CFW_LOGD("InputExhausted delayed until after output of sample@%lld", + last.first()->mTime); + last.second() = true; + return; + } + CFW_LOGV(""); + MOZ_ASSERT(mCallback); + mCallback->InputExhausted(); +} + +void +DecoderCallbackFuzzingWrapper::DrainComplete() +{ + if (!mTaskQueue->IsCurrentThreadIn()) { + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::DrainComplete)); + return; + } + MOZ_ASSERT(mCallback); + if (mDelayedOutput.empty()) { + // No queued output -> Draining is complete now. + CFW_LOGV("No delayed output -> DrainComplete now"); + mCallback->DrainComplete(); + } else { + // Queued output waiting -> Make sure we call DrainComplete when it's empty. + CFW_LOGD("Delayed output -> DrainComplete later"); + mDraining = true; + } +} + +void +DecoderCallbackFuzzingWrapper::ReleaseMediaResources() +{ + if (!mTaskQueue->IsCurrentThreadIn()) { + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ReleaseMediaResources)); + return; + } + CFW_LOGV(""); + MOZ_ASSERT(mCallback); + mCallback->ReleaseMediaResources(); +} + +bool +DecoderCallbackFuzzingWrapper::OnReaderTaskQueue() +{ + CFW_LOGV(""); + MOZ_ASSERT(mCallback); + return mCallback->OnReaderTaskQueue(); +} + +void +DecoderCallbackFuzzingWrapper::ScheduleOutputDelayedFrame() +{ + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + if (mDelayedOutputRequest.Exists()) { + // A delayed output is already scheduled, no need for more than one timer. + return; + } + RefPtr<DecoderCallbackFuzzingWrapper> self = this; + mDelayedOutputRequest.Begin( + mDelayedOutputTimer->WaitUntil( + mPreviousOutput + mFrameOutputMinimumInterval, + __func__) + ->Then(mTaskQueue, __func__, + [self] () -> void { + if (self->mDelayedOutputRequest.Exists()) { + self->mDelayedOutputRequest.Complete(); + self->OutputDelayedFrame(); + } + }, + [self] () -> void { + if (self->mDelayedOutputRequest.Exists()) { + self->mDelayedOutputRequest.Complete(); + self->ClearDelayedOutput(); + } + })); +} + +void +DecoderCallbackFuzzingWrapper::OutputDelayedFrame() +{ + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + if (mDelayedOutput.empty()) { + if (mDraining) { + // No more output, and we were draining -> Send DrainComplete. + mDraining = false; + mCallback->DrainComplete(); + } + return; + } + MediaDataAndInputExhausted& data = mDelayedOutput.front(); + CFW_LOGD("Outputting delayed sample@%lld, remaining:%d", + data.first()->mTime, int(mDelayedOutput.size() - 1)); + mPreviousOutput = TimeStamp::Now(); + mCallback->Output(data.first()); + if (data.second()) { + CFW_LOGD("InputExhausted after delayed sample@%lld", data.first()->mTime); + mCallback->InputExhausted(); + } + mDelayedOutput.pop_front(); + if (!mDelayedOutput.empty()) { + // More output -> Send it later. + ScheduleOutputDelayedFrame(); + } else if (mDraining) { + // No more output, and we were draining -> Send DrainComplete. + CFW_LOGD("DrainComplete"); + mDraining = false; + mCallback->DrainComplete(); + } +} + +void +DecoderCallbackFuzzingWrapper::ClearDelayedOutput() +{ + if (!mTaskQueue->IsCurrentThreadIn()) { + DFW_LOGV("(dispatching self)"); + mTaskQueue->Dispatch(NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ClearDelayedOutput)); + return; + } + DFW_LOGV(""); + // In case a timer hasn't lapsed yet, before destroying the timer and its + // attached waitUntil() promise, the 'Then' request must be disconnected. + mDelayedOutputRequest.DisconnectIfExists(); + mDelayedOutputTimer = nullptr; + mDelayedOutput.clear(); +} + +void +DecoderCallbackFuzzingWrapper::Shutdown() +{ + CFW_LOGV("Clear delayed output (if any) before shutting down mTaskQueue"); + ClearDelayedOutput(); + // Await idle here, so that 'ClearDelayedOutput' runs to completion before + // the task queue is shutdown (and tasks can't be queued anymore). + mTaskQueue->AwaitIdle(); + + CFW_LOGV("Shutting down mTaskQueue"); + mTaskQueue->BeginShutdown(); + mTaskQueue->AwaitIdle(); + CFW_LOGV("mTaskQueue shut down"); +} + +} // namespace mozilla |