--- a/dom/media/Benchmark.cpp
+++ b/dom/media/Benchmark.cpp
@@ -7,246 +7,315 @@
#include "Benchmark.h"
#include "DecoderTraits.h"
#include "PDMFactory.h"
#include "WebMDemuxer.h"
#include "BufferMediaResource.h"
#include "WebMSample.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
+#include "MP4Demuxer.h"
+
+#include <iostream>
+#include <fstream>
+#include "MediaData.h"
namespace mozilla {
-const char* Benchmark::sBenchmarkFpsPref = "media.benchmark.fps";
-bool Benchmark::sHasRunTest = false;
-
-const uint32_t BenchmarkDecoder::sStartupFrames = 1;
+const char* VP9Benchmark::sBenchmarkFpsPref = "media.benchmark.vp9.fps";
+bool VP9Benchmark::sHasRunTest = false;
bool
-Benchmark::IsVP9DecodeFast()
+VP9Benchmark::IsVP9DecodeFast()
{
MOZ_ASSERT(NS_IsMainThread());
if (true || !sHasRunTest) {
sHasRunTest = true;
if (true || !Preferences::HasUserValue(sBenchmarkFpsPref)) {
- RefPtr<Benchmark> estimiser = new Benchmark();
+ RefPtr<WebMDemuxer> demuxer =
+ new WebMDemuxer(new BufferMediaResource(sWebMSample, sizeof(sWebMSample), nullptr,
+ NS_LITERAL_CSTRING("video/webm")));
+ RefPtr<Benchmark> estimiser =
+ new Benchmark(demuxer,
+ {
+ Preferences::GetInt("media.benchmark.frames", 300),
+ 1,
+ 8,
+ TimeDuration::FromMilliseconds(
+ Preferences::GetUint("media.benchmark.timeout", 1000))
+ });
+ estimiser->Run()->Then(
+ AbstractThread::MainThread(), __func__,
+ [](uint32_t aDecodeFps) {
+ Preferences::SetUint(sBenchmarkFpsPref, aDecodeFps);
+ Telemetry::Accumulate(Telemetry::ID::VIDEO_VP9_BENCHMARK_FPS, aDecodeFps);
+
+ // H264 benchmark
+ // This is how it can be used, I will remove it from final commit
+ // of course.
+ std::ifstream infile;
+ infile.open("/Users/jyavenard/Downloads/g5dv.mp4", std::ios::binary);
+ infile.seekg(0, std::ios::end);
+ size_t fileSize = infile.tellg();
+ RefPtr<MediaByteBuffer> data = new MediaByteBuffer;
+ data->SetLength(fileSize);
+ infile.seekg(0, std::ios::beg);
+ infile.read((char*)data->Elements(), fileSize);
+
+ RefPtr<MP4Demuxer> mp4demuxer =
+ new MP4Demuxer(new BufferMediaResource(data->Elements(), data->Length(), nullptr,
+ NS_LITERAL_CSTRING("video/mp4")));
+ RefPtr<Benchmark> mp4estimiser = new Benchmark(mp4demuxer);
+ mp4estimiser->Run()->Then(
+ AbstractThread::MainThread(), __func__,
+ [](uint32_t aDecodeFps) {
+ Preferences::SetUint("media.benchmark.h264.fps", aDecodeFps);
+ },
+ []() { });
+
+ },
+ []() { });
}
}
if (!Preferences::HasUserValue(sBenchmarkFpsPref)) {
return false;
}
uint32_t decodeFps = Preferences::GetUint(sBenchmarkFpsPref);
uint32_t threshold =
Preferences::GetUint("media.benchmark.vp9.threshold", 150);
return decodeFps >= threshold;
}
-Benchmark::Benchmark()
+Benchmark::Benchmark(MediaDataDemuxer* aDemuxer, const Parameters& aParameters)
: QueueObject(AbstractThread::MainThread())
+ , mParameters(aParameters)
, mKeepAliveUntilComplete(this)
- , mPlaybackState(this)
+ , mPlaybackState(this, aDemuxer)
+{
+ MOZ_COUNT_CTOR(Benchmark);
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+Benchmark::~Benchmark()
{
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_COUNT_DTOR(Benchmark);
+}
+RefPtr<Benchmark::BenchmarkPromise>
+Benchmark::Run()
+{
+ RefPtr<BenchmarkPromise> p = mPromise.Ensure(__func__);
RefPtr<Benchmark> self = this;
mPlaybackState.Dispatch(
NS_NewRunnableFunction([self]() { self->mPlaybackState.DemuxSamples(); }));
+ return p;
}
void
-Benchmark::SaveResult(uint32_t aDecodeFps)
+Benchmark::ReturnResult(uint32_t aDecodeFps)
{
MOZ_ASSERT(NS_IsMainThread());
- Preferences::SetUint(sBenchmarkFpsPref, aDecodeFps);
- Telemetry::Accumulate(Telemetry::ID::VIDEO_VP9_BENCHMARK_FPS, aDecodeFps);
-}
-
-void
-Benchmark::Drain(RefPtr<MediaDataDecoder> aDecoder)
-{
- RefPtr<Benchmark> self = this;
- mPlaybackState.Dispatch(NS_NewRunnableFunction([self, aDecoder]() {
- self->mPlaybackState.Drain(aDecoder);
- }));
+ mPromise.ResolveIfExists(aDecodeFps, __func__);
}
void
Benchmark::Dispose()
{
MOZ_ASSERT(NS_IsMainThread());
- mPlaybackState.Shutdown();
mKeepAliveUntilComplete = nullptr;
+ mPromise.RejectIfExists(false, __func__);
}
-bool
-Benchmark::IsOnPlaybackThread()
-{
- return mPlaybackState.OnThread();
-}
-
-BenchmarkPlayback::BenchmarkPlayback(Benchmark* aMainThreadState)
+BenchmarkPlayback::BenchmarkPlayback(Benchmark* aMainThreadState,
+ MediaDataDemuxer* aDemuxer)
: QueueObject(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
, mMainThreadState(aMainThreadState)
- , mDecoderState(aMainThreadState, new FlushableTaskQueue(GetMediaThreadPool(
- MediaThreadType::PLATFORM_DECODER)))
+ , mDecoderTaskQueue(new FlushableTaskQueue(GetMediaThreadPool(
+ MediaThreadType::PLATFORM_DECODER)))
+ , mDemuxer(aDemuxer)
+ , mSampleIndex(0)
+ , mFrameCount(0)
+ , mFinished(false)
{
+ MOZ_ASSERT(NS_IsMainThread());
+ PDMFactory::Init();
}
void
BenchmarkPlayback::DemuxSamples()
{
MOZ_ASSERT(OnThread());
- RefPtr<MediaDataDemuxer> demuxer = new WebMDemuxer(
- new BufferMediaResource(sWebMSample, sizeof(sWebMSample), nullptr,
- NS_LITERAL_CSTRING("video/webm")));
- RefPtr<Benchmark> ref = mMainThreadState;
- demuxer->Init()->Then(
+ RefPtr<Benchmark> ref(mMainThreadState);
+ mDemuxer->Init()->Then(
Thread(), __func__,
- [this, ref, demuxer](nsresult aResult) {
+ [this, ref](nsresult aResult) {
MOZ_ASSERT(OnThread());
- RefPtr<MediaTrackDemuxer> track =
- demuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
-
- RefPtr<MediaTrackDemuxer::SamplesPromise> promise = track->GetSamples(8);
-
- promise->Then(
- ref->Thread(), __func__,
- [this, ref, track](RefPtr<MediaTrackDemuxer::SamplesHolder> aHolder) {
- mDecoderState.Init(Move(*track->GetInfo()), aHolder->mSamples);
- },
- [ref](DemuxerFailureReason aReason) { ref->Dispose(); });
+ mTrackDemuxer =
+ mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0);
+ if (!mTrackDemuxer) {
+ MainThreadShutdown();
+ }
+ DemuxNextSample();
},
- [ref](DemuxerFailureReason aReason) { ref->Dispose(); });
-}
-
-void
-BenchmarkPlayback::Drain(RefPtr<MediaDataDecoder> aDecoder)
-{
- MOZ_ASSERT(OnThread());
-
- aDecoder->Drain();
+ [this, ref](DemuxerFailureReason aReason) { MainThreadShutdown(); });
}
void
-BenchmarkPlayback::Shutdown()
+BenchmarkPlayback::DemuxNextSample()
{
- MOZ_ASSERT(NS_IsMainThread());
-
- mDecoderState.Thread()->AsTaskQueue()->BeginShutdown();
- Thread()->AsTaskQueue()->BeginShutdown();
-}
+ MOZ_ASSERT(OnThread());
-BenchmarkDecoder::BenchmarkDecoder(Benchmark* aMainThreadState,
- RefPtr<FlushableTaskQueue> aTaskQueue)
- : QueueObject(aTaskQueue)
- , mTaskQueue(aTaskQueue)
- , mMainThreadState(aMainThreadState)
- , mSampleIndex(0)
- , mFrameCount(0)
- , mFramesToMeasure(Preferences::GetUint("media.benchmark.frames", 300))
- , mTimeout(TimeDuration::FromMilliseconds(
- Preferences::GetUint("media.benchmark.timeout", 1000)))
- , mFinished(false)
-{
- MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<Benchmark> ref(mMainThreadState);
+ RefPtr<MediaTrackDemuxer::SamplesPromise> promise = mTrackDemuxer->GetSamples();
+ promise->Then(
+ Thread(), __func__,
+ [this, ref](RefPtr<MediaTrackDemuxer::SamplesHolder> aHolder) {
+ mSamples.AppendElements(Move(aHolder->mSamples));
+ if (ref->mParameters.mStopAtFrame > 0 &&
+ mSamples.Length() == (size_t)ref->mParameters.mStopAtFrame) {
+ InitDecoder(Move(*mTrackDemuxer->GetInfo()));
+ } else {
+ Dispatch(NS_NewRunnableFunction([this, ref]() { DemuxNextSample(); }));
+ }
+ },
+ [this, ref](DemuxerFailureReason aReason) {
+ switch (aReason) {
+ case DemuxerFailureReason::END_OF_STREAM:
+ InitDecoder(Move(*mTrackDemuxer->GetInfo()));
+ break;
+ default:
+ MainThreadShutdown();
+ }
+ });
}
void
-BenchmarkDecoder::Init(TrackInfo&& aInfo,
- nsTArray<RefPtr<MediaRawData> >& aSamples)
+BenchmarkPlayback::InitDecoder(TrackInfo&& aInfo)
{
- MOZ_ASSERT(NS_IsMainThread());
-
- mSamples = aSamples;
+ MOZ_ASSERT(OnThread());
RefPtr<PDMFactory> platform = new PDMFactory();
- platform->Init();
-
- mDecoder = platform->CreateDecoder(aInfo, mTaskQueue, this);
- RefPtr<Benchmark> ref = mMainThreadState;
+ mDecoder = platform->CreateDecoder(aInfo, mDecoderTaskQueue, this);
+ if (!mDecoder) {
+ MainThreadShutdown();
+ return;
+ }
+ RefPtr<Benchmark> ref(mMainThreadState);
mDecoder->Init()->Then(
ref->Thread(), __func__,
- [this, ref, aSamples](TrackInfo::TrackType aTrackType) {
+ [this, ref](TrackInfo::TrackType aTrackType) {
Dispatch(NS_NewRunnableFunction([this, ref]() { InputExhausted(); }));
},
[this, ref](MediaDataDecoder::DecoderFailureReason aReason) {
- ref->Dispose();
+ MainThreadShutdown();
});
}
void
-BenchmarkDecoder::MainThreadShutdown()
-{
- MOZ_ASSERT(OnThread());
-
- RefPtr<Benchmark> ref = mMainThreadState;
- ref->Dispatch(NS_NewRunnableFunction([ref]() { ref->Dispose(); }));
-}
-
-void
-BenchmarkDecoder::Output(MediaData* aData)
+BenchmarkPlayback::MainThreadShutdown()
{
MOZ_ASSERT(OnThread());
- if (mFinished) {
- return;
+ if (mDecoder) {
+ mDecoder->Flush();
+ mDecoder->Shutdown();
+ mDecoder = nullptr;
}
- mFrameCount++;
- if (mFrameCount == sStartupFrames) {
- mDecodeStartTime = TimeStamp::Now();
+
+ mDecoderTaskQueue->BeginShutdown();
+ mDecoderTaskQueue->AwaitShutdownAndIdle();
+ mDecoderTaskQueue = nullptr;
+
+ if (mTrackDemuxer) {
+ mTrackDemuxer->Reset();
+ mTrackDemuxer->BreakCycles();
+ mTrackDemuxer = nullptr;
}
- uint32_t frames = mFrameCount - sStartupFrames;
- TimeDuration elapsedTime = TimeStamp::Now() - mDecodeStartTime;
- if (frames == mFramesToMeasure || elapsedTime >= mTimeout) {
- uint32_t decodeFps = frames / elapsedTime.ToSeconds();
- mFinished = true;
+
+ RefPtr<Benchmark> ref(mMainThreadState);
+ Thread()->AsTaskQueue()->BeginShutdown()->Then(
+ ref->Thread(), __func__,
+ [ref]() { ref->Dispose(); },
+ []() { MOZ_CRASH("not reached"); });
+}
- RefPtr<Benchmark> ref = mMainThreadState;
- RefPtr<MediaDataDecoder> decoder = mDecoder;
- ref->Dispatch(NS_NewRunnableFunction([ref, decoder, decodeFps]() {
- ref->Drain(decoder);
- ref->SaveResult(decodeFps);
- }));
- }
+void
+BenchmarkPlayback::Output(MediaData* aData)
+{
+ RefPtr<Benchmark> ref(mMainThreadState);
+ Dispatch(NS_NewRunnableFunction([this, ref]() {
+ mFrameCount++;
+ if (mFrameCount == ref->mParameters.mStartupFrame) {
+ mDecodeStartTime = TimeStamp::Now();
+ }
+ int32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
+ TimeDuration elapsedTime = TimeStamp::Now() - mDecodeStartTime;
+ if (!mFinished &&
+ (frames == ref->mParameters.mFramesToMeasure ||
+ elapsedTime >= ref->mParameters.mTimeout)) {
+ uint32_t decodeFps = frames / elapsedTime.ToSeconds();
+ mFinished = true;
+ MainThreadShutdown();
+ ref->Dispatch(NS_NewRunnableFunction([ref, decodeFps]() {
+ ref->ReturnResult(decodeFps);
+ }));
+ }
+ }));
}
void
-BenchmarkDecoder::Error()
+BenchmarkPlayback::Error()
{
- MOZ_ASSERT(OnThread());
-
- MainThreadShutdown();
+ RefPtr<Benchmark> ref(mMainThreadState);
+ Dispatch(NS_NewRunnableFunction([this, ref]() { MainThreadShutdown(); }));
}
void
-BenchmarkDecoder::InputExhausted()
+BenchmarkPlayback::InputExhausted()
{
- MOZ_ASSERT(OnThread());
-
- mDecoder->Input(mSamples[mSampleIndex]);
- mSampleIndex++;
- if (mSampleIndex == mSamples.Length()) {
- mSampleIndex = 0;
- }
+ RefPtr<Benchmark> ref(mMainThreadState);
+ Dispatch(NS_NewRunnableFunction([this, ref]() {
+ MOZ_ASSERT(OnThread());
+ if (mFinished || mSampleIndex >= mSamples.Length()) {
+ return;
+ }
+ mDecoder->Input(mSamples[mSampleIndex]);
+ mSampleIndex++;
+ if (ref->mParameters.mStopAtFrame > 0 &&
+ mSampleIndex == (size_t)ref->mParameters.mStopAtFrame) {
+ mSampleIndex = 0;
+ } else if (mSampleIndex == mSamples.Length()) {
+ mDecoder->Drain();
+ }
+ }));
}
void
-BenchmarkDecoder::DrainComplete()
+BenchmarkPlayback::DrainComplete()
{
- MOZ_ASSERT(OnThread());
-
- MainThreadShutdown();
+ RefPtr<Benchmark> ref(mMainThreadState);
+ Dispatch(NS_NewRunnableFunction([this, ref]() {
+ int32_t frames = mFrameCount - ref->mParameters.mStartupFrame;
+ TimeDuration elapsedTime = TimeStamp::Now() - mDecodeStartTime;
+ uint32_t decodeFps = frames / elapsedTime.ToSeconds();
+ mFinished = true;
+ MainThreadShutdown();
+ ref->Dispatch(NS_NewRunnableFunction([ref, decodeFps]() {
+ ref->ReturnResult(decodeFps);
+ }));
+ }));
}
bool
-BenchmarkDecoder::OnReaderTaskQueue()
+BenchmarkPlayback::OnReaderTaskQueue()
{
- return mMainThreadState->IsOnPlaybackThread();
+ return OnThread();
}
+
}
--- a/dom/media/Benchmark.h
+++ b/dom/media/Benchmark.h
@@ -2,83 +2,109 @@
/* 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/. */
#ifndef MOZILLA_BENCHMARK_H
#define MOZILLA_BENCHMARK_H
+#include "MediaDataDemuxer.h"
+#include "QueueObject.h"
+#include "PlatformDecoderModule.h"
#include "mozilla/RefPtr.h"
+#include "mozilla/TaskQueue.h"
#include "mozilla/TimeStamp.h"
-#include "QueueObject.h"
+#include "nsCOMPtr.h"
namespace mozilla {
class FlushableTaskQueue;
class Benchmark;
-class BenchmarkPlayback;
-class BenchmarkDecoder : public QueueObject, private MediaDataDecoderCallback
+class BenchmarkPlayback : public QueueObject, private MediaDataDecoderCallback
{
public:
- BenchmarkDecoder(Benchmark* aMainThreadState,
- RefPtr<FlushableTaskQueue> aTaskQueue);
- void Init(TrackInfo&& aInfo, nsTArray<RefPtr<MediaRawData>>& aSamples);
+ explicit BenchmarkPlayback(Benchmark* aMainThreadState, MediaDataDemuxer* aDemuxer);
+ void DemuxSamples();
+ void DemuxNextSample();
+ void MainThreadShutdown();
+ void InitDecoder(TrackInfo&& aInfo);
- void MainThreadShutdown();
- MediaRawData* PopNextSample();
-
+ // MediaDataDecoderCallback
+ // Those methods are called on the MediaDataDecoder's task queue.
void Output(MediaData* aData) override;
void Error() override;
void InputExhausted() override;
void DrainComplete() override;
bool OnReaderTaskQueue() override;
- RefPtr<FlushableTaskQueue> mTaskQueue;
- Benchmark* mMainThreadState;
+private:
+ Atomic<Benchmark*> mMainThreadState;
+
+ RefPtr<FlushableTaskQueue> mDecoderTaskQueue;
RefPtr<MediaDataDecoder> mDecoder;
+
+ RefPtr<TaskQueue> mTaskQueue;
+ // Object only accessed on mTaskQueue
+ RefPtr<MediaDataDemuxer> mDemuxer;
+ RefPtr<MediaTrackDemuxer> mTrackDemuxer;
nsTArray<RefPtr<MediaRawData>> mSamples;
size_t mSampleIndex;
TimeStamp mDecodeStartTime;
uint32_t mFrameCount;
- const uint32_t mFramesToMeasure;
- const TimeDuration mTimeout;
- static const uint32_t sStartupFrames;
bool mFinished;
};
-class BenchmarkPlayback : public QueueObject
-{
-public:
- explicit BenchmarkPlayback(Benchmark* aMainThreadState);
- void DemuxSamples();
- void Drain(RefPtr<MediaDataDecoder> aDecoder);
- void Shutdown();
-
-private:
- Benchmark* mMainThreadState;
- BenchmarkDecoder mDecoderState;
-};
-
class Benchmark : public QueueObject
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Benchmark)
- static bool IsVP9DecodeFast();
+ struct Parameters
+ {
+ Parameters()
+ : mFramesToMeasure(-1)
+ , mStartupFrame(1)
+ , mStopAtFrame(-1)
+ , mTimeout(TimeDuration::Forever()) {}
- void SaveResult(uint32_t aDecodeFps);
- void Drain(RefPtr<MediaDataDecoder> aDecoder);
+ Parameters(int32_t aFramesToMeasure,
+ uint32_t aStartupFrame,
+ int32_t aStopAtFrame,
+ const TimeDuration& aTimeout)
+ : mFramesToMeasure(aFramesToMeasure)
+ , mStartupFrame(aStartupFrame)
+ , mStopAtFrame(aStopAtFrame)
+ , mTimeout(aTimeout) {}
+
+ const int32_t mFramesToMeasure;
+ const uint32_t mStartupFrame;
+ const int32_t mStopAtFrame;
+ const TimeDuration mTimeout;
+ };
+
+ typedef MozPromise<uint32_t, bool, /* IsExclusive = */ true> BenchmarkPromise;
+
+ Benchmark(MediaDataDemuxer* aDemuxer, const Parameters& aParameters = Parameters());
+ RefPtr<BenchmarkPromise> Run();
+ void ReturnResult(uint32_t aDecodeFps);
void Dispose();
- bool IsOnPlaybackThread();
+
+ const Parameters mParameters;
private:
- Benchmark();
- virtual ~Benchmark() {}
+ virtual ~Benchmark();
RefPtr<Benchmark> mKeepAliveUntilComplete;
BenchmarkPlayback mPlaybackState;
+ MozPromiseHolder<BenchmarkPromise> mPromise;
+};
+
+class VP9Benchmark
+{
+public:
+ static bool IsVP9DecodeFast();
static const char* sBenchmarkFpsPref;
static bool sHasRunTest;
};
}
#endif