Mypal/dom/media/platforms/wrappers/FuzzingWrapper.cpp

329 lines
10 KiB
C++

/* -*- 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