summaryrefslogtreecommitdiffstats
path: root/dom/media/platforms/wrappers/FuzzingWrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/platforms/wrappers/FuzzingWrapper.cpp')
-rw-r--r--dom/media/platforms/wrappers/FuzzingWrapper.cpp328
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