summaryrefslogtreecommitdiffstats
path: root/dom/media/mediacapabilities/DecoderBenchmark.cpp
blob: b5c9a2a693ace689da1dee4c4e9e188ff308fa1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/* -*- 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 "DecoderBenchmark.h"
#include "mozilla/BenchmarkStorageChild.h"
#include "mozilla/media/MediaUtils.h"
#include "mozilla/StaticPrefs_media.h"

namespace mozilla {

void DecoderBenchmark::StoreScore(const nsACString& aDecoderName,
                                  const nsACString& aKey,
                                  RefPtr<FrameStatistics> aStats) {
  FrameStatisticsData statsData = aStats->GetFrameStatisticsData();
  uint64_t totalFrames = FrameStatistics::GetTotalFrames(statsData);
  uint64_t droppedFrames = FrameStatistics::GetDroppedFrames(statsData);

  MOZ_ASSERT(droppedFrames <= totalFrames);
  MOZ_ASSERT(totalFrames >= mLastTotalFrames);
  MOZ_ASSERT(droppedFrames >= mLastDroppedFrames);

  uint64_t diffTotalFrames = totalFrames - mLastTotalFrames;
  uint64_t diffDroppedFrames = droppedFrames - mLastDroppedFrames;

  /* Update now in case the method returns at the if check bellow. */
  mLastTotalFrames = totalFrames;
  mLastDroppedFrames = droppedFrames;

  /* A minimum number of 10 frames is required to store the score. */
  if (diffTotalFrames < 10) {
    return;
  }

  int32_t percentage =
      100 - 100 * float(diffDroppedFrames) / float(diffTotalFrames);

  MOZ_ASSERT(percentage >= 0);

  Put(aDecoderName, aKey, percentage);
}

void DecoderBenchmark::Put(const nsACString& aDecoderName,
                           const nsACString& aKey, int32_t aValue) {
  MOZ_ASSERT(NS_IsMainThread());
  const nsCString name(aDecoderName);
  const nsCString key(aKey);
  BenchmarkStorageChild::Instance()->SendPut(name, key, aValue);
}

RefPtr<BenchmarkScorePromise> DecoderBenchmark::GetScore(
    const nsACString& aDecoderName, const nsACString& aKey) {
  if (NS_IsMainThread()) {
    return Get(aDecoderName, aKey);
  }

  RefPtr<DecoderBenchmark> self = this;
  const nsCString decoderName(aDecoderName);
  const nsCString key(aKey);
  return InvokeAsync(
      GetMainThreadSerialEventTarget(), __func__,
      [self, decoderName, key] { return self->Get(decoderName, key); });
}

RefPtr<BenchmarkScorePromise> DecoderBenchmark::Get(
    const nsACString& aDecoderName, const nsACString& aKey) {
  MOZ_ASSERT(NS_IsMainThread());

  const nsCString name(aDecoderName);
  const nsCString key(aKey);
  return BenchmarkStorageChild::Instance()->SendGet(name, key)->Then(
      GetCurrentSerialEventTarget(), __func__,
      [](int32_t aResult) {
        return BenchmarkScorePromise::CreateAndResolve(aResult, __func__);
      },
      [](ipc::ResponseRejectReason&&) {
        return BenchmarkScorePromise::CreateAndReject(NS_ERROR_FAILURE,
                                                      __func__);
      });
}

/* The key string consists of the video properties resolution, framerate,
 * and bitdepth. There are various levels for each of them. The key is
 * formated by the closest level, for example, a video with width=1920,
 * height=1080, frameRate=24, and bitdepth=8bit will have the key:
 * "ResolutionLevel5-FrameRateLevel1-8bit". */

#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))

/* Keep them sorted */
const uint32_t PixelLevels[] = {
    /* 256x144   =*/36864,
    /* 426x240   =*/102240,
    /* 640x360   =*/230400,
    /* 854x480   =*/409920,
    /* 1280x720  =*/921600,
    /* 1920x1080 =*/2073600,
    /* 2560x1440 =*/3686400,
    /* 3840x2160 =*/8294400,
};
const size_t PixelLevelsSize = NELEMS(PixelLevels);

const uint32_t FrameRateLevels[] = {
    15, 24, 30, 50, 60,
};
const size_t FrameRateLevelsSize = NELEMS(FrameRateLevels);

/* static */
nsCString KeyUtil::FindLevel(const uint32_t aLevels[], const size_t length,
                             uint32_t aValue) {
  MOZ_ASSERT(aValue);
  if (aValue <= aLevels[0]) {
    return "Level0"_ns;
  }
  nsAutoCString level("Level");
  size_t lastIndex = length - 1;
  if (aValue >= aLevels[lastIndex]) {
    level.AppendInt(static_cast<uint32_t>(lastIndex));
    return std::move(level);
  }
  for (size_t i = 0; i < lastIndex; ++i) {
    if (aValue >= aLevels[i + 1]) {
      continue;
    }
    if (aValue - aLevels[i] < aLevels[i + 1] - aValue) {
      level.AppendInt(static_cast<uint32_t>(i));
      return std::move(level);
    }
    level.AppendInt(static_cast<uint32_t>(i + 1));
    return std::move(level);
  }
  MOZ_CRASH("Array is not sorted");
  return ""_ns;
}

/* static */
nsCString KeyUtil::BitDepthToStr(uint8_t aBitDepth) {
  switch (aBitDepth) {
    case 8:  // ColorDepth::COLOR_8
      return "-8bit"_ns;
    case 10:  // ColorDepth::COLOR_10
    case 12:  // ColorDepth::COLOR_12
    case 16:  // ColorDepth::COLOR_16
      return "-non8bit"_ns;
  }
  MOZ_ASSERT_UNREACHABLE("invalid color depth value");
  return ""_ns;
}

/* static */
nsCString KeyUtil::CreateKey(const DecoderBenchmarkInfo& aBenchInfo) {
  nsAutoCString key("Resolution");
  key.Append(FindLevel(PixelLevels, PixelLevelsSize,
                       aBenchInfo.mWidth * aBenchInfo.mHeight));

  key.Append("-FrameRate");
  key.Append(
      FindLevel(FrameRateLevels, FrameRateLevelsSize, aBenchInfo.mFrameRate));

  key.Append(BitDepthToStr(aBenchInfo.mBitDepth));

  return std::move(key);
}

void DecoderBenchmark::Store(const DecoderBenchmarkInfo& aBenchInfo,
                             RefPtr<FrameStatistics> aStats) {
  if (!XRE_IsContentProcess()) {
    NS_WARNING(
        "Storing a benchmark is only allowed only from the content process.");
    return;
  }
  StoreScore(aBenchInfo.mContentType, KeyUtil::CreateKey(aBenchInfo), aStats);
}

/* static */
RefPtr<BenchmarkScorePromise> DecoderBenchmark::Get(
    const DecoderBenchmarkInfo& aBenchInfo) {
  if (!XRE_IsContentProcess()) {
    NS_WARNING(
        "Getting a benchmark is only allowed only from the content process.");
    return BenchmarkScorePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
  }
  // There is no need for any of the data members to query the database, thus
  // it can be a static method.
  auto bench = MakeRefPtr<DecoderBenchmark>();
  return bench->GetScore(aBenchInfo.mContentType,
                         KeyUtil::CreateKey(aBenchInfo));
}

static nsTHashMap<nsCStringHashKey, int32_t> DecoderVersionTable() {
  nsTHashMap<nsCStringHashKey, int32_t> decoderVersionTable;

  /*
   * For the decoders listed here, the benchmark version number will be checked.
   * If the version number does not exist in the database or is different than
   * the version number listed here, all the benchmark entries for this decoder
   * will be erased. An example of assigning the version number `1` for AV1
   * decoder is:
   *
   * decoderVersionTable.InsertOrUpdate("video/av1"_ns, 1);
   *
   * For the decoders not listed here the `CheckVersion` method exits early, to
   * avoid sending unecessary IPC messages.
   */

  return decoderVersionTable;
}

/* static */
void DecoderBenchmark::CheckVersion(const nsACString& aDecoderName) {
  if (!XRE_IsContentProcess()) {
    NS_WARNING(
        "Checking version is only allowed only from the content process.");
    return;
  }

  if (!StaticPrefs::media_mediacapabilities_from_database()) {
    return;
  }

  nsCString name(aDecoderName);
  int32_t version;
  if (!DecoderVersionTable().Get(name, &version)) {
    // A version is not set for that decoder ignore.
    return;
  }

  if (NS_IsMainThread()) {
    BenchmarkStorageChild::Instance()->SendCheckVersion(name, version);
    return;
  }

  DebugOnly<nsresult> rv =
      GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
          "DecoderBenchmark::CheckVersion", [name, version]() {
            BenchmarkStorageChild::Instance()->SendCheckVersion(name, version);
          }));
  MOZ_ASSERT(NS_SUCCEEDED(rv));
}

}  // namespace mozilla