1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "BufferMediaResource.h"
10 #include "MediaData.h"
11 #include "MediaPrefs.h"
12 #include "PDMFactory.h"
13 #include "VideoUtils.h"
14 #include "WebMDemuxer.h"
16 #include "mozilla/AbstractThread.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/SharedThreadPool.h"
19 #include "mozilla/SystemGroup.h"
20 #include "mozilla/TaskQueue.h"
21 #include "mozilla/Telemetry.h"
22 #include "mozilla/dom/ContentChild.h"
23 #include "mozilla/gfx/gfxVars.h"
25 #ifndef MOZ_WIDGET_ANDROID
26 #include "WebMSample.h"
29 using namespace mozilla::gfx
;
33 // Update this version number to force re-running the benchmark. Such as when
34 // an improvement to FFVP9 or LIBVPX is deemed worthwhile.
35 const uint32_t VP9Benchmark::sBenchmarkVersionID
= 3;
37 const char* VP9Benchmark::sBenchmarkFpsPref
= "media.benchmark.vp9.fps";
38 const char* VP9Benchmark::sBenchmarkFpsVersionCheck
= "media.benchmark.vp9.versioncheck";
39 bool VP9Benchmark::sHasRunTest
= false;
43 VP9Benchmark::IsVP9DecodeFast()
45 MOZ_ASSERT(NS_IsMainThread());
47 // Disable VP9 estimizer on Mac, see bug 1400787.
48 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_APPLEMEDIA)
51 bool hasPref
= Preferences::HasUserValue(sBenchmarkFpsPref
);
52 uint32_t hadRecentUpdate
= Preferences::GetUint(sBenchmarkFpsVersionCheck
, 0U);
54 if (!sHasRunTest
&& (!hasPref
|| hadRecentUpdate
!= sBenchmarkVersionID
)) {
57 RefPtr
<WebMDemuxer
> demuxer
= new WebMDemuxer(
58 new BufferMediaResource(sWebMSample
, sizeof(sWebMSample
)));
59 RefPtr
<Benchmark
> estimiser
=
60 new Benchmark(demuxer
,
62 Preferences::GetInt("media.benchmark.frames", 300), // frames to measure
63 1, // start benchmarking after decoding this frame.
64 8, // loop after decoding that many frames.
65 TimeDuration::FromMilliseconds(
66 Preferences::GetUint("media.benchmark.timeout", 1000))
68 estimiser
->Run()->Then(
69 SystemGroup::AbstractMainThreadFor(TaskCategory::Other
), __func__
,
70 [](uint32_t aDecodeFps
) {
71 if (XRE_IsContentProcess()) {
72 dom::ContentChild
* contentChild
= dom::ContentChild::GetSingleton();
74 contentChild
->SendNotifyBenchmarkResult(NS_LITERAL_STRING("VP9"),
78 Preferences::SetUint(sBenchmarkFpsPref
, aDecodeFps
);
79 Preferences::SetUint(sBenchmarkFpsVersionCheck
, sBenchmarkVersionID
);
81 Telemetry::Accumulate(Telemetry::HistogramID::VIDEO_VP9_BENCHMARK_FPS
, aDecodeFps
);
90 uint32_t decodeFps
= Preferences::GetUint(sBenchmarkFpsPref
);
92 Preferences::GetUint("media.benchmark.vp9.threshold", 150);
94 return decodeFps
>= threshold
;
98 Benchmark::Benchmark(MediaDataDemuxer
* aDemuxer
, const Parameters
& aParameters
)
99 : QueueObject(AbstractThread::MainThread())
100 , mParameters(aParameters
)
101 , mKeepAliveUntilComplete(this)
102 , mPlaybackState(this, aDemuxer
)
104 MOZ_COUNT_CTOR(Benchmark
);
105 MOZ_ASSERT(Thread(), "Must be run in task queue");
108 Benchmark::~Benchmark()
110 MOZ_COUNT_DTOR(Benchmark
);
113 RefPtr
<Benchmark::BenchmarkPromise
>
116 MOZ_ASSERT(OnThread());
118 RefPtr
<BenchmarkPromise
> p
= mPromise
.Ensure(__func__
);
119 RefPtr
<Benchmark
> self
= this;
120 mPlaybackState
.Dispatch(NS_NewRunnableFunction(
121 "Benchmark::Run", [self
]() { self
->mPlaybackState
.DemuxSamples(); }));
126 Benchmark::ReturnResult(uint32_t aDecodeFps
)
128 MOZ_ASSERT(OnThread());
130 mPromise
.ResolveIfExists(aDecodeFps
, __func__
);
134 Benchmark::ReturnError(const MediaResult
& aError
)
136 MOZ_ASSERT(OnThread());
138 mPromise
.RejectIfExists(aError
, __func__
);
144 MOZ_ASSERT(OnThread());
146 mKeepAliveUntilComplete
= nullptr;
152 MOZ_ASSERT(NS_IsMainThread());
153 gfxVars::Initialize();
154 gfxPrefs::GetSingleton();
155 MediaPrefs::GetSingleton();
158 BenchmarkPlayback::BenchmarkPlayback(Benchmark
* aMainThreadState
,
159 MediaDataDemuxer
* aDemuxer
)
160 : QueueObject(new TaskQueue(
161 GetMediaThreadPool(MediaThreadType::PLAYBACK
),
162 "BenchmarkPlayback::QueueObject"))
163 , mMainThreadState(aMainThreadState
)
164 , mDecoderTaskQueue(new TaskQueue(
165 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER
),
166 "BenchmarkPlayback::mDecoderTaskQueue"))
173 MOZ_ASSERT(static_cast<Benchmark
*>(mMainThreadState
)->OnThread());
177 BenchmarkPlayback::DemuxSamples()
179 MOZ_ASSERT(OnThread());
181 RefPtr
<Benchmark
> ref(mMainThreadState
);
182 mDemuxer
->Init()->Then(
184 [this, ref
](nsresult aResult
) {
185 MOZ_ASSERT(OnThread());
186 if (mDemuxer
->GetNumberTracks(TrackInfo::kVideoTrack
)) {
188 mDemuxer
->GetTrackDemuxer(TrackInfo::kVideoTrack
, 0);
189 } else if (mDemuxer
->GetNumberTracks(TrackInfo::kAudioTrack
)) {
191 mDemuxer
->GetTrackDemuxer(TrackInfo::kAudioTrack
, 0);
193 if (!mTrackDemuxer
) {
194 Error(MediaResult(NS_ERROR_FAILURE
, "Can't create track demuxer"));
199 [this, ref
](const MediaResult
& aError
) { Error(aError
); });
203 BenchmarkPlayback::DemuxNextSample()
205 MOZ_ASSERT(OnThread());
207 RefPtr
<Benchmark
> ref(mMainThreadState
);
208 RefPtr
<MediaTrackDemuxer::SamplesPromise
> promise
= mTrackDemuxer
->GetSamples();
211 [this, ref
](RefPtr
<MediaTrackDemuxer::SamplesHolder
> aHolder
) {
212 mSamples
.AppendElements(Move(aHolder
->mSamples
));
213 if (ref
->mParameters
.mStopAtFrame
&&
214 mSamples
.Length() == (size_t)ref
->mParameters
.mStopAtFrame
.ref()) {
215 InitDecoder(Move(*mTrackDemuxer
->GetInfo()));
217 Dispatch(NS_NewRunnableFunction("BenchmarkPlayback::DemuxNextSample",
218 [this, ref
]() { DemuxNextSample(); }));
221 [this, ref
](const MediaResult
& aError
) {
222 switch (aError
.Code()) {
223 case NS_ERROR_DOM_MEDIA_END_OF_STREAM
:
224 InitDecoder(Move(*mTrackDemuxer
->GetInfo()));
234 BenchmarkPlayback::InitDecoder(TrackInfo
&& aInfo
)
236 MOZ_ASSERT(OnThread());
238 RefPtr
<PDMFactory
> platform
= new PDMFactory();
239 mDecoder
= platform
->CreateDecoder({ aInfo
, mDecoderTaskQueue
});
241 Error(MediaResult(NS_ERROR_FAILURE
, "Failed to create decoder"));
244 RefPtr
<Benchmark
> ref(mMainThreadState
);
245 mDecoder
->Init()->Then(
247 [this, ref
](TrackInfo::TrackType aTrackType
) { InputExhausted(); },
248 [this, ref
](const MediaResult
& aError
) { Error(aError
); });
252 BenchmarkPlayback::FinalizeShutdown()
254 MOZ_ASSERT(OnThread());
256 MOZ_ASSERT(!mDecoder
, "mDecoder must have been shutdown already");
257 mDecoderTaskQueue
->BeginShutdown();
258 mDecoderTaskQueue
->AwaitShutdownAndIdle();
259 mDecoderTaskQueue
= nullptr;
262 mTrackDemuxer
->Reset();
263 mTrackDemuxer
->BreakCycles();
264 mTrackDemuxer
= nullptr;
268 RefPtr
<Benchmark
> ref(mMainThreadState
);
269 Thread()->AsTaskQueue()->BeginShutdown()->Then(
270 ref
->Thread(), __func__
,
271 [ref
]() { ref
->Dispose(); },
272 []() { MOZ_CRASH("not reached"); });
276 BenchmarkPlayback::MainThreadShutdown()
278 MOZ_ASSERT(OnThread());
280 MOZ_ASSERT(!mFinished
, "We've already shutdown");
285 RefPtr
<Benchmark
> ref(mMainThreadState
);
286 mDecoder
->Flush()->Then(
289 mDecoder
->Shutdown()->Then(
294 []() { MOZ_CRASH("not reached"); });
297 []() { MOZ_CRASH("not reached"); });
304 BenchmarkPlayback::Output(const MediaDataDecoder::DecodedData
& aResults
)
306 MOZ_ASSERT(OnThread());
307 MOZ_ASSERT(!mFinished
);
309 RefPtr
<Benchmark
> ref(mMainThreadState
);
310 mFrameCount
+= aResults
.Length();
311 if (!mDecodeStartTime
&& mFrameCount
>= ref
->mParameters
.mStartupFrame
) {
312 mDecodeStartTime
= Some(TimeStamp::Now());
314 TimeStamp now
= TimeStamp::Now();
315 int32_t frames
= mFrameCount
- ref
->mParameters
.mStartupFrame
;
316 TimeDuration elapsedTime
= now
- mDecodeStartTime
.refOr(now
);
317 if (((frames
== ref
->mParameters
.mFramesToMeasure
) && frames
> 0) ||
318 elapsedTime
>= ref
->mParameters
.mTimeout
|| mDrained
) {
319 uint32_t decodeFps
= frames
/ elapsedTime
.ToSeconds();
320 MainThreadShutdown();
322 NS_NewRunnableFunction("BenchmarkPlayback::Output", [ref
, decodeFps
]() {
323 ref
->ReturnResult(decodeFps
);
329 BenchmarkPlayback::Error(const MediaResult
& aError
)
331 MOZ_ASSERT(OnThread());
333 RefPtr
<Benchmark
> ref(mMainThreadState
);
334 MainThreadShutdown();
335 ref
->Dispatch(NS_NewRunnableFunction(
336 "BenchmarkPlayback::Error",
337 [ref
, aError
]() { ref
->ReturnError(aError
); }));
341 BenchmarkPlayback::InputExhausted()
343 MOZ_ASSERT(OnThread());
344 MOZ_ASSERT(!mFinished
);
346 if (mSampleIndex
>= mSamples
.Length()) {
347 Error(MediaResult(NS_ERROR_FAILURE
, "Nothing left to decode"));
351 RefPtr
<MediaRawData
> sample
= mSamples
[mSampleIndex
];
352 RefPtr
<Benchmark
> ref(mMainThreadState
);
353 RefPtr
<MediaDataDecoder::DecodePromise
> p
= mDecoder
->Decode(sample
);
356 if (mSampleIndex
== mSamples
.Length() && !ref
->mParameters
.mStopAtFrame
) {
357 // Complete current frame decode then drain if still necessary.
358 p
->Then(Thread(), __func__
,
359 [ref
, this](const MediaDataDecoder::DecodedData
& aResults
) {
362 mDecoder
->Drain()->Then(
364 [ref
, this](const MediaDataDecoder::DecodedData
& aResults
) {
367 MOZ_ASSERT(mFinished
, "We must be done now");
369 [ref
, this](const MediaResult
& aError
) { Error(aError
); });
372 [ref
, this](const MediaResult
& aError
) { Error(aError
); });
374 if (mSampleIndex
== mSamples
.Length() && ref
->mParameters
.mStopAtFrame
) {
378 p
->Then(Thread(), __func__
,
379 [ref
, this](const MediaDataDecoder::DecodedData
& aResults
) {
385 [ref
, this](const MediaResult
& aError
) { Error(aError
); });
389 } // namespace mozilla