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/. */
6
7 #include "DecoderBenchmark.h"
8 #include "mozilla/BenchmarkStorageChild.h"
9 #include "mozilla/media/MediaUtils.h"
10 #include "mozilla/StaticPrefs_media.h"
11
12 namespace mozilla {
13
StoreScore(const nsACString & aDecoderName,const nsACString & aKey,RefPtr<FrameStatistics> aStats)14 void DecoderBenchmark::StoreScore(const nsACString& aDecoderName,
15 const nsACString& aKey,
16 RefPtr<FrameStatistics> aStats) {
17 FrameStatisticsData statsData = aStats->GetFrameStatisticsData();
18 uint64_t totalFrames = FrameStatistics::GetTotalFrames(statsData);
19 uint64_t droppedFrames = FrameStatistics::GetDroppedFrames(statsData);
20
21 MOZ_ASSERT(droppedFrames <= totalFrames);
22 MOZ_ASSERT(totalFrames >= mLastTotalFrames);
23 MOZ_ASSERT(droppedFrames >= mLastDroppedFrames);
24
25 uint64_t diffTotalFrames = totalFrames - mLastTotalFrames;
26 uint64_t diffDroppedFrames = droppedFrames - mLastDroppedFrames;
27
28 /* Update now in case the method returns at the if check bellow. */
29 mLastTotalFrames = totalFrames;
30 mLastDroppedFrames = droppedFrames;
31
32 /* A minimum number of 10 frames is required to store the score. */
33 if (diffTotalFrames < 10) {
34 return;
35 }
36
37 int32_t percentage =
38 100 - 100 * float(diffDroppedFrames) / float(diffTotalFrames);
39
40 MOZ_ASSERT(percentage >= 0);
41
42 Put(aDecoderName, aKey, percentage);
43 }
44
Put(const nsACString & aDecoderName,const nsACString & aKey,int32_t aValue)45 void DecoderBenchmark::Put(const nsACString& aDecoderName,
46 const nsACString& aKey, int32_t aValue) {
47 MOZ_ASSERT(NS_IsMainThread());
48 const nsCString name(aDecoderName);
49 const nsCString key(aKey);
50 BenchmarkStorageChild::Instance()->SendPut(name, key, aValue);
51 }
52
GetScore(const nsACString & aDecoderName,const nsACString & aKey)53 RefPtr<BenchmarkScorePromise> DecoderBenchmark::GetScore(
54 const nsACString& aDecoderName, const nsACString& aKey) {
55 if (NS_IsMainThread()) {
56 return Get(aDecoderName, aKey);
57 }
58
59 RefPtr<DecoderBenchmark> self = this;
60 const nsCString decoderName(aDecoderName);
61 const nsCString key(aKey);
62 return InvokeAsync(
63 GetMainThreadSerialEventTarget(), __func__,
64 [self, decoderName, key] { return self->Get(decoderName, key); });
65 }
66
Get(const nsACString & aDecoderName,const nsACString & aKey)67 RefPtr<BenchmarkScorePromise> DecoderBenchmark::Get(
68 const nsACString& aDecoderName, const nsACString& aKey) {
69 MOZ_ASSERT(NS_IsMainThread());
70
71 const nsCString name(aDecoderName);
72 const nsCString key(aKey);
73 return BenchmarkStorageChild::Instance()->SendGet(name, key)->Then(
74 GetCurrentSerialEventTarget(), __func__,
75 [](int32_t aResult) {
76 return BenchmarkScorePromise::CreateAndResolve(aResult, __func__);
77 },
78 [](ipc::ResponseRejectReason&&) {
79 return BenchmarkScorePromise::CreateAndReject(NS_ERROR_FAILURE,
80 __func__);
81 });
82 }
83
84 /* The key string consists of the video properties resolution, framerate,
85 * and bitdepth. There are various levels for each of them. The key is
86 * formated by the closest level, for example, a video with width=1920,
87 * height=1080, frameRate=24, and bitdepth=8bit will have the key:
88 * "ResolutionLevel5-FrameRateLevel1-8bit". */
89
90 #define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
91
92 /* Keep them sorted */
93 const uint32_t PixelLevels[] = {
94 /* 256x144 =*/36864,
95 /* 426x240 =*/102240,
96 /* 640x360 =*/230400,
97 /* 854x480 =*/409920,
98 /* 1280x720 =*/921600,
99 /* 1920x1080 =*/2073600,
100 /* 2560x1440 =*/3686400,
101 /* 3840x2160 =*/8294400,
102 };
103 const size_t PixelLevelsSize = NELEMS(PixelLevels);
104
105 const uint32_t FrameRateLevels[] = {
106 15, 24, 30, 50, 60,
107 };
108 const size_t FrameRateLevelsSize = NELEMS(FrameRateLevels);
109
110 /* static */
FindLevel(const uint32_t aLevels[],const size_t length,uint32_t aValue)111 nsCString KeyUtil::FindLevel(const uint32_t aLevels[], const size_t length,
112 uint32_t aValue) {
113 MOZ_ASSERT(aValue);
114 if (aValue <= aLevels[0]) {
115 return "Level0"_ns;
116 }
117 nsAutoCString level("Level");
118 size_t lastIndex = length - 1;
119 if (aValue >= aLevels[lastIndex]) {
120 level.AppendInt(static_cast<uint32_t>(lastIndex));
121 return std::move(level);
122 }
123 for (size_t i = 0; i < lastIndex; ++i) {
124 if (aValue >= aLevels[i + 1]) {
125 continue;
126 }
127 if (aValue - aLevels[i] < aLevels[i + 1] - aValue) {
128 level.AppendInt(static_cast<uint32_t>(i));
129 return std::move(level);
130 }
131 level.AppendInt(static_cast<uint32_t>(i + 1));
132 return std::move(level);
133 }
134 MOZ_CRASH("Array is not sorted");
135 return ""_ns;
136 }
137
138 /* static */
BitDepthToStr(uint8_t aBitDepth)139 nsCString KeyUtil::BitDepthToStr(uint8_t aBitDepth) {
140 switch (aBitDepth) {
141 case 8: // ColorDepth::COLOR_8
142 return "-8bit"_ns;
143 case 10: // ColorDepth::COLOR_10
144 case 12: // ColorDepth::COLOR_12
145 case 16: // ColorDepth::COLOR_16
146 return "-non8bit"_ns;
147 }
148 MOZ_ASSERT_UNREACHABLE("invalid color depth value");
149 return ""_ns;
150 }
151
152 /* static */
CreateKey(const DecoderBenchmarkInfo & aBenchInfo)153 nsCString KeyUtil::CreateKey(const DecoderBenchmarkInfo& aBenchInfo) {
154 nsAutoCString key("Resolution");
155 key.Append(FindLevel(PixelLevels, PixelLevelsSize,
156 aBenchInfo.mWidth * aBenchInfo.mHeight));
157
158 key.Append("-FrameRate");
159 key.Append(
160 FindLevel(FrameRateLevels, FrameRateLevelsSize, aBenchInfo.mFrameRate));
161
162 key.Append(BitDepthToStr(aBenchInfo.mBitDepth));
163
164 return std::move(key);
165 }
166
Store(const DecoderBenchmarkInfo & aBenchInfo,RefPtr<FrameStatistics> aStats)167 void DecoderBenchmark::Store(const DecoderBenchmarkInfo& aBenchInfo,
168 RefPtr<FrameStatistics> aStats) {
169 if (!XRE_IsContentProcess()) {
170 NS_WARNING(
171 "Storing a benchmark is only allowed only from the content process.");
172 return;
173 }
174 StoreScore(aBenchInfo.mContentType, KeyUtil::CreateKey(aBenchInfo), aStats);
175 }
176
177 /* static */
Get(const DecoderBenchmarkInfo & aBenchInfo)178 RefPtr<BenchmarkScorePromise> DecoderBenchmark::Get(
179 const DecoderBenchmarkInfo& aBenchInfo) {
180 if (!XRE_IsContentProcess()) {
181 NS_WARNING(
182 "Getting a benchmark is only allowed only from the content process.");
183 return BenchmarkScorePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
184 }
185 // There is no need for any of the data members to query the database, thus
186 // it can be a static method.
187 auto bench = MakeRefPtr<DecoderBenchmark>();
188 return bench->GetScore(aBenchInfo.mContentType,
189 KeyUtil::CreateKey(aBenchInfo));
190 }
191
DecoderVersionTable()192 static nsTHashMap<nsCStringHashKey, int32_t> DecoderVersionTable() {
193 nsTHashMap<nsCStringHashKey, int32_t> decoderVersionTable;
194
195 /*
196 * For the decoders listed here, the benchmark version number will be checked.
197 * If the version number does not exist in the database or is different than
198 * the version number listed here, all the benchmark entries for this decoder
199 * will be erased. An example of assigning the version number `1` for AV1
200 * decoder is:
201 *
202 * decoderVersionTable.InsertOrUpdate("video/av1"_ns, 1);
203 *
204 * For the decoders not listed here the `CheckVersion` method exits early, to
205 * avoid sending unecessary IPC messages.
206 */
207
208 return decoderVersionTable;
209 }
210
211 /* static */
CheckVersion(const nsACString & aDecoderName)212 void DecoderBenchmark::CheckVersion(const nsACString& aDecoderName) {
213 if (!XRE_IsContentProcess()) {
214 NS_WARNING(
215 "Checking version is only allowed only from the content process.");
216 return;
217 }
218
219 if (!StaticPrefs::media_mediacapabilities_from_database()) {
220 return;
221 }
222
223 nsCString name(aDecoderName);
224 int32_t version;
225 if (!DecoderVersionTable().Get(name, &version)) {
226 // A version is not set for that decoder ignore.
227 return;
228 }
229
230 if (NS_IsMainThread()) {
231 BenchmarkStorageChild::Instance()->SendCheckVersion(name, version);
232 return;
233 }
234
235 DebugOnly<nsresult> rv =
236 GetMainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
237 "DecoderBenchmark::CheckVersion", [name, version]() {
238 BenchmarkStorageChild::Instance()->SendCheckVersion(name, version);
239 }));
240 MOZ_ASSERT(NS_SUCCEEDED(rv));
241 }
242
243 } // namespace mozilla
244