From d95b52b77fce44cd5c9b7e8d00d98a730cdea189 Mon Sep 17 00:00:00 2001 From: Alex Chronopoulos Date: Tue, 6 Aug 2019 09:24:36 +0000 Subject: [PATCH] Bug 1530996 - Store the benchmark calculated from the frame statistics. r=jya Create a class that gets the video properties and the frame statistics, calculates the score in percentage for that video playback, creates a key string according to video properties and trigger the storage of the score. The video properties used are the resolution, the frame rate and the bitdepth. For the key, a range of levels has been created for each property and the video is categorised on the closest levels. The key consists of the various levels like: "ResolutionLevel5-FrameRateLevel1-8bit". Finaly, it uses the IPDL protocol to store the pair of the score and the key. Differential Revision: https://phabricator.services.mozilla.com/D38314 --HG-- extra : moz-landing-system : lando --- .../mediacapabilities/DecoderBenchmark.cpp | 184 ++++++++++++++++++ .../mediacapabilities/DecoderBenchmark.h | 62 ++++++ dom/media/mediacapabilities/moz.build | 5 + 3 files changed, 251 insertions(+) create mode 100644 dom/media/mediacapabilities/DecoderBenchmark.cpp create mode 100644 dom/media/mediacapabilities/DecoderBenchmark.h diff --git a/dom/media/mediacapabilities/DecoderBenchmark.cpp b/dom/media/mediacapabilities/DecoderBenchmark.cpp new file mode 100644 index 000000000000..549e8c628b6d --- /dev/null +++ b/dom/media/mediacapabilities/DecoderBenchmark.cpp @@ -0,0 +1,184 @@ +/* -*- 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" + +namespace mozilla { + +void DecoderBenchmark::StoreScore(const nsACString& aDecoderName, + const nsACString& aKey, + RefPtr aStats) { + uint64_t parsedFrames = aStats->GetParsedFrames(); + uint64_t droppedFrames = aStats->GetDroppedFrames(); + + MOZ_ASSERT(droppedFrames <= parsedFrames); + MOZ_ASSERT(parsedFrames >= mLastParsedFrames); + MOZ_ASSERT(droppedFrames >= mLastDroppedFrames); + + uint64_t diffParsedFrames = parsedFrames - mLastParsedFrames; + uint64_t diffDroppedFrames = droppedFrames - mLastDroppedFrames; + + /* Update now in case the method returns at the if check bellow. */ + mLastParsedFrames = parsedFrames; + mLastDroppedFrames = droppedFrames; + + /* A minimum number of 10 frames is required to store the score. */ + if (diffParsedFrames < 10) { + return; + } + + int32_t percentage = + 100 - 100 * float(diffDroppedFrames) / float(diffParsedFrames); + + 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 DecoderBenchmark::GetScore( + const nsACString& aDecoderName, const nsACString& aKey) { + if (NS_IsMainThread()) { + return Get(aDecoderName, aKey); + } + + RefPtr self = this; + const nsCString decoderName(aDecoderName); + const nsCString key(aKey); + return InvokeAsync( + GetMainThreadSerialEventTarget(), __func__, + [self, decoderName, key] { return self->Get(decoderName, key); }); +} + +RefPtr 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( + GetCurrentThreadSerialEventTarget(), __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); + +nsCString FindLevel(const uint32_t aLevels[], const size_t length, + uint32_t aValue) { + MOZ_ASSERT(aValue); + if (aValue <= aLevels[0]) { + return NS_LITERAL_CSTRING("Level0"); + } + nsAutoCString level("Level"); + size_t lastIndex = length - 1; + if (aValue >= aLevels[lastIndex]) { + level.AppendInt(static_cast(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(i)); + return std::move(level); + } + level.AppendInt(static_cast(i + 1)); + return std::move(level); + } + MOZ_CRASH("Array is not sorted"); + return NS_LITERAL_CSTRING(""); +} + +nsCString BitDepthToStr(uint8_t aBitDepth) { + switch (aBitDepth) { + case 8: // ColorDepth::COLOR_8 + return NS_LITERAL_CSTRING("-8bit"); + case 10: // ColorDepth::COLOR_10 + case 12: // ColorDepth::COLOR_12 + case 16: // ColorDepth::COLOR_16 + return NS_LITERAL_CSTRING("-non8bit"); + } + MOZ_ASSERT_UNREACHABLE("invalid color depth value"); + return NS_LITERAL_CSTRING(""); +} + +nsCString CreateStoreKey(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 aStats) { + if (!XRE_IsContentProcess()) { + NS_WARNING("Storing a benchmark allowed only from the content process."); + return; + } + StoreScore(aBenchInfo.mContentType, CreateStoreKey(aBenchInfo), aStats); +} + +/* static */ +RefPtr DecoderBenchmark::Get( + const DecoderBenchmarkInfo& aBenchInfo) { + if (!XRE_IsContentProcess()) { + NS_WARNING("Getting a benchmark 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(); + return bench->GetScore(aBenchInfo.mContentType, CreateStoreKey(aBenchInfo)); +} + +} // namespace mozilla diff --git a/dom/media/mediacapabilities/DecoderBenchmark.h b/dom/media/mediacapabilities/DecoderBenchmark.h new file mode 100644 index 000000000000..60317979265c --- /dev/null +++ b/dom/media/mediacapabilities/DecoderBenchmark.h @@ -0,0 +1,62 @@ +/* -*- 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/. */ + +#ifndef MOZILLA_DECODER_BENCHMARK_H +#define MOZILLA_DECODER_BENCHMARK_H + +#include "FrameStatistics.h" +#include "mozilla/BenchmarkStorageChild.h" +#include "mozilla/KeyValueStorage.h" + +namespace mozilla { + +typedef KeyValueStorage::GetPromise BenchmarkScorePromise; + +struct DecoderBenchmarkInfo final { + const nsCString mContentType; + const int32_t mWidth; + const int32_t mHeight; + const int32_t mFrameRate; + const uint32_t mBitDepth; +}; + +class DecoderBenchmark final { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecoderBenchmark) + DecoderBenchmark() = default; + + public: + void Store(const DecoderBenchmarkInfo& aBenchInfo, + RefPtr aStats); + + static RefPtr Get( + const DecoderBenchmarkInfo& aBenchInfo); + + private: + void StoreScore(const nsACString& aDecoderName, const nsACString& aKey, + RefPtr aStats); + + RefPtr GetScore(const nsACString& aDecoderName, + const nsACString& aKey); + + void Put(const nsACString& aDecoderName, const nsACString& aKey, + int32_t aValue); + + RefPtr Get(const nsACString& aDecoderName, + const nsACString& aKey); + ~DecoderBenchmark() = default; + + // Keep the last ParsedFrames and DroppedFrames from FrameStatistics. + // FrameStatistics keep an ever-increasing counter across the entire video and + // even when there are resolution changes. This code is called whenever there + // is a resolution change and we need to calculate the benchmark since the + // last call. + uint64_t mLastParsedFrames = 0; + uint64_t mLastDroppedFrames = 0; +}; + +} // namespace mozilla + +#endif // MOZILLA_DECODER_BENCHMARK_H diff --git a/dom/media/mediacapabilities/moz.build b/dom/media/mediacapabilities/moz.build index e296aa71aa83..ce3da942c571 100644 --- a/dom/media/mediacapabilities/moz.build +++ b/dom/media/mediacapabilities/moz.build @@ -13,9 +13,14 @@ EXPORTS.mozilla += [ 'KeyValueStorage.h', ] +EXPORTS += [ + 'DecoderBenchmark.h', +] + UNIFIED_SOURCES += [ 'BenchmarkStorageChild.cpp', 'BenchmarkStorageParent.cpp', + 'DecoderBenchmark.cpp', 'KeyValueStorage.cpp', 'MediaCapabilities.cpp', ]