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 "MediaDataDecoderProxy.h"
12 #include "PDMFactory.h"
13 #include "VideoUtils.h"
14 #include "WebMDemuxer.h"
15 #include "mozilla/AbstractThread.h"
16 #include "mozilla/Components.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/SharedThreadPool.h"
19 #include "mozilla/StaticMutex.h"
20 #include "mozilla/StaticPrefs_media.h"
21 #include "mozilla/TaskQueue.h"
22 #include "mozilla/Telemetry.h"
23 #include "mozilla/dom/ContentChild.h"
24 #include "mozilla/gfx/gfxVars.h"
25 #include "nsGkAtoms.h"
26 #include "nsIGfxInfo.h"
28 #ifndef MOZ_WIDGET_ANDROID
29 # include "WebMSample.h"
32 using namespace mozilla::gfx
;
36 // Update this version number to force re-running the benchmark. Such as when
37 // an improvement to FFVP9 or LIBVPX is deemed worthwhile.
38 const uint32_t VP9Benchmark::sBenchmarkVersionID
= 5;
40 const char* VP9Benchmark::sBenchmarkFpsPref
= "media.benchmark.vp9.fps";
41 const char* VP9Benchmark::sBenchmarkFpsVersionCheck
=
42 "media.benchmark.vp9.versioncheck";
43 bool VP9Benchmark::sHasRunTest
= false;
46 bool VP9Benchmark::ShouldRun() {
47 #if defined(MOZ_WIDGET_ANDROID)
48 // Assume that the VP9 software decoder will always be too slow.
51 # if defined(MOZ_APPLEMEDIA)
52 const nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
53 nsString vendorID
, deviceID
;
54 gfxInfo
->GetAdapterVendorID(vendorID
);
55 // We won't run the VP9 benchmark on mac using an Intel GPU as performance are
56 // poor, see bug 1404042.
57 if (vendorID
.EqualsLiteral("0x8086")) {
67 uint32_t VP9Benchmark::MediaBenchmarkVp9Fps() {
71 return StaticPrefs::media_benchmark_vp9_fps();
75 bool VP9Benchmark::IsVP9DecodeFast(bool aDefault
) {
76 #if defined(MOZ_WIDGET_ANDROID)
82 static StaticMutex sMutex MOZ_UNANNOTATED
;
83 uint32_t decodeFps
= StaticPrefs::media_benchmark_vp9_fps();
84 uint32_t hadRecentUpdate
= StaticPrefs::media_benchmark_vp9_versioncheck();
87 StaticMutexAutoLock
lock(sMutex
);
88 needBenchmark
= !sHasRunTest
&&
89 (decodeFps
== 0 || hadRecentUpdate
!= sBenchmarkVersionID
);
94 RefPtr
<WebMDemuxer
> demuxer
= new WebMDemuxer(
95 new BufferMediaResource(sWebMSample
, sizeof(sWebMSample
)));
96 RefPtr
<Benchmark
> estimiser
= new Benchmark(
98 {StaticPrefs::media_benchmark_frames(), // frames to measure
99 1, // start benchmarking after decoding this frame.
100 8, // loop after decoding that many frames.
101 TimeDuration::FromMilliseconds(
102 StaticPrefs::media_benchmark_timeout())});
103 estimiser
->Run()->Then(
104 AbstractThread::MainThread(), __func__
,
105 [](uint32_t aDecodeFps
) {
106 if (XRE_IsContentProcess()) {
107 dom::ContentChild
* contentChild
= dom::ContentChild::GetSingleton();
109 contentChild
->SendNotifyBenchmarkResult(u
"VP9"_ns
, aDecodeFps
);
112 Preferences::SetUint(sBenchmarkFpsPref
, aDecodeFps
);
113 Preferences::SetUint(sBenchmarkFpsVersionCheck
,
114 sBenchmarkVersionID
);
120 if (decodeFps
== 0) {
124 return decodeFps
>= StaticPrefs::media_benchmark_vp9_threshold();
128 Benchmark::Benchmark(MediaDataDemuxer
* aDemuxer
, const Parameters
& aParameters
)
130 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
131 "Benchmark::QueueObject")),
132 mParameters(aParameters
),
133 mKeepAliveUntilComplete(this),
134 mPlaybackState(this, aDemuxer
) {
135 MOZ_COUNT_CTOR(Benchmark
);
138 Benchmark::~Benchmark() { MOZ_COUNT_DTOR(Benchmark
); }
140 RefPtr
<Benchmark::BenchmarkPromise
> Benchmark::Run() {
141 RefPtr
<Benchmark
> self
= this;
142 return InvokeAsync(Thread(), __func__
, [self
] {
143 RefPtr
<BenchmarkPromise
> p
= self
->mPromise
.Ensure(__func__
);
144 self
->mPlaybackState
.Dispatch(NS_NewRunnableFunction(
145 "Benchmark::Run", [self
]() { self
->mPlaybackState
.DemuxSamples(); }));
150 void Benchmark::ReturnResult(uint32_t aDecodeFps
) {
151 MOZ_ASSERT(OnThread());
153 mPromise
.ResolveIfExists(aDecodeFps
, __func__
);
156 void Benchmark::ReturnError(const MediaResult
& aError
) {
157 MOZ_ASSERT(OnThread());
159 mPromise
.RejectIfExists(aError
, __func__
);
162 void Benchmark::Dispose() {
163 MOZ_ASSERT(OnThread());
165 mKeepAliveUntilComplete
= nullptr;
168 void Benchmark::Init() {
169 MOZ_ASSERT(NS_IsMainThread());
170 gfxVars::Initialize();
173 BenchmarkPlayback::BenchmarkPlayback(Benchmark
* aGlobalState
,
174 MediaDataDemuxer
* aDemuxer
)
176 TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR
),
177 "BenchmarkPlayback::QueueObject")),
178 mGlobalState(aGlobalState
),
179 mDecoderTaskQueue(TaskQueue::Create(
180 GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER
),
181 "BenchmarkPlayback::mDecoderTaskQueue")),
188 void BenchmarkPlayback::DemuxSamples() {
189 MOZ_ASSERT(OnThread());
191 RefPtr
<Benchmark
> ref(mGlobalState
);
192 mDemuxer
->Init()->Then(
194 [this, ref
](nsresult aResult
) {
195 MOZ_ASSERT(OnThread());
196 if (mDemuxer
->GetNumberTracks(TrackInfo::kVideoTrack
)) {
197 mTrackDemuxer
= mDemuxer
->GetTrackDemuxer(TrackInfo::kVideoTrack
, 0);
198 } else if (mDemuxer
->GetNumberTracks(TrackInfo::kAudioTrack
)) {
199 mTrackDemuxer
= mDemuxer
->GetTrackDemuxer(TrackInfo::kAudioTrack
, 0);
201 if (!mTrackDemuxer
) {
202 Error(MediaResult(NS_ERROR_FAILURE
, "Can't create track demuxer"));
207 [this, ref
](const MediaResult
& aError
) { Error(aError
); });
210 void BenchmarkPlayback::DemuxNextSample() {
211 MOZ_ASSERT(OnThread());
213 RefPtr
<Benchmark
> ref(mGlobalState
);
214 RefPtr
<MediaTrackDemuxer::SamplesPromise
> promise
=
215 mTrackDemuxer
->GetSamples();
218 [this, ref
](RefPtr
<MediaTrackDemuxer::SamplesHolder
> aHolder
) {
219 mSamples
.AppendElements(std::move(aHolder
->GetMovableSamples()));
220 if (ref
->mParameters
.mStopAtFrame
&&
221 mSamples
.Length() == ref
->mParameters
.mStopAtFrame
.ref()) {
222 InitDecoder(mTrackDemuxer
->GetInfo());
225 NS_NewRunnableFunction("BenchmarkPlayback::DemuxNextSample",
226 [this, ref
]() { DemuxNextSample(); }));
229 [this, ref
](const MediaResult
& aError
) {
230 switch (aError
.Code()) {
231 case NS_ERROR_DOM_MEDIA_END_OF_STREAM
:
232 InitDecoder(mTrackDemuxer
->GetInfo());
241 void BenchmarkPlayback::InitDecoder(UniquePtr
<TrackInfo
>&& aInfo
) {
242 MOZ_ASSERT(OnThread());
245 Error(MediaResult(NS_ERROR_FAILURE
, "Invalid TrackInfo"));
249 RefPtr
<PDMFactory
> platform
= new PDMFactory();
250 mInfo
= std::move(aInfo
);
251 RefPtr
<Benchmark
> ref(mGlobalState
);
252 platform
->CreateDecoder(CreateDecoderParams
{*mInfo
})
255 [this, ref
](RefPtr
<MediaDataDecoder
>&& aDecoder
) {
256 mDecoder
= new MediaDataDecoderProxy(
257 aDecoder
.forget(), do_AddRef(mDecoderTaskQueue
.get()));
258 mDecoder
->Init()->Then(
260 [this, ref
](TrackInfo::TrackType aTrackType
) {
263 [this, ref
](const MediaResult
& aError
) { Error(aError
); });
265 [this, ref
](const MediaResult
& aError
) { Error(aError
); });
268 void BenchmarkPlayback::FinalizeShutdown() {
269 MOZ_ASSERT(OnThread());
271 MOZ_ASSERT(mFinished
, "GlobalShutdown must have been run");
272 MOZ_ASSERT(!mDecoder
, "mDecoder must have been shutdown already");
273 MOZ_ASSERT(!mDemuxer
, "mDemuxer must have been shutdown already");
274 MOZ_DIAGNOSTIC_ASSERT(mDecoderTaskQueue
->IsEmpty());
275 mDecoderTaskQueue
= nullptr;
277 RefPtr
<Benchmark
> ref(mGlobalState
);
278 ref
->Thread()->Dispatch(NS_NewRunnableFunction(
279 "BenchmarkPlayback::FinalizeShutdown", [ref
]() { ref
->Dispose(); }));
282 void BenchmarkPlayback::GlobalShutdown() {
283 MOZ_ASSERT(OnThread());
285 MOZ_ASSERT(!mFinished
, "We've already shutdown");
290 mTrackDemuxer
->Reset();
291 mTrackDemuxer
->BreakCycles();
292 mTrackDemuxer
= nullptr;
297 RefPtr
<Benchmark
> ref(mGlobalState
);
298 mDecoder
->Flush()->Then(
301 mDecoder
->Shutdown()->Then(
302 Thread(), __func__
, [ref
, this]() { FinalizeShutdown(); },
303 []() { MOZ_CRASH("not reached"); });
307 []() { MOZ_CRASH("not reached"); });
313 void BenchmarkPlayback::Output(MediaDataDecoder::DecodedData
&& aResults
) {
314 MOZ_ASSERT(OnThread());
315 MOZ_ASSERT(!mFinished
);
317 RefPtr
<Benchmark
> ref(mGlobalState
);
318 mFrameCount
+= aResults
.Length();
319 if (!mDecodeStartTime
&& mFrameCount
>= ref
->mParameters
.mStartupFrame
) {
320 mDecodeStartTime
= Some(TimeStamp::Now());
322 TimeStamp now
= TimeStamp::Now();
323 uint32_t frames
= mFrameCount
- ref
->mParameters
.mStartupFrame
;
324 TimeDuration elapsedTime
= now
- mDecodeStartTime
.refOr(now
);
325 if (((frames
== ref
->mParameters
.mFramesToMeasure
) &&
326 mFrameCount
> ref
->mParameters
.mStartupFrame
&& frames
> 0) ||
327 elapsedTime
>= ref
->mParameters
.mTimeout
|| mDrained
) {
328 uint32_t decodeFps
= frames
/ elapsedTime
.ToSeconds();
330 ref
->Dispatch(NS_NewRunnableFunction(
331 "BenchmarkPlayback::Output",
332 [ref
, decodeFps
]() { ref
->ReturnResult(decodeFps
); }));
336 void BenchmarkPlayback::Error(const MediaResult
& aError
) {
337 MOZ_ASSERT(OnThread());
339 RefPtr
<Benchmark
> ref(mGlobalState
);
342 NS_NewRunnableFunction("BenchmarkPlayback::Error",
343 [ref
, aError
]() { ref
->ReturnError(aError
); }));
346 void BenchmarkPlayback::InputExhausted() {
347 MOZ_ASSERT(OnThread());
348 MOZ_ASSERT(!mFinished
);
350 if (mSampleIndex
>= mSamples
.Length()) {
351 Error(MediaResult(NS_ERROR_FAILURE
, "Nothing left to decode"));
355 RefPtr
<MediaRawData
> sample
= mSamples
[mSampleIndex
];
356 RefPtr
<Benchmark
> ref(mGlobalState
);
357 RefPtr
<MediaDataDecoder::DecodePromise
> p
= mDecoder
->Decode(sample
);
360 if (mSampleIndex
== mSamples
.Length() && !ref
->mParameters
.mStopAtFrame
) {
361 // Complete current frame decode then drain if still necessary.
364 [ref
, this](MediaDataDecoder::DecodedData
&& aResults
) {
365 Output(std::move(aResults
));
367 mDecoder
->Drain()->Then(
369 [ref
, this](MediaDataDecoder::DecodedData
&& aResults
) {
371 Output(std::move(aResults
));
372 MOZ_ASSERT(mFinished
, "We must be done now");
374 [ref
, this](const MediaResult
& aError
) { Error(aError
); });
377 [ref
, this](const MediaResult
& aError
) { Error(aError
); });
379 if (mSampleIndex
== mSamples
.Length() && ref
->mParameters
.mStopAtFrame
) {
385 [ref
, this](MediaDataDecoder::DecodedData
&& aResults
) {
386 Output(std::move(aResults
));
391 [ref
, this](const MediaResult
& aError
) { Error(aError
); });
395 } // namespace mozilla