From f2f414f0efe0f81976be52e2dd920713467350af Mon Sep 17 00:00:00 2001 From: gcarq Date: Thu, 25 Jan 2018 18:08:49 +0100 Subject: [PATCH 1/9] adapt PKGBUILD and update upstream patches --- PKGBUILD | 53 ++++++++------- ...-clang-r1.patch => chromium-clang-r2.patch | 8 +-- ...disable-SharedArrayBuffer-by-default.patch | 35 ---------- chromium-exclude_unwind_tables.patch | 25 ++----- chromium-memcpy-r0.patch | 34 ++++++++++ ...mUTF8-for-UnicodeString-construction.patch | 67 +++++++++++++++++++ chromium-webrtc-r0.patch | 34 ---------- 7 files changed, 139 insertions(+), 117 deletions(-) rename chromium-clang-r1.patch => chromium-clang-r2.patch (73%) delete mode 100644 chromium-disable-SharedArrayBuffer-by-default.patch create mode 100644 chromium-memcpy-r0.patch create mode 100644 chromium-use-fromUTF8-for-UnicodeString-construction.patch delete mode 100644 chromium-webrtc-r0.patch diff --git a/PKGBUILD b/PKGBUILD index 3ec6449..cbf875d 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -5,8 +5,8 @@ # Contributor: Daniel J Griffiths pkgname=inox -pkgver=63.0.3239.132 -pkgrel=2 +pkgver=64.0.3282.119 +pkgrel=1 _launcher_ver=5 pkgdesc="Chromium Spin-off to enhance privacy by disabling data transmission to Google" arch=('x86_64') @@ -16,7 +16,7 @@ depends=('gtk3' 'nss' 'alsa-lib' 'xdg-utils' 'libxss' 'libcups' 'libgcrypt' 'ttf-font' 'systemd' 'dbus' 'libpulse' 'pciutils' 'json-glib' 'desktop-file-utils' 'hicolor-icon-theme') makedepends=('python2' 'gperf' 'yasm' 'mesa' 'ninja' 'nodejs' 'git' 'libva' - 'clang' 'lld' 'llvm') + 'clang' 'llvm') optdepends=('pepper-flash: support for Flash content' 'kdialog: needed for file dialogs in KDE' 'gnome-keyring: for storing passwords in GNOME keyring' @@ -30,14 +30,14 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom chromium-$pkgver.txt::https://chromium.googlesource.com/chromium/src.git/+/$pkgver?format=TEXT https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/product_logo_{16,22,24,32,48,64,128,256}.png # Patches from Arch Linux - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-disable-SharedArrayBuffer-by-default.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-use-fromUTF8-for-UnicodeString-construction.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-exclude_unwind_tables.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-omnibox-unescape-fragment.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-skia-harmony.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-widevine.patch # Patches from Gentoo - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-clang-r1.patch - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-webrtc-r0.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-clang-r2.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-memcpy-r0.patch # Misc https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-libva-r2.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-r15.patch @@ -65,9 +65,9 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/9000-disable-metrics.patch) -sha256sums=('84c46c2c42faaa102abe0647ee1213615a2522627124924c2741ddc2161b3d8d' +sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' '4dc3428f2c927955d9ae117f2fb24d098cc6dd67adb760ac9c82b522ec8b0587' - 'ab330f30c14ea3b5e77976d674304b91cfb02251fe8771cecb0bb4092c7f6b74' + 'a86a8ec67aed5a94557257b9826c5b8fe37005e8376e75986fee77acd066539a' '71471fa4690894420f9e04a2e9a622af620d92ac2714a35f9a4c4e90fa3968dd' '4a533acefbbc1567b0d74a1c0903e9179b8c59c1beabe748850795815366e509' '7b88830c5e0e9819f514ad68aae885d427541a907e25607e47dee1b0f38975fd' @@ -76,13 +76,13 @@ sha256sums=('84c46c2c42faaa102abe0647ee1213615a2522627124924c2741ddc2161b3d8d' '53a1e8da18069eb4d6ab3af9c923c22a0f020241a4839c3140e3601052ddf6ff' '896993987d4ef9f0ac7db454f288117316c2c80ed0b6764019afd760db222dad' '3df9b3bbdc07fde63d9e400954dcc6ab6e0e5454f0ef6447570eef0549337354' - '1e040caa43ba34c627fe3750fb44c781a74298d010ef40657ab8deb4780db70b' - 'e53dc6f259acd39df13874f8a0f440528fae764b859dd71447991a5b1fac7c9c' + 'f846218089a7b095d275e9cb3b74b28586d72f2137968c8c4e09b6f8232d694b' + '9478f1ec1a3c53425306cf41c2d0555c215a4f106955d9d6adfff38044530ce8' '814eb2cecb10cb697e24036b08aac41e88d0e38971741f9e946200764e2401ae' 'feca54ab09ac0fc9d0626770a6b899a6ac5a12173c7d0c1005bc3964ec83e7b3' 'd6fdcb922e5a7fbe15759d39ccc8ea4225821c44d98054ce0f23f9d1f00c9808' - 'ab5368a3e3a67fa63b33fefc6788ad5b4a79089ef4db1011a14c3bee9fdf70c6' - 'bcb2f4588cf5dcf75cde855c7431e94fdcc34bdd68b876a90f65ab9938594562' + '4495e8b29dae242c79ffe4beefc5171eb3c7aacb7e9aebfd2d4d69b9d8c958d3' + 'f6227987c30f8b8a1e0cb5f3863698543a890e6f4bd20ff9384735e1122e66da' '73275413f078b1217a11e5a099777c1ace11a667144d5106975d1ff650540321' 'a15b2ca40b5ca17d4763e41e226fb5faca22277027e8321675c87038dd9879d5' '22726f4f16c7ac9d0c1afc47d6aa40bc02f6de42cfa86a9153781e1d50b58181' @@ -110,17 +110,18 @@ sha256sums=('84c46c2c42faaa102abe0647ee1213615a2522627124924c2741ddc2161b3d8d' # Possible replacements are listed in build/linux/unbundle/replace_gn_files.py # Keys are the names in the above script; values are the dependencies in Arch readonly -A _system_libs=( - #[ffmpeg]=ffmpeg # https://crbug.com/731766 + #[ffmpeg]=ffmpeg # https://crbug.com/731766 [flac]=flac - #[freetype]=freetype2 # Using 'use_system_freetype=true' until M65 - #[harfbuzz-ng]=harfbuzz # Using 'use_system_harfbuzz=true' until M65 - #[icu]=icu # https://crbug.com/772655 + need M64 for ICU 60 + #[fontconfig]=fontconfig # Enable for M65 + #[freetype]=freetype2 # Using 'use_system_freetype=true' until M65 + #[harfbuzz-ng]=harfbuzz # Using 'use_system_harfbuzz=true' until M65 + [icu]=icu [libdrm]= [libjpeg]=libjpeg - #[libpng]=libpng # https://crbug.com/752403#c10 - #[libvpx]=libvpx # https://bugs.gentoo.org/611394 + #[libpng]=libpng # https://crbug.com/752403#c10 + #[libvpx]=libvpx # https://bugs.gentoo.org/611394 [libwebp]=libwebp - [libxml]=libxml2 + #[libxml]=libxml2 # https://crbug.com/736026 [libxslt]=libxslt [opus]=opus [re2]=re2 @@ -158,25 +159,25 @@ prepare() { # https://chromium-review.googlesource.com/c/chromium/src/+/712575 patch -Np1 -i ../chromium-exclude_unwind_tables.patch + # https://crbug.com/772655 + patch -Np1 -i ../chromium-use-fromUTF8-for-UnicodeString-construction.patch + # https://crbug.com/789163 patch -Np1 -i ../chromium-omnibox-unescape-fragment.patch - # https://crbug.com/798864 - patch -Np1 -i ../chromium-disable-SharedArrayBuffer-by-default.patch - # https://crbug.com/skia/6663#c10 patch -Np4 -i ../chromium-skia-harmony.patch # Fixes from Gentoo - patch -Np1 -i ../chromium-clang-r1.patch - patch -Np1 -i ../chromium-webrtc-r0.patch + patch -Np1 -i ../chromium-memcpy-r0.patch + patch -Np1 -i ../chromium-clang-r2.patch # Remove compiler flags not supported by our system clang sed -i \ -e '/"-Wno-enum-compare-switch"/d' \ -e '/"-Wno-null-pointer-arithmetic"/d' \ -e '/"-Wno-tautological-unsigned-zero-compare"/d' \ - -e '/"-Wno-tautological-unsigned-enum-zero-compare"/d' \ + -e '/"-Wno-tautological-constant-compare"/d' build/config/compiler/BUILD.gn msg2 'Applying VA-API patches' @@ -266,7 +267,7 @@ build() { local _flags=( 'custom_toolchain="//build/toolchain/linux/unbundle:default"' 'host_toolchain="//build/toolchain/linux/unbundle:default"' - 'use_lld=true' + 'use_lld=false' 'clang_use_chrome_plugins=false' 'symbol_level=0' 'is_debug=false' diff --git a/chromium-clang-r1.patch b/chromium-clang-r2.patch similarity index 73% rename from chromium-clang-r1.patch rename to chromium-clang-r2.patch index 0fef167..aaee167 100644 --- a/chromium-clang-r1.patch +++ b/chromium-clang-r2.patch @@ -1,6 +1,6 @@ ---- a/build/config/compiler/BUILD.gn.orig 2017-10-04 08:50:15.014675936 +0000 -+++ b/build/config/compiler/BUILD.gn 2017-10-04 08:50:32.831024327 +0000 -@@ -429,18 +429,6 @@ +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -422,18 +422,6 @@ cflags += [ "-fcolor-diagnostics" ] } @@ -12,7 +12,7 @@ - "-Xclang", - "-mllvm", - "-Xclang", -- "-instcombine-lower-dbg-declare=1", +- "-instcombine-lower-dbg-declare=0", - ] - } - diff --git a/chromium-disable-SharedArrayBuffer-by-default.patch b/chromium-disable-SharedArrayBuffer-by-default.patch deleted file mode 100644 index 302abf9..0000000 --- a/chromium-disable-SharedArrayBuffer-by-default.patch +++ /dev/null @@ -1,35 +0,0 @@ -From a354b4ecf2434f2f6460b33031aeaf646edf5e64 Mon Sep 17 00:00:00 2001 -From: Brad Nelson -Date: Thu, 4 Jan 2018 00:36:18 -0800 -Subject: [PATCH] Disable SharedArrayBuffer by default. - -BUG=chromium:798864 -R=jschuh@chromium.org,binji@chromium.org - -Change-Id: I5ebfae41a4b4c7e89faf071e6d009ea3d9ca30d4 -Reviewed-on: https://chromium-review.googlesource.com/849429 -Reviewed-by: Ben Smith -Reviewed-by: Justin Schuh -Reviewed-by: Darin Fisher -Commit-Queue: Brad Nelson -Cr-Commit-Position: refs/heads/master@{#527460} ---- - content/public/common/content_features.cc | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc -index 847a1af6db7b..9075bd853d0f 100644 ---- a/content/public/common/content_features.cc -+++ b/content/public/common/content_features.cc -@@ -312,7 +312,7 @@ const base::Feature kServiceWorkerScriptFullCodeCache{ - - // http://tc39.github.io/ecmascript_sharedmem/shmem.html - const base::Feature kSharedArrayBuffer{"SharedArrayBuffer", -- base::FEATURE_ENABLED_BY_DEFAULT}; -+ base::FEATURE_DISABLED_BY_DEFAULT}; - - // An experiment to require process isolation for the sign-in origin, - // https://accounts.google.com. Launch bug: https://crbug.com/739418. --- -2.15.1 - diff --git a/chromium-exclude_unwind_tables.patch b/chromium-exclude_unwind_tables.patch index 6723259..1961f15 100644 --- a/chromium-exclude_unwind_tables.patch +++ b/chromium-exclude_unwind_tables.patch @@ -1,21 +1,8 @@ -From 1fc37227522ccd314f82ec893ed15c6129296604 Mon Sep 17 00:00:00 2001 -From: Paul Jensen -Date: Wed, 11 Oct 2017 08:37:34 -0400 -Subject: [PATCH] Move exclude_unwind_tables back into declare_args - -There is desire to adjust this flag manually. - -BUG=762629 -R=thakis@chromium.org - -Change-Id: I3bd134c19270cd1f729b3ea078674e734493d4ab ---- - diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni index ad40fd9..50e19a4 100644 --- a/build/config/compiler/compiler.gni +++ b/build/config/compiler/compiler.gni -@@ -68,18 +68,18 @@ +@@ -68,19 +68,19 @@ # Whether or not we should use position independent code. use_pic = true @@ -25,8 +12,9 @@ index ad40fd9..50e19a4 100644 + # server. For unofficial (e.g. development) builds and non-Chrome branded + # (e.g. Cronet which doesn't use Crashpad, crbug.com/479283) builds it's + # useful to be able to unwind at runtime. -+ exclude_unwind_tables = (is_chrome_branded && is_official_build) || -+ (is_chromecast && !is_cast_desktop_build && !is_debug) ++ exclude_unwind_tables = ++ (is_chrome_branded && is_official_build) || ++ (is_chromecast && !is_cast_desktop_build && !is_debug && !is_fuchsia) } assert(!is_cfi || use_thin_lto, "CFI requires ThinLTO") @@ -36,8 +24,9 @@ index ad40fd9..50e19a4 100644 -# For unofficial (e.g. development) builds and non-Chrome branded (e.g. Cronet -# which doesn't use Crashpad, crbug.com/479283) builds it's useful to be able -# to unwind at runtime. --exclude_unwind_tables = (is_chrome_branded && is_official_build) || -- (is_chromecast && !is_cast_desktop_build && !is_debug) +-exclude_unwind_tables = +- (is_chrome_branded && is_official_build) || +- (is_chromecast && !is_cast_desktop_build && !is_debug && !is_fuchsia) - # If true, optimize for size. Does not affect windows builds. # Linux & Mac favor speed over size. diff --git a/chromium-memcpy-r0.patch b/chromium-memcpy-r0.patch new file mode 100644 index 0000000..c8b2e19 --- /dev/null +++ b/chromium-memcpy-r0.patch @@ -0,0 +1,34 @@ +From 4942f56ceb6d60d6f54ebca8e6eba8ba01c278e8 Mon Sep 17 00:00:00 2001 +From: Tomas Popela +Date: Thu, 7 Dec 2017 22:33:34 +0000 +Subject: [PATCH] memcpy used without including string.h + +Compiling Chromium with Clang 4.0.1 and using libstdc++ will fail on using +memcpy without including string.h. + +Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel +Change-Id: Idced1d5de3baf6b520d4a2d61774120642ead1a8 +Reviewed-on: https://chromium-review.googlesource.com/813737 +Reviewed-by: Thomas Anderson +Reviewed-by: vmpstr +Commit-Queue: Thomas Anderson +Cr-Commit-Position: refs/heads/master@{#522579} +--- + cc/paint/raw_memory_transfer_cache_entry.cc | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/cc/paint/raw_memory_transfer_cache_entry.cc b/cc/paint/raw_memory_transfer_cache_entry.cc +index 9e4660c685ee..95ad50b1a338 100644 +--- a/cc/paint/raw_memory_transfer_cache_entry.cc ++++ b/cc/paint/raw_memory_transfer_cache_entry.cc +@@ -4,6 +4,8 @@ + + #include "cc/paint/raw_memory_transfer_cache_entry.h" + ++#include ++ + namespace cc { + + ClientRawMemoryTransferCacheEntry::ClientRawMemoryTransferCacheEntry( +-- +2.15.1 diff --git a/chromium-use-fromUTF8-for-UnicodeString-construction.patch b/chromium-use-fromUTF8-for-UnicodeString-construction.patch new file mode 100644 index 0000000..796390d --- /dev/null +++ b/chromium-use-fromUTF8-for-UnicodeString-construction.patch @@ -0,0 +1,67 @@ +From e58fa0ba66272c5f28828b15d06c7e42a9882b3b Mon Sep 17 00:00:00 2001 +From: Jungshik Shin +Date: Sat, 16 Dec 2017 04:19:27 +0000 +Subject: [PATCH] Use fromUTF8() for UnicodeString construction from UTF-8 + +Chrome's copy of ICU is built with U_CHARSET_IS_UTF8=1 so that |char *| +buffer is treated as UTF-8 when constructing UnicodeString() regardless +of the default encoding of the current locale on Linux or non-Unicode code +page on Windows. + +However, some Linux distros do not set U_CHARSET_IS_UTF=1 when building +ICU and Chromium build with system_icu crashes when Chromium is run in +non-UTF-8 locale (e.g. 'C'). + +To make Chromium work in a non-UTF-8 locale (which is pretty rare these +days), use 'icu::UnicodeString::fromUTF8(StringPiece)' instead of +'icu::UnicodeString(const char*)'. + +Bug: 772655 +Test: components_unittests --gtest_filter=*IDN* +Test: Chromium built with system_icu does not crash in C locale. +Change-Id: I0daa284ec06b8e83814fc70eb8e9e5c96444ebfa +Reviewed-on: https://chromium-review.googlesource.com/831247 +Reviewed-by: Peter Kasting +Commit-Queue: Jungshik Shin +Cr-Commit-Position: refs/heads/master@{#524586} +--- + components/url_formatter/idn_spoof_checker.cc | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/components/url_formatter/idn_spoof_checker.cc b/components/url_formatter/idn_spoof_checker.cc +index a88c5e8f8331..aee748d8a4d5 100644 +--- a/components/url_formatter/idn_spoof_checker.cc ++++ b/components/url_formatter/idn_spoof_checker.cc +@@ -110,8 +110,8 @@ IDNSpoofChecker::IDNSpoofChecker() { + + // These Cyrillic letters look like Latin. A domain label entirely made of + // these letters is blocked as a simplified whole-script-spoofable. +- cyrillic_letters_latin_alike_ = +- icu::UnicodeSet(icu::UnicodeString("[асԁеһіјӏорԛѕԝхуъЬҽпгѵѡ]"), status); ++ cyrillic_letters_latin_alike_ = icu::UnicodeSet( ++ icu::UnicodeString::fromUTF8("[асԁеһіјӏорԛѕԝхуъЬҽпгѵѡ]"), status); + cyrillic_letters_latin_alike_.freeze(); + + cyrillic_letters_ = +@@ -141,8 +141,8 @@ IDNSpoofChecker::IDNSpoofChecker() { + UParseError parse_error; + diacritic_remover_.reset(icu::Transliterator::createFromRules( + UNICODE_STRING_SIMPLE("DropAcc"), +- icu::UnicodeString("::NFD; ::[:Nonspacing Mark:] Remove; ::NFC;" +- " ł > l; ø > o; đ > d;"), ++ icu::UnicodeString::fromUTF8("::NFD; ::[:Nonspacing Mark:] Remove; ::NFC;" ++ " ł > l; ø > o; đ > d;"), + UTRANS_FORWARD, parse_error, status)); + + // Supplement the Unicode confusable list by the following mapping. +@@ -158,7 +158,7 @@ IDNSpoofChecker::IDNSpoofChecker() { + // - U+0D1F (ട) => s + extra_confusable_mapper_.reset(icu::Transliterator::createFromRules( + UNICODE_STRING_SIMPLE("ExtraConf"), +- icu::UnicodeString( ++ icu::UnicodeString::fromUTF8( + "ӏ > l; [кĸκ] > k; п > n; [ƅь] > b; в > b; м > m; н > h; " + "т > t; [шщ] > w; ട > s;"), + UTRANS_FORWARD, parse_error, status)); +-- +2.15.1 diff --git a/chromium-webrtc-r0.patch b/chromium-webrtc-r0.patch deleted file mode 100644 index e14b185..0000000 --- a/chromium-webrtc-r0.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 7f90e2cceda0458cf56026eb6ccffb961a47804b Mon Sep 17 00:00:00 2001 -From: Raphael Kubo da Costa -Date: Fri, 13 Oct 2017 15:49:32 +0200 -Subject: [PATCH] IWYU: Include math.h for round(3). - -math.h was being implicitly included, which can break the build with -alternative libc implementations. - -Bug: None -Change-Id: I969b320b65d0f44abb33d3e1036cfbcb859a4952 -Reviewed-on: https://webrtc-review.googlesource.com/9384 -Reviewed-by: Tommi -Commit-Queue: Raphael Kubo da Costa (rakuco) -Cr-Commit-Position: refs/heads/master@{#20292} ---- - p2p/base/port.cc | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/p2p/base/port.cc b/p2p/base/port.cc -index a1b478d11..81aa0aadb 100644 ---- a/third_party/webrtc/p2p/base/port.cc -+++ b/third_party/webrtc/p2p/base/port.cc -@@ -10,6 +10,8 @@ - - #include "p2p/base/port.h" - -+#include -+ - #include - #include - --- -2.15.0.rc2 - From 3a580bfdd538a33d9f475ed2a1ca355daacff0a4 Mon Sep 17 00:00:00 2001 From: gcarq Date: Thu, 25 Jan 2018 18:15:59 +0100 Subject: [PATCH 2/9] add missing escape character --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index cbf875d..691d776 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -177,7 +177,7 @@ prepare() { -e '/"-Wno-enum-compare-switch"/d' \ -e '/"-Wno-null-pointer-arithmetic"/d' \ -e '/"-Wno-tautological-unsigned-zero-compare"/d' \ - -e '/"-Wno-tautological-constant-compare"/d' + -e '/"-Wno-tautological-constant-compare"/d' \ build/config/compiler/BUILD.gn msg2 'Applying VA-API patches' From a38dd8b0a11971796fde41ff6b22201ebfb26f42 Mon Sep 17 00:00:00 2001 From: xsmile <> Date: Mon, 15 Jan 2018 13:49:20 +0100 Subject: [PATCH 3/9] 64.0.3282.85: Update Inox patches --- 0001-fix-building-without-safebrowsing.patch | 246 ++++++++++--------- 0005-disable-default-extensions.patch | 18 +- 0006-modify-default-prefs.patch | 24 +- 0007-disable-web-resource-service.patch | 49 +--- 0008-restore-classic-ntp.patch | 38 +-- 0012-branding.patch | 8 +- 0014-disable-translation-lang-fetch.patch | 8 +- 0016-chromium-sandbox-pie.patch | 2 +- 0017-disable-new-avatar-menu.patch | 2 +- 9000-disable-metrics.patch | 73 ++++-- PKGBUILD | 18 +- 11 files changed, 245 insertions(+), 241 deletions(-) diff --git a/0001-fix-building-without-safebrowsing.patch b/0001-fix-building-without-safebrowsing.patch index e065e64..0273505 100644 --- a/0001-fix-building-without-safebrowsing.patch +++ b/0001-fix-building-without-safebrowsing.patch @@ -1,6 +1,14 @@ --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc -@@ -643,7 +643,6 @@ class CertificateReportingServiceCertRep +@@ -639,6 +639,7 @@ void SetApplicationLocaleOnIOThread(cons + g_io_thread_application_locale.Get() = locale; + } + ++#if 0 + // An implementation of the SSLCertReporter interface used by + // SSLErrorHandler. Uses CertificateReportingService to send reports. The + // service handles queueing and re-sending of failed reports. Each certificate +@@ -654,7 +655,6 @@ class CertificateReportingServiceCertRep // SSLCertReporter implementation void ReportInvalidCertificateChain( const std::string& serialized_report) override { @@ -8,7 +16,15 @@ } private: -@@ -1704,7 +1703,7 @@ void ChromeContentBrowserClient::AppendE +@@ -662,6 +662,7 @@ class CertificateReportingServiceCertRep + + DISALLOW_COPY_AND_ASSIGN(CertificateReportingServiceCertReporter); + }; ++#endif + + #if defined(OS_ANDROID) + float GetDeviceScaleAdjustment() { +@@ -1752,7 +1753,7 @@ void ChromeContentBrowserClient::AppendE // Disable client-side phishing detection in the renderer if it is // disabled in the Profile preferences or the browser process. if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled) || @@ -17,24 +33,16 @@ command_line->AppendSwitch( switches::kDisableClientSidePhishingDetection); } -@@ -2281,15 +2280,9 @@ void ChromeContentBrowserClient::AllowCe - // Otherwise, display an SSL blocking page. The interstitial page takes - // ownership of ssl_blocking_page. - -- CertificateReportingService* cert_reporting_service = -- CertificateReportingServiceFactory::GetForBrowserContext( -- web_contents->GetBrowserContext()); -- std::unique_ptr cert_reporter( -- new CertificateReportingServiceCertReporter(cert_reporting_service)); -- +@@ -2361,7 +2362,7 @@ void ChromeContentBrowserClient::AllowCe SSLErrorHandler::HandleSSLError( web_contents, cert_error, ssl_info, request_url, strict_enforcement, -- expired_previous_decision, std::move(cert_reporter), callback); -+ expired_previous_decision, nullptr, callback); + expired_previous_decision, +- std::make_unique(web_contents), ++ nullptr, + callback, SSLErrorHandler::BlockingPageReadyCallback()); } - void ChromeContentBrowserClient::SelectClientCertificate( -@@ -2478,8 +2471,6 @@ bool ChromeContentBrowserClient::CanCrea +@@ -2551,8 +2552,6 @@ bool ChromeContentBrowserClient::CanCrea void ChromeContentBrowserClient::ResourceDispatcherHostCreated() { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -43,7 +51,7 @@ for (size_t i = 0; i < extra_parts_.size(); ++i) extra_parts_[i]->ResourceDispatcherHostCreated(); -@@ -2996,17 +2987,6 @@ void ChromeContentBrowserClient::ExposeI +@@ -3043,17 +3042,6 @@ void ChromeContentBrowserClient::ExposeI base::RetainedRef(context))); } @@ -61,7 +69,16 @@ #if defined(OS_WIN) if (base::FeatureList::IsEnabled(features::kModuleDatabase)) { // Add the ModuleEventSink interface. This is the interface used by renderer -@@ -3585,12 +3565,6 @@ ChromeContentBrowserClient::CreateURLLoa +@@ -3508,7 +3496,7 @@ ChromeContentBrowserClient::CreateThrott + switches::kCommittedInterstitials)) { + throttles.push_back(std::make_unique( + handle, +- std::make_unique(web_contents), ++ nullptr, + base::Bind(&SSLErrorHandler::HandleSSLError))); + } + +@@ -3697,12 +3685,6 @@ ChromeContentBrowserClient::CreateURLLoa std::vector> result; @@ -74,7 +91,7 @@ return result; } -@@ -3646,18 +3620,3 @@ void ChromeContentBrowserClient::SetDefa +@@ -3781,18 +3763,3 @@ void ChromeContentBrowserClient::SetDefa const storage::QuotaSettings* settings) { g_default_quota_settings = settings; } @@ -125,7 +142,7 @@ GetIOTaskRunner(), std::move(delegate)); --- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc +++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc -@@ -399,8 +399,7 @@ void NotifyUIThreadOfRequestComplete( +@@ -406,8 +406,7 @@ void NotifyUIThreadOfRequestComplete( } // namespace ChromeResourceDispatcherHostDelegate::ChromeResourceDispatcherHostDelegate() @@ -135,17 +152,16 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) , user_script_listener_(new extensions::UserScriptListener()) #endif -@@ -450,9 +449,6 @@ void ChromeResourceDispatcherHostDelegat +@@ -457,8 +456,6 @@ void ChromeResourceDispatcherHostDelegat content::AppCacheService* appcache_service, ResourceType resource_type, std::vector>* throttles) { - if (safe_browsing_.get()) - safe_browsing_->OnResourceRequest(request); -- - const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); + ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context); + client_hints::RequestBeginning(request, io_data->GetCookieSettings()); - #if BUILDFLAG(ENABLE_OFFLINE_PAGES) -@@ -638,13 +634,13 @@ void ChromeResourceDispatcherHostDelegat +@@ -644,13 +641,13 @@ void ChromeResourceDispatcherHostDelegat content::ResourceThrottle* first_throttle = NULL; #if defined(OS_ANDROID) first_throttle = DataReductionProxyResourceThrottle::MaybeCreate( @@ -304,7 +320,7 @@ --- a/chrome/browser/download/chrome_download_manager_delegate.cc +++ b/chrome/browser/download/chrome_download_manager_delegate.cc -@@ -228,13 +228,6 @@ ChromeDownloadManagerDelegate::~ChromeDo +@@ -280,13 +280,6 @@ ChromeDownloadManagerDelegate::~ChromeDo void ChromeDownloadManagerDelegate::SetDownloadManager(DownloadManager* dm) { download_manager_ = dm; @@ -318,7 +334,7 @@ } void ChromeDownloadManagerDelegate::Shutdown() { -@@ -498,16 +491,6 @@ void ChromeDownloadManagerDelegate::Choo +@@ -556,16 +549,6 @@ download::InProgressCache* ChromeDownloa void ChromeDownloadManagerDelegate::SanitizeSavePackageResourceName( base::FilePath* filename) { @@ -337,7 +353,7 @@ void ChromeDownloadManagerDelegate::OpenDownloadUsingPlatformHandler( --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc -@@ -70,7 +70,6 @@ +@@ -71,7 +71,6 @@ #include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/printing/print_preview_dialog_controller.h" #include "chrome/browser/profiles/profile_manager.h" @@ -345,15 +361,15 @@ #include "chrome/browser/shell_integration.h" #include "chrome/browser/status_icons/status_tray.h" #include "chrome/browser/ui/browser_dialogs.h" -@@ -217,7 +216,6 @@ BrowserProcessImpl::BrowserProcessImpl( +@@ -220,7 +219,6 @@ BrowserProcessImpl::BrowserProcessImpl( created_icon_manager_(false), created_notification_ui_manager_(false), created_notification_bridge_(false), - created_safe_browsing_service_(false), created_subresource_filter_ruleset_service_(false), + created_optimization_guide_service_(false), shutting_down_(false), - tearing_down_(false), -@@ -312,8 +310,6 @@ void BrowserProcessImpl::StartTearDown() +@@ -309,8 +307,6 @@ void BrowserProcessImpl::StartTearDown() // that URLFetcher operation before going away.) metrics_services_manager_.reset(); intranet_redirect_detector_.reset(); @@ -362,7 +378,7 @@ network_time_tracker_.reset(); #if BUILDFLAG(ENABLE_PLUGINS) plugins_resource_service_.reset(); -@@ -917,22 +913,6 @@ StatusTray* BrowserProcessImpl::status_t +@@ -922,22 +918,6 @@ StatusTray* BrowserProcessImpl::status_t return status_tray_.get(); } @@ -385,7 +401,7 @@ subresource_filter::ContentRulesetService* BrowserProcessImpl::subresource_filter_ruleset_service() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -@@ -1207,16 +1187,6 @@ void BrowserProcessImpl::CreateBackgroun +@@ -1230,16 +1210,6 @@ void BrowserProcessImpl::CreateBackgroun #endif } @@ -404,7 +420,7 @@ created_subresource_filter_ruleset_service_ = true; --- a/chrome/browser/browser_process_impl.h +++ b/chrome/browser/browser_process_impl.h -@@ -133,9 +133,6 @@ class BrowserProcessImpl : public Browse +@@ -134,9 +134,6 @@ class BrowserProcessImpl : public Browse void set_background_mode_manager_for_test( std::unique_ptr manager) override; StatusTray* status_tray() override; @@ -413,8 +429,8 @@ - override; subresource_filter::ContentRulesetService* subresource_filter_ruleset_service() override; - -@@ -263,9 +260,6 @@ class BrowserProcessImpl : public Browse + optimization_guide::OptimizationGuideService* optimization_guide_service() +@@ -269,9 +266,6 @@ class BrowserProcessImpl : public Browse std::unique_ptr background_mode_manager_; #endif @@ -426,9 +442,9 @@ subresource_filter_ruleset_service_; --- a/chrome/browser/browser_process.h +++ b/chrome/browser/browser_process.h -@@ -43,10 +43,6 @@ class WatchDogThread; - class WebRtcLogUploader; - #endif +@@ -46,10 +46,6 @@ namespace content { + class NetworkConnectionTracker; + } -namespace safe_browsing { -class SafeBrowsingService; @@ -437,7 +453,7 @@ namespace subresource_filter { class ContentRulesetService; } -@@ -119,10 +115,6 @@ namespace resource_coordinator { +@@ -126,10 +122,6 @@ namespace resource_coordinator { class TabManager; } @@ -445,10 +461,10 @@ -class ClientSideDetectionService; -} - - namespace ukm { - class UkmRecorder; - } -@@ -244,14 +236,6 @@ class BrowserProcess { + // NOT THREAD SAFE, call only from the main thread. + // These functions shouldn't return NULL unless otherwise noted. + class BrowserProcess { +@@ -250,14 +242,6 @@ class BrowserProcess { // on this platform (or this is a unit test). virtual StatusTray* status_tray() = 0; @@ -724,7 +740,7 @@ extension.erase(0, 1); --- a/chrome/browser/component_updater/file_type_policies_component_installer.cc +++ b/chrome/browser/component_updater/file_type_policies_component_installer.cc -@@ -36,20 +36,6 @@ const uint8_t kPublicKeySHA256[32] = { +@@ -38,20 +38,6 @@ const uint8_t kPublicKeySHA256[32] = { const char kFileTypePoliciesManifestName[] = "File Type Policies"; void LoadFileTypesFromDisk(const base::FilePath& pb_path) { @@ -780,8 +796,8 @@ void DownloadTargetDeterminer::OnDownloadDestroyed( --- a/chrome/browser/permissions/permission_uma_util.cc +++ b/chrome/browser/permissions/permission_uma_util.cc -@@ -696,8 +696,6 @@ void PermissionUmaUtil::RecordPermission - PermissionPersistDecision::UNSPECIFIED, +@@ -498,8 +498,6 @@ void PermissionUmaUtil::RecordPermission + requesting_origin, permission, action, source_ui, gesture_type, autoblocker->GetDismissCount(requesting_origin, permission), autoblocker->GetIgnoreCount(requesting_origin, permission)); - g_browser_process->safe_browsing_service() @@ -803,7 +819,7 @@ return !download_item_->IsDone(); --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -2307,8 +2307,6 @@ split_static_library("browser") { +@@ -2349,8 +2349,6 @@ split_static_library("browser") { "download/download_commands.h", "download/download_crx_util.cc", "download/download_crx_util.h", @@ -811,10 +827,10 @@ - "download/download_danger_prompt.h", "download/download_dir_policy_handler.cc", "download/download_dir_policy_handler.h", - "download/download_shelf.cc", + "download/download_dir_util.cc", --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn -@@ -1993,7 +1993,6 @@ split_static_library("ui") { +@@ -2047,7 +2047,6 @@ split_static_library("ui") { "cocoa/dialog_text_field_editor.mm", "cocoa/download/background_theme.h", "cocoa/download/background_theme.mm", @@ -822,7 +838,7 @@ "cocoa/download/download_item_button.h", "cocoa/download/download_item_button.mm", "cocoa/download/download_item_cell.h", -@@ -2650,7 +2649,6 @@ split_static_library("ui") { +@@ -2707,7 +2706,6 @@ split_static_library("ui") { "views/cookie_info_view.h", "views/device_chooser_content_view.cc", "views/device_chooser_content_view.h", @@ -832,7 +848,7 @@ "views/exclusive_access_bubble_views.cc", --- a/chrome/browser/ssl/security_state_tab_helper.cc +++ b/chrome/browser/ssl/security_state_tab_helper.cc -@@ -181,6 +181,7 @@ bool SecurityStateTabHelper::UsedPolicyI +@@ -180,6 +180,7 @@ bool SecurityStateTabHelper::UsedPolicyI security_state::MaliciousContentStatus SecurityStateTabHelper::GetMaliciousContentStatus() const { @@ -840,7 +856,7 @@ content::NavigationEntry* entry = web_contents()->GetController().GetVisibleEntry(); if (!entry) -@@ -236,6 +237,7 @@ SecurityStateTabHelper::GetMaliciousCont +@@ -235,6 +236,7 @@ SecurityStateTabHelper::GetMaliciousCont break; } } @@ -850,9 +866,9 @@ --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc -@@ -703,40 +703,6 @@ void ChromeBrowsingDataRemoverDelegate:: - CONTENT_SETTINGS_TYPE_CLIENT_HINTS, base::Time(), - base::Bind(&WebsiteSettingsFilterAdapter, filter)); +@@ -720,40 +720,6 @@ void ChromeBrowsingDataRemoverDelegate:: + CONTENT_SETTINGS_TYPE_CLIENT_HINTS, base::Time(), + base::Bind(&WebsiteSettingsFilterAdapter, filter)); - // Clear the safebrowsing cookies only if time period is for "all time". It - // doesn't make sense to apply the time period of deleting in the last X @@ -893,35 +909,32 @@ --- a/chrome/browser/notifications/platform_notification_service_impl.cc +++ b/chrome/browser/notifications/platform_notification_service_impl.cc -@@ -26,8 +26,6 @@ +@@ -27,8 +27,6 @@ #include "chrome/browser/profiles/profile_attributes_storage.h" #include "chrome/browser/profiles/profile_io_data.h" #include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/safe_browsing/ping_manager.h" -#include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/ui/browser.h" - #include "chrome/common/chrome_features.h" - #include "chrome/common/features.h" -@@ -94,18 +92,6 @@ void CancelNotification(const std::strin - notification_id)); - } + #include "chrome/browser/ui/browser_list.h" + #include "chrome/browser/ui/browser_window.h" +@@ -81,6 +79,7 @@ namespace { + // permission without having an associated renderer process yet. + const int kInvalidRenderProcessId = -1; --void ReportNotificationImageOnIOThread( -- scoped_refptr safe_browsing_service, -- Profile* profile, -- const GURL& origin, -- const SkBitmap& image) { -- DCHECK_CURRENTLY_ON(BrowserThread::IO); -- if (!safe_browsing_service || !safe_browsing_service->enabled()) -- return; -- safe_browsing_service->ping_manager()->ReportNotificationImage( -- profile, safe_browsing_service->database_manager(), origin, image); --} -- - } // namespace ++#if 0 + void ReportNotificationImageOnIOThread( + scoped_refptr safe_browsing_service, + Profile* profile, +@@ -92,6 +91,7 @@ void ReportNotificationImageOnIOThread( + safe_browsing_service->ping_manager()->ReportNotificationImage( + profile, safe_browsing_service->database_manager(), origin, image); + } ++#endif - // static -@@ -462,13 +448,6 @@ Notification PlatformNotificationService + // Whether a web notification should be displayed when chrome is in full + // screen mode. +@@ -516,13 +516,6 @@ PlatformNotificationServiceImpl::CreateN notification.set_type(message_center::NOTIFICATION_TYPE_IMAGE); notification.set_image( gfx::Image::CreateFrom1xBitmap(notification_resources.image)); @@ -999,7 +1012,7 @@ #include "chrome/browser/sync/chrome_sync_client.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/translate/translate_ranker_metrics_provider.h" -@@ -661,9 +660,6 @@ void ChromeMetricsServiceClient::Registe +@@ -699,9 +698,6 @@ void ChromeMetricsServiceClient::Registe metrics_service_->RegisterMetricsProvider( base::MakeUnique()); @@ -1025,17 +1038,17 @@ // WebContentsObserver implementation. Sets a flag so that when the database --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc -@@ -208,7 +208,6 @@ EnsureBrowserContextKeyedServiceFactorie +@@ -214,7 +214,6 @@ EnsureBrowserContextKeyedServiceFactorie #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) CaptivePortalServiceFactory::GetInstance(); #endif - CertificateReportingServiceFactory::GetInstance(); #if defined(OS_ANDROID) - chrome::android::DataUseUITabModelFactory::GetInstance(); + android::DataUseUITabModelFactory::GetInstance(); #endif --- a/chrome/browser/ssl/captive_portal_blocking_page.cc +++ b/chrome/browser/ssl/captive_portal_blocking_page.cc -@@ -83,13 +83,6 @@ CaptivePortalBlockingPage::CaptivePortal +@@ -85,13 +85,6 @@ CaptivePortalBlockingPage::CaptivePortal login_url_(login_url), ssl_info_(ssl_info), callback_(callback) { @@ -1049,7 +1062,7 @@ captive_portal::CaptivePortalMetrics::LogCaptivePortalBlockingPageEvent( captive_portal::CaptivePortalMetrics::SHOW_ALL); } -@@ -207,10 +200,7 @@ void CaptivePortalBlockingPage::Populate +@@ -209,10 +202,7 @@ void CaptivePortalBlockingPage::Populate load_time_data->SetString("explanationParagraph", base::string16()); load_time_data->SetString("finalParagraph", base::string16()); @@ -1061,7 +1074,7 @@ } void CaptivePortalBlockingPage::CommandReceived(const std::string& command) { -@@ -285,12 +275,6 @@ void CaptivePortalBlockingPage::OnProcee +@@ -287,12 +277,6 @@ void CaptivePortalBlockingPage::OnProcee void CaptivePortalBlockingPage::OnDontProceed() { UpdateMetricsAfterSecurityInterstitial(); @@ -1087,7 +1100,7 @@ void CertReportHelper::SetSSLCertReporterForTesting( --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc -@@ -274,11 +274,6 @@ void TabHelpers::AttachTabHelpers(WebCon +@@ -288,11 +288,6 @@ void TabHelpers::AttachTabHelpers(WebCon new ChromePDFWebContentsHelperClient())); PluginObserver::CreateForWebContents(web_contents); SadTabHelper::CreateForWebContents(web_contents); @@ -1096,12 +1109,12 @@ - web_contents); - safe_browsing::TriggerCreator::MaybeCreateTriggersForWebContents( - profile, web_contents); - TabContentsSyncedTabDelegate::CreateForWebContents(web_contents); - TabDialogs::CreateForWebContents(web_contents); - ThumbnailTabHelper::CreateForWebContents(web_contents); + SearchTabHelper::CreateForWebContents(web_contents); + if (base::FeatureList::IsEnabled(features::kTabMetricsLogging)) + TabActivityWatcher::WatchWebContents(web_contents); --- a/chrome/browser/permissions/permission_decision_auto_blocker.cc +++ b/chrome/browser/permissions/permission_decision_auto_blocker.cc -@@ -16,7 +16,6 @@ +@@ -18,7 +18,6 @@ #include "chrome/browser/permissions/permission_util.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" @@ -1109,7 +1122,7 @@ #include "chrome/common/chrome_features.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/safe_browsing/db/database_manager.h" -@@ -431,10 +430,6 @@ PermissionDecisionAutoBlocker::Permissio +@@ -422,10 +421,6 @@ PermissionDecisionAutoBlocker::Permissio db_manager_(nullptr), safe_browsing_timeout_(kCheckUrlTimeoutMs), clock_(new base::DefaultClock()) { @@ -1263,7 +1276,7 @@ sources = [ "chrome_cleaner/chrome_cleaner_controller_impl_win.cc", "chrome_cleaner/chrome_cleaner_controller_impl_win.h", -@@ -70,6 +75,7 @@ static_library("safe_browsing") { +@@ -73,6 +78,7 @@ static_library("safe_browsing") { ] deps += [ "//components/keep_alive_registry" ] } @@ -1273,7 +1286,7 @@ # "Safe Browsing Basic" files used for safe browsing in full mode --- a/chrome/browser/net/system_network_context_manager.cc +++ b/chrome/browser/net/system_network_context_manager.cc -@@ -49,17 +49,11 @@ content::mojom::NetworkContextParamsPtr +@@ -50,17 +50,11 @@ content::mojom::NetworkContextParamsPtr // Called on IOThread to disable QUIC for HttpNetworkSessions not using the // network service. Note that re-enabling QUIC dynamically is not supported for // simpliciy and requires a browser restart. @@ -1292,7 +1305,7 @@ } } // namespace -@@ -131,11 +125,7 @@ void SystemNetworkContextManager::Disabl +@@ -132,11 +126,7 @@ void SystemNetworkContextManager::Disabl if (!io_thread) return; @@ -1307,7 +1320,7 @@ } --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc -@@ -401,9 +401,8 @@ void AddBluetoothStrings(content::WebUID +@@ -405,9 +405,8 @@ void AddBluetoothStrings(content::WebUID #endif void AddChangePasswordStrings(content::WebUIDataSource* html_source) { @@ -1326,20 +1339,19 @@ #include "chrome/renderer/prerender/prerender_helper.h" #include "chrome/renderer/prerender/prerenderer_client.h" -#include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h" - #include "chrome/renderer/searchbox/search_bouncer.h" - #include "chrome/renderer/searchbox/searchbox.h" - #include "chrome/renderer/searchbox/searchbox_extension.h" -@@ -82,9 +81,6 @@ + #include "chrome/renderer/tts_dispatcher.h" + #include "chrome/renderer/worker_content_settings_client.h" + #include "components/autofill/content/renderer/autofill_agent.h" +@@ -79,8 +78,6 @@ #include "components/network_hints/renderer/prescient_networking_dispatcher.h" #include "components/password_manager/content/renderer/credential_manager_client.h" #include "components/pdf/renderer/pepper_pdf_host.h" -#include "components/safe_browsing/renderer/renderer_url_loader_throttle.h" -#include "components/safe_browsing/renderer/threat_dom_details.h" --#include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h" - #include "components/signin/core/common/profile_management_switches.h" + #include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h" #include "components/spellcheck/spellcheck_build_features.h" #include "components/startup_metric_utils/common/startup_metric.mojom.h" -@@ -449,9 +445,6 @@ void ChromeContentRendererClient::Render +@@ -449,9 +446,6 @@ void ChromeContentRendererClient::Render thread->AddObserver(spellcheck_.get()); } #endif @@ -1349,7 +1361,7 @@ prerender_dispatcher_.reset(new prerender::PrerenderDispatcher()); subresource_filter_ruleset_dealer_.reset( new subresource_filter::UnverifiedRulesetDealer()); -@@ -564,10 +557,6 @@ void ChromeContentRendererClient::Render +@@ -566,10 +560,6 @@ void ChromeContentRendererClient::Render new nacl::NaClHelper(render_frame); #endif @@ -1360,7 +1372,7 @@ #if BUILDFLAG(ENABLE_PRINTING) new printing::PrintRenderFrameHelper( render_frame, base::MakeUnique()); -@@ -1282,16 +1271,6 @@ bool ChromeContentRendererClient::WillSe +@@ -1316,16 +1306,6 @@ bool ChromeContentRendererClient::WillSe const blink::WebURL& url, std::vector>* throttles, GURL* new_url) { @@ -1377,18 +1389,18 @@ // Check whether the request should be allowed. If not allowed, we reset the // URL to something invalid to prevent the request and cause an error. #if BUILDFLAG(ENABLE_EXTENSIONS) -@@ -1387,9 +1366,7 @@ bool ChromeContentRendererClient::IsExte +@@ -1423,9 +1403,7 @@ bool ChromeContentRendererClient::IsExte std::unique_ptr ChromeContentRendererClient::CreateWebSocketHandshakeThrottle() { - InitSafeBrowsingIfNecessary(); - return base::MakeUnique( - safe_browsing_.get()); -+ return nullptr; ++ return std::unique_ptr(nullptr); } std::unique_ptr -@@ -1681,13 +1658,6 @@ ChromeContentRendererClient::GetTaskSche +@@ -1717,13 +1695,6 @@ ChromeContentRendererClient::GetTaskSche GetRendererTaskSchedulerInitParamsFromCommandLine(); } @@ -1404,23 +1416,15 @@ base::Time cert_validity_start, --- a/chrome/renderer/chrome_content_renderer_client.h +++ b/chrome/renderer/chrome_content_renderer_client.h -@@ -19,7 +19,6 @@ +@@ -20,7 +20,6 @@ #include "chrome/renderer/media/chrome_key_systems_provider.h" #include "components/nacl/common/features.h" #include "components/rappor/public/interfaces/rappor_recorder.mojom.h" -#include "components/safe_browsing/common/safe_browsing.mojom.h" #include "components/spellcheck/spellcheck_build_features.h" #include "content/public/renderer/content_renderer_client.h" - #include "extensions/features/features.h" -@@ -27,6 +26,7 @@ - #include "media/media_features.h" - #include "ppapi/features/features.h" - #include "printing/features/features.h" -+#include "third_party/WebKit/public/platform/WebSocketHandshakeThrottle.h" - #include "v8/include/v8.h" - - #if defined (OS_CHROMEOS) -@@ -70,10 +70,6 @@ namespace prerender { + #include "content/public/renderer/render_thread.h" +@@ -71,10 +70,6 @@ namespace prerender { class PrerenderDispatcher; } @@ -1431,9 +1435,9 @@ namespace subresource_filter { class UnverifiedRulesetDealer; } -@@ -244,9 +240,6 @@ class ChromeContentRendererClient : publ - static GURL GetNaClContentHandlerURL(const std::string& actual_mime_type, - const content::WebPluginInfo& plugin); +@@ -255,9 +250,6 @@ class ChromeContentRendererClient + void GetInterface(const std::string& name, + mojo::ScopedMessagePipeHandle request_handle) override; - // Initialises |safe_browsing_| if it is not already initialised. - void InitSafeBrowsingIfNecessary(); @@ -1441,9 +1445,9 @@ void GetNavigationErrorStringsInternal( content::RenderFrame* render_frame, const blink::WebURLRequest& failed_request, -@@ -278,12 +271,9 @@ class ChromeContentRendererClient : publ +@@ -289,12 +281,9 @@ class ChromeContentRendererClient - chrome::ChromeKeySystemsProvider key_systems_provider_; + ChromeKeySystemsProvider key_systems_provider_; - safe_browsing::mojom::SafeBrowsingPtr safe_browsing_; - diff --git a/0005-disable-default-extensions.patch b/0005-disable-default-extensions.patch index f9a2cd4..8a73898 100644 --- a/0005-disable-default-extensions.patch +++ b/0005-disable-default-extensions.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/extensions/component_loader.cc +++ b/chrome/browser/extensions/component_loader.cc -@@ -441,11 +441,6 @@ void ComponentLoader::AddWebStoreApp() { +@@ -422,11 +422,6 @@ void ComponentLoader::AddWebStoreApp() { if (!IsNormalSession()) return; #endif @@ -12,7 +12,7 @@ } scoped_refptr ComponentLoader::CreateExtension( -@@ -500,11 +495,6 @@ void ComponentLoader::AddDefaultComponen +@@ -481,11 +476,6 @@ void ComponentLoader::AddDefaultComponen Add(IDR_BOOKMARKS_MANIFEST, base::FilePath(FILE_PATH_LITERAL("bookmark_manager"))); } @@ -24,9 +24,9 @@ #endif // defined(OS_CHROMEOS) if (!skip_session_components) { -@@ -582,13 +572,6 @@ void ComponentLoader::AddDefaultComponen - AddHotwordAudioVerificationApp(); - AddHotwordHelperExtension(); +@@ -561,13 +551,6 @@ void ComponentLoader::AddDefaultComponen + + AddHangoutServicesExtension(); AddImageLoaderExtension(); - - bool install_feedback = enable_background_extensions_during_testing; @@ -40,16 +40,13 @@ #if defined(OS_CHROMEOS) --- a/chrome/browser/extensions/external_component_loader.cc +++ b/chrome/browser/extensions/external_component_loader.cc -@@ -34,12 +34,6 @@ ExternalComponentLoader::~ExternalCompon +@@ -31,9 +31,6 @@ ExternalComponentLoader::~ExternalCompon void ExternalComponentLoader::StartLoading() { auto prefs = std::make_unique(); -#if defined(GOOGLE_CHROME_BUILD) - AddExternalExtension(extension_misc::kInAppPaymentsSupportAppId, prefs.get()); -#endif // defined(GOOGLE_CHROME_BUILD) -- -- if (HotwordServiceFactory::IsHotwordAllowed(profile_)) -- AddExternalExtension(extension_misc::kHotwordSharedModuleId, prefs.get()); #if defined(OS_CHROMEOS) { @@ -89,11 +86,10 @@ void WebstoreInstaller::ReportFailure(const std::string& error, --- a/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc +++ b/chrome/browser/extensions/component_extensions_whitelist/whitelist.cc -@@ -26,8 +26,6 @@ namespace extensions { +@@ -26,7 +26,6 @@ namespace extensions { bool IsComponentExtensionWhitelisted(const std::string& extension_id) { const char* const kAllowed[] = { -- extension_misc::kHotwordSharedModuleId, - extension_misc::kInAppPaymentsSupportAppId, extension_misc::kMediaRouterStableExtensionId, extension_misc::kPdfExtensionId, diff --git a/0006-modify-default-prefs.patch b/0006-modify-default-prefs.patch index b079f91..57610f4 100644 --- a/0006-modify-default-prefs.patch +++ b/0006-modify-default-prefs.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc -@@ -866,7 +866,7 @@ void ChromeContentBrowserClient::Registe +@@ -879,7 +879,7 @@ void ChromeContentBrowserClient::Registe void ChromeContentBrowserClient::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref(prefs::kDisable3DAPIs, false); @@ -11,12 +11,12 @@ --- a/chrome/browser/ui/browser_ui_prefs.cc +++ b/chrome/browser/ui/browser_ui_prefs.cc -@@ -62,11 +62,11 @@ void RegisterBrowserUserPrefs(user_prefs +@@ -59,11 +59,11 @@ void RegisterBrowserUserPrefs(user_prefs + registry->RegisterBooleanPref(prefs::kWebAppCreateInAppsMenu, true); registry->RegisterBooleanPref(prefs::kWebAppCreateInQuickLaunchBar, true); registry->RegisterBooleanPref( - prefs::kEnableTranslate, -- true, -+ false, +- prefs::kOfferTranslateEnabled, true, ++ prefs::kOfferTranslateEnabled, false, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); registry->RegisterStringPref(prefs::kCloudPrintEmail, std::string()); registry->RegisterBooleanPref(prefs::kCloudPrintProxyEnabled, true); @@ -60,7 +60,7 @@ void BackgroundModeManager::RegisterProfile(Profile* profile) { --- a/components/content_settings/core/browser/cookie_settings.cc +++ b/components/content_settings/core/browser/cookie_settings.cc -@@ -80,7 +80,7 @@ void CookieSettings::GetCookieSettings( +@@ -88,7 +88,7 @@ void CookieSettings::GetCookieSettings( void CookieSettings::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( @@ -82,7 +82,7 @@ --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc -@@ -281,7 +281,7 @@ void AutofillManager::RegisterProfilePre +@@ -217,7 +217,7 @@ void AutofillManager::RegisterProfilePre registry->RegisterIntegerPref( prefs::kAutofillCreditCardSigninPromoImpressionCount, 0); registry->RegisterBooleanPref( @@ -104,7 +104,7 @@ --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc -@@ -610,7 +610,7 @@ void IOThread::RegisterPrefs(PrefRegistr +@@ -612,7 +612,7 @@ void IOThread::RegisterPrefs(PrefRegistr std::string()); registry->RegisterBooleanPref(prefs::kEnableReferrers, true); data_reduction_proxy::RegisterPrefs(registry); @@ -112,10 +112,10 @@ + registry->RegisterBooleanPref(prefs::kBuiltInDnsClientEnabled, false); registry->RegisterBooleanPref(prefs::kQuickCheckEnabled, true); registry->RegisterBooleanPref(prefs::kPacHttpsUrlStrippingEnabled, true); - } + #if defined(OS_POSIX) --- a/chrome/browser/signin/signin_promo.cc +++ b/chrome/browser/signin/signin_promo.cc -@@ -328,8 +328,8 @@ void ForceWebBasedSigninFlowForTesting(b +@@ -390,8 +390,8 @@ void ForceWebBasedSigninFlowForTesting(b void RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterIntegerPref(prefs::kSignInPromoStartupCount, 0); @@ -197,7 +197,7 @@ // functionality that are only available in chrome://extensions/ but which --- a/components/safe_browsing/common/safe_browsing_prefs.cc +++ b/components/safe_browsing/common/safe_browsing_prefs.cc -@@ -370,9 +370,9 @@ void RegisterProfilePrefs(PrefRegistrySi +@@ -334,9 +334,9 @@ void RegisterProfilePrefs(PrefRegistrySi registry->RegisterBooleanPref( prefs::kSafeBrowsingSawInterstitialScoutReporting, false); registry->RegisterBooleanPref( @@ -211,7 +211,7 @@ false); --- a/components/password_manager/core/browser/password_manager.cc +++ b/components/password_manager/core/browser/password_manager.cc -@@ -207,10 +207,10 @@ PasswordFormManager* FindMatchedManager( +@@ -235,10 +235,10 @@ PasswordFormManager* FindMatchedManager( void PasswordManager::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( diff --git a/0007-disable-web-resource-service.patch b/0007-disable-web-resource-service.patch index eeb8088..1ea9c27 100644 --- a/0007-disable-web-resource-service.patch +++ b/0007-disable-web-resource-service.patch @@ -1,47 +1,18 @@ --- a/components/web_resource/web_resource_service.cc +++ b/components/web_resource/web_resource_service.cc -@@ -126,44 +126,7 @@ bool WebResourceService::GetFetchSchedul +@@ -126,6 +126,7 @@ bool WebResourceService::GetFetchSchedul // Initializes the fetching of data from the resource server. Data // load calls OnURLFetchComplete. void WebResourceService::StartFetch() { -- // Set to false so that next fetch can be scheduled after this fetch or -- // if we recieve notification that resource is allowed. -- fetch_scheduled_ = false; -- // Check whether fetching is allowed. -- if (!resource_request_allowed_notifier_->ResourceRequestsAllowed()) -- return; -- -- // First, put our next cache load on the MessageLoop. -- ScheduleFetch(cache_update_delay_ms_); -- -- // Set cache update time in preferences. -- prefs_->SetString(last_update_time_pref_name_, -- base::DoubleToString(base::Time::Now().ToDoubleT())); -- -- // If we are still fetching data, exit. -- if (in_fetch_) -- return; -- in_fetch_ = true; -- -- GURL web_resource_server = -- application_locale_.empty() -- ? web_resource_server_ -- : google_util::AppendGoogleLocaleParam(web_resource_server_, -- application_locale_); -- -- DVLOG(1) << "WebResourceService StartFetch " << web_resource_server; -- url_fetcher_ = net::URLFetcher::Create( -- web_resource_server, net::URLFetcher::GET, this, traffic_annotation_); -- data_use_measurement::DataUseUserData::AttachToFetcher( -- url_fetcher_.get(), -- data_use_measurement::DataUseUserData::WEB_RESOURCE_SERVICE); -- // Do not let url fetcher affect existing state in system context -- // (by setting cookies, for example). -- url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE | -- net::LOAD_DO_NOT_SEND_COOKIES | -- net::LOAD_DO_NOT_SAVE_COOKIES); -- url_fetcher_->SetRequestContext(request_context_.get()); -- url_fetcher_->Start(); ++#if 0 + // Set to false so that next fetch can be scheduled after this fetch or + // if we recieve notification that resource is allowed. + fetch_scheduled_ = false; +@@ -164,6 +165,8 @@ void WebResourceService::StartFetch() { + net::LOAD_DO_NOT_SAVE_COOKIES); + url_fetcher_->SetRequestContext(request_context_.get()); + url_fetcher_->Start(); ++#endif + in_fetch_ = false; } diff --git a/0008-restore-classic-ntp.patch b/0008-restore-classic-ntp.patch index ffd72b5..c3dcb87 100644 --- a/0008-restore-classic-ntp.patch +++ b/0008-restore-classic-ntp.patch @@ -1,24 +1,28 @@ --- a/chrome/browser/search/search.cc +++ b/chrome/browser/search/search.cc -@@ -170,17 +170,7 @@ struct NewTabURLDetails { - return NewTabURLDetails(local_url, NEW_TAB_URL_VALID); - - NewTabURLState state = IsValidNewTabURL(profile, search_provider_url); -- switch (state) { -- case NEW_TAB_URL_VALID: -- // We can use the search provider's page. -- return NewTabURLDetails(search_provider_url, state); -- case NEW_TAB_URL_INCOGNITO: -- // Incognito has its own New Tab. -- return NewTabURLDetails(GURL(), state); -- default: -- // Use the local New Tab otherwise. -- return NewTabURLDetails(local_url, state); -- } -+ return NewTabURLDetails(local_url, state); +@@ -154,22 +154,6 @@ struct NewTabURLDetails { + GetDefaultSearchProviderTemplateURL(profile); + if (!profile || !template_url) + return NewTabURLDetails(local_url, NEW_TAB_URL_BAD); +- +- GURL search_provider_url(template_url->new_tab_url_ref().ReplaceSearchTerms( +- TemplateURLRef::SearchTermsArgs(base::string16()), +- UIThreadSearchTermsData(profile))); +- +- if (ShouldDelayRemoteNTP(search_provider_url, profile)) +- return NewTabURLDetails(local_url, NEW_TAB_URL_VALID); +- +- if (!search_provider_url.is_valid()) +- return NewTabURLDetails(local_url, NEW_TAB_URL_NOT_SET); +- if (!search_provider_url.SchemeIsCryptographic()) +- return NewTabURLDetails(local_url, NEW_TAB_URL_INSECURE); +- if (!IsURLAllowedForSupervisedUser(search_provider_url, profile)) +- return NewTabURLDetails(local_url, NEW_TAB_URL_BLOCKED); +- +- return NewTabURLDetails(search_provider_url, NEW_TAB_URL_VALID); } - GURL url; + const GURL url; --- a/components/ntp_snippets/features.cc +++ b/components/ntp_snippets/features.cc @@ -38,16 +38,16 @@ const base::Feature* const kAllFeatures[ diff --git a/0012-branding.patch b/0012-branding.patch index fa47f91..47cf085 100644 --- a/0012-branding.patch +++ b/0012-branding.patch @@ -55,7 +55,7 @@ -@@ -278,7 +278,7 @@ If you update this file, be sure also to +@@ -275,7 +275,7 @@ If you update this file, be sure also to @@ -64,7 +64,7 @@ -@@ -608,7 +608,7 @@ Chromium is unable to recover your setti +@@ -609,7 +609,7 @@ Chromium is unable to recover your setti @@ -73,7 +73,7 @@ Update &Chromium -@@ -616,7 +616,7 @@ Chromium is unable to recover your setti +@@ -617,7 +617,7 @@ Chromium is unable to recover your setti @@ -82,7 +82,7 @@ Update &Chromium -@@ -633,7 +633,7 @@ Chromium is unable to recover your setti +@@ -634,7 +634,7 @@ Chromium is unable to recover your setti diff --git a/0014-disable-translation-lang-fetch.patch b/0014-disable-translation-lang-fetch.patch index df96d04..3f5fa56 100644 --- a/0014-disable-translation-lang-fetch.patch +++ b/0014-disable-translation-lang-fetch.patch @@ -1,6 +1,6 @@ --- a/components/translate/core/browser/translate_language_list.cc +++ b/components/translate/core/browser/translate_language_list.cc -@@ -206,6 +206,8 @@ GURL TranslateLanguageList::TranslateLan +@@ -207,6 +207,8 @@ GURL TranslateLanguageList::TranslateLan } void TranslateLanguageList::RequestLanguageList() { @@ -31,7 +31,7 @@ } --- a/components/translate/core/browser/translate_ranker_impl.cc +++ b/components/translate/core/browser/translate_ranker_impl.cc -@@ -148,14 +148,10 @@ TranslateRankerImpl::TranslateRankerImpl +@@ -150,14 +150,10 @@ TranslateRankerImpl::TranslateRankerImpl ukm::UkmRecorder* ukm_recorder) : ukm_recorder_(ukm_recorder), is_logging_enabled_(false), @@ -49,8 +49,8 @@ + is_previous_language_matches_override_enabled_(false), weak_ptr_factory_(this) { if (is_query_enabled_ || is_enforcement_enabled_) { - model_loader_ = base::MakeUnique( -@@ -229,6 +225,8 @@ bool TranslateRankerImpl::ShouldOfferTra + model_loader_ = base::MakeUnique( +@@ -231,6 +227,8 @@ bool TranslateRankerImpl::ShouldOfferTra // (or become False). const bool kDefaultResponse = true; diff --git a/0016-chromium-sandbox-pie.patch b/0016-chromium-sandbox-pie.patch index a3dbdd1..7162c2d 100644 --- a/0016-chromium-sandbox-pie.patch +++ b/0016-chromium-sandbox-pie.patch @@ -1,6 +1,6 @@ --- a/sandbox/linux/BUILD.gn +++ b/sandbox/linux/BUILD.gn -@@ -313,6 +313,12 @@ if (is_linux) { +@@ -315,6 +315,12 @@ if (is_linux) { # These files have a suspicious comparison. # TODO fix this and re-enable this warning. "-Wno-sign-compare", diff --git a/0017-disable-new-avatar-menu.patch b/0017-disable-new-avatar-menu.patch index 5197f9e..52f6bf9 100644 --- a/0017-disable-new-avatar-menu.patch +++ b/0017-disable-new-avatar-menu.patch @@ -1,6 +1,6 @@ --- a/components/signin/core/browser/signin_manager.cc +++ b/components/signin/core/browser/signin_manager.cc -@@ -289,7 +289,7 @@ void SigninManager::OnGoogleServicesUser +@@ -299,7 +299,7 @@ void SigninManager::OnGoogleServicesUser } bool SigninManager::IsSigninAllowed() const { diff --git a/9000-disable-metrics.patch b/9000-disable-metrics.patch index 158d4b6..b3adcfd 100644 --- a/9000-disable-metrics.patch +++ b/9000-disable-metrics.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc -@@ -1215,6 +1215,11 @@ const FeatureEntry::FeatureVariation kSp +@@ -1211,6 +1211,11 @@ const FeatureEntry::FeatureVariation kSp // // When adding a new choice, add it to the end of the list. const FeatureEntry kFeatureEntries[] = { @@ -14,7 +14,7 @@ SINGLE_VALUE_TYPE(switches::kIgnoreGpuBlacklist)}, --- a/base/metrics/histogram_base.h +++ b/base/metrics/histogram_base.h -@@ -84,6 +84,12 @@ BASE_EXPORT HistogramBase* DeserializeHi +@@ -94,6 +94,12 @@ BASE_EXPORT HistogramBase* DeserializeHi class BASE_EXPORT HistogramBase { public: @@ -29,15 +29,15 @@ typedef int32_t Count; // Used to manipulate counts in temporaries. --- a/base/metrics/histogram_base.cc +++ b/base/metrics/histogram_base.cc -@@ -9,6 +9,7 @@ - #include +@@ -10,6 +10,7 @@ + #include #include +#include "base/command_line.h" #include "base/json/json_string_value_serializer.h" + #include "base/lazy_instance.h" #include "base/logging.h" - #include "base/metrics/histogram.h" -@@ -60,6 +61,12 @@ HistogramBase* DeserializeHistogramInfo( +@@ -64,6 +65,12 @@ HistogramBase* DeserializeHistogramInfo( } } @@ -48,8 +48,8 @@ +} + const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX; - HistogramBase* HistogramBase::report_histogram_ = nullptr; + HistogramBase::HistogramBase(const char* name) --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc @@ -124,7 +124,8 @@ class Histogram::Factory { @@ -91,8 +91,8 @@ minimum_, maximum_, registered_ranges, -@@ -232,6 +238,10 @@ HistogramBase* Histogram::Factory::Build - ReportHistogramActivity(*histogram, HISTOGRAM_LOOKUP); +@@ -226,6 +232,10 @@ HistogramBase* Histogram::Factory::Build + } } + if (!enabled_) @@ -102,7 +102,7 @@ CHECK_EQ(histogram_type_, histogram->GetHistogramType()) << name_; if (bucket_count_ != 0 && !histogram->HasConstructionArguments(minimum_, maximum_, bucket_count_)) { -@@ -468,10 +478,16 @@ bool Histogram::HasConstructionArguments +@@ -462,10 +472,16 @@ bool Histogram::HasConstructionArguments } void Histogram::Add(int value) { @@ -119,7 +119,7 @@ DCHECK_EQ(0, ranges(0)); DCHECK_EQ(kSampleType_MAX, ranges(bucket_count())); -@@ -526,10 +542,16 @@ std::unique_ptr Histog +@@ -520,10 +536,16 @@ std::unique_ptr Histog } void Histogram::AddSamples(const HistogramSamples& samples) { @@ -165,13 +165,13 @@ DCHECK(!histogram_ref); // Should never have been set. DCHECK(!allocator); // Shouldn't have failed. flags &= ~HistogramBase::kIsPersistent; -- tentative_histogram.reset(new SparseHistogram(name)); -+ tentative_histogram.reset(new SparseHistogram(overridden_name)); +- tentative_histogram.reset(new SparseHistogram(GetPermanentName(name))); ++ tentative_histogram.reset(new SparseHistogram(GetPermanentName(overridden_name))); tentative_histogram->SetFlags(flags); } -@@ -68,6 +73,10 @@ HistogramBase* SparseHistogram::FactoryG - ReportHistogramActivity(*histogram, HISTOGRAM_LOOKUP); +@@ -64,6 +69,10 @@ HistogramBase* SparseHistogram::FactoryG + } } + if (!enabled_) @@ -181,7 +181,7 @@ CHECK_EQ(SPARSE_HISTOGRAM, histogram->GetHistogramType()); return histogram; } -@@ -101,10 +110,16 @@ bool SparseHistogram::HasConstructionArg +@@ -97,10 +106,16 @@ bool SparseHistogram::HasConstructionArg } void SparseHistogram::Add(Sample value) { @@ -198,7 +198,7 @@ if (count <= 0) { NOTREACHED(); return; -@@ -150,11 +165,17 @@ std::unique_ptr Sparse +@@ -146,11 +161,17 @@ std::unique_ptr Sparse } void SparseHistogram::AddSamples(const HistogramSamples& samples) { @@ -268,7 +268,7 @@ #include "base/location.h" #include "base/memory/ptr_util.h" #include "base/metrics/histogram_base.h" -@@ -809,6 +810,8 @@ bool MetricsService::UmaMetricsProperlyS +@@ -802,6 +803,8 @@ bool MetricsService::UmaMetricsProperlyS void MetricsService::RegisterMetricsProvider( std::unique_ptr provider) { DCHECK_EQ(INITIALIZED, state_); @@ -279,7 +279,20 @@ --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc -@@ -215,13 +215,17 @@ void TabHelpers::AttachTabHelpers(WebCon +@@ -204,8 +204,10 @@ void TabHelpers::AttachTabHelpers(WebCon + ChromeTranslateClient::CreateForWebContents(web_contents); + ClientHintsObserver::CreateForWebContents(web_contents); + CoreTabHelper::CreateForWebContents(web_contents); +- data_use_measurement::DataUseWebContentsObserver::CreateForWebContents( +- web_contents); ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch("enable-metrics")) { ++ data_use_measurement::DataUseWebContentsObserver::CreateForWebContents( ++ web_contents); ++ } + ExternalProtocolObserver::CreateForWebContents(web_contents); + favicon::CreateContentFaviconDriverForWebContents(web_contents); + FindTabHelper::CreateForWebContents(web_contents); +@@ -220,14 +222,20 @@ void TabHelpers::AttachTabHelpers(WebCon HistoryTabHelper::CreateForWebContents(web_contents); InfoBarService::CreateForWebContents(web_contents); InstallableManager::CreateForWebContents(web_contents); @@ -288,20 +301,34 @@ + if (base::CommandLine::ForCurrentProcess()->HasSwitch("enable-metrics")) { + metrics::RendererUptimeWebContentsObserver::CreateForWebContents( + web_contents); -+ } ++ }; if (content::IsBrowserSideNavigationEnabled()) MixedContentSettingsTabHelper::CreateForWebContents(web_contents); NavigationCorrectionTabObserver::CreateForWebContents(web_contents); - NavigationMetricsRecorder::CreateForWebContents(web_contents); -- chrome::InitializePageLoadMetricsForWebContents(web_contents); + if (base::CommandLine::ForCurrentProcess()->HasSwitch("enable-metrics")) { + NavigationMetricsRecorder::CreateForWebContents(web_contents); ++ }; + OutOfMemoryReporter::CreateForWebContents(web_contents); +- chrome::InitializePageLoadMetricsForWebContents(web_contents); ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch("enable-metrics")) { + chrome::InitializePageLoadMetricsForWebContents(web_contents); + } PDFPluginPlaceholderObserver::CreateForWebContents(web_contents); PermissionRequestManager::CreateForWebContents(web_contents); - PopupBlockerTabHelper::CreateForWebContents(web_contents); -@@ -285,7 +289,8 @@ void TabHelpers::AttachTabHelpers(WebCon + // The PopupBlockerTabHelper has an implicit dependency on +@@ -252,7 +260,9 @@ void TabHelpers::AttachTabHelpers(WebCon + // is taken over by ChromeContentSettingsClient. http://crbug.com/387075 + TabSpecificContentSettings::CreateForWebContents(web_contents); + TabUIHelper::CreateForWebContents(web_contents); +- ukm::InitializeSourceUrlRecorderForWebContents(web_contents); ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch("enable-metrics")) { ++ ukm::InitializeSourceUrlRecorderForWebContents(web_contents); ++ } + vr::VrTabHelper::CreateForWebContents(web_contents); + + // NO! Do not just add your tab helper here. This is a large alphabetized +@@ -302,7 +312,8 @@ void TabHelpers::AttachTabHelpers(WebCon #if defined(OS_WIN) || defined(OS_MACOSX) || \ (defined(OS_LINUX) && !defined(OS_CHROMEOS)) diff --git a/PKGBUILD b/PKGBUILD index 691d776..c086d2c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -83,9 +83,11 @@ sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' 'd6fdcb922e5a7fbe15759d39ccc8ea4225821c44d98054ce0f23f9d1f00c9808' '4495e8b29dae242c79ffe4beefc5171eb3c7aacb7e9aebfd2d4d69b9d8c958d3' 'f6227987c30f8b8a1e0cb5f3863698543a890e6f4bd20ff9384735e1122e66da' - '73275413f078b1217a11e5a099777c1ace11a667144d5106975d1ff650540321' - 'a15b2ca40b5ca17d4763e41e226fb5faca22277027e8321675c87038dd9879d5' - '22726f4f16c7ac9d0c1afc47d6aa40bc02f6de42cfa86a9153781e1d50b58181' + '1336c9a790c0bd7fa8cc00d0c58d6f6374cc311beb1a9db0a1696f4ddb21bfde' + '8a81a14af625c8b79006d1b9b4321d5487bc2e56a3fb3a677f9a8dab369be7af' + '0a9186ab591773f8fb6cbc908f9bbf4bc1508f1095b6c1cd7479aac945045373' + 'b82047df666e6bbf66e0c0911d20c5001bd1100fd08adafa92cac5f02a887a01' + 'd1e112adb135a823907aae33b189cb775d48e6afa785a26a452fc833824cd2e8' '605cca8be9828a29cc96d473847eef9452d572fe6a56dacd96426a202310ba58' 'fb91a7e30e2615e4eb0626b0fdcf97b92d4a727a52023730f408b02fee436c8d' '94d20bc91ce6f4c9405293b4480670af9e7c3a79f2b87268e663dc2f063cb6e4' @@ -95,12 +97,12 @@ sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' 'cf050473adae5b83680346b369997b5ead55dce282515f185e4096c5ed39f11d' '3190a507dfa00e863a0e622b5738db5cf19947f696ac7a790f427510cc15d1e1' '476593cf1e3bbf2539732567a70b0acea14033370317baf868f3d9701e4a1d5d' - '6f0768e13f2218597f7c39f4398381934333ec302756147e488defa01cbb1c4c' + '0b7332739e7f5eabb54213449fabed35e98d46c334a9e15398582659755a89c3' 'c79f12e444d2c7b9b61de8d6698033cc8a84bb35f949908b3a366105367237b0' - 'bfb04c0c51b7f48e01ac514c5dfe26b3a93ffc2aa517f846c01d6f2668247a38' + 'f80106b8127b60a62c006653154a26ebe68dd4aec5c551bae5321fa4e5ccef3f' '795686bf0dd7bfac0f596155be8fc7ed3f6294a6b764f793cd1614085562ce38' - '216829c72f1cc378bc66fb4f62f047cccd31684d946ba9a406b6e7a8f1351677' - '438a53a389a39568038cd353b5a8cd07d6e4e28277326ae64cd7ecce7caf93fa' + '5dc10c49cfc3ea65505e07366420eda0fc4878d0b0cebbfbcd8ad7daa88b3ded' + 'a1a5cb2c68abb02e7cdad3108a5a4d00beac86ae9341df98eb20495fcc400d45' 'cb2bd17fbbd9184f15eb24d3b23deca92d06cb4b9ec31bd6944504e130d69ff8' 'c17556772059a64873ddac383f2976e3befb5c07c3019b641c989ffb5683c4cd' '80d2974001708c288a54c24e1dc896ef25916552b740765f6066a244c05ffcd5' @@ -287,6 +289,7 @@ build() { 'linux_use_bundled_binutils=false' 'use_custom_libcxx=false' 'use_system_libjpeg=true' + 'use_system_libpng=true' 'use_vaapi=true' 'enable_hangout_services_extension=false' 'enable_widevine=true' @@ -296,7 +299,6 @@ build() { 'enable_remoting=false' 'enable_google_now=false' 'safe_browsing_mode=0' - 'enable_hotwording=false' ) if check_option strip y; then From 2dc58dc21dc0ecd4677b08704a55db0f43128b8b Mon Sep 17 00:00:00 2001 From: xsmile <> Date: Sat, 20 Jan 2018 18:38:04 +0100 Subject: [PATCH 4/9] clean up 0008-restore-classic-ntp.patch --- 0008-restore-classic-ntp.patch | 16 ++++++++++++---- PKGBUILD | 20 +++++++++----------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/0008-restore-classic-ntp.patch b/0008-restore-classic-ntp.patch index c3dcb87..cefdb67 100644 --- a/0008-restore-classic-ntp.patch +++ b/0008-restore-classic-ntp.patch @@ -1,9 +1,16 @@ --- a/chrome/browser/search/search.cc +++ b/chrome/browser/search/search.cc -@@ -154,22 +154,6 @@ struct NewTabURLDetails { - GetDefaultSearchProviderTemplateURL(profile); - if (!profile || !template_url) - return NewTabURLDetails(local_url, NEW_TAB_URL_BAD); +@@ -147,29 +147,7 @@ struct NewTabURLDetails { + + const GURL local_url(chrome::kChromeSearchLocalNtpUrl); + +- if (ShouldShowLocalNewTab(profile)) +- return NewTabURLDetails(local_url, NEW_TAB_URL_VALID); +- +- const TemplateURL* template_url = +- GetDefaultSearchProviderTemplateURL(profile); +- if (!profile || !template_url) +- return NewTabURLDetails(local_url, NEW_TAB_URL_BAD); - - GURL search_provider_url(template_url->new_tab_url_ref().ReplaceSearchTerms( - TemplateURLRef::SearchTermsArgs(base::string16()), @@ -20,6 +27,7 @@ - return NewTabURLDetails(local_url, NEW_TAB_URL_BLOCKED); - - return NewTabURLDetails(search_provider_url, NEW_TAB_URL_VALID); ++ return NewTabURLDetails(local_url, NEW_TAB_URL_VALID); } const GURL url; diff --git a/PKGBUILD b/PKGBUILD index c086d2c..26ad228 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -83,17 +83,15 @@ sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' 'd6fdcb922e5a7fbe15759d39ccc8ea4225821c44d98054ce0f23f9d1f00c9808' '4495e8b29dae242c79ffe4beefc5171eb3c7aacb7e9aebfd2d4d69b9d8c958d3' 'f6227987c30f8b8a1e0cb5f3863698543a890e6f4bd20ff9384735e1122e66da' - '1336c9a790c0bd7fa8cc00d0c58d6f6374cc311beb1a9db0a1696f4ddb21bfde' - '8a81a14af625c8b79006d1b9b4321d5487bc2e56a3fb3a677f9a8dab369be7af' - '0a9186ab591773f8fb6cbc908f9bbf4bc1508f1095b6c1cd7479aac945045373' - 'b82047df666e6bbf66e0c0911d20c5001bd1100fd08adafa92cac5f02a887a01' - 'd1e112adb135a823907aae33b189cb775d48e6afa785a26a452fc833824cd2e8' + '73275413f078b1217a11e5a099777c1ace11a667144d5106975d1ff650540321' + 'a15b2ca40b5ca17d4763e41e226fb5faca22277027e8321675c87038dd9879d5' + '3805b5fc088f716a659095e652c313b1b8548ad622cda5ef4290c04e20895d97' '605cca8be9828a29cc96d473847eef9452d572fe6a56dacd96426a202310ba58' 'fb91a7e30e2615e4eb0626b0fdcf97b92d4a727a52023730f408b02fee436c8d' - '94d20bc91ce6f4c9405293b4480670af9e7c3a79f2b87268e663dc2f063cb6e4' - '08a1dcbb5ae2cb38815026f2f723da383f4275c58dc992078e6c7b006c651ec1' - 'bd9194b0a1da60879ce36ac389da6b229be9be5ae6acfba04e3cb0e1cb15ea9f' - 'e20a71da88b78dcd0d5b490f66ecebcca4aa2e0d7b22df453c7adc90c1da2c02' + '6ba0ad7d91b2f3bbf03fc4a3236a04310a0c57505e1688c7e11ace9dcea1dded' + '2e8ba84204840f5f57b68456a70895c7ab07286efb4b165911e3f0c8072ded62' + '7781ecd43e3c28f7d1e9158e043d6f98a190b5ee3c2c5ebe91644ea27e0b42ee' + 'daf9dcbeed8c6cd5d0012086680e8ee252121f56d96624d920d5b46d300a4052' 'cf050473adae5b83680346b369997b5ead55dce282515f185e4096c5ed39f11d' '3190a507dfa00e863a0e622b5738db5cf19947f696ac7a790f427510cc15d1e1' '476593cf1e3bbf2539732567a70b0acea14033370317baf868f3d9701e4a1d5d' @@ -102,12 +100,12 @@ sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' 'f80106b8127b60a62c006653154a26ebe68dd4aec5c551bae5321fa4e5ccef3f' '795686bf0dd7bfac0f596155be8fc7ed3f6294a6b764f793cd1614085562ce38' '5dc10c49cfc3ea65505e07366420eda0fc4878d0b0cebbfbcd8ad7daa88b3ded' - 'a1a5cb2c68abb02e7cdad3108a5a4d00beac86ae9341df98eb20495fcc400d45' + '023d8150ead7c8611621f300719d75106816d683a3e00be41e483254ce4ebadd' 'cb2bd17fbbd9184f15eb24d3b23deca92d06cb4b9ec31bd6944504e130d69ff8' 'c17556772059a64873ddac383f2976e3befb5c07c3019b641c989ffb5683c4cd' '80d2974001708c288a54c24e1dc896ef25916552b740765f6066a244c05ffcd5' 'dbe942b1eaba525ca6b81d398462a70360fc2043cbfe5d4105657c3bd721e592' - '42425ef9f374020ce583225ad4ac5201aa0d7d08b91a4ba2b52c0ea5ffb153b7') + '52412cb1da3169f246bb99f1299c91e1da3c14ca23876475918f534bb887f8c4') # Possible replacements are listed in build/linux/unbundle/replace_gn_files.py # Keys are the names in the above script; values are the dependencies in Arch From 389d6eaf59295b6803f5c24d27f1f994b5da4bc9 Mon Sep 17 00:00:00 2001 From: xsmile <> Date: Fri, 26 Jan 2018 18:53:27 +0100 Subject: [PATCH 5/9] update VA-API patches --- PKGBUILD | 22 +- chromium-vaapi-init.patch | 223 + chromium-vaapi-move.patch | 15235 ++++++++++++++++ ...aapi-r15.patch => chromium-vaapi-r16.patch | 236 +- chromium-vaapi-rgbx.patch | 35 + 5 files changed, 15573 insertions(+), 178 deletions(-) create mode 100644 chromium-vaapi-init.patch create mode 100644 chromium-vaapi-move.patch rename chromium-vaapi-r15.patch => chromium-vaapi-r16.patch (76%) create mode 100644 chromium-vaapi-rgbx.patch diff --git a/PKGBUILD b/PKGBUILD index 26ad228..57a146e 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -39,8 +39,10 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-clang-r2.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-memcpy-r0.patch # Misc - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-libva-r2.patch - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-r15.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-move.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-init.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-rgbx.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-r16.patch # Inox patchset https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0001-fix-building-without-safebrowsing.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0003-disable-autofill-download-manager.patch @@ -83,9 +85,11 @@ sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' 'd6fdcb922e5a7fbe15759d39ccc8ea4225821c44d98054ce0f23f9d1f00c9808' '4495e8b29dae242c79ffe4beefc5171eb3c7aacb7e9aebfd2d4d69b9d8c958d3' 'f6227987c30f8b8a1e0cb5f3863698543a890e6f4bd20ff9384735e1122e66da' - '73275413f078b1217a11e5a099777c1ace11a667144d5106975d1ff650540321' - 'a15b2ca40b5ca17d4763e41e226fb5faca22277027e8321675c87038dd9879d5' - '3805b5fc088f716a659095e652c313b1b8548ad622cda5ef4290c04e20895d97' + '1336c9a790c0bd7fa8cc00d0c58d6f6374cc311beb1a9db0a1696f4ddb21bfde' + '8a81a14af625c8b79006d1b9b4321d5487bc2e56a3fb3a677f9a8dab369be7af' + '0a9186ab591773f8fb6cbc908f9bbf4bc1508f1095b6c1cd7479aac945045373' + 'b82047df666e6bbf66e0c0911d20c5001bd1100fd08adafa92cac5f02a887a01' + 'd1e112adb135a823907aae33b189cb775d48e6afa785a26a452fc833824cd2e8' '605cca8be9828a29cc96d473847eef9452d572fe6a56dacd96426a202310ba58' 'fb91a7e30e2615e4eb0626b0fdcf97b92d4a727a52023730f408b02fee436c8d' '6ba0ad7d91b2f3bbf03fc4a3236a04310a0c57505e1688c7e11ace9dcea1dded' @@ -100,7 +104,7 @@ sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' 'f80106b8127b60a62c006653154a26ebe68dd4aec5c551bae5321fa4e5ccef3f' '795686bf0dd7bfac0f596155be8fc7ed3f6294a6b764f793cd1614085562ce38' '5dc10c49cfc3ea65505e07366420eda0fc4878d0b0cebbfbcd8ad7daa88b3ded' - '023d8150ead7c8611621f300719d75106816d683a3e00be41e483254ce4ebadd' + 'a1a5cb2c68abb02e7cdad3108a5a4d00beac86ae9341df98eb20495fcc400d45' 'cb2bd17fbbd9184f15eb24d3b23deca92d06cb4b9ec31bd6944504e130d69ff8' 'c17556772059a64873ddac383f2976e3befb5c07c3019b641c989ffb5683c4cd' '80d2974001708c288a54c24e1dc896ef25916552b740765f6066a244c05ffcd5' @@ -181,8 +185,10 @@ prepare() { build/config/compiler/BUILD.gn msg2 'Applying VA-API patches' - patch -Np1 -i ../chromium-libva-r2.patch - patch -Np1 -i ../chromium-vaapi-r15.patch + patch -Np1 -i ../chromium-vaapi-move.patch + patch -Np1 -i ../chromium-vaapi-init.patch + patch -Np1 -i ../chromium-vaapi-rgbx.patch + patch -Np1 -i ../chromium-vaapi-r16.patch msg2 'Applying Inox patchset' # Apply patches to fix building diff --git a/chromium-vaapi-init.patch b/chromium-vaapi-init.patch new file mode 100644 index 0000000..0cd4557 --- /dev/null +++ b/chromium-vaapi-init.patch @@ -0,0 +1,223 @@ +From a57aa298ed3c3b76056d6e24fb09db05dca2f465 Mon Sep 17 00:00:00 2001 +From: Daniel Charles +Date: Thu, 25 Jan 2018 13:01:37 -0800 +Subject: [PATCH] vaapi initialization: move it to vaapi_wrapper + +vaapi loading of libraries happens on the Pre and Post Sandbox +functions. Moving dynamic loading of libaries, i.e. libva,libva-drm +and i965_drv_video shared libraries to vaapi_wrapper. + +When calling PreSandbox function in vaapi_wrapper libva will open +i965_drv_video shared library and both will be available for use + +BUG=chromium:785117 +TEST="video initialization of h/w dec/enc, VAVDA/VAVEA/VAJDA subjective" +TEST="testing and include unittests and autotests" + +Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel +Change-Id: I862bb49f1167d7437e80387882cb9081ad53f54b +Signed-off-by: Daniel Charles +--- + +--- a/content/gpu/gpu_sandbox_hook_linux.cc ++++ b/content/gpu/gpu_sandbox_hook_linux.cc +@@ -28,10 +28,6 @@ + #include "services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h" + #include "services/service_manager/sandbox/linux/sandbox_linux.h" + +-#if BUILDFLAG(USE_VAAPI) +-#include +-#endif +- + using sandbox::bpf_dsl::Policy; + using sandbox::syscall_broker::BrokerFilePermission; + using sandbox::syscall_broker::BrokerProcess; +@@ -47,22 +43,6 @@ inline bool IsChromeOS() { + #endif + } + +-inline bool IsArchitectureX86_64() { +-#if defined(__x86_64__) +- return true; +-#else +- return false; +-#endif +-} +- +-inline bool IsArchitectureI386() { +-#if defined(__i386__) +- return true; +-#else +- return false; +-#endif +-} +- + inline bool IsArchitectureArm() { + #if defined(ARCH_CPU_ARM_FAMILY) + return true; +@@ -87,14 +67,6 @@ inline bool UseLibV4L2() { + #endif + } + +-inline bool IsLibVAVersion2() { +-#if BUILDFLAG(USE_VAAPI) && VA_MAJOR_VERSION == 1 +- return true; +-#else +- return false; +-#endif +-} +- + constexpr int dlopen_flag = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE; + + void AddV4L2GpuWhitelist( +@@ -259,51 +231,6 @@ void LoadV4L2Libraries() { + } + } + +-void LoadStandardLibraries( +- const service_manager::SandboxSeccompBPF::Options& options) { +- if (IsArchitectureX86_64() || IsArchitectureI386()) { +- // Accelerated video dlopen()'s some shared objects +- // inside the sandbox, so preload them now. +- if (options.vaapi_accelerated_video_encode_enabled || +- options.accelerated_video_decode_enabled) { +- if (IsLibVAVersion2()) { +- if (IsArchitectureX86_64()) { +- dlopen("/usr/lib64/va/drivers/i965_drv_video.so", dlopen_flag); +- dlopen("/usr/lib64/va/drivers/hybrid_drv_video.so", dlopen_flag); +- } else if (IsArchitectureI386()) { +- dlopen("/usr/lib/va/drivers/i965_drv_video.so", dlopen_flag); +- } +- dlopen("libva.so.2", dlopen_flag); +-#if defined(USE_OZONE) +- dlopen("libva-drm.so.2", dlopen_flag); +-#endif +- } else { +- // If we are linked against libva 1, we have two cases to handle: +- // - the sysroot includes both libva 1 and 2, in which case the drivers +- // are in /usr/lib{64}/va1/ +- // - the sysroot only includes libva 1, in which case the drivers are +- // are in /usr/lib{64}/va/ +- // This is ugly, but temporary until all builds have switched to libva 2. +- if (IsArchitectureX86_64()) { +- if (!dlopen("/usr/lib64/va1/drivers/i965_drv_video.so", dlopen_flag)) +- dlopen("/usr/lib64/va/drivers/i965_drv_video.so", dlopen_flag); +- if (!dlopen("/usr/lib64/va1/drivers/hybrid_drv_video.so", dlopen_flag)) +- dlopen("/usr/lib64/va/drivers/hybrid_drv_video.so", dlopen_flag); +- } else if (IsArchitectureI386()) { +- if (!dlopen("/usr/lib/va1/drivers/i965_drv_video.so", dlopen_flag)) +- dlopen("/usr/lib/va/drivers/i965_drv_video.so", dlopen_flag); +- } +- dlopen("libva.so.1", dlopen_flag); +-#if defined(USE_OZONE) +- dlopen("libva-drm.so.1", dlopen_flag); +-#elif defined(USE_X11) +- dlopen("libva-x11.so.1", dlopen_flag); +-#endif +- } +- } +- } +-} +- + bool LoadLibrariesForGpu( + const service_manager::SandboxSeccompBPF::Options& options) { + if (IsChromeOS()) { +@@ -316,7 +243,6 @@ bool LoadLibrariesForGpu( + if (options.use_amd_specific_policies) + return LoadAmdGpuLibraries(); + } +- LoadStandardLibraries(options); + return true; + } + +--- a/media/gpu/vaapi/vaapi_wrapper.cc ++++ b/media/gpu/vaapi/vaapi_wrapper.cc +@@ -163,9 +163,6 @@ class VADisplayState { + void SetDrmFd(base::PlatformFile fd) { drm_fd_.reset(HANDLE_EINTR(dup(fd))); } + + private: +- // Returns false on init failure. +- static bool PostSandboxInitialization(); +- + // Protected by |va_lock_|. + int refcount_; + +@@ -200,43 +197,12 @@ void VADisplayState::PreSandboxInitializ + VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile()); + } + +-// static +-bool VADisplayState::PostSandboxInitialization() { +- const std::string va_suffix(std::to_string(VA_MAJOR_VERSION + 1)); +- StubPathMap paths; +- +- paths[kModuleVa].push_back(std::string("libva.so.") + va_suffix); +- paths[kModuleVa_drm].push_back(std::string("libva-drm.so.") + va_suffix); +-#if defined(USE_X11) +- // libva-x11 does not exist on libva >= 2 +- if (VA_MAJOR_VERSION == 0) +- paths[kModuleVa_x11].push_back("libva-x11.so.1"); +-#endif +- +- const bool success = InitializeStubs(paths); +- if (!success) { +- static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; +-#if defined(OS_CHROMEOS) +- // When Chrome runs on Linux with target_os="chromeos", do not log error +- // message without VAAPI libraries. +- LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS()) << kErrorMsg; +-#else +- DVLOG(1) << kErrorMsg; +-#endif +- } +- return success; +-} +- + VADisplayState::VADisplayState() + : refcount_(0), va_display_(nullptr), va_initialized_(false) {} + + bool VADisplayState::Initialize() { + va_lock_.AssertAcquired(); + +- static bool result = PostSandboxInitialization(); +- if (!result) +- return false; +- + if (refcount_++ > 0) + return true; + +@@ -1367,6 +1333,34 @@ void VaapiWrapper::DeinitializeVpp() { + // static + void VaapiWrapper::PreSandboxInitialization() { + VADisplayState::PreSandboxInitialization(); ++ ++ const std::string va_suffix(std::to_string(VA_MAJOR_VERSION + 1)); ++ StubPathMap paths; ++ ++ paths[kModuleVa].push_back(std::string("libva.so.") + va_suffix); ++ paths[kModuleVa_drm].push_back(std::string("libva-drm.so.") + va_suffix); ++#if defined(USE_X11) ++ paths[kModuleVa_x11].push_back(std::string("libva-x11.so.") + va_suffix); ++#endif ++ ++ static bool result = InitializeStubs(paths); ++ if (!result) { ++ static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; ++#if defined(OS_CHROMEOS) ++ // When Chrome runs on Linux with target_os="chromeos", do not log error ++ // message without VAAPI libraries. ++ LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS()) << kErrorMsg; ++#else ++ DVLOG(1) << kErrorMsg; ++#endif ++ } ++ // next command will dlopen all necessary libraries for ++ // va-api to function properly, to know: ++ // libva.so ++ // i965_drv_video.so ++ // hybrid_drv_video.so (platforms that support it) ++ // libva-x11.so (X11) or libva-drm.so (Ozone). ++ VASupportedProfiles::Get(); + } + + } // namespace media diff --git a/chromium-vaapi-move.patch b/chromium-vaapi-move.patch new file mode 100644 index 0000000..1eb8fad --- /dev/null +++ b/chromium-vaapi-move.patch @@ -0,0 +1,15235 @@ +From 42e955c9b3030121dd945e103a39381a92221bdb Mon Sep 17 00:00:00 2001 +From: Miguel Casas +Date: Mon, 04 Dec 2017 14:18:12 +0000 +Subject: [PATCH] vaapi: move vaapi* files to //media/gpu/vaapi/ folder + +This CL moves the (remaining) vaapi related files from +//media/gpu to the existing//media/gpu/vaapi. + +Fully automatic: + +$ git mv media/gpu/vaapi_* media/gpu/vaapi/ +$ git mv media/gpu/va_s* media/gpu/vaapi/ +$ tools/git/mass-rename.p + +TBR= sadrul@chromium.org, avi@chromium.org for +the two first files that just get an include path udpate. + +Bug: none +Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel +Change-Id: Ia1dcbdef3695bae5879d0951fc78cf6dcef3325f +Reviewed-on: https://chromium-review.googlesource.com/801636 +Commit-Queue: Miguel Casas +Reviewed-by: Pawel Osciak +Reviewed-by: Dale Curtis +Cr-Commit-Position: refs/heads/master@{#521330} +--- + +--- a/components/viz/service/main/viz_main_impl.cc ++++ b/components/viz/service/main/viz_main_impl.cc +@@ -29,7 +29,7 @@ + #include "services/service_manager/public/cpp/connector.h" + + #if defined(OS_CHROMEOS) && BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #endif + + namespace { +--- a/content/gpu/gpu_main.cc ++++ b/content/gpu/gpu_main.cc +@@ -95,7 +95,7 @@ + #endif + + #if BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #endif + + namespace content { +--- a/media/gpu/BUILD.gn ++++ b/media/gpu/BUILD.gn +@@ -310,26 +310,26 @@ component("gpu") { + + if (use_vaapi) { + sources += [ +- "va_surface.cc", +- "va_surface.h", +- "vaapi/vaapi_picture_factory.cc", +- "vaapi/vaapi_picture_factory.h", ++ "vaapi/va_surface.cc", ++ "vaapi/va_surface.h", ++ "vaapi/vaapi_jpeg_decode_accelerator.cc", ++ "vaapi/vaapi_jpeg_decode_accelerator.h", ++ "vaapi/vaapi_jpeg_decoder.cc", ++ "vaapi/vaapi_jpeg_decoder.h", ++ "vaapi/vaapi_jpeg_encode_accelerator.cc", ++ "vaapi/vaapi_jpeg_encode_accelerator.h", ++ "vaapi/vaapi_jpeg_encoder.cc", ++ "vaapi/vaapi_jpeg_encoder.h", + "vaapi/vaapi_picture.cc", + "vaapi/vaapi_picture.h", +- "vaapi_jpeg_decode_accelerator.cc", +- "vaapi_jpeg_decode_accelerator.h", +- "vaapi_jpeg_decoder.cc", +- "vaapi_jpeg_decoder.h", +- "vaapi_jpeg_encode_accelerator.cc", +- "vaapi_jpeg_encode_accelerator.h", +- "vaapi_jpeg_encoder.cc", +- "vaapi_jpeg_encoder.h", +- "vaapi_video_decode_accelerator.cc", +- "vaapi_video_decode_accelerator.h", +- "vaapi_video_encode_accelerator.cc", +- "vaapi_video_encode_accelerator.h", +- "vaapi_wrapper.cc", +- "vaapi_wrapper.h", ++ "vaapi/vaapi_picture_factory.cc", ++ "vaapi/vaapi_picture_factory.h", ++ "vaapi/vaapi_video_decode_accelerator.cc", ++ "vaapi/vaapi_video_decode_accelerator.h", ++ "vaapi/vaapi_video_encode_accelerator.cc", ++ "vaapi/vaapi_video_encode_accelerator.h", ++ "vaapi/vaapi_wrapper.cc", ++ "vaapi/vaapi_wrapper.h", + ] + get_target_outputs(":libva_generate_stubs") + configs += [ "//third_party/libyuv:libyuv_config" ] + deps += [ +@@ -596,7 +596,7 @@ source_set("unit_tests") { + ] + + if (use_vaapi) { +- sources += [ "vaapi_video_decode_accelerator_unittest.cc" ] ++ sources += [ "vaapi/vaapi_video_decode_accelerator_unittest.cc" ] + deps += [ + ":gpu", + "//base/test:test_support", +--- a/media/gpu/gpu_jpeg_decode_accelerator_factory.cc ++++ b/media/gpu/gpu_jpeg_decode_accelerator_factory.cc +@@ -17,7 +17,7 @@ + #endif + + #if BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_jpeg_decode_accelerator.h" ++#include "media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h" + #endif + + #if defined(USE_V4L2_JDA) +--- a/media/gpu/gpu_video_decode_accelerator_factory.cc ++++ b/media/gpu/gpu_video_decode_accelerator_factory.cc +@@ -34,7 +34,7 @@ + #include "media/gpu/android/device_info.h" + #endif + #if BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_video_decode_accelerator.h" ++#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" + #include "ui/gl/gl_implementation.h" + #endif + +--- a/media/gpu/gpu_video_encode_accelerator_factory.cc ++++ b/media/gpu/gpu_video_encode_accelerator_factory.cc +@@ -24,7 +24,7 @@ + #include "media/gpu/media_foundation_video_encode_accelerator_win.h" + #endif + #if BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_video_encode_accelerator.h" ++#include "media/gpu/vaapi/vaapi_video_encode_accelerator.h" + #endif + + namespace media { +--- a/media/gpu/jpeg_decode_accelerator_unittest.cc ++++ b/media/gpu/jpeg_decode_accelerator_unittest.cc +@@ -35,7 +35,7 @@ + #include "ui/gfx/codec/jpeg_codec.h" + + #if BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #endif + + namespace media { +--- a/media/gpu/va_surface.cc ++++ /dev/null +@@ -1,24 +0,0 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/va_surface.h" +- +-namespace media { +- +-VASurface::VASurface(VASurfaceID va_surface_id, +- const gfx::Size& size, +- unsigned int format, +- const ReleaseCB& release_cb) +- : va_surface_id_(va_surface_id), +- size_(size), +- format_(format), +- release_cb_(release_cb) { +- DCHECK(!release_cb_.is_null()); +-} +- +-VASurface::~VASurface() { +- release_cb_.Run(va_surface_id_); +-} +- +-} // namespace media +--- a/media/gpu/va_surface.h ++++ /dev/null +@@ -1,117 +0,0 @@ +-// Copyright 2013 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +-// +-// This file contains the definition of VASurface class, used for decoding by +-// VaapiVideoDecodeAccelerator and VaapiH264Decoder. +- +-#ifndef MEDIA_GPU_VA_SURFACE_H_ +-#define MEDIA_GPU_VA_SURFACE_H_ +- +-#include +- +-#include "base/callback.h" +-#include "base/macros.h" +-#include "base/memory/ref_counted.h" +-#include "media/gpu/media_gpu_export.h" +-#include "ui/gfx/geometry/size.h" +- +-namespace media { +- +-// A VA-API-specific decode surface used by VaapiH264Decoder to decode into +-// and use as reference for decoding other surfaces. It is also handed by the +-// decoder to VaapiVideoDecodeAccelerator when the contents of the surface are +-// ready and should be displayed. VAVDA converts the surface contents into an +-// X/Drm Pixmap bound to a texture for display and releases its reference to it. +-// Decoder releases its references to the surface when it's done decoding and +-// using it as reference. Note that a surface may still be used for reference +-// after it's been sent to output and also after it is no longer used by VAVDA. +-// Thus, the surface can be in use by both VAVDA and the Decoder at the same +-// time, or by either of them, with the restriction that VAVDA will never get +-// the surface until the contents are ready, and it is guaranteed that the +-// contents will not change after that. +-// When both the decoder and VAVDA release their references to the surface, +-// it is freed and the release callback is executed to put the surface back +-// into available surfaces pool, which is managed externally. +-// +-// VASurfaceID is allocated in VaapiWrapper. +-// | +-// +----->| +-// | v +-// | VASurfaceID is put onto VaapiVideoDecodeAccelerator::available_va_surfaces_ +-// | | list. +-// | v +-// | VASurfaceID is taken off of the VVDA:available_va_surfaces_ when +-// | | VaapiH264Decoder requests more output surfaces, is wrapped into +-// | | a VASurface and passed to VaapiH264Decoder. +-// | v +-// | VASurface is put onto VaapiH264Decoder::available_va_surfaces_, keeping +-// | | the only reference to it until it's needed for decoding. +-// | v +-// | VaapiH264Decoder starts decoding a new frame. It takes a VASurface off of +-// | | VHD::available_va_surfaces_ and assigns it to a DecodeSurface, +-// | | which now keeps the only reference. +-// | v +-// | DecodeSurface is used for decoding, putting data into associated VASurface. +-// | | +-// | |--------------------------------------------------+ +-// | | | +-// | v v +-// | DecodeSurface is to be output. VaapiH264Decoder uses the +-// | VaapiH264Decoder passes the associated DecodeSurface and associated +-// | VASurface to VaapiVideoDecodeAccelerator, VASurface as reference for +-// | which stores it (taking a ref) on decoding more frames. +-// | pending_output_cbs_ queue until an output | +-// | VaapiPicture becomes available. v +-// | | Once the DecodeSurface is not +-// | | needed as reference anymore, +-// | v it is released, releasing the +-// | A VaapiPicture becomes available after associated VASurface reference. +-// | the client of VVDA returns | +-// | a PictureBuffer associated with it. VVDA | +-// | puts the contents of the VASurface into | +-// | it and releases the reference to VASurface. | +-// | | | +-// | '---------------------------------------' +-// | | +-// | v +-// | Neither VVDA nor VHD hold a reference to VASurface. VASurface is released, +-// | ReleaseCB gets called in its destructor, which puts the associated +-// | VASurfaceID back onto VVDA::available_va_surfaces_. +-// | | +-// '-------------------------------------| +-// | +-// v +-// VaapiWrapper frees VASurfaceID. +-// +-class MEDIA_GPU_EXPORT VASurface +- : public base::RefCountedThreadSafe { +- public: +- // Provided by user, will be called when all references to the surface +- // are released. +- using ReleaseCB = base::Callback; +- +- VASurface(VASurfaceID va_surface_id, +- const gfx::Size& size, +- unsigned int format, +- const ReleaseCB& release_cb); +- +- VASurfaceID id() const { return va_surface_id_; } +- const gfx::Size& size() const { return size_; } +- unsigned int format() const { return format_; } +- +- private: +- friend class base::RefCountedThreadSafe; +- ~VASurface(); +- +- const VASurfaceID va_surface_id_; +- const gfx::Size size_; +- const unsigned int format_; +- const ReleaseCB release_cb_; +- +- DISALLOW_COPY_AND_ASSIGN(VASurface); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VA_SURFACE_H_ +--- /dev/null ++++ b/media/gpu/vaapi/va_surface.cc +@@ -0,0 +1,24 @@ ++// Copyright 2017 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/va_surface.h" ++ ++namespace media { ++ ++VASurface::VASurface(VASurfaceID va_surface_id, ++ const gfx::Size& size, ++ unsigned int format, ++ const ReleaseCB& release_cb) ++ : va_surface_id_(va_surface_id), ++ size_(size), ++ format_(format), ++ release_cb_(release_cb) { ++ DCHECK(!release_cb_.is_null()); ++} ++ ++VASurface::~VASurface() { ++ release_cb_.Run(va_surface_id_); ++} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/va_surface.h +@@ -0,0 +1,117 @@ ++// Copyright 2013 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++// ++// This file contains the definition of VASurface class, used for decoding by ++// VaapiVideoDecodeAccelerator and VaapiH264Decoder. ++ ++#ifndef MEDIA_GPU_VAAPI_VA_SURFACE_H_ ++#define MEDIA_GPU_VAAPI_VA_SURFACE_H_ ++ ++#include ++ ++#include "base/callback.h" ++#include "base/macros.h" ++#include "base/memory/ref_counted.h" ++#include "media/gpu/media_gpu_export.h" ++#include "ui/gfx/geometry/size.h" ++ ++namespace media { ++ ++// A VA-API-specific decode surface used by VaapiH264Decoder to decode into ++// and use as reference for decoding other surfaces. It is also handed by the ++// decoder to VaapiVideoDecodeAccelerator when the contents of the surface are ++// ready and should be displayed. VAVDA converts the surface contents into an ++// X/Drm Pixmap bound to a texture for display and releases its reference to it. ++// Decoder releases its references to the surface when it's done decoding and ++// using it as reference. Note that a surface may still be used for reference ++// after it's been sent to output and also after it is no longer used by VAVDA. ++// Thus, the surface can be in use by both VAVDA and the Decoder at the same ++// time, or by either of them, with the restriction that VAVDA will never get ++// the surface until the contents are ready, and it is guaranteed that the ++// contents will not change after that. ++// When both the decoder and VAVDA release their references to the surface, ++// it is freed and the release callback is executed to put the surface back ++// into available surfaces pool, which is managed externally. ++// ++// VASurfaceID is allocated in VaapiWrapper. ++// | ++// +----->| ++// | v ++// | VASurfaceID is put onto VaapiVideoDecodeAccelerator::available_va_surfaces_ ++// | | list. ++// | v ++// | VASurfaceID is taken off of the VVDA:available_va_surfaces_ when ++// | | VaapiH264Decoder requests more output surfaces, is wrapped into ++// | | a VASurface and passed to VaapiH264Decoder. ++// | v ++// | VASurface is put onto VaapiH264Decoder::available_va_surfaces_, keeping ++// | | the only reference to it until it's needed for decoding. ++// | v ++// | VaapiH264Decoder starts decoding a new frame. It takes a VASurface off of ++// | | VHD::available_va_surfaces_ and assigns it to a DecodeSurface, ++// | | which now keeps the only reference. ++// | v ++// | DecodeSurface is used for decoding, putting data into associated VASurface. ++// | | ++// | |--------------------------------------------------+ ++// | | | ++// | v v ++// | DecodeSurface is to be output. VaapiH264Decoder uses the ++// | VaapiH264Decoder passes the associated DecodeSurface and associated ++// | VASurface to VaapiVideoDecodeAccelerator, VASurface as reference for ++// | which stores it (taking a ref) on decoding more frames. ++// | pending_output_cbs_ queue until an output | ++// | VaapiPicture becomes available. v ++// | | Once the DecodeSurface is not ++// | | needed as reference anymore, ++// | v it is released, releasing the ++// | A VaapiPicture becomes available after associated VASurface reference. ++// | the client of VVDA returns | ++// | a PictureBuffer associated with it. VVDA | ++// | puts the contents of the VASurface into | ++// | it and releases the reference to VASurface. | ++// | | | ++// | '---------------------------------------' ++// | | ++// | v ++// | Neither VVDA nor VHD hold a reference to VASurface. VASurface is released, ++// | ReleaseCB gets called in its destructor, which puts the associated ++// | VASurfaceID back onto VVDA::available_va_surfaces_. ++// | | ++// '-------------------------------------| ++// | ++// v ++// VaapiWrapper frees VASurfaceID. ++// ++class MEDIA_GPU_EXPORT VASurface ++ : public base::RefCountedThreadSafe { ++ public: ++ // Provided by user, will be called when all references to the surface ++ // are released. ++ using ReleaseCB = base::Callback; ++ ++ VASurface(VASurfaceID va_surface_id, ++ const gfx::Size& size, ++ unsigned int format, ++ const ReleaseCB& release_cb); ++ ++ VASurfaceID id() const { return va_surface_id_; } ++ const gfx::Size& size() const { return size_; } ++ unsigned int format() const { return format_; } ++ ++ private: ++ friend class base::RefCountedThreadSafe; ++ ~VASurface(); ++ ++ const VASurfaceID va_surface_id_; ++ const gfx::Size size_; ++ const unsigned int format_; ++ const ReleaseCB release_cb_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VASurface); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VA_SURFACE_H_ +--- a/media/gpu/vaapi/vaapi_drm_picture.cc ++++ b/media/gpu/vaapi/vaapi_drm_picture.cc +@@ -5,8 +5,8 @@ + #include "media/gpu/vaapi/vaapi_drm_picture.h" + + #include "base/file_descriptor_posix.h" +-#include "media/gpu/va_surface.h" +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/va_surface.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #include "ui/gfx/gpu_memory_buffer.h" + #include "ui/gfx/native_pixmap.h" + #include "ui/gl/gl_bindings.h" +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc +@@ -0,0 +1,325 @@ ++// Copyright 2015 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h" ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "base/bind.h" ++#include "base/logging.h" ++#include "base/metrics/histogram_macros.h" ++#include "base/threading/thread_task_runner_handle.h" ++#include "base/trace_event/trace_event.h" ++#include "gpu/ipc/service/gpu_channel.h" ++#include "media/base/video_frame.h" ++#include "media/filters/jpeg_parser.h" ++#include "media/gpu/shared_memory_region.h" ++#include "media/gpu/vaapi/vaapi_picture.h" ++#include "third_party/libyuv/include/libyuv.h" ++ ++#define VLOGF(level) VLOG(level) << __func__ << "(): " ++#define DVLOGF(level) DVLOG(level) << __func__ << "(): " ++ ++namespace media { ++ ++namespace { ++// UMA errors that the VaapiJpegDecodeAccelerator class reports. ++enum VAJDADecoderFailure { ++ VAAPI_ERROR = 0, ++ VAJDA_DECODER_FAILURES_MAX, ++}; ++ ++static void ReportToUMA(VAJDADecoderFailure failure) { ++ UMA_HISTOGRAM_ENUMERATION("Media.VAJDA.DecoderFailure", failure, ++ VAJDA_DECODER_FAILURES_MAX + 1); ++} ++ ++static unsigned int VaSurfaceFormatForJpeg( ++ const JpegFrameHeader& frame_header) { ++ // The range of sampling factor is [1, 4]. Pack them into integer to make the ++ // matching code simpler. For example, 0x211 means the sampling factor are 2, ++ // 1, 1 for 3 components. ++ unsigned int h = 0, v = 0; ++ for (int i = 0; i < frame_header.num_components; i++) { ++ DCHECK_LE(frame_header.components[i].horizontal_sampling_factor, 4); ++ DCHECK_LE(frame_header.components[i].vertical_sampling_factor, 4); ++ h = h << 4 | frame_header.components[i].horizontal_sampling_factor; ++ v = v << 4 | frame_header.components[i].vertical_sampling_factor; ++ } ++ ++ switch (frame_header.num_components) { ++ case 1: // Grey image ++ return VA_RT_FORMAT_YUV400; ++ ++ case 3: // Y Cb Cr color image ++ // See https://en.wikipedia.org/wiki/Chroma_subsampling for the ++ // definition of these numbers. ++ if (h == 0x211 && v == 0x211) ++ return VA_RT_FORMAT_YUV420; ++ ++ if (h == 0x211 && v == 0x111) ++ return VA_RT_FORMAT_YUV422; ++ ++ if (h == 0x111 && v == 0x111) ++ return VA_RT_FORMAT_YUV444; ++ ++ if (h == 0x411 && v == 0x111) ++ return VA_RT_FORMAT_YUV411; ++ } ++ VLOGF(1) << "Unsupported sampling factor: num_components=" ++ << frame_header.num_components << ", h=" << std::hex << h ++ << ", v=" << v; ++ ++ return 0; ++} ++ ++} // namespace ++ ++VaapiJpegDecodeAccelerator::DecodeRequest::DecodeRequest( ++ int32_t bitstream_buffer_id, ++ std::unique_ptr shm, ++ const scoped_refptr& video_frame) ++ : bitstream_buffer_id(bitstream_buffer_id), ++ shm(std::move(shm)), ++ video_frame(video_frame) {} ++ ++VaapiJpegDecodeAccelerator::DecodeRequest::~DecodeRequest() {} ++ ++void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id, ++ Error error) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ VLOGF(1) << "Notifying of error " << error; ++ DCHECK(client_); ++ client_->NotifyError(bitstream_buffer_id, error); ++} ++ ++void VaapiJpegDecodeAccelerator::NotifyErrorFromDecoderThread( ++ int32_t bitstream_buffer_id, ++ Error error) { ++ DCHECK(decoder_task_runner_->BelongsToCurrentThread()); ++ task_runner_->PostTask(FROM_HERE, ++ base::Bind(&VaapiJpegDecodeAccelerator::NotifyError, ++ weak_this_, bitstream_buffer_id, error)); ++} ++ ++void VaapiJpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ client_->VideoFrameReady(bitstream_buffer_id); ++} ++ ++VaapiJpegDecodeAccelerator::VaapiJpegDecodeAccelerator( ++ const scoped_refptr& io_task_runner) ++ : task_runner_(base::ThreadTaskRunnerHandle::Get()), ++ io_task_runner_(io_task_runner), ++ decoder_thread_("VaapiJpegDecoderThread"), ++ va_surface_id_(VA_INVALID_SURFACE), ++ weak_this_factory_(this) { ++ weak_this_ = weak_this_factory_.GetWeakPtr(); ++} ++ ++VaapiJpegDecodeAccelerator::~VaapiJpegDecodeAccelerator() { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ VLOGF(2) << "Destroying VaapiJpegDecodeAccelerator"; ++ ++ weak_this_factory_.InvalidateWeakPtrs(); ++ decoder_thread_.Stop(); ++} ++ ++bool VaapiJpegDecodeAccelerator::Initialize(Client* client) { ++ VLOGF(2); ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ client_ = client; ++ ++ vaapi_wrapper_ = ++ VaapiWrapper::Create(VaapiWrapper::kDecode, VAProfileJPEGBaseline, ++ base::Bind(&ReportToUMA, VAAPI_ERROR)); ++ ++ if (!vaapi_wrapper_.get()) { ++ VLOGF(1) << "Failed initializing VAAPI"; ++ return false; ++ } ++ ++ if (!decoder_thread_.Start()) { ++ VLOGF(1) << "Failed to start decoding thread."; ++ return false; ++ } ++ decoder_task_runner_ = decoder_thread_.task_runner(); ++ ++ return true; ++} ++ ++bool VaapiJpegDecodeAccelerator::OutputPicture( ++ VASurfaceID va_surface_id, ++ int32_t input_buffer_id, ++ const scoped_refptr& video_frame) { ++ DCHECK(decoder_task_runner_->BelongsToCurrentThread()); ++ ++ TRACE_EVENT1("jpeg", "VaapiJpegDecodeAccelerator::OutputPicture", ++ "input_buffer_id", input_buffer_id); ++ ++ DVLOGF(4) << "Outputting VASurface " << va_surface_id ++ << " into video_frame associated with input buffer id " ++ << input_buffer_id; ++ ++ VAImage image; ++ VAImageFormat format; ++ const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); ++ memset(&image, 0, sizeof(image)); ++ memset(&format, 0, sizeof(format)); ++ format.fourcc = kI420Fourcc; ++ format.byte_order = VA_LSB_FIRST; ++ format.bits_per_pixel = 12; // 12 for I420 ++ ++ uint8_t* mem = nullptr; ++ gfx::Size coded_size = video_frame->coded_size(); ++ if (!vaapi_wrapper_->GetVaImage(va_surface_id, &format, coded_size, &image, ++ reinterpret_cast(&mem))) { ++ VLOGF(1) << "Cannot get VAImage"; ++ return false; ++ } ++ ++ // Copy image content from VAImage to VideoFrame. ++ // The component order of VAImage I420 are Y, U, and V. ++ DCHECK_EQ(image.num_planes, 3u); ++ DCHECK_GE(image.width, coded_size.width()); ++ DCHECK_GE(image.height, coded_size.height()); ++ const uint8_t* src_y = mem + image.offsets[0]; ++ const uint8_t* src_u = mem + image.offsets[1]; ++ const uint8_t* src_v = mem + image.offsets[2]; ++ size_t src_y_stride = image.pitches[0]; ++ size_t src_u_stride = image.pitches[1]; ++ size_t src_v_stride = image.pitches[2]; ++ uint8_t* dst_y = video_frame->data(VideoFrame::kYPlane); ++ uint8_t* dst_u = video_frame->data(VideoFrame::kUPlane); ++ uint8_t* dst_v = video_frame->data(VideoFrame::kVPlane); ++ size_t dst_y_stride = video_frame->stride(VideoFrame::kYPlane); ++ size_t dst_u_stride = video_frame->stride(VideoFrame::kUPlane); ++ size_t dst_v_stride = video_frame->stride(VideoFrame::kVPlane); ++ ++ if (libyuv::I420Copy(src_y, src_y_stride, // Y ++ src_u, src_u_stride, // U ++ src_v, src_v_stride, // V ++ dst_y, dst_y_stride, // Y ++ dst_u, dst_u_stride, // U ++ dst_v, dst_v_stride, // V ++ coded_size.width(), coded_size.height())) { ++ VLOGF(1) << "I420Copy failed"; ++ return false; ++ } ++ ++ vaapi_wrapper_->ReturnVaImage(&image); ++ ++ task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::VideoFrameReady, ++ weak_this_, input_buffer_id)); ++ ++ return true; ++} ++ ++void VaapiJpegDecodeAccelerator::DecodeTask( ++ const std::unique_ptr& request) { ++ DVLOGF(4); ++ DCHECK(decoder_task_runner_->BelongsToCurrentThread()); ++ TRACE_EVENT0("jpeg", "DecodeTask"); ++ ++ JpegParseResult parse_result; ++ if (!ParseJpegPicture( ++ reinterpret_cast(request->shm->memory()), ++ request->shm->size(), &parse_result)) { ++ VLOGF(1) << "ParseJpegPicture failed"; ++ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, ++ PARSE_JPEG_FAILED); ++ return; ++ } ++ ++ unsigned int new_va_rt_format = ++ VaSurfaceFormatForJpeg(parse_result.frame_header); ++ if (!new_va_rt_format) { ++ VLOGF(1) << "Unsupported subsampling"; ++ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, ++ UNSUPPORTED_JPEG); ++ return; ++ } ++ ++ // Reuse VASurface if size doesn't change. ++ gfx::Size new_coded_size(parse_result.frame_header.coded_width, ++ parse_result.frame_header.coded_height); ++ if (new_coded_size != coded_size_ || va_surface_id_ == VA_INVALID_SURFACE || ++ new_va_rt_format != va_rt_format_) { ++ vaapi_wrapper_->DestroySurfaces(); ++ va_surface_id_ = VA_INVALID_SURFACE; ++ va_rt_format_ = new_va_rt_format; ++ ++ std::vector va_surfaces; ++ if (!vaapi_wrapper_->CreateSurfaces(va_rt_format_, new_coded_size, 1, ++ &va_surfaces)) { ++ VLOGF(1) << "Create VA surface failed"; ++ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, ++ PLATFORM_FAILURE); ++ return; ++ } ++ va_surface_id_ = va_surfaces[0]; ++ coded_size_ = new_coded_size; ++ } ++ ++ if (!VaapiJpegDecoder::Decode(vaapi_wrapper_.get(), parse_result, ++ va_surface_id_)) { ++ VLOGF(1) << "Decode JPEG failed"; ++ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, ++ PLATFORM_FAILURE); ++ return; ++ } ++ ++ if (!OutputPicture(va_surface_id_, request->bitstream_buffer_id, ++ request->video_frame)) { ++ VLOGF(1) << "Output picture failed"; ++ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, ++ PLATFORM_FAILURE); ++ return; ++ } ++} ++ ++void VaapiJpegDecodeAccelerator::Decode( ++ const BitstreamBuffer& bitstream_buffer, ++ const scoped_refptr& video_frame) { ++ DCHECK(io_task_runner_->BelongsToCurrentThread()); ++ TRACE_EVENT1("jpeg", "Decode", "input_id", bitstream_buffer.id()); ++ ++ DVLOGF(4) << "Mapping new input buffer id: " << bitstream_buffer.id() ++ << " size: " << bitstream_buffer.size(); ++ ++ // SharedMemoryRegion will take over the |bitstream_buffer.handle()|. ++ std::unique_ptr shm( ++ new SharedMemoryRegion(bitstream_buffer, true)); ++ ++ if (bitstream_buffer.id() < 0) { ++ VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); ++ NotifyErrorFromDecoderThread(bitstream_buffer.id(), INVALID_ARGUMENT); ++ return; ++ } ++ ++ if (!shm->Map()) { ++ VLOGF(1) << "Failed to map input buffer"; ++ NotifyErrorFromDecoderThread(bitstream_buffer.id(), UNREADABLE_INPUT); ++ return; ++ } ++ ++ std::unique_ptr request( ++ new DecodeRequest(bitstream_buffer.id(), std::move(shm), video_frame)); ++ ++ decoder_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::DecodeTask, ++ base::Unretained(this), base::Passed(&request))); ++} ++ ++bool VaapiJpegDecodeAccelerator::IsSupported() { ++ return VaapiWrapper::IsJpegDecodeSupported(); ++} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h +@@ -0,0 +1,121 @@ ++// Copyright 2015 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODE_ACCELERATOR_H_ ++#define MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODE_ACCELERATOR_H_ ++ ++#include ++ ++#include ++ ++#include "base/macros.h" ++#include "base/memory/linked_ptr.h" ++#include "base/memory/weak_ptr.h" ++#include "base/single_thread_task_runner.h" ++#include "base/synchronization/lock.h" ++#include "base/threading/thread.h" ++#include "media/base/bitstream_buffer.h" ++#include "media/gpu/media_gpu_export.h" ++#include "media/gpu/shared_memory_region.h" ++#include "media/gpu/vaapi/vaapi_jpeg_decoder.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++#include "media/video/jpeg_decode_accelerator.h" ++ ++namespace media { ++ ++// Class to provide JPEG decode acceleration for Intel systems with hardware ++// support for it, and on which libva is available. ++// Decoding tasks are performed in a separate decoding thread. ++// ++// Threading/life-cycle: this object is created & destroyed on the GPU ++// ChildThread. A few methods on it are called on the decoder thread which is ++// stopped during |this->Destroy()|, so any tasks posted to the decoder thread ++// can assume |*this| is still alive. See |weak_this_| below for more details. ++class MEDIA_GPU_EXPORT VaapiJpegDecodeAccelerator ++ : public JpegDecodeAccelerator { ++ public: ++ VaapiJpegDecodeAccelerator( ++ const scoped_refptr& io_task_runner); ++ ~VaapiJpegDecodeAccelerator() override; ++ ++ // JpegDecodeAccelerator implementation. ++ bool Initialize(JpegDecodeAccelerator::Client* client) override; ++ void Decode(const BitstreamBuffer& bitstream_buffer, ++ const scoped_refptr& video_frame) override; ++ bool IsSupported() override; ++ ++ private: ++ // An input buffer and the corresponding output video frame awaiting ++ // consumption, provided by the client. ++ struct DecodeRequest { ++ DecodeRequest(int32_t bitstream_buffer_id, ++ std::unique_ptr shm, ++ const scoped_refptr& video_frame); ++ ~DecodeRequest(); ++ ++ int32_t bitstream_buffer_id; ++ std::unique_ptr shm; ++ scoped_refptr video_frame; ++ }; ++ ++ // Notifies the client that an error has occurred and decoding cannot ++ // continue. ++ void NotifyError(int32_t bitstream_buffer_id, Error error); ++ void NotifyErrorFromDecoderThread(int32_t bitstream_buffer_id, Error error); ++ void VideoFrameReady(int32_t bitstream_buffer_id); ++ ++ // Processes one decode |request|. ++ void DecodeTask(const std::unique_ptr& request); ++ ++ // Puts contents of |va_surface| into given |video_frame|, releases the ++ // surface and passes the |input_buffer_id| of the resulting picture to ++ // client for output. ++ bool OutputPicture(VASurfaceID va_surface_id, ++ int32_t input_buffer_id, ++ const scoped_refptr& video_frame); ++ ++ // ChildThread's task runner. ++ scoped_refptr task_runner_; ++ ++ // GPU IO task runner. ++ scoped_refptr io_task_runner_; ++ ++ // The client of this class. ++ Client* client_; ++ ++ // WeakPtr<> pointing to |this| for use in posting tasks from the decoder ++ // thread back to the ChildThread. Because the decoder thread is a member of ++ // this class, any task running on the decoder thread is guaranteed that this ++ // object is still alive. As a result, tasks posted from ChildThread to ++ // decoder thread should use base::Unretained(this), and tasks posted from the ++ // decoder thread to the ChildThread should use |weak_this_|. ++ base::WeakPtr weak_this_; ++ ++ scoped_refptr vaapi_wrapper_; ++ ++ // Comes after vaapi_wrapper_ to ensure its destructor is executed before ++ // |vaapi_wrapper_| is destroyed. ++ std::unique_ptr decoder_; ++ base::Thread decoder_thread_; ++ // Use this to post tasks to |decoder_thread_| instead of ++ // |decoder_thread_.task_runner()| because the latter will be NULL once ++ // |decoder_thread_.Stop()| returns. ++ scoped_refptr decoder_task_runner_; ++ ++ // The current VA surface for decoding. ++ VASurfaceID va_surface_id_; ++ // The coded size associated with |va_surface_id_|. ++ gfx::Size coded_size_; ++ // The VA RT format associated with |va_surface_id_|. ++ unsigned int va_rt_format_; ++ ++ // The WeakPtrFactory for |weak_this_|. ++ base::WeakPtrFactory weak_this_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecodeAccelerator); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODE_ACCELERATOR_H_ +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_decoder.cc +@@ -0,0 +1,229 @@ ++// Copyright 2015 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_jpeg_decoder.h" ++ ++#include ++#include ++ ++#include "base/logging.h" ++#include "base/macros.h" ++#include "media/filters/jpeg_parser.h" ++ ++namespace media { ++ ++// VAAPI only support subset of JPEG profiles. This function determines a given ++// parsed JPEG result is supported or not. ++static bool IsVaapiSupportedJpeg(const JpegParseResult& jpeg) { ++ if (jpeg.frame_header.visible_width < 1 || ++ jpeg.frame_header.visible_height < 1) { ++ DLOG(ERROR) << "width(" << jpeg.frame_header.visible_width ++ << ") and height(" << jpeg.frame_header.visible_height ++ << ") should be at least 1"; ++ return false; ++ } ++ ++ // Size 64k*64k is the maximum in the JPEG standard. VAAPI doesn't support ++ // resolutions larger than 16k*16k. ++ const int kMaxDimension = 16384; ++ if (jpeg.frame_header.coded_width > kMaxDimension || ++ jpeg.frame_header.coded_height > kMaxDimension) { ++ DLOG(ERROR) << "VAAPI doesn't support size(" ++ << jpeg.frame_header.coded_width << "*" ++ << jpeg.frame_header.coded_height << ") larger than " ++ << kMaxDimension << "*" << kMaxDimension; ++ return false; ++ } ++ ++ if (jpeg.frame_header.num_components != 3) { ++ DLOG(ERROR) << "VAAPI doesn't support num_components(" ++ << static_cast(jpeg.frame_header.num_components) ++ << ") != 3"; ++ return false; ++ } ++ ++ if (jpeg.frame_header.components[0].horizontal_sampling_factor < ++ jpeg.frame_header.components[1].horizontal_sampling_factor || ++ jpeg.frame_header.components[0].horizontal_sampling_factor < ++ jpeg.frame_header.components[2].horizontal_sampling_factor) { ++ DLOG(ERROR) << "VAAPI doesn't supports horizontal sampling factor of Y" ++ << " smaller than Cb and Cr"; ++ return false; ++ } ++ ++ if (jpeg.frame_header.components[0].vertical_sampling_factor < ++ jpeg.frame_header.components[1].vertical_sampling_factor || ++ jpeg.frame_header.components[0].vertical_sampling_factor < ++ jpeg.frame_header.components[2].vertical_sampling_factor) { ++ DLOG(ERROR) << "VAAPI doesn't supports vertical sampling factor of Y" ++ << " smaller than Cb and Cr"; ++ return false; ++ } ++ ++ return true; ++} ++ ++static void FillPictureParameters( ++ const JpegFrameHeader& frame_header, ++ VAPictureParameterBufferJPEGBaseline* pic_param) { ++ memset(pic_param, 0, sizeof(*pic_param)); ++ pic_param->picture_width = frame_header.coded_width; ++ pic_param->picture_height = frame_header.coded_height; ++ pic_param->num_components = frame_header.num_components; ++ ++ for (int i = 0; i < pic_param->num_components; i++) { ++ pic_param->components[i].component_id = frame_header.components[i].id; ++ pic_param->components[i].h_sampling_factor = ++ frame_header.components[i].horizontal_sampling_factor; ++ pic_param->components[i].v_sampling_factor = ++ frame_header.components[i].vertical_sampling_factor; ++ pic_param->components[i].quantiser_table_selector = ++ frame_header.components[i].quantization_table_selector; ++ } ++} ++ ++static void FillIQMatrix(const JpegQuantizationTable* q_table, ++ VAIQMatrixBufferJPEGBaseline* iq_matrix) { ++ memset(iq_matrix, 0, sizeof(*iq_matrix)); ++ static_assert(kJpegMaxQuantizationTableNum == ++ arraysize(iq_matrix->load_quantiser_table), ++ "max number of quantization table mismatched"); ++ for (size_t i = 0; i < kJpegMaxQuantizationTableNum; i++) { ++ if (!q_table[i].valid) ++ continue; ++ iq_matrix->load_quantiser_table[i] = 1; ++ static_assert( ++ arraysize(iq_matrix->quantiser_table[i]) == arraysize(q_table[i].value), ++ "number of quantization entries mismatched"); ++ for (size_t j = 0; j < arraysize(q_table[i].value); j++) ++ iq_matrix->quantiser_table[i][j] = q_table[i].value[j]; ++ } ++} ++ ++static void FillHuffmanTable(const JpegHuffmanTable* dc_table, ++ const JpegHuffmanTable* ac_table, ++ VAHuffmanTableBufferJPEGBaseline* huffman_table) { ++ memset(huffman_table, 0, sizeof(*huffman_table)); ++ // Use default huffman tables if not specified in header. ++ bool has_huffman_table = false; ++ for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { ++ if (dc_table[i].valid || ac_table[i].valid) { ++ has_huffman_table = true; ++ break; ++ } ++ } ++ if (!has_huffman_table) { ++ dc_table = kDefaultDcTable; ++ ac_table = kDefaultAcTable; ++ } ++ ++ static_assert(kJpegMaxHuffmanTableNumBaseline == ++ arraysize(huffman_table->load_huffman_table), ++ "max number of huffman table mismatched"); ++ static_assert(sizeof(huffman_table->huffman_table[0].num_dc_codes) == ++ sizeof(dc_table[0].code_length), ++ "size of huffman table code length mismatch"); ++ static_assert(sizeof(huffman_table->huffman_table[0].dc_values[0]) == ++ sizeof(dc_table[0].code_value[0]), ++ "size of huffman table code value mismatch"); ++ for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { ++ if (!dc_table[i].valid || !ac_table[i].valid) ++ continue; ++ huffman_table->load_huffman_table[i] = 1; ++ ++ memcpy(huffman_table->huffman_table[i].num_dc_codes, ++ dc_table[i].code_length, ++ sizeof(huffman_table->huffman_table[i].num_dc_codes)); ++ memcpy(huffman_table->huffman_table[i].dc_values, dc_table[i].code_value, ++ sizeof(huffman_table->huffman_table[i].dc_values)); ++ memcpy(huffman_table->huffman_table[i].num_ac_codes, ++ ac_table[i].code_length, ++ sizeof(huffman_table->huffman_table[i].num_ac_codes)); ++ memcpy(huffman_table->huffman_table[i].ac_values, ac_table[i].code_value, ++ sizeof(huffman_table->huffman_table[i].ac_values)); ++ } ++} ++ ++static void FillSliceParameters( ++ const JpegParseResult& parse_result, ++ VASliceParameterBufferJPEGBaseline* slice_param) { ++ memset(slice_param, 0, sizeof(*slice_param)); ++ slice_param->slice_data_size = parse_result.data_size; ++ slice_param->slice_data_offset = 0; ++ slice_param->slice_data_flag = VA_SLICE_DATA_FLAG_ALL; ++ slice_param->slice_horizontal_position = 0; ++ slice_param->slice_vertical_position = 0; ++ slice_param->num_components = parse_result.scan.num_components; ++ for (int i = 0; i < slice_param->num_components; i++) { ++ slice_param->components[i].component_selector = ++ parse_result.scan.components[i].component_selector; ++ slice_param->components[i].dc_table_selector = ++ parse_result.scan.components[i].dc_selector; ++ slice_param->components[i].ac_table_selector = ++ parse_result.scan.components[i].ac_selector; ++ } ++ slice_param->restart_interval = parse_result.restart_interval; ++ ++ // Cast to int to prevent overflow. ++ int max_h_factor = ++ parse_result.frame_header.components[0].horizontal_sampling_factor; ++ int max_v_factor = ++ parse_result.frame_header.components[0].vertical_sampling_factor; ++ int mcu_cols = parse_result.frame_header.coded_width / (max_h_factor * 8); ++ DCHECK_GT(mcu_cols, 0); ++ int mcu_rows = parse_result.frame_header.coded_height / (max_v_factor * 8); ++ DCHECK_GT(mcu_rows, 0); ++ slice_param->num_mcus = mcu_rows * mcu_cols; ++} ++ ++// static ++bool VaapiJpegDecoder::Decode(VaapiWrapper* vaapi_wrapper, ++ const JpegParseResult& parse_result, ++ VASurfaceID va_surface) { ++ DCHECK_NE(va_surface, VA_INVALID_SURFACE); ++ if (!IsVaapiSupportedJpeg(parse_result)) ++ return false; ++ ++ // Set picture parameters. ++ VAPictureParameterBufferJPEGBaseline pic_param; ++ FillPictureParameters(parse_result.frame_header, &pic_param); ++ if (!vaapi_wrapper->SubmitBuffer(VAPictureParameterBufferType, ++ sizeof(pic_param), &pic_param)) ++ return false; ++ ++ // Set quantization table. ++ VAIQMatrixBufferJPEGBaseline iq_matrix; ++ FillIQMatrix(parse_result.q_table, &iq_matrix); ++ if (!vaapi_wrapper->SubmitBuffer(VAIQMatrixBufferType, sizeof(iq_matrix), ++ &iq_matrix)) ++ return false; ++ ++ // Set huffman table. ++ VAHuffmanTableBufferJPEGBaseline huffman_table; ++ FillHuffmanTable(parse_result.dc_table, parse_result.ac_table, ++ &huffman_table); ++ if (!vaapi_wrapper->SubmitBuffer(VAHuffmanTableBufferType, ++ sizeof(huffman_table), &huffman_table)) ++ return false; ++ ++ // Set slice parameters. ++ VASliceParameterBufferJPEGBaseline slice_param; ++ FillSliceParameters(parse_result, &slice_param); ++ if (!vaapi_wrapper->SubmitBuffer(VASliceParameterBufferType, ++ sizeof(slice_param), &slice_param)) ++ return false; ++ ++ // Set scan data. ++ if (!vaapi_wrapper->SubmitBuffer(VASliceDataBufferType, ++ parse_result.data_size, ++ const_cast(parse_result.data))) ++ return false; ++ ++ if (!vaapi_wrapper->ExecuteAndDestroyPendingBuffers(va_surface)) ++ return false; ++ ++ return true; ++} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_decoder.h +@@ -0,0 +1,43 @@ ++// Copyright 2015 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODER_H_ ++#define MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODER_H_ ++ ++#include "base/macros.h" ++#include "media/gpu/media_gpu_export.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++ ++namespace media { ++ ++struct JpegParseResult; ++ ++// A JPEG decoder that utilizes VA-API hardware video decode acceleration on ++// Intel systems. Provides functionality to allow plugging VAAPI HW ++// acceleration into the JpegDecodeAccelerator framework. ++// ++// Clients of this class are expected to manage VA surfaces created via ++// VaapiWrapper, parse JPEG picture via ParseJpegPicture, and then pass ++// them to this class. ++class MEDIA_GPU_EXPORT VaapiJpegDecoder { ++ public: ++ // Decode a JPEG picture. It will fill VA-API parameters and call ++ // corresponding VA-API methods according to parsed JPEG result ++ // |parse_result|. Decoded data will be outputted to the given |va_surface|. ++ // Return false on failure. ++ // |vaapi_wrapper| should be initialized in kDecode mode with ++ // VAProfileJPEGBaseline profile. ++ // |va_surface| should be created with size at least as large as the picture ++ // size. ++ static bool Decode(VaapiWrapper* vaapi_wrapper, ++ const JpegParseResult& parse_result, ++ VASurfaceID va_surface); ++ ++ private: ++ DISALLOW_IMPLICIT_CONSTRUCTORS(VaapiJpegDecoder); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODER_H_ +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc +@@ -0,0 +1,139 @@ ++// Copyright 2015 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include ++#include ++ ++#include ++ ++// This has to be included first. ++// See http://code.google.com/p/googletest/issues/detail?id=371 ++#include "testing/gtest/include/gtest/gtest.h" ++ ++#include "base/at_exit.h" ++#include "base/bind.h" ++#include "base/files/file_util.h" ++#include "base/logging.h" ++#include "base/md5.h" ++#include "base/path_service.h" ++#include "base/strings/string_piece.h" ++#include "media/base/test_data_util.h" ++#include "media/base/video_frame.h" ++#include "media/filters/jpeg_parser.h" ++#include "media/gpu/vaapi/vaapi_jpeg_decoder.h" ++ ++namespace media { ++namespace { ++ ++const char* kTestFilename = "pixel-1280x720.jpg"; ++const char* kExpectedMd5Sum = "6e9e1716073c9a9a1282e3f0e0dab743"; ++ ++void LogOnError() { ++ LOG(FATAL) << "Oh noes! Decoder failed"; ++} ++ ++class VaapiJpegDecoderTest : public ::testing::Test { ++ protected: ++ VaapiJpegDecoderTest() {} ++ ++ void SetUp() override { ++ base::Closure report_error_cb = base::Bind(&LogOnError); ++ wrapper_ = VaapiWrapper::Create(VaapiWrapper::kDecode, ++ VAProfileJPEGBaseline, report_error_cb); ++ ASSERT_TRUE(wrapper_); ++ ++ base::FilePath input_file = GetTestDataFilePath(kTestFilename); ++ ++ ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data_)) ++ << "failed to read input data from " << input_file.value(); ++ } ++ ++ void TearDown() override { wrapper_ = nullptr; } ++ ++ bool VerifyDecode(const JpegParseResult& parse_result, ++ const std::string& md5sum); ++ ++ protected: ++ scoped_refptr wrapper_; ++ std::string jpeg_data_; ++}; ++ ++bool VaapiJpegDecoderTest::VerifyDecode(const JpegParseResult& parse_result, ++ const std::string& expected_md5sum) { ++ gfx::Size size(parse_result.frame_header.coded_width, ++ parse_result.frame_header.coded_height); ++ ++ std::vector va_surfaces; ++ if (!wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)) ++ return false; ++ ++ if (!VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])) { ++ LOG(ERROR) << "Decode failed"; ++ return false; ++ } ++ ++ VAImage image; ++ VAImageFormat format; ++ const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); ++ memset(&image, 0, sizeof(image)); ++ memset(&format, 0, sizeof(format)); ++ format.fourcc = kI420Fourcc; ++ format.byte_order = VA_LSB_FIRST; ++ format.bits_per_pixel = 12; // 12 for I420 ++ ++ void* mem; ++ if (!wrapper_->GetVaImage(va_surfaces[0], &format, size, &image, &mem)) { ++ LOG(ERROR) << "Cannot get VAImage"; ++ return false; ++ } ++ EXPECT_EQ(kI420Fourcc, image.format.fourcc); ++ ++ base::StringPiece result(reinterpret_cast(mem), ++ VideoFrame::AllocationSize(PIXEL_FORMAT_I420, size)); ++ EXPECT_EQ(expected_md5sum, base::MD5String(result)); ++ ++ wrapper_->ReturnVaImage(&image); ++ ++ return true; ++} ++ ++TEST_F(VaapiJpegDecoderTest, DecodeSuccess) { ++ JpegParseResult parse_result; ++ ASSERT_TRUE( ++ ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), ++ jpeg_data_.size(), &parse_result)); ++ ++ EXPECT_TRUE(VerifyDecode(parse_result, kExpectedMd5Sum)); ++} ++ ++TEST_F(VaapiJpegDecoderTest, DecodeFail) { ++ JpegParseResult parse_result; ++ ASSERT_TRUE( ++ ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), ++ jpeg_data_.size(), &parse_result)); ++ ++ // Not supported by VAAPI. ++ parse_result.frame_header.num_components = 1; ++ parse_result.scan.num_components = 1; ++ ++ gfx::Size size(parse_result.frame_header.coded_width, ++ parse_result.frame_header.coded_height); ++ ++ std::vector va_surfaces; ++ ASSERT_TRUE( ++ wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)); ++ ++ EXPECT_FALSE( ++ VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])); ++} ++ ++} // namespace ++} // namespace media ++ ++int main(int argc, char** argv) { ++ testing::InitGoogleTest(&argc, argv); ++ base::AtExitManager exit_manager; ++ media::VaapiWrapper::PreSandboxInitialization(); ++ return RUN_ALL_TESTS(); ++} +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc +@@ -0,0 +1,268 @@ ++// Copyright 2017 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h" ++ ++#include ++ ++#include ++#include ++ ++#include "base/bind.h" ++#include "base/logging.h" ++#include "base/metrics/histogram_macros.h" ++#include "base/sequence_checker.h" ++#include "base/task_scheduler/post_task.h" ++#include "base/threading/thread_task_runner_handle.h" ++#include "base/trace_event/trace_event.h" ++#include "media/base/bind_to_current_loop.h" ++#include "media/base/video_frame.h" ++#include "media/gpu/vaapi/vaapi_jpeg_encoder.h" ++ ++namespace media { ++ ++namespace { ++ ++// UMA results that the VaapiJpegEncodeAccelerator class reports. ++// These values are persisted to logs, and should therefore never be renumbered ++// nor reused. ++enum VAJEAEncoderResult { ++ VAAPI_SUCCESS = 0, ++ VAAPI_ERROR, ++ VAJEA_ENCODER_RESULT_MAX = VAAPI_ERROR, ++}; ++ ++static void ReportToUMA(VAJEAEncoderResult result) { ++ UMA_HISTOGRAM_ENUMERATION("Media.VAJEA.EncoderResult", result, ++ VAJEAEncoderResult::VAJEA_ENCODER_RESULT_MAX + 1); ++} ++} // namespace ++ ++VaapiJpegEncodeAccelerator::EncodeRequest::EncodeRequest( ++ scoped_refptr video_frame, ++ std::unique_ptr shm, ++ int quality) ++ : video_frame(std::move(video_frame)), ++ shm(std::move(shm)), ++ quality(quality) {} ++ ++VaapiJpegEncodeAccelerator::EncodeRequest::~EncodeRequest() {} ++ ++class VaapiJpegEncodeAccelerator::Encoder { ++ public: ++ Encoder(scoped_refptr vaapi_wrapper, ++ base::RepeatingCallback video_frame_ready_cb, ++ base::RepeatingCallback notify_error_cb); ++ ~Encoder(); ++ ++ // Processes one encode |request|. ++ void EncodeTask(std::unique_ptr request); ++ ++ private: ++ // |cached_output_buffer_id_| is the last allocated VABuffer during ++ // EncodeTask() and |cached_output_buffer_size_| is the size of it. ++ // If the next call to EncodeTask() does not require a buffer bigger than ++ // |cached_output_buffer_size_|, |cached_output_buffer_id_| will be reused. ++ size_t cached_output_buffer_size_; ++ VABufferID cached_output_buffer_id_; ++ ++ std::unique_ptr jpeg_encoder_; ++ scoped_refptr vaapi_wrapper_; ++ ++ base::RepeatingCallback video_frame_ready_cb_; ++ base::RepeatingCallback notify_error_cb_; ++ ++ SEQUENCE_CHECKER(sequence_checker_); ++ ++ DISALLOW_COPY_AND_ASSIGN(Encoder); ++}; ++ ++VaapiJpegEncodeAccelerator::Encoder::Encoder( ++ scoped_refptr vaapi_wrapper, ++ base::RepeatingCallback video_frame_ready_cb, ++ base::RepeatingCallback notify_error_cb) ++ : cached_output_buffer_size_(0), ++ jpeg_encoder_(new VaapiJpegEncoder(vaapi_wrapper)), ++ vaapi_wrapper_(std::move(vaapi_wrapper)), ++ video_frame_ready_cb_(std::move(video_frame_ready_cb)), ++ notify_error_cb_(std::move(notify_error_cb)) { ++ DETACH_FROM_SEQUENCE(sequence_checker_); ++} ++ ++VaapiJpegEncodeAccelerator::Encoder::~Encoder() { ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++} ++ ++void VaapiJpegEncodeAccelerator::Encoder::EncodeTask( ++ std::unique_ptr request) { ++ TRACE_EVENT0("jpeg", "EncodeTask"); ++ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); ++ ++ const int video_frame_id = request->video_frame->unique_id(); ++ gfx::Size input_size = request->video_frame->coded_size(); ++ std::vector va_surfaces; ++ if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, input_size, 1, ++ &va_surfaces)) { ++ VLOG(1) << "Failed to create VA surface"; ++ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); ++ return; ++ } ++ VASurfaceID va_surface_id = va_surfaces[0]; ++ ++ if (!vaapi_wrapper_->UploadVideoFrameToSurface(request->video_frame, ++ va_surface_id)) { ++ VLOG(1) << "Failed to upload video frame to VA surface"; ++ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); ++ return; ++ } ++ ++ // Create output buffer for encoding result. ++ size_t max_coded_buffer_size = ++ VaapiJpegEncoder::GetMaxCodedBufferSize(input_size); ++ if (max_coded_buffer_size > cached_output_buffer_size_) { ++ vaapi_wrapper_->DestroyCodedBuffers(); ++ cached_output_buffer_size_ = 0; ++ ++ VABufferID output_buffer_id; ++ if (!vaapi_wrapper_->CreateCodedBuffer(max_coded_buffer_size, ++ &output_buffer_id)) { ++ VLOG(1) << "Failed to create VA buffer for encoding output"; ++ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); ++ return; ++ } ++ cached_output_buffer_size_ = max_coded_buffer_size; ++ cached_output_buffer_id_ = output_buffer_id; ++ } ++ ++ if (!jpeg_encoder_->Encode(input_size, request->quality, va_surface_id, ++ cached_output_buffer_id_)) { ++ VLOG(1) << "Encode JPEG failed"; ++ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); ++ return; ++ } ++ ++ // Get the encoded output. DownloadFromCodedBuffer() is a blocking call. It ++ // would wait until encoding is finished. ++ size_t encoded_size = 0; ++ if (!vaapi_wrapper_->DownloadFromCodedBuffer( ++ cached_output_buffer_id_, va_surface_id, ++ static_cast(request->shm->memory()), request->shm->size(), ++ &encoded_size)) { ++ VLOG(1) << "Failed to retrieve output image from VA coded buffer"; ++ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); ++ } ++ ++ video_frame_ready_cb_.Run(request->video_frame->unique_id(), encoded_size); ++} ++ ++VaapiJpegEncodeAccelerator::VaapiJpegEncodeAccelerator( ++ scoped_refptr io_task_runner) ++ : task_runner_(base::ThreadTaskRunnerHandle::Get()), ++ io_task_runner_(std::move(io_task_runner)), ++ weak_this_factory_(this) { ++ weak_this_ = weak_this_factory_.GetWeakPtr(); ++} ++ ++VaapiJpegEncodeAccelerator::~VaapiJpegEncodeAccelerator() { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ DVLOG(1) << "Destroying VaapiJpegEncodeAccelerator"; ++ ++ weak_this_factory_.InvalidateWeakPtrs(); ++ encoder_task_runner_->DeleteSoon(FROM_HERE, std::move(encoder_)); ++} ++ ++void VaapiJpegEncodeAccelerator::NotifyError(int video_frame_id, ++ Status status) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ DLOG(ERROR) << "Notifying error: " << status; ++ DCHECK(client_); ++ client_->NotifyError(video_frame_id, status); ++} ++ ++void VaapiJpegEncodeAccelerator::VideoFrameReady(int video_frame_id, ++ size_t encoded_picture_size) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ReportToUMA(VAJEAEncoderResult::VAAPI_SUCCESS); ++ ++ client_->VideoFrameReady(video_frame_id, encoded_picture_size); ++} ++ ++JpegEncodeAccelerator::Status VaapiJpegEncodeAccelerator::Initialize( ++ JpegEncodeAccelerator::Client* client) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ if (!VaapiWrapper::IsJpegEncodeSupported()) { ++ return HW_JPEG_ENCODE_NOT_SUPPORTED; ++ } ++ ++ client_ = client; ++ scoped_refptr vaapi_wrapper = VaapiWrapper::Create( ++ VaapiWrapper::kEncode, VAProfileJPEGBaseline, ++ base::Bind(&ReportToUMA, VAJEAEncoderResult::VAAPI_ERROR)); ++ ++ if (!vaapi_wrapper) { ++ VLOG(1) << "Failed initializing VAAPI"; ++ return PLATFORM_FAILURE; ++ } ++ ++ encoder_task_runner_ = base::CreateSingleThreadTaskRunnerWithTraits( ++ {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); ++ if (!encoder_task_runner_) { ++ VLOG(1) << "Failed to create encoder task runner."; ++ return THREAD_CREATION_FAILED; ++ } ++ ++ encoder_ = std::make_unique( ++ std::move(vaapi_wrapper), ++ BindToCurrentLoop(base::BindRepeating( ++ &VaapiJpegEncodeAccelerator::VideoFrameReady, weak_this_)), ++ BindToCurrentLoop(base::BindRepeating( ++ &VaapiJpegEncodeAccelerator::NotifyError, weak_this_))); ++ ++ return ENCODE_OK; ++} ++ ++size_t VaapiJpegEncodeAccelerator::GetMaxCodedBufferSize( ++ const gfx::Size& picture_size) { ++ return VaapiJpegEncoder::GetMaxCodedBufferSize(picture_size); ++} ++ ++void VaapiJpegEncodeAccelerator::Encode( ++ scoped_refptr video_frame, ++ int quality, ++ const BitstreamBuffer& bitstream_buffer) { ++ DCHECK(io_task_runner_->BelongsToCurrentThread()); ++ ++ int video_frame_id = video_frame->unique_id(); ++ TRACE_EVENT1("jpeg", "Encode", "input_id", video_frame_id); ++ ++ // TODO(shenghao): support other YUV formats. ++ if (video_frame->format() != VideoPixelFormat::PIXEL_FORMAT_I420) { ++ VLOG(1) << "Unsupported input format: " << video_frame->format(); ++ task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, ++ weak_this_, video_frame_id, INVALID_ARGUMENT)); ++ return; ++ } ++ ++ // SharedMemoryRegion will take ownership of the |bitstream_buffer.handle()|. ++ auto shm = std::make_unique(bitstream_buffer, false); ++ if (!shm->Map()) { ++ VLOG(1) << "Failed to map output buffer"; ++ task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, weak_this_, ++ video_frame_id, INACCESSIBLE_OUTPUT_BUFFER)); ++ return; ++ } ++ ++ auto request = std::make_unique(std::move(video_frame), ++ std::move(shm), quality); ++ encoder_task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiJpegEncodeAccelerator::Encoder::EncodeTask, ++ base::Unretained(encoder_.get()), base::Passed(&request))); ++} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h +@@ -0,0 +1,96 @@ ++// Copyright 2017 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ ++#define MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ ++ ++#include ++ ++#include "base/macros.h" ++#include "base/memory/weak_ptr.h" ++#include "base/single_thread_task_runner.h" ++#include "media/base/bitstream_buffer.h" ++#include "media/gpu/media_gpu_export.h" ++#include "media/gpu/shared_memory_region.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++#include "media/video/jpeg_encode_accelerator.h" ++ ++namespace media { ++ ++// Class to provide JPEG encode acceleration for Intel systems with hardware ++// support for it, and on which libva is available. ++// Encoding tasks are performed in a separate encoding thread. ++// ++// Threading/life-cycle: this object is created & destroyed on the GPU ++// ChildThread. Methods in nested class Encoder are called on the encoder ++// thread which is stopped during destructor, so the callbacks bound with ++// a weak this can be run on the encoder thread because it can assume ++// VaapiJpegEncodeAccelerator is still alive. ++class MEDIA_GPU_EXPORT VaapiJpegEncodeAccelerator ++ : public JpegEncodeAccelerator { ++ public: ++ explicit VaapiJpegEncodeAccelerator( ++ scoped_refptr io_task_runner); ++ ~VaapiJpegEncodeAccelerator() override; ++ ++ // JpegEncodeAccelerator implementation. ++ Status Initialize(JpegEncodeAccelerator::Client* client) override; ++ size_t GetMaxCodedBufferSize(const gfx::Size& picture_size) override; ++ ++ // Currently only I420 format is supported for |video_frame|. ++ void Encode(scoped_refptr video_frame, ++ int quality, ++ const BitstreamBuffer& bitstream_buffer) override; ++ ++ private: ++ // An input video frame and the corresponding output buffer awaiting ++ // consumption, provided by the client. ++ struct EncodeRequest { ++ EncodeRequest(scoped_refptr video_frame, ++ std::unique_ptr shm, ++ int quality); ++ ~EncodeRequest(); ++ ++ scoped_refptr video_frame; ++ std::unique_ptr shm; ++ int quality; ++ ++ DISALLOW_COPY_AND_ASSIGN(EncodeRequest); ++ }; ++ ++ // The Encoder class is a collection of methods that run on ++ // |encoder_task_runner_|. ++ class Encoder; ++ ++ // Notifies the client that an error has occurred and encoding cannot ++ // continue. ++ void NotifyError(int video_frame_id, Status status); ++ ++ void VideoFrameReady(int video_frame_id, size_t encoded_picture_size); ++ ++ // ChildThread's task runner. ++ scoped_refptr task_runner_; ++ ++ // GPU IO task runner. ++ scoped_refptr io_task_runner_; ++ ++ // The client of this class. ++ Client* client_; ++ ++ // Use this to post tasks to encoder thread. ++ scoped_refptr encoder_task_runner_; ++ ++ std::unique_ptr encoder_; ++ ++ // |weak_this_| is used to post tasks from |encoder_task_runner_| to ++ // |task_runner_|. ++ base::WeakPtr weak_this_; ++ base::WeakPtrFactory weak_this_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncodeAccelerator); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_encoder.cc +@@ -0,0 +1,427 @@ ++// Copyright 2017 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_jpeg_encoder.h" ++ ++#include ++#include ++#include ++ ++#include "base/logging.h" ++#include "base/macros.h" ++#include "base/numerics/safe_conversions.h" ++#include "media/filters/jpeg_parser.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++ ++#define ARRAY_MEMCPY_CHECKED(to, from) \ ++ do { \ ++ static_assert(sizeof(to) == sizeof(from), \ ++ #from " and " #to " arrays must be of same size"); \ ++ memcpy(to, from, sizeof(to)); \ ++ } while (0) ++ ++namespace media { ++ ++namespace { ++ ++// JPEG header only uses 2 bytes to represent width and height. ++const int kMaxDimension = 65535; ++const size_t kDctSize2 = 64; ++const size_t kNumDcRunSizeBits = 16; ++const size_t kNumAcRunSizeBits = 16; ++const size_t kNumDcCodeWordsHuffVal = 12; ++const size_t kNumAcCodeWordsHuffVal = 162; ++const size_t kJpegHeaderSize = 83 + (kDctSize2 * 2) + (kNumDcRunSizeBits * 2) + ++ (kNumDcCodeWordsHuffVal * 2) + ++ (kNumAcRunSizeBits * 2) + ++ (kNumAcCodeWordsHuffVal * 2); ++ ++const uint8_t kZigZag8x8[64] = { ++ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, ++ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, ++ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, ++ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; ++ ++const JpegQuantizationTable kDefaultQuantTable[2] = { ++ // Table K.1 Luminance quantization table values. ++ { ++ true, ++ {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, ++ 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, ++ 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, ++ 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}, ++ }, ++ // Table K.2 Chrominance quantization table values. ++ { ++ true, ++ {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, ++ 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, ++ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, ++ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, ++ }, ++}; ++ ++using JPEGHeader = uint8_t[kJpegHeaderSize]; ++ ++void FillPictureParameters(const gfx::Size& input_size, ++ int quality, ++ VABufferID output_buffer_id, ++ VAEncPictureParameterBufferJPEG* pic_param) { ++ pic_param->picture_width = input_size.width(); ++ pic_param->picture_height = input_size.height(); ++ pic_param->num_components = 3; ++ ++ // Output buffer. ++ pic_param->coded_buf = output_buffer_id; ++ pic_param->quality = quality; ++ // Profile = Baseline. ++ pic_param->pic_flags.bits.profile = 0; ++ // Sequential encoding. ++ pic_param->pic_flags.bits.progressive = 0; ++ // Uses Huffman coding. ++ pic_param->pic_flags.bits.huffman = 1; ++ // Input format is interleaved (YUV). ++ pic_param->pic_flags.bits.interleaved = 0; ++ // Non-differential Encoding. ++ pic_param->pic_flags.bits.differential = 0; ++ // Only 8 bit sample depth is currently supported. ++ pic_param->sample_bit_depth = 8; ++ pic_param->num_scan = 1; ++} ++ ++void FillQMatrix(VAQMatrixBufferJPEG* q_matrix) { ++ // Fill the raw, unscaled quantization tables for libva. The VAAPI driver is ++ // responsible for scaling the quantization tables based on picture ++ // parameter quality. ++ const JpegQuantizationTable& luminance = kDefaultQuantTable[0]; ++ static_assert( ++ arraysize(luminance.value) == arraysize(q_matrix->lum_quantiser_matrix), ++ "Luminance quantization table size mismatch."); ++ static_assert(arraysize(kZigZag8x8) == arraysize(luminance.value), ++ "Luminance quantization table size mismatch."); ++ q_matrix->load_lum_quantiser_matrix = 1; ++ for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { ++ q_matrix->lum_quantiser_matrix[i] = luminance.value[kZigZag8x8[i]]; ++ } ++ ++ const JpegQuantizationTable& chrominance = kDefaultQuantTable[1]; ++ static_assert(arraysize(chrominance.value) == ++ arraysize(q_matrix->chroma_quantiser_matrix), ++ "Chrominance quantization table size mismatch."); ++ static_assert(arraysize(kZigZag8x8) == arraysize(chrominance.value), ++ "Chrominance quantization table size mismatch."); ++ q_matrix->load_chroma_quantiser_matrix = 1; ++ for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { ++ q_matrix->chroma_quantiser_matrix[i] = chrominance.value[kZigZag8x8[i]]; ++ } ++} ++ ++void FillHuffmanTableParameters( ++ VAHuffmanTableBufferJPEGBaseline* huff_table_param) { ++ static_assert(arraysize(kDefaultDcTable) == arraysize(kDefaultAcTable), ++ "DC table and AC table size mismatch."); ++ static_assert( ++ arraysize(kDefaultDcTable) == arraysize(huff_table_param->huffman_table), ++ "DC table and destination table size mismatch."); ++ ++ for (size_t i = 0; i < arraysize(kDefaultDcTable); ++i) { ++ const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; ++ const JpegHuffmanTable& acTable = kDefaultAcTable[i]; ++ huff_table_param->load_huffman_table[i] = true; ++ ++ // Load DC Table. ++ ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_dc_codes, ++ dcTable.code_length); ++ // |code_values| of JpegHuffmanTable needs to hold DC and AC code values ++ // so it has different size than ++ // |huff_table_param->huffman_table[i].dc_values|. Therefore we can't use ++ // ARRAY_MEMCPY_CHECKED() here. ++ static_assert(arraysize(huff_table_param->huffman_table[i].dc_values) <= ++ arraysize(dcTable.code_value), ++ "DC table code value array too small."); ++ memcpy(huff_table_param->huffman_table[i].dc_values, &dcTable.code_value[0], ++ sizeof(huff_table_param->huffman_table[i].dc_values)); ++ ++ // Load AC Table. ++ ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_ac_codes, ++ acTable.code_length); ++ ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].ac_values, ++ acTable.code_value); ++ ++ memset(huff_table_param->huffman_table[i].pad, 0, ++ sizeof(huff_table_param->huffman_table[i].pad)); ++ } ++} ++ ++void FillSliceParameters(VAEncSliceParameterBufferJPEG* slice_param) { ++ slice_param->restart_interval = 0; ++ slice_param->num_components = 3; ++ ++ slice_param->components[0].component_selector = 1; ++ slice_param->components[0].dc_table_selector = 0; ++ slice_param->components[0].ac_table_selector = 0; ++ ++ slice_param->components[1].component_selector = 2; ++ slice_param->components[1].dc_table_selector = 1; ++ slice_param->components[1].ac_table_selector = 1; ++ ++ slice_param->components[2].component_selector = 3; ++ slice_param->components[2].dc_table_selector = 1; ++ slice_param->components[2].ac_table_selector = 1; ++} ++ ++size_t FillJpegHeader(const gfx::Size& input_size, ++ int quality, ++ JPEGHeader& header) { ++ unsigned int width = input_size.width(); ++ unsigned int height = input_size.height(); ++ ++ size_t idx = 0; ++ ++ // Start Of Input. ++ static const uint8_t kSOI[] = {0xFF, JPEG_SOI}; ++ memcpy(header, kSOI, sizeof(kSOI)); ++ idx += sizeof(kSOI); ++ ++ // Application Segment - JFIF standard 1.01. ++ // TODO(shenghao): Use Exif (JPEG_APP1) instead. ++ static const uint8_t kAppSegment[] = { ++ 0xFF, JPEG_APP0, 0x00, ++ 0x10, // Segment length:16 (2-byte). ++ 0x4A, // J ++ 0x46, // F ++ 0x49, // I ++ 0x46, // F ++ 0x00, // 0 ++ 0x01, // Major version. ++ 0x01, // Minor version. ++ 0x01, // Density units 0:no units, 1:pixels per inch, ++ // 2: pixels per cm. ++ 0x00, ++ 0x48, // X density (2-byte). ++ 0x00, ++ 0x48, // Y density (2-byte). ++ 0x00, // Thumbnail width. ++ 0x00 // Thumbnail height. ++ }; ++ memcpy(header + idx, kAppSegment, sizeof(kAppSegment)); ++ idx += sizeof(kAppSegment); ++ ++ if (quality <= 0) { ++ quality = 1; ++ } ++ ++ // Normalize quality factor. ++ // Unlike VAQMatrixBufferJPEG, we have to scale quantization table in JPEG ++ // header by ourselves. ++ uint32_t quality_normalized = base::saturated_cast( ++ (quality < 50) ? (5000 / quality) : (200 - (quality * 2))); ++ ++ // Quantization Tables. ++ for (size_t i = 0; i < 2; ++i) { ++ const uint8_t kQuantSegment[] = { ++ 0xFF, JPEG_DQT, 0x00, ++ 0x03 + kDctSize2, // Segment length:67 (2-byte). ++ static_cast(i) // Precision (4-bit high) = 0, ++ // Index (4-bit low) = i. ++ }; ++ memcpy(header + idx, kQuantSegment, sizeof(kQuantSegment)); ++ idx += sizeof(kQuantSegment); ++ ++ const JpegQuantizationTable& quant_table = kDefaultQuantTable[i]; ++ for (size_t j = 0; j < kDctSize2; ++j) { ++ uint32_t scaled_quant_value = ++ (quant_table.value[kZigZag8x8[j]] * quality_normalized) / 100; ++ scaled_quant_value = std::min(255u, std::max(1u, scaled_quant_value)); ++ header[idx++] = static_cast(scaled_quant_value); ++ } ++ } ++ ++ // Start of Frame - Baseline. ++ const uint8_t kStartOfFrame[] = { ++ 0xFF, ++ JPEG_SOF0, // Baseline. ++ 0x00, ++ 0x11, // Segment length:17 (2-byte). ++ 8, // Data precision. ++ static_cast((height >> 8) & 0xFF), ++ static_cast(height & 0xFF), ++ static_cast((width >> 8) & 0xFF), ++ static_cast(width & 0xFF), ++ 0x03, // Number of Components. ++ }; ++ memcpy(header + idx, kStartOfFrame, sizeof(kStartOfFrame)); ++ idx += sizeof(kStartOfFrame); ++ for (uint8_t i = 0; i < 3; ++i) { ++ // These are the values for U and V planes. ++ uint8_t h_sample_factor = 1; ++ uint8_t v_sample_factor = 1; ++ uint8_t quant_table_number = 1; ++ if (!i) { ++ // These are the values for Y plane. ++ h_sample_factor = 2; ++ v_sample_factor = 2; ++ quant_table_number = 0; ++ } ++ ++ header[idx++] = i + 1; ++ // Horizontal Sample Factor (4-bit high), ++ // Vertical Sample Factor (4-bit low). ++ header[idx++] = (h_sample_factor << 4) | v_sample_factor; ++ header[idx++] = quant_table_number; ++ } ++ ++ static const uint8_t kDcSegment[] = { ++ 0xFF, JPEG_DHT, 0x00, ++ 0x1F, // Segment length:31 (2-byte). ++ }; ++ static const uint8_t kAcSegment[] = { ++ 0xFF, JPEG_DHT, 0x00, ++ 0xB5, // Segment length:181 (2-byte). ++ }; ++ ++ // Huffman Tables. ++ for (size_t i = 0; i < 2; ++i) { ++ // DC Table. ++ memcpy(header + idx, kDcSegment, sizeof(kDcSegment)); ++ idx += sizeof(kDcSegment); ++ ++ // Type (4-bit high) = 0:DC, Index (4-bit low). ++ header[idx++] = static_cast(i); ++ ++ const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; ++ for (size_t j = 0; j < kNumDcRunSizeBits; ++j) ++ header[idx++] = dcTable.code_length[j]; ++ for (size_t j = 0; j < kNumDcCodeWordsHuffVal; ++j) ++ header[idx++] = dcTable.code_value[j]; ++ ++ // AC Table. ++ memcpy(header + idx, kAcSegment, sizeof(kAcSegment)); ++ idx += sizeof(kAcSegment); ++ ++ // Type (4-bit high) = 1:AC, Index (4-bit low). ++ header[idx++] = 0x10 | static_cast(i); ++ ++ const JpegHuffmanTable& acTable = kDefaultAcTable[i]; ++ for (size_t j = 0; j < kNumAcRunSizeBits; ++j) ++ header[idx++] = acTable.code_length[j]; ++ for (size_t j = 0; j < kNumAcCodeWordsHuffVal; ++j) ++ header[idx++] = acTable.code_value[j]; ++ } ++ ++ // Start of Scan. ++ static const uint8_t kStartOfScan[] = { ++ 0xFF, JPEG_SOS, 0x00, ++ 0x0C, // Segment Length:12 (2-byte). ++ 0x03 // Number of components in scan. ++ }; ++ memcpy(header + idx, kStartOfScan, sizeof(kStartOfScan)); ++ idx += sizeof(kStartOfScan); ++ ++ for (uint8_t i = 0; i < 3; ++i) { ++ uint8_t dc_table_number = 1; ++ uint8_t ac_table_number = 1; ++ if (!i) { ++ dc_table_number = 0; ++ ac_table_number = 0; ++ } ++ ++ header[idx++] = i + 1; ++ // DC Table Selector (4-bit high), AC Table Selector (4-bit low). ++ header[idx++] = (dc_table_number << 4) | ac_table_number; ++ } ++ header[idx++] = 0x00; // 0 for Baseline. ++ header[idx++] = 0x3F; // 63 for Baseline. ++ header[idx++] = 0x00; // 0 for Baseline. ++ ++ return idx << 3; ++} ++ ++} // namespace ++ ++VaapiJpegEncoder::VaapiJpegEncoder(scoped_refptr vaapi_wrapper) ++ : vaapi_wrapper_(vaapi_wrapper), ++ q_matrix_cached_(nullptr), ++ huff_table_param_cached_(nullptr), ++ slice_param_cached_(nullptr) {} ++ ++VaapiJpegEncoder::~VaapiJpegEncoder() {} ++ ++size_t VaapiJpegEncoder::GetMaxCodedBufferSize(const gfx::Size& size) { ++ return size.GetArea() * 3 / 2 + kJpegHeaderSize; ++} ++ ++bool VaapiJpegEncoder::Encode(const gfx::Size& input_size, ++ int quality, ++ VASurfaceID surface_id, ++ VABufferID output_buffer_id) { ++ DCHECK_NE(surface_id, VA_INVALID_SURFACE); ++ ++ if (input_size.width() > kMaxDimension || ++ input_size.height() > kMaxDimension) { ++ return false; ++ } ++ ++ // Set picture parameters. ++ VAEncPictureParameterBufferJPEG pic_param; ++ FillPictureParameters(input_size, quality, output_buffer_id, &pic_param); ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, ++ sizeof(pic_param), &pic_param)) { ++ return false; ++ } ++ ++ if (!q_matrix_cached_) { ++ q_matrix_cached_.reset(new VAQMatrixBufferJPEG()); ++ FillQMatrix(q_matrix_cached_.get()); ++ } ++ if (!vaapi_wrapper_->SubmitBuffer(VAQMatrixBufferType, ++ sizeof(*q_matrix_cached_), ++ q_matrix_cached_.get())) { ++ return false; ++ } ++ ++ if (!huff_table_param_cached_) { ++ huff_table_param_cached_.reset(new VAHuffmanTableBufferJPEGBaseline()); ++ FillHuffmanTableParameters(huff_table_param_cached_.get()); ++ } ++ if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType, ++ sizeof(*huff_table_param_cached_), ++ huff_table_param_cached_.get())) { ++ return false; ++ } ++ ++ // Set slice parameters. ++ if (!slice_param_cached_) { ++ slice_param_cached_.reset(new VAEncSliceParameterBufferJPEG()); ++ FillSliceParameters(slice_param_cached_.get()); ++ } ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, ++ sizeof(*slice_param_cached_), ++ slice_param_cached_.get())) { ++ return false; ++ } ++ ++ JPEGHeader header_data; ++ size_t length_in_bits = FillJpegHeader(input_size, quality, header_data); ++ ++ VAEncPackedHeaderParameterBuffer header_param; ++ memset(&header_param, 0, sizeof(header_param)); ++ header_param.type = VAEncPackedHeaderRawData; ++ header_param.bit_length = length_in_bits; ++ header_param.has_emulation_bytes = 0; ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, ++ sizeof(header_param), &header_param)) { ++ return false; ++ } ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, ++ (length_in_bits + 7) / 8, header_data)) { ++ return false; ++ } ++ ++ // Submit the |surface_id| which contains input YUV frame and begin encoding. ++ return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(surface_id); ++} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_jpeg_encoder.h +@@ -0,0 +1,65 @@ ++// Copyright 2017 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODER_H_ ++#define MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODER_H_ ++ ++#include ++#include ++ ++#include "base/macros.h" ++#include "base/memory/scoped_refptr.h" ++#include "media/gpu/media_gpu_export.h" ++#include "ui/gfx/geometry/size.h" ++ ++namespace media { ++ ++class VaapiWrapper; ++ ++// A collection of methods that utilize VA-API hardware video encode ++// acceleration on Intel systems. Provides functionality to allow plugging VAAPI ++// HW acceleration into the JpegEncodeAccelerator framework. ++// ++// Clients are expected to manage VA surfaces and VA buffers created via ++// VaapiWrapper, and pass them to this class. ++class MEDIA_GPU_EXPORT VaapiJpegEncoder { ++ public: ++ // |vaapi_wrapper| should be initialized in VaapiWrapper::kEncode ++ // mode with VAProfileJPEGBaseline profile. ++ explicit VaapiJpegEncoder(scoped_refptr vaapi_wrapper); ++ ~VaapiJpegEncoder(); ++ ++ // Encode a JPEG picture. It will fill VA-API parameters and call ++ // corresponding VA-API methods according to |input_size|. ++ // |quality| is the JPEG image quality ++ // |surface_id| is the VA surface that contains input image. ++ // |output_buffer_id| is the ID of VA buffer that encoded image will be ++ // stored. The size of it should be at least as large as ++ // GetMaxCodedBufferSize(). ++ // Return false on failure. ++ bool Encode(const gfx::Size& input_size, ++ int quality, ++ VASurfaceID surface_id, ++ VABufferID output_buffer_id); ++ ++ // Gets the maximum possible encoded result size. ++ // |size| is the dimension of the YUV image to be encoded. ++ static size_t GetMaxCodedBufferSize(const gfx::Size& size); ++ ++ private: ++ scoped_refptr vaapi_wrapper_; ++ ++ // |q_matrix_cached_|, |huff_table_param_cached_| and |slice_param_cached_| ++ // are created when Encode() is called the first time. After that, they will ++ // directly be used for all the subsequent Encode() calls. ++ std::unique_ptr q_matrix_cached_; ++ std::unique_ptr huff_table_param_cached_; ++ std::unique_ptr slice_param_cached_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncoder); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODER_H_ +--- a/media/gpu/vaapi/vaapi_picture.cc ++++ b/media/gpu/vaapi/vaapi_picture.cc +@@ -4,7 +4,7 @@ + + #include "media/gpu/vaapi/vaapi_picture.h" + +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #include "ui/gl/gl_bindings.h" + #include "ui/gl/gl_implementation.h" + +--- a/media/gpu/vaapi/vaapi_picture_factory.cc ++++ b/media/gpu/vaapi/vaapi_picture_factory.cc +@@ -4,7 +4,7 @@ + + #include "media/gpu/vaapi/vaapi_picture_factory.h" + +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #include "ui/gl/gl_bindings.h" + + #include "media/gpu/vaapi/vaapi_drm_picture.h" +--- a/media/gpu/vaapi/vaapi_tfp_picture.cc ++++ b/media/gpu/vaapi/vaapi_tfp_picture.cc +@@ -4,8 +4,8 @@ + + #include "media/gpu/vaapi/vaapi_tfp_picture.h" + +-#include "media/gpu/va_surface.h" +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/va_surface.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #include "ui/gfx/x/x11_types.h" + #include "ui/gl/gl_bindings.h" + #include "ui/gl/gl_image_glx.h" +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc +@@ -0,0 +1,1871 @@ ++// Copyright (c) 2012 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" ++ ++#include ++ ++#include ++ ++#include ++ ++#include "base/bind.h" ++#include "base/files/scoped_file.h" ++#include "base/logging.h" ++#include "base/macros.h" ++#include "base/metrics/histogram_macros.h" ++#include "base/stl_util.h" ++#include "base/strings/string_util.h" ++#include "base/synchronization/waitable_event.h" ++#include "base/threading/thread_task_runner_handle.h" ++#include "base/trace_event/trace_event.h" ++#include "gpu/ipc/service/gpu_channel.h" ++#include "media/base/bind_to_current_loop.h" ++#include "media/gpu/accelerated_video_decoder.h" ++#include "media/gpu/format_utils.h" ++#include "media/gpu/h264_decoder.h" ++#include "media/gpu/vaapi/vaapi_picture.h" ++#include "media/gpu/vp8_decoder.h" ++#include "media/gpu/vp9_decoder.h" ++#include "media/video/picture.h" ++#include "ui/gl/gl_image.h" ++ ++#define DVLOGF(level) DVLOG(level) << __func__ << "(): " ++#define VLOGF(level) VLOG(level) << __func__ << "(): " ++ ++namespace media { ++ ++namespace { ++// UMA errors that the VaapiVideoDecodeAccelerator class reports. ++enum VAVDADecoderFailure { ++ VAAPI_ERROR = 0, ++ VAVDA_DECODER_FAILURES_MAX, ++}; ++// from ITU-T REC H.264 spec ++// section 8.5.6 ++// "Inverse scanning process for 4x4 transform coefficients and scaling lists" ++static const int kZigzagScan4x4[16] = {0, 1, 4, 8, 5, 2, 3, 6, ++ 9, 12, 13, 10, 7, 11, 14, 15}; ++ ++// section 8.5.7 ++// "Inverse scanning process for 8x8 transform coefficients and scaling lists" ++static const uint8_t kZigzagScan8x8[64] = { ++ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, ++ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, ++ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, ++ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; ++ ++// Returns the preferred VA_RT_FORMAT for the given |profile|. ++unsigned int GetVaFormatForVideoCodecProfile(VideoCodecProfile profile) { ++ if (profile == VP9PROFILE_PROFILE2 || profile == VP9PROFILE_PROFILE3) ++ return VA_RT_FORMAT_YUV420_10BPP; ++ return VA_RT_FORMAT_YUV420; ++} ++ ++} // namespace ++ ++static void ReportToUMA(VAVDADecoderFailure failure) { ++ UMA_HISTOGRAM_ENUMERATION("Media.VAVDA.DecoderFailure", failure, ++ VAVDA_DECODER_FAILURES_MAX + 1); ++} ++ ++#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ ++ do { \ ++ if (!(result)) { \ ++ VLOGF(1) << log; \ ++ NotifyError(error_code); \ ++ return ret; \ ++ } \ ++ } while (0) ++ ++class VaapiVideoDecodeAccelerator::VaapiDecodeSurface ++ : public base::RefCountedThreadSafe { ++ public: ++ VaapiDecodeSurface(int32_t bitstream_id, ++ const scoped_refptr& va_surface); ++ ++ int32_t bitstream_id() const { return bitstream_id_; } ++ scoped_refptr va_surface() { return va_surface_; } ++ gfx::Rect visible_rect() const { return visible_rect_; } ++ ++ void set_visible_rect(const gfx::Rect& visible_rect) { ++ visible_rect_ = visible_rect; ++ } ++ ++ private: ++ friend class base::RefCountedThreadSafe; ++ ~VaapiDecodeSurface(); ++ ++ const int32_t bitstream_id_; ++ const scoped_refptr va_surface_; ++ gfx::Rect visible_rect_; ++}; ++ ++VaapiVideoDecodeAccelerator::VaapiDecodeSurface::VaapiDecodeSurface( ++ int32_t bitstream_id, ++ const scoped_refptr& va_surface) ++ : bitstream_id_(bitstream_id), va_surface_(va_surface) {} ++ ++VaapiVideoDecodeAccelerator::VaapiDecodeSurface::~VaapiDecodeSurface() {} ++ ++class VaapiH264Picture : public H264Picture { ++ public: ++ explicit VaapiH264Picture( ++ scoped_refptr surface) ++ : dec_surface_(surface) {} ++ ++ VaapiH264Picture* AsVaapiH264Picture() override { return this; } ++ scoped_refptr dec_surface() { ++ return dec_surface_; ++ } ++ ++ private: ++ ~VaapiH264Picture() override {} ++ ++ scoped_refptr dec_surface_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiH264Picture); ++}; ++ ++class VaapiVideoDecodeAccelerator::VaapiH264Accelerator ++ : public H264Decoder::H264Accelerator { ++ public: ++ VaapiH264Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, ++ VaapiWrapper* vaapi_wrapper); ++ ~VaapiH264Accelerator() override; ++ ++ // H264Decoder::H264Accelerator implementation. ++ scoped_refptr CreateH264Picture() override; ++ ++ bool SubmitFrameMetadata(const H264SPS* sps, ++ const H264PPS* pps, ++ const H264DPB& dpb, ++ const H264Picture::Vector& ref_pic_listp0, ++ const H264Picture::Vector& ref_pic_listb0, ++ const H264Picture::Vector& ref_pic_listb1, ++ const scoped_refptr& pic) override; ++ ++ bool SubmitSlice(const H264PPS* pps, ++ const H264SliceHeader* slice_hdr, ++ const H264Picture::Vector& ref_pic_list0, ++ const H264Picture::Vector& ref_pic_list1, ++ const scoped_refptr& pic, ++ const uint8_t* data, ++ size_t size) override; ++ ++ bool SubmitDecode(const scoped_refptr& pic) override; ++ bool OutputPicture(const scoped_refptr& pic) override; ++ ++ void Reset() override; ++ ++ private: ++ scoped_refptr H264PictureToVaapiDecodeSurface( ++ const scoped_refptr& pic); ++ ++ void FillVAPicture(VAPictureH264* va_pic, scoped_refptr pic); ++ int FillVARefFramesFromDPB(const H264DPB& dpb, ++ VAPictureH264* va_pics, ++ int num_pics); ++ ++ VaapiWrapper* vaapi_wrapper_; ++ VaapiVideoDecodeAccelerator* vaapi_dec_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiH264Accelerator); ++}; ++ ++class VaapiVP8Picture : public VP8Picture { ++ public: ++ explicit VaapiVP8Picture( ++ scoped_refptr surface) ++ : dec_surface_(surface) {} ++ ++ VaapiVP8Picture* AsVaapiVP8Picture() override { return this; } ++ scoped_refptr dec_surface() { ++ return dec_surface_; ++ } ++ ++ private: ++ ~VaapiVP8Picture() override {} ++ ++ scoped_refptr dec_surface_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiVP8Picture); ++}; ++ ++class VaapiVideoDecodeAccelerator::VaapiVP8Accelerator ++ : public VP8Decoder::VP8Accelerator { ++ public: ++ VaapiVP8Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, ++ VaapiWrapper* vaapi_wrapper); ++ ~VaapiVP8Accelerator() override; ++ ++ // VP8Decoder::VP8Accelerator implementation. ++ scoped_refptr CreateVP8Picture() override; ++ ++ bool SubmitDecode(const scoped_refptr& pic, ++ const Vp8FrameHeader* frame_hdr, ++ const scoped_refptr& last_frame, ++ const scoped_refptr& golden_frame, ++ const scoped_refptr& alt_frame) override; ++ ++ bool OutputPicture(const scoped_refptr& pic) override; ++ ++ private: ++ scoped_refptr VP8PictureToVaapiDecodeSurface( ++ const scoped_refptr& pic); ++ ++ VaapiWrapper* vaapi_wrapper_; ++ VaapiVideoDecodeAccelerator* vaapi_dec_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiVP8Accelerator); ++}; ++ ++class VaapiVP9Picture : public VP9Picture { ++ public: ++ explicit VaapiVP9Picture( ++ scoped_refptr surface) ++ : dec_surface_(surface) {} ++ ++ VaapiVP9Picture* AsVaapiVP9Picture() override { return this; } ++ scoped_refptr dec_surface() { ++ return dec_surface_; ++ } ++ ++ private: ++ ~VaapiVP9Picture() override {} ++ ++ scoped_refptr dec_surface_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiVP9Picture); ++}; ++ ++class VaapiVideoDecodeAccelerator::VaapiVP9Accelerator ++ : public VP9Decoder::VP9Accelerator { ++ public: ++ VaapiVP9Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, ++ VaapiWrapper* vaapi_wrapper); ++ ~VaapiVP9Accelerator() override; ++ ++ // VP9Decoder::VP9Accelerator implementation. ++ scoped_refptr CreateVP9Picture() override; ++ ++ bool SubmitDecode(const scoped_refptr& pic, ++ const Vp9SegmentationParams& seg, ++ const Vp9LoopFilterParams& lf, ++ const std::vector>& ref_pictures, ++ const base::Closure& done_cb) override; ++ ++ bool OutputPicture(const scoped_refptr& pic) override; ++ ++ bool IsFrameContextRequired() const override { return false; } ++ ++ bool GetFrameContext(const scoped_refptr& pic, ++ Vp9FrameContext* frame_ctx) override; ++ ++ private: ++ scoped_refptr VP9PictureToVaapiDecodeSurface( ++ const scoped_refptr& pic); ++ ++ VaapiWrapper* vaapi_wrapper_; ++ VaapiVideoDecodeAccelerator* vaapi_dec_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiVP9Accelerator); ++}; ++ ++class VaapiVideoDecodeAccelerator::InputBuffer { ++ public: ++ InputBuffer() = default; ++ InputBuffer(uint32_t id, ++ std::unique_ptr shm, ++ base::OnceCallback release_cb) ++ : id_(id), shm_(std::move(shm)), release_cb_(std::move(release_cb)) {} ++ ~InputBuffer() { ++ VLOGF(4) << "id = " << id_; ++ if (release_cb_) ++ std::move(release_cb_).Run(id_); ++ } ++ ++ // Indicates this is a dummy buffer for flush request. ++ bool IsFlushRequest() const { return shm_ == nullptr; } ++ int32_t id() const { return id_; } ++ SharedMemoryRegion* shm() const { return shm_.get(); } ++ ++ private: ++ const int32_t id_ = -1; ++ const std::unique_ptr shm_; ++ base::OnceCallback release_cb_; ++ ++ DISALLOW_COPY_AND_ASSIGN(InputBuffer); ++}; ++ ++void VaapiVideoDecodeAccelerator::NotifyError(Error error) { ++ if (!task_runner_->BelongsToCurrentThread()) { ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ task_runner_->PostTask(FROM_HERE, ++ base::Bind(&VaapiVideoDecodeAccelerator::NotifyError, ++ weak_this_, error)); ++ return; ++ } ++ ++ // Post Cleanup() as a task so we don't recursively acquire lock_. ++ task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::Cleanup, weak_this_)); ++ ++ VLOGF(1) << "Notifying of error " << error; ++ if (client_) { ++ client_->NotifyError(error); ++ client_ptr_factory_.reset(); ++ } ++} ++ ++VaapiPicture* VaapiVideoDecodeAccelerator::PictureById( ++ int32_t picture_buffer_id) { ++ Pictures::iterator it = pictures_.find(picture_buffer_id); ++ if (it == pictures_.end()) { ++ VLOGF(4) << "Picture id " << picture_buffer_id << " does not exist"; ++ return NULL; ++ } ++ ++ return it->second.get(); ++} ++ ++VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( ++ const MakeGLContextCurrentCallback& make_context_current_cb, ++ const BindGLImageCallback& bind_image_cb) ++ : state_(kUninitialized), ++ input_ready_(&lock_), ++ vaapi_picture_factory_(new VaapiPictureFactory()), ++ surfaces_available_(&lock_), ++ task_runner_(base::ThreadTaskRunnerHandle::Get()), ++ decoder_thread_("VaapiDecoderThread"), ++ num_frames_at_client_(0), ++ finish_flush_pending_(false), ++ awaiting_va_surfaces_recycle_(false), ++ requested_num_pics_(0), ++ output_format_(gfx::BufferFormat::BGRX_8888), ++ profile_(VIDEO_CODEC_PROFILE_UNKNOWN), ++ make_context_current_cb_(make_context_current_cb), ++ bind_image_cb_(bind_image_cb), ++ weak_this_factory_(this) { ++ weak_this_ = weak_this_factory_.GetWeakPtr(); ++ va_surface_release_cb_ = BindToCurrentLoop( ++ base::Bind(&VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_)); ++} ++ ++VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++} ++ ++bool VaapiVideoDecodeAccelerator::Initialize(const Config& config, ++ Client* client) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ if (config.is_encrypted()) { ++ NOTREACHED() << "Encrypted streams are not supported for this VDA"; ++ return false; ++ } ++ ++ switch (config.output_mode) { ++ case Config::OutputMode::ALLOCATE: ++ output_format_ = vaapi_picture_factory_->GetBufferFormatForAllocateMode(); ++ break; ++ ++ case Config::OutputMode::IMPORT: ++ output_format_ = vaapi_picture_factory_->GetBufferFormatForImportMode(); ++ break; ++ ++ default: ++ NOTREACHED() << "Only ALLOCATE and IMPORT OutputModes are supported"; ++ return false; ++ } ++ ++ client_ptr_factory_.reset(new base::WeakPtrFactory(client)); ++ client_ = client_ptr_factory_->GetWeakPtr(); ++ ++ VideoCodecProfile profile = config.profile; ++ ++ base::AutoLock auto_lock(lock_); ++ DCHECK_EQ(state_, kUninitialized); ++ VLOGF(2) << "Initializing VAVDA, profile: " << GetProfileName(profile); ++ ++ vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec( ++ VaapiWrapper::kDecode, profile, base::Bind(&ReportToUMA, VAAPI_ERROR)); ++ ++ if (!vaapi_wrapper_.get()) { ++ VLOGF(1) << "Failed initializing VAAPI for profile " ++ << GetProfileName(profile); ++ return false; ++ } ++ ++ if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { ++ h264_accelerator_.reset( ++ new VaapiH264Accelerator(this, vaapi_wrapper_.get())); ++ decoder_.reset(new H264Decoder(h264_accelerator_.get())); ++ } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { ++ vp8_accelerator_.reset(new VaapiVP8Accelerator(this, vaapi_wrapper_.get())); ++ decoder_.reset(new VP8Decoder(vp8_accelerator_.get())); ++ } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) { ++ vp9_accelerator_.reset(new VaapiVP9Accelerator(this, vaapi_wrapper_.get())); ++ decoder_.reset(new VP9Decoder(vp9_accelerator_.get())); ++ } else { ++ VLOGF(1) << "Unsupported profile " << GetProfileName(profile); ++ return false; ++ } ++ profile_ = profile; ++ ++ CHECK(decoder_thread_.Start()); ++ decoder_thread_task_runner_ = decoder_thread_.task_runner(); ++ ++ state_ = kIdle; ++ output_mode_ = config.output_mode; ++ return true; ++} ++ ++void VaapiVideoDecodeAccelerator::OutputPicture( ++ const scoped_refptr& va_surface, ++ int32_t input_id, ++ gfx::Rect visible_rect, ++ VaapiPicture* picture) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ int32_t output_id = picture->picture_buffer_id(); ++ ++ VLOGF(4) << "Outputting VASurface " << va_surface->id() ++ << " into pixmap bound to picture buffer id " << output_id; ++ { ++ TRACE_EVENT2("Video Decoder", "VAVDA::DownloadFromSurface", "input_id", ++ input_id, "output_id", output_id); ++ RETURN_AND_NOTIFY_ON_FAILURE(picture->DownloadFromSurface(va_surface), ++ "Failed putting surface into pixmap", ++ PLATFORM_FAILURE, ); ++ } ++ // Notify the client a picture is ready to be displayed. ++ ++num_frames_at_client_; ++ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); ++ VLOGF(4) << "Notifying output picture id " << output_id << " for input " ++ << input_id ++ << " is ready. visible rect: " << visible_rect.ToString(); ++ if (client_) { ++ // TODO(hubbe): Use the correct color space. http://crbug.com/647725 ++ client_->PictureReady(Picture(output_id, input_id, visible_rect, ++ gfx::ColorSpace(), picture->AllowOverlay())); ++ } ++} ++ ++void VaapiVideoDecodeAccelerator::TryOutputSurface() { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ // Handle Destroy() arriving while pictures are queued for output. ++ if (!client_) ++ return; ++ ++ if (pending_output_cbs_.empty() || output_buffers_.empty()) ++ return; ++ ++ OutputCB output_cb = pending_output_cbs_.front(); ++ pending_output_cbs_.pop(); ++ ++ VaapiPicture* picture = PictureById(output_buffers_.front()); ++ DCHECK(picture); ++ output_buffers_.pop(); ++ ++ output_cb.Run(picture); ++ ++ if (finish_flush_pending_ && pending_output_cbs_.empty()) ++ FinishFlush(); ++} ++ ++void VaapiVideoDecodeAccelerator::QueueInputBuffer( ++ const BitstreamBuffer& bitstream_buffer) { ++ VLOGF(4) << "Queueing new input buffer id: " << bitstream_buffer.id() ++ << " size: " << (int)bitstream_buffer.size(); ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ TRACE_EVENT1("Video Decoder", "QueueInputBuffer", "input_id", ++ bitstream_buffer.id()); ++ ++ base::AutoLock auto_lock(lock_); ++ if (bitstream_buffer.size() == 0) { ++ DCHECK(!base::SharedMemory::IsHandleValid(bitstream_buffer.handle())); ++ // Dummy buffer for flush. ++ auto flush_buffer = base::MakeUnique(); ++ DCHECK(flush_buffer->IsFlushRequest()); ++ input_buffers_.push(std::move(flush_buffer)); ++ } else { ++ std::unique_ptr shm( ++ new SharedMemoryRegion(bitstream_buffer, true)); ++ RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(), "Failed to map input buffer", ++ UNREADABLE_INPUT, ); ++ ++ auto input_buffer = base::MakeUnique( ++ bitstream_buffer.id(), std::move(shm), ++ BindToCurrentLoop( ++ base::Bind(&Client::NotifyEndOfBitstreamBuffer, client_))); ++ input_buffers_.push(std::move(input_buffer)); ++ ++ TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); ++ } ++ ++ input_ready_.Signal(); ++ ++ switch (state_) { ++ case kIdle: ++ state_ = kDecoding; ++ decoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, ++ base::Unretained(this))); ++ break; ++ ++ case kDecoding: ++ // Decoder already running. ++ break; ++ ++ case kResetting: ++ // When resetting, allow accumulating bitstream buffers, so that ++ // the client can queue after-seek-buffers while we are finishing with ++ // the before-seek one. ++ break; ++ ++ default: ++ VLOGF(1) << "Decode/Flush request from client in invalid state: " ++ << state_; ++ NotifyError(PLATFORM_FAILURE); ++ break; ++ } ++} ++ ++bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ lock_.AssertAcquired(); ++ ++ if (curr_input_buffer_.get()) ++ return true; ++ ++ // Will only wait if it is expected that in current state new buffers will ++ // be queued from the client via Decode(). The state can change during wait. ++ while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { ++ input_ready_.Wait(); ++ } ++ ++ // We could have got woken up in a different state or never got to sleep ++ // due to current state. ++ if (state_ != kDecoding && state_ != kIdle) ++ return false; ++ ++ DCHECK(!input_buffers_.empty()); ++ curr_input_buffer_ = std::move(input_buffers_.front()); ++ input_buffers_.pop(); ++ ++ if (curr_input_buffer_->IsFlushRequest()) { ++ VLOGF(4) << "New flush buffer"; ++ return true; ++ } ++ ++ VLOGF(4) << "New current input buffer, id: " << curr_input_buffer_->id() ++ << " size: " << curr_input_buffer_->shm()->size() << "B"; ++ decoder_->SetStream( ++ static_cast(curr_input_buffer_->shm()->memory()), ++ curr_input_buffer_->shm()->size()); ++ ++ return true; ++} ++ ++void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ lock_.AssertAcquired(); ++ DCHECK(curr_input_buffer_.get()); ++ curr_input_buffer_.reset(); ++ ++ TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); ++} ++ ++// TODO(posciak): refactor the whole class to remove sleeping in wait for ++// surfaces, and reschedule DecodeTask instead. ++bool VaapiVideoDecodeAccelerator::WaitForSurfaces_Locked() { ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ lock_.AssertAcquired(); ++ ++ while (available_va_surfaces_.empty() && ++ (state_ == kDecoding || state_ == kIdle)) { ++ surfaces_available_.Wait(); ++ } ++ ++ return state_ == kDecoding || state_ == kIdle; ++} ++ ++void VaapiVideoDecodeAccelerator::DecodeTask() { ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ base::AutoLock auto_lock(lock_); ++ ++ if (state_ != kDecoding) ++ return; ++ ++ // Main decode task. ++ VLOGF(4) << "Decode task"; ++ ++ // Try to decode what stream data is (still) in the decoder until we run out ++ // of it. ++ while (GetInputBuffer_Locked()) { ++ DCHECK(curr_input_buffer_.get()); ++ ++ if (curr_input_buffer_->IsFlushRequest()) { ++ FlushTask(); ++ break; ++ } ++ ++ AcceleratedVideoDecoder::DecodeResult res; ++ { ++ // We are OK releasing the lock here, as decoder never calls our methods ++ // directly and we will reacquire the lock before looking at state again. ++ // This is the main decode function of the decoder and while keeping ++ // the lock for its duration would be fine, it would defeat the purpose ++ // of having a separate decoder thread. ++ base::AutoUnlock auto_unlock(lock_); ++ TRACE_EVENT0("Video Decoder", "VAVDA::Decode"); ++ res = decoder_->Decode(); ++ } ++ ++ switch (res) { ++ case AcceleratedVideoDecoder::kAllocateNewSurfaces: ++ VLOGF(2) << "Decoder requesting a new set of surfaces"; ++ task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, ++ weak_this_, decoder_->GetRequiredNumOfPictures(), ++ decoder_->GetPicSize())); ++ // We'll get rescheduled once ProvidePictureBuffers() finishes. ++ return; ++ ++ case AcceleratedVideoDecoder::kRanOutOfStreamData: ++ ReturnCurrInputBuffer_Locked(); ++ break; ++ ++ case AcceleratedVideoDecoder::kRanOutOfSurfaces: ++ // No more output buffers in the decoder, try getting more or go to ++ // sleep waiting for them. ++ if (!WaitForSurfaces_Locked()) ++ return; ++ ++ break; ++ ++ case AcceleratedVideoDecoder::kNeedContextUpdate: ++ // This should not happen as we return false from ++ // IsFrameContextRequired(). ++ NOTREACHED() << "Context updates not supported"; ++ return; ++ ++ case AcceleratedVideoDecoder::kDecodeError: ++ RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", ++ PLATFORM_FAILURE, ); ++ return; ++ } ++ } ++} ++ ++void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, ++ gfx::Size size) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ DCHECK(!awaiting_va_surfaces_recycle_); ++ ++ // At this point decoder has stopped running and has already posted onto our ++ // loop any remaining output request callbacks, which executed before we got ++ // here. Some of them might have been pended though, because we might not ++ // have had enough TFPictures to output surfaces to. Initiate a wait cycle, ++ // which will wait for client to return enough PictureBuffers to us, so that ++ // we can finish all pending output callbacks, releasing associated surfaces. ++ VLOGF(2) << "Initiating surface set change"; ++ awaiting_va_surfaces_recycle_ = true; ++ ++ requested_num_pics_ = num_pics; ++ requested_pic_size_ = size; ++ ++ TryFinishSurfaceSetChange(); ++} ++ ++void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ if (!awaiting_va_surfaces_recycle_) ++ return; ++ ++ if (!pending_output_cbs_.empty() || ++ pictures_.size() != available_va_surfaces_.size()) { ++ // Either: ++ // 1. Not all pending pending output callbacks have been executed yet. ++ // Wait for the client to return enough pictures and retry later. ++ // 2. The above happened and all surface release callbacks have been posted ++ // as the result, but not all have executed yet. Post ourselves after them ++ // to let them release surfaces. ++ DVLOGF(2) << "Awaiting pending output/surface release callbacks to finish"; ++ task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, ++ weak_this_)); ++ return; ++ } ++ ++ // All surfaces released, destroy them and dismiss all PictureBuffers. ++ awaiting_va_surfaces_recycle_ = false; ++ available_va_surfaces_.clear(); ++ vaapi_wrapper_->DestroySurfaces(); ++ ++ for (Pictures::iterator iter = pictures_.begin(); iter != pictures_.end(); ++ ++iter) { ++ VLOGF(2) << "Dismissing picture id: " << iter->first; ++ if (client_) ++ client_->DismissPictureBuffer(iter->first); ++ } ++ pictures_.clear(); ++ ++ // And ask for a new set as requested. ++ VLOGF(2) << "Requesting " << requested_num_pics_ ++ << " pictures of size: " << requested_pic_size_.ToString(); ++ ++ VideoPixelFormat format = GfxBufferFormatToVideoPixelFormat(output_format_); ++ task_runner_->PostTask( ++ FROM_HERE, base::Bind(&Client::ProvidePictureBuffers, client_, ++ requested_num_pics_, format, 1, requested_pic_size_, ++ vaapi_picture_factory_->GetGLTextureTarget())); ++} ++ ++void VaapiVideoDecodeAccelerator::Decode( ++ const BitstreamBuffer& bitstream_buffer) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", ++ bitstream_buffer.id()); ++ ++ if (bitstream_buffer.id() < 0) { ++ if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) ++ base::SharedMemory::CloseHandle(bitstream_buffer.handle()); ++ VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); ++ NotifyError(INVALID_ARGUMENT); ++ return; ++ } ++ ++ // Skip empty buffers. VaapiVDA uses empty buffer as dummy buffer for flush ++ // internally. ++ if (bitstream_buffer.size() == 0) { ++ if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) ++ base::SharedMemory::CloseHandle(bitstream_buffer.handle()); ++ if (client_) ++ client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id()); ++ return; ++ } ++ ++ QueueInputBuffer(bitstream_buffer); ++} ++ ++void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( ++ VASurfaceID va_surface_id) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ base::AutoLock auto_lock(lock_); ++ ++ available_va_surfaces_.push_back(va_surface_id); ++ surfaces_available_.Signal(); ++} ++ ++void VaapiVideoDecodeAccelerator::AssignPictureBuffers( ++ const std::vector& buffers) { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ base::AutoLock auto_lock(lock_); ++ DCHECK(pictures_.empty()); ++ ++ while (!output_buffers_.empty()) ++ output_buffers_.pop(); ++ ++ RETURN_AND_NOTIFY_ON_FAILURE( ++ buffers.size() >= requested_num_pics_, ++ "Got an invalid number of picture buffers. (Got " << buffers.size() ++ << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); ++ DCHECK(requested_pic_size_ == buffers[0].size()); ++ ++ const unsigned int va_format = GetVaFormatForVideoCodecProfile(profile_); ++ std::vector va_surface_ids; ++ RETURN_AND_NOTIFY_ON_FAILURE( ++ vaapi_wrapper_->CreateSurfaces(va_format, requested_pic_size_, ++ buffers.size(), &va_surface_ids), ++ "Failed creating VA Surfaces", PLATFORM_FAILURE, ); ++ DCHECK_EQ(va_surface_ids.size(), buffers.size()); ++ ++ for (size_t i = 0; i < buffers.size(); ++i) { ++ uint32_t client_id = !buffers[i].client_texture_ids().empty() ++ ? buffers[i].client_texture_ids()[0] ++ : 0; ++ uint32_t service_id = !buffers[i].service_texture_ids().empty() ++ ? buffers[i].service_texture_ids()[0] ++ : 0; ++ ++ std::unique_ptr picture(vaapi_picture_factory_->Create( ++ vaapi_wrapper_, make_context_current_cb_, bind_image_cb_, ++ buffers[i].id(), requested_pic_size_, service_id, client_id)); ++ RETURN_AND_NOTIFY_ON_FAILURE( ++ picture.get(), "Failed creating a VaapiPicture", PLATFORM_FAILURE, ); ++ ++ if (output_mode_ == Config::OutputMode::ALLOCATE) { ++ RETURN_AND_NOTIFY_ON_FAILURE( ++ picture->Allocate(output_format_), ++ "Failed to allocate memory for a VaapiPicture", PLATFORM_FAILURE, ); ++ output_buffers_.push(buffers[i].id()); ++ } ++ bool inserted = ++ pictures_.insert(std::make_pair(buffers[i].id(), std::move(picture))) ++ .second; ++ DCHECK(inserted); ++ ++ available_va_surfaces_.push_back(va_surface_ids[i]); ++ surfaces_available_.Signal(); ++ } ++ ++ // Resume DecodeTask if it is still in decoding state. ++ if (state_ == kDecoding) { ++ decoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, ++ base::Unretained(this))); ++ } ++} ++ ++#if defined(USE_OZONE) ++static void CloseGpuMemoryBufferHandle( ++ const gfx::GpuMemoryBufferHandle& handle) { ++ for (const auto& fd : handle.native_pixmap_handle.fds) { ++ // Close the fd by wrapping it in a ScopedFD and letting ++ // it fall out of scope. ++ base::ScopedFD scoped_fd(fd.fd); ++ } ++} ++ ++void VaapiVideoDecodeAccelerator::ImportBufferForPicture( ++ int32_t picture_buffer_id, ++ const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) { ++ VLOGF(2) << "Importing picture id: " << picture_buffer_id; ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ if (output_mode_ != Config::OutputMode::IMPORT) { ++ CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); ++ VLOGF(1) << "Cannot import in non-import mode"; ++ NotifyError(INVALID_ARGUMENT); ++ return; ++ } ++ ++ VaapiPicture* picture = PictureById(picture_buffer_id); ++ if (!picture) { ++ CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); ++ ++ // It's possible that we've already posted a DismissPictureBuffer for this ++ // picture, but it has not yet executed when this ImportBufferForPicture ++ // was posted to us by the client. In that case just ignore this (we've ++ // already dismissed it and accounted for that). ++ VLOGF(3) << "got picture id=" << picture_buffer_id ++ << " not in use (anymore?)."; ++ return; ++ } ++ ++ if (!picture->ImportGpuMemoryBufferHandle(output_format_, ++ gpu_memory_buffer_handle)) { ++ // ImportGpuMemoryBufferHandle will close the handles even on failure, so ++ // we don't need to do this ourselves. ++ VLOGF(1) << "Failed to import GpuMemoryBufferHandle"; ++ NotifyError(PLATFORM_FAILURE); ++ return; ++ } ++ ++ ReusePictureBuffer(picture_buffer_id); ++} ++#endif ++ ++void VaapiVideoDecodeAccelerator::ReusePictureBuffer( ++ int32_t picture_buffer_id) { ++ VLOGF(4) << "picture id=" << picture_buffer_id; ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", ++ picture_buffer_id); ++ ++ if (!PictureById(picture_buffer_id)) { ++ // It's possible that we've already posted a DismissPictureBuffer for this ++ // picture, but it has not yet executed when this ReusePictureBuffer ++ // was posted to us by the client. In that case just ignore this (we've ++ // already dismissed it and accounted for that). ++ VLOGF(3) << "got picture id=" << picture_buffer_id ++ << " not in use (anymore?)."; ++ return; ++ } ++ ++ --num_frames_at_client_; ++ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); ++ ++ output_buffers_.push(picture_buffer_id); ++ TryOutputSurface(); ++} ++ ++void VaapiVideoDecodeAccelerator::FlushTask() { ++ VLOGF(2); ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ DCHECK(curr_input_buffer_.get() && curr_input_buffer_->IsFlushRequest()); ++ ++ curr_input_buffer_.reset(); ++ ++ // First flush all the pictures that haven't been outputted, notifying the ++ // client to output them. ++ bool res = decoder_->Flush(); ++ RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", ++ PLATFORM_FAILURE, ); ++ ++ // Put the decoder in idle state, ready to resume. ++ decoder_->Reset(); ++ ++ task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); ++} ++ ++void VaapiVideoDecodeAccelerator::Flush() { ++ VLOGF(2) << "Got flush request"; ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ // Queue a dummy buffer, which means flush. ++ QueueInputBuffer(media::BitstreamBuffer()); ++} ++ ++void VaapiVideoDecodeAccelerator::FinishFlush() { ++ VLOGF(2); ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ finish_flush_pending_ = false; ++ ++ base::AutoLock auto_lock(lock_); ++ if (state_ != kDecoding) { ++ DCHECK(state_ == kDestroying || state_ == kResetting) << state_; ++ return; ++ } ++ ++ // Still waiting for textures from client to finish outputting all pending ++ // frames. Try again later. ++ if (!pending_output_cbs_.empty()) { ++ finish_flush_pending_ = true; ++ return; ++ } ++ ++ // Resume decoding if necessary. ++ if (input_buffers_.empty()) { ++ state_ = kIdle; ++ } else { ++ decoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, ++ base::Unretained(this))); ++ } ++ ++ task_runner_->PostTask(FROM_HERE, ++ base::Bind(&Client::NotifyFlushDone, client_)); ++} ++ ++void VaapiVideoDecodeAccelerator::ResetTask() { ++ VLOGF(2); ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ ++ // All the decoding tasks from before the reset request from client are done ++ // by now, as this task was scheduled after them and client is expected not ++ // to call Decode() after Reset() and before NotifyResetDone. ++ decoder_->Reset(); ++ ++ base::AutoLock auto_lock(lock_); ++ ++ // Return current input buffer, if present. ++ if (curr_input_buffer_.get()) ++ ReturnCurrInputBuffer_Locked(); ++ ++ // And let client know that we are done with reset. ++ task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); ++} ++ ++void VaapiVideoDecodeAccelerator::Reset() { ++ VLOGF(2) << "Got reset request"; ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ // This will make any new decode tasks exit early. ++ base::AutoLock auto_lock(lock_); ++ state_ = kResetting; ++ finish_flush_pending_ = false; ++ ++ // Drop all remaining input buffers, if present. ++ while (!input_buffers_.empty()) ++ input_buffers_.pop(); ++ TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); ++ ++ decoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, ++ base::Unretained(this))); ++ ++ input_ready_.Signal(); ++ surfaces_available_.Signal(); ++} ++ ++void VaapiVideoDecodeAccelerator::FinishReset() { ++ VLOGF(2); ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ base::AutoLock auto_lock(lock_); ++ ++ if (state_ != kResetting) { ++ DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; ++ return; // We could've gotten destroyed already. ++ } ++ ++ // Drop pending outputs. ++ while (!pending_output_cbs_.empty()) ++ pending_output_cbs_.pop(); ++ ++ if (awaiting_va_surfaces_recycle_) { ++ // Decoder requested a new surface set while we were waiting for it to ++ // finish the last DecodeTask, running at the time of Reset(). ++ // Let the surface set change finish first before resetting. ++ task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); ++ return; ++ } ++ ++ state_ = kIdle; ++ ++ task_runner_->PostTask(FROM_HERE, ++ base::Bind(&Client::NotifyResetDone, client_)); ++ ++ // The client might have given us new buffers via Decode() while we were ++ // resetting and might be waiting for our move, and not call Decode() anymore ++ // until we return something. Post a DecodeTask() so that we won't ++ // sleep forever waiting for Decode() in that case. Having two of them ++ // in the pipe is harmless, the additional one will return as soon as it sees ++ // that we are back in kDecoding state. ++ if (!input_buffers_.empty()) { ++ state_ = kDecoding; ++ decoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, ++ base::Unretained(this))); ++ } ++} ++ ++void VaapiVideoDecodeAccelerator::Cleanup() { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ ++ base::AutoLock auto_lock(lock_); ++ if (state_ == kUninitialized || state_ == kDestroying) ++ return; ++ ++ VLOGF(2) << "Destroying VAVDA"; ++ state_ = kDestroying; ++ ++ client_ptr_factory_.reset(); ++ weak_this_factory_.InvalidateWeakPtrs(); ++ ++ // Signal all potential waiters on the decoder_thread_, let them early-exit, ++ // as we've just moved to the kDestroying state, and wait for all tasks ++ // to finish. ++ input_ready_.Signal(); ++ surfaces_available_.Signal(); ++ { ++ base::AutoUnlock auto_unlock(lock_); ++ decoder_thread_.Stop(); ++ } ++ ++ state_ = kUninitialized; ++} ++ ++void VaapiVideoDecodeAccelerator::Destroy() { ++ DCHECK(task_runner_->BelongsToCurrentThread()); ++ Cleanup(); ++ delete this; ++} ++ ++bool VaapiVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( ++ const base::WeakPtr& decode_client, ++ const scoped_refptr& decode_task_runner) { ++ return false; ++} ++ ++bool VaapiVideoDecodeAccelerator::DecodeSurface( ++ const scoped_refptr& dec_surface) { ++ const bool result = vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( ++ dec_surface->va_surface()->id()); ++ if (!result) ++ VLOGF(1) << "Failed decoding picture"; ++ return result; ++} ++ ++void VaapiVideoDecodeAccelerator::SurfaceReady( ++ const scoped_refptr& dec_surface) { ++ if (!task_runner_->BelongsToCurrentThread()) { ++ task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::SurfaceReady, ++ weak_this_, dec_surface)); ++ return; ++ } ++ ++ DCHECK(!awaiting_va_surfaces_recycle_); ++ ++ { ++ base::AutoLock auto_lock(lock_); ++ // Drop any requests to output if we are resetting or being destroyed. ++ if (state_ == kResetting || state_ == kDestroying) ++ return; ++ } ++ ++ pending_output_cbs_.push( ++ base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, weak_this_, ++ dec_surface->va_surface(), dec_surface->bitstream_id(), ++ dec_surface->visible_rect())); ++ ++ TryOutputSurface(); ++} ++ ++scoped_refptr ++VaapiVideoDecodeAccelerator::CreateSurface() { ++ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); ++ base::AutoLock auto_lock(lock_); ++ ++ if (available_va_surfaces_.empty()) ++ return nullptr; ++ ++ DCHECK(!awaiting_va_surfaces_recycle_); ++ scoped_refptr va_surface(new VASurface( ++ available_va_surfaces_.front(), requested_pic_size_, ++ vaapi_wrapper_->va_surface_format(), va_surface_release_cb_)); ++ available_va_surfaces_.pop_front(); ++ ++ return new VaapiDecodeSurface(curr_input_buffer_->id(), va_surface); ++} ++ ++VaapiVideoDecodeAccelerator::VaapiH264Accelerator::VaapiH264Accelerator( ++ VaapiVideoDecodeAccelerator* vaapi_dec, ++ VaapiWrapper* vaapi_wrapper) ++ : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { ++ DCHECK(vaapi_wrapper_); ++ DCHECK(vaapi_dec_); ++} ++ ++VaapiVideoDecodeAccelerator::VaapiH264Accelerator::~VaapiH264Accelerator() {} ++ ++scoped_refptr ++VaapiVideoDecodeAccelerator::VaapiH264Accelerator::CreateH264Picture() { ++ scoped_refptr va_surface = vaapi_dec_->CreateSurface(); ++ if (!va_surface) ++ return nullptr; ++ ++ return new VaapiH264Picture(std::move(va_surface)); ++} ++ ++// Fill |va_pic| with default/neutral values. ++static void InitVAPicture(VAPictureH264* va_pic) { ++ memset(va_pic, 0, sizeof(*va_pic)); ++ va_pic->picture_id = VA_INVALID_ID; ++ va_pic->flags = VA_PICTURE_H264_INVALID; ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitFrameMetadata( ++ const H264SPS* sps, ++ const H264PPS* pps, ++ const H264DPB& dpb, ++ const H264Picture::Vector& ref_pic_listp0, ++ const H264Picture::Vector& ref_pic_listb0, ++ const H264Picture::Vector& ref_pic_listb1, ++ const scoped_refptr& pic) { ++ VAPictureParameterBufferH264 pic_param; ++ memset(&pic_param, 0, sizeof(pic_param)); ++ ++#define FROM_SPS_TO_PP(a) pic_param.a = sps->a ++#define FROM_SPS_TO_PP2(a, b) pic_param.b = sps->a ++ FROM_SPS_TO_PP2(pic_width_in_mbs_minus1, picture_width_in_mbs_minus1); ++ // This assumes non-interlaced video ++ FROM_SPS_TO_PP2(pic_height_in_map_units_minus1, picture_height_in_mbs_minus1); ++ FROM_SPS_TO_PP(bit_depth_luma_minus8); ++ FROM_SPS_TO_PP(bit_depth_chroma_minus8); ++#undef FROM_SPS_TO_PP ++#undef FROM_SPS_TO_PP2 ++ ++#define FROM_SPS_TO_PP_SF(a) pic_param.seq_fields.bits.a = sps->a ++#define FROM_SPS_TO_PP_SF2(a, b) pic_param.seq_fields.bits.b = sps->a ++ FROM_SPS_TO_PP_SF(chroma_format_idc); ++ FROM_SPS_TO_PP_SF2(separate_colour_plane_flag, ++ residual_colour_transform_flag); ++ FROM_SPS_TO_PP_SF(gaps_in_frame_num_value_allowed_flag); ++ FROM_SPS_TO_PP_SF(frame_mbs_only_flag); ++ FROM_SPS_TO_PP_SF(mb_adaptive_frame_field_flag); ++ FROM_SPS_TO_PP_SF(direct_8x8_inference_flag); ++ pic_param.seq_fields.bits.MinLumaBiPredSize8x8 = (sps->level_idc >= 31); ++ FROM_SPS_TO_PP_SF(log2_max_frame_num_minus4); ++ FROM_SPS_TO_PP_SF(pic_order_cnt_type); ++ FROM_SPS_TO_PP_SF(log2_max_pic_order_cnt_lsb_minus4); ++ FROM_SPS_TO_PP_SF(delta_pic_order_always_zero_flag); ++#undef FROM_SPS_TO_PP_SF ++#undef FROM_SPS_TO_PP_SF2 ++ ++#define FROM_PPS_TO_PP(a) pic_param.a = pps->a ++ FROM_PPS_TO_PP(pic_init_qp_minus26); ++ FROM_PPS_TO_PP(pic_init_qs_minus26); ++ FROM_PPS_TO_PP(chroma_qp_index_offset); ++ FROM_PPS_TO_PP(second_chroma_qp_index_offset); ++#undef FROM_PPS_TO_PP ++ ++#define FROM_PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = pps->a ++#define FROM_PPS_TO_PP_PF2(a, b) pic_param.pic_fields.bits.b = pps->a ++ FROM_PPS_TO_PP_PF(entropy_coding_mode_flag); ++ FROM_PPS_TO_PP_PF(weighted_pred_flag); ++ FROM_PPS_TO_PP_PF(weighted_bipred_idc); ++ FROM_PPS_TO_PP_PF(transform_8x8_mode_flag); ++ ++ pic_param.pic_fields.bits.field_pic_flag = 0; ++ FROM_PPS_TO_PP_PF(constrained_intra_pred_flag); ++ FROM_PPS_TO_PP_PF2(bottom_field_pic_order_in_frame_present_flag, ++ pic_order_present_flag); ++ FROM_PPS_TO_PP_PF(deblocking_filter_control_present_flag); ++ FROM_PPS_TO_PP_PF(redundant_pic_cnt_present_flag); ++ pic_param.pic_fields.bits.reference_pic_flag = pic->ref; ++#undef FROM_PPS_TO_PP_PF ++#undef FROM_PPS_TO_PP_PF2 ++ ++ pic_param.frame_num = pic->frame_num; ++ ++ InitVAPicture(&pic_param.CurrPic); ++ FillVAPicture(&pic_param.CurrPic, pic); ++ ++ // Init reference pictures' array. ++ for (int i = 0; i < 16; ++i) ++ InitVAPicture(&pic_param.ReferenceFrames[i]); ++ ++ // And fill it with picture info from DPB. ++ FillVARefFramesFromDPB(dpb, pic_param.ReferenceFrames, ++ arraysize(pic_param.ReferenceFrames)); ++ ++ pic_param.num_ref_frames = sps->max_num_ref_frames; ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, ++ sizeof(pic_param), &pic_param)) ++ return false; ++ ++ VAIQMatrixBufferH264 iq_matrix_buf; ++ memset(&iq_matrix_buf, 0, sizeof(iq_matrix_buf)); ++ ++ if (pps->pic_scaling_matrix_present_flag) { ++ for (int i = 0; i < 6; ++i) { ++ for (int j = 0; j < 16; ++j) ++ iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = ++ pps->scaling_list4x4[i][j]; ++ } ++ ++ for (int i = 0; i < 2; ++i) { ++ for (int j = 0; j < 64; ++j) ++ iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = ++ pps->scaling_list8x8[i][j]; ++ } ++ } else { ++ for (int i = 0; i < 6; ++i) { ++ for (int j = 0; j < 16; ++j) ++ iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = ++ sps->scaling_list4x4[i][j]; ++ } ++ ++ for (int i = 0; i < 2; ++i) { ++ for (int j = 0; j < 64; ++j) ++ iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = ++ sps->scaling_list8x8[i][j]; ++ } ++ } ++ ++ return vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, ++ sizeof(iq_matrix_buf), &iq_matrix_buf); ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitSlice( ++ const H264PPS* pps, ++ const H264SliceHeader* slice_hdr, ++ const H264Picture::Vector& ref_pic_list0, ++ const H264Picture::Vector& ref_pic_list1, ++ const scoped_refptr& pic, ++ const uint8_t* data, ++ size_t size) { ++ VASliceParameterBufferH264 slice_param; ++ memset(&slice_param, 0, sizeof(slice_param)); ++ ++ slice_param.slice_data_size = slice_hdr->nalu_size; ++ slice_param.slice_data_offset = 0; ++ slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; ++ slice_param.slice_data_bit_offset = slice_hdr->header_bit_size; ++ ++#define SHDRToSP(a) slice_param.a = slice_hdr->a ++ SHDRToSP(first_mb_in_slice); ++ slice_param.slice_type = slice_hdr->slice_type % 5; ++ SHDRToSP(direct_spatial_mv_pred_flag); ++ ++ // TODO posciak: make sure parser sets those even when override flags ++ // in slice header is off. ++ SHDRToSP(num_ref_idx_l0_active_minus1); ++ SHDRToSP(num_ref_idx_l1_active_minus1); ++ SHDRToSP(cabac_init_idc); ++ SHDRToSP(slice_qp_delta); ++ SHDRToSP(disable_deblocking_filter_idc); ++ SHDRToSP(slice_alpha_c0_offset_div2); ++ SHDRToSP(slice_beta_offset_div2); ++ ++ if (((slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) && ++ pps->weighted_pred_flag) || ++ (slice_hdr->IsBSlice() && pps->weighted_bipred_idc == 1)) { ++ SHDRToSP(luma_log2_weight_denom); ++ SHDRToSP(chroma_log2_weight_denom); ++ ++ SHDRToSP(luma_weight_l0_flag); ++ SHDRToSP(luma_weight_l1_flag); ++ ++ SHDRToSP(chroma_weight_l0_flag); ++ SHDRToSP(chroma_weight_l1_flag); ++ ++ for (int i = 0; i <= slice_param.num_ref_idx_l0_active_minus1; ++i) { ++ slice_param.luma_weight_l0[i] = ++ slice_hdr->pred_weight_table_l0.luma_weight[i]; ++ slice_param.luma_offset_l0[i] = ++ slice_hdr->pred_weight_table_l0.luma_offset[i]; ++ ++ for (int j = 0; j < 2; ++j) { ++ slice_param.chroma_weight_l0[i][j] = ++ slice_hdr->pred_weight_table_l0.chroma_weight[i][j]; ++ slice_param.chroma_offset_l0[i][j] = ++ slice_hdr->pred_weight_table_l0.chroma_offset[i][j]; ++ } ++ } ++ ++ if (slice_hdr->IsBSlice()) { ++ for (int i = 0; i <= slice_param.num_ref_idx_l1_active_minus1; ++i) { ++ slice_param.luma_weight_l1[i] = ++ slice_hdr->pred_weight_table_l1.luma_weight[i]; ++ slice_param.luma_offset_l1[i] = ++ slice_hdr->pred_weight_table_l1.luma_offset[i]; ++ ++ for (int j = 0; j < 2; ++j) { ++ slice_param.chroma_weight_l1[i][j] = ++ slice_hdr->pred_weight_table_l1.chroma_weight[i][j]; ++ slice_param.chroma_offset_l1[i][j] = ++ slice_hdr->pred_weight_table_l1.chroma_offset[i][j]; ++ } ++ } ++ } ++ } ++ ++ static_assert( ++ arraysize(slice_param.RefPicList0) == arraysize(slice_param.RefPicList1), ++ "Invalid RefPicList sizes"); ++ ++ for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) { ++ InitVAPicture(&slice_param.RefPicList0[i]); ++ InitVAPicture(&slice_param.RefPicList1[i]); ++ } ++ ++ for (size_t i = 0; ++ i < ref_pic_list0.size() && i < arraysize(slice_param.RefPicList0); ++ ++i) { ++ if (ref_pic_list0[i]) ++ FillVAPicture(&slice_param.RefPicList0[i], ref_pic_list0[i]); ++ } ++ for (size_t i = 0; ++ i < ref_pic_list1.size() && i < arraysize(slice_param.RefPicList1); ++ ++i) { ++ if (ref_pic_list1[i]) ++ FillVAPicture(&slice_param.RefPicList1[i], ref_pic_list1[i]); ++ } ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, ++ sizeof(slice_param), &slice_param)) ++ return false; ++ ++ // Can't help it, blame libva... ++ void* non_const_ptr = const_cast(data); ++ return vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, size, ++ non_const_ptr); ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitDecode( ++ const scoped_refptr& pic) { ++ VLOGF(4) << "Decoding POC " << pic->pic_order_cnt; ++ scoped_refptr dec_surface = ++ H264PictureToVaapiDecodeSurface(pic); ++ ++ return vaapi_dec_->DecodeSurface(dec_surface); ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::OutputPicture( ++ const scoped_refptr& pic) { ++ scoped_refptr dec_surface = ++ H264PictureToVaapiDecodeSurface(pic); ++ dec_surface->set_visible_rect(pic->visible_rect); ++ vaapi_dec_->SurfaceReady(dec_surface); ++ ++ return true; ++} ++ ++void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::Reset() { ++ vaapi_wrapper_->DestroyPendingBuffers(); ++} ++ ++scoped_refptr ++VaapiVideoDecodeAccelerator::VaapiH264Accelerator:: ++ H264PictureToVaapiDecodeSurface(const scoped_refptr& pic) { ++ VaapiH264Picture* vaapi_pic = pic->AsVaapiH264Picture(); ++ CHECK(vaapi_pic); ++ return vaapi_pic->dec_surface(); ++} ++ ++void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVAPicture( ++ VAPictureH264* va_pic, ++ scoped_refptr pic) { ++ VASurfaceID va_surface_id = VA_INVALID_SURFACE; ++ ++ if (!pic->nonexisting) { ++ scoped_refptr dec_surface = ++ H264PictureToVaapiDecodeSurface(pic); ++ va_surface_id = dec_surface->va_surface()->id(); ++ } ++ ++ va_pic->picture_id = va_surface_id; ++ va_pic->frame_idx = pic->frame_num; ++ va_pic->flags = 0; ++ ++ switch (pic->field) { ++ case H264Picture::FIELD_NONE: ++ break; ++ case H264Picture::FIELD_TOP: ++ va_pic->flags |= VA_PICTURE_H264_TOP_FIELD; ++ break; ++ case H264Picture::FIELD_BOTTOM: ++ va_pic->flags |= VA_PICTURE_H264_BOTTOM_FIELD; ++ break; ++ } ++ ++ if (pic->ref) { ++ va_pic->flags |= pic->long_term ? VA_PICTURE_H264_LONG_TERM_REFERENCE ++ : VA_PICTURE_H264_SHORT_TERM_REFERENCE; ++ } ++ ++ va_pic->TopFieldOrderCnt = pic->top_field_order_cnt; ++ va_pic->BottomFieldOrderCnt = pic->bottom_field_order_cnt; ++} ++ ++int VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVARefFramesFromDPB( ++ const H264DPB& dpb, ++ VAPictureH264* va_pics, ++ int num_pics) { ++ H264Picture::Vector::const_reverse_iterator rit; ++ int i; ++ ++ // Return reference frames in reverse order of insertion. ++ // Libva does not document this, but other implementations (e.g. mplayer) ++ // do it this way as well. ++ for (rit = dpb.rbegin(), i = 0; rit != dpb.rend() && i < num_pics; ++rit) { ++ if ((*rit)->ref) ++ FillVAPicture(&va_pics[i++], *rit); ++ } ++ ++ return i; ++} ++ ++VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::VaapiVP8Accelerator( ++ VaapiVideoDecodeAccelerator* vaapi_dec, ++ VaapiWrapper* vaapi_wrapper) ++ : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { ++ DCHECK(vaapi_wrapper_); ++ DCHECK(vaapi_dec_); ++} ++ ++VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::~VaapiVP8Accelerator() {} ++ ++scoped_refptr ++VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::CreateVP8Picture() { ++ scoped_refptr va_surface = vaapi_dec_->CreateSurface(); ++ if (!va_surface) ++ return nullptr; ++ ++ return new VaapiVP8Picture(std::move(va_surface)); ++} ++ ++#define ARRAY_MEMCPY_CHECKED(to, from) \ ++ do { \ ++ static_assert(sizeof(to) == sizeof(from), \ ++ #from " and " #to " arrays must be of same size"); \ ++ memcpy(to, from, sizeof(to)); \ ++ } while (0) ++ ++bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::SubmitDecode( ++ const scoped_refptr& pic, ++ const Vp8FrameHeader* frame_hdr, ++ const scoped_refptr& last_frame, ++ const scoped_refptr& golden_frame, ++ const scoped_refptr& alt_frame) { ++ VAIQMatrixBufferVP8 iq_matrix_buf; ++ memset(&iq_matrix_buf, 0, sizeof(VAIQMatrixBufferVP8)); ++ ++ const Vp8SegmentationHeader& sgmnt_hdr = frame_hdr->segmentation_hdr; ++ const Vp8QuantizationHeader& quant_hdr = frame_hdr->quantization_hdr; ++ static_assert(arraysize(iq_matrix_buf.quantization_index) == kMaxMBSegments, ++ "incorrect quantization matrix size"); ++ for (size_t i = 0; i < kMaxMBSegments; ++i) { ++ int q = quant_hdr.y_ac_qi; ++ ++ if (sgmnt_hdr.segmentation_enabled) { ++ if (sgmnt_hdr.segment_feature_mode == ++ Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) ++ q = sgmnt_hdr.quantizer_update_value[i]; ++ else ++ q += sgmnt_hdr.quantizer_update_value[i]; ++ } ++ ++#define CLAMP_Q(q) std::min(std::max(q, 0), 127) ++ static_assert(arraysize(iq_matrix_buf.quantization_index[i]) == 6, ++ "incorrect quantization matrix size"); ++ iq_matrix_buf.quantization_index[i][0] = CLAMP_Q(q); ++ iq_matrix_buf.quantization_index[i][1] = CLAMP_Q(q + quant_hdr.y_dc_delta); ++ iq_matrix_buf.quantization_index[i][2] = CLAMP_Q(q + quant_hdr.y2_dc_delta); ++ iq_matrix_buf.quantization_index[i][3] = CLAMP_Q(q + quant_hdr.y2_ac_delta); ++ iq_matrix_buf.quantization_index[i][4] = CLAMP_Q(q + quant_hdr.uv_dc_delta); ++ iq_matrix_buf.quantization_index[i][5] = CLAMP_Q(q + quant_hdr.uv_ac_delta); ++#undef CLAMP_Q ++ } ++ ++ if (!vaapi_wrapper_->SubmitBuffer( ++ VAIQMatrixBufferType, sizeof(VAIQMatrixBufferVP8), &iq_matrix_buf)) ++ return false; ++ ++ VAProbabilityDataBufferVP8 prob_buf; ++ memset(&prob_buf, 0, sizeof(VAProbabilityDataBufferVP8)); ++ ++ const Vp8EntropyHeader& entr_hdr = frame_hdr->entropy_hdr; ++ ARRAY_MEMCPY_CHECKED(prob_buf.dct_coeff_probs, entr_hdr.coeff_probs); ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAProbabilityBufferType, ++ sizeof(VAProbabilityDataBufferVP8), ++ &prob_buf)) ++ return false; ++ ++ VAPictureParameterBufferVP8 pic_param; ++ memset(&pic_param, 0, sizeof(VAPictureParameterBufferVP8)); ++ pic_param.frame_width = frame_hdr->width; ++ pic_param.frame_height = frame_hdr->height; ++ ++ if (last_frame) { ++ scoped_refptr last_frame_surface = ++ VP8PictureToVaapiDecodeSurface(last_frame); ++ pic_param.last_ref_frame = last_frame_surface->va_surface()->id(); ++ } else { ++ pic_param.last_ref_frame = VA_INVALID_SURFACE; ++ } ++ ++ if (golden_frame) { ++ scoped_refptr golden_frame_surface = ++ VP8PictureToVaapiDecodeSurface(golden_frame); ++ pic_param.golden_ref_frame = golden_frame_surface->va_surface()->id(); ++ } else { ++ pic_param.golden_ref_frame = VA_INVALID_SURFACE; ++ } ++ ++ if (alt_frame) { ++ scoped_refptr alt_frame_surface = ++ VP8PictureToVaapiDecodeSurface(alt_frame); ++ pic_param.alt_ref_frame = alt_frame_surface->va_surface()->id(); ++ } else { ++ pic_param.alt_ref_frame = VA_INVALID_SURFACE; ++ } ++ ++ pic_param.out_of_loop_frame = VA_INVALID_SURFACE; ++ ++ const Vp8LoopFilterHeader& lf_hdr = frame_hdr->loopfilter_hdr; ++ ++#define FHDR_TO_PP_PF(a, b) pic_param.pic_fields.bits.a = (b) ++ FHDR_TO_PP_PF(key_frame, frame_hdr->IsKeyframe() ? 0 : 1); ++ FHDR_TO_PP_PF(version, frame_hdr->version); ++ FHDR_TO_PP_PF(segmentation_enabled, sgmnt_hdr.segmentation_enabled); ++ FHDR_TO_PP_PF(update_mb_segmentation_map, ++ sgmnt_hdr.update_mb_segmentation_map); ++ FHDR_TO_PP_PF(update_segment_feature_data, ++ sgmnt_hdr.update_segment_feature_data); ++ FHDR_TO_PP_PF(filter_type, lf_hdr.type); ++ FHDR_TO_PP_PF(sharpness_level, lf_hdr.sharpness_level); ++ FHDR_TO_PP_PF(loop_filter_adj_enable, lf_hdr.loop_filter_adj_enable); ++ FHDR_TO_PP_PF(mode_ref_lf_delta_update, lf_hdr.mode_ref_lf_delta_update); ++ FHDR_TO_PP_PF(sign_bias_golden, frame_hdr->sign_bias_golden); ++ FHDR_TO_PP_PF(sign_bias_alternate, frame_hdr->sign_bias_alternate); ++ FHDR_TO_PP_PF(mb_no_coeff_skip, frame_hdr->mb_no_skip_coeff); ++ FHDR_TO_PP_PF(loop_filter_disable, lf_hdr.level == 0); ++#undef FHDR_TO_PP_PF ++ ++ ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, sgmnt_hdr.segment_prob); ++ ++ static_assert(arraysize(sgmnt_hdr.lf_update_value) == ++ arraysize(pic_param.loop_filter_level), ++ "loop filter level arrays mismatch"); ++ for (size_t i = 0; i < arraysize(sgmnt_hdr.lf_update_value); ++i) { ++ int lf_level = lf_hdr.level; ++ if (sgmnt_hdr.segmentation_enabled) { ++ if (sgmnt_hdr.segment_feature_mode == ++ Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) ++ lf_level = sgmnt_hdr.lf_update_value[i]; ++ else ++ lf_level += sgmnt_hdr.lf_update_value[i]; ++ } ++ ++ // Clamp to [0..63] range. ++ lf_level = std::min(std::max(lf_level, 0), 63); ++ pic_param.loop_filter_level[i] = lf_level; ++ } ++ ++ static_assert( ++ arraysize(lf_hdr.ref_frame_delta) == ++ arraysize(pic_param.loop_filter_deltas_ref_frame) && ++ arraysize(lf_hdr.mb_mode_delta) == ++ arraysize(pic_param.loop_filter_deltas_mode) && ++ arraysize(lf_hdr.ref_frame_delta) == arraysize(lf_hdr.mb_mode_delta), ++ "loop filter deltas arrays size mismatch"); ++ for (size_t i = 0; i < arraysize(lf_hdr.ref_frame_delta); ++i) { ++ pic_param.loop_filter_deltas_ref_frame[i] = lf_hdr.ref_frame_delta[i]; ++ pic_param.loop_filter_deltas_mode[i] = lf_hdr.mb_mode_delta[i]; ++ } ++ ++#define FHDR_TO_PP(a) pic_param.a = frame_hdr->a ++ FHDR_TO_PP(prob_skip_false); ++ FHDR_TO_PP(prob_intra); ++ FHDR_TO_PP(prob_last); ++ FHDR_TO_PP(prob_gf); ++#undef FHDR_TO_PP ++ ++ ARRAY_MEMCPY_CHECKED(pic_param.y_mode_probs, entr_hdr.y_mode_probs); ++ ARRAY_MEMCPY_CHECKED(pic_param.uv_mode_probs, entr_hdr.uv_mode_probs); ++ ARRAY_MEMCPY_CHECKED(pic_param.mv_probs, entr_hdr.mv_probs); ++ ++ pic_param.bool_coder_ctx.range = frame_hdr->bool_dec_range; ++ pic_param.bool_coder_ctx.value = frame_hdr->bool_dec_value; ++ pic_param.bool_coder_ctx.count = frame_hdr->bool_dec_count; ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, ++ sizeof(pic_param), &pic_param)) ++ return false; ++ ++ VASliceParameterBufferVP8 slice_param; ++ memset(&slice_param, 0, sizeof(slice_param)); ++ slice_param.slice_data_size = frame_hdr->frame_size; ++ slice_param.slice_data_offset = frame_hdr->first_part_offset; ++ slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; ++ slice_param.macroblock_offset = frame_hdr->macroblock_bit_offset; ++ // Number of DCT partitions plus control partition. ++ slice_param.num_of_partitions = frame_hdr->num_of_dct_partitions + 1; ++ ++ // Per VAAPI, this size only includes the size of the macroblock data in ++ // the first partition (in bytes), so we have to subtract the header size. ++ slice_param.partition_size[0] = ++ frame_hdr->first_part_size - ((frame_hdr->macroblock_bit_offset + 7) / 8); ++ ++ for (size_t i = 0; i < frame_hdr->num_of_dct_partitions; ++i) ++ slice_param.partition_size[i + 1] = frame_hdr->dct_partition_sizes[i]; ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, ++ sizeof(VASliceParameterBufferVP8), ++ &slice_param)) ++ return false; ++ ++ void* non_const_ptr = const_cast(frame_hdr->data); ++ if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, ++ frame_hdr->frame_size, non_const_ptr)) ++ return false; ++ ++ scoped_refptr dec_surface = ++ VP8PictureToVaapiDecodeSurface(pic); ++ ++ return vaapi_dec_->DecodeSurface(dec_surface); ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::OutputPicture( ++ const scoped_refptr& pic) { ++ scoped_refptr dec_surface = ++ VP8PictureToVaapiDecodeSurface(pic); ++ dec_surface->set_visible_rect(pic->visible_rect); ++ vaapi_dec_->SurfaceReady(dec_surface); ++ return true; ++} ++ ++scoped_refptr ++VaapiVideoDecodeAccelerator::VaapiVP8Accelerator:: ++ VP8PictureToVaapiDecodeSurface(const scoped_refptr& pic) { ++ VaapiVP8Picture* vaapi_pic = pic->AsVaapiVP8Picture(); ++ CHECK(vaapi_pic); ++ return vaapi_pic->dec_surface(); ++} ++ ++VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::VaapiVP9Accelerator( ++ VaapiVideoDecodeAccelerator* vaapi_dec, ++ VaapiWrapper* vaapi_wrapper) ++ : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { ++ DCHECK(vaapi_wrapper_); ++ DCHECK(vaapi_dec_); ++} ++ ++VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::~VaapiVP9Accelerator() {} ++ ++scoped_refptr ++VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::CreateVP9Picture() { ++ scoped_refptr va_surface = vaapi_dec_->CreateSurface(); ++ if (!va_surface) ++ return nullptr; ++ ++ return new VaapiVP9Picture(std::move(va_surface)); ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::SubmitDecode( ++ const scoped_refptr& pic, ++ const Vp9SegmentationParams& seg, ++ const Vp9LoopFilterParams& lf, ++ const std::vector>& ref_pictures, ++ const base::Closure& done_cb) { ++ // |done_cb| should be null as we return false from IsFrameContextRequired(). ++ DCHECK(done_cb.is_null()); ++ ++ VADecPictureParameterBufferVP9 pic_param; ++ memset(&pic_param, 0, sizeof(pic_param)); ++ ++ const Vp9FrameHeader* frame_hdr = pic->frame_hdr.get(); ++ DCHECK(frame_hdr); ++ ++ pic_param.frame_width = base::checked_cast(frame_hdr->frame_width); ++ pic_param.frame_height = ++ base::checked_cast(frame_hdr->frame_height); ++ ++ CHECK_EQ(ref_pictures.size(), arraysize(pic_param.reference_frames)); ++ for (size_t i = 0; i < arraysize(pic_param.reference_frames); ++i) { ++ VASurfaceID va_surface_id; ++ if (ref_pictures[i]) { ++ scoped_refptr surface = ++ VP9PictureToVaapiDecodeSurface(ref_pictures[i]); ++ va_surface_id = surface->va_surface()->id(); ++ } else { ++ va_surface_id = VA_INVALID_SURFACE; ++ } ++ ++ pic_param.reference_frames[i] = va_surface_id; ++ } ++ ++#define FHDR_TO_PP_PF1(a) pic_param.pic_fields.bits.a = frame_hdr->a ++#define FHDR_TO_PP_PF2(a, b) pic_param.pic_fields.bits.a = b ++ FHDR_TO_PP_PF2(subsampling_x, frame_hdr->subsampling_x == 1); ++ FHDR_TO_PP_PF2(subsampling_y, frame_hdr->subsampling_y == 1); ++ FHDR_TO_PP_PF2(frame_type, frame_hdr->IsKeyframe() ? 0 : 1); ++ FHDR_TO_PP_PF1(show_frame); ++ FHDR_TO_PP_PF1(error_resilient_mode); ++ FHDR_TO_PP_PF1(intra_only); ++ FHDR_TO_PP_PF1(allow_high_precision_mv); ++ FHDR_TO_PP_PF2(mcomp_filter_type, frame_hdr->interpolation_filter); ++ FHDR_TO_PP_PF1(frame_parallel_decoding_mode); ++ FHDR_TO_PP_PF1(reset_frame_context); ++ FHDR_TO_PP_PF1(refresh_frame_context); ++ FHDR_TO_PP_PF2(frame_context_idx, frame_hdr->frame_context_idx_to_save_probs); ++ FHDR_TO_PP_PF2(segmentation_enabled, seg.enabled); ++ FHDR_TO_PP_PF2(segmentation_temporal_update, seg.temporal_update); ++ FHDR_TO_PP_PF2(segmentation_update_map, seg.update_map); ++ FHDR_TO_PP_PF2(last_ref_frame, frame_hdr->ref_frame_idx[0]); ++ FHDR_TO_PP_PF2(last_ref_frame_sign_bias, ++ frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_LAST]); ++ FHDR_TO_PP_PF2(golden_ref_frame, frame_hdr->ref_frame_idx[1]); ++ FHDR_TO_PP_PF2(golden_ref_frame_sign_bias, ++ frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_GOLDEN]); ++ FHDR_TO_PP_PF2(alt_ref_frame, frame_hdr->ref_frame_idx[2]); ++ FHDR_TO_PP_PF2(alt_ref_frame_sign_bias, ++ frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_ALTREF]); ++ FHDR_TO_PP_PF2(lossless_flag, frame_hdr->quant_params.IsLossless()); ++#undef FHDR_TO_PP_PF2 ++#undef FHDR_TO_PP_PF1 ++ ++ pic_param.filter_level = lf.level; ++ pic_param.sharpness_level = lf.sharpness; ++ pic_param.log2_tile_rows = frame_hdr->tile_rows_log2; ++ pic_param.log2_tile_columns = frame_hdr->tile_cols_log2; ++ pic_param.frame_header_length_in_bytes = frame_hdr->uncompressed_header_size; ++ pic_param.first_partition_size = frame_hdr->header_size_in_bytes; ++ ++ ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, seg.tree_probs); ++ ARRAY_MEMCPY_CHECKED(pic_param.segment_pred_probs, seg.pred_probs); ++ ++ pic_param.profile = frame_hdr->profile; ++ pic_param.bit_depth = frame_hdr->bit_depth; ++ DCHECK((pic_param.profile == 0 && pic_param.bit_depth == 8) || ++ (pic_param.profile == 2 && pic_param.bit_depth == 10)); ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, ++ sizeof(pic_param), &pic_param)) ++ return false; ++ ++ VASliceParameterBufferVP9 slice_param; ++ memset(&slice_param, 0, sizeof(slice_param)); ++ slice_param.slice_data_size = frame_hdr->frame_size; ++ slice_param.slice_data_offset = 0; ++ slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; ++ ++ static_assert(arraysize(Vp9SegmentationParams::feature_enabled) == ++ arraysize(slice_param.seg_param), ++ "seg_param array of incorrect size"); ++ for (size_t i = 0; i < arraysize(slice_param.seg_param); ++i) { ++ VASegmentParameterVP9& seg_param = slice_param.seg_param[i]; ++#define SEG_TO_SP_SF(a, b) seg_param.segment_flags.fields.a = b ++ SEG_TO_SP_SF( ++ segment_reference_enabled, ++ seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); ++ SEG_TO_SP_SF(segment_reference, ++ seg.FeatureData(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); ++ SEG_TO_SP_SF(segment_reference_skipped, ++ seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_SKIP)); ++#undef SEG_TO_SP_SF ++ ++ ARRAY_MEMCPY_CHECKED(seg_param.filter_level, lf.lvl[i]); ++ ++ seg_param.luma_dc_quant_scale = seg.y_dequant[i][0]; ++ seg_param.luma_ac_quant_scale = seg.y_dequant[i][1]; ++ seg_param.chroma_dc_quant_scale = seg.uv_dequant[i][0]; ++ seg_param.chroma_ac_quant_scale = seg.uv_dequant[i][1]; ++ } ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, ++ sizeof(slice_param), &slice_param)) ++ return false; ++ ++ void* non_const_ptr = const_cast(frame_hdr->data); ++ if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, ++ frame_hdr->frame_size, non_const_ptr)) ++ return false; ++ ++ scoped_refptr dec_surface = ++ VP9PictureToVaapiDecodeSurface(pic); ++ ++ return vaapi_dec_->DecodeSurface(dec_surface); ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::OutputPicture( ++ const scoped_refptr& pic) { ++ scoped_refptr dec_surface = ++ VP9PictureToVaapiDecodeSurface(pic); ++ dec_surface->set_visible_rect(pic->visible_rect); ++ vaapi_dec_->SurfaceReady(dec_surface); ++ return true; ++} ++ ++bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::GetFrameContext( ++ const scoped_refptr& pic, ++ Vp9FrameContext* frame_ctx) { ++ NOTIMPLEMENTED() << "Frame context update not supported"; ++ return false; ++} ++ ++scoped_refptr ++VaapiVideoDecodeAccelerator::VaapiVP9Accelerator:: ++ VP9PictureToVaapiDecodeSurface(const scoped_refptr& pic) { ++ VaapiVP9Picture* vaapi_pic = pic->AsVaapiVP9Picture(); ++ CHECK(vaapi_pic); ++ return vaapi_pic->dec_surface(); ++} ++ ++// static ++VideoDecodeAccelerator::SupportedProfiles ++VaapiVideoDecodeAccelerator::GetSupportedProfiles() { ++ return VaapiWrapper::GetSupportedDecodeProfiles(); ++} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.h +@@ -0,0 +1,325 @@ ++// Copyright (c) 2012 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++// ++// This file contains an implementation of VideoDecoderAccelerator ++// that utilizes hardware video decoder present on Intel CPUs. ++ ++#ifndef MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ ++#define MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "base/containers/queue.h" ++#include "base/logging.h" ++#include "base/macros.h" ++#include "base/memory/linked_ptr.h" ++#include "base/memory/ref_counted.h" ++#include "base/memory/weak_ptr.h" ++#include "base/single_thread_task_runner.h" ++#include "base/synchronization/condition_variable.h" ++#include "base/synchronization/lock.h" ++#include "base/threading/thread.h" ++#include "media/base/bitstream_buffer.h" ++#include "media/gpu/gpu_video_decode_accelerator_helpers.h" ++#include "media/gpu/media_gpu_export.h" ++#include "media/gpu/shared_memory_region.h" ++#include "media/gpu/vaapi/vaapi_picture_factory.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++#include "media/video/picture.h" ++#include "media/video/video_decode_accelerator.h" ++ ++namespace gl { ++class GLImage; ++} ++ ++namespace media { ++ ++class AcceleratedVideoDecoder; ++class VaapiPicture; ++ ++// Class to provide video decode acceleration for Intel systems with hardware ++// support for it, and on which libva is available. ++// Decoding tasks are performed in a separate decoding thread. ++// ++// Threading/life-cycle: this object is created & destroyed on the GPU ++// ChildThread. A few methods on it are called on the decoder thread which is ++// stopped during |this->Destroy()|, so any tasks posted to the decoder thread ++// can assume |*this| is still alive. See |weak_this_| below for more details. ++class MEDIA_GPU_EXPORT VaapiVideoDecodeAccelerator ++ : public VideoDecodeAccelerator { ++ public: ++ // Wrapper of a VASurface with id and visible area. ++ class VaapiDecodeSurface; ++ ++ VaapiVideoDecodeAccelerator( ++ const MakeGLContextCurrentCallback& make_context_current_cb, ++ const BindGLImageCallback& bind_image_cb); ++ ++ ~VaapiVideoDecodeAccelerator() override; ++ ++ // VideoDecodeAccelerator implementation. ++ bool Initialize(const Config& config, Client* client) override; ++ void Decode(const BitstreamBuffer& bitstream_buffer) override; ++ void AssignPictureBuffers(const std::vector& buffers) override; ++#if defined(USE_OZONE) ++ void ImportBufferForPicture( ++ int32_t picture_buffer_id, ++ const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override; ++#endif ++ void ReusePictureBuffer(int32_t picture_buffer_id) override; ++ void Flush() override; ++ void Reset() override; ++ void Destroy() override; ++ bool TryToSetupDecodeOnSeparateThread( ++ const base::WeakPtr& decode_client, ++ const scoped_refptr& decode_task_runner) ++ override; ++ ++ static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(); ++ ++ private: ++ friend class VaapiVideoDecodeAcceleratorTest; ++ class VaapiH264Accelerator; ++ class VaapiVP8Accelerator; ++ class VaapiVP9Accelerator; ++ ++ // An input buffer with id provided by the client and awaiting consumption. ++ class InputBuffer; ++ ++ // Notify the client that an error has occurred and decoding cannot continue. ++ void NotifyError(Error error); ++ ++ // Queue a input buffer for decode. ++ void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer); ++ ++ // Get a new input buffer from the queue and set it up in decoder. This will ++ // sleep if no input buffers are available. Return true if a new buffer has ++ // been set up, false if an early exit has been requested (due to initiated ++ // reset/flush/destroy). ++ bool GetInputBuffer_Locked(); ++ ++ // Signal the client that the current buffer has been read and can be ++ // returned. Will also release the mapping. ++ void ReturnCurrInputBuffer_Locked(); ++ ++ // Wait for more surfaces to become available. Return true once they do or ++ // false if an early exit has been requested (due to an initiated ++ // reset/flush/destroy). ++ bool WaitForSurfaces_Locked(); ++ ++ // Continue decoding given input buffers and sleep waiting for input/output ++ // as needed. Will exit if a new set of surfaces or reset/flush/destroy ++ // is requested. ++ void DecodeTask(); ++ ++ // Scheduled after receiving a flush request and executed after the current ++ // decoding task finishes decoding pending inputs. Makes the decoder return ++ // all remaining output pictures and puts it in an idle state, ready ++ // to resume if needed and schedules a FinishFlush. ++ void FlushTask(); ++ ++ // Scheduled by the FlushTask after decoder is flushed to put VAVDA into idle ++ // state and notify the client that flushing has been finished. ++ void FinishFlush(); ++ ++ // Scheduled after receiving a reset request and executed after the current ++ // decoding task finishes decoding the current frame. Puts the decoder into ++ // an idle state, ready to resume if needed, discarding decoded but not yet ++ // outputted pictures (decoder keeps ownership of their associated picture ++ // buffers). Schedules a FinishReset afterwards. ++ void ResetTask(); ++ ++ // Scheduled by ResetTask after it's done putting VAVDA into an idle state. ++ // Drops remaining input buffers and notifies the client that reset has been ++ // finished. ++ void FinishReset(); ++ ++ // Helper for Destroy(), doing all the actual work except for deleting self. ++ void Cleanup(); ++ ++ // Get a usable framebuffer configuration for use in binding textures ++ // or return false on failure. ++ bool InitializeFBConfig(); ++ ++ // Callback to be executed once we have a |va_surface| to be output and ++ // an available |picture| to use for output. ++ // Puts contents of |va_surface| into given |picture|, releases the surface ++ // and passes the resulting picture to client to output the given ++ // |visible_rect| part of it. ++ void OutputPicture(const scoped_refptr& va_surface, ++ int32_t input_id, ++ gfx::Rect visible_rect, ++ VaapiPicture* picture); ++ ++ // Try to OutputPicture() if we have both a ready surface and picture. ++ void TryOutputSurface(); ++ ++ // Called when a VASurface is no longer in use by the decoder or is not being ++ // synced/waiting to be synced to a picture. Returns it to available surfaces ++ // pool. ++ void RecycleVASurfaceID(VASurfaceID va_surface_id); ++ ++ // Initiate wait cycle for surfaces to be released before we release them ++ // and allocate new ones, as requested by the decoder. ++ void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size); ++ ++ // Check if the surfaces have been released or post ourselves for later. ++ void TryFinishSurfaceSetChange(); ++ ++ // ++ // Below methods are used by accelerator implementations. ++ // ++ // Decode of |dec_surface| is ready to be submitted and all codec-specific ++ // settings are set in hardware. ++ bool DecodeSurface(const scoped_refptr& dec_surface); ++ ++ // |dec_surface| is ready to be outputted once decode is finished. ++ // This can be called before decode is actually done in hardware, and this ++ // method is responsible for maintaining the ordering, i.e. the surfaces have ++ // to be outputted in the same order as SurfaceReady is called. ++ // On Intel, we don't have to explicitly maintain the ordering however, as the ++ // driver will maintain ordering, as well as dependencies, and will process ++ // each submitted command in order, and run each command only if its ++ // dependencies are ready. ++ void SurfaceReady(const scoped_refptr& dec_surface); ++ ++ // Return a new VaapiDecodeSurface for decoding into, or nullptr if not ++ // available. ++ scoped_refptr CreateSurface(); ++ ++ // VAVDA state. ++ enum State { ++ // Initialize() not called yet or failed. ++ kUninitialized, ++ // DecodeTask running. ++ kDecoding, ++ // Resetting, waiting for decoder to finish current task and cleanup. ++ kResetting, ++ // Idle, decoder in state ready to start/resume decoding. ++ kIdle, ++ // Destroying, waiting for the decoder to finish current task. ++ kDestroying, ++ }; ++ ++ // Protects input buffer and surface queues and state_. ++ base::Lock lock_; ++ State state_; ++ Config::OutputMode output_mode_; ++ ++ // Queue of available InputBuffers (picture_buffer_ids). ++ base::queue> input_buffers_; ++ // Signalled when input buffers are queued onto |input_buffers_| queue. ++ base::ConditionVariable input_ready_; ++ ++ // Current input buffer at decoder. ++ std::unique_ptr curr_input_buffer_; ++ ++ // Queue for incoming output buffers (texture ids). ++ using OutputBuffers = base::queue; ++ OutputBuffers output_buffers_; ++ ++ std::unique_ptr vaapi_picture_factory_; ++ ++ scoped_refptr vaapi_wrapper_; ++ ++ // All allocated Pictures, regardless of their current state. Pictures are ++ // allocated once using |create_vaapi_picture_callback_| and destroyed at the ++ // end of decode. Comes after |vaapi_wrapper_| to ensure all pictures are ++ // destroyed before said |vaapi_wrapper_| is destroyed. ++ using Pictures = std::map>; ++ Pictures pictures_; ++ ++ // Return a VaapiPicture associated with given client-provided id. ++ VaapiPicture* PictureById(int32_t picture_buffer_id); ++ ++ // VA Surfaces no longer in use that can be passed back to the decoder for ++ // reuse, once it requests them. ++ std::list available_va_surfaces_; ++ // Signalled when output surfaces are queued onto the available_va_surfaces_ ++ // queue. ++ base::ConditionVariable surfaces_available_; ++ ++ // Pending output requests from the decoder. When it indicates that we should ++ // output a surface and we have an available Picture (i.e. texture) ready ++ // to use, we'll execute the callback passing the Picture. The callback ++ // will put the contents of the surface into the picture and return it to ++ // the client, releasing the surface as well. ++ // If we don't have any available Pictures at the time when the decoder ++ // requests output, we'll store the request on pending_output_cbs_ queue for ++ // later and run it once the client gives us more textures ++ // via ReusePictureBuffer(). ++ using OutputCB = base::Callback; ++ base::queue pending_output_cbs_; ++ ++ // ChildThread's task runner. ++ scoped_refptr task_runner_; ++ ++ // WeakPtr<> pointing to |this| for use in posting tasks from the decoder ++ // thread back to the ChildThread. Because the decoder thread is a member of ++ // this class, any task running on the decoder thread is guaranteed that this ++ // object is still alive. As a result, tasks posted from ChildThread to ++ // decoder thread should use base::Unretained(this), and tasks posted from the ++ // decoder thread to the ChildThread should use |weak_this_|. ++ base::WeakPtr weak_this_; ++ ++ // Callback used when creating VASurface objects. ++ VASurface::ReleaseCB va_surface_release_cb_; ++ ++ // To expose client callbacks from VideoDecodeAccelerator. ++ // NOTE: all calls to these objects *MUST* be executed on task_runner_. ++ std::unique_ptr> client_ptr_factory_; ++ base::WeakPtr client_; ++ ++ // Accelerators come after vaapi_wrapper_ to ensure they are destroyed first. ++ std::unique_ptr h264_accelerator_; ++ std::unique_ptr vp8_accelerator_; ++ std::unique_ptr vp9_accelerator_; ++ // After *_accelerator_ to ensure correct destruction order. ++ std::unique_ptr decoder_; ++ ++ base::Thread decoder_thread_; ++ // Use this to post tasks to |decoder_thread_| instead of ++ // |decoder_thread_.message_loop()| because the latter will be NULL once ++ // |decoder_thread_.Stop()| returns. ++ scoped_refptr decoder_thread_task_runner_; ++ ++ int num_frames_at_client_; ++ ++ // Whether we are waiting for any pending_output_cbs_ to be run before ++ // NotifyingFlushDone. ++ bool finish_flush_pending_; ++ ++ // Decoder requested a new surface set and we are waiting for all the surfaces ++ // to be returned before we can free them. ++ bool awaiting_va_surfaces_recycle_; ++ ++ // Last requested number/resolution of output picture buffers and their ++ // format. ++ size_t requested_num_pics_; ++ gfx::Size requested_pic_size_; ++ gfx::BufferFormat output_format_; ++ VideoCodecProfile profile_; ++ ++ // Callback to make GL context current. ++ MakeGLContextCurrentCallback make_context_current_cb_; ++ ++ // Callback to bind a GLImage to a given texture. ++ BindGLImageCallback bind_image_cb_; ++ ++ // The WeakPtrFactory for |weak_this_|. ++ base::WeakPtrFactory weak_this_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc +@@ -0,0 +1,367 @@ ++// Copyright 2017 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" ++ ++#include "base/bind.h" ++#include "base/memory/ptr_util.h" ++#include "base/run_loop.h" ++#include "base/test/scoped_task_environment.h" ++#include "media/gpu/accelerated_video_decoder.h" ++#include "media/gpu/format_utils.h" ++#include "media/gpu/vaapi/vaapi_picture.h" ++#include "media/gpu/vaapi/vaapi_picture_factory.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++ ++using ::testing::_; ++using ::testing::DoAll; ++using ::testing::Invoke; ++using ::testing::Return; ++using ::testing::TestWithParam; ++using ::testing::ValuesIn; ++using ::testing::WithArgs; ++ ++namespace media { ++ ++namespace { ++ ++ACTION_P(RunClosure, closure) { ++ closure.Run(); ++} ++ ++constexpr VideoCodecProfile kCodecProfiles[] = {H264PROFILE_MIN, VP8PROFILE_MIN, ++ VP9PROFILE_MIN}; ++constexpr int kBitstreamId = 123; ++constexpr size_t kInputSize = 256; ++ ++} // namespace ++ ++class MockAcceleratedVideoDecoder : public AcceleratedVideoDecoder { ++ public: ++ MockAcceleratedVideoDecoder() = default; ++ ~MockAcceleratedVideoDecoder() override = default; ++ ++ MOCK_METHOD2(SetStream, void(const uint8_t* ptr, size_t size)); ++ MOCK_METHOD0(Flush, bool()); ++ MOCK_METHOD0(Reset, void()); ++ MOCK_METHOD0(Decode, DecodeResult()); ++ MOCK_CONST_METHOD0(GetPicSize, gfx::Size()); ++ MOCK_CONST_METHOD0(GetRequiredNumOfPictures, size_t()); ++}; ++ ++class MockVaapiWrapper : public VaapiWrapper { ++ public: ++ MockVaapiWrapper() = default; ++ MOCK_METHOD4( ++ CreateSurfaces, ++ bool(unsigned int, const gfx::Size&, size_t, std::vector*)); ++ MOCK_METHOD0(DestroySurfaces, void()); ++ ++ private: ++ ~MockVaapiWrapper() override = default; ++}; ++ ++class MockVaapiPicture : public VaapiPicture { ++ public: ++ MockVaapiPicture(const scoped_refptr& vaapi_wrapper, ++ const MakeGLContextCurrentCallback& make_context_current_cb, ++ const BindGLImageCallback& bind_image_cb, ++ int32_t picture_buffer_id, ++ const gfx::Size& size, ++ uint32_t texture_id, ++ uint32_t client_texture_id) ++ : VaapiPicture(vaapi_wrapper, ++ make_context_current_cb, ++ bind_image_cb, ++ picture_buffer_id, ++ size, ++ texture_id, ++ client_texture_id) {} ++ ~MockVaapiPicture() override = default; ++ ++ // VaapiPicture implementation. ++ bool Allocate(gfx::BufferFormat format) override { return true; } ++ bool ImportGpuMemoryBufferHandle( ++ gfx::BufferFormat format, ++ const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override { ++ return true; ++ } ++ bool DownloadFromSurface( ++ const scoped_refptr& va_surface) override { ++ return true; ++ } ++ bool AllowOverlay() const override { return false; } ++}; ++ ++class MockVaapiPictureFactory : public VaapiPictureFactory { ++ public: ++ MockVaapiPictureFactory() = default; ++ ~MockVaapiPictureFactory() override = default; ++ ++ MOCK_METHOD2(MockCreateVaapiPicture, void(VaapiWrapper*, const gfx::Size&)); ++ std::unique_ptr Create( ++ const scoped_refptr& vaapi_wrapper, ++ const MakeGLContextCurrentCallback& make_context_current_cb, ++ const BindGLImageCallback& bind_image_cb, ++ int32_t picture_buffer_id, ++ const gfx::Size& size, ++ uint32_t texture_id, ++ uint32_t client_texture_id) override { ++ MockCreateVaapiPicture(vaapi_wrapper.get(), size); ++ return std::make_unique( ++ vaapi_wrapper, make_context_current_cb, bind_image_cb, ++ picture_buffer_id, size, texture_id, client_texture_id); ++ } ++}; ++ ++class VaapiVideoDecodeAcceleratorTest : public TestWithParam, ++ public VideoDecodeAccelerator::Client { ++ public: ++ VaapiVideoDecodeAcceleratorTest() ++ : vda_(base::Bind([] { return true; }), ++ base::Bind([](uint32_t client_texture_id, ++ uint32_t texture_target, ++ const scoped_refptr& image, ++ bool can_bind_to_sampler) { return true; })), ++ decoder_thread_("VaapiVideoDecodeAcceleratorTestThread"), ++ mock_decoder_(new MockAcceleratedVideoDecoder), ++ mock_vaapi_picture_factory_(new MockVaapiPictureFactory()), ++ mock_vaapi_wrapper_(new MockVaapiWrapper()), ++ weak_ptr_factory_(this) { ++ decoder_thread_.Start(); ++ ++ // Don't want to go through a vda_->Initialize() because it binds too many ++ // items of the environment. Instead, just start the decoder thread. ++ vda_.decoder_thread_task_runner_ = decoder_thread_.task_runner(); ++ ++ // Plug in all the mocks and ourselves as the |client_|. ++ vda_.decoder_.reset(mock_decoder_); ++ vda_.client_ = weak_ptr_factory_.GetWeakPtr(); ++ vda_.vaapi_wrapper_ = mock_vaapi_wrapper_; ++ vda_.vaapi_picture_factory_.reset(mock_vaapi_picture_factory_); ++ ++ vda_.state_ = VaapiVideoDecodeAccelerator::kIdle; ++ } ++ ~VaapiVideoDecodeAcceleratorTest() {} ++ ++ void SetUp() override { ++ in_shm_.reset(new base::SharedMemory); ++ ASSERT_TRUE(in_shm_->CreateAndMapAnonymous(kInputSize)); ++ } ++ ++ void SetVdaStateToUnitialized() { ++ vda_.state_ = VaapiVideoDecodeAccelerator::kUninitialized; ++ } ++ ++ void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer) { ++ vda_.QueueInputBuffer(bitstream_buffer); ++ } ++ ++ void AssignPictureBuffers(const std::vector& picture_buffers) { ++ vda_.AssignPictureBuffers(picture_buffers); ++ } ++ ++ // Reset epilogue, needed to get |vda_| worker thread out of its Wait(). ++ void ResetSequence() { ++ base::RunLoop run_loop; ++ base::Closure quit_closure = run_loop.QuitClosure(); ++ EXPECT_CALL(*mock_decoder_, Reset()); ++ EXPECT_CALL(*this, NotifyResetDone()).WillOnce(RunClosure(quit_closure)); ++ vda_.Reset(); ++ run_loop.Run(); ++ } ++ ++ // VideoDecodeAccelerator::Client methods. ++ MOCK_METHOD1(NotifyInitializationComplete, void(bool)); ++ MOCK_METHOD5( ++ ProvidePictureBuffers, ++ void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t)); ++ MOCK_METHOD1(DismissPictureBuffer, void(int32_t)); ++ MOCK_METHOD1(PictureReady, void(const Picture&)); ++ MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t)); ++ MOCK_METHOD0(NotifyFlushDone, void()); ++ MOCK_METHOD0(NotifyResetDone, void()); ++ MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error)); ++ ++ base::test::ScopedTaskEnvironment scoped_task_environment_; ++ ++ // The class under test and a worker thread for it. ++ VaapiVideoDecodeAccelerator vda_; ++ base::Thread decoder_thread_; ++ ++ // Ownership passed to |vda_|, but we retain a pointer to it for MOCK checks. ++ MockAcceleratedVideoDecoder* mock_decoder_; ++ MockVaapiPictureFactory* mock_vaapi_picture_factory_; ++ ++ scoped_refptr mock_vaapi_wrapper_; ++ ++ std::unique_ptr in_shm_; ++ ++ private: ++ base::WeakPtrFactory weak_ptr_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAcceleratorTest); ++}; ++ ++// This test checks that QueueInputBuffer() fails when state is kUnitialized. ++TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndError) { ++ SetVdaStateToUnitialized(); ++ ++ base::SharedMemoryHandle handle; ++ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); ++ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); ++ ++ EXPECT_CALL(*this, ++ NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)); ++ QueueInputBuffer(bitstream_buffer); ++} ++ ++// Verifies that Decode() returning kDecodeError ends up pinging NotifyError(). ++TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeError) { ++ base::SharedMemoryHandle handle; ++ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); ++ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); ++ ++ base::RunLoop run_loop; ++ base::Closure quit_closure = run_loop.QuitClosure(); ++ EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); ++ EXPECT_CALL(*mock_decoder_, Decode()) ++ .WillOnce(Return(AcceleratedVideoDecoder::kDecodeError)); ++ EXPECT_CALL(*this, NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)) ++ .WillOnce(RunClosure(quit_closure)); ++ ++ QueueInputBuffer(bitstream_buffer); ++ run_loop.Run(); ++} ++ ++// Tests usual startup sequence: a BitstreamBuffer is enqueued for decode, ++// |vda_| asks for PictureBuffers, that we provide, and then the same Decode() ++// is tried again. ++TEST_P(VaapiVideoDecodeAcceleratorTest, ++ QueueInputBufferAndAssignPictureBuffersAndDecode) { ++ // Try and QueueInputBuffer(), |vda_| will ping us to ProvidePictureBuffers(). ++ const uint32_t kNumPictures = 2; ++ const gfx::Size kPictureSize(64, 48); ++ { ++ base::SharedMemoryHandle handle; ++ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); ++ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); ++ ++ base::RunLoop run_loop; ++ base::Closure quit_closure = run_loop.QuitClosure(); ++ EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); ++ EXPECT_CALL(*mock_decoder_, Decode()) ++ .WillOnce(Return(AcceleratedVideoDecoder::kAllocateNewSurfaces)); ++ ++ EXPECT_CALL(*mock_decoder_, GetRequiredNumOfPictures()) ++ .WillOnce(Return(kNumPictures)); ++ EXPECT_CALL(*mock_decoder_, GetPicSize()).WillOnce(Return(kPictureSize)); ++ EXPECT_CALL(*mock_vaapi_wrapper_, DestroySurfaces()); ++ ++ EXPECT_CALL(*this, ++ ProvidePictureBuffers(kNumPictures, _, 1, kPictureSize, _)) ++ .WillOnce(RunClosure(quit_closure)); ++ ++ QueueInputBuffer(bitstream_buffer); ++ run_loop.Run(); ++ } ++ // AssignPictureBuffers() accordingly and expect another go at Decode(). ++ { ++ base::RunLoop run_loop; ++ base::Closure quit_closure = run_loop.QuitClosure(); ++ ++ const std::vector kPictureBuffers( ++ {{2, kPictureSize}, {3, kPictureSize}}); ++ EXPECT_EQ(kPictureBuffers.size(), kNumPictures); ++ ++ EXPECT_CALL(*mock_vaapi_wrapper_, ++ CreateSurfaces(_, kPictureSize, kNumPictures, _)) ++ .WillOnce(DoAll( ++ WithArgs<3>(Invoke([](std::vector* va_surface_ids) { ++ va_surface_ids->resize(kNumPictures); ++ })), ++ Return(true))); ++ EXPECT_CALL(*mock_vaapi_picture_factory_, ++ MockCreateVaapiPicture(mock_vaapi_wrapper_.get(), kPictureSize)) ++ .Times(2); ++ ++ EXPECT_CALL(*mock_decoder_, Decode()) ++ .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); ++ EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) ++ .WillOnce(RunClosure(quit_closure)); ++ ++ AssignPictureBuffers(kPictureBuffers); ++ run_loop.Run(); ++ } ++ ++ ResetSequence(); ++} ++ ++// Verifies that Decode() replying kRanOutOfStreamData (to signal it's finished) ++// rolls to a NotifyEndOfBitstreamBuffer(). ++TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeFinished) { ++ base::SharedMemoryHandle handle; ++ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); ++ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); ++ ++ { ++ base::RunLoop run_loop; ++ base::Closure quit_closure = run_loop.QuitClosure(); ++ EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); ++ EXPECT_CALL(*mock_decoder_, Decode()) ++ .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); ++ EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) ++ .WillOnce(RunClosure(quit_closure)); ++ ++ QueueInputBuffer(bitstream_buffer); ++ run_loop.Run(); ++ } ++ ++ ResetSequence(); ++} ++ ++// Verify that it is possible to select DRM(egl) and TFP(glx) at runtime. ++TEST_P(VaapiVideoDecodeAcceleratorTest, SupportedPlatforms) { ++ EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationNone, ++ mock_vaapi_picture_factory_->GetVaapiImplementation( ++ gl::kGLImplementationNone)); ++ EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationDrm, ++ mock_vaapi_picture_factory_->GetVaapiImplementation( ++ gl::kGLImplementationEGLGLES2)); ++ ++#if defined(USE_X11) ++ EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationX11, ++ mock_vaapi_picture_factory_->GetVaapiImplementation( ++ gl::kGLImplementationDesktopGL)); ++#endif ++} ++ ++// Verifies the expected buffer format for each output mode. ++TEST_P(VaapiVideoDecodeAcceleratorTest, PictureBufferFormat) { ++ gfx::BufferFormat allocate_format = ++ mock_vaapi_picture_factory_->GetBufferFormatForAllocateMode(); ++ gfx::BufferFormat import_format = ++ mock_vaapi_picture_factory_->GetBufferFormatForImportMode(); ++ ++#if defined(USE_OZONE) ++ EXPECT_EQ(gfx::BufferFormat::BGRX_8888, allocate_format); ++#else ++ EXPECT_EQ(gfx::BufferFormat::RGBX_8888, allocate_format); ++#endif // USE_OZONE ++ ++ EXPECT_EQ(gfx::BufferFormat::YVU_420, import_format); ++ ++ EXPECT_EQ(PIXEL_FORMAT_XRGB, ++ GfxBufferFormatToVideoPixelFormat(allocate_format)); ++ EXPECT_EQ(PIXEL_FORMAT_YV12, ++ GfxBufferFormatToVideoPixelFormat(import_format)); ++} ++ ++INSTANTIATE_TEST_CASE_P(/* No prefix. */, ++ VaapiVideoDecodeAcceleratorTest, ++ ValuesIn(kCodecProfiles)); ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc +@@ -0,0 +1,1102 @@ ++// Copyright 2014 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_video_encode_accelerator.h" ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "base/bind.h" ++#include "base/callback.h" ++#include "base/macros.h" ++#include "base/metrics/histogram_macros.h" ++#include "base/numerics/safe_conversions.h" ++#include "base/single_thread_task_runner.h" ++#include "base/threading/thread_task_runner_handle.h" ++#include "media/base/bind_to_current_loop.h" ++#include "media/gpu/h264_dpb.h" ++#include "media/gpu/shared_memory_region.h" ++ ++#define VLOGF(level) VLOG(level) << __func__ << "(): " ++#define DVLOGF(level) DVLOG(level) << __func__ << "(): " ++ ++#define NOTIFY_ERROR(error, msg) \ ++ do { \ ++ SetState(kError); \ ++ VLOGF(1) << msg; \ ++ VLOGF(1) << "Calling NotifyError(" << error << ")"; \ ++ NotifyError(error); \ ++ } while (0) ++ ++namespace media { ++ ++namespace { ++// Need 2 surfaces for each frame: one for input data and one for ++// reconstructed picture, which is later used for reference. ++const size_t kMinSurfacesToEncode = 2; ++ ++// Subjectively chosen. ++const size_t kNumInputBuffers = 4; ++const size_t kMaxNumReferenceFrames = 4; ++ ++// TODO(owenlin): Adjust the value after b/71367113 is fixed. ++const size_t kExtraOutputBufferSize = 32768; // bytes ++ ++// We need up to kMaxNumReferenceFrames surfaces for reference, plus one ++// for input and one for encode (which will be added to the set of reference ++// frames for subsequent frames). Actual execution of HW encode is done ++// in parallel, and we want to process more frames in the meantime. ++// To have kNumInputBuffers in flight, we need a full set of reference + ++// encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and ++// (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames ++// in flight. ++const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode + ++ kMinSurfacesToEncode * (kNumInputBuffers - 1); ++ ++// An IDR every 2048 frames, an I frame every 256 and no B frames. ++// We choose IDR period to equal MaxFrameNum so it must be a power of 2. ++const int kIDRPeriod = 2048; ++const int kIPeriod = 256; ++const int kIPPeriod = 1; ++ ++const int kDefaultFramerate = 30; ++ ++// HRD parameters (ch. E.2.2 in spec). ++const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters. ++const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters. ++ ++const int kDefaultQP = 26; ++// All Intel codecs can do at least 4.1. ++const int kDefaultLevelIDC = 41; ++const int kChromaFormatIDC = 1; // 4:2:0 ++ ++// Arbitrarily chosen bitrate window size for rate control, in ms. ++const int kCPBWindowSizeMs = 1500; ++ ++// UMA errors that the VaapiVideoEncodeAccelerator class reports. ++enum VAVEAEncoderFailure { ++ VAAPI_ERROR = 0, ++ VAVEA_ENCODER_FAILURES_MAX, ++}; ++} ++ ++// Round |value| up to |alignment|, which must be a power of 2. ++static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) { ++ // Check that |alignment| is a power of 2. ++ DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); ++ return ((value + (alignment - 1)) & ~(alignment - 1)); ++} ++ ++static void ReportToUMA(VAVEAEncoderFailure failure) { ++ UMA_HISTOGRAM_ENUMERATION("Media.VAVEA.EncoderFailure", failure, ++ VAVEA_ENCODER_FAILURES_MAX + 1); ++} ++ ++struct VaapiVideoEncodeAccelerator::InputFrameRef { ++ InputFrameRef(const scoped_refptr& frame, bool force_keyframe) ++ : frame(frame), force_keyframe(force_keyframe) {} ++ const scoped_refptr frame; ++ const bool force_keyframe; ++}; ++ ++struct VaapiVideoEncodeAccelerator::BitstreamBufferRef { ++ BitstreamBufferRef(int32_t id, std::unique_ptr shm) ++ : id(id), shm(std::move(shm)) {} ++ const int32_t id; ++ const std::unique_ptr shm; ++}; ++ ++VideoEncodeAccelerator::SupportedProfiles ++VaapiVideoEncodeAccelerator::GetSupportedProfiles() { ++ return VaapiWrapper::GetSupportedEncodeProfiles(); ++} ++ ++static unsigned int Log2OfPowerOf2(unsigned int x) { ++ CHECK_GT(x, 0u); ++ DCHECK_EQ(x & (x - 1), 0u); ++ ++ int log = 0; ++ while (x > 1) { ++ x >>= 1; ++ ++log; ++ } ++ return log; ++} ++ ++VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator() ++ : profile_(VIDEO_CODEC_PROFILE_UNKNOWN), ++ mb_width_(0), ++ mb_height_(0), ++ output_buffer_byte_size_(0), ++ state_(kUninitialized), ++ frame_num_(0), ++ idr_pic_id_(0), ++ bitrate_(0), ++ framerate_(0), ++ cpb_size_(0), ++ encoding_parameters_changed_(false), ++ encoder_thread_("VAVEAEncoderThread"), ++ child_task_runner_(base::ThreadTaskRunnerHandle::Get()), ++ weak_this_ptr_factory_(this) { ++ VLOGF(2); ++ weak_this_ = weak_this_ptr_factory_.GetWeakPtr(); ++ max_ref_idx_l0_size_ = kMaxNumReferenceFrames; ++ qp_ = kDefaultQP; ++ idr_period_ = kIDRPeriod; ++ i_period_ = kIPeriod; ++ ip_period_ = kIPPeriod; ++} ++ ++VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() { ++ VLOGF(2); ++ DCHECK(child_task_runner_->BelongsToCurrentThread()); ++ DCHECK(!encoder_thread_.IsRunning()); ++} ++ ++bool VaapiVideoEncodeAccelerator::Initialize( ++ VideoPixelFormat format, ++ const gfx::Size& input_visible_size, ++ VideoCodecProfile output_profile, ++ uint32_t initial_bitrate, ++ Client* client) { ++ DCHECK(child_task_runner_->BelongsToCurrentThread()); ++ DCHECK(!encoder_thread_.IsRunning()); ++ DCHECK_EQ(state_, kUninitialized); ++ ++ VLOGF(2) << "Initializing VAVEA, input_format: " ++ << VideoPixelFormatToString(format) ++ << ", input_visible_size: " << input_visible_size.ToString() ++ << ", output_profile: " << GetProfileName(output_profile) ++ << ", initial_bitrate: " << initial_bitrate; ++ ++ client_ptr_factory_.reset(new base::WeakPtrFactory(client)); ++ client_ = client_ptr_factory_->GetWeakPtr(); ++ ++ const SupportedProfiles& profiles = GetSupportedProfiles(); ++ auto profile = find_if(profiles.begin(), profiles.end(), ++ [output_profile](const SupportedProfile& profile) { ++ return profile.profile == output_profile; ++ }); ++ if (profile == profiles.end()) { ++ VLOGF(1) << "Unsupported output profile " << GetProfileName(output_profile); ++ return false; ++ } ++ if (input_visible_size.width() > profile->max_resolution.width() || ++ input_visible_size.height() > profile->max_resolution.height()) { ++ VLOGF(1) << "Input size too big: " << input_visible_size.ToString() ++ << ", max supported size: " << profile->max_resolution.ToString(); ++ return false; ++ } ++ ++ if (format != PIXEL_FORMAT_I420) { ++ VLOGF(1) << "Unsupported input format: " ++ << VideoPixelFormatToString(format); ++ return false; ++ } ++ ++ profile_ = output_profile; ++ visible_size_ = input_visible_size; ++ // 4:2:0 format has to be 2-aligned. ++ DCHECK_EQ(visible_size_.width() % 2, 0); ++ DCHECK_EQ(visible_size_.height() % 2, 0); ++ coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16), ++ RoundUpToPowerOf2(visible_size_.height(), 16)); ++ mb_width_ = coded_size_.width() / 16; ++ mb_height_ = coded_size_.height() / 16; ++ output_buffer_byte_size_ = coded_size_.GetArea() + kExtraOutputBufferSize; ++ ++ UpdateRates(initial_bitrate, kDefaultFramerate); ++ ++ vaapi_wrapper_ = ++ VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile, ++ base::Bind(&ReportToUMA, VAAPI_ERROR)); ++ if (!vaapi_wrapper_.get()) { ++ VLOGF(1) << "Failed initializing VAAPI for profile " ++ << GetProfileName(output_profile); ++ return false; ++ } ++ ++ if (!encoder_thread_.Start()) { ++ VLOGF(1) << "Failed to start encoder thread"; ++ return false; ++ } ++ encoder_thread_task_runner_ = encoder_thread_.task_runner(); ++ ++ // Finish the remaining initialization on the encoder thread. ++ encoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask, ++ base::Unretained(this))); ++ ++ return true; ++} ++ ++void VaapiVideoEncodeAccelerator::InitializeTask() { ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ DCHECK_EQ(state_, kUninitialized); ++ VLOGF(2); ++ ++ va_surface_release_cb_ = BindToCurrentLoop( ++ base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID, ++ base::Unretained(this))); ++ ++ if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, coded_size_, ++ kNumSurfaces, ++ &available_va_surface_ids_)) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces"); ++ return; ++ } ++ ++ UpdateSPS(); ++ GeneratePackedSPS(); ++ ++ UpdatePPS(); ++ GeneratePackedPPS(); ++ ++ child_task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers, ++ coded_size_, output_buffer_byte_size_)); ++ ++ SetState(kEncoding); ++} ++ ++void VaapiVideoEncodeAccelerator::RecycleVASurfaceID( ++ VASurfaceID va_surface_id) { ++ DVLOGF(4) << "va_surface_id: " << va_surface_id; ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ ++ available_va_surface_ids_.push_back(va_surface_id); ++ EncodeFrameTask(); ++} ++ ++void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) { ++ current_pic_ = new H264Picture(); ++ ++ // If the current picture is an IDR picture, frame_num shall be equal to 0. ++ if (force_keyframe) ++ frame_num_ = 0; ++ ++ current_pic_->frame_num = frame_num_++; ++ frame_num_ %= idr_period_; ++ ++ if (current_pic_->frame_num == 0) { ++ current_pic_->idr = true; ++ // H264 spec mandates idr_pic_id to differ between two consecutive IDRs. ++ idr_pic_id_ ^= 1; ++ ref_pic_list0_.clear(); ++ } ++ ++ if (current_pic_->frame_num % i_period_ == 0) ++ current_pic_->type = H264SliceHeader::kISlice; ++ else ++ current_pic_->type = H264SliceHeader::kPSlice; ++ ++ if (current_pic_->type != H264SliceHeader::kBSlice) ++ current_pic_->ref = true; ++ ++ current_pic_->pic_order_cnt = current_pic_->frame_num * 2; ++ current_pic_->top_field_order_cnt = current_pic_->pic_order_cnt; ++ current_pic_->pic_order_cnt_lsb = current_pic_->pic_order_cnt; ++ ++ current_encode_job_->keyframe = current_pic_->idr; ++ ++ DVLOGF(4) << "Starting a new frame, type: " << current_pic_->type ++ << (force_keyframe ? " (forced keyframe)" : "") ++ << " frame_num: " << current_pic_->frame_num ++ << " POC: " << current_pic_->pic_order_cnt; ++} ++ ++void VaapiVideoEncodeAccelerator::EndFrame() { ++ DCHECK(current_pic_); ++ // Store the picture on the list of reference pictures and keep the list ++ // below maximum size, dropping oldest references. ++ if (current_pic_->ref) ++ ref_pic_list0_.push_front(current_encode_job_->recon_surface); ++ size_t max_num_ref_frames = ++ base::checked_cast(current_sps_.max_num_ref_frames); ++ while (ref_pic_list0_.size() > max_num_ref_frames) ++ ref_pic_list0_.pop_back(); ++ ++ submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release())); ++} ++ ++static void InitVAPicture(VAPictureH264* va_pic) { ++ memset(va_pic, 0, sizeof(*va_pic)); ++ va_pic->picture_id = VA_INVALID_ID; ++ va_pic->flags = VA_PICTURE_H264_INVALID; ++} ++ ++bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() { ++ DCHECK(current_pic_); ++ VAEncSequenceParameterBufferH264 seq_param; ++ memset(&seq_param, 0, sizeof(seq_param)); ++ ++#define SPS_TO_SP(a) seq_param.a = current_sps_.a; ++ SPS_TO_SP(seq_parameter_set_id); ++ SPS_TO_SP(level_idc); ++ ++ seq_param.intra_period = i_period_; ++ seq_param.intra_idr_period = idr_period_; ++ seq_param.ip_period = ip_period_; ++ seq_param.bits_per_second = bitrate_; ++ ++ SPS_TO_SP(max_num_ref_frames); ++ seq_param.picture_width_in_mbs = mb_width_; ++ seq_param.picture_height_in_mbs = mb_height_; ++ ++#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a; ++ SPS_TO_SP_FS(chroma_format_idc); ++ SPS_TO_SP_FS(frame_mbs_only_flag); ++ SPS_TO_SP_FS(log2_max_frame_num_minus4); ++ SPS_TO_SP_FS(pic_order_cnt_type); ++ SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4); ++#undef SPS_TO_SP_FS ++ ++ SPS_TO_SP(bit_depth_luma_minus8); ++ SPS_TO_SP(bit_depth_chroma_minus8); ++ ++ SPS_TO_SP(frame_cropping_flag); ++ if (current_sps_.frame_cropping_flag) { ++ SPS_TO_SP(frame_crop_left_offset); ++ SPS_TO_SP(frame_crop_right_offset); ++ SPS_TO_SP(frame_crop_top_offset); ++ SPS_TO_SP(frame_crop_bottom_offset); ++ } ++ ++ SPS_TO_SP(vui_parameters_present_flag); ++#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a; ++ SPS_TO_SP_VF(timing_info_present_flag); ++#undef SPS_TO_SP_VF ++ SPS_TO_SP(num_units_in_tick); ++ SPS_TO_SP(time_scale); ++#undef SPS_TO_SP ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType, ++ sizeof(seq_param), &seq_param)) ++ return false; ++ ++ VAEncPictureParameterBufferH264 pic_param; ++ memset(&pic_param, 0, sizeof(pic_param)); ++ ++ pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id(); ++ pic_param.CurrPic.TopFieldOrderCnt = current_pic_->top_field_order_cnt; ++ pic_param.CurrPic.BottomFieldOrderCnt = current_pic_->bottom_field_order_cnt; ++ pic_param.CurrPic.flags = 0; ++ ++ for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i) ++ InitVAPicture(&pic_param.ReferenceFrames[i]); ++ ++ DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames)); ++ RefPicList::const_iterator iter = ref_pic_list0_.begin(); ++ for (size_t i = 0; ++ i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end(); ++ ++iter, ++i) { ++ pic_param.ReferenceFrames[i].picture_id = (*iter)->id(); ++ pic_param.ReferenceFrames[i].flags = 0; ++ } ++ ++ pic_param.coded_buf = current_encode_job_->coded_buffer; ++ pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; ++ pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id; ++ pic_param.frame_num = current_pic_->frame_num; ++ pic_param.pic_init_qp = qp_; ++ pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1; ++ pic_param.pic_fields.bits.idr_pic_flag = current_pic_->idr; ++ pic_param.pic_fields.bits.reference_pic_flag = current_pic_->ref; ++#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a; ++ PPS_TO_PP_PF(entropy_coding_mode_flag); ++ PPS_TO_PP_PF(transform_8x8_mode_flag); ++ PPS_TO_PP_PF(deblocking_filter_control_present_flag); ++#undef PPS_TO_PP_PF ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, ++ sizeof(pic_param), &pic_param)) ++ return false; ++ ++ VAEncSliceParameterBufferH264 slice_param; ++ memset(&slice_param, 0, sizeof(slice_param)); ++ ++ slice_param.num_macroblocks = mb_width_ * mb_height_; ++ slice_param.macroblock_info = VA_INVALID_ID; ++ slice_param.slice_type = current_pic_->type; ++ slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; ++ slice_param.idr_pic_id = idr_pic_id_; ++ slice_param.pic_order_cnt_lsb = current_pic_->pic_order_cnt_lsb; ++ slice_param.num_ref_idx_active_override_flag = true; ++ ++ for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) ++ InitVAPicture(&slice_param.RefPicList0[i]); ++ ++ for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i) ++ InitVAPicture(&slice_param.RefPicList1[i]); ++ ++ DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0)); ++ iter = ref_pic_list0_.begin(); ++ for (size_t i = 0; ++ i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end(); ++ ++iter, ++i) { ++ InitVAPicture(&slice_param.RefPicList0[i]); ++ slice_param.RefPicList0[i].picture_id = (*iter)->id(); ++ slice_param.RefPicList0[i].flags = 0; ++ } ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, ++ sizeof(slice_param), &slice_param)) ++ return false; ++ ++ VAEncMiscParameterRateControl rate_control_param; ++ memset(&rate_control_param, 0, sizeof(rate_control_param)); ++ rate_control_param.bits_per_second = bitrate_; ++ rate_control_param.target_percentage = 90; ++ rate_control_param.window_size = kCPBWindowSizeMs; ++ rate_control_param.initial_qp = qp_; ++ rate_control_param.rc_flags.bits.disable_frame_skip = true; ++ ++ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( ++ VAEncMiscParameterTypeRateControl, sizeof(rate_control_param), ++ &rate_control_param)) ++ return false; ++ ++ VAEncMiscParameterFrameRate framerate_param; ++ memset(&framerate_param, 0, sizeof(framerate_param)); ++ framerate_param.framerate = framerate_; ++ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( ++ VAEncMiscParameterTypeFrameRate, sizeof(framerate_param), ++ &framerate_param)) ++ return false; ++ ++ VAEncMiscParameterHRD hrd_param; ++ memset(&hrd_param, 0, sizeof(hrd_param)); ++ hrd_param.buffer_size = cpb_size_; ++ hrd_param.initial_buffer_fullness = cpb_size_ / 2; ++ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( ++ VAEncMiscParameterTypeHRD, sizeof(hrd_param), &hrd_param)) ++ return false; ++ ++ return true; ++} ++ ++bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() { ++ DCHECK(current_pic_); ++ if (current_pic_->type != H264SliceHeader::kISlice) ++ return true; ++ ++ // Submit SPS. ++ VAEncPackedHeaderParameterBuffer par_buffer; ++ memset(&par_buffer, 0, sizeof(par_buffer)); ++ par_buffer.type = VAEncPackedHeaderSequence; ++ par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8; ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, ++ sizeof(par_buffer), &par_buffer)) ++ return false; ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, ++ packed_sps_.BytesInBuffer(), ++ packed_sps_.data())) ++ return false; ++ ++ // Submit PPS. ++ memset(&par_buffer, 0, sizeof(par_buffer)); ++ par_buffer.type = VAEncPackedHeaderPicture; ++ par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8; ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, ++ sizeof(par_buffer), &par_buffer)) ++ return false; ++ ++ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, ++ packed_pps_.BytesInBuffer(), ++ packed_pps_.data())) ++ return false; ++ ++ return true; ++} ++ ++bool VaapiVideoEncodeAccelerator::ExecuteEncode() { ++ DCHECK(current_pic_); ++ DVLOGF(4) << "Encoding frame_num: " << current_pic_->frame_num; ++ return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( ++ current_encode_job_->input_surface->id()); ++} ++ ++bool VaapiVideoEncodeAccelerator::UploadFrame( ++ const scoped_refptr& frame) { ++ return vaapi_wrapper_->UploadVideoFrameToSurface( ++ frame, current_encode_job_->input_surface->id()); ++} ++ ++void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() { ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ ++ if (state_ != kEncoding) ++ return; ++ ++ while (!submitted_encode_jobs_.empty()) { ++ linked_ptr encode_job = submitted_encode_jobs_.front(); ++ // An null job indicates a flush command. ++ if (encode_job == nullptr) { ++ submitted_encode_jobs_.pop(); ++ DVLOGF(2) << "FlushDone"; ++ DCHECK(flush_callback_); ++ child_task_runner_->PostTask( ++ FROM_HERE, base::BindOnce(std::move(flush_callback_), true)); ++ continue; ++ } ++ ++ if (available_bitstream_buffers_.empty()) ++ break; ++ auto buffer = available_bitstream_buffers_.front(); ++ ++ available_bitstream_buffers_.pop(); ++ submitted_encode_jobs_.pop(); ++ ++ uint8_t* target_data = reinterpret_cast(buffer->shm->memory()); ++ ++ size_t data_size = 0; ++ if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer( ++ encode_job->coded_buffer, encode_job->input_surface->id(), ++ target_data, buffer->shm->size(), &data_size)) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer"); ++ return; ++ } ++ ++ DVLOGF(4) << "Returning bitstream buffer " ++ << (encode_job->keyframe ? "(keyframe)" : "") ++ << " id: " << buffer->id << " size: " << data_size; ++ ++ child_task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&Client::BitstreamBufferReady, client_, buffer->id, ++ data_size, encode_job->keyframe, encode_job->timestamp)); ++ break; ++ } ++} ++ ++void VaapiVideoEncodeAccelerator::Encode(const scoped_refptr& frame, ++ bool force_keyframe) { ++ DVLOGF(4) << "Frame timestamp: " << frame->timestamp().InMilliseconds() ++ << " force_keyframe: " << force_keyframe; ++ DCHECK(child_task_runner_->BelongsToCurrentThread()); ++ ++ encoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask, ++ base::Unretained(this), frame, force_keyframe)); ++} ++ ++bool VaapiVideoEncodeAccelerator::PrepareNextJob(base::TimeDelta timestamp) { ++ if (available_va_surface_ids_.size() < kMinSurfacesToEncode) ++ return false; ++ ++ DCHECK(!current_encode_job_); ++ current_encode_job_.reset(new EncodeJob()); ++ ++ if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_, ++ ¤t_encode_job_->coded_buffer)) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer"); ++ return false; ++ } ++ ++ current_encode_job_->timestamp = timestamp; ++ ++ current_encode_job_->input_surface = new VASurface( ++ available_va_surface_ids_.back(), coded_size_, ++ vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); ++ available_va_surface_ids_.pop_back(); ++ ++ current_encode_job_->recon_surface = new VASurface( ++ available_va_surface_ids_.back(), coded_size_, ++ vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); ++ available_va_surface_ids_.pop_back(); ++ ++ // Reference surfaces are needed until the job is done, but they get ++ // removed from ref_pic_list0_ when it's full at the end of job submission. ++ // Keep refs to them along with the job and only release after sync. ++ current_encode_job_->reference_surfaces = ref_pic_list0_; ++ ++ return true; ++} ++ ++void VaapiVideoEncodeAccelerator::EncodeTask( ++ const scoped_refptr& frame, ++ bool force_keyframe) { ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ DCHECK_NE(state_, kUninitialized); ++ ++ encoder_input_queue_.push( ++ make_linked_ptr(new InputFrameRef(frame, force_keyframe))); ++ EncodeFrameTask(); ++} ++ ++void VaapiVideoEncodeAccelerator::EncodeFrameTask() { ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ ++ if (state_ != kEncoding || encoder_input_queue_.empty()) ++ return; ++ ++ if (!PrepareNextJob(encoder_input_queue_.front()->frame->timestamp())) { ++ DVLOGF(4) << "Not ready for next frame yet"; ++ return; ++ } ++ ++ linked_ptr frame_ref = encoder_input_queue_.front(); ++ encoder_input_queue_.pop(); ++ ++ if (!UploadFrame(frame_ref->frame)) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW."); ++ return; ++ } ++ ++ BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_); ++ encoding_parameters_changed_ = false; ++ ++ if (!SubmitFrameParameters()) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters."); ++ return; ++ } ++ ++ if (!SubmitHeadersIfNeeded()) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers."); ++ return; ++ } ++ ++ if (!ExecuteEncode()) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW."); ++ return; ++ } ++ ++ EndFrame(); ++ TryToReturnBitstreamBuffer(); ++} ++ ++void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer( ++ const BitstreamBuffer& buffer) { ++ DVLOGF(4) << "id: " << buffer.id(); ++ DCHECK(child_task_runner_->BelongsToCurrentThread()); ++ ++ if (buffer.size() < output_buffer_byte_size_) { ++ NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small"); ++ return; ++ } ++ ++ std::unique_ptr shm( ++ new SharedMemoryRegion(buffer, false)); ++ if (!shm->Map()) { ++ NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory."); ++ return; ++ } ++ ++ std::unique_ptr buffer_ref( ++ new BitstreamBufferRef(buffer.id(), std::move(shm))); ++ ++ encoder_thread_task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask, ++ base::Unretained(this), base::Passed(&buffer_ref))); ++} ++ ++void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask( ++ std::unique_ptr buffer_ref) { ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ DCHECK_NE(state_, kUninitialized); ++ ++ available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release())); ++ TryToReturnBitstreamBuffer(); ++} ++ ++void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange( ++ uint32_t bitrate, ++ uint32_t framerate) { ++ VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; ++ DCHECK(child_task_runner_->BelongsToCurrentThread()); ++ ++ encoder_thread_task_runner_->PostTask( ++ FROM_HERE, ++ base::Bind( ++ &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask, ++ base::Unretained(this), bitrate, framerate)); ++} ++ ++void VaapiVideoEncodeAccelerator::UpdateRates(uint32_t bitrate, ++ uint32_t framerate) { ++ if (encoder_thread_.IsRunning()) ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ DCHECK_NE(bitrate, 0u); ++ DCHECK_NE(framerate, 0u); ++ bitrate_ = bitrate; ++ framerate_ = framerate; ++ cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000; ++} ++ ++void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask( ++ uint32_t bitrate, ++ uint32_t framerate) { ++ VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ DCHECK_NE(state_, kUninitialized); ++ ++ // This is a workaround to zero being temporarily, as part of the initial ++ // setup, provided by the webrtc video encode and a zero bitrate and ++ // framerate not being accepted by VAAPI ++ // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps ++ // it could be pulled up to RTCVideoEncoder ++ if (bitrate < 1) ++ bitrate = 1; ++ if (framerate < 1) ++ framerate = 1; ++ ++ if (bitrate_ == bitrate && framerate_ == framerate) ++ return; ++ ++ UpdateRates(bitrate, framerate); ++ ++ UpdateSPS(); ++ GeneratePackedSPS(); ++ ++ // Submit new parameters along with next frame that will be processed. ++ encoding_parameters_changed_ = true; ++} ++ ++void VaapiVideoEncodeAccelerator::Flush(FlushCallback flush_callback) { ++ DVLOGF(2); ++ DCHECK(child_task_runner_->BelongsToCurrentThread()); ++ if (flush_callback_) { ++ NOTIFY_ERROR(kIllegalStateError, "There is a pending flush"); ++ std::move(flush_callback).Run(false); ++ return; ++ } ++ flush_callback_ = std::move(flush_callback); ++ encoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::FlushTask, ++ base::Unretained(this))); ++} ++ ++void VaapiVideoEncodeAccelerator::FlushTask() { ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ ++ // Insert an null job to indicate a flush command. ++ submitted_encode_jobs_.push(linked_ptr(nullptr)); ++ TryToReturnBitstreamBuffer(); ++} ++ ++void VaapiVideoEncodeAccelerator::Destroy() { ++ DCHECK(child_task_runner_->BelongsToCurrentThread()); ++ ++ // Can't call client anymore after Destroy() returns. ++ client_ptr_factory_.reset(); ++ weak_this_ptr_factory_.InvalidateWeakPtrs(); ++ ++ // Early-exit encoder tasks if they are running and join the thread. ++ if (encoder_thread_.IsRunning()) { ++ encoder_thread_.task_runner()->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask, ++ base::Unretained(this))); ++ encoder_thread_.Stop(); ++ } ++ ++ if (flush_callback_) ++ std::move(flush_callback_).Run(false); ++ ++ delete this; ++} ++ ++void VaapiVideoEncodeAccelerator::DestroyTask() { ++ VLOGF(2); ++ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); ++ SetState(kError); ++} ++ ++void VaapiVideoEncodeAccelerator::UpdateSPS() { ++ memset(¤t_sps_, 0, sizeof(H264SPS)); ++ ++ // Spec A.2 and A.3. ++ switch (profile_) { ++ case H264PROFILE_BASELINE: ++ // Due to crbug.com/345569, we don't distinguish between constrained ++ // and non-constrained baseline profiles. Since many codecs can't do ++ // non-constrained, and constrained is usually what we mean (and it's a ++ // subset of non-constrained), default to it. ++ current_sps_.profile_idc = H264SPS::kProfileIDCBaseline; ++ current_sps_.constraint_set0_flag = true; ++ break; ++ case H264PROFILE_MAIN: ++ current_sps_.profile_idc = H264SPS::kProfileIDCMain; ++ current_sps_.constraint_set1_flag = true; ++ break; ++ case H264PROFILE_HIGH: ++ current_sps_.profile_idc = H264SPS::kProfileIDCHigh; ++ break; ++ default: ++ NOTIMPLEMENTED(); ++ return; ++ } ++ ++ current_sps_.level_idc = kDefaultLevelIDC; ++ current_sps_.seq_parameter_set_id = 0; ++ current_sps_.chroma_format_idc = kChromaFormatIDC; ++ ++ DCHECK_GE(idr_period_, 1u << 4); ++ current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4; ++ current_sps_.pic_order_cnt_type = 0; ++ current_sps_.log2_max_pic_order_cnt_lsb_minus4 = ++ Log2OfPowerOf2(idr_period_ * 2) - 4; ++ current_sps_.max_num_ref_frames = max_ref_idx_l0_size_; ++ ++ current_sps_.frame_mbs_only_flag = true; ++ ++ DCHECK_GT(mb_width_, 0u); ++ DCHECK_GT(mb_height_, 0u); ++ current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1; ++ DCHECK(current_sps_.frame_mbs_only_flag); ++ current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1; ++ ++ if (visible_size_ != coded_size_) { ++ // Visible size differs from coded size, fill crop information. ++ current_sps_.frame_cropping_flag = true; ++ DCHECK(!current_sps_.separate_colour_plane_flag); ++ // Spec table 6-1. Only 4:2:0 for now. ++ DCHECK_EQ(current_sps_.chroma_format_idc, 1); ++ // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0. ++ const unsigned int crop_unit_x = 2; ++ const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag); ++ current_sps_.frame_crop_left_offset = 0; ++ current_sps_.frame_crop_right_offset = ++ (coded_size_.width() - visible_size_.width()) / crop_unit_x; ++ current_sps_.frame_crop_top_offset = 0; ++ current_sps_.frame_crop_bottom_offset = ++ (coded_size_.height() - visible_size_.height()) / crop_unit_y; ++ } ++ ++ current_sps_.vui_parameters_present_flag = true; ++ current_sps_.timing_info_present_flag = true; ++ current_sps_.num_units_in_tick = 1; ++ current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec. ++ current_sps_.fixed_frame_rate_flag = true; ++ ++ current_sps_.nal_hrd_parameters_present_flag = true; ++ // H.264 spec ch. E.2.2. ++ current_sps_.cpb_cnt_minus1 = 0; ++ current_sps_.bit_rate_scale = kBitRateScale; ++ current_sps_.cpb_size_scale = kCPBSizeScale; ++ current_sps_.bit_rate_value_minus1[0] = ++ (bitrate_ >> (kBitRateScale + H264SPS::kBitRateScaleConstantTerm)) - 1; ++ current_sps_.cpb_size_value_minus1[0] = ++ (cpb_size_ >> (kCPBSizeScale + H264SPS::kCPBSizeScaleConstantTerm)) - 1; ++ current_sps_.cbr_flag[0] = true; ++ current_sps_.initial_cpb_removal_delay_length_minus_1 = ++ H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; ++ current_sps_.cpb_removal_delay_length_minus1 = ++ H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; ++ current_sps_.dpb_output_delay_length_minus1 = ++ H264SPS::kDefaultDPBOutputDelayLength - 1; ++ current_sps_.time_offset_length = H264SPS::kDefaultTimeOffsetLength; ++ current_sps_.low_delay_hrd_flag = false; ++} ++ ++void VaapiVideoEncodeAccelerator::GeneratePackedSPS() { ++ packed_sps_.Reset(); ++ ++ packed_sps_.BeginNALU(H264NALU::kSPS, 3); ++ ++ packed_sps_.AppendBits(8, current_sps_.profile_idc); ++ packed_sps_.AppendBool(current_sps_.constraint_set0_flag); ++ packed_sps_.AppendBool(current_sps_.constraint_set1_flag); ++ packed_sps_.AppendBool(current_sps_.constraint_set2_flag); ++ packed_sps_.AppendBool(current_sps_.constraint_set3_flag); ++ packed_sps_.AppendBool(current_sps_.constraint_set4_flag); ++ packed_sps_.AppendBool(current_sps_.constraint_set5_flag); ++ packed_sps_.AppendBits(2, 0); // reserved_zero_2bits ++ packed_sps_.AppendBits(8, current_sps_.level_idc); ++ packed_sps_.AppendUE(current_sps_.seq_parameter_set_id); ++ ++ if (current_sps_.profile_idc == H264SPS::kProfileIDCHigh) { ++ packed_sps_.AppendUE(current_sps_.chroma_format_idc); ++ if (current_sps_.chroma_format_idc == 3) ++ packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag); ++ packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8); ++ packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8); ++ packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag); ++ packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag); ++ CHECK(!current_sps_.seq_scaling_matrix_present_flag); ++ } ++ ++ packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4); ++ packed_sps_.AppendUE(current_sps_.pic_order_cnt_type); ++ if (current_sps_.pic_order_cnt_type == 0) ++ packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4); ++ else if (current_sps_.pic_order_cnt_type == 1) { ++ CHECK(1); ++ } ++ ++ packed_sps_.AppendUE(current_sps_.max_num_ref_frames); ++ packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag); ++ packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1); ++ packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1); ++ ++ packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag); ++ if (!current_sps_.frame_mbs_only_flag) ++ packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag); ++ ++ packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag); ++ ++ packed_sps_.AppendBool(current_sps_.frame_cropping_flag); ++ if (current_sps_.frame_cropping_flag) { ++ packed_sps_.AppendUE(current_sps_.frame_crop_left_offset); ++ packed_sps_.AppendUE(current_sps_.frame_crop_right_offset); ++ packed_sps_.AppendUE(current_sps_.frame_crop_top_offset); ++ packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset); ++ } ++ ++ packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag); ++ if (current_sps_.vui_parameters_present_flag) { ++ packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag ++ packed_sps_.AppendBool(false); // overscan_info_present_flag ++ packed_sps_.AppendBool(false); // video_signal_type_present_flag ++ packed_sps_.AppendBool(false); // chroma_loc_info_present_flag ++ ++ packed_sps_.AppendBool(current_sps_.timing_info_present_flag); ++ if (current_sps_.timing_info_present_flag) { ++ packed_sps_.AppendBits(32, current_sps_.num_units_in_tick); ++ packed_sps_.AppendBits(32, current_sps_.time_scale); ++ packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag); ++ } ++ ++ packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag); ++ if (current_sps_.nal_hrd_parameters_present_flag) { ++ packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1); ++ packed_sps_.AppendBits(4, current_sps_.bit_rate_scale); ++ packed_sps_.AppendBits(4, current_sps_.cpb_size_scale); ++ CHECK_LT(base::checked_cast(current_sps_.cpb_cnt_minus1), ++ arraysize(current_sps_.bit_rate_value_minus1)); ++ for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) { ++ packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]); ++ packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]); ++ packed_sps_.AppendBool(current_sps_.cbr_flag[i]); ++ } ++ packed_sps_.AppendBits( ++ 5, current_sps_.initial_cpb_removal_delay_length_minus_1); ++ packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1); ++ packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1); ++ packed_sps_.AppendBits(5, current_sps_.time_offset_length); ++ } ++ ++ packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag ++ if (current_sps_.nal_hrd_parameters_present_flag) ++ packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag); ++ ++ packed_sps_.AppendBool(false); // pic_struct_present_flag ++ packed_sps_.AppendBool(true); // bitstream_restriction_flag ++ ++ packed_sps_.AppendBool(false); // motion_vectors_over_pic_boundaries_flag ++ packed_sps_.AppendUE(2); // max_bytes_per_pic_denom ++ packed_sps_.AppendUE(1); // max_bits_per_mb_denom ++ packed_sps_.AppendUE(16); // log2_max_mv_length_horizontal ++ packed_sps_.AppendUE(16); // log2_max_mv_length_vertical ++ ++ // Explicitly set max_num_reorder_frames to 0 to allow the decoder to ++ // output pictures early. ++ packed_sps_.AppendUE(0); // max_num_reorder_frames ++ ++ // The value of max_dec_frame_buffering shall be greater than or equal to ++ // max_num_ref_frames. ++ const unsigned int max_dec_frame_buffering = ++ current_sps_.max_num_ref_frames; ++ packed_sps_.AppendUE(max_dec_frame_buffering); ++ } ++ ++ packed_sps_.FinishNALU(); ++} ++ ++void VaapiVideoEncodeAccelerator::UpdatePPS() { ++ memset(¤t_pps_, 0, sizeof(H264PPS)); ++ ++ current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id; ++ current_pps_.pic_parameter_set_id = 0; ++ ++ current_pps_.entropy_coding_mode_flag = ++ current_sps_.profile_idc >= H264SPS::kProfileIDCMain; ++ ++ CHECK_GT(max_ref_idx_l0_size_, 0u); ++ current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1; ++ current_pps_.num_ref_idx_l1_default_active_minus1 = 0; ++ DCHECK_LE(qp_, 51u); ++ current_pps_.pic_init_qp_minus26 = qp_ - 26; ++ current_pps_.deblocking_filter_control_present_flag = true; ++ current_pps_.transform_8x8_mode_flag = ++ (current_sps_.profile_idc == H264SPS::kProfileIDCHigh); ++} ++ ++void VaapiVideoEncodeAccelerator::GeneratePackedPPS() { ++ packed_pps_.Reset(); ++ ++ packed_pps_.BeginNALU(H264NALU::kPPS, 3); ++ ++ packed_pps_.AppendUE(current_pps_.pic_parameter_set_id); ++ packed_pps_.AppendUE(current_pps_.seq_parameter_set_id); ++ packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag); ++ packed_pps_.AppendBool( ++ current_pps_.bottom_field_pic_order_in_frame_present_flag); ++ CHECK_EQ(current_pps_.num_slice_groups_minus1, 0); ++ packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1); ++ ++ packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1); ++ packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1); ++ ++ packed_pps_.AppendBool(current_pps_.weighted_pred_flag); ++ packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc); ++ ++ packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26); ++ packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26); ++ packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset); ++ ++ packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag); ++ packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag); ++ packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag); ++ ++ packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag); ++ packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag); ++ DCHECK(!current_pps_.pic_scaling_matrix_present_flag); ++ packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset); ++ ++ packed_pps_.FinishNALU(); ++} ++ ++void VaapiVideoEncodeAccelerator::SetState(State state) { ++ // Only touch state on encoder thread, unless it's not running. ++ if (encoder_thread_.IsRunning() && ++ !encoder_thread_task_runner_->BelongsToCurrentThread()) { ++ encoder_thread_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::SetState, ++ base::Unretained(this), state)); ++ return; ++ } ++ ++ VLOGF(2) << "setting state to: " << state; ++ state_ = state; ++} ++ ++void VaapiVideoEncodeAccelerator::NotifyError(Error error) { ++ if (!child_task_runner_->BelongsToCurrentThread()) { ++ child_task_runner_->PostTask( ++ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::NotifyError, ++ weak_this_, error)); ++ return; ++ } ++ ++ if (client_) { ++ client_->NotifyError(error); ++ client_ptr_factory_.reset(); ++ } ++} ++ ++VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob() ++ : coded_buffer(VA_INVALID_ID), keyframe(false) {} ++ ++VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h +@@ -0,0 +1,275 @@ ++// Copyright 2014 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ ++#define MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "base/containers/queue.h" ++#include "base/macros.h" ++#include "base/memory/linked_ptr.h" ++#include "base/threading/thread.h" ++#include "media/filters/h264_bitstream_buffer.h" ++#include "media/gpu/h264_dpb.h" ++#include "media/gpu/media_gpu_export.h" ++#include "media/gpu/vaapi/va_surface.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++#include "media/video/video_encode_accelerator.h" ++ ++namespace media { ++ ++// A VideoEncodeAccelerator implementation that uses VA-API ++// (http://www.freedesktop.org/wiki/Software/vaapi) for HW-accelerated ++// video encode. ++class MEDIA_GPU_EXPORT VaapiVideoEncodeAccelerator ++ : public VideoEncodeAccelerator { ++ public: ++ VaapiVideoEncodeAccelerator(); ++ ~VaapiVideoEncodeAccelerator() override; ++ ++ // VideoEncodeAccelerator implementation. ++ VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles() override; ++ bool Initialize(VideoPixelFormat format, ++ const gfx::Size& input_visible_size, ++ VideoCodecProfile output_profile, ++ uint32_t initial_bitrate, ++ Client* client) override; ++ void Encode(const scoped_refptr& frame, ++ bool force_keyframe) override; ++ void UseOutputBitstreamBuffer(const BitstreamBuffer& buffer) override; ++ void RequestEncodingParametersChange(uint32_t bitrate, ++ uint32_t framerate) override; ++ void Destroy() override; ++ void Flush(FlushCallback flush_callback) override; ++ ++ private: ++ // Reference picture list. ++ typedef std::list> RefPicList; ++ ++ // Encode job for one frame. Created when an input frame is awaiting and ++ // enough resources are available to proceed. Once the job is prepared and ++ // submitted to the hardware, it awaits on the submitted_encode_jobs_ queue ++ // for an output bitstream buffer to become available. Once one is ready, ++ // the encoded bytes are downloaded to it and job resources are released ++ // and become available for reuse. ++ struct EncodeJob { ++ // Input surface for video frame data. ++ scoped_refptr input_surface; ++ // Surface for a reconstructed picture, which is used for reference ++ // for subsequent frames. ++ scoped_refptr recon_surface; ++ // Buffer that will contain output bitstream for this frame. ++ VABufferID coded_buffer; ++ // Reference surfaces required to encode this picture. We keep references ++ // to them here, because we may discard some of them from ref_pic_list* ++ // before the HW job is done. ++ RefPicList reference_surfaces; ++ // True if this job will produce a keyframe. Used to report ++ // to BitstreamBufferReady(). ++ bool keyframe; ++ // Source timestamp. ++ base::TimeDelta timestamp; ++ ++ EncodeJob(); ++ ~EncodeJob(); ++ }; ++ ++ // Encoder state. ++ enum State { ++ kUninitialized, ++ kEncoding, ++ kError, ++ }; ++ ++ // Holds input frames coming from the client ready to be encoded. ++ struct InputFrameRef; ++ // Holds output buffers coming from the client ready to be filled. ++ struct BitstreamBufferRef; ++ ++ // Tasks for each of the VEA interface calls to be executed on the ++ // encoder thread. ++ void InitializeTask(); ++ void EncodeTask(const scoped_refptr& frame, bool force_keyframe); ++ void UseOutputBitstreamBufferTask( ++ std::unique_ptr buffer_ref); ++ void RequestEncodingParametersChangeTask(uint32_t bitrate, ++ uint32_t framerate); ++ void DestroyTask(); ++ void FlushTask(); ++ ++ // Prepare and schedule an encode job if we have an input to encode ++ // and enough resources to proceed. ++ void EncodeFrameTask(); ++ ++ // Fill current_sps_/current_pps_ with current values. ++ void UpdateSPS(); ++ void UpdatePPS(); ++ void UpdateRates(uint32_t bitrate, uint32_t framerate); ++ ++ // Generate packed SPS and PPS in packed_sps_/packed_pps_, using ++ // values in current_sps_/current_pps_. ++ void GeneratePackedSPS(); ++ void GeneratePackedPPS(); ++ ++ // Check if we have sufficient resources for a new encode job, claim them and ++ // fill current_encode_job_ with them. ++ // Return false if we cannot start a new job yet, true otherwise. ++ bool PrepareNextJob(base::TimeDelta timestamp); ++ ++ // Begin a new frame, making it a keyframe if |force_keyframe| is true, ++ // updating current_pic_. ++ void BeginFrame(bool force_keyframe); ++ ++ // End current frame, updating reference picture lists and storing current ++ // job in the jobs awaiting completion on submitted_encode_jobs_. ++ void EndFrame(); ++ ++ // Submit parameters for the current frame to the hardware. ++ bool SubmitFrameParameters(); ++ // Submit keyframe headers to the hardware if the current frame is a keyframe. ++ bool SubmitHeadersIfNeeded(); ++ ++ // Upload image data from |frame| to the input surface for current job. ++ bool UploadFrame(const scoped_refptr& frame); ++ ++ // Execute encode in hardware. This does not block and will return before ++ // the job is finished. ++ bool ExecuteEncode(); ++ ++ // Callback that returns a no longer used VASurfaceID to ++ // available_va_surface_ids_ for reuse. ++ void RecycleVASurfaceID(VASurfaceID va_surface_id); ++ ++ // Tries to return a bitstream buffer if both a submitted job awaits to ++ // be completed and we have bitstream buffers from the client available ++ // to download the encoded data to. ++ void TryToReturnBitstreamBuffer(); ++ ++ // Puts the encoder into en error state and notifies client about the error. ++ void NotifyError(Error error); ++ ++ // Sets the encoder state on the correct thread. ++ void SetState(State state); ++ ++ // VaapiWrapper is the owner of all HW resources (surfaces and buffers) ++ // and will free them on destruction. ++ scoped_refptr vaapi_wrapper_; ++ ++ // Input profile and sizes. ++ VideoCodecProfile profile_; ++ gfx::Size visible_size_; ++ gfx::Size coded_size_; // Macroblock-aligned. ++ // Width/height in macroblocks. ++ unsigned int mb_width_; ++ unsigned int mb_height_; ++ ++ // Maximum size of the reference list 0. ++ unsigned int max_ref_idx_l0_size_; ++ ++ // Initial QP. ++ unsigned int qp_; ++ ++ // IDR frame period. ++ unsigned int idr_period_; ++ // I frame period. ++ unsigned int i_period_; ++ // IP period, i.e. how often do we need to have either an I or a P frame in ++ // the stream. Period of 1 means we can have no B frames. ++ unsigned int ip_period_; ++ ++ // Size in bytes required for input bitstream buffers. ++ size_t output_buffer_byte_size_; ++ ++ // All of the members below must be accessed on the encoder_thread_, ++ // while it is running. ++ ++ // Encoder state. Encode tasks will only run in kEncoding state. ++ State state_; ++ ++ // frame_num to be used for the next frame. ++ unsigned int frame_num_; ++ // idr_pic_id to be used for the next frame. ++ unsigned int idr_pic_id_; ++ ++ // Current bitrate in bps. ++ unsigned int bitrate_; ++ // Current fps. ++ unsigned int framerate_; ++ // CPB size in bits, i.e. bitrate in kbps * window size in ms/1000. ++ unsigned int cpb_size_; ++ // True if the parameters have changed and we need to submit a keyframe ++ // with updated parameters. ++ bool encoding_parameters_changed_; ++ ++ // Job currently being prepared for encode. ++ std::unique_ptr current_encode_job_; ++ ++ // Current SPS, PPS and their packed versions. Packed versions are their NALUs ++ // in AnnexB format *without* emulation prevention three-byte sequences ++ // (those will be added by the driver). ++ H264SPS current_sps_; ++ H264BitstreamBuffer packed_sps_; ++ H264PPS current_pps_; ++ H264BitstreamBuffer packed_pps_; ++ ++ // Picture currently being prepared for encode. ++ scoped_refptr current_pic_; ++ ++ // VA surfaces available for reuse. ++ std::vector available_va_surface_ids_; ++ ++ // VA buffers for coded frames. ++ std::vector available_va_buffer_ids_; ++ ++ // Currently active reference surfaces. ++ RefPicList ref_pic_list0_; ++ ++ // Callback via which finished VA surfaces are returned to us. ++ VASurface::ReleaseCB va_surface_release_cb_; ++ ++ // VideoFrames passed from the client, waiting to be encoded. ++ base::queue> encoder_input_queue_; ++ ++ // BitstreamBuffers mapped, ready to be filled. ++ base::queue> available_bitstream_buffers_; ++ ++ // Jobs submitted for encode, awaiting bitstream buffers to become available. ++ // A pending flush command, indicated by a null job, will be also put in the ++ // queue. ++ base::queue> submitted_encode_jobs_; ++ ++ // Encoder thread. All tasks are executed on it. ++ base::Thread encoder_thread_; ++ scoped_refptr encoder_thread_task_runner_; ++ ++ const scoped_refptr child_task_runner_; ++ ++ // To expose client callbacks from VideoEncodeAccelerator. ++ // NOTE: all calls to these objects *MUST* be executed on ++ // child_task_runner_. ++ std::unique_ptr> client_ptr_factory_; ++ base::WeakPtr client_; ++ ++ // WeakPtr to post from the encoder thread back to the ChildThread, as it may ++ // outlive this. Posting from the ChildThread using base::Unretained(this) ++ // to the encoder thread is safe, because |this| always outlives the encoder ++ // thread (it's a member of this class). ++ base::WeakPtr weak_this_; ++ ++ // The completion callback of the Flush() function. ++ FlushCallback flush_callback_; ++ ++ base::WeakPtrFactory weak_this_ptr_factory_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiVideoEncodeAccelerator); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_wrapper.cc +@@ -0,0 +1,1372 @@ ++// Copyright 2013 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "media/gpu/vaapi/vaapi_wrapper.h" ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include "base/bind.h" ++#include "base/callback_helpers.h" ++#include "base/environment.h" ++#include "base/logging.h" ++#include "base/macros.h" ++#include "base/numerics/safe_conversions.h" ++#include "base/stl_util.h" ++#include "base/sys_info.h" ++#include "build/build_config.h" ++ ++// Auto-generated for dlopen libva libraries ++#include "media/gpu/vaapi/va_stubs.h" ++ ++#include "media/gpu/vaapi/vaapi_picture.h" ++#include "third_party/libyuv/include/libyuv.h" ++#include "ui/gfx/buffer_format_util.h" ++#include "ui/gfx/native_pixmap.h" ++#include "ui/gl/gl_bindings.h" ++#include "ui/gl/gl_implementation.h" ++ ++#if defined(USE_X11) ++#include ++#include "ui/gfx/x/x11_types.h" // nogncheck ++#endif ++ ++#if defined(USE_OZONE) ++#include "ui/ozone/public/ozone_platform.h" ++#include "ui/ozone/public/surface_factory_ozone.h" ++#endif ++ ++using media_gpu_vaapi::kModuleVa; ++using media_gpu_vaapi::kModuleVa_drm; ++#if defined(USE_X11) ++using media_gpu_vaapi::kModuleVa_x11; ++#endif ++using media_gpu_vaapi::InitializeStubs; ++using media_gpu_vaapi::StubPathMap; ++ ++#define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ ++ do { \ ++ LOG(ERROR) << err_msg << " VA error: " << vaErrorStr(va_error); \ ++ report_error_to_uma_cb_.Run(); \ ++ } while (0) ++ ++#define VA_LOG_ON_ERROR(va_error, err_msg) \ ++ do { \ ++ if ((va_error) != VA_STATUS_SUCCESS) \ ++ LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ ++ } while (0) ++ ++#define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ ++ do { \ ++ if ((va_error) != VA_STATUS_SUCCESS) { \ ++ LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ ++ return (ret); \ ++ } \ ++ } while (0) ++ ++namespace { ++ ++uint32_t BufferFormatToVAFourCC(gfx::BufferFormat fmt) { ++ switch (fmt) { ++ case gfx::BufferFormat::BGRX_8888: ++ return VA_FOURCC_BGRX; ++ case gfx::BufferFormat::BGRA_8888: ++ return VA_FOURCC_BGRA; ++ case gfx::BufferFormat::RGBX_8888: ++ return VA_FOURCC_RGBX; ++ case gfx::BufferFormat::UYVY_422: ++ return VA_FOURCC_UYVY; ++ case gfx::BufferFormat::YVU_420: ++ return VA_FOURCC_YV12; ++ default: ++ NOTREACHED(); ++ return 0; ++ } ++} ++ ++uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt) { ++ switch (fmt) { ++ case gfx::BufferFormat::UYVY_422: ++ return VA_RT_FORMAT_YUV422; ++ case gfx::BufferFormat::BGRX_8888: ++ case gfx::BufferFormat::BGRA_8888: ++ case gfx::BufferFormat::RGBX_8888: ++ return VA_RT_FORMAT_RGB32; ++ case gfx::BufferFormat::YVU_420: ++ return VA_RT_FORMAT_YUV420; ++ default: ++ NOTREACHED(); ++ return 0; ++ } ++} ++ ++} // namespace ++ ++namespace media { ++ ++namespace { ++ ++// Maximum framerate of encoded profile. This value is an arbitary limit ++// and not taken from HW documentation. ++const int kMaxEncoderFramerate = 30; ++ ++// Attributes required for encode. This only applies to video encode, not JPEG ++// encode. ++static const VAConfigAttrib kVideoEncodeVAConfigAttribs[] = { ++ {VAConfigAttribRateControl, VA_RC_CBR}, ++ {VAConfigAttribEncPackedHeaders, ++ VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE}, ++}; ++ ++// A map between VideoCodecProfile and VAProfile. ++static const struct { ++ VideoCodecProfile profile; ++ VAProfile va_profile; ++} kProfileMap[] = { ++ {H264PROFILE_BASELINE, VAProfileH264Baseline}, ++ {H264PROFILE_MAIN, VAProfileH264Main}, ++ // TODO(posciak): See if we can/want to support other variants of ++ // H264PROFILE_HIGH*. ++ {H264PROFILE_HIGH, VAProfileH264High}, ++ {VP8PROFILE_ANY, VAProfileVP8Version0_3}, ++ {VP9PROFILE_PROFILE0, VAProfileVP9Profile0}, ++ {VP9PROFILE_PROFILE1, VAProfileVP9Profile1}, ++ {VP9PROFILE_PROFILE2, VAProfileVP9Profile2}, ++ {VP9PROFILE_PROFILE3, VAProfileVP9Profile3}, ++}; ++ ++// This class is a wrapper around its |va_display_| (and its associated ++// |va_lock_|) to guarantee mutual exclusion and singleton behaviour. ++class VADisplayState { ++ public: ++ static VADisplayState* Get(); ++ ++ // Initialize static data before sandbox is enabled. ++ static void PreSandboxInitialization(); ++ ++ VADisplayState(); ++ ~VADisplayState() = delete; ++ ++ // |va_lock_| must be held on entry. ++ bool Initialize(); ++ void Deinitialize(VAStatus* status); ++ ++ base::Lock* va_lock() { return &va_lock_; } ++ VADisplay va_display() const { return va_display_; } ++ ++ void SetDrmFd(base::PlatformFile fd) { drm_fd_.reset(HANDLE_EINTR(dup(fd))); } ++ ++ private: ++ // Returns false on init failure. ++ static bool PostSandboxInitialization(); ++ ++ // Protected by |va_lock_|. ++ int refcount_; ++ ++ // Libva is not thread safe, so we have to do locking for it ourselves. ++ // This lock is to be taken for the duration of all VA-API calls and for ++ // the entire job submission sequence in ExecuteAndDestroyPendingBuffers(). ++ base::Lock va_lock_; ++ ++ // Drm fd used to obtain access to the driver interface by VA. ++ base::ScopedFD drm_fd_; ++ ++ // The VADisplay handle. ++ VADisplay va_display_; ++ ++ // True if vaInitialize() has been called successfully. ++ bool va_initialized_; ++}; ++ ++// static ++VADisplayState* VADisplayState::Get() { ++ static VADisplayState* display_state = new VADisplayState(); ++ return display_state; ++} ++ ++// static ++void VADisplayState::PreSandboxInitialization() { ++ const char kDriRenderNode0Path[] = "/dev/dri/renderD128"; ++ base::File drm_file = base::File( ++ base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path), ++ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); ++ if (drm_file.IsValid()) ++ VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile()); ++} ++ ++// static ++bool VADisplayState::PostSandboxInitialization() { ++ const std::string va_suffix(std::to_string(VA_MAJOR_VERSION + 1)); ++ StubPathMap paths; ++ ++ paths[kModuleVa].push_back(std::string("libva.so.") + va_suffix); ++ paths[kModuleVa_drm].push_back(std::string("libva-drm.so.") + va_suffix); ++#if defined(USE_X11) ++ // libva-x11 does not exist on libva >= 2 ++ if (VA_MAJOR_VERSION == 0) ++ paths[kModuleVa_x11].push_back("libva-x11.so.1"); ++#endif ++ ++ const bool success = InitializeStubs(paths); ++ if (!success) { ++ static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; ++#if defined(OS_CHROMEOS) ++ // When Chrome runs on Linux with target_os="chromeos", do not log error ++ // message without VAAPI libraries. ++ LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS()) << kErrorMsg; ++#else ++ DVLOG(1) << kErrorMsg; ++#endif ++ } ++ return success; ++} ++ ++VADisplayState::VADisplayState() ++ : refcount_(0), va_display_(nullptr), va_initialized_(false) {} ++ ++bool VADisplayState::Initialize() { ++ va_lock_.AssertAcquired(); ++ ++ static bool result = PostSandboxInitialization(); ++ if (!result) ++ return false; ++ ++ if (refcount_++ > 0) ++ return true; ++ ++ switch (gl::GetGLImplementation()) { ++ case gl::kGLImplementationEGLGLES2: ++ va_display_ = vaGetDisplayDRM(drm_fd_.get()); ++ break; ++ case gl::kGLImplementationDesktopGL: ++#if defined(USE_X11) ++ va_display_ = vaGetDisplay(gfx::GetXDisplay()); ++#else ++ LOG(WARNING) << "HW video decode acceleration not available without " ++ "DesktopGL (GLX)."; ++#endif // USE_X11 ++ break; ++ // Cannot infer platform from GL, try all available displays ++ case gl::kGLImplementationNone: ++#if defined(USE_X11) ++ va_display_ = vaGetDisplay(gfx::GetXDisplay()); ++ if (vaDisplayIsValid(va_display_)) ++ break; ++#endif // USE_X11 ++ va_display_ = vaGetDisplayDRM(drm_fd_.get()); ++ break; ++ ++ default: ++ LOG(WARNING) << "HW video decode acceleration not available for " ++ << gl::GetGLImplementationName(gl::GetGLImplementation()); ++ return false; ++ } ++ ++ if (!vaDisplayIsValid(va_display_)) { ++ LOG(ERROR) << "Could not get a valid VA display"; ++ return false; ++ } ++ ++ // Set VA logging level to enable error messages, unless already set ++ constexpr char libva_log_level_env[] = "LIBVA_MESSAGING_LEVEL"; ++ std::unique_ptr env(base::Environment::Create()); ++ if (!env->HasVar(libva_log_level_env)) ++ env->SetVar(libva_log_level_env, "1"); ++ ++ // The VAAPI version. ++ int major_version, minor_version; ++ VAStatus va_res = vaInitialize(va_display_, &major_version, &minor_version); ++ if (va_res != VA_STATUS_SUCCESS) { ++ LOG(ERROR) << "vaInitialize failed: " << vaErrorStr(va_res); ++ return false; ++ } ++ ++ va_initialized_ = true; ++ DVLOG(1) << "VAAPI version: " << major_version << "." << minor_version; ++ ++ if (major_version != VA_MAJOR_VERSION || minor_version != VA_MINOR_VERSION) { ++ LOG(ERROR) << "This build of Chromium requires VA-API version " ++ << VA_MAJOR_VERSION << "." << VA_MINOR_VERSION ++ << ", system version: " << major_version << "." << minor_version; ++ return false; ++ } ++ return true; ++} ++ ++void VADisplayState::Deinitialize(VAStatus* status) { ++ va_lock_.AssertAcquired(); ++ if (--refcount_ > 0) ++ return; ++ ++ // Must check if vaInitialize completed successfully, to work around a bug in ++ // libva. The bug was fixed upstream: ++ // http://lists.freedesktop.org/archives/libva/2013-July/001807.html ++ // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once ++ // the fix has rolled out sufficiently. ++ if (va_initialized_ && va_display_) ++ *status = vaTerminate(va_display_); ++ va_initialized_ = false; ++ va_display_ = nullptr; ++} ++ ++static std::vector GetRequiredAttribs( ++ VaapiWrapper::CodecMode mode, ++ VAProfile profile) { ++ std::vector required_attribs; ++ // VAConfigAttribRTFormat is common to both encode and decode |mode|s. ++ if (profile == VAProfileVP9Profile2 || profile == VAProfileVP9Profile3) { ++ required_attribs.push_back( ++ {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420_10BPP}); ++ } else { ++ required_attribs.push_back({VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}); ++ } ++ if (mode == VaapiWrapper::kEncode && profile != VAProfileJPEGBaseline) { ++ required_attribs.insert( ++ required_attribs.end(), kVideoEncodeVAConfigAttribs, ++ kVideoEncodeVAConfigAttribs + arraysize(kVideoEncodeVAConfigAttribs)); ++ } ++ return required_attribs; ++} ++ ++static VAEntrypoint GetVaEntryPoint(VaapiWrapper::CodecMode mode, ++ VAProfile profile) { ++ switch (mode) { ++ case VaapiWrapper::kDecode: ++ return VAEntrypointVLD; ++ case VaapiWrapper::kEncode: ++ if (profile == VAProfileJPEGBaseline) ++ return VAEntrypointEncPicture; ++ else ++ return VAEntrypointEncSlice; ++ case VaapiWrapper::kCodecModeMax: ++ NOTREACHED(); ++ return VAEntrypointVLD; ++ } ++} ++ ++// This class encapsulates reading and giving access to the list of supported ++// ProfileInfo entries, in a singleton way. ++class VASupportedProfiles { ++ public: ++ struct ProfileInfo { ++ VAProfile va_profile; ++ gfx::Size max_resolution; ++ }; ++ static VASupportedProfiles* Get(); ++ ++ std::vector GetSupportedProfileInfosForCodecMode( ++ VaapiWrapper::CodecMode mode); ++ ++ bool IsProfileSupported(VaapiWrapper::CodecMode mode, VAProfile va_profile); ++ ++ private: ++ VASupportedProfiles(); ++ ~VASupportedProfiles() = default; ++ ++ bool GetSupportedVAProfiles(std::vector* profiles); ++ ++ // Gets supported profile infos for |mode|. ++ std::vector GetSupportedProfileInfosForCodecModeInternal( ++ VaapiWrapper::CodecMode mode); ++ ++ // |va_lock_| must be held on entry in the following _Locked methods. ++ ++ // Checks if |va_profile| supports |entrypoint| or not. ++ bool IsEntrypointSupported_Locked(VAProfile va_profile, ++ VAEntrypoint entrypoint); ++ // Returns true if |va_profile| for |entrypoint| with |required_attribs| is ++ // supported. ++ bool AreAttribsSupported_Locked( ++ VAProfile va_profile, ++ VAEntrypoint entrypoint, ++ const std::vector& required_attribs); ++ // Gets maximum resolution for |va_profile| and |entrypoint| with ++ // |required_attribs|. If return value is true, |resolution| is the maximum ++ // resolution. ++ bool GetMaxResolution_Locked(VAProfile va_profile, ++ VAEntrypoint entrypoint, ++ std::vector& required_attribs, ++ gfx::Size* resolution); ++ ++ std::vector supported_profiles_[VaapiWrapper::kCodecModeMax]; ++ ++ // Pointer to VADisplayState's members |va_lock_| and its |va_display_|. ++ base::Lock* va_lock_; ++ VADisplay va_display_; ++ ++ const base::Closure report_error_to_uma_cb_; ++}; ++ ++// static ++VASupportedProfiles* VASupportedProfiles::Get() { ++ static VASupportedProfiles* profile_infos = new VASupportedProfiles(); ++ return profile_infos; ++} ++ ++std::vector ++VASupportedProfiles::GetSupportedProfileInfosForCodecMode( ++ VaapiWrapper::CodecMode mode) { ++ return supported_profiles_[mode]; ++} ++ ++bool VASupportedProfiles::IsProfileSupported(VaapiWrapper::CodecMode mode, ++ VAProfile va_profile) { ++ for (const auto& profile : supported_profiles_[mode]) { ++ if (profile.va_profile == va_profile) ++ return true; ++ } ++ return false; ++} ++ ++VASupportedProfiles::VASupportedProfiles() ++ : va_lock_(VADisplayState::Get()->va_lock()), ++ va_display_(nullptr), ++ report_error_to_uma_cb_(base::Bind(&base::DoNothing)) { ++ static_assert(arraysize(supported_profiles_) == VaapiWrapper::kCodecModeMax, ++ "The array size of supported profile is incorrect."); ++ { ++ base::AutoLock auto_lock(*va_lock_); ++ if (!VADisplayState::Get()->Initialize()) ++ return; ++ } ++ ++ va_display_ = VADisplayState::Get()->va_display(); ++ DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; ++ ++ for (size_t i = 0; i < VaapiWrapper::kCodecModeMax; ++i) { ++ supported_profiles_[i] = GetSupportedProfileInfosForCodecModeInternal( ++ static_cast(i)); ++ } ++ ++ { ++ base::AutoLock auto_lock(*va_lock_); ++ VAStatus va_res = VA_STATUS_SUCCESS; ++ VADisplayState::Get()->Deinitialize(&va_res); ++ VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); ++ va_display_ = nullptr; ++ } ++} ++ ++std::vector ++VASupportedProfiles::GetSupportedProfileInfosForCodecModeInternal( ++ VaapiWrapper::CodecMode mode) { ++ std::vector supported_profile_infos; ++ std::vector va_profiles; ++ if (!GetSupportedVAProfiles(&va_profiles)) ++ return supported_profile_infos; ++ ++ base::AutoLock auto_lock(*va_lock_); ++ for (const auto& va_profile : va_profiles) { ++ VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); ++ std::vector required_attribs = ++ GetRequiredAttribs(mode, va_profile); ++ if (!IsEntrypointSupported_Locked(va_profile, entrypoint)) ++ continue; ++ if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs)) ++ continue; ++ ProfileInfo profile_info; ++ if (!GetMaxResolution_Locked(va_profile, entrypoint, required_attribs, ++ &profile_info.max_resolution)) { ++ LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile ++ << " and entrypoint " << entrypoint; ++ continue; ++ } ++ profile_info.va_profile = va_profile; ++ supported_profile_infos.push_back(profile_info); ++ } ++ return supported_profile_infos; ++} ++ ++bool VASupportedProfiles::GetSupportedVAProfiles( ++ std::vector* profiles) { ++ base::AutoLock auto_lock(*va_lock_); ++ // Query the driver for supported profiles. ++ const int max_profiles = vaMaxNumProfiles(va_display_); ++ std::vector supported_profiles( ++ base::checked_cast(max_profiles)); ++ ++ int num_supported_profiles; ++ VAStatus va_res = vaQueryConfigProfiles(va_display_, &supported_profiles[0], ++ &num_supported_profiles); ++ VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false); ++ if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) { ++ LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_supported_profiles; ++ return false; ++ } ++ ++ supported_profiles.resize(base::checked_cast(num_supported_profiles)); ++ *profiles = supported_profiles; ++ return true; ++} ++ ++bool VASupportedProfiles::IsEntrypointSupported_Locked( ++ VAProfile va_profile, ++ VAEntrypoint entrypoint) { ++ va_lock_->AssertAcquired(); ++ // Query the driver for supported entrypoints. ++ int max_entrypoints = vaMaxNumEntrypoints(va_display_); ++ std::vector supported_entrypoints( ++ base::checked_cast(max_entrypoints)); ++ ++ int num_supported_entrypoints; ++ VAStatus va_res = vaQueryConfigEntrypoints(va_display_, va_profile, ++ &supported_entrypoints[0], ++ &num_supported_entrypoints); ++ VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false); ++ if (num_supported_entrypoints < 0 || ++ num_supported_entrypoints > max_entrypoints) { ++ LOG(ERROR) << "vaQueryConfigEntrypoints returned: " ++ << num_supported_entrypoints; ++ return false; ++ } ++ ++ return base::ContainsValue(supported_entrypoints, entrypoint); ++} ++ ++bool VASupportedProfiles::AreAttribsSupported_Locked( ++ VAProfile va_profile, ++ VAEntrypoint entrypoint, ++ const std::vector& required_attribs) { ++ va_lock_->AssertAcquired(); ++ // Query the driver for required attributes. ++ std::vector attribs = required_attribs; ++ for (size_t i = 0; i < required_attribs.size(); ++i) ++ attribs[i].value = 0; ++ ++ VAStatus va_res = vaGetConfigAttributes(va_display_, va_profile, entrypoint, ++ &attribs[0], attribs.size()); ++ VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); ++ ++ for (size_t i = 0; i < required_attribs.size(); ++i) { ++ if (attribs[i].type != required_attribs[i].type || ++ (attribs[i].value & required_attribs[i].value) != ++ required_attribs[i].value) { ++ DVLOG(1) << "Unsupported value " << required_attribs[i].value ++ << " for attribute type " << required_attribs[i].type; ++ return false; ++ } ++ } ++ return true; ++} ++ ++bool VASupportedProfiles::GetMaxResolution_Locked( ++ VAProfile va_profile, ++ VAEntrypoint entrypoint, ++ std::vector& required_attribs, ++ gfx::Size* resolution) { ++ va_lock_->AssertAcquired(); ++ VAConfigID va_config_id; ++ VAStatus va_res = ++ vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], ++ required_attribs.size(), &va_config_id); ++ VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); ++ ++ // Calls vaQuerySurfaceAttributes twice. The first time is to get the number ++ // of attributes to prepare the space and the second time is to get all ++ // attributes. ++ unsigned int num_attribs; ++ va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, nullptr, ++ &num_attribs); ++ VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); ++ if (!num_attribs) ++ return false; ++ ++ std::vector attrib_list( ++ base::checked_cast(num_attribs)); ++ ++ va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, &attrib_list[0], ++ &num_attribs); ++ VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); ++ ++ resolution->SetSize(0, 0); ++ for (const auto& attrib : attrib_list) { ++ if (attrib.type == VASurfaceAttribMaxWidth) ++ resolution->set_width(attrib.value.value.i); ++ else if (attrib.type == VASurfaceAttribMaxHeight) ++ resolution->set_height(attrib.value.value.i); ++ } ++ if (resolution->IsEmpty()) { ++ LOG(ERROR) << "Wrong codec resolution: " << resolution->ToString(); ++ return false; ++ } ++ return true; ++} ++ ++// Maps VideoCodecProfile enum values to VaProfile values. This function ++// includes a workaround for https://crbug.com/345569: if va_profile is h264 ++// baseline and it is not supported, we try constrained baseline. ++VAProfile ProfileToVAProfile(VideoCodecProfile profile, ++ VaapiWrapper::CodecMode mode) { ++ VAProfile va_profile = VAProfileNone; ++ for (size_t i = 0; i < arraysize(kProfileMap); ++i) { ++ if (kProfileMap[i].profile == profile) { ++ va_profile = kProfileMap[i].va_profile; ++ break; ++ } ++ } ++ if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile) && ++ va_profile == VAProfileH264Baseline) { ++ // https://crbug.com/345569: ProfileIDToVideoCodecProfile() currently strips ++ // the information whether the profile is constrained or not, so we have no ++ // way to know here. Try for baseline first, but if it is not supported, ++ // try constrained baseline and hope this is what it actually is ++ // (which in practice is true for a great majority of cases). ++ if (VASupportedProfiles::Get()->IsProfileSupported( ++ mode, VAProfileH264ConstrainedBaseline)) { ++ va_profile = VAProfileH264ConstrainedBaseline; ++ DVLOG(1) << "Fall back to constrained baseline profile."; ++ } ++ } ++ return va_profile; ++} ++ ++void DestroyVAImage(VADisplay va_display, VAImage image) { ++ if (image.image_id != VA_INVALID_ID) ++ vaDestroyImage(va_display, image.image_id); ++} ++ ++} // namespace ++ ++VaapiWrapper::VaapiWrapper() ++ : va_surface_format_(0), ++ va_display_(NULL), ++ va_config_id_(VA_INVALID_ID), ++ va_context_id_(VA_INVALID_ID), ++ va_vpp_config_id_(VA_INVALID_ID), ++ va_vpp_context_id_(VA_INVALID_ID), ++ va_vpp_buffer_id_(VA_INVALID_ID) { ++ va_lock_ = VADisplayState::Get()->va_lock(); ++} ++ ++VaapiWrapper::~VaapiWrapper() { ++ DestroyPendingBuffers(); ++ DestroyCodedBuffers(); ++ DestroySurfaces(); ++ DeinitializeVpp(); ++ Deinitialize(); ++} ++ ++// static ++scoped_refptr VaapiWrapper::Create( ++ CodecMode mode, ++ VAProfile va_profile, ++ const base::Closure& report_error_to_uma_cb) { ++ if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile)) { ++ DVLOG(1) << "Unsupported va_profile: " << va_profile; ++ return nullptr; ++ } ++ ++ scoped_refptr vaapi_wrapper(new VaapiWrapper()); ++ if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) { ++ if (vaapi_wrapper->Initialize(mode, va_profile)) ++ return vaapi_wrapper; ++ } ++ LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " << va_profile; ++ return nullptr; ++} ++ ++// static ++scoped_refptr VaapiWrapper::CreateForVideoCodec( ++ CodecMode mode, ++ VideoCodecProfile profile, ++ const base::Closure& report_error_to_uma_cb) { ++ VAProfile va_profile = ProfileToVAProfile(profile, mode); ++ return Create(mode, va_profile, report_error_to_uma_cb); ++} ++ ++// static ++VideoEncodeAccelerator::SupportedProfiles ++VaapiWrapper::GetSupportedEncodeProfiles() { ++ VideoEncodeAccelerator::SupportedProfiles profiles; ++ std::vector encode_profile_infos = ++ VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kEncode); ++ ++ for (size_t i = 0; i < arraysize(kProfileMap); ++i) { ++ VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode); ++ if (va_profile == VAProfileNone) ++ continue; ++ for (const auto& profile_info : encode_profile_infos) { ++ if (profile_info.va_profile == va_profile) { ++ VideoEncodeAccelerator::SupportedProfile profile; ++ profile.profile = kProfileMap[i].profile; ++ profile.max_resolution = profile_info.max_resolution; ++ profile.max_framerate_numerator = kMaxEncoderFramerate; ++ profile.max_framerate_denominator = 1; ++ profiles.push_back(profile); ++ break; ++ } ++ } ++ } ++ return profiles; ++} ++ ++// static ++VideoDecodeAccelerator::SupportedProfiles ++VaapiWrapper::GetSupportedDecodeProfiles() { ++ VideoDecodeAccelerator::SupportedProfiles profiles; ++ std::vector decode_profile_infos = ++ VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kDecode); ++ ++ for (size_t i = 0; i < arraysize(kProfileMap); ++i) { ++ VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kDecode); ++ if (va_profile == VAProfileNone) ++ continue; ++ for (const auto& profile_info : decode_profile_infos) { ++ if (profile_info.va_profile == va_profile) { ++ VideoDecodeAccelerator::SupportedProfile profile; ++ profile.profile = kProfileMap[i].profile; ++ profile.max_resolution = profile_info.max_resolution; ++ profile.min_resolution.SetSize(16, 16); ++ profiles.push_back(profile); ++ break; ++ } ++ } ++ } ++ return profiles; ++} ++ ++// static ++bool VaapiWrapper::IsJpegDecodeSupported() { ++ return VASupportedProfiles::Get()->IsProfileSupported(kDecode, ++ VAProfileJPEGBaseline); ++} ++ ++// static ++bool VaapiWrapper::IsJpegEncodeSupported() { ++ return VASupportedProfiles::Get()->IsProfileSupported(kEncode, ++ VAProfileJPEGBaseline); ++} ++ ++void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { ++ base::AutoLock auto_lock(*va_lock_); ++ VADisplayAttribute item = {VADisplayAttribRenderMode, ++ 1, // At least support '_LOCAL_OVERLAY'. ++ -1, // The maximum possible support 'ALL'. ++ VA_RENDER_MODE_LOCAL_GPU, ++ VA_DISPLAY_ATTRIB_SETTABLE}; ++ ++ VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1); ++ if (va_res != VA_STATUS_SUCCESS) ++ DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; ++} ++ ++bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) { ++ report_error_to_uma_cb_ = report_error_to_uma_cb; ++ { ++ base::AutoLock auto_lock(*va_lock_); ++ if (!VADisplayState::Get()->Initialize()) ++ return false; ++ } ++ ++ va_display_ = VADisplayState::Get()->va_display(); ++ DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; ++ return true; ++} ++ ++bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) { ++ TryToSetVADisplayAttributeToLocalGPU(); ++ ++ VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); ++ std::vector required_attribs = ++ GetRequiredAttribs(mode, va_profile); ++ base::AutoLock auto_lock(*va_lock_); ++ VAStatus va_res = ++ vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], ++ required_attribs.size(), &va_config_id_); ++ VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); ++ ++ return true; ++} ++ ++void VaapiWrapper::Deinitialize() { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ if (va_config_id_ != VA_INVALID_ID) { ++ VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); ++ } ++ ++ VAStatus va_res = VA_STATUS_SUCCESS; ++ VADisplayState::Get()->Deinitialize(&va_res); ++ VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); ++ ++ va_config_id_ = VA_INVALID_ID; ++ va_display_ = NULL; ++} ++ ++bool VaapiWrapper::CreateSurfaces(unsigned int va_format, ++ const gfx::Size& size, ++ size_t num_surfaces, ++ std::vector* va_surfaces) { ++ base::AutoLock auto_lock(*va_lock_); ++ DVLOG(2) << "Creating " << num_surfaces << " surfaces"; ++ ++ DCHECK(va_surfaces->empty()); ++ DCHECK(va_surface_ids_.empty()); ++ DCHECK_EQ(va_surface_format_, 0u); ++ va_surface_ids_.resize(num_surfaces); ++ ++ // Allocate surfaces in driver. ++ VAStatus va_res = ++ vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), ++ &va_surface_ids_[0], va_surface_ids_.size(), NULL, 0); ++ ++ VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); ++ if (va_res != VA_STATUS_SUCCESS) { ++ va_surface_ids_.clear(); ++ return false; ++ } ++ ++ // And create a context associated with them. ++ va_res = vaCreateContext(va_display_, va_config_id_, size.width(), ++ size.height(), VA_PROGRESSIVE, &va_surface_ids_[0], ++ va_surface_ids_.size(), &va_context_id_); ++ ++ VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); ++ if (va_res != VA_STATUS_SUCCESS) { ++ DestroySurfaces_Locked(); ++ return false; ++ } ++ ++ *va_surfaces = va_surface_ids_; ++ va_surface_format_ = va_format; ++ return true; ++} ++ ++void VaapiWrapper::DestroySurfaces() { ++ base::AutoLock auto_lock(*va_lock_); ++ DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; ++ ++ DestroySurfaces_Locked(); ++} ++ ++void VaapiWrapper::DestroySurfaces_Locked() { ++ va_lock_->AssertAcquired(); ++ ++ if (va_context_id_ != VA_INVALID_ID) { ++ VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); ++ } ++ ++ if (!va_surface_ids_.empty()) { ++ VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0], ++ va_surface_ids_.size()); ++ VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); ++ } ++ ++ va_surface_ids_.clear(); ++ va_context_id_ = VA_INVALID_ID; ++ va_surface_format_ = 0; ++} ++ ++scoped_refptr VaapiWrapper::CreateUnownedSurface( ++ unsigned int va_format, ++ const gfx::Size& size, ++ const std::vector& va_attribs) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ std::vector attribs(va_attribs); ++ VASurfaceID va_surface_id; ++ VAStatus va_res = ++ vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), ++ &va_surface_id, 1, &attribs[0], attribs.size()); ++ ++ scoped_refptr va_surface; ++ VA_SUCCESS_OR_RETURN(va_res, "Failed to create unowned VASurface", ++ va_surface); ++ ++ // This is safe to use Unretained() here, because the VDA takes care ++ // of the destruction order. All the surfaces will be destroyed ++ // before VaapiWrapper. ++ va_surface = new VASurface( ++ va_surface_id, size, va_format, ++ base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this))); ++ ++ return va_surface; ++} ++ ++scoped_refptr VaapiWrapper::CreateVASurfaceForPixmap( ++ const scoped_refptr& pixmap) { ++ // Create a VASurface for a NativePixmap by importing the underlying dmabufs. ++ VASurfaceAttribExternalBuffers va_attrib_extbuf; ++ memset(&va_attrib_extbuf, 0, sizeof(va_attrib_extbuf)); ++ ++ va_attrib_extbuf.pixel_format = ++ BufferFormatToVAFourCC(pixmap->GetBufferFormat()); ++ gfx::Size size = pixmap->GetBufferSize(); ++ va_attrib_extbuf.width = size.width(); ++ va_attrib_extbuf.height = size.height(); ++ ++ size_t num_fds = pixmap->GetDmaBufFdCount(); ++ size_t num_planes = ++ gfx::NumberOfPlanesForBufferFormat(pixmap->GetBufferFormat()); ++ if (num_fds == 0 || num_fds > num_planes) { ++ LOG(ERROR) << "Invalid number of dmabuf fds: " << num_fds ++ << " , planes: " << num_planes; ++ return nullptr; ++ } ++ ++ for (size_t i = 0; i < num_planes; ++i) { ++ va_attrib_extbuf.pitches[i] = pixmap->GetDmaBufPitch(i); ++ va_attrib_extbuf.offsets[i] = pixmap->GetDmaBufOffset(i); ++ DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i] ++ << " offset: " << va_attrib_extbuf.offsets[i]; ++ } ++ va_attrib_extbuf.num_planes = num_planes; ++ ++ std::vector fds(num_fds); ++ for (size_t i = 0; i < num_fds; ++i) { ++ int dmabuf_fd = pixmap->GetDmaBufFd(i); ++ if (dmabuf_fd < 0) { ++ LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap"; ++ return nullptr; ++ } ++ fds[i] = dmabuf_fd; ++ } ++ va_attrib_extbuf.buffers = fds.data(); ++ va_attrib_extbuf.num_buffers = fds.size(); ++ ++ va_attrib_extbuf.flags = 0; ++ va_attrib_extbuf.private_data = NULL; ++ ++ std::vector va_attribs(2); ++ ++ va_attribs[0].type = VASurfaceAttribMemoryType; ++ va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; ++ va_attribs[0].value.type = VAGenericValueTypeInteger; ++ va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; ++ ++ va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; ++ va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; ++ va_attribs[1].value.type = VAGenericValueTypePointer; ++ va_attribs[1].value.value.p = &va_attrib_extbuf; ++ ++ scoped_refptr va_surface = CreateUnownedSurface( ++ BufferFormatToVARTFormat(pixmap->GetBufferFormat()), size, va_attribs); ++ if (!va_surface) { ++ LOG(ERROR) << "Failed to create VASurface for an Ozone NativePixmap"; ++ return nullptr; ++ } ++ ++ return va_surface; ++} ++ ++void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1); ++ VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces on surface failed"); ++} ++ ++bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, ++ size_t size, ++ void* buffer) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VABufferID buffer_id; ++ VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, va_buffer_type, ++ size, 1, buffer, &buffer_id); ++ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); ++ ++ switch (va_buffer_type) { ++ case VASliceParameterBufferType: ++ case VASliceDataBufferType: ++ case VAEncSliceParameterBufferType: ++ pending_slice_bufs_.push_back(buffer_id); ++ break; ++ ++ default: ++ pending_va_bufs_.push_back(buffer_id); ++ break; ++ } ++ ++ return true; ++} ++ ++bool VaapiWrapper::SubmitVAEncMiscParamBuffer( ++ VAEncMiscParameterType misc_param_type, ++ size_t size, ++ void* buffer) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VABufferID buffer_id; ++ VAStatus va_res = vaCreateBuffer( ++ va_display_, va_context_id_, VAEncMiscParameterBufferType, ++ sizeof(VAEncMiscParameterBuffer) + size, 1, NULL, &buffer_id); ++ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); ++ ++ void* data_ptr = NULL; ++ va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr); ++ VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); ++ if (va_res != VA_STATUS_SUCCESS) { ++ vaDestroyBuffer(va_display_, buffer_id); ++ return false; ++ } ++ ++ DCHECK(data_ptr); ++ ++ VAEncMiscParameterBuffer* misc_param = ++ reinterpret_cast(data_ptr); ++ misc_param->type = misc_param_type; ++ memcpy(misc_param->data, buffer, size); ++ va_res = vaUnmapBuffer(va_display_, buffer_id); ++ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); ++ ++ pending_va_bufs_.push_back(buffer_id); ++ return true; ++} ++ ++void VaapiWrapper::DestroyPendingBuffers() { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ for (const auto& pending_va_buf : pending_va_bufs_) { ++ VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); ++ } ++ ++ for (const auto& pending_slice_buf : pending_slice_bufs_) { ++ VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); ++ } ++ ++ pending_va_bufs_.clear(); ++ pending_slice_bufs_.clear(); ++} ++ ++bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) { ++ base::AutoLock auto_lock(*va_lock_); ++ VAStatus va_res = ++ vaCreateBuffer(va_display_, va_context_id_, VAEncCodedBufferType, size, 1, ++ NULL, buffer_id); ++ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false); ++ ++ const auto is_new_entry = coded_buffers_.insert(*buffer_id).second; ++ DCHECK(is_new_entry); ++ return true; ++} ++ ++void VaapiWrapper::DestroyCodedBuffers() { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ for (std::set::const_iterator iter = coded_buffers_.begin(); ++ iter != coded_buffers_.end(); ++iter) { ++ VAStatus va_res = vaDestroyBuffer(va_display_, *iter); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); ++ } ++ ++ coded_buffers_.clear(); ++} ++ ++bool VaapiWrapper::Execute(VASurfaceID va_surface_id) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); ++ DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); ++ DVLOG(4) << "Target VA surface " << va_surface_id; ++ ++ // Get ready to execute for given surface. ++ VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, va_surface_id); ++ VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); ++ ++ if (pending_va_bufs_.size() > 0) { ++ // Commit parameter and slice buffers. ++ va_res = vaRenderPicture(va_display_, va_context_id_, &pending_va_bufs_[0], ++ pending_va_bufs_.size()); ++ VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); ++ } ++ ++ if (pending_slice_bufs_.size() > 0) { ++ va_res = ++ vaRenderPicture(va_display_, va_context_id_, &pending_slice_bufs_[0], ++ pending_slice_bufs_.size()); ++ VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); ++ } ++ ++ // Instruct HW codec to start processing committed buffers. ++ // Does not block and the job is not finished after this returns. ++ va_res = vaEndPicture(va_display_, va_context_id_); ++ VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); ++ ++ return true; ++} ++ ++bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { ++ bool result = Execute(va_surface_id); ++ DestroyPendingBuffers(); ++ return result; ++} ++ ++#if defined(USE_X11) ++bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, ++ Pixmap x_pixmap, ++ gfx::Size dest_size) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); ++ VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); ++ ++ // Put the data into an X Pixmap. ++ va_res = vaPutSurface(va_display_, ++ va_surface_id, ++ x_pixmap, ++ 0, 0, dest_size.width(), dest_size.height(), ++ 0, 0, dest_size.width(), dest_size.height(), ++ NULL, 0, 0); ++ VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false); ++ return true; ++} ++#endif // USE_X11 ++ ++bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id, ++ VAImageFormat* format, ++ const gfx::Size& size, ++ VAImage* image, ++ void** mem) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); ++ VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); ++ ++ va_res = ++ vaCreateImage(va_display_, format, size.width(), size.height(), image); ++ VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false); ++ ++ va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(), ++ size.height(), image->image_id); ++ VA_LOG_ON_ERROR(va_res, "vaGetImage failed"); ++ ++ if (va_res == VA_STATUS_SUCCESS) { ++ // Map the VAImage into memory ++ va_res = vaMapBuffer(va_display_, image->buf, mem); ++ VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); ++ } ++ ++ if (va_res != VA_STATUS_SUCCESS) { ++ va_res = vaDestroyImage(va_display_, image->image_id); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); ++ return false; ++ } ++ ++ return true; ++} ++ ++void VaapiWrapper::ReturnVaImage(VAImage* image) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VAStatus va_res = vaUnmapBuffer(va_display_, image->buf); ++ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); ++ ++ va_res = vaDestroyImage(va_display_, image->image_id); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); ++} ++ ++bool VaapiWrapper::UploadVideoFrameToSurface( ++ const scoped_refptr& frame, ++ VASurfaceID va_surface_id) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VAImage image; ++ VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image); ++ VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false); ++ base::ScopedClosureRunner vaimage_deleter( ++ base::Bind(&DestroyVAImage, va_display_, image)); ++ ++ if (image.format.fourcc != VA_FOURCC_NV12) { ++ LOG(ERROR) << "Unsupported image format: " << image.format.fourcc; ++ return false; ++ } ++ ++ if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) { ++ LOG(ERROR) << "Buffer too small to fit the frame."; ++ return false; ++ } ++ ++ void* image_ptr = NULL; ++ va_res = vaMapBuffer(va_display_, image.buf, &image_ptr); ++ VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); ++ DCHECK(image_ptr); ++ ++ int ret = 0; ++ { ++ base::AutoUnlock auto_unlock(*va_lock_); ++ ret = libyuv::I420ToNV12( ++ frame->data(VideoFrame::kYPlane), frame->stride(VideoFrame::kYPlane), ++ frame->data(VideoFrame::kUPlane), frame->stride(VideoFrame::kUPlane), ++ frame->data(VideoFrame::kVPlane), frame->stride(VideoFrame::kVPlane), ++ static_cast(image_ptr) + image.offsets[0], image.pitches[0], ++ static_cast(image_ptr) + image.offsets[1], image.pitches[1], ++ image.width, image.height); ++ } ++ ++ va_res = vaUnmapBuffer(va_display_, image.buf); ++ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); ++ ++ return ret == 0; ++} ++ ++bool VaapiWrapper::DownloadFromCodedBuffer(VABufferID buffer_id, ++ VASurfaceID sync_surface_id, ++ uint8_t* target_ptr, ++ size_t target_size, ++ size_t* coded_data_size) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id); ++ VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); ++ ++ VACodedBufferSegment* buffer_segment = NULL; ++ va_res = vaMapBuffer(va_display_, buffer_id, ++ reinterpret_cast(&buffer_segment)); ++ VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); ++ DCHECK(target_ptr); ++ ++ { ++ base::AutoUnlock auto_unlock(*va_lock_); ++ *coded_data_size = 0; ++ ++ while (buffer_segment) { ++ DCHECK(buffer_segment->buf); ++ ++ if (buffer_segment->size > target_size) { ++ LOG(ERROR) << "Insufficient output buffer size"; ++ break; ++ } ++ ++ memcpy(target_ptr, buffer_segment->buf, buffer_segment->size); ++ ++ target_ptr += buffer_segment->size; ++ *coded_data_size += buffer_segment->size; ++ target_size -= buffer_segment->size; ++ ++ buffer_segment = ++ reinterpret_cast(buffer_segment->next); ++ } ++ } ++ ++ va_res = vaUnmapBuffer(va_display_, buffer_id); ++ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); ++ return buffer_segment == NULL; ++} ++ ++bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id, ++ VASurfaceID sync_surface_id, ++ uint8_t* target_ptr, ++ size_t target_size, ++ size_t* coded_data_size) { ++ bool result = DownloadFromCodedBuffer(buffer_id, sync_surface_id, target_ptr, ++ target_size, coded_data_size); ++ ++ VAStatus va_res = vaDestroyBuffer(va_display_, buffer_id); ++ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); ++ const auto was_found = coded_buffers_.erase(buffer_id); ++ DCHECK(was_found); ++ ++ return result; ++} ++ ++bool VaapiWrapper::BlitSurface( ++ const scoped_refptr& va_surface_src, ++ const scoped_refptr& va_surface_dest) { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ // Initialize the post processing engine if not already done. ++ if (va_vpp_buffer_id_ == VA_INVALID_ID) { ++ if (!InitializeVpp_Locked()) ++ return false; ++ } ++ ++ VAProcPipelineParameterBuffer* pipeline_param; ++ VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_, va_vpp_buffer_id_, ++ reinterpret_cast(&pipeline_param)), ++ "Couldn't map vpp buffer", false); ++ ++ memset(pipeline_param, 0, sizeof *pipeline_param); ++ const gfx::Size src_size = va_surface_src->size(); ++ const gfx::Size dest_size = va_surface_dest->size(); ++ ++ VARectangle input_region; ++ input_region.x = input_region.y = 0; ++ input_region.width = src_size.width(); ++ input_region.height = src_size.height(); ++ pipeline_param->surface_region = &input_region; ++ pipeline_param->surface = va_surface_src->id(); ++ pipeline_param->surface_color_standard = VAProcColorStandardNone; ++ ++ VARectangle output_region; ++ output_region.x = output_region.y = 0; ++ output_region.width = dest_size.width(); ++ output_region.height = dest_size.height(); ++ pipeline_param->output_region = &output_region; ++ pipeline_param->output_background_color = 0xff000000; ++ pipeline_param->output_color_standard = VAProcColorStandardNone; ++ pipeline_param->filter_flags = VA_FILTER_SCALING_DEFAULT; ++ ++ VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_, va_vpp_buffer_id_), ++ "Couldn't unmap vpp buffer", false); ++ ++ VA_SUCCESS_OR_RETURN( ++ vaBeginPicture(va_display_, va_vpp_context_id_, va_surface_dest->id()), ++ "Couldn't begin picture", false); ++ ++ VA_SUCCESS_OR_RETURN( ++ vaRenderPicture(va_display_, va_vpp_context_id_, &va_vpp_buffer_id_, 1), ++ "Couldn't render picture", false); ++ ++ VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_, va_vpp_context_id_), ++ "Couldn't end picture", false); ++ ++ return true; ++} ++ ++bool VaapiWrapper::InitializeVpp_Locked() { ++ va_lock_->AssertAcquired(); ++ ++ VA_SUCCESS_OR_RETURN( ++ vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0, ++ &va_vpp_config_id_), ++ "Couldn't create config", false); ++ ++ // The size of the picture for the context is irrelevant in the case ++ // of the VPP, just passing 1x1. ++ VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_, va_vpp_config_id_, 1, 1, 0, ++ NULL, 0, &va_vpp_context_id_), ++ "Couldn't create context", false); ++ ++ VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_, va_vpp_context_id_, ++ VAProcPipelineParameterBufferType, ++ sizeof(VAProcPipelineParameterBuffer), 1, ++ NULL, &va_vpp_buffer_id_), ++ "Couldn't create buffer", false); ++ ++ return true; ++} ++ ++void VaapiWrapper::DeinitializeVpp() { ++ base::AutoLock auto_lock(*va_lock_); ++ ++ if (va_vpp_buffer_id_ != VA_INVALID_ID) { ++ vaDestroyBuffer(va_display_, va_vpp_buffer_id_); ++ va_vpp_buffer_id_ = VA_INVALID_ID; ++ } ++ if (va_vpp_context_id_ != VA_INVALID_ID) { ++ vaDestroyContext(va_display_, va_vpp_context_id_); ++ va_vpp_context_id_ = VA_INVALID_ID; ++ } ++ if (va_vpp_config_id_ != VA_INVALID_ID) { ++ vaDestroyConfig(va_display_, va_vpp_config_id_); ++ va_vpp_config_id_ = VA_INVALID_ID; ++ } ++} ++ ++// static ++void VaapiWrapper::PreSandboxInitialization() { ++ VADisplayState::PreSandboxInitialization(); ++} ++ ++} // namespace media +--- /dev/null ++++ b/media/gpu/vaapi/vaapi_wrapper.h +@@ -0,0 +1,288 @@ ++// Copyright 2013 The Chromium Authors. All rights reserved. ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++// ++// This file contains an implementation of VaapiWrapper, used by ++// VaapiVideoDecodeAccelerator and VaapiH264Decoder for decode, ++// and VaapiVideoEncodeAccelerator for encode, to interface ++// with libva (VA-API library for hardware video codec). ++ ++#ifndef MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ ++#define MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "base/files/file.h" ++#include "base/macros.h" ++#include "base/memory/ref_counted.h" ++#include "base/synchronization/lock.h" ++#include "media/base/video_decoder_config.h" ++#include "media/base/video_frame.h" ++#include "media/gpu/media_gpu_export.h" ++#include "media/gpu/vaapi/va_surface.h" ++#include "media/video/jpeg_decode_accelerator.h" ++#include "media/video/video_decode_accelerator.h" ++#include "media/video/video_encode_accelerator.h" ++#include "ui/gfx/geometry/size.h" ++ ++#if defined(USE_X11) ++#include "ui/gfx/x/x11.h" ++#endif // USE_X11 ++ ++namespace gfx { ++class NativePixmap; ++} ++ ++namespace media { ++ ++// This class handles VA-API calls and ensures proper locking of VA-API calls ++// to libva, the userspace shim to the HW codec driver. libva is not ++// thread-safe, so we have to perform locking ourselves. This class is fully ++// synchronous and its methods can be called from any thread and may wait on ++// the va_lock_ while other, concurrent calls run. ++// ++// This class is responsible for managing VAAPI connection, contexts and state. ++// It is also responsible for managing and freeing VABuffers (not VASurfaces), ++// which are used to queue parameters and slice data to the HW codec, ++// as well as underlying memory for VASurfaces themselves. ++class MEDIA_GPU_EXPORT VaapiWrapper ++ : public base::RefCountedThreadSafe { ++ public: ++ enum CodecMode { ++ kDecode, ++ kEncode, ++ kCodecModeMax, ++ }; ++ ++ // Return an instance of VaapiWrapper initialized for |va_profile| and ++ // |mode|. |report_error_to_uma_cb| will be called independently from ++ // reporting errors to clients via method return values. ++ static scoped_refptr Create( ++ CodecMode mode, ++ VAProfile va_profile, ++ const base::Closure& report_error_to_uma_cb); ++ ++ // Create VaapiWrapper for VideoCodecProfile. It maps VideoCodecProfile ++ // |profile| to VAProfile. ++ // |report_error_to_uma_cb| will be called independently from reporting ++ // errors to clients via method return values. ++ static scoped_refptr CreateForVideoCodec( ++ CodecMode mode, ++ VideoCodecProfile profile, ++ const base::Closure& report_error_to_uma_cb); ++ ++ // Return the supported video encode profiles. ++ static VideoEncodeAccelerator::SupportedProfiles GetSupportedEncodeProfiles(); ++ ++ // Return the supported video decode profiles. ++ static VideoDecodeAccelerator::SupportedProfiles GetSupportedDecodeProfiles(); ++ ++ // Return true when JPEG decode is supported. ++ static bool IsJpegDecodeSupported(); ++ ++ // Return true when JPEG encode is supported. ++ static bool IsJpegEncodeSupported(); ++ ++ // Create |num_surfaces| backing surfaces in driver for VASurfaces of ++ // |va_format|, each of size |size|. Returns true when successful, with the ++ // created IDs in |va_surfaces| to be managed and later wrapped in ++ // VASurfaces. ++ // The client must DestroySurfaces() each time before calling this method ++ // again to free the allocated surfaces first, but is not required to do so ++ // at destruction time, as this will be done automatically from ++ // the destructor. ++ virtual bool CreateSurfaces(unsigned int va_format, ++ const gfx::Size& size, ++ size_t num_surfaces, ++ std::vector* va_surfaces); ++ ++ // Free all memory allocated in CreateSurfaces. ++ virtual void DestroySurfaces(); ++ ++ // Create a VASurface of |va_format|, |size| and using |va_attribs| ++ // attributes. The ownership of the surface is transferred to the ++ // caller. It differs from surfaces created using CreateSurfaces(), ++ // where VaapiWrapper is the owner of the surfaces. ++ scoped_refptr CreateUnownedSurface( ++ unsigned int va_format, ++ const gfx::Size& size, ++ const std::vector& va_attribs); ++ ++ // Create a VASurface for |pixmap|. The ownership of the surface is ++ // transferred to the caller. It differs from surfaces created using ++ // CreateSurfaces(), where VaapiWrapper is the owner of the surfaces. ++ scoped_refptr CreateVASurfaceForPixmap( ++ const scoped_refptr& pixmap); ++ ++ // Submit parameters or slice data of |va_buffer_type|, copying them from ++ // |buffer| of size |size|, into HW codec. The data in |buffer| is no ++ // longer needed and can be freed after this method returns. ++ // Data submitted via this method awaits in the HW codec until ++ // ExecuteAndDestroyPendingBuffers() is called to execute or ++ // DestroyPendingBuffers() is used to cancel a pending job. ++ bool SubmitBuffer(VABufferType va_buffer_type, size_t size, void* buffer); ++ ++ // Submit a VAEncMiscParameterBuffer of given |misc_param_type|, copying its ++ // data from |buffer| of size |size|, into HW codec. The data in |buffer| is ++ // no longer needed and can be freed after this method returns. ++ // Data submitted via this method awaits in the HW codec until ++ // ExecuteAndDestroyPendingBuffers() is called to execute or ++ // DestroyPendingBuffers() is used to cancel a pending job. ++ bool SubmitVAEncMiscParamBuffer(VAEncMiscParameterType misc_param_type, ++ size_t size, ++ void* buffer); ++ ++ // Cancel and destroy all buffers queued to the HW codec via SubmitBuffer(). ++ // Useful when a pending job is to be cancelled (on reset or error). ++ void DestroyPendingBuffers(); ++ ++ // Execute job in hardware on target |va_surface_id| and destroy pending ++ // buffers. Return false if Execute() fails. ++ bool ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id); ++ ++#if defined(USE_X11) ++ // Put data from |va_surface_id| into |x_pixmap| of size ++ // |dest_size|, converting/scaling to it. ++ bool PutSurfaceIntoPixmap(VASurfaceID va_surface_id, ++ Pixmap x_pixmap, ++ gfx::Size dest_size); ++#endif // USE_X11 ++ ++ // Get a VAImage from a VASurface |va_surface_id| and map it into memory with ++ // given |format| and |size|. The output is |image| and the mapped memory is ++ // |mem|. If |format| doesn't equal to the internal format, the underlying ++ // implementation will do format conversion if supported. |size| should be ++ // smaller than or equal to the surface. If |size| is smaller, the image will ++ // be cropped. The VAImage should be released using the ReturnVaImage ++ // function. Returns true when successful. ++ bool GetVaImage(VASurfaceID va_surface_id, ++ VAImageFormat* format, ++ const gfx::Size& size, ++ VAImage* image, ++ void** mem); ++ ++ // Release the VAImage (and the associated memory mapping) obtained from ++ // GetVaImage(). ++ void ReturnVaImage(VAImage* image); ++ ++ // Upload contents of |frame| into |va_surface_id| for encode. ++ bool UploadVideoFrameToSurface(const scoped_refptr& frame, ++ VASurfaceID va_surface_id); ++ ++ // Create a buffer of |size| bytes to be used as encode output. ++ bool CreateCodedBuffer(size_t size, VABufferID* buffer_id); ++ ++ // Download the contents of the buffer with given |buffer_id| into a buffer of ++ // size |target_size|, pointed to by |target_ptr|. The number of bytes ++ // downloaded will be returned in |coded_data_size|. |sync_surface_id| will ++ // be used as a sync point, i.e. it will have to become idle before starting ++ // the download. |sync_surface_id| should be the source surface passed ++ // to the encode job. ++ bool DownloadFromCodedBuffer(VABufferID buffer_id, ++ VASurfaceID sync_surface_id, ++ uint8_t* target_ptr, ++ size_t target_size, ++ size_t* coded_data_size); ++ ++ // See DownloadFromCodedBuffer() for details. After downloading, it deletes ++ // the VA buffer with |buffer_id|. ++ bool DownloadAndDestroyCodedBuffer(VABufferID buffer_id, ++ VASurfaceID sync_surface_id, ++ uint8_t* target_ptr, ++ size_t target_size, ++ size_t* coded_data_size); ++ ++ // Destroy all previously-allocated (and not yet destroyed) coded buffers. ++ void DestroyCodedBuffers(); ++ ++ // Blits a VASurface |va_surface_src| into another VASurface ++ // |va_surface_dest| applying pixel format conversion and scaling ++ // if needed. ++ bool BlitSurface(const scoped_refptr& va_surface_src, ++ const scoped_refptr& va_surface_dest); ++ ++ // Initialize static data before sandbox is enabled. ++ static void PreSandboxInitialization(); ++ ++ // Get the created surfaces format. ++ unsigned int va_surface_format() const { return va_surface_format_; } ++ ++ protected: ++ VaapiWrapper(); ++ virtual ~VaapiWrapper(); ++ ++ private: ++ friend class base::RefCountedThreadSafe; ++ ++ bool Initialize(CodecMode mode, VAProfile va_profile); ++ void Deinitialize(); ++ bool VaInitialize(const base::Closure& report_error_to_uma_cb); ++ ++ // Free all memory allocated in CreateSurfaces. ++ void DestroySurfaces_Locked(); ++ // Destroys a |va_surface| created using CreateUnownedSurface. ++ void DestroyUnownedSurface(VASurfaceID va_surface_id); ++ ++ // Initialize the video post processing context with the |size| of ++ // the input pictures to be processed. ++ bool InitializeVpp_Locked(); ++ ++ // Deinitialize the video post processing context. ++ void DeinitializeVpp(); ++ ++ // Execute pending job in hardware and destroy pending buffers. Return false ++ // if vaapi driver refuses to accept parameter or slice buffers submitted ++ // by client, or if execution fails in hardware. ++ bool Execute(VASurfaceID va_surface_id); ++ ++ // Attempt to set render mode to "render to texture.". Failure is non-fatal. ++ void TryToSetVADisplayAttributeToLocalGPU(); ++ ++ // Pointer to VADisplayState's member |va_lock_|. Guaranteed to be valid for ++ // the lifetime of VaapiWrapper. ++ base::Lock* va_lock_; ++ ++ // Allocated ids for VASurfaces. ++ std::vector va_surface_ids_; ++ ++ // VA format of surfaces with va_surface_ids_. ++ unsigned int va_surface_format_; ++ ++ // VA handles. ++ // All valid after successful Initialize() and until Deinitialize(). ++ VADisplay va_display_; ++ VAConfigID va_config_id_; ++ // Created for the current set of va_surface_ids_ in CreateSurfaces() and ++ // valid until DestroySurfaces(). ++ VAContextID va_context_id_; ++ ++ // Data queued up for HW codec, to be committed on next execution. ++ std::vector pending_slice_bufs_; ++ std::vector pending_va_bufs_; ++ ++ // Bitstream buffers for encode. ++ std::set coded_buffers_; ++ ++ // Called to report codec errors to UMA. Errors to clients are reported via ++ // return values from public methods. ++ base::Closure report_error_to_uma_cb_; ++ ++ // VPP (Video Post Processing) context, this is used to convert ++ // pictures used by the decoder to RGBA pictures usable by GL or the ++ // display hardware. ++ VAConfigID va_vpp_config_id_; ++ VAContextID va_vpp_context_id_; ++ VABufferID va_vpp_buffer_id_; ++ ++ DISALLOW_COPY_AND_ASSIGN(VaapiWrapper); ++}; ++ ++} // namespace media ++ ++#endif // MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ +--- a/media/gpu/vaapi_jpeg_decode_accelerator.cc ++++ /dev/null +@@ -1,325 +0,0 @@ +-// Copyright 2015 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_jpeg_decode_accelerator.h" +- +-#include +-#include +- +-#include +-#include +- +-#include "base/bind.h" +-#include "base/logging.h" +-#include "base/metrics/histogram_macros.h" +-#include "base/threading/thread_task_runner_handle.h" +-#include "base/trace_event/trace_event.h" +-#include "gpu/ipc/service/gpu_channel.h" +-#include "media/base/video_frame.h" +-#include "media/filters/jpeg_parser.h" +-#include "media/gpu/shared_memory_region.h" +-#include "media/gpu/vaapi/vaapi_picture.h" +-#include "third_party/libyuv/include/libyuv.h" +- +-#define VLOGF(level) VLOG(level) << __func__ << "(): " +-#define DVLOGF(level) DVLOG(level) << __func__ << "(): " +- +-namespace media { +- +-namespace { +-// UMA errors that the VaapiJpegDecodeAccelerator class reports. +-enum VAJDADecoderFailure { +- VAAPI_ERROR = 0, +- VAJDA_DECODER_FAILURES_MAX, +-}; +- +-static void ReportToUMA(VAJDADecoderFailure failure) { +- UMA_HISTOGRAM_ENUMERATION("Media.VAJDA.DecoderFailure", failure, +- VAJDA_DECODER_FAILURES_MAX + 1); +-} +- +-static unsigned int VaSurfaceFormatForJpeg( +- const JpegFrameHeader& frame_header) { +- // The range of sampling factor is [1, 4]. Pack them into integer to make the +- // matching code simpler. For example, 0x211 means the sampling factor are 2, +- // 1, 1 for 3 components. +- unsigned int h = 0, v = 0; +- for (int i = 0; i < frame_header.num_components; i++) { +- DCHECK_LE(frame_header.components[i].horizontal_sampling_factor, 4); +- DCHECK_LE(frame_header.components[i].vertical_sampling_factor, 4); +- h = h << 4 | frame_header.components[i].horizontal_sampling_factor; +- v = v << 4 | frame_header.components[i].vertical_sampling_factor; +- } +- +- switch (frame_header.num_components) { +- case 1: // Grey image +- return VA_RT_FORMAT_YUV400; +- +- case 3: // Y Cb Cr color image +- // See https://en.wikipedia.org/wiki/Chroma_subsampling for the +- // definition of these numbers. +- if (h == 0x211 && v == 0x211) +- return VA_RT_FORMAT_YUV420; +- +- if (h == 0x211 && v == 0x111) +- return VA_RT_FORMAT_YUV422; +- +- if (h == 0x111 && v == 0x111) +- return VA_RT_FORMAT_YUV444; +- +- if (h == 0x411 && v == 0x111) +- return VA_RT_FORMAT_YUV411; +- } +- VLOGF(1) << "Unsupported sampling factor: num_components=" +- << frame_header.num_components << ", h=" << std::hex << h +- << ", v=" << v; +- +- return 0; +-} +- +-} // namespace +- +-VaapiJpegDecodeAccelerator::DecodeRequest::DecodeRequest( +- int32_t bitstream_buffer_id, +- std::unique_ptr shm, +- const scoped_refptr& video_frame) +- : bitstream_buffer_id(bitstream_buffer_id), +- shm(std::move(shm)), +- video_frame(video_frame) {} +- +-VaapiJpegDecodeAccelerator::DecodeRequest::~DecodeRequest() {} +- +-void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id, +- Error error) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- VLOGF(1) << "Notifying of error " << error; +- DCHECK(client_); +- client_->NotifyError(bitstream_buffer_id, error); +-} +- +-void VaapiJpegDecodeAccelerator::NotifyErrorFromDecoderThread( +- int32_t bitstream_buffer_id, +- Error error) { +- DCHECK(decoder_task_runner_->BelongsToCurrentThread()); +- task_runner_->PostTask(FROM_HERE, +- base::Bind(&VaapiJpegDecodeAccelerator::NotifyError, +- weak_this_, bitstream_buffer_id, error)); +-} +- +-void VaapiJpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- client_->VideoFrameReady(bitstream_buffer_id); +-} +- +-VaapiJpegDecodeAccelerator::VaapiJpegDecodeAccelerator( +- const scoped_refptr& io_task_runner) +- : task_runner_(base::ThreadTaskRunnerHandle::Get()), +- io_task_runner_(io_task_runner), +- decoder_thread_("VaapiJpegDecoderThread"), +- va_surface_id_(VA_INVALID_SURFACE), +- weak_this_factory_(this) { +- weak_this_ = weak_this_factory_.GetWeakPtr(); +-} +- +-VaapiJpegDecodeAccelerator::~VaapiJpegDecodeAccelerator() { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- VLOGF(2) << "Destroying VaapiJpegDecodeAccelerator"; +- +- weak_this_factory_.InvalidateWeakPtrs(); +- decoder_thread_.Stop(); +-} +- +-bool VaapiJpegDecodeAccelerator::Initialize(Client* client) { +- VLOGF(2); +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- client_ = client; +- +- vaapi_wrapper_ = +- VaapiWrapper::Create(VaapiWrapper::kDecode, VAProfileJPEGBaseline, +- base::Bind(&ReportToUMA, VAAPI_ERROR)); +- +- if (!vaapi_wrapper_.get()) { +- VLOGF(1) << "Failed initializing VAAPI"; +- return false; +- } +- +- if (!decoder_thread_.Start()) { +- VLOGF(1) << "Failed to start decoding thread."; +- return false; +- } +- decoder_task_runner_ = decoder_thread_.task_runner(); +- +- return true; +-} +- +-bool VaapiJpegDecodeAccelerator::OutputPicture( +- VASurfaceID va_surface_id, +- int32_t input_buffer_id, +- const scoped_refptr& video_frame) { +- DCHECK(decoder_task_runner_->BelongsToCurrentThread()); +- +- TRACE_EVENT1("jpeg", "VaapiJpegDecodeAccelerator::OutputPicture", +- "input_buffer_id", input_buffer_id); +- +- DVLOGF(4) << "Outputting VASurface " << va_surface_id +- << " into video_frame associated with input buffer id " +- << input_buffer_id; +- +- VAImage image; +- VAImageFormat format; +- const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); +- memset(&image, 0, sizeof(image)); +- memset(&format, 0, sizeof(format)); +- format.fourcc = kI420Fourcc; +- format.byte_order = VA_LSB_FIRST; +- format.bits_per_pixel = 12; // 12 for I420 +- +- uint8_t* mem = nullptr; +- gfx::Size coded_size = video_frame->coded_size(); +- if (!vaapi_wrapper_->GetVaImage(va_surface_id, &format, coded_size, &image, +- reinterpret_cast(&mem))) { +- VLOGF(1) << "Cannot get VAImage"; +- return false; +- } +- +- // Copy image content from VAImage to VideoFrame. +- // The component order of VAImage I420 are Y, U, and V. +- DCHECK_EQ(image.num_planes, 3u); +- DCHECK_GE(image.width, coded_size.width()); +- DCHECK_GE(image.height, coded_size.height()); +- const uint8_t* src_y = mem + image.offsets[0]; +- const uint8_t* src_u = mem + image.offsets[1]; +- const uint8_t* src_v = mem + image.offsets[2]; +- size_t src_y_stride = image.pitches[0]; +- size_t src_u_stride = image.pitches[1]; +- size_t src_v_stride = image.pitches[2]; +- uint8_t* dst_y = video_frame->data(VideoFrame::kYPlane); +- uint8_t* dst_u = video_frame->data(VideoFrame::kUPlane); +- uint8_t* dst_v = video_frame->data(VideoFrame::kVPlane); +- size_t dst_y_stride = video_frame->stride(VideoFrame::kYPlane); +- size_t dst_u_stride = video_frame->stride(VideoFrame::kUPlane); +- size_t dst_v_stride = video_frame->stride(VideoFrame::kVPlane); +- +- if (libyuv::I420Copy(src_y, src_y_stride, // Y +- src_u, src_u_stride, // U +- src_v, src_v_stride, // V +- dst_y, dst_y_stride, // Y +- dst_u, dst_u_stride, // U +- dst_v, dst_v_stride, // V +- coded_size.width(), coded_size.height())) { +- VLOGF(1) << "I420Copy failed"; +- return false; +- } +- +- vaapi_wrapper_->ReturnVaImage(&image); +- +- task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::VideoFrameReady, +- weak_this_, input_buffer_id)); +- +- return true; +-} +- +-void VaapiJpegDecodeAccelerator::DecodeTask( +- const std::unique_ptr& request) { +- DVLOGF(4); +- DCHECK(decoder_task_runner_->BelongsToCurrentThread()); +- TRACE_EVENT0("jpeg", "DecodeTask"); +- +- JpegParseResult parse_result; +- if (!ParseJpegPicture( +- reinterpret_cast(request->shm->memory()), +- request->shm->size(), &parse_result)) { +- VLOGF(1) << "ParseJpegPicture failed"; +- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, +- PARSE_JPEG_FAILED); +- return; +- } +- +- unsigned int new_va_rt_format = +- VaSurfaceFormatForJpeg(parse_result.frame_header); +- if (!new_va_rt_format) { +- VLOGF(1) << "Unsupported subsampling"; +- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, +- UNSUPPORTED_JPEG); +- return; +- } +- +- // Reuse VASurface if size doesn't change. +- gfx::Size new_coded_size(parse_result.frame_header.coded_width, +- parse_result.frame_header.coded_height); +- if (new_coded_size != coded_size_ || va_surface_id_ == VA_INVALID_SURFACE || +- new_va_rt_format != va_rt_format_) { +- vaapi_wrapper_->DestroySurfaces(); +- va_surface_id_ = VA_INVALID_SURFACE; +- va_rt_format_ = new_va_rt_format; +- +- std::vector va_surfaces; +- if (!vaapi_wrapper_->CreateSurfaces(va_rt_format_, new_coded_size, 1, +- &va_surfaces)) { +- VLOGF(1) << "Create VA surface failed"; +- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, +- PLATFORM_FAILURE); +- return; +- } +- va_surface_id_ = va_surfaces[0]; +- coded_size_ = new_coded_size; +- } +- +- if (!VaapiJpegDecoder::Decode(vaapi_wrapper_.get(), parse_result, +- va_surface_id_)) { +- VLOGF(1) << "Decode JPEG failed"; +- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, +- PLATFORM_FAILURE); +- return; +- } +- +- if (!OutputPicture(va_surface_id_, request->bitstream_buffer_id, +- request->video_frame)) { +- VLOGF(1) << "Output picture failed"; +- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, +- PLATFORM_FAILURE); +- return; +- } +-} +- +-void VaapiJpegDecodeAccelerator::Decode( +- const BitstreamBuffer& bitstream_buffer, +- const scoped_refptr& video_frame) { +- DCHECK(io_task_runner_->BelongsToCurrentThread()); +- TRACE_EVENT1("jpeg", "Decode", "input_id", bitstream_buffer.id()); +- +- DVLOGF(4) << "Mapping new input buffer id: " << bitstream_buffer.id() +- << " size: " << bitstream_buffer.size(); +- +- // SharedMemoryRegion will take over the |bitstream_buffer.handle()|. +- std::unique_ptr shm( +- new SharedMemoryRegion(bitstream_buffer, true)); +- +- if (bitstream_buffer.id() < 0) { +- VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); +- NotifyErrorFromDecoderThread(bitstream_buffer.id(), INVALID_ARGUMENT); +- return; +- } +- +- if (!shm->Map()) { +- VLOGF(1) << "Failed to map input buffer"; +- NotifyErrorFromDecoderThread(bitstream_buffer.id(), UNREADABLE_INPUT); +- return; +- } +- +- std::unique_ptr request( +- new DecodeRequest(bitstream_buffer.id(), std::move(shm), video_frame)); +- +- decoder_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::DecodeTask, +- base::Unretained(this), base::Passed(&request))); +-} +- +-bool VaapiJpegDecodeAccelerator::IsSupported() { +- return VaapiWrapper::IsJpegDecodeSupported(); +-} +- +-} // namespace media +--- a/media/gpu/vaapi_jpeg_decode_accelerator.h ++++ /dev/null +@@ -1,121 +0,0 @@ +-// Copyright 2015 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#ifndef MEDIA_GPU_VAAPI_JPEG_DECODE_ACCELERATOR_H_ +-#define MEDIA_GPU_VAAPI_JPEG_DECODE_ACCELERATOR_H_ +- +-#include +- +-#include +- +-#include "base/macros.h" +-#include "base/memory/linked_ptr.h" +-#include "base/memory/weak_ptr.h" +-#include "base/single_thread_task_runner.h" +-#include "base/synchronization/lock.h" +-#include "base/threading/thread.h" +-#include "media/base/bitstream_buffer.h" +-#include "media/gpu/media_gpu_export.h" +-#include "media/gpu/shared_memory_region.h" +-#include "media/gpu/vaapi_jpeg_decoder.h" +-#include "media/gpu/vaapi_wrapper.h" +-#include "media/video/jpeg_decode_accelerator.h" +- +-namespace media { +- +-// Class to provide JPEG decode acceleration for Intel systems with hardware +-// support for it, and on which libva is available. +-// Decoding tasks are performed in a separate decoding thread. +-// +-// Threading/life-cycle: this object is created & destroyed on the GPU +-// ChildThread. A few methods on it are called on the decoder thread which is +-// stopped during |this->Destroy()|, so any tasks posted to the decoder thread +-// can assume |*this| is still alive. See |weak_this_| below for more details. +-class MEDIA_GPU_EXPORT VaapiJpegDecodeAccelerator +- : public JpegDecodeAccelerator { +- public: +- VaapiJpegDecodeAccelerator( +- const scoped_refptr& io_task_runner); +- ~VaapiJpegDecodeAccelerator() override; +- +- // JpegDecodeAccelerator implementation. +- bool Initialize(JpegDecodeAccelerator::Client* client) override; +- void Decode(const BitstreamBuffer& bitstream_buffer, +- const scoped_refptr& video_frame) override; +- bool IsSupported() override; +- +- private: +- // An input buffer and the corresponding output video frame awaiting +- // consumption, provided by the client. +- struct DecodeRequest { +- DecodeRequest(int32_t bitstream_buffer_id, +- std::unique_ptr shm, +- const scoped_refptr& video_frame); +- ~DecodeRequest(); +- +- int32_t bitstream_buffer_id; +- std::unique_ptr shm; +- scoped_refptr video_frame; +- }; +- +- // Notifies the client that an error has occurred and decoding cannot +- // continue. +- void NotifyError(int32_t bitstream_buffer_id, Error error); +- void NotifyErrorFromDecoderThread(int32_t bitstream_buffer_id, Error error); +- void VideoFrameReady(int32_t bitstream_buffer_id); +- +- // Processes one decode |request|. +- void DecodeTask(const std::unique_ptr& request); +- +- // Puts contents of |va_surface| into given |video_frame|, releases the +- // surface and passes the |input_buffer_id| of the resulting picture to +- // client for output. +- bool OutputPicture(VASurfaceID va_surface_id, +- int32_t input_buffer_id, +- const scoped_refptr& video_frame); +- +- // ChildThread's task runner. +- scoped_refptr task_runner_; +- +- // GPU IO task runner. +- scoped_refptr io_task_runner_; +- +- // The client of this class. +- Client* client_; +- +- // WeakPtr<> pointing to |this| for use in posting tasks from the decoder +- // thread back to the ChildThread. Because the decoder thread is a member of +- // this class, any task running on the decoder thread is guaranteed that this +- // object is still alive. As a result, tasks posted from ChildThread to +- // decoder thread should use base::Unretained(this), and tasks posted from the +- // decoder thread to the ChildThread should use |weak_this_|. +- base::WeakPtr weak_this_; +- +- scoped_refptr vaapi_wrapper_; +- +- // Comes after vaapi_wrapper_ to ensure its destructor is executed before +- // |vaapi_wrapper_| is destroyed. +- std::unique_ptr decoder_; +- base::Thread decoder_thread_; +- // Use this to post tasks to |decoder_thread_| instead of +- // |decoder_thread_.task_runner()| because the latter will be NULL once +- // |decoder_thread_.Stop()| returns. +- scoped_refptr decoder_task_runner_; +- +- // The current VA surface for decoding. +- VASurfaceID va_surface_id_; +- // The coded size associated with |va_surface_id_|. +- gfx::Size coded_size_; +- // The VA RT format associated with |va_surface_id_|. +- unsigned int va_rt_format_; +- +- // The WeakPtrFactory for |weak_this_|. +- base::WeakPtrFactory weak_this_factory_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecodeAccelerator); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VAAPI_JPEG_DECODE_ACCELERATOR_H_ +--- a/media/gpu/vaapi_jpeg_decoder.cc ++++ /dev/null +@@ -1,229 +0,0 @@ +-// Copyright 2015 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_jpeg_decoder.h" +- +-#include +-#include +- +-#include "base/logging.h" +-#include "base/macros.h" +-#include "media/filters/jpeg_parser.h" +- +-namespace media { +- +-// VAAPI only support subset of JPEG profiles. This function determines a given +-// parsed JPEG result is supported or not. +-static bool IsVaapiSupportedJpeg(const JpegParseResult& jpeg) { +- if (jpeg.frame_header.visible_width < 1 || +- jpeg.frame_header.visible_height < 1) { +- DLOG(ERROR) << "width(" << jpeg.frame_header.visible_width +- << ") and height(" << jpeg.frame_header.visible_height +- << ") should be at least 1"; +- return false; +- } +- +- // Size 64k*64k is the maximum in the JPEG standard. VAAPI doesn't support +- // resolutions larger than 16k*16k. +- const int kMaxDimension = 16384; +- if (jpeg.frame_header.coded_width > kMaxDimension || +- jpeg.frame_header.coded_height > kMaxDimension) { +- DLOG(ERROR) << "VAAPI doesn't support size(" +- << jpeg.frame_header.coded_width << "*" +- << jpeg.frame_header.coded_height << ") larger than " +- << kMaxDimension << "*" << kMaxDimension; +- return false; +- } +- +- if (jpeg.frame_header.num_components != 3) { +- DLOG(ERROR) << "VAAPI doesn't support num_components(" +- << static_cast(jpeg.frame_header.num_components) +- << ") != 3"; +- return false; +- } +- +- if (jpeg.frame_header.components[0].horizontal_sampling_factor < +- jpeg.frame_header.components[1].horizontal_sampling_factor || +- jpeg.frame_header.components[0].horizontal_sampling_factor < +- jpeg.frame_header.components[2].horizontal_sampling_factor) { +- DLOG(ERROR) << "VAAPI doesn't supports horizontal sampling factor of Y" +- << " smaller than Cb and Cr"; +- return false; +- } +- +- if (jpeg.frame_header.components[0].vertical_sampling_factor < +- jpeg.frame_header.components[1].vertical_sampling_factor || +- jpeg.frame_header.components[0].vertical_sampling_factor < +- jpeg.frame_header.components[2].vertical_sampling_factor) { +- DLOG(ERROR) << "VAAPI doesn't supports vertical sampling factor of Y" +- << " smaller than Cb and Cr"; +- return false; +- } +- +- return true; +-} +- +-static void FillPictureParameters( +- const JpegFrameHeader& frame_header, +- VAPictureParameterBufferJPEGBaseline* pic_param) { +- memset(pic_param, 0, sizeof(*pic_param)); +- pic_param->picture_width = frame_header.coded_width; +- pic_param->picture_height = frame_header.coded_height; +- pic_param->num_components = frame_header.num_components; +- +- for (int i = 0; i < pic_param->num_components; i++) { +- pic_param->components[i].component_id = frame_header.components[i].id; +- pic_param->components[i].h_sampling_factor = +- frame_header.components[i].horizontal_sampling_factor; +- pic_param->components[i].v_sampling_factor = +- frame_header.components[i].vertical_sampling_factor; +- pic_param->components[i].quantiser_table_selector = +- frame_header.components[i].quantization_table_selector; +- } +-} +- +-static void FillIQMatrix(const JpegQuantizationTable* q_table, +- VAIQMatrixBufferJPEGBaseline* iq_matrix) { +- memset(iq_matrix, 0, sizeof(*iq_matrix)); +- static_assert(kJpegMaxQuantizationTableNum == +- arraysize(iq_matrix->load_quantiser_table), +- "max number of quantization table mismatched"); +- for (size_t i = 0; i < kJpegMaxQuantizationTableNum; i++) { +- if (!q_table[i].valid) +- continue; +- iq_matrix->load_quantiser_table[i] = 1; +- static_assert( +- arraysize(iq_matrix->quantiser_table[i]) == arraysize(q_table[i].value), +- "number of quantization entries mismatched"); +- for (size_t j = 0; j < arraysize(q_table[i].value); j++) +- iq_matrix->quantiser_table[i][j] = q_table[i].value[j]; +- } +-} +- +-static void FillHuffmanTable(const JpegHuffmanTable* dc_table, +- const JpegHuffmanTable* ac_table, +- VAHuffmanTableBufferJPEGBaseline* huffman_table) { +- memset(huffman_table, 0, sizeof(*huffman_table)); +- // Use default huffman tables if not specified in header. +- bool has_huffman_table = false; +- for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { +- if (dc_table[i].valid || ac_table[i].valid) { +- has_huffman_table = true; +- break; +- } +- } +- if (!has_huffman_table) { +- dc_table = kDefaultDcTable; +- ac_table = kDefaultAcTable; +- } +- +- static_assert(kJpegMaxHuffmanTableNumBaseline == +- arraysize(huffman_table->load_huffman_table), +- "max number of huffman table mismatched"); +- static_assert(sizeof(huffman_table->huffman_table[0].num_dc_codes) == +- sizeof(dc_table[0].code_length), +- "size of huffman table code length mismatch"); +- static_assert(sizeof(huffman_table->huffman_table[0].dc_values[0]) == +- sizeof(dc_table[0].code_value[0]), +- "size of huffman table code value mismatch"); +- for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { +- if (!dc_table[i].valid || !ac_table[i].valid) +- continue; +- huffman_table->load_huffman_table[i] = 1; +- +- memcpy(huffman_table->huffman_table[i].num_dc_codes, +- dc_table[i].code_length, +- sizeof(huffman_table->huffman_table[i].num_dc_codes)); +- memcpy(huffman_table->huffman_table[i].dc_values, dc_table[i].code_value, +- sizeof(huffman_table->huffman_table[i].dc_values)); +- memcpy(huffman_table->huffman_table[i].num_ac_codes, +- ac_table[i].code_length, +- sizeof(huffman_table->huffman_table[i].num_ac_codes)); +- memcpy(huffman_table->huffman_table[i].ac_values, ac_table[i].code_value, +- sizeof(huffman_table->huffman_table[i].ac_values)); +- } +-} +- +-static void FillSliceParameters( +- const JpegParseResult& parse_result, +- VASliceParameterBufferJPEGBaseline* slice_param) { +- memset(slice_param, 0, sizeof(*slice_param)); +- slice_param->slice_data_size = parse_result.data_size; +- slice_param->slice_data_offset = 0; +- slice_param->slice_data_flag = VA_SLICE_DATA_FLAG_ALL; +- slice_param->slice_horizontal_position = 0; +- slice_param->slice_vertical_position = 0; +- slice_param->num_components = parse_result.scan.num_components; +- for (int i = 0; i < slice_param->num_components; i++) { +- slice_param->components[i].component_selector = +- parse_result.scan.components[i].component_selector; +- slice_param->components[i].dc_table_selector = +- parse_result.scan.components[i].dc_selector; +- slice_param->components[i].ac_table_selector = +- parse_result.scan.components[i].ac_selector; +- } +- slice_param->restart_interval = parse_result.restart_interval; +- +- // Cast to int to prevent overflow. +- int max_h_factor = +- parse_result.frame_header.components[0].horizontal_sampling_factor; +- int max_v_factor = +- parse_result.frame_header.components[0].vertical_sampling_factor; +- int mcu_cols = parse_result.frame_header.coded_width / (max_h_factor * 8); +- DCHECK_GT(mcu_cols, 0); +- int mcu_rows = parse_result.frame_header.coded_height / (max_v_factor * 8); +- DCHECK_GT(mcu_rows, 0); +- slice_param->num_mcus = mcu_rows * mcu_cols; +-} +- +-// static +-bool VaapiJpegDecoder::Decode(VaapiWrapper* vaapi_wrapper, +- const JpegParseResult& parse_result, +- VASurfaceID va_surface) { +- DCHECK_NE(va_surface, VA_INVALID_SURFACE); +- if (!IsVaapiSupportedJpeg(parse_result)) +- return false; +- +- // Set picture parameters. +- VAPictureParameterBufferJPEGBaseline pic_param; +- FillPictureParameters(parse_result.frame_header, &pic_param); +- if (!vaapi_wrapper->SubmitBuffer(VAPictureParameterBufferType, +- sizeof(pic_param), &pic_param)) +- return false; +- +- // Set quantization table. +- VAIQMatrixBufferJPEGBaseline iq_matrix; +- FillIQMatrix(parse_result.q_table, &iq_matrix); +- if (!vaapi_wrapper->SubmitBuffer(VAIQMatrixBufferType, sizeof(iq_matrix), +- &iq_matrix)) +- return false; +- +- // Set huffman table. +- VAHuffmanTableBufferJPEGBaseline huffman_table; +- FillHuffmanTable(parse_result.dc_table, parse_result.ac_table, +- &huffman_table); +- if (!vaapi_wrapper->SubmitBuffer(VAHuffmanTableBufferType, +- sizeof(huffman_table), &huffman_table)) +- return false; +- +- // Set slice parameters. +- VASliceParameterBufferJPEGBaseline slice_param; +- FillSliceParameters(parse_result, &slice_param); +- if (!vaapi_wrapper->SubmitBuffer(VASliceParameterBufferType, +- sizeof(slice_param), &slice_param)) +- return false; +- +- // Set scan data. +- if (!vaapi_wrapper->SubmitBuffer(VASliceDataBufferType, +- parse_result.data_size, +- const_cast(parse_result.data))) +- return false; +- +- if (!vaapi_wrapper->ExecuteAndDestroyPendingBuffers(va_surface)) +- return false; +- +- return true; +-} +- +-} // namespace media +--- a/media/gpu/vaapi_jpeg_decoder.h ++++ /dev/null +@@ -1,43 +0,0 @@ +-// Copyright 2015 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#ifndef MEDIA_GPU_VAAPI_JPEG_DECODER_H_ +-#define MEDIA_GPU_VAAPI_JPEG_DECODER_H_ +- +-#include "base/macros.h" +-#include "media/gpu/media_gpu_export.h" +-#include "media/gpu/vaapi_wrapper.h" +- +-namespace media { +- +-struct JpegParseResult; +- +-// A JPEG decoder that utilizes VA-API hardware video decode acceleration on +-// Intel systems. Provides functionality to allow plugging VAAPI HW +-// acceleration into the JpegDecodeAccelerator framework. +-// +-// Clients of this class are expected to manage VA surfaces created via +-// VaapiWrapper, parse JPEG picture via ParseJpegPicture, and then pass +-// them to this class. +-class MEDIA_GPU_EXPORT VaapiJpegDecoder { +- public: +- // Decode a JPEG picture. It will fill VA-API parameters and call +- // corresponding VA-API methods according to parsed JPEG result +- // |parse_result|. Decoded data will be outputted to the given |va_surface|. +- // Return false on failure. +- // |vaapi_wrapper| should be initialized in kDecode mode with +- // VAProfileJPEGBaseline profile. +- // |va_surface| should be created with size at least as large as the picture +- // size. +- static bool Decode(VaapiWrapper* vaapi_wrapper, +- const JpegParseResult& parse_result, +- VASurfaceID va_surface); +- +- private: +- DISALLOW_IMPLICIT_CONSTRUCTORS(VaapiJpegDecoder); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VAAPI_JPEG_DECODER_H_ +--- a/media/gpu/vaapi_jpeg_decoder_unittest.cc ++++ /dev/null +@@ -1,139 +0,0 @@ +-// Copyright 2015 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include +-#include +- +-#include +- +-// This has to be included first. +-// See http://code.google.com/p/googletest/issues/detail?id=371 +-#include "testing/gtest/include/gtest/gtest.h" +- +-#include "base/at_exit.h" +-#include "base/bind.h" +-#include "base/files/file_util.h" +-#include "base/logging.h" +-#include "base/md5.h" +-#include "base/path_service.h" +-#include "base/strings/string_piece.h" +-#include "media/base/test_data_util.h" +-#include "media/base/video_frame.h" +-#include "media/filters/jpeg_parser.h" +-#include "media/gpu/vaapi_jpeg_decoder.h" +- +-namespace media { +-namespace { +- +-const char* kTestFilename = "pixel-1280x720.jpg"; +-const char* kExpectedMd5Sum = "6e9e1716073c9a9a1282e3f0e0dab743"; +- +-void LogOnError() { +- LOG(FATAL) << "Oh noes! Decoder failed"; +-} +- +-class VaapiJpegDecoderTest : public ::testing::Test { +- protected: +- VaapiJpegDecoderTest() {} +- +- void SetUp() override { +- base::Closure report_error_cb = base::Bind(&LogOnError); +- wrapper_ = VaapiWrapper::Create(VaapiWrapper::kDecode, +- VAProfileJPEGBaseline, report_error_cb); +- ASSERT_TRUE(wrapper_); +- +- base::FilePath input_file = GetTestDataFilePath(kTestFilename); +- +- ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data_)) +- << "failed to read input data from " << input_file.value(); +- } +- +- void TearDown() override { wrapper_ = nullptr; } +- +- bool VerifyDecode(const JpegParseResult& parse_result, +- const std::string& md5sum); +- +- protected: +- scoped_refptr wrapper_; +- std::string jpeg_data_; +-}; +- +-bool VaapiJpegDecoderTest::VerifyDecode(const JpegParseResult& parse_result, +- const std::string& expected_md5sum) { +- gfx::Size size(parse_result.frame_header.coded_width, +- parse_result.frame_header.coded_height); +- +- std::vector va_surfaces; +- if (!wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)) +- return false; +- +- if (!VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])) { +- LOG(ERROR) << "Decode failed"; +- return false; +- } +- +- VAImage image; +- VAImageFormat format; +- const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); +- memset(&image, 0, sizeof(image)); +- memset(&format, 0, sizeof(format)); +- format.fourcc = kI420Fourcc; +- format.byte_order = VA_LSB_FIRST; +- format.bits_per_pixel = 12; // 12 for I420 +- +- void* mem; +- if (!wrapper_->GetVaImage(va_surfaces[0], &format, size, &image, &mem)) { +- LOG(ERROR) << "Cannot get VAImage"; +- return false; +- } +- EXPECT_EQ(kI420Fourcc, image.format.fourcc); +- +- base::StringPiece result(reinterpret_cast(mem), +- VideoFrame::AllocationSize(PIXEL_FORMAT_I420, size)); +- EXPECT_EQ(expected_md5sum, base::MD5String(result)); +- +- wrapper_->ReturnVaImage(&image); +- +- return true; +-} +- +-TEST_F(VaapiJpegDecoderTest, DecodeSuccess) { +- JpegParseResult parse_result; +- ASSERT_TRUE( +- ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), +- jpeg_data_.size(), &parse_result)); +- +- EXPECT_TRUE(VerifyDecode(parse_result, kExpectedMd5Sum)); +-} +- +-TEST_F(VaapiJpegDecoderTest, DecodeFail) { +- JpegParseResult parse_result; +- ASSERT_TRUE( +- ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), +- jpeg_data_.size(), &parse_result)); +- +- // Not supported by VAAPI. +- parse_result.frame_header.num_components = 1; +- parse_result.scan.num_components = 1; +- +- gfx::Size size(parse_result.frame_header.coded_width, +- parse_result.frame_header.coded_height); +- +- std::vector va_surfaces; +- ASSERT_TRUE( +- wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)); +- +- EXPECT_FALSE( +- VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])); +-} +- +-} // namespace +-} // namespace media +- +-int main(int argc, char** argv) { +- testing::InitGoogleTest(&argc, argv); +- base::AtExitManager exit_manager; +- media::VaapiWrapper::PreSandboxInitialization(); +- return RUN_ALL_TESTS(); +-} +--- a/media/gpu/vaapi_jpeg_encode_accelerator.cc ++++ /dev/null +@@ -1,268 +0,0 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_jpeg_encode_accelerator.h" +- +-#include +- +-#include +-#include +- +-#include "base/bind.h" +-#include "base/logging.h" +-#include "base/metrics/histogram_macros.h" +-#include "base/sequence_checker.h" +-#include "base/task_scheduler/post_task.h" +-#include "base/threading/thread_task_runner_handle.h" +-#include "base/trace_event/trace_event.h" +-#include "media/base/bind_to_current_loop.h" +-#include "media/base/video_frame.h" +-#include "media/gpu/vaapi_jpeg_encoder.h" +- +-namespace media { +- +-namespace { +- +-// UMA results that the VaapiJpegEncodeAccelerator class reports. +-// These values are persisted to logs, and should therefore never be renumbered +-// nor reused. +-enum VAJEAEncoderResult { +- VAAPI_SUCCESS = 0, +- VAAPI_ERROR, +- VAJEA_ENCODER_RESULT_MAX = VAAPI_ERROR, +-}; +- +-static void ReportToUMA(VAJEAEncoderResult result) { +- UMA_HISTOGRAM_ENUMERATION("Media.VAJEA.EncoderResult", result, +- VAJEAEncoderResult::VAJEA_ENCODER_RESULT_MAX + 1); +-} +-} // namespace +- +-VaapiJpegEncodeAccelerator::EncodeRequest::EncodeRequest( +- scoped_refptr video_frame, +- std::unique_ptr shm, +- int quality) +- : video_frame(std::move(video_frame)), +- shm(std::move(shm)), +- quality(quality) {} +- +-VaapiJpegEncodeAccelerator::EncodeRequest::~EncodeRequest() {} +- +-class VaapiJpegEncodeAccelerator::Encoder { +- public: +- Encoder(scoped_refptr vaapi_wrapper, +- base::RepeatingCallback video_frame_ready_cb, +- base::RepeatingCallback notify_error_cb); +- ~Encoder(); +- +- // Processes one encode |request|. +- void EncodeTask(std::unique_ptr request); +- +- private: +- // |cached_output_buffer_id_| is the last allocated VABuffer during +- // EncodeTask() and |cached_output_buffer_size_| is the size of it. +- // If the next call to EncodeTask() does not require a buffer bigger than +- // |cached_output_buffer_size_|, |cached_output_buffer_id_| will be reused. +- size_t cached_output_buffer_size_; +- VABufferID cached_output_buffer_id_; +- +- std::unique_ptr jpeg_encoder_; +- scoped_refptr vaapi_wrapper_; +- +- base::RepeatingCallback video_frame_ready_cb_; +- base::RepeatingCallback notify_error_cb_; +- +- SEQUENCE_CHECKER(sequence_checker_); +- +- DISALLOW_COPY_AND_ASSIGN(Encoder); +-}; +- +-VaapiJpegEncodeAccelerator::Encoder::Encoder( +- scoped_refptr vaapi_wrapper, +- base::RepeatingCallback video_frame_ready_cb, +- base::RepeatingCallback notify_error_cb) +- : cached_output_buffer_size_(0), +- jpeg_encoder_(new VaapiJpegEncoder(vaapi_wrapper)), +- vaapi_wrapper_(std::move(vaapi_wrapper)), +- video_frame_ready_cb_(std::move(video_frame_ready_cb)), +- notify_error_cb_(std::move(notify_error_cb)) { +- DETACH_FROM_SEQUENCE(sequence_checker_); +-} +- +-VaapiJpegEncodeAccelerator::Encoder::~Encoder() { +- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +-} +- +-void VaapiJpegEncodeAccelerator::Encoder::EncodeTask( +- std::unique_ptr request) { +- TRACE_EVENT0("jpeg", "EncodeTask"); +- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +- +- const int video_frame_id = request->video_frame->unique_id(); +- gfx::Size input_size = request->video_frame->coded_size(); +- std::vector va_surfaces; +- if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, input_size, 1, +- &va_surfaces)) { +- VLOG(1) << "Failed to create VA surface"; +- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); +- return; +- } +- VASurfaceID va_surface_id = va_surfaces[0]; +- +- if (!vaapi_wrapper_->UploadVideoFrameToSurface(request->video_frame, +- va_surface_id)) { +- VLOG(1) << "Failed to upload video frame to VA surface"; +- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); +- return; +- } +- +- // Create output buffer for encoding result. +- size_t max_coded_buffer_size = +- VaapiJpegEncoder::GetMaxCodedBufferSize(input_size); +- if (max_coded_buffer_size > cached_output_buffer_size_) { +- vaapi_wrapper_->DestroyCodedBuffers(); +- cached_output_buffer_size_ = 0; +- +- VABufferID output_buffer_id; +- if (!vaapi_wrapper_->CreateCodedBuffer(max_coded_buffer_size, +- &output_buffer_id)) { +- VLOG(1) << "Failed to create VA buffer for encoding output"; +- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); +- return; +- } +- cached_output_buffer_size_ = max_coded_buffer_size; +- cached_output_buffer_id_ = output_buffer_id; +- } +- +- if (!jpeg_encoder_->Encode(input_size, request->quality, va_surface_id, +- cached_output_buffer_id_)) { +- VLOG(1) << "Encode JPEG failed"; +- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); +- return; +- } +- +- // Get the encoded output. DownloadFromCodedBuffer() is a blocking call. It +- // would wait until encoding is finished. +- size_t encoded_size = 0; +- if (!vaapi_wrapper_->DownloadFromCodedBuffer( +- cached_output_buffer_id_, va_surface_id, +- static_cast(request->shm->memory()), request->shm->size(), +- &encoded_size)) { +- VLOG(1) << "Failed to retrieve output image from VA coded buffer"; +- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); +- } +- +- video_frame_ready_cb_.Run(request->video_frame->unique_id(), encoded_size); +-} +- +-VaapiJpegEncodeAccelerator::VaapiJpegEncodeAccelerator( +- scoped_refptr io_task_runner) +- : task_runner_(base::ThreadTaskRunnerHandle::Get()), +- io_task_runner_(std::move(io_task_runner)), +- weak_this_factory_(this) { +- weak_this_ = weak_this_factory_.GetWeakPtr(); +-} +- +-VaapiJpegEncodeAccelerator::~VaapiJpegEncodeAccelerator() { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- DVLOG(1) << "Destroying VaapiJpegEncodeAccelerator"; +- +- weak_this_factory_.InvalidateWeakPtrs(); +- encoder_task_runner_->DeleteSoon(FROM_HERE, std::move(encoder_)); +-} +- +-void VaapiJpegEncodeAccelerator::NotifyError(int video_frame_id, +- Status status) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- DLOG(ERROR) << "Notifying error: " << status; +- DCHECK(client_); +- client_->NotifyError(video_frame_id, status); +-} +- +-void VaapiJpegEncodeAccelerator::VideoFrameReady(int video_frame_id, +- size_t encoded_picture_size) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- ReportToUMA(VAJEAEncoderResult::VAAPI_SUCCESS); +- +- client_->VideoFrameReady(video_frame_id, encoded_picture_size); +-} +- +-JpegEncodeAccelerator::Status VaapiJpegEncodeAccelerator::Initialize( +- JpegEncodeAccelerator::Client* client) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- if (!VaapiWrapper::IsJpegEncodeSupported()) { +- return HW_JPEG_ENCODE_NOT_SUPPORTED; +- } +- +- client_ = client; +- scoped_refptr vaapi_wrapper = VaapiWrapper::Create( +- VaapiWrapper::kEncode, VAProfileJPEGBaseline, +- base::Bind(&ReportToUMA, VAJEAEncoderResult::VAAPI_ERROR)); +- +- if (!vaapi_wrapper) { +- VLOG(1) << "Failed initializing VAAPI"; +- return PLATFORM_FAILURE; +- } +- +- encoder_task_runner_ = base::CreateSingleThreadTaskRunnerWithTraits( +- {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); +- if (!encoder_task_runner_) { +- VLOG(1) << "Failed to create encoder task runner."; +- return THREAD_CREATION_FAILED; +- } +- +- encoder_ = std::make_unique( +- std::move(vaapi_wrapper), +- BindToCurrentLoop(base::BindRepeating( +- &VaapiJpegEncodeAccelerator::VideoFrameReady, weak_this_)), +- BindToCurrentLoop(base::BindRepeating( +- &VaapiJpegEncodeAccelerator::NotifyError, weak_this_))); +- +- return ENCODE_OK; +-} +- +-size_t VaapiJpegEncodeAccelerator::GetMaxCodedBufferSize( +- const gfx::Size& picture_size) { +- return VaapiJpegEncoder::GetMaxCodedBufferSize(picture_size); +-} +- +-void VaapiJpegEncodeAccelerator::Encode( +- scoped_refptr video_frame, +- int quality, +- const BitstreamBuffer& bitstream_buffer) { +- DCHECK(io_task_runner_->BelongsToCurrentThread()); +- +- int video_frame_id = video_frame->unique_id(); +- TRACE_EVENT1("jpeg", "Encode", "input_id", video_frame_id); +- +- // TODO(shenghao): support other YUV formats. +- if (video_frame->format() != VideoPixelFormat::PIXEL_FORMAT_I420) { +- VLOG(1) << "Unsupported input format: " << video_frame->format(); +- task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, +- weak_this_, video_frame_id, INVALID_ARGUMENT)); +- return; +- } +- +- // SharedMemoryRegion will take ownership of the |bitstream_buffer.handle()|. +- auto shm = std::make_unique(bitstream_buffer, false); +- if (!shm->Map()) { +- VLOG(1) << "Failed to map output buffer"; +- task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, weak_this_, +- video_frame_id, INACCESSIBLE_OUTPUT_BUFFER)); +- return; +- } +- +- auto request = std::make_unique(std::move(video_frame), +- std::move(shm), quality); +- encoder_task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiJpegEncodeAccelerator::Encoder::EncodeTask, +- base::Unretained(encoder_.get()), base::Passed(&request))); +-} +- +-} // namespace media +--- a/media/gpu/vaapi_jpeg_encode_accelerator.h ++++ /dev/null +@@ -1,96 +0,0 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#ifndef MEDIA_GPU_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ +-#define MEDIA_GPU_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ +- +-#include +- +-#include "base/macros.h" +-#include "base/memory/weak_ptr.h" +-#include "base/single_thread_task_runner.h" +-#include "media/base/bitstream_buffer.h" +-#include "media/gpu/media_gpu_export.h" +-#include "media/gpu/shared_memory_region.h" +-#include "media/gpu/vaapi_wrapper.h" +-#include "media/video/jpeg_encode_accelerator.h" +- +-namespace media { +- +-// Class to provide JPEG encode acceleration for Intel systems with hardware +-// support for it, and on which libva is available. +-// Encoding tasks are performed in a separate encoding thread. +-// +-// Threading/life-cycle: this object is created & destroyed on the GPU +-// ChildThread. Methods in nested class Encoder are called on the encoder +-// thread which is stopped during destructor, so the callbacks bound with +-// a weak this can be run on the encoder thread because it can assume +-// VaapiJpegEncodeAccelerator is still alive. +-class MEDIA_GPU_EXPORT VaapiJpegEncodeAccelerator +- : public JpegEncodeAccelerator { +- public: +- explicit VaapiJpegEncodeAccelerator( +- scoped_refptr io_task_runner); +- ~VaapiJpegEncodeAccelerator() override; +- +- // JpegEncodeAccelerator implementation. +- Status Initialize(JpegEncodeAccelerator::Client* client) override; +- size_t GetMaxCodedBufferSize(const gfx::Size& picture_size) override; +- +- // Currently only I420 format is supported for |video_frame|. +- void Encode(scoped_refptr video_frame, +- int quality, +- const BitstreamBuffer& bitstream_buffer) override; +- +- private: +- // An input video frame and the corresponding output buffer awaiting +- // consumption, provided by the client. +- struct EncodeRequest { +- EncodeRequest(scoped_refptr video_frame, +- std::unique_ptr shm, +- int quality); +- ~EncodeRequest(); +- +- scoped_refptr video_frame; +- std::unique_ptr shm; +- int quality; +- +- DISALLOW_COPY_AND_ASSIGN(EncodeRequest); +- }; +- +- // The Encoder class is a collection of methods that run on +- // |encoder_task_runner_|. +- class Encoder; +- +- // Notifies the client that an error has occurred and encoding cannot +- // continue. +- void NotifyError(int video_frame_id, Status status); +- +- void VideoFrameReady(int video_frame_id, size_t encoded_picture_size); +- +- // ChildThread's task runner. +- scoped_refptr task_runner_; +- +- // GPU IO task runner. +- scoped_refptr io_task_runner_; +- +- // The client of this class. +- Client* client_; +- +- // Use this to post tasks to encoder thread. +- scoped_refptr encoder_task_runner_; +- +- std::unique_ptr encoder_; +- +- // |weak_this_| is used to post tasks from |encoder_task_runner_| to +- // |task_runner_|. +- base::WeakPtr weak_this_; +- base::WeakPtrFactory weak_this_factory_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncodeAccelerator); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ +--- a/media/gpu/vaapi_jpeg_encoder.cc ++++ /dev/null +@@ -1,427 +0,0 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_jpeg_encoder.h" +- +-#include +-#include +-#include +- +-#include "base/logging.h" +-#include "base/macros.h" +-#include "base/numerics/safe_conversions.h" +-#include "media/filters/jpeg_parser.h" +-#include "media/gpu/vaapi_wrapper.h" +- +-#define ARRAY_MEMCPY_CHECKED(to, from) \ +- do { \ +- static_assert(sizeof(to) == sizeof(from), \ +- #from " and " #to " arrays must be of same size"); \ +- memcpy(to, from, sizeof(to)); \ +- } while (0) +- +-namespace media { +- +-namespace { +- +-// JPEG header only uses 2 bytes to represent width and height. +-const int kMaxDimension = 65535; +-const size_t kDctSize2 = 64; +-const size_t kNumDcRunSizeBits = 16; +-const size_t kNumAcRunSizeBits = 16; +-const size_t kNumDcCodeWordsHuffVal = 12; +-const size_t kNumAcCodeWordsHuffVal = 162; +-const size_t kJpegHeaderSize = 83 + (kDctSize2 * 2) + (kNumDcRunSizeBits * 2) + +- (kNumDcCodeWordsHuffVal * 2) + +- (kNumAcRunSizeBits * 2) + +- (kNumAcCodeWordsHuffVal * 2); +- +-const uint8_t kZigZag8x8[64] = { +- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, +- 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, +- 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, +- 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; +- +-const JpegQuantizationTable kDefaultQuantTable[2] = { +- // Table K.1 Luminance quantization table values. +- { +- true, +- {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, +- 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, +- 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, +- 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}, +- }, +- // Table K.2 Chrominance quantization table values. +- { +- true, +- {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, +- 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, +- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, +- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, +- }, +-}; +- +-using JPEGHeader = uint8_t[kJpegHeaderSize]; +- +-void FillPictureParameters(const gfx::Size& input_size, +- int quality, +- VABufferID output_buffer_id, +- VAEncPictureParameterBufferJPEG* pic_param) { +- pic_param->picture_width = input_size.width(); +- pic_param->picture_height = input_size.height(); +- pic_param->num_components = 3; +- +- // Output buffer. +- pic_param->coded_buf = output_buffer_id; +- pic_param->quality = quality; +- // Profile = Baseline. +- pic_param->pic_flags.bits.profile = 0; +- // Sequential encoding. +- pic_param->pic_flags.bits.progressive = 0; +- // Uses Huffman coding. +- pic_param->pic_flags.bits.huffman = 1; +- // Input format is interleaved (YUV). +- pic_param->pic_flags.bits.interleaved = 0; +- // Non-differential Encoding. +- pic_param->pic_flags.bits.differential = 0; +- // Only 8 bit sample depth is currently supported. +- pic_param->sample_bit_depth = 8; +- pic_param->num_scan = 1; +-} +- +-void FillQMatrix(VAQMatrixBufferJPEG* q_matrix) { +- // Fill the raw, unscaled quantization tables for libva. The VAAPI driver is +- // responsible for scaling the quantization tables based on picture +- // parameter quality. +- const JpegQuantizationTable& luminance = kDefaultQuantTable[0]; +- static_assert( +- arraysize(luminance.value) == arraysize(q_matrix->lum_quantiser_matrix), +- "Luminance quantization table size mismatch."); +- static_assert(arraysize(kZigZag8x8) == arraysize(luminance.value), +- "Luminance quantization table size mismatch."); +- q_matrix->load_lum_quantiser_matrix = 1; +- for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { +- q_matrix->lum_quantiser_matrix[i] = luminance.value[kZigZag8x8[i]]; +- } +- +- const JpegQuantizationTable& chrominance = kDefaultQuantTable[1]; +- static_assert(arraysize(chrominance.value) == +- arraysize(q_matrix->chroma_quantiser_matrix), +- "Chrominance quantization table size mismatch."); +- static_assert(arraysize(kZigZag8x8) == arraysize(chrominance.value), +- "Chrominance quantization table size mismatch."); +- q_matrix->load_chroma_quantiser_matrix = 1; +- for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { +- q_matrix->chroma_quantiser_matrix[i] = chrominance.value[kZigZag8x8[i]]; +- } +-} +- +-void FillHuffmanTableParameters( +- VAHuffmanTableBufferJPEGBaseline* huff_table_param) { +- static_assert(arraysize(kDefaultDcTable) == arraysize(kDefaultAcTable), +- "DC table and AC table size mismatch."); +- static_assert( +- arraysize(kDefaultDcTable) == arraysize(huff_table_param->huffman_table), +- "DC table and destination table size mismatch."); +- +- for (size_t i = 0; i < arraysize(kDefaultDcTable); ++i) { +- const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; +- const JpegHuffmanTable& acTable = kDefaultAcTable[i]; +- huff_table_param->load_huffman_table[i] = true; +- +- // Load DC Table. +- ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_dc_codes, +- dcTable.code_length); +- // |code_values| of JpegHuffmanTable needs to hold DC and AC code values +- // so it has different size than +- // |huff_table_param->huffman_table[i].dc_values|. Therefore we can't use +- // ARRAY_MEMCPY_CHECKED() here. +- static_assert(arraysize(huff_table_param->huffman_table[i].dc_values) <= +- arraysize(dcTable.code_value), +- "DC table code value array too small."); +- memcpy(huff_table_param->huffman_table[i].dc_values, &dcTable.code_value[0], +- sizeof(huff_table_param->huffman_table[i].dc_values)); +- +- // Load AC Table. +- ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_ac_codes, +- acTable.code_length); +- ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].ac_values, +- acTable.code_value); +- +- memset(huff_table_param->huffman_table[i].pad, 0, +- sizeof(huff_table_param->huffman_table[i].pad)); +- } +-} +- +-void FillSliceParameters(VAEncSliceParameterBufferJPEG* slice_param) { +- slice_param->restart_interval = 0; +- slice_param->num_components = 3; +- +- slice_param->components[0].component_selector = 1; +- slice_param->components[0].dc_table_selector = 0; +- slice_param->components[0].ac_table_selector = 0; +- +- slice_param->components[1].component_selector = 2; +- slice_param->components[1].dc_table_selector = 1; +- slice_param->components[1].ac_table_selector = 1; +- +- slice_param->components[2].component_selector = 3; +- slice_param->components[2].dc_table_selector = 1; +- slice_param->components[2].ac_table_selector = 1; +-} +- +-size_t FillJpegHeader(const gfx::Size& input_size, +- int quality, +- JPEGHeader& header) { +- unsigned int width = input_size.width(); +- unsigned int height = input_size.height(); +- +- size_t idx = 0; +- +- // Start Of Input. +- static const uint8_t kSOI[] = {0xFF, JPEG_SOI}; +- memcpy(header, kSOI, sizeof(kSOI)); +- idx += sizeof(kSOI); +- +- // Application Segment - JFIF standard 1.01. +- // TODO(shenghao): Use Exif (JPEG_APP1) instead. +- static const uint8_t kAppSegment[] = { +- 0xFF, JPEG_APP0, 0x00, +- 0x10, // Segment length:16 (2-byte). +- 0x4A, // J +- 0x46, // F +- 0x49, // I +- 0x46, // F +- 0x00, // 0 +- 0x01, // Major version. +- 0x01, // Minor version. +- 0x01, // Density units 0:no units, 1:pixels per inch, +- // 2: pixels per cm. +- 0x00, +- 0x48, // X density (2-byte). +- 0x00, +- 0x48, // Y density (2-byte). +- 0x00, // Thumbnail width. +- 0x00 // Thumbnail height. +- }; +- memcpy(header + idx, kAppSegment, sizeof(kAppSegment)); +- idx += sizeof(kAppSegment); +- +- if (quality <= 0) { +- quality = 1; +- } +- +- // Normalize quality factor. +- // Unlike VAQMatrixBufferJPEG, we have to scale quantization table in JPEG +- // header by ourselves. +- uint32_t quality_normalized = base::saturated_cast( +- (quality < 50) ? (5000 / quality) : (200 - (quality * 2))); +- +- // Quantization Tables. +- for (size_t i = 0; i < 2; ++i) { +- const uint8_t kQuantSegment[] = { +- 0xFF, JPEG_DQT, 0x00, +- 0x03 + kDctSize2, // Segment length:67 (2-byte). +- static_cast(i) // Precision (4-bit high) = 0, +- // Index (4-bit low) = i. +- }; +- memcpy(header + idx, kQuantSegment, sizeof(kQuantSegment)); +- idx += sizeof(kQuantSegment); +- +- const JpegQuantizationTable& quant_table = kDefaultQuantTable[i]; +- for (size_t j = 0; j < kDctSize2; ++j) { +- uint32_t scaled_quant_value = +- (quant_table.value[kZigZag8x8[j]] * quality_normalized) / 100; +- scaled_quant_value = std::min(255u, std::max(1u, scaled_quant_value)); +- header[idx++] = static_cast(scaled_quant_value); +- } +- } +- +- // Start of Frame - Baseline. +- const uint8_t kStartOfFrame[] = { +- 0xFF, +- JPEG_SOF0, // Baseline. +- 0x00, +- 0x11, // Segment length:17 (2-byte). +- 8, // Data precision. +- static_cast((height >> 8) & 0xFF), +- static_cast(height & 0xFF), +- static_cast((width >> 8) & 0xFF), +- static_cast(width & 0xFF), +- 0x03, // Number of Components. +- }; +- memcpy(header + idx, kStartOfFrame, sizeof(kStartOfFrame)); +- idx += sizeof(kStartOfFrame); +- for (uint8_t i = 0; i < 3; ++i) { +- // These are the values for U and V planes. +- uint8_t h_sample_factor = 1; +- uint8_t v_sample_factor = 1; +- uint8_t quant_table_number = 1; +- if (!i) { +- // These are the values for Y plane. +- h_sample_factor = 2; +- v_sample_factor = 2; +- quant_table_number = 0; +- } +- +- header[idx++] = i + 1; +- // Horizontal Sample Factor (4-bit high), +- // Vertical Sample Factor (4-bit low). +- header[idx++] = (h_sample_factor << 4) | v_sample_factor; +- header[idx++] = quant_table_number; +- } +- +- static const uint8_t kDcSegment[] = { +- 0xFF, JPEG_DHT, 0x00, +- 0x1F, // Segment length:31 (2-byte). +- }; +- static const uint8_t kAcSegment[] = { +- 0xFF, JPEG_DHT, 0x00, +- 0xB5, // Segment length:181 (2-byte). +- }; +- +- // Huffman Tables. +- for (size_t i = 0; i < 2; ++i) { +- // DC Table. +- memcpy(header + idx, kDcSegment, sizeof(kDcSegment)); +- idx += sizeof(kDcSegment); +- +- // Type (4-bit high) = 0:DC, Index (4-bit low). +- header[idx++] = static_cast(i); +- +- const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; +- for (size_t j = 0; j < kNumDcRunSizeBits; ++j) +- header[idx++] = dcTable.code_length[j]; +- for (size_t j = 0; j < kNumDcCodeWordsHuffVal; ++j) +- header[idx++] = dcTable.code_value[j]; +- +- // AC Table. +- memcpy(header + idx, kAcSegment, sizeof(kAcSegment)); +- idx += sizeof(kAcSegment); +- +- // Type (4-bit high) = 1:AC, Index (4-bit low). +- header[idx++] = 0x10 | static_cast(i); +- +- const JpegHuffmanTable& acTable = kDefaultAcTable[i]; +- for (size_t j = 0; j < kNumAcRunSizeBits; ++j) +- header[idx++] = acTable.code_length[j]; +- for (size_t j = 0; j < kNumAcCodeWordsHuffVal; ++j) +- header[idx++] = acTable.code_value[j]; +- } +- +- // Start of Scan. +- static const uint8_t kStartOfScan[] = { +- 0xFF, JPEG_SOS, 0x00, +- 0x0C, // Segment Length:12 (2-byte). +- 0x03 // Number of components in scan. +- }; +- memcpy(header + idx, kStartOfScan, sizeof(kStartOfScan)); +- idx += sizeof(kStartOfScan); +- +- for (uint8_t i = 0; i < 3; ++i) { +- uint8_t dc_table_number = 1; +- uint8_t ac_table_number = 1; +- if (!i) { +- dc_table_number = 0; +- ac_table_number = 0; +- } +- +- header[idx++] = i + 1; +- // DC Table Selector (4-bit high), AC Table Selector (4-bit low). +- header[idx++] = (dc_table_number << 4) | ac_table_number; +- } +- header[idx++] = 0x00; // 0 for Baseline. +- header[idx++] = 0x3F; // 63 for Baseline. +- header[idx++] = 0x00; // 0 for Baseline. +- +- return idx << 3; +-} +- +-} // namespace +- +-VaapiJpegEncoder::VaapiJpegEncoder(scoped_refptr vaapi_wrapper) +- : vaapi_wrapper_(vaapi_wrapper), +- q_matrix_cached_(nullptr), +- huff_table_param_cached_(nullptr), +- slice_param_cached_(nullptr) {} +- +-VaapiJpegEncoder::~VaapiJpegEncoder() {} +- +-size_t VaapiJpegEncoder::GetMaxCodedBufferSize(const gfx::Size& size) { +- return size.GetArea() * 3 / 2 + kJpegHeaderSize; +-} +- +-bool VaapiJpegEncoder::Encode(const gfx::Size& input_size, +- int quality, +- VASurfaceID surface_id, +- VABufferID output_buffer_id) { +- DCHECK_NE(surface_id, VA_INVALID_SURFACE); +- +- if (input_size.width() > kMaxDimension || +- input_size.height() > kMaxDimension) { +- return false; +- } +- +- // Set picture parameters. +- VAEncPictureParameterBufferJPEG pic_param; +- FillPictureParameters(input_size, quality, output_buffer_id, &pic_param); +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, +- sizeof(pic_param), &pic_param)) { +- return false; +- } +- +- if (!q_matrix_cached_) { +- q_matrix_cached_.reset(new VAQMatrixBufferJPEG()); +- FillQMatrix(q_matrix_cached_.get()); +- } +- if (!vaapi_wrapper_->SubmitBuffer(VAQMatrixBufferType, +- sizeof(*q_matrix_cached_), +- q_matrix_cached_.get())) { +- return false; +- } +- +- if (!huff_table_param_cached_) { +- huff_table_param_cached_.reset(new VAHuffmanTableBufferJPEGBaseline()); +- FillHuffmanTableParameters(huff_table_param_cached_.get()); +- } +- if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType, +- sizeof(*huff_table_param_cached_), +- huff_table_param_cached_.get())) { +- return false; +- } +- +- // Set slice parameters. +- if (!slice_param_cached_) { +- slice_param_cached_.reset(new VAEncSliceParameterBufferJPEG()); +- FillSliceParameters(slice_param_cached_.get()); +- } +- if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, +- sizeof(*slice_param_cached_), +- slice_param_cached_.get())) { +- return false; +- } +- +- JPEGHeader header_data; +- size_t length_in_bits = FillJpegHeader(input_size, quality, header_data); +- +- VAEncPackedHeaderParameterBuffer header_param; +- memset(&header_param, 0, sizeof(header_param)); +- header_param.type = VAEncPackedHeaderRawData; +- header_param.bit_length = length_in_bits; +- header_param.has_emulation_bytes = 0; +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, +- sizeof(header_param), &header_param)) { +- return false; +- } +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, +- (length_in_bits + 7) / 8, header_data)) { +- return false; +- } +- +- // Submit the |surface_id| which contains input YUV frame and begin encoding. +- return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(surface_id); +-} +- +-} // namespace media +--- a/media/gpu/vaapi_jpeg_encoder.h ++++ /dev/null +@@ -1,65 +0,0 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#ifndef MEDIA_GPU_VAAPI_JPEG_ENCODER_H_ +-#define MEDIA_GPU_VAAPI_JPEG_ENCODER_H_ +- +-#include +-#include +- +-#include "base/macros.h" +-#include "base/memory/scoped_refptr.h" +-#include "media/gpu/media_gpu_export.h" +-#include "ui/gfx/geometry/size.h" +- +-namespace media { +- +-class VaapiWrapper; +- +-// A collection of methods that utilize VA-API hardware video encode +-// acceleration on Intel systems. Provides functionality to allow plugging VAAPI +-// HW acceleration into the JpegEncodeAccelerator framework. +-// +-// Clients are expected to manage VA surfaces and VA buffers created via +-// VaapiWrapper, and pass them to this class. +-class MEDIA_GPU_EXPORT VaapiJpegEncoder { +- public: +- // |vaapi_wrapper| should be initialized in VaapiWrapper::kEncode +- // mode with VAProfileJPEGBaseline profile. +- explicit VaapiJpegEncoder(scoped_refptr vaapi_wrapper); +- ~VaapiJpegEncoder(); +- +- // Encode a JPEG picture. It will fill VA-API parameters and call +- // corresponding VA-API methods according to |input_size|. +- // |quality| is the JPEG image quality +- // |surface_id| is the VA surface that contains input image. +- // |output_buffer_id| is the ID of VA buffer that encoded image will be +- // stored. The size of it should be at least as large as +- // GetMaxCodedBufferSize(). +- // Return false on failure. +- bool Encode(const gfx::Size& input_size, +- int quality, +- VASurfaceID surface_id, +- VABufferID output_buffer_id); +- +- // Gets the maximum possible encoded result size. +- // |size| is the dimension of the YUV image to be encoded. +- static size_t GetMaxCodedBufferSize(const gfx::Size& size); +- +- private: +- scoped_refptr vaapi_wrapper_; +- +- // |q_matrix_cached_|, |huff_table_param_cached_| and |slice_param_cached_| +- // are created when Encode() is called the first time. After that, they will +- // directly be used for all the subsequent Encode() calls. +- std::unique_ptr q_matrix_cached_; +- std::unique_ptr huff_table_param_cached_; +- std::unique_ptr slice_param_cached_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncoder); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VAAPI_JPEG_ENCODER_H_ +--- a/media/gpu/vaapi_video_decode_accelerator.cc ++++ /dev/null +@@ -1,1871 +0,0 @@ +-// Copyright (c) 2012 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_video_decode_accelerator.h" +- +-#include +- +-#include +- +-#include +- +-#include "base/bind.h" +-#include "base/files/scoped_file.h" +-#include "base/logging.h" +-#include "base/macros.h" +-#include "base/metrics/histogram_macros.h" +-#include "base/stl_util.h" +-#include "base/strings/string_util.h" +-#include "base/synchronization/waitable_event.h" +-#include "base/threading/thread_task_runner_handle.h" +-#include "base/trace_event/trace_event.h" +-#include "gpu/ipc/service/gpu_channel.h" +-#include "media/base/bind_to_current_loop.h" +-#include "media/gpu/accelerated_video_decoder.h" +-#include "media/gpu/format_utils.h" +-#include "media/gpu/h264_decoder.h" +-#include "media/gpu/vaapi/vaapi_picture.h" +-#include "media/gpu/vp8_decoder.h" +-#include "media/gpu/vp9_decoder.h" +-#include "media/video/picture.h" +-#include "ui/gl/gl_image.h" +- +-#define DVLOGF(level) DVLOG(level) << __func__ << "(): " +-#define VLOGF(level) VLOG(level) << __func__ << "(): " +- +-namespace media { +- +-namespace { +-// UMA errors that the VaapiVideoDecodeAccelerator class reports. +-enum VAVDADecoderFailure { +- VAAPI_ERROR = 0, +- VAVDA_DECODER_FAILURES_MAX, +-}; +-// from ITU-T REC H.264 spec +-// section 8.5.6 +-// "Inverse scanning process for 4x4 transform coefficients and scaling lists" +-static const int kZigzagScan4x4[16] = {0, 1, 4, 8, 5, 2, 3, 6, +- 9, 12, 13, 10, 7, 11, 14, 15}; +- +-// section 8.5.7 +-// "Inverse scanning process for 8x8 transform coefficients and scaling lists" +-static const uint8_t kZigzagScan8x8[64] = { +- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, +- 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, +- 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, +- 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; +- +-// Returns the preferred VA_RT_FORMAT for the given |profile|. +-unsigned int GetVaFormatForVideoCodecProfile(VideoCodecProfile profile) { +- if (profile == VP9PROFILE_PROFILE2 || profile == VP9PROFILE_PROFILE3) +- return VA_RT_FORMAT_YUV420_10BPP; +- return VA_RT_FORMAT_YUV420; +-} +- +-} // namespace +- +-static void ReportToUMA(VAVDADecoderFailure failure) { +- UMA_HISTOGRAM_ENUMERATION("Media.VAVDA.DecoderFailure", failure, +- VAVDA_DECODER_FAILURES_MAX + 1); +-} +- +-#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ +- do { \ +- if (!(result)) { \ +- VLOGF(1) << log; \ +- NotifyError(error_code); \ +- return ret; \ +- } \ +- } while (0) +- +-class VaapiVideoDecodeAccelerator::VaapiDecodeSurface +- : public base::RefCountedThreadSafe { +- public: +- VaapiDecodeSurface(int32_t bitstream_id, +- const scoped_refptr& va_surface); +- +- int32_t bitstream_id() const { return bitstream_id_; } +- scoped_refptr va_surface() { return va_surface_; } +- gfx::Rect visible_rect() const { return visible_rect_; } +- +- void set_visible_rect(const gfx::Rect& visible_rect) { +- visible_rect_ = visible_rect; +- } +- +- private: +- friend class base::RefCountedThreadSafe; +- ~VaapiDecodeSurface(); +- +- const int32_t bitstream_id_; +- const scoped_refptr va_surface_; +- gfx::Rect visible_rect_; +-}; +- +-VaapiVideoDecodeAccelerator::VaapiDecodeSurface::VaapiDecodeSurface( +- int32_t bitstream_id, +- const scoped_refptr& va_surface) +- : bitstream_id_(bitstream_id), va_surface_(va_surface) {} +- +-VaapiVideoDecodeAccelerator::VaapiDecodeSurface::~VaapiDecodeSurface() {} +- +-class VaapiH264Picture : public H264Picture { +- public: +- explicit VaapiH264Picture( +- scoped_refptr surface) +- : dec_surface_(surface) {} +- +- VaapiH264Picture* AsVaapiH264Picture() override { return this; } +- scoped_refptr dec_surface() { +- return dec_surface_; +- } +- +- private: +- ~VaapiH264Picture() override {} +- +- scoped_refptr dec_surface_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiH264Picture); +-}; +- +-class VaapiVideoDecodeAccelerator::VaapiH264Accelerator +- : public H264Decoder::H264Accelerator { +- public: +- VaapiH264Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, +- VaapiWrapper* vaapi_wrapper); +- ~VaapiH264Accelerator() override; +- +- // H264Decoder::H264Accelerator implementation. +- scoped_refptr CreateH264Picture() override; +- +- bool SubmitFrameMetadata(const H264SPS* sps, +- const H264PPS* pps, +- const H264DPB& dpb, +- const H264Picture::Vector& ref_pic_listp0, +- const H264Picture::Vector& ref_pic_listb0, +- const H264Picture::Vector& ref_pic_listb1, +- const scoped_refptr& pic) override; +- +- bool SubmitSlice(const H264PPS* pps, +- const H264SliceHeader* slice_hdr, +- const H264Picture::Vector& ref_pic_list0, +- const H264Picture::Vector& ref_pic_list1, +- const scoped_refptr& pic, +- const uint8_t* data, +- size_t size) override; +- +- bool SubmitDecode(const scoped_refptr& pic) override; +- bool OutputPicture(const scoped_refptr& pic) override; +- +- void Reset() override; +- +- private: +- scoped_refptr H264PictureToVaapiDecodeSurface( +- const scoped_refptr& pic); +- +- void FillVAPicture(VAPictureH264* va_pic, scoped_refptr pic); +- int FillVARefFramesFromDPB(const H264DPB& dpb, +- VAPictureH264* va_pics, +- int num_pics); +- +- VaapiWrapper* vaapi_wrapper_; +- VaapiVideoDecodeAccelerator* vaapi_dec_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiH264Accelerator); +-}; +- +-class VaapiVP8Picture : public VP8Picture { +- public: +- explicit VaapiVP8Picture( +- scoped_refptr surface) +- : dec_surface_(surface) {} +- +- VaapiVP8Picture* AsVaapiVP8Picture() override { return this; } +- scoped_refptr dec_surface() { +- return dec_surface_; +- } +- +- private: +- ~VaapiVP8Picture() override {} +- +- scoped_refptr dec_surface_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiVP8Picture); +-}; +- +-class VaapiVideoDecodeAccelerator::VaapiVP8Accelerator +- : public VP8Decoder::VP8Accelerator { +- public: +- VaapiVP8Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, +- VaapiWrapper* vaapi_wrapper); +- ~VaapiVP8Accelerator() override; +- +- // VP8Decoder::VP8Accelerator implementation. +- scoped_refptr CreateVP8Picture() override; +- +- bool SubmitDecode(const scoped_refptr& pic, +- const Vp8FrameHeader* frame_hdr, +- const scoped_refptr& last_frame, +- const scoped_refptr& golden_frame, +- const scoped_refptr& alt_frame) override; +- +- bool OutputPicture(const scoped_refptr& pic) override; +- +- private: +- scoped_refptr VP8PictureToVaapiDecodeSurface( +- const scoped_refptr& pic); +- +- VaapiWrapper* vaapi_wrapper_; +- VaapiVideoDecodeAccelerator* vaapi_dec_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiVP8Accelerator); +-}; +- +-class VaapiVP9Picture : public VP9Picture { +- public: +- explicit VaapiVP9Picture( +- scoped_refptr surface) +- : dec_surface_(surface) {} +- +- VaapiVP9Picture* AsVaapiVP9Picture() override { return this; } +- scoped_refptr dec_surface() { +- return dec_surface_; +- } +- +- private: +- ~VaapiVP9Picture() override {} +- +- scoped_refptr dec_surface_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiVP9Picture); +-}; +- +-class VaapiVideoDecodeAccelerator::VaapiVP9Accelerator +- : public VP9Decoder::VP9Accelerator { +- public: +- VaapiVP9Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, +- VaapiWrapper* vaapi_wrapper); +- ~VaapiVP9Accelerator() override; +- +- // VP9Decoder::VP9Accelerator implementation. +- scoped_refptr CreateVP9Picture() override; +- +- bool SubmitDecode(const scoped_refptr& pic, +- const Vp9SegmentationParams& seg, +- const Vp9LoopFilterParams& lf, +- const std::vector>& ref_pictures, +- const base::Closure& done_cb) override; +- +- bool OutputPicture(const scoped_refptr& pic) override; +- +- bool IsFrameContextRequired() const override { return false; } +- +- bool GetFrameContext(const scoped_refptr& pic, +- Vp9FrameContext* frame_ctx) override; +- +- private: +- scoped_refptr VP9PictureToVaapiDecodeSurface( +- const scoped_refptr& pic); +- +- VaapiWrapper* vaapi_wrapper_; +- VaapiVideoDecodeAccelerator* vaapi_dec_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiVP9Accelerator); +-}; +- +-class VaapiVideoDecodeAccelerator::InputBuffer { +- public: +- InputBuffer() = default; +- InputBuffer(uint32_t id, +- std::unique_ptr shm, +- base::OnceCallback release_cb) +- : id_(id), shm_(std::move(shm)), release_cb_(std::move(release_cb)) {} +- ~InputBuffer() { +- VLOGF(4) << "id = " << id_; +- if (release_cb_) +- std::move(release_cb_).Run(id_); +- } +- +- // Indicates this is a dummy buffer for flush request. +- bool IsFlushRequest() const { return shm_ == nullptr; } +- int32_t id() const { return id_; } +- SharedMemoryRegion* shm() const { return shm_.get(); } +- +- private: +- const int32_t id_ = -1; +- const std::unique_ptr shm_; +- base::OnceCallback release_cb_; +- +- DISALLOW_COPY_AND_ASSIGN(InputBuffer); +-}; +- +-void VaapiVideoDecodeAccelerator::NotifyError(Error error) { +- if (!task_runner_->BelongsToCurrentThread()) { +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- task_runner_->PostTask(FROM_HERE, +- base::Bind(&VaapiVideoDecodeAccelerator::NotifyError, +- weak_this_, error)); +- return; +- } +- +- // Post Cleanup() as a task so we don't recursively acquire lock_. +- task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::Cleanup, weak_this_)); +- +- VLOGF(1) << "Notifying of error " << error; +- if (client_) { +- client_->NotifyError(error); +- client_ptr_factory_.reset(); +- } +-} +- +-VaapiPicture* VaapiVideoDecodeAccelerator::PictureById( +- int32_t picture_buffer_id) { +- Pictures::iterator it = pictures_.find(picture_buffer_id); +- if (it == pictures_.end()) { +- VLOGF(4) << "Picture id " << picture_buffer_id << " does not exist"; +- return NULL; +- } +- +- return it->second.get(); +-} +- +-VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( +- const MakeGLContextCurrentCallback& make_context_current_cb, +- const BindGLImageCallback& bind_image_cb) +- : state_(kUninitialized), +- input_ready_(&lock_), +- vaapi_picture_factory_(new VaapiPictureFactory()), +- surfaces_available_(&lock_), +- task_runner_(base::ThreadTaskRunnerHandle::Get()), +- decoder_thread_("VaapiDecoderThread"), +- num_frames_at_client_(0), +- finish_flush_pending_(false), +- awaiting_va_surfaces_recycle_(false), +- requested_num_pics_(0), +- output_format_(gfx::BufferFormat::BGRX_8888), +- profile_(VIDEO_CODEC_PROFILE_UNKNOWN), +- make_context_current_cb_(make_context_current_cb), +- bind_image_cb_(bind_image_cb), +- weak_this_factory_(this) { +- weak_this_ = weak_this_factory_.GetWeakPtr(); +- va_surface_release_cb_ = BindToCurrentLoop( +- base::Bind(&VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_)); +-} +- +-VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { +- DCHECK(task_runner_->BelongsToCurrentThread()); +-} +- +-bool VaapiVideoDecodeAccelerator::Initialize(const Config& config, +- Client* client) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- if (config.is_encrypted()) { +- NOTREACHED() << "Encrypted streams are not supported for this VDA"; +- return false; +- } +- +- switch (config.output_mode) { +- case Config::OutputMode::ALLOCATE: +- output_format_ = vaapi_picture_factory_->GetBufferFormatForAllocateMode(); +- break; +- +- case Config::OutputMode::IMPORT: +- output_format_ = vaapi_picture_factory_->GetBufferFormatForImportMode(); +- break; +- +- default: +- NOTREACHED() << "Only ALLOCATE and IMPORT OutputModes are supported"; +- return false; +- } +- +- client_ptr_factory_.reset(new base::WeakPtrFactory(client)); +- client_ = client_ptr_factory_->GetWeakPtr(); +- +- VideoCodecProfile profile = config.profile; +- +- base::AutoLock auto_lock(lock_); +- DCHECK_EQ(state_, kUninitialized); +- VLOGF(2) << "Initializing VAVDA, profile: " << GetProfileName(profile); +- +- vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec( +- VaapiWrapper::kDecode, profile, base::Bind(&ReportToUMA, VAAPI_ERROR)); +- +- if (!vaapi_wrapper_.get()) { +- VLOGF(1) << "Failed initializing VAAPI for profile " +- << GetProfileName(profile); +- return false; +- } +- +- if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { +- h264_accelerator_.reset( +- new VaapiH264Accelerator(this, vaapi_wrapper_.get())); +- decoder_.reset(new H264Decoder(h264_accelerator_.get())); +- } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { +- vp8_accelerator_.reset(new VaapiVP8Accelerator(this, vaapi_wrapper_.get())); +- decoder_.reset(new VP8Decoder(vp8_accelerator_.get())); +- } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) { +- vp9_accelerator_.reset(new VaapiVP9Accelerator(this, vaapi_wrapper_.get())); +- decoder_.reset(new VP9Decoder(vp9_accelerator_.get())); +- } else { +- VLOGF(1) << "Unsupported profile " << GetProfileName(profile); +- return false; +- } +- profile_ = profile; +- +- CHECK(decoder_thread_.Start()); +- decoder_thread_task_runner_ = decoder_thread_.task_runner(); +- +- state_ = kIdle; +- output_mode_ = config.output_mode; +- return true; +-} +- +-void VaapiVideoDecodeAccelerator::OutputPicture( +- const scoped_refptr& va_surface, +- int32_t input_id, +- gfx::Rect visible_rect, +- VaapiPicture* picture) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- int32_t output_id = picture->picture_buffer_id(); +- +- VLOGF(4) << "Outputting VASurface " << va_surface->id() +- << " into pixmap bound to picture buffer id " << output_id; +- { +- TRACE_EVENT2("Video Decoder", "VAVDA::DownloadFromSurface", "input_id", +- input_id, "output_id", output_id); +- RETURN_AND_NOTIFY_ON_FAILURE(picture->DownloadFromSurface(va_surface), +- "Failed putting surface into pixmap", +- PLATFORM_FAILURE, ); +- } +- // Notify the client a picture is ready to be displayed. +- ++num_frames_at_client_; +- TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); +- VLOGF(4) << "Notifying output picture id " << output_id << " for input " +- << input_id +- << " is ready. visible rect: " << visible_rect.ToString(); +- if (client_) { +- // TODO(hubbe): Use the correct color space. http://crbug.com/647725 +- client_->PictureReady(Picture(output_id, input_id, visible_rect, +- gfx::ColorSpace(), picture->AllowOverlay())); +- } +-} +- +-void VaapiVideoDecodeAccelerator::TryOutputSurface() { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- // Handle Destroy() arriving while pictures are queued for output. +- if (!client_) +- return; +- +- if (pending_output_cbs_.empty() || output_buffers_.empty()) +- return; +- +- OutputCB output_cb = pending_output_cbs_.front(); +- pending_output_cbs_.pop(); +- +- VaapiPicture* picture = PictureById(output_buffers_.front()); +- DCHECK(picture); +- output_buffers_.pop(); +- +- output_cb.Run(picture); +- +- if (finish_flush_pending_ && pending_output_cbs_.empty()) +- FinishFlush(); +-} +- +-void VaapiVideoDecodeAccelerator::QueueInputBuffer( +- const BitstreamBuffer& bitstream_buffer) { +- VLOGF(4) << "Queueing new input buffer id: " << bitstream_buffer.id() +- << " size: " << (int)bitstream_buffer.size(); +- DCHECK(task_runner_->BelongsToCurrentThread()); +- TRACE_EVENT1("Video Decoder", "QueueInputBuffer", "input_id", +- bitstream_buffer.id()); +- +- base::AutoLock auto_lock(lock_); +- if (bitstream_buffer.size() == 0) { +- DCHECK(!base::SharedMemory::IsHandleValid(bitstream_buffer.handle())); +- // Dummy buffer for flush. +- auto flush_buffer = base::MakeUnique(); +- DCHECK(flush_buffer->IsFlushRequest()); +- input_buffers_.push(std::move(flush_buffer)); +- } else { +- std::unique_ptr shm( +- new SharedMemoryRegion(bitstream_buffer, true)); +- RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(), "Failed to map input buffer", +- UNREADABLE_INPUT, ); +- +- auto input_buffer = base::MakeUnique( +- bitstream_buffer.id(), std::move(shm), +- BindToCurrentLoop( +- base::Bind(&Client::NotifyEndOfBitstreamBuffer, client_))); +- input_buffers_.push(std::move(input_buffer)); +- +- TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); +- } +- +- input_ready_.Signal(); +- +- switch (state_) { +- case kIdle: +- state_ = kDecoding; +- decoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, +- base::Unretained(this))); +- break; +- +- case kDecoding: +- // Decoder already running. +- break; +- +- case kResetting: +- // When resetting, allow accumulating bitstream buffers, so that +- // the client can queue after-seek-buffers while we are finishing with +- // the before-seek one. +- break; +- +- default: +- VLOGF(1) << "Decode/Flush request from client in invalid state: " +- << state_; +- NotifyError(PLATFORM_FAILURE); +- break; +- } +-} +- +-bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- lock_.AssertAcquired(); +- +- if (curr_input_buffer_.get()) +- return true; +- +- // Will only wait if it is expected that in current state new buffers will +- // be queued from the client via Decode(). The state can change during wait. +- while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { +- input_ready_.Wait(); +- } +- +- // We could have got woken up in a different state or never got to sleep +- // due to current state. +- if (state_ != kDecoding && state_ != kIdle) +- return false; +- +- DCHECK(!input_buffers_.empty()); +- curr_input_buffer_ = std::move(input_buffers_.front()); +- input_buffers_.pop(); +- +- if (curr_input_buffer_->IsFlushRequest()) { +- VLOGF(4) << "New flush buffer"; +- return true; +- } +- +- VLOGF(4) << "New current input buffer, id: " << curr_input_buffer_->id() +- << " size: " << curr_input_buffer_->shm()->size() << "B"; +- decoder_->SetStream( +- static_cast(curr_input_buffer_->shm()->memory()), +- curr_input_buffer_->shm()->size()); +- +- return true; +-} +- +-void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- lock_.AssertAcquired(); +- DCHECK(curr_input_buffer_.get()); +- curr_input_buffer_.reset(); +- +- TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); +-} +- +-// TODO(posciak): refactor the whole class to remove sleeping in wait for +-// surfaces, and reschedule DecodeTask instead. +-bool VaapiVideoDecodeAccelerator::WaitForSurfaces_Locked() { +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- lock_.AssertAcquired(); +- +- while (available_va_surfaces_.empty() && +- (state_ == kDecoding || state_ == kIdle)) { +- surfaces_available_.Wait(); +- } +- +- return state_ == kDecoding || state_ == kIdle; +-} +- +-void VaapiVideoDecodeAccelerator::DecodeTask() { +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- base::AutoLock auto_lock(lock_); +- +- if (state_ != kDecoding) +- return; +- +- // Main decode task. +- VLOGF(4) << "Decode task"; +- +- // Try to decode what stream data is (still) in the decoder until we run out +- // of it. +- while (GetInputBuffer_Locked()) { +- DCHECK(curr_input_buffer_.get()); +- +- if (curr_input_buffer_->IsFlushRequest()) { +- FlushTask(); +- break; +- } +- +- AcceleratedVideoDecoder::DecodeResult res; +- { +- // We are OK releasing the lock here, as decoder never calls our methods +- // directly and we will reacquire the lock before looking at state again. +- // This is the main decode function of the decoder and while keeping +- // the lock for its duration would be fine, it would defeat the purpose +- // of having a separate decoder thread. +- base::AutoUnlock auto_unlock(lock_); +- TRACE_EVENT0("Video Decoder", "VAVDA::Decode"); +- res = decoder_->Decode(); +- } +- +- switch (res) { +- case AcceleratedVideoDecoder::kAllocateNewSurfaces: +- VLOGF(2) << "Decoder requesting a new set of surfaces"; +- task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, +- weak_this_, decoder_->GetRequiredNumOfPictures(), +- decoder_->GetPicSize())); +- // We'll get rescheduled once ProvidePictureBuffers() finishes. +- return; +- +- case AcceleratedVideoDecoder::kRanOutOfStreamData: +- ReturnCurrInputBuffer_Locked(); +- break; +- +- case AcceleratedVideoDecoder::kRanOutOfSurfaces: +- // No more output buffers in the decoder, try getting more or go to +- // sleep waiting for them. +- if (!WaitForSurfaces_Locked()) +- return; +- +- break; +- +- case AcceleratedVideoDecoder::kNeedContextUpdate: +- // This should not happen as we return false from +- // IsFrameContextRequired(). +- NOTREACHED() << "Context updates not supported"; +- return; +- +- case AcceleratedVideoDecoder::kDecodeError: +- RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", +- PLATFORM_FAILURE, ); +- return; +- } +- } +-} +- +-void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, +- gfx::Size size) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- DCHECK(!awaiting_va_surfaces_recycle_); +- +- // At this point decoder has stopped running and has already posted onto our +- // loop any remaining output request callbacks, which executed before we got +- // here. Some of them might have been pended though, because we might not +- // have had enough TFPictures to output surfaces to. Initiate a wait cycle, +- // which will wait for client to return enough PictureBuffers to us, so that +- // we can finish all pending output callbacks, releasing associated surfaces. +- VLOGF(2) << "Initiating surface set change"; +- awaiting_va_surfaces_recycle_ = true; +- +- requested_num_pics_ = num_pics; +- requested_pic_size_ = size; +- +- TryFinishSurfaceSetChange(); +-} +- +-void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- if (!awaiting_va_surfaces_recycle_) +- return; +- +- if (!pending_output_cbs_.empty() || +- pictures_.size() != available_va_surfaces_.size()) { +- // Either: +- // 1. Not all pending pending output callbacks have been executed yet. +- // Wait for the client to return enough pictures and retry later. +- // 2. The above happened and all surface release callbacks have been posted +- // as the result, but not all have executed yet. Post ourselves after them +- // to let them release surfaces. +- DVLOGF(2) << "Awaiting pending output/surface release callbacks to finish"; +- task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, +- weak_this_)); +- return; +- } +- +- // All surfaces released, destroy them and dismiss all PictureBuffers. +- awaiting_va_surfaces_recycle_ = false; +- available_va_surfaces_.clear(); +- vaapi_wrapper_->DestroySurfaces(); +- +- for (Pictures::iterator iter = pictures_.begin(); iter != pictures_.end(); +- ++iter) { +- VLOGF(2) << "Dismissing picture id: " << iter->first; +- if (client_) +- client_->DismissPictureBuffer(iter->first); +- } +- pictures_.clear(); +- +- // And ask for a new set as requested. +- VLOGF(2) << "Requesting " << requested_num_pics_ +- << " pictures of size: " << requested_pic_size_.ToString(); +- +- VideoPixelFormat format = GfxBufferFormatToVideoPixelFormat(output_format_); +- task_runner_->PostTask( +- FROM_HERE, base::Bind(&Client::ProvidePictureBuffers, client_, +- requested_num_pics_, format, 1, requested_pic_size_, +- vaapi_picture_factory_->GetGLTextureTarget())); +-} +- +-void VaapiVideoDecodeAccelerator::Decode( +- const BitstreamBuffer& bitstream_buffer) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", +- bitstream_buffer.id()); +- +- if (bitstream_buffer.id() < 0) { +- if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) +- base::SharedMemory::CloseHandle(bitstream_buffer.handle()); +- VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); +- NotifyError(INVALID_ARGUMENT); +- return; +- } +- +- // Skip empty buffers. VaapiVDA uses empty buffer as dummy buffer for flush +- // internally. +- if (bitstream_buffer.size() == 0) { +- if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) +- base::SharedMemory::CloseHandle(bitstream_buffer.handle()); +- if (client_) +- client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id()); +- return; +- } +- +- QueueInputBuffer(bitstream_buffer); +-} +- +-void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( +- VASurfaceID va_surface_id) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- base::AutoLock auto_lock(lock_); +- +- available_va_surfaces_.push_back(va_surface_id); +- surfaces_available_.Signal(); +-} +- +-void VaapiVideoDecodeAccelerator::AssignPictureBuffers( +- const std::vector& buffers) { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- base::AutoLock auto_lock(lock_); +- DCHECK(pictures_.empty()); +- +- while (!output_buffers_.empty()) +- output_buffers_.pop(); +- +- RETURN_AND_NOTIFY_ON_FAILURE( +- buffers.size() >= requested_num_pics_, +- "Got an invalid number of picture buffers. (Got " << buffers.size() +- << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); +- DCHECK(requested_pic_size_ == buffers[0].size()); +- +- const unsigned int va_format = GetVaFormatForVideoCodecProfile(profile_); +- std::vector va_surface_ids; +- RETURN_AND_NOTIFY_ON_FAILURE( +- vaapi_wrapper_->CreateSurfaces(va_format, requested_pic_size_, +- buffers.size(), &va_surface_ids), +- "Failed creating VA Surfaces", PLATFORM_FAILURE, ); +- DCHECK_EQ(va_surface_ids.size(), buffers.size()); +- +- for (size_t i = 0; i < buffers.size(); ++i) { +- uint32_t client_id = !buffers[i].client_texture_ids().empty() +- ? buffers[i].client_texture_ids()[0] +- : 0; +- uint32_t service_id = !buffers[i].service_texture_ids().empty() +- ? buffers[i].service_texture_ids()[0] +- : 0; +- +- std::unique_ptr picture(vaapi_picture_factory_->Create( +- vaapi_wrapper_, make_context_current_cb_, bind_image_cb_, +- buffers[i].id(), requested_pic_size_, service_id, client_id)); +- RETURN_AND_NOTIFY_ON_FAILURE( +- picture.get(), "Failed creating a VaapiPicture", PLATFORM_FAILURE, ); +- +- if (output_mode_ == Config::OutputMode::ALLOCATE) { +- RETURN_AND_NOTIFY_ON_FAILURE( +- picture->Allocate(output_format_), +- "Failed to allocate memory for a VaapiPicture", PLATFORM_FAILURE, ); +- output_buffers_.push(buffers[i].id()); +- } +- bool inserted = +- pictures_.insert(std::make_pair(buffers[i].id(), std::move(picture))) +- .second; +- DCHECK(inserted); +- +- available_va_surfaces_.push_back(va_surface_ids[i]); +- surfaces_available_.Signal(); +- } +- +- // Resume DecodeTask if it is still in decoding state. +- if (state_ == kDecoding) { +- decoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, +- base::Unretained(this))); +- } +-} +- +-#if defined(USE_OZONE) +-static void CloseGpuMemoryBufferHandle( +- const gfx::GpuMemoryBufferHandle& handle) { +- for (const auto& fd : handle.native_pixmap_handle.fds) { +- // Close the fd by wrapping it in a ScopedFD and letting +- // it fall out of scope. +- base::ScopedFD scoped_fd(fd.fd); +- } +-} +- +-void VaapiVideoDecodeAccelerator::ImportBufferForPicture( +- int32_t picture_buffer_id, +- const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) { +- VLOGF(2) << "Importing picture id: " << picture_buffer_id; +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- if (output_mode_ != Config::OutputMode::IMPORT) { +- CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); +- VLOGF(1) << "Cannot import in non-import mode"; +- NotifyError(INVALID_ARGUMENT); +- return; +- } +- +- VaapiPicture* picture = PictureById(picture_buffer_id); +- if (!picture) { +- CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); +- +- // It's possible that we've already posted a DismissPictureBuffer for this +- // picture, but it has not yet executed when this ImportBufferForPicture +- // was posted to us by the client. In that case just ignore this (we've +- // already dismissed it and accounted for that). +- VLOGF(3) << "got picture id=" << picture_buffer_id +- << " not in use (anymore?)."; +- return; +- } +- +- if (!picture->ImportGpuMemoryBufferHandle(output_format_, +- gpu_memory_buffer_handle)) { +- // ImportGpuMemoryBufferHandle will close the handles even on failure, so +- // we don't need to do this ourselves. +- VLOGF(1) << "Failed to import GpuMemoryBufferHandle"; +- NotifyError(PLATFORM_FAILURE); +- return; +- } +- +- ReusePictureBuffer(picture_buffer_id); +-} +-#endif +- +-void VaapiVideoDecodeAccelerator::ReusePictureBuffer( +- int32_t picture_buffer_id) { +- VLOGF(4) << "picture id=" << picture_buffer_id; +- DCHECK(task_runner_->BelongsToCurrentThread()); +- TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", +- picture_buffer_id); +- +- if (!PictureById(picture_buffer_id)) { +- // It's possible that we've already posted a DismissPictureBuffer for this +- // picture, but it has not yet executed when this ReusePictureBuffer +- // was posted to us by the client. In that case just ignore this (we've +- // already dismissed it and accounted for that). +- VLOGF(3) << "got picture id=" << picture_buffer_id +- << " not in use (anymore?)."; +- return; +- } +- +- --num_frames_at_client_; +- TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); +- +- output_buffers_.push(picture_buffer_id); +- TryOutputSurface(); +-} +- +-void VaapiVideoDecodeAccelerator::FlushTask() { +- VLOGF(2); +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- DCHECK(curr_input_buffer_.get() && curr_input_buffer_->IsFlushRequest()); +- +- curr_input_buffer_.reset(); +- +- // First flush all the pictures that haven't been outputted, notifying the +- // client to output them. +- bool res = decoder_->Flush(); +- RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", +- PLATFORM_FAILURE, ); +- +- // Put the decoder in idle state, ready to resume. +- decoder_->Reset(); +- +- task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); +-} +- +-void VaapiVideoDecodeAccelerator::Flush() { +- VLOGF(2) << "Got flush request"; +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- // Queue a dummy buffer, which means flush. +- QueueInputBuffer(media::BitstreamBuffer()); +-} +- +-void VaapiVideoDecodeAccelerator::FinishFlush() { +- VLOGF(2); +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- finish_flush_pending_ = false; +- +- base::AutoLock auto_lock(lock_); +- if (state_ != kDecoding) { +- DCHECK(state_ == kDestroying || state_ == kResetting) << state_; +- return; +- } +- +- // Still waiting for textures from client to finish outputting all pending +- // frames. Try again later. +- if (!pending_output_cbs_.empty()) { +- finish_flush_pending_ = true; +- return; +- } +- +- // Resume decoding if necessary. +- if (input_buffers_.empty()) { +- state_ = kIdle; +- } else { +- decoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, +- base::Unretained(this))); +- } +- +- task_runner_->PostTask(FROM_HERE, +- base::Bind(&Client::NotifyFlushDone, client_)); +-} +- +-void VaapiVideoDecodeAccelerator::ResetTask() { +- VLOGF(2); +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- +- // All the decoding tasks from before the reset request from client are done +- // by now, as this task was scheduled after them and client is expected not +- // to call Decode() after Reset() and before NotifyResetDone. +- decoder_->Reset(); +- +- base::AutoLock auto_lock(lock_); +- +- // Return current input buffer, if present. +- if (curr_input_buffer_.get()) +- ReturnCurrInputBuffer_Locked(); +- +- // And let client know that we are done with reset. +- task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); +-} +- +-void VaapiVideoDecodeAccelerator::Reset() { +- VLOGF(2) << "Got reset request"; +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- // This will make any new decode tasks exit early. +- base::AutoLock auto_lock(lock_); +- state_ = kResetting; +- finish_flush_pending_ = false; +- +- // Drop all remaining input buffers, if present. +- while (!input_buffers_.empty()) +- input_buffers_.pop(); +- TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); +- +- decoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, +- base::Unretained(this))); +- +- input_ready_.Signal(); +- surfaces_available_.Signal(); +-} +- +-void VaapiVideoDecodeAccelerator::FinishReset() { +- VLOGF(2); +- DCHECK(task_runner_->BelongsToCurrentThread()); +- base::AutoLock auto_lock(lock_); +- +- if (state_ != kResetting) { +- DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; +- return; // We could've gotten destroyed already. +- } +- +- // Drop pending outputs. +- while (!pending_output_cbs_.empty()) +- pending_output_cbs_.pop(); +- +- if (awaiting_va_surfaces_recycle_) { +- // Decoder requested a new surface set while we were waiting for it to +- // finish the last DecodeTask, running at the time of Reset(). +- // Let the surface set change finish first before resetting. +- task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); +- return; +- } +- +- state_ = kIdle; +- +- task_runner_->PostTask(FROM_HERE, +- base::Bind(&Client::NotifyResetDone, client_)); +- +- // The client might have given us new buffers via Decode() while we were +- // resetting and might be waiting for our move, and not call Decode() anymore +- // until we return something. Post a DecodeTask() so that we won't +- // sleep forever waiting for Decode() in that case. Having two of them +- // in the pipe is harmless, the additional one will return as soon as it sees +- // that we are back in kDecoding state. +- if (!input_buffers_.empty()) { +- state_ = kDecoding; +- decoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, +- base::Unretained(this))); +- } +-} +- +-void VaapiVideoDecodeAccelerator::Cleanup() { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- +- base::AutoLock auto_lock(lock_); +- if (state_ == kUninitialized || state_ == kDestroying) +- return; +- +- VLOGF(2) << "Destroying VAVDA"; +- state_ = kDestroying; +- +- client_ptr_factory_.reset(); +- weak_this_factory_.InvalidateWeakPtrs(); +- +- // Signal all potential waiters on the decoder_thread_, let them early-exit, +- // as we've just moved to the kDestroying state, and wait for all tasks +- // to finish. +- input_ready_.Signal(); +- surfaces_available_.Signal(); +- { +- base::AutoUnlock auto_unlock(lock_); +- decoder_thread_.Stop(); +- } +- +- state_ = kUninitialized; +-} +- +-void VaapiVideoDecodeAccelerator::Destroy() { +- DCHECK(task_runner_->BelongsToCurrentThread()); +- Cleanup(); +- delete this; +-} +- +-bool VaapiVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( +- const base::WeakPtr& decode_client, +- const scoped_refptr& decode_task_runner) { +- return false; +-} +- +-bool VaapiVideoDecodeAccelerator::DecodeSurface( +- const scoped_refptr& dec_surface) { +- const bool result = vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( +- dec_surface->va_surface()->id()); +- if (!result) +- VLOGF(1) << "Failed decoding picture"; +- return result; +-} +- +-void VaapiVideoDecodeAccelerator::SurfaceReady( +- const scoped_refptr& dec_surface) { +- if (!task_runner_->BelongsToCurrentThread()) { +- task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::SurfaceReady, +- weak_this_, dec_surface)); +- return; +- } +- +- DCHECK(!awaiting_va_surfaces_recycle_); +- +- { +- base::AutoLock auto_lock(lock_); +- // Drop any requests to output if we are resetting or being destroyed. +- if (state_ == kResetting || state_ == kDestroying) +- return; +- } +- +- pending_output_cbs_.push( +- base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, weak_this_, +- dec_surface->va_surface(), dec_surface->bitstream_id(), +- dec_surface->visible_rect())); +- +- TryOutputSurface(); +-} +- +-scoped_refptr +-VaapiVideoDecodeAccelerator::CreateSurface() { +- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); +- base::AutoLock auto_lock(lock_); +- +- if (available_va_surfaces_.empty()) +- return nullptr; +- +- DCHECK(!awaiting_va_surfaces_recycle_); +- scoped_refptr va_surface(new VASurface( +- available_va_surfaces_.front(), requested_pic_size_, +- vaapi_wrapper_->va_surface_format(), va_surface_release_cb_)); +- available_va_surfaces_.pop_front(); +- +- return new VaapiDecodeSurface(curr_input_buffer_->id(), va_surface); +-} +- +-VaapiVideoDecodeAccelerator::VaapiH264Accelerator::VaapiH264Accelerator( +- VaapiVideoDecodeAccelerator* vaapi_dec, +- VaapiWrapper* vaapi_wrapper) +- : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { +- DCHECK(vaapi_wrapper_); +- DCHECK(vaapi_dec_); +-} +- +-VaapiVideoDecodeAccelerator::VaapiH264Accelerator::~VaapiH264Accelerator() {} +- +-scoped_refptr +-VaapiVideoDecodeAccelerator::VaapiH264Accelerator::CreateH264Picture() { +- scoped_refptr va_surface = vaapi_dec_->CreateSurface(); +- if (!va_surface) +- return nullptr; +- +- return new VaapiH264Picture(std::move(va_surface)); +-} +- +-// Fill |va_pic| with default/neutral values. +-static void InitVAPicture(VAPictureH264* va_pic) { +- memset(va_pic, 0, sizeof(*va_pic)); +- va_pic->picture_id = VA_INVALID_ID; +- va_pic->flags = VA_PICTURE_H264_INVALID; +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitFrameMetadata( +- const H264SPS* sps, +- const H264PPS* pps, +- const H264DPB& dpb, +- const H264Picture::Vector& ref_pic_listp0, +- const H264Picture::Vector& ref_pic_listb0, +- const H264Picture::Vector& ref_pic_listb1, +- const scoped_refptr& pic) { +- VAPictureParameterBufferH264 pic_param; +- memset(&pic_param, 0, sizeof(pic_param)); +- +-#define FROM_SPS_TO_PP(a) pic_param.a = sps->a +-#define FROM_SPS_TO_PP2(a, b) pic_param.b = sps->a +- FROM_SPS_TO_PP2(pic_width_in_mbs_minus1, picture_width_in_mbs_minus1); +- // This assumes non-interlaced video +- FROM_SPS_TO_PP2(pic_height_in_map_units_minus1, picture_height_in_mbs_minus1); +- FROM_SPS_TO_PP(bit_depth_luma_minus8); +- FROM_SPS_TO_PP(bit_depth_chroma_minus8); +-#undef FROM_SPS_TO_PP +-#undef FROM_SPS_TO_PP2 +- +-#define FROM_SPS_TO_PP_SF(a) pic_param.seq_fields.bits.a = sps->a +-#define FROM_SPS_TO_PP_SF2(a, b) pic_param.seq_fields.bits.b = sps->a +- FROM_SPS_TO_PP_SF(chroma_format_idc); +- FROM_SPS_TO_PP_SF2(separate_colour_plane_flag, +- residual_colour_transform_flag); +- FROM_SPS_TO_PP_SF(gaps_in_frame_num_value_allowed_flag); +- FROM_SPS_TO_PP_SF(frame_mbs_only_flag); +- FROM_SPS_TO_PP_SF(mb_adaptive_frame_field_flag); +- FROM_SPS_TO_PP_SF(direct_8x8_inference_flag); +- pic_param.seq_fields.bits.MinLumaBiPredSize8x8 = (sps->level_idc >= 31); +- FROM_SPS_TO_PP_SF(log2_max_frame_num_minus4); +- FROM_SPS_TO_PP_SF(pic_order_cnt_type); +- FROM_SPS_TO_PP_SF(log2_max_pic_order_cnt_lsb_minus4); +- FROM_SPS_TO_PP_SF(delta_pic_order_always_zero_flag); +-#undef FROM_SPS_TO_PP_SF +-#undef FROM_SPS_TO_PP_SF2 +- +-#define FROM_PPS_TO_PP(a) pic_param.a = pps->a +- FROM_PPS_TO_PP(pic_init_qp_minus26); +- FROM_PPS_TO_PP(pic_init_qs_minus26); +- FROM_PPS_TO_PP(chroma_qp_index_offset); +- FROM_PPS_TO_PP(second_chroma_qp_index_offset); +-#undef FROM_PPS_TO_PP +- +-#define FROM_PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = pps->a +-#define FROM_PPS_TO_PP_PF2(a, b) pic_param.pic_fields.bits.b = pps->a +- FROM_PPS_TO_PP_PF(entropy_coding_mode_flag); +- FROM_PPS_TO_PP_PF(weighted_pred_flag); +- FROM_PPS_TO_PP_PF(weighted_bipred_idc); +- FROM_PPS_TO_PP_PF(transform_8x8_mode_flag); +- +- pic_param.pic_fields.bits.field_pic_flag = 0; +- FROM_PPS_TO_PP_PF(constrained_intra_pred_flag); +- FROM_PPS_TO_PP_PF2(bottom_field_pic_order_in_frame_present_flag, +- pic_order_present_flag); +- FROM_PPS_TO_PP_PF(deblocking_filter_control_present_flag); +- FROM_PPS_TO_PP_PF(redundant_pic_cnt_present_flag); +- pic_param.pic_fields.bits.reference_pic_flag = pic->ref; +-#undef FROM_PPS_TO_PP_PF +-#undef FROM_PPS_TO_PP_PF2 +- +- pic_param.frame_num = pic->frame_num; +- +- InitVAPicture(&pic_param.CurrPic); +- FillVAPicture(&pic_param.CurrPic, pic); +- +- // Init reference pictures' array. +- for (int i = 0; i < 16; ++i) +- InitVAPicture(&pic_param.ReferenceFrames[i]); +- +- // And fill it with picture info from DPB. +- FillVARefFramesFromDPB(dpb, pic_param.ReferenceFrames, +- arraysize(pic_param.ReferenceFrames)); +- +- pic_param.num_ref_frames = sps->max_num_ref_frames; +- +- if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, +- sizeof(pic_param), &pic_param)) +- return false; +- +- VAIQMatrixBufferH264 iq_matrix_buf; +- memset(&iq_matrix_buf, 0, sizeof(iq_matrix_buf)); +- +- if (pps->pic_scaling_matrix_present_flag) { +- for (int i = 0; i < 6; ++i) { +- for (int j = 0; j < 16; ++j) +- iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = +- pps->scaling_list4x4[i][j]; +- } +- +- for (int i = 0; i < 2; ++i) { +- for (int j = 0; j < 64; ++j) +- iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = +- pps->scaling_list8x8[i][j]; +- } +- } else { +- for (int i = 0; i < 6; ++i) { +- for (int j = 0; j < 16; ++j) +- iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = +- sps->scaling_list4x4[i][j]; +- } +- +- for (int i = 0; i < 2; ++i) { +- for (int j = 0; j < 64; ++j) +- iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = +- sps->scaling_list8x8[i][j]; +- } +- } +- +- return vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, +- sizeof(iq_matrix_buf), &iq_matrix_buf); +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitSlice( +- const H264PPS* pps, +- const H264SliceHeader* slice_hdr, +- const H264Picture::Vector& ref_pic_list0, +- const H264Picture::Vector& ref_pic_list1, +- const scoped_refptr& pic, +- const uint8_t* data, +- size_t size) { +- VASliceParameterBufferH264 slice_param; +- memset(&slice_param, 0, sizeof(slice_param)); +- +- slice_param.slice_data_size = slice_hdr->nalu_size; +- slice_param.slice_data_offset = 0; +- slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; +- slice_param.slice_data_bit_offset = slice_hdr->header_bit_size; +- +-#define SHDRToSP(a) slice_param.a = slice_hdr->a +- SHDRToSP(first_mb_in_slice); +- slice_param.slice_type = slice_hdr->slice_type % 5; +- SHDRToSP(direct_spatial_mv_pred_flag); +- +- // TODO posciak: make sure parser sets those even when override flags +- // in slice header is off. +- SHDRToSP(num_ref_idx_l0_active_minus1); +- SHDRToSP(num_ref_idx_l1_active_minus1); +- SHDRToSP(cabac_init_idc); +- SHDRToSP(slice_qp_delta); +- SHDRToSP(disable_deblocking_filter_idc); +- SHDRToSP(slice_alpha_c0_offset_div2); +- SHDRToSP(slice_beta_offset_div2); +- +- if (((slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) && +- pps->weighted_pred_flag) || +- (slice_hdr->IsBSlice() && pps->weighted_bipred_idc == 1)) { +- SHDRToSP(luma_log2_weight_denom); +- SHDRToSP(chroma_log2_weight_denom); +- +- SHDRToSP(luma_weight_l0_flag); +- SHDRToSP(luma_weight_l1_flag); +- +- SHDRToSP(chroma_weight_l0_flag); +- SHDRToSP(chroma_weight_l1_flag); +- +- for (int i = 0; i <= slice_param.num_ref_idx_l0_active_minus1; ++i) { +- slice_param.luma_weight_l0[i] = +- slice_hdr->pred_weight_table_l0.luma_weight[i]; +- slice_param.luma_offset_l0[i] = +- slice_hdr->pred_weight_table_l0.luma_offset[i]; +- +- for (int j = 0; j < 2; ++j) { +- slice_param.chroma_weight_l0[i][j] = +- slice_hdr->pred_weight_table_l0.chroma_weight[i][j]; +- slice_param.chroma_offset_l0[i][j] = +- slice_hdr->pred_weight_table_l0.chroma_offset[i][j]; +- } +- } +- +- if (slice_hdr->IsBSlice()) { +- for (int i = 0; i <= slice_param.num_ref_idx_l1_active_minus1; ++i) { +- slice_param.luma_weight_l1[i] = +- slice_hdr->pred_weight_table_l1.luma_weight[i]; +- slice_param.luma_offset_l1[i] = +- slice_hdr->pred_weight_table_l1.luma_offset[i]; +- +- for (int j = 0; j < 2; ++j) { +- slice_param.chroma_weight_l1[i][j] = +- slice_hdr->pred_weight_table_l1.chroma_weight[i][j]; +- slice_param.chroma_offset_l1[i][j] = +- slice_hdr->pred_weight_table_l1.chroma_offset[i][j]; +- } +- } +- } +- } +- +- static_assert( +- arraysize(slice_param.RefPicList0) == arraysize(slice_param.RefPicList1), +- "Invalid RefPicList sizes"); +- +- for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) { +- InitVAPicture(&slice_param.RefPicList0[i]); +- InitVAPicture(&slice_param.RefPicList1[i]); +- } +- +- for (size_t i = 0; +- i < ref_pic_list0.size() && i < arraysize(slice_param.RefPicList0); +- ++i) { +- if (ref_pic_list0[i]) +- FillVAPicture(&slice_param.RefPicList0[i], ref_pic_list0[i]); +- } +- for (size_t i = 0; +- i < ref_pic_list1.size() && i < arraysize(slice_param.RefPicList1); +- ++i) { +- if (ref_pic_list1[i]) +- FillVAPicture(&slice_param.RefPicList1[i], ref_pic_list1[i]); +- } +- +- if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, +- sizeof(slice_param), &slice_param)) +- return false; +- +- // Can't help it, blame libva... +- void* non_const_ptr = const_cast(data); +- return vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, size, +- non_const_ptr); +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitDecode( +- const scoped_refptr& pic) { +- VLOGF(4) << "Decoding POC " << pic->pic_order_cnt; +- scoped_refptr dec_surface = +- H264PictureToVaapiDecodeSurface(pic); +- +- return vaapi_dec_->DecodeSurface(dec_surface); +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::OutputPicture( +- const scoped_refptr& pic) { +- scoped_refptr dec_surface = +- H264PictureToVaapiDecodeSurface(pic); +- dec_surface->set_visible_rect(pic->visible_rect); +- vaapi_dec_->SurfaceReady(dec_surface); +- +- return true; +-} +- +-void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::Reset() { +- vaapi_wrapper_->DestroyPendingBuffers(); +-} +- +-scoped_refptr +-VaapiVideoDecodeAccelerator::VaapiH264Accelerator:: +- H264PictureToVaapiDecodeSurface(const scoped_refptr& pic) { +- VaapiH264Picture* vaapi_pic = pic->AsVaapiH264Picture(); +- CHECK(vaapi_pic); +- return vaapi_pic->dec_surface(); +-} +- +-void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVAPicture( +- VAPictureH264* va_pic, +- scoped_refptr pic) { +- VASurfaceID va_surface_id = VA_INVALID_SURFACE; +- +- if (!pic->nonexisting) { +- scoped_refptr dec_surface = +- H264PictureToVaapiDecodeSurface(pic); +- va_surface_id = dec_surface->va_surface()->id(); +- } +- +- va_pic->picture_id = va_surface_id; +- va_pic->frame_idx = pic->frame_num; +- va_pic->flags = 0; +- +- switch (pic->field) { +- case H264Picture::FIELD_NONE: +- break; +- case H264Picture::FIELD_TOP: +- va_pic->flags |= VA_PICTURE_H264_TOP_FIELD; +- break; +- case H264Picture::FIELD_BOTTOM: +- va_pic->flags |= VA_PICTURE_H264_BOTTOM_FIELD; +- break; +- } +- +- if (pic->ref) { +- va_pic->flags |= pic->long_term ? VA_PICTURE_H264_LONG_TERM_REFERENCE +- : VA_PICTURE_H264_SHORT_TERM_REFERENCE; +- } +- +- va_pic->TopFieldOrderCnt = pic->top_field_order_cnt; +- va_pic->BottomFieldOrderCnt = pic->bottom_field_order_cnt; +-} +- +-int VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVARefFramesFromDPB( +- const H264DPB& dpb, +- VAPictureH264* va_pics, +- int num_pics) { +- H264Picture::Vector::const_reverse_iterator rit; +- int i; +- +- // Return reference frames in reverse order of insertion. +- // Libva does not document this, but other implementations (e.g. mplayer) +- // do it this way as well. +- for (rit = dpb.rbegin(), i = 0; rit != dpb.rend() && i < num_pics; ++rit) { +- if ((*rit)->ref) +- FillVAPicture(&va_pics[i++], *rit); +- } +- +- return i; +-} +- +-VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::VaapiVP8Accelerator( +- VaapiVideoDecodeAccelerator* vaapi_dec, +- VaapiWrapper* vaapi_wrapper) +- : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { +- DCHECK(vaapi_wrapper_); +- DCHECK(vaapi_dec_); +-} +- +-VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::~VaapiVP8Accelerator() {} +- +-scoped_refptr +-VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::CreateVP8Picture() { +- scoped_refptr va_surface = vaapi_dec_->CreateSurface(); +- if (!va_surface) +- return nullptr; +- +- return new VaapiVP8Picture(std::move(va_surface)); +-} +- +-#define ARRAY_MEMCPY_CHECKED(to, from) \ +- do { \ +- static_assert(sizeof(to) == sizeof(from), \ +- #from " and " #to " arrays must be of same size"); \ +- memcpy(to, from, sizeof(to)); \ +- } while (0) +- +-bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::SubmitDecode( +- const scoped_refptr& pic, +- const Vp8FrameHeader* frame_hdr, +- const scoped_refptr& last_frame, +- const scoped_refptr& golden_frame, +- const scoped_refptr& alt_frame) { +- VAIQMatrixBufferVP8 iq_matrix_buf; +- memset(&iq_matrix_buf, 0, sizeof(VAIQMatrixBufferVP8)); +- +- const Vp8SegmentationHeader& sgmnt_hdr = frame_hdr->segmentation_hdr; +- const Vp8QuantizationHeader& quant_hdr = frame_hdr->quantization_hdr; +- static_assert(arraysize(iq_matrix_buf.quantization_index) == kMaxMBSegments, +- "incorrect quantization matrix size"); +- for (size_t i = 0; i < kMaxMBSegments; ++i) { +- int q = quant_hdr.y_ac_qi; +- +- if (sgmnt_hdr.segmentation_enabled) { +- if (sgmnt_hdr.segment_feature_mode == +- Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) +- q = sgmnt_hdr.quantizer_update_value[i]; +- else +- q += sgmnt_hdr.quantizer_update_value[i]; +- } +- +-#define CLAMP_Q(q) std::min(std::max(q, 0), 127) +- static_assert(arraysize(iq_matrix_buf.quantization_index[i]) == 6, +- "incorrect quantization matrix size"); +- iq_matrix_buf.quantization_index[i][0] = CLAMP_Q(q); +- iq_matrix_buf.quantization_index[i][1] = CLAMP_Q(q + quant_hdr.y_dc_delta); +- iq_matrix_buf.quantization_index[i][2] = CLAMP_Q(q + quant_hdr.y2_dc_delta); +- iq_matrix_buf.quantization_index[i][3] = CLAMP_Q(q + quant_hdr.y2_ac_delta); +- iq_matrix_buf.quantization_index[i][4] = CLAMP_Q(q + quant_hdr.uv_dc_delta); +- iq_matrix_buf.quantization_index[i][5] = CLAMP_Q(q + quant_hdr.uv_ac_delta); +-#undef CLAMP_Q +- } +- +- if (!vaapi_wrapper_->SubmitBuffer( +- VAIQMatrixBufferType, sizeof(VAIQMatrixBufferVP8), &iq_matrix_buf)) +- return false; +- +- VAProbabilityDataBufferVP8 prob_buf; +- memset(&prob_buf, 0, sizeof(VAProbabilityDataBufferVP8)); +- +- const Vp8EntropyHeader& entr_hdr = frame_hdr->entropy_hdr; +- ARRAY_MEMCPY_CHECKED(prob_buf.dct_coeff_probs, entr_hdr.coeff_probs); +- +- if (!vaapi_wrapper_->SubmitBuffer(VAProbabilityBufferType, +- sizeof(VAProbabilityDataBufferVP8), +- &prob_buf)) +- return false; +- +- VAPictureParameterBufferVP8 pic_param; +- memset(&pic_param, 0, sizeof(VAPictureParameterBufferVP8)); +- pic_param.frame_width = frame_hdr->width; +- pic_param.frame_height = frame_hdr->height; +- +- if (last_frame) { +- scoped_refptr last_frame_surface = +- VP8PictureToVaapiDecodeSurface(last_frame); +- pic_param.last_ref_frame = last_frame_surface->va_surface()->id(); +- } else { +- pic_param.last_ref_frame = VA_INVALID_SURFACE; +- } +- +- if (golden_frame) { +- scoped_refptr golden_frame_surface = +- VP8PictureToVaapiDecodeSurface(golden_frame); +- pic_param.golden_ref_frame = golden_frame_surface->va_surface()->id(); +- } else { +- pic_param.golden_ref_frame = VA_INVALID_SURFACE; +- } +- +- if (alt_frame) { +- scoped_refptr alt_frame_surface = +- VP8PictureToVaapiDecodeSurface(alt_frame); +- pic_param.alt_ref_frame = alt_frame_surface->va_surface()->id(); +- } else { +- pic_param.alt_ref_frame = VA_INVALID_SURFACE; +- } +- +- pic_param.out_of_loop_frame = VA_INVALID_SURFACE; +- +- const Vp8LoopFilterHeader& lf_hdr = frame_hdr->loopfilter_hdr; +- +-#define FHDR_TO_PP_PF(a, b) pic_param.pic_fields.bits.a = (b) +- FHDR_TO_PP_PF(key_frame, frame_hdr->IsKeyframe() ? 0 : 1); +- FHDR_TO_PP_PF(version, frame_hdr->version); +- FHDR_TO_PP_PF(segmentation_enabled, sgmnt_hdr.segmentation_enabled); +- FHDR_TO_PP_PF(update_mb_segmentation_map, +- sgmnt_hdr.update_mb_segmentation_map); +- FHDR_TO_PP_PF(update_segment_feature_data, +- sgmnt_hdr.update_segment_feature_data); +- FHDR_TO_PP_PF(filter_type, lf_hdr.type); +- FHDR_TO_PP_PF(sharpness_level, lf_hdr.sharpness_level); +- FHDR_TO_PP_PF(loop_filter_adj_enable, lf_hdr.loop_filter_adj_enable); +- FHDR_TO_PP_PF(mode_ref_lf_delta_update, lf_hdr.mode_ref_lf_delta_update); +- FHDR_TO_PP_PF(sign_bias_golden, frame_hdr->sign_bias_golden); +- FHDR_TO_PP_PF(sign_bias_alternate, frame_hdr->sign_bias_alternate); +- FHDR_TO_PP_PF(mb_no_coeff_skip, frame_hdr->mb_no_skip_coeff); +- FHDR_TO_PP_PF(loop_filter_disable, lf_hdr.level == 0); +-#undef FHDR_TO_PP_PF +- +- ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, sgmnt_hdr.segment_prob); +- +- static_assert(arraysize(sgmnt_hdr.lf_update_value) == +- arraysize(pic_param.loop_filter_level), +- "loop filter level arrays mismatch"); +- for (size_t i = 0; i < arraysize(sgmnt_hdr.lf_update_value); ++i) { +- int lf_level = lf_hdr.level; +- if (sgmnt_hdr.segmentation_enabled) { +- if (sgmnt_hdr.segment_feature_mode == +- Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) +- lf_level = sgmnt_hdr.lf_update_value[i]; +- else +- lf_level += sgmnt_hdr.lf_update_value[i]; +- } +- +- // Clamp to [0..63] range. +- lf_level = std::min(std::max(lf_level, 0), 63); +- pic_param.loop_filter_level[i] = lf_level; +- } +- +- static_assert( +- arraysize(lf_hdr.ref_frame_delta) == +- arraysize(pic_param.loop_filter_deltas_ref_frame) && +- arraysize(lf_hdr.mb_mode_delta) == +- arraysize(pic_param.loop_filter_deltas_mode) && +- arraysize(lf_hdr.ref_frame_delta) == arraysize(lf_hdr.mb_mode_delta), +- "loop filter deltas arrays size mismatch"); +- for (size_t i = 0; i < arraysize(lf_hdr.ref_frame_delta); ++i) { +- pic_param.loop_filter_deltas_ref_frame[i] = lf_hdr.ref_frame_delta[i]; +- pic_param.loop_filter_deltas_mode[i] = lf_hdr.mb_mode_delta[i]; +- } +- +-#define FHDR_TO_PP(a) pic_param.a = frame_hdr->a +- FHDR_TO_PP(prob_skip_false); +- FHDR_TO_PP(prob_intra); +- FHDR_TO_PP(prob_last); +- FHDR_TO_PP(prob_gf); +-#undef FHDR_TO_PP +- +- ARRAY_MEMCPY_CHECKED(pic_param.y_mode_probs, entr_hdr.y_mode_probs); +- ARRAY_MEMCPY_CHECKED(pic_param.uv_mode_probs, entr_hdr.uv_mode_probs); +- ARRAY_MEMCPY_CHECKED(pic_param.mv_probs, entr_hdr.mv_probs); +- +- pic_param.bool_coder_ctx.range = frame_hdr->bool_dec_range; +- pic_param.bool_coder_ctx.value = frame_hdr->bool_dec_value; +- pic_param.bool_coder_ctx.count = frame_hdr->bool_dec_count; +- +- if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, +- sizeof(pic_param), &pic_param)) +- return false; +- +- VASliceParameterBufferVP8 slice_param; +- memset(&slice_param, 0, sizeof(slice_param)); +- slice_param.slice_data_size = frame_hdr->frame_size; +- slice_param.slice_data_offset = frame_hdr->first_part_offset; +- slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; +- slice_param.macroblock_offset = frame_hdr->macroblock_bit_offset; +- // Number of DCT partitions plus control partition. +- slice_param.num_of_partitions = frame_hdr->num_of_dct_partitions + 1; +- +- // Per VAAPI, this size only includes the size of the macroblock data in +- // the first partition (in bytes), so we have to subtract the header size. +- slice_param.partition_size[0] = +- frame_hdr->first_part_size - ((frame_hdr->macroblock_bit_offset + 7) / 8); +- +- for (size_t i = 0; i < frame_hdr->num_of_dct_partitions; ++i) +- slice_param.partition_size[i + 1] = frame_hdr->dct_partition_sizes[i]; +- +- if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, +- sizeof(VASliceParameterBufferVP8), +- &slice_param)) +- return false; +- +- void* non_const_ptr = const_cast(frame_hdr->data); +- if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, +- frame_hdr->frame_size, non_const_ptr)) +- return false; +- +- scoped_refptr dec_surface = +- VP8PictureToVaapiDecodeSurface(pic); +- +- return vaapi_dec_->DecodeSurface(dec_surface); +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::OutputPicture( +- const scoped_refptr& pic) { +- scoped_refptr dec_surface = +- VP8PictureToVaapiDecodeSurface(pic); +- dec_surface->set_visible_rect(pic->visible_rect); +- vaapi_dec_->SurfaceReady(dec_surface); +- return true; +-} +- +-scoped_refptr +-VaapiVideoDecodeAccelerator::VaapiVP8Accelerator:: +- VP8PictureToVaapiDecodeSurface(const scoped_refptr& pic) { +- VaapiVP8Picture* vaapi_pic = pic->AsVaapiVP8Picture(); +- CHECK(vaapi_pic); +- return vaapi_pic->dec_surface(); +-} +- +-VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::VaapiVP9Accelerator( +- VaapiVideoDecodeAccelerator* vaapi_dec, +- VaapiWrapper* vaapi_wrapper) +- : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { +- DCHECK(vaapi_wrapper_); +- DCHECK(vaapi_dec_); +-} +- +-VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::~VaapiVP9Accelerator() {} +- +-scoped_refptr +-VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::CreateVP9Picture() { +- scoped_refptr va_surface = vaapi_dec_->CreateSurface(); +- if (!va_surface) +- return nullptr; +- +- return new VaapiVP9Picture(std::move(va_surface)); +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::SubmitDecode( +- const scoped_refptr& pic, +- const Vp9SegmentationParams& seg, +- const Vp9LoopFilterParams& lf, +- const std::vector>& ref_pictures, +- const base::Closure& done_cb) { +- // |done_cb| should be null as we return false from IsFrameContextRequired(). +- DCHECK(done_cb.is_null()); +- +- VADecPictureParameterBufferVP9 pic_param; +- memset(&pic_param, 0, sizeof(pic_param)); +- +- const Vp9FrameHeader* frame_hdr = pic->frame_hdr.get(); +- DCHECK(frame_hdr); +- +- pic_param.frame_width = base::checked_cast(frame_hdr->frame_width); +- pic_param.frame_height = +- base::checked_cast(frame_hdr->frame_height); +- +- CHECK_EQ(ref_pictures.size(), arraysize(pic_param.reference_frames)); +- for (size_t i = 0; i < arraysize(pic_param.reference_frames); ++i) { +- VASurfaceID va_surface_id; +- if (ref_pictures[i]) { +- scoped_refptr surface = +- VP9PictureToVaapiDecodeSurface(ref_pictures[i]); +- va_surface_id = surface->va_surface()->id(); +- } else { +- va_surface_id = VA_INVALID_SURFACE; +- } +- +- pic_param.reference_frames[i] = va_surface_id; +- } +- +-#define FHDR_TO_PP_PF1(a) pic_param.pic_fields.bits.a = frame_hdr->a +-#define FHDR_TO_PP_PF2(a, b) pic_param.pic_fields.bits.a = b +- FHDR_TO_PP_PF2(subsampling_x, frame_hdr->subsampling_x == 1); +- FHDR_TO_PP_PF2(subsampling_y, frame_hdr->subsampling_y == 1); +- FHDR_TO_PP_PF2(frame_type, frame_hdr->IsKeyframe() ? 0 : 1); +- FHDR_TO_PP_PF1(show_frame); +- FHDR_TO_PP_PF1(error_resilient_mode); +- FHDR_TO_PP_PF1(intra_only); +- FHDR_TO_PP_PF1(allow_high_precision_mv); +- FHDR_TO_PP_PF2(mcomp_filter_type, frame_hdr->interpolation_filter); +- FHDR_TO_PP_PF1(frame_parallel_decoding_mode); +- FHDR_TO_PP_PF1(reset_frame_context); +- FHDR_TO_PP_PF1(refresh_frame_context); +- FHDR_TO_PP_PF2(frame_context_idx, frame_hdr->frame_context_idx_to_save_probs); +- FHDR_TO_PP_PF2(segmentation_enabled, seg.enabled); +- FHDR_TO_PP_PF2(segmentation_temporal_update, seg.temporal_update); +- FHDR_TO_PP_PF2(segmentation_update_map, seg.update_map); +- FHDR_TO_PP_PF2(last_ref_frame, frame_hdr->ref_frame_idx[0]); +- FHDR_TO_PP_PF2(last_ref_frame_sign_bias, +- frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_LAST]); +- FHDR_TO_PP_PF2(golden_ref_frame, frame_hdr->ref_frame_idx[1]); +- FHDR_TO_PP_PF2(golden_ref_frame_sign_bias, +- frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_GOLDEN]); +- FHDR_TO_PP_PF2(alt_ref_frame, frame_hdr->ref_frame_idx[2]); +- FHDR_TO_PP_PF2(alt_ref_frame_sign_bias, +- frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_ALTREF]); +- FHDR_TO_PP_PF2(lossless_flag, frame_hdr->quant_params.IsLossless()); +-#undef FHDR_TO_PP_PF2 +-#undef FHDR_TO_PP_PF1 +- +- pic_param.filter_level = lf.level; +- pic_param.sharpness_level = lf.sharpness; +- pic_param.log2_tile_rows = frame_hdr->tile_rows_log2; +- pic_param.log2_tile_columns = frame_hdr->tile_cols_log2; +- pic_param.frame_header_length_in_bytes = frame_hdr->uncompressed_header_size; +- pic_param.first_partition_size = frame_hdr->header_size_in_bytes; +- +- ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, seg.tree_probs); +- ARRAY_MEMCPY_CHECKED(pic_param.segment_pred_probs, seg.pred_probs); +- +- pic_param.profile = frame_hdr->profile; +- pic_param.bit_depth = frame_hdr->bit_depth; +- DCHECK((pic_param.profile == 0 && pic_param.bit_depth == 8) || +- (pic_param.profile == 2 && pic_param.bit_depth == 10)); +- +- if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, +- sizeof(pic_param), &pic_param)) +- return false; +- +- VASliceParameterBufferVP9 slice_param; +- memset(&slice_param, 0, sizeof(slice_param)); +- slice_param.slice_data_size = frame_hdr->frame_size; +- slice_param.slice_data_offset = 0; +- slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; +- +- static_assert(arraysize(Vp9SegmentationParams::feature_enabled) == +- arraysize(slice_param.seg_param), +- "seg_param array of incorrect size"); +- for (size_t i = 0; i < arraysize(slice_param.seg_param); ++i) { +- VASegmentParameterVP9& seg_param = slice_param.seg_param[i]; +-#define SEG_TO_SP_SF(a, b) seg_param.segment_flags.fields.a = b +- SEG_TO_SP_SF( +- segment_reference_enabled, +- seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); +- SEG_TO_SP_SF(segment_reference, +- seg.FeatureData(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); +- SEG_TO_SP_SF(segment_reference_skipped, +- seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_SKIP)); +-#undef SEG_TO_SP_SF +- +- ARRAY_MEMCPY_CHECKED(seg_param.filter_level, lf.lvl[i]); +- +- seg_param.luma_dc_quant_scale = seg.y_dequant[i][0]; +- seg_param.luma_ac_quant_scale = seg.y_dequant[i][1]; +- seg_param.chroma_dc_quant_scale = seg.uv_dequant[i][0]; +- seg_param.chroma_ac_quant_scale = seg.uv_dequant[i][1]; +- } +- +- if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, +- sizeof(slice_param), &slice_param)) +- return false; +- +- void* non_const_ptr = const_cast(frame_hdr->data); +- if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, +- frame_hdr->frame_size, non_const_ptr)) +- return false; +- +- scoped_refptr dec_surface = +- VP9PictureToVaapiDecodeSurface(pic); +- +- return vaapi_dec_->DecodeSurface(dec_surface); +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::OutputPicture( +- const scoped_refptr& pic) { +- scoped_refptr dec_surface = +- VP9PictureToVaapiDecodeSurface(pic); +- dec_surface->set_visible_rect(pic->visible_rect); +- vaapi_dec_->SurfaceReady(dec_surface); +- return true; +-} +- +-bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::GetFrameContext( +- const scoped_refptr& pic, +- Vp9FrameContext* frame_ctx) { +- NOTIMPLEMENTED() << "Frame context update not supported"; +- return false; +-} +- +-scoped_refptr +-VaapiVideoDecodeAccelerator::VaapiVP9Accelerator:: +- VP9PictureToVaapiDecodeSurface(const scoped_refptr& pic) { +- VaapiVP9Picture* vaapi_pic = pic->AsVaapiVP9Picture(); +- CHECK(vaapi_pic); +- return vaapi_pic->dec_surface(); +-} +- +-// static +-VideoDecodeAccelerator::SupportedProfiles +-VaapiVideoDecodeAccelerator::GetSupportedProfiles() { +- return VaapiWrapper::GetSupportedDecodeProfiles(); +-} +- +-} // namespace media +--- a/media/gpu/vaapi_video_decode_accelerator.h ++++ /dev/null +@@ -1,325 +0,0 @@ +-// Copyright (c) 2012 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +-// +-// This file contains an implementation of VideoDecoderAccelerator +-// that utilizes hardware video decoder present on Intel CPUs. +- +-#ifndef MEDIA_GPU_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ +-#define MEDIA_GPU_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ +- +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +- +-#include "base/containers/queue.h" +-#include "base/logging.h" +-#include "base/macros.h" +-#include "base/memory/linked_ptr.h" +-#include "base/memory/ref_counted.h" +-#include "base/memory/weak_ptr.h" +-#include "base/single_thread_task_runner.h" +-#include "base/synchronization/condition_variable.h" +-#include "base/synchronization/lock.h" +-#include "base/threading/thread.h" +-#include "media/base/bitstream_buffer.h" +-#include "media/gpu/gpu_video_decode_accelerator_helpers.h" +-#include "media/gpu/media_gpu_export.h" +-#include "media/gpu/shared_memory_region.h" +-#include "media/gpu/vaapi/vaapi_picture_factory.h" +-#include "media/gpu/vaapi_wrapper.h" +-#include "media/video/picture.h" +-#include "media/video/video_decode_accelerator.h" +- +-namespace gl { +-class GLImage; +-} +- +-namespace media { +- +-class AcceleratedVideoDecoder; +-class VaapiPicture; +- +-// Class to provide video decode acceleration for Intel systems with hardware +-// support for it, and on which libva is available. +-// Decoding tasks are performed in a separate decoding thread. +-// +-// Threading/life-cycle: this object is created & destroyed on the GPU +-// ChildThread. A few methods on it are called on the decoder thread which is +-// stopped during |this->Destroy()|, so any tasks posted to the decoder thread +-// can assume |*this| is still alive. See |weak_this_| below for more details. +-class MEDIA_GPU_EXPORT VaapiVideoDecodeAccelerator +- : public VideoDecodeAccelerator { +- public: +- // Wrapper of a VASurface with id and visible area. +- class VaapiDecodeSurface; +- +- VaapiVideoDecodeAccelerator( +- const MakeGLContextCurrentCallback& make_context_current_cb, +- const BindGLImageCallback& bind_image_cb); +- +- ~VaapiVideoDecodeAccelerator() override; +- +- // VideoDecodeAccelerator implementation. +- bool Initialize(const Config& config, Client* client) override; +- void Decode(const BitstreamBuffer& bitstream_buffer) override; +- void AssignPictureBuffers(const std::vector& buffers) override; +-#if defined(USE_OZONE) +- void ImportBufferForPicture( +- int32_t picture_buffer_id, +- const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override; +-#endif +- void ReusePictureBuffer(int32_t picture_buffer_id) override; +- void Flush() override; +- void Reset() override; +- void Destroy() override; +- bool TryToSetupDecodeOnSeparateThread( +- const base::WeakPtr& decode_client, +- const scoped_refptr& decode_task_runner) +- override; +- +- static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(); +- +- private: +- friend class VaapiVideoDecodeAcceleratorTest; +- class VaapiH264Accelerator; +- class VaapiVP8Accelerator; +- class VaapiVP9Accelerator; +- +- // An input buffer with id provided by the client and awaiting consumption. +- class InputBuffer; +- +- // Notify the client that an error has occurred and decoding cannot continue. +- void NotifyError(Error error); +- +- // Queue a input buffer for decode. +- void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer); +- +- // Get a new input buffer from the queue and set it up in decoder. This will +- // sleep if no input buffers are available. Return true if a new buffer has +- // been set up, false if an early exit has been requested (due to initiated +- // reset/flush/destroy). +- bool GetInputBuffer_Locked(); +- +- // Signal the client that the current buffer has been read and can be +- // returned. Will also release the mapping. +- void ReturnCurrInputBuffer_Locked(); +- +- // Wait for more surfaces to become available. Return true once they do or +- // false if an early exit has been requested (due to an initiated +- // reset/flush/destroy). +- bool WaitForSurfaces_Locked(); +- +- // Continue decoding given input buffers and sleep waiting for input/output +- // as needed. Will exit if a new set of surfaces or reset/flush/destroy +- // is requested. +- void DecodeTask(); +- +- // Scheduled after receiving a flush request and executed after the current +- // decoding task finishes decoding pending inputs. Makes the decoder return +- // all remaining output pictures and puts it in an idle state, ready +- // to resume if needed and schedules a FinishFlush. +- void FlushTask(); +- +- // Scheduled by the FlushTask after decoder is flushed to put VAVDA into idle +- // state and notify the client that flushing has been finished. +- void FinishFlush(); +- +- // Scheduled after receiving a reset request and executed after the current +- // decoding task finishes decoding the current frame. Puts the decoder into +- // an idle state, ready to resume if needed, discarding decoded but not yet +- // outputted pictures (decoder keeps ownership of their associated picture +- // buffers). Schedules a FinishReset afterwards. +- void ResetTask(); +- +- // Scheduled by ResetTask after it's done putting VAVDA into an idle state. +- // Drops remaining input buffers and notifies the client that reset has been +- // finished. +- void FinishReset(); +- +- // Helper for Destroy(), doing all the actual work except for deleting self. +- void Cleanup(); +- +- // Get a usable framebuffer configuration for use in binding textures +- // or return false on failure. +- bool InitializeFBConfig(); +- +- // Callback to be executed once we have a |va_surface| to be output and +- // an available |picture| to use for output. +- // Puts contents of |va_surface| into given |picture|, releases the surface +- // and passes the resulting picture to client to output the given +- // |visible_rect| part of it. +- void OutputPicture(const scoped_refptr& va_surface, +- int32_t input_id, +- gfx::Rect visible_rect, +- VaapiPicture* picture); +- +- // Try to OutputPicture() if we have both a ready surface and picture. +- void TryOutputSurface(); +- +- // Called when a VASurface is no longer in use by the decoder or is not being +- // synced/waiting to be synced to a picture. Returns it to available surfaces +- // pool. +- void RecycleVASurfaceID(VASurfaceID va_surface_id); +- +- // Initiate wait cycle for surfaces to be released before we release them +- // and allocate new ones, as requested by the decoder. +- void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size); +- +- // Check if the surfaces have been released or post ourselves for later. +- void TryFinishSurfaceSetChange(); +- +- // +- // Below methods are used by accelerator implementations. +- // +- // Decode of |dec_surface| is ready to be submitted and all codec-specific +- // settings are set in hardware. +- bool DecodeSurface(const scoped_refptr& dec_surface); +- +- // |dec_surface| is ready to be outputted once decode is finished. +- // This can be called before decode is actually done in hardware, and this +- // method is responsible for maintaining the ordering, i.e. the surfaces have +- // to be outputted in the same order as SurfaceReady is called. +- // On Intel, we don't have to explicitly maintain the ordering however, as the +- // driver will maintain ordering, as well as dependencies, and will process +- // each submitted command in order, and run each command only if its +- // dependencies are ready. +- void SurfaceReady(const scoped_refptr& dec_surface); +- +- // Return a new VaapiDecodeSurface for decoding into, or nullptr if not +- // available. +- scoped_refptr CreateSurface(); +- +- // VAVDA state. +- enum State { +- // Initialize() not called yet or failed. +- kUninitialized, +- // DecodeTask running. +- kDecoding, +- // Resetting, waiting for decoder to finish current task and cleanup. +- kResetting, +- // Idle, decoder in state ready to start/resume decoding. +- kIdle, +- // Destroying, waiting for the decoder to finish current task. +- kDestroying, +- }; +- +- // Protects input buffer and surface queues and state_. +- base::Lock lock_; +- State state_; +- Config::OutputMode output_mode_; +- +- // Queue of available InputBuffers (picture_buffer_ids). +- base::queue> input_buffers_; +- // Signalled when input buffers are queued onto |input_buffers_| queue. +- base::ConditionVariable input_ready_; +- +- // Current input buffer at decoder. +- std::unique_ptr curr_input_buffer_; +- +- // Queue for incoming output buffers (texture ids). +- using OutputBuffers = base::queue; +- OutputBuffers output_buffers_; +- +- std::unique_ptr vaapi_picture_factory_; +- +- scoped_refptr vaapi_wrapper_; +- +- // All allocated Pictures, regardless of their current state. Pictures are +- // allocated once using |create_vaapi_picture_callback_| and destroyed at the +- // end of decode. Comes after |vaapi_wrapper_| to ensure all pictures are +- // destroyed before said |vaapi_wrapper_| is destroyed. +- using Pictures = std::map>; +- Pictures pictures_; +- +- // Return a VaapiPicture associated with given client-provided id. +- VaapiPicture* PictureById(int32_t picture_buffer_id); +- +- // VA Surfaces no longer in use that can be passed back to the decoder for +- // reuse, once it requests them. +- std::list available_va_surfaces_; +- // Signalled when output surfaces are queued onto the available_va_surfaces_ +- // queue. +- base::ConditionVariable surfaces_available_; +- +- // Pending output requests from the decoder. When it indicates that we should +- // output a surface and we have an available Picture (i.e. texture) ready +- // to use, we'll execute the callback passing the Picture. The callback +- // will put the contents of the surface into the picture and return it to +- // the client, releasing the surface as well. +- // If we don't have any available Pictures at the time when the decoder +- // requests output, we'll store the request on pending_output_cbs_ queue for +- // later and run it once the client gives us more textures +- // via ReusePictureBuffer(). +- using OutputCB = base::Callback; +- base::queue pending_output_cbs_; +- +- // ChildThread's task runner. +- scoped_refptr task_runner_; +- +- // WeakPtr<> pointing to |this| for use in posting tasks from the decoder +- // thread back to the ChildThread. Because the decoder thread is a member of +- // this class, any task running on the decoder thread is guaranteed that this +- // object is still alive. As a result, tasks posted from ChildThread to +- // decoder thread should use base::Unretained(this), and tasks posted from the +- // decoder thread to the ChildThread should use |weak_this_|. +- base::WeakPtr weak_this_; +- +- // Callback used when creating VASurface objects. +- VASurface::ReleaseCB va_surface_release_cb_; +- +- // To expose client callbacks from VideoDecodeAccelerator. +- // NOTE: all calls to these objects *MUST* be executed on task_runner_. +- std::unique_ptr> client_ptr_factory_; +- base::WeakPtr client_; +- +- // Accelerators come after vaapi_wrapper_ to ensure they are destroyed first. +- std::unique_ptr h264_accelerator_; +- std::unique_ptr vp8_accelerator_; +- std::unique_ptr vp9_accelerator_; +- // After *_accelerator_ to ensure correct destruction order. +- std::unique_ptr decoder_; +- +- base::Thread decoder_thread_; +- // Use this to post tasks to |decoder_thread_| instead of +- // |decoder_thread_.message_loop()| because the latter will be NULL once +- // |decoder_thread_.Stop()| returns. +- scoped_refptr decoder_thread_task_runner_; +- +- int num_frames_at_client_; +- +- // Whether we are waiting for any pending_output_cbs_ to be run before +- // NotifyingFlushDone. +- bool finish_flush_pending_; +- +- // Decoder requested a new surface set and we are waiting for all the surfaces +- // to be returned before we can free them. +- bool awaiting_va_surfaces_recycle_; +- +- // Last requested number/resolution of output picture buffers and their +- // format. +- size_t requested_num_pics_; +- gfx::Size requested_pic_size_; +- gfx::BufferFormat output_format_; +- VideoCodecProfile profile_; +- +- // Callback to make GL context current. +- MakeGLContextCurrentCallback make_context_current_cb_; +- +- // Callback to bind a GLImage to a given texture. +- BindGLImageCallback bind_image_cb_; +- +- // The WeakPtrFactory for |weak_this_|. +- base::WeakPtrFactory weak_this_factory_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ +--- a/media/gpu/vaapi_video_decode_accelerator_unittest.cc ++++ /dev/null +@@ -1,367 +0,0 @@ +-// Copyright 2017 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_video_decode_accelerator.h" +- +-#include "base/bind.h" +-#include "base/memory/ptr_util.h" +-#include "base/run_loop.h" +-#include "base/test/scoped_task_environment.h" +-#include "media/gpu/accelerated_video_decoder.h" +-#include "media/gpu/format_utils.h" +-#include "media/gpu/vaapi/vaapi_picture.h" +-#include "media/gpu/vaapi/vaapi_picture_factory.h" +-#include "media/gpu/vaapi_wrapper.h" +-#include "testing/gmock/include/gmock/gmock.h" +-#include "testing/gtest/include/gtest/gtest.h" +- +-using ::testing::_; +-using ::testing::DoAll; +-using ::testing::Invoke; +-using ::testing::Return; +-using ::testing::TestWithParam; +-using ::testing::ValuesIn; +-using ::testing::WithArgs; +- +-namespace media { +- +-namespace { +- +-ACTION_P(RunClosure, closure) { +- closure.Run(); +-} +- +-constexpr VideoCodecProfile kCodecProfiles[] = {H264PROFILE_MIN, VP8PROFILE_MIN, +- VP9PROFILE_MIN}; +-constexpr int kBitstreamId = 123; +-constexpr size_t kInputSize = 256; +- +-} // namespace +- +-class MockAcceleratedVideoDecoder : public AcceleratedVideoDecoder { +- public: +- MockAcceleratedVideoDecoder() = default; +- ~MockAcceleratedVideoDecoder() override = default; +- +- MOCK_METHOD2(SetStream, void(const uint8_t* ptr, size_t size)); +- MOCK_METHOD0(Flush, bool()); +- MOCK_METHOD0(Reset, void()); +- MOCK_METHOD0(Decode, DecodeResult()); +- MOCK_CONST_METHOD0(GetPicSize, gfx::Size()); +- MOCK_CONST_METHOD0(GetRequiredNumOfPictures, size_t()); +-}; +- +-class MockVaapiWrapper : public VaapiWrapper { +- public: +- MockVaapiWrapper() = default; +- MOCK_METHOD4( +- CreateSurfaces, +- bool(unsigned int, const gfx::Size&, size_t, std::vector*)); +- MOCK_METHOD0(DestroySurfaces, void()); +- +- private: +- ~MockVaapiWrapper() override = default; +-}; +- +-class MockVaapiPicture : public VaapiPicture { +- public: +- MockVaapiPicture(const scoped_refptr& vaapi_wrapper, +- const MakeGLContextCurrentCallback& make_context_current_cb, +- const BindGLImageCallback& bind_image_cb, +- int32_t picture_buffer_id, +- const gfx::Size& size, +- uint32_t texture_id, +- uint32_t client_texture_id) +- : VaapiPicture(vaapi_wrapper, +- make_context_current_cb, +- bind_image_cb, +- picture_buffer_id, +- size, +- texture_id, +- client_texture_id) {} +- ~MockVaapiPicture() override = default; +- +- // VaapiPicture implementation. +- bool Allocate(gfx::BufferFormat format) override { return true; } +- bool ImportGpuMemoryBufferHandle( +- gfx::BufferFormat format, +- const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override { +- return true; +- } +- bool DownloadFromSurface( +- const scoped_refptr& va_surface) override { +- return true; +- } +- bool AllowOverlay() const override { return false; } +-}; +- +-class MockVaapiPictureFactory : public VaapiPictureFactory { +- public: +- MockVaapiPictureFactory() = default; +- ~MockVaapiPictureFactory() override = default; +- +- MOCK_METHOD2(MockCreateVaapiPicture, void(VaapiWrapper*, const gfx::Size&)); +- std::unique_ptr Create( +- const scoped_refptr& vaapi_wrapper, +- const MakeGLContextCurrentCallback& make_context_current_cb, +- const BindGLImageCallback& bind_image_cb, +- int32_t picture_buffer_id, +- const gfx::Size& size, +- uint32_t texture_id, +- uint32_t client_texture_id) override { +- MockCreateVaapiPicture(vaapi_wrapper.get(), size); +- return std::make_unique( +- vaapi_wrapper, make_context_current_cb, bind_image_cb, +- picture_buffer_id, size, texture_id, client_texture_id); +- } +-}; +- +-class VaapiVideoDecodeAcceleratorTest : public TestWithParam, +- public VideoDecodeAccelerator::Client { +- public: +- VaapiVideoDecodeAcceleratorTest() +- : vda_(base::Bind([] { return true; }), +- base::Bind([](uint32_t client_texture_id, +- uint32_t texture_target, +- const scoped_refptr& image, +- bool can_bind_to_sampler) { return true; })), +- decoder_thread_("VaapiVideoDecodeAcceleratorTestThread"), +- mock_decoder_(new MockAcceleratedVideoDecoder), +- mock_vaapi_picture_factory_(new MockVaapiPictureFactory()), +- mock_vaapi_wrapper_(new MockVaapiWrapper()), +- weak_ptr_factory_(this) { +- decoder_thread_.Start(); +- +- // Don't want to go through a vda_->Initialize() because it binds too many +- // items of the environment. Instead, just start the decoder thread. +- vda_.decoder_thread_task_runner_ = decoder_thread_.task_runner(); +- +- // Plug in all the mocks and ourselves as the |client_|. +- vda_.decoder_.reset(mock_decoder_); +- vda_.client_ = weak_ptr_factory_.GetWeakPtr(); +- vda_.vaapi_wrapper_ = mock_vaapi_wrapper_; +- vda_.vaapi_picture_factory_.reset(mock_vaapi_picture_factory_); +- +- vda_.state_ = VaapiVideoDecodeAccelerator::kIdle; +- } +- ~VaapiVideoDecodeAcceleratorTest() {} +- +- void SetUp() override { +- in_shm_.reset(new base::SharedMemory); +- ASSERT_TRUE(in_shm_->CreateAndMapAnonymous(kInputSize)); +- } +- +- void SetVdaStateToUnitialized() { +- vda_.state_ = VaapiVideoDecodeAccelerator::kUninitialized; +- } +- +- void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer) { +- vda_.QueueInputBuffer(bitstream_buffer); +- } +- +- void AssignPictureBuffers(const std::vector& picture_buffers) { +- vda_.AssignPictureBuffers(picture_buffers); +- } +- +- // Reset epilogue, needed to get |vda_| worker thread out of its Wait(). +- void ResetSequence() { +- base::RunLoop run_loop; +- base::Closure quit_closure = run_loop.QuitClosure(); +- EXPECT_CALL(*mock_decoder_, Reset()); +- EXPECT_CALL(*this, NotifyResetDone()).WillOnce(RunClosure(quit_closure)); +- vda_.Reset(); +- run_loop.Run(); +- } +- +- // VideoDecodeAccelerator::Client methods. +- MOCK_METHOD1(NotifyInitializationComplete, void(bool)); +- MOCK_METHOD5( +- ProvidePictureBuffers, +- void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t)); +- MOCK_METHOD1(DismissPictureBuffer, void(int32_t)); +- MOCK_METHOD1(PictureReady, void(const Picture&)); +- MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t)); +- MOCK_METHOD0(NotifyFlushDone, void()); +- MOCK_METHOD0(NotifyResetDone, void()); +- MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error)); +- +- base::test::ScopedTaskEnvironment scoped_task_environment_; +- +- // The class under test and a worker thread for it. +- VaapiVideoDecodeAccelerator vda_; +- base::Thread decoder_thread_; +- +- // Ownership passed to |vda_|, but we retain a pointer to it for MOCK checks. +- MockAcceleratedVideoDecoder* mock_decoder_; +- MockVaapiPictureFactory* mock_vaapi_picture_factory_; +- +- scoped_refptr mock_vaapi_wrapper_; +- +- std::unique_ptr in_shm_; +- +- private: +- base::WeakPtrFactory weak_ptr_factory_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAcceleratorTest); +-}; +- +-// This test checks that QueueInputBuffer() fails when state is kUnitialized. +-TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndError) { +- SetVdaStateToUnitialized(); +- +- base::SharedMemoryHandle handle; +- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); +- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); +- +- EXPECT_CALL(*this, +- NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)); +- QueueInputBuffer(bitstream_buffer); +-} +- +-// Verifies that Decode() returning kDecodeError ends up pinging NotifyError(). +-TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeError) { +- base::SharedMemoryHandle handle; +- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); +- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); +- +- base::RunLoop run_loop; +- base::Closure quit_closure = run_loop.QuitClosure(); +- EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); +- EXPECT_CALL(*mock_decoder_, Decode()) +- .WillOnce(Return(AcceleratedVideoDecoder::kDecodeError)); +- EXPECT_CALL(*this, NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)) +- .WillOnce(RunClosure(quit_closure)); +- +- QueueInputBuffer(bitstream_buffer); +- run_loop.Run(); +-} +- +-// Tests usual startup sequence: a BitstreamBuffer is enqueued for decode, +-// |vda_| asks for PictureBuffers, that we provide, and then the same Decode() +-// is tried again. +-TEST_P(VaapiVideoDecodeAcceleratorTest, +- QueueInputBufferAndAssignPictureBuffersAndDecode) { +- // Try and QueueInputBuffer(), |vda_| will ping us to ProvidePictureBuffers(). +- const uint32_t kNumPictures = 2; +- const gfx::Size kPictureSize(64, 48); +- { +- base::SharedMemoryHandle handle; +- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); +- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); +- +- base::RunLoop run_loop; +- base::Closure quit_closure = run_loop.QuitClosure(); +- EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); +- EXPECT_CALL(*mock_decoder_, Decode()) +- .WillOnce(Return(AcceleratedVideoDecoder::kAllocateNewSurfaces)); +- +- EXPECT_CALL(*mock_decoder_, GetRequiredNumOfPictures()) +- .WillOnce(Return(kNumPictures)); +- EXPECT_CALL(*mock_decoder_, GetPicSize()).WillOnce(Return(kPictureSize)); +- EXPECT_CALL(*mock_vaapi_wrapper_, DestroySurfaces()); +- +- EXPECT_CALL(*this, +- ProvidePictureBuffers(kNumPictures, _, 1, kPictureSize, _)) +- .WillOnce(RunClosure(quit_closure)); +- +- QueueInputBuffer(bitstream_buffer); +- run_loop.Run(); +- } +- // AssignPictureBuffers() accordingly and expect another go at Decode(). +- { +- base::RunLoop run_loop; +- base::Closure quit_closure = run_loop.QuitClosure(); +- +- const std::vector kPictureBuffers( +- {{2, kPictureSize}, {3, kPictureSize}}); +- EXPECT_EQ(kPictureBuffers.size(), kNumPictures); +- +- EXPECT_CALL(*mock_vaapi_wrapper_, +- CreateSurfaces(_, kPictureSize, kNumPictures, _)) +- .WillOnce(DoAll( +- WithArgs<3>(Invoke([](std::vector* va_surface_ids) { +- va_surface_ids->resize(kNumPictures); +- })), +- Return(true))); +- EXPECT_CALL(*mock_vaapi_picture_factory_, +- MockCreateVaapiPicture(mock_vaapi_wrapper_.get(), kPictureSize)) +- .Times(2); +- +- EXPECT_CALL(*mock_decoder_, Decode()) +- .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); +- EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) +- .WillOnce(RunClosure(quit_closure)); +- +- AssignPictureBuffers(kPictureBuffers); +- run_loop.Run(); +- } +- +- ResetSequence(); +-} +- +-// Verifies that Decode() replying kRanOutOfStreamData (to signal it's finished) +-// rolls to a NotifyEndOfBitstreamBuffer(). +-TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeFinished) { +- base::SharedMemoryHandle handle; +- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); +- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); +- +- { +- base::RunLoop run_loop; +- base::Closure quit_closure = run_loop.QuitClosure(); +- EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); +- EXPECT_CALL(*mock_decoder_, Decode()) +- .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); +- EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) +- .WillOnce(RunClosure(quit_closure)); +- +- QueueInputBuffer(bitstream_buffer); +- run_loop.Run(); +- } +- +- ResetSequence(); +-} +- +-// Verify that it is possible to select DRM(egl) and TFP(glx) at runtime. +-TEST_P(VaapiVideoDecodeAcceleratorTest, SupportedPlatforms) { +- EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationNone, +- mock_vaapi_picture_factory_->GetVaapiImplementation( +- gl::kGLImplementationNone)); +- EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationDrm, +- mock_vaapi_picture_factory_->GetVaapiImplementation( +- gl::kGLImplementationEGLGLES2)); +- +-#if defined(USE_X11) +- EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationX11, +- mock_vaapi_picture_factory_->GetVaapiImplementation( +- gl::kGLImplementationDesktopGL)); +-#endif +-} +- +-// Verifies the expected buffer format for each output mode. +-TEST_P(VaapiVideoDecodeAcceleratorTest, PictureBufferFormat) { +- gfx::BufferFormat allocate_format = +- mock_vaapi_picture_factory_->GetBufferFormatForAllocateMode(); +- gfx::BufferFormat import_format = +- mock_vaapi_picture_factory_->GetBufferFormatForImportMode(); +- +-#if defined(USE_OZONE) +- EXPECT_EQ(gfx::BufferFormat::BGRX_8888, allocate_format); +-#else +- EXPECT_EQ(gfx::BufferFormat::RGBX_8888, allocate_format); +-#endif // USE_OZONE +- +- EXPECT_EQ(gfx::BufferFormat::YVU_420, import_format); +- +- EXPECT_EQ(PIXEL_FORMAT_XRGB, +- GfxBufferFormatToVideoPixelFormat(allocate_format)); +- EXPECT_EQ(PIXEL_FORMAT_YV12, +- GfxBufferFormatToVideoPixelFormat(import_format)); +-} +- +-INSTANTIATE_TEST_CASE_P(/* No prefix. */, +- VaapiVideoDecodeAcceleratorTest, +- ValuesIn(kCodecProfiles)); +- +-} // namespace media +--- a/media/gpu/vaapi_video_encode_accelerator.cc ++++ /dev/null +@@ -1,1102 +0,0 @@ +-// Copyright 2014 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_video_encode_accelerator.h" +- +-#include +- +-#include +-#include +- +-#include +- +-#include "base/bind.h" +-#include "base/callback.h" +-#include "base/macros.h" +-#include "base/metrics/histogram_macros.h" +-#include "base/numerics/safe_conversions.h" +-#include "base/single_thread_task_runner.h" +-#include "base/threading/thread_task_runner_handle.h" +-#include "media/base/bind_to_current_loop.h" +-#include "media/gpu/h264_dpb.h" +-#include "media/gpu/shared_memory_region.h" +- +-#define VLOGF(level) VLOG(level) << __func__ << "(): " +-#define DVLOGF(level) DVLOG(level) << __func__ << "(): " +- +-#define NOTIFY_ERROR(error, msg) \ +- do { \ +- SetState(kError); \ +- VLOGF(1) << msg; \ +- VLOGF(1) << "Calling NotifyError(" << error << ")"; \ +- NotifyError(error); \ +- } while (0) +- +-namespace media { +- +-namespace { +-// Need 2 surfaces for each frame: one for input data and one for +-// reconstructed picture, which is later used for reference. +-const size_t kMinSurfacesToEncode = 2; +- +-// Subjectively chosen. +-const size_t kNumInputBuffers = 4; +-const size_t kMaxNumReferenceFrames = 4; +- +-// TODO(owenlin): Adjust the value after b/71367113 is fixed. +-const size_t kExtraOutputBufferSize = 32768; // bytes +- +-// We need up to kMaxNumReferenceFrames surfaces for reference, plus one +-// for input and one for encode (which will be added to the set of reference +-// frames for subsequent frames). Actual execution of HW encode is done +-// in parallel, and we want to process more frames in the meantime. +-// To have kNumInputBuffers in flight, we need a full set of reference + +-// encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and +-// (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames +-// in flight. +-const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode + +- kMinSurfacesToEncode * (kNumInputBuffers - 1); +- +-// An IDR every 2048 frames, an I frame every 256 and no B frames. +-// We choose IDR period to equal MaxFrameNum so it must be a power of 2. +-const int kIDRPeriod = 2048; +-const int kIPeriod = 256; +-const int kIPPeriod = 1; +- +-const int kDefaultFramerate = 30; +- +-// HRD parameters (ch. E.2.2 in spec). +-const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters. +-const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters. +- +-const int kDefaultQP = 26; +-// All Intel codecs can do at least 4.1. +-const int kDefaultLevelIDC = 41; +-const int kChromaFormatIDC = 1; // 4:2:0 +- +-// Arbitrarily chosen bitrate window size for rate control, in ms. +-const int kCPBWindowSizeMs = 1500; +- +-// UMA errors that the VaapiVideoEncodeAccelerator class reports. +-enum VAVEAEncoderFailure { +- VAAPI_ERROR = 0, +- VAVEA_ENCODER_FAILURES_MAX, +-}; +-} +- +-// Round |value| up to |alignment|, which must be a power of 2. +-static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) { +- // Check that |alignment| is a power of 2. +- DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); +- return ((value + (alignment - 1)) & ~(alignment - 1)); +-} +- +-static void ReportToUMA(VAVEAEncoderFailure failure) { +- UMA_HISTOGRAM_ENUMERATION("Media.VAVEA.EncoderFailure", failure, +- VAVEA_ENCODER_FAILURES_MAX + 1); +-} +- +-struct VaapiVideoEncodeAccelerator::InputFrameRef { +- InputFrameRef(const scoped_refptr& frame, bool force_keyframe) +- : frame(frame), force_keyframe(force_keyframe) {} +- const scoped_refptr frame; +- const bool force_keyframe; +-}; +- +-struct VaapiVideoEncodeAccelerator::BitstreamBufferRef { +- BitstreamBufferRef(int32_t id, std::unique_ptr shm) +- : id(id), shm(std::move(shm)) {} +- const int32_t id; +- const std::unique_ptr shm; +-}; +- +-VideoEncodeAccelerator::SupportedProfiles +-VaapiVideoEncodeAccelerator::GetSupportedProfiles() { +- return VaapiWrapper::GetSupportedEncodeProfiles(); +-} +- +-static unsigned int Log2OfPowerOf2(unsigned int x) { +- CHECK_GT(x, 0u); +- DCHECK_EQ(x & (x - 1), 0u); +- +- int log = 0; +- while (x > 1) { +- x >>= 1; +- ++log; +- } +- return log; +-} +- +-VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator() +- : profile_(VIDEO_CODEC_PROFILE_UNKNOWN), +- mb_width_(0), +- mb_height_(0), +- output_buffer_byte_size_(0), +- state_(kUninitialized), +- frame_num_(0), +- idr_pic_id_(0), +- bitrate_(0), +- framerate_(0), +- cpb_size_(0), +- encoding_parameters_changed_(false), +- encoder_thread_("VAVEAEncoderThread"), +- child_task_runner_(base::ThreadTaskRunnerHandle::Get()), +- weak_this_ptr_factory_(this) { +- VLOGF(2); +- weak_this_ = weak_this_ptr_factory_.GetWeakPtr(); +- max_ref_idx_l0_size_ = kMaxNumReferenceFrames; +- qp_ = kDefaultQP; +- idr_period_ = kIDRPeriod; +- i_period_ = kIPeriod; +- ip_period_ = kIPPeriod; +-} +- +-VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() { +- VLOGF(2); +- DCHECK(child_task_runner_->BelongsToCurrentThread()); +- DCHECK(!encoder_thread_.IsRunning()); +-} +- +-bool VaapiVideoEncodeAccelerator::Initialize( +- VideoPixelFormat format, +- const gfx::Size& input_visible_size, +- VideoCodecProfile output_profile, +- uint32_t initial_bitrate, +- Client* client) { +- DCHECK(child_task_runner_->BelongsToCurrentThread()); +- DCHECK(!encoder_thread_.IsRunning()); +- DCHECK_EQ(state_, kUninitialized); +- +- VLOGF(2) << "Initializing VAVEA, input_format: " +- << VideoPixelFormatToString(format) +- << ", input_visible_size: " << input_visible_size.ToString() +- << ", output_profile: " << GetProfileName(output_profile) +- << ", initial_bitrate: " << initial_bitrate; +- +- client_ptr_factory_.reset(new base::WeakPtrFactory(client)); +- client_ = client_ptr_factory_->GetWeakPtr(); +- +- const SupportedProfiles& profiles = GetSupportedProfiles(); +- auto profile = find_if(profiles.begin(), profiles.end(), +- [output_profile](const SupportedProfile& profile) { +- return profile.profile == output_profile; +- }); +- if (profile == profiles.end()) { +- VLOGF(1) << "Unsupported output profile " << GetProfileName(output_profile); +- return false; +- } +- if (input_visible_size.width() > profile->max_resolution.width() || +- input_visible_size.height() > profile->max_resolution.height()) { +- VLOGF(1) << "Input size too big: " << input_visible_size.ToString() +- << ", max supported size: " << profile->max_resolution.ToString(); +- return false; +- } +- +- if (format != PIXEL_FORMAT_I420) { +- VLOGF(1) << "Unsupported input format: " +- << VideoPixelFormatToString(format); +- return false; +- } +- +- profile_ = output_profile; +- visible_size_ = input_visible_size; +- // 4:2:0 format has to be 2-aligned. +- DCHECK_EQ(visible_size_.width() % 2, 0); +- DCHECK_EQ(visible_size_.height() % 2, 0); +- coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16), +- RoundUpToPowerOf2(visible_size_.height(), 16)); +- mb_width_ = coded_size_.width() / 16; +- mb_height_ = coded_size_.height() / 16; +- output_buffer_byte_size_ = coded_size_.GetArea() + kExtraOutputBufferSize; +- +- UpdateRates(initial_bitrate, kDefaultFramerate); +- +- vaapi_wrapper_ = +- VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile, +- base::Bind(&ReportToUMA, VAAPI_ERROR)); +- if (!vaapi_wrapper_.get()) { +- VLOGF(1) << "Failed initializing VAAPI for profile " +- << GetProfileName(output_profile); +- return false; +- } +- +- if (!encoder_thread_.Start()) { +- VLOGF(1) << "Failed to start encoder thread"; +- return false; +- } +- encoder_thread_task_runner_ = encoder_thread_.task_runner(); +- +- // Finish the remaining initialization on the encoder thread. +- encoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask, +- base::Unretained(this))); +- +- return true; +-} +- +-void VaapiVideoEncodeAccelerator::InitializeTask() { +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- DCHECK_EQ(state_, kUninitialized); +- VLOGF(2); +- +- va_surface_release_cb_ = BindToCurrentLoop( +- base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID, +- base::Unretained(this))); +- +- if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, coded_size_, +- kNumSurfaces, +- &available_va_surface_ids_)) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces"); +- return; +- } +- +- UpdateSPS(); +- GeneratePackedSPS(); +- +- UpdatePPS(); +- GeneratePackedPPS(); +- +- child_task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers, +- coded_size_, output_buffer_byte_size_)); +- +- SetState(kEncoding); +-} +- +-void VaapiVideoEncodeAccelerator::RecycleVASurfaceID( +- VASurfaceID va_surface_id) { +- DVLOGF(4) << "va_surface_id: " << va_surface_id; +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- +- available_va_surface_ids_.push_back(va_surface_id); +- EncodeFrameTask(); +-} +- +-void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) { +- current_pic_ = new H264Picture(); +- +- // If the current picture is an IDR picture, frame_num shall be equal to 0. +- if (force_keyframe) +- frame_num_ = 0; +- +- current_pic_->frame_num = frame_num_++; +- frame_num_ %= idr_period_; +- +- if (current_pic_->frame_num == 0) { +- current_pic_->idr = true; +- // H264 spec mandates idr_pic_id to differ between two consecutive IDRs. +- idr_pic_id_ ^= 1; +- ref_pic_list0_.clear(); +- } +- +- if (current_pic_->frame_num % i_period_ == 0) +- current_pic_->type = H264SliceHeader::kISlice; +- else +- current_pic_->type = H264SliceHeader::kPSlice; +- +- if (current_pic_->type != H264SliceHeader::kBSlice) +- current_pic_->ref = true; +- +- current_pic_->pic_order_cnt = current_pic_->frame_num * 2; +- current_pic_->top_field_order_cnt = current_pic_->pic_order_cnt; +- current_pic_->pic_order_cnt_lsb = current_pic_->pic_order_cnt; +- +- current_encode_job_->keyframe = current_pic_->idr; +- +- DVLOGF(4) << "Starting a new frame, type: " << current_pic_->type +- << (force_keyframe ? " (forced keyframe)" : "") +- << " frame_num: " << current_pic_->frame_num +- << " POC: " << current_pic_->pic_order_cnt; +-} +- +-void VaapiVideoEncodeAccelerator::EndFrame() { +- DCHECK(current_pic_); +- // Store the picture on the list of reference pictures and keep the list +- // below maximum size, dropping oldest references. +- if (current_pic_->ref) +- ref_pic_list0_.push_front(current_encode_job_->recon_surface); +- size_t max_num_ref_frames = +- base::checked_cast(current_sps_.max_num_ref_frames); +- while (ref_pic_list0_.size() > max_num_ref_frames) +- ref_pic_list0_.pop_back(); +- +- submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release())); +-} +- +-static void InitVAPicture(VAPictureH264* va_pic) { +- memset(va_pic, 0, sizeof(*va_pic)); +- va_pic->picture_id = VA_INVALID_ID; +- va_pic->flags = VA_PICTURE_H264_INVALID; +-} +- +-bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() { +- DCHECK(current_pic_); +- VAEncSequenceParameterBufferH264 seq_param; +- memset(&seq_param, 0, sizeof(seq_param)); +- +-#define SPS_TO_SP(a) seq_param.a = current_sps_.a; +- SPS_TO_SP(seq_parameter_set_id); +- SPS_TO_SP(level_idc); +- +- seq_param.intra_period = i_period_; +- seq_param.intra_idr_period = idr_period_; +- seq_param.ip_period = ip_period_; +- seq_param.bits_per_second = bitrate_; +- +- SPS_TO_SP(max_num_ref_frames); +- seq_param.picture_width_in_mbs = mb_width_; +- seq_param.picture_height_in_mbs = mb_height_; +- +-#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a; +- SPS_TO_SP_FS(chroma_format_idc); +- SPS_TO_SP_FS(frame_mbs_only_flag); +- SPS_TO_SP_FS(log2_max_frame_num_minus4); +- SPS_TO_SP_FS(pic_order_cnt_type); +- SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4); +-#undef SPS_TO_SP_FS +- +- SPS_TO_SP(bit_depth_luma_minus8); +- SPS_TO_SP(bit_depth_chroma_minus8); +- +- SPS_TO_SP(frame_cropping_flag); +- if (current_sps_.frame_cropping_flag) { +- SPS_TO_SP(frame_crop_left_offset); +- SPS_TO_SP(frame_crop_right_offset); +- SPS_TO_SP(frame_crop_top_offset); +- SPS_TO_SP(frame_crop_bottom_offset); +- } +- +- SPS_TO_SP(vui_parameters_present_flag); +-#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a; +- SPS_TO_SP_VF(timing_info_present_flag); +-#undef SPS_TO_SP_VF +- SPS_TO_SP(num_units_in_tick); +- SPS_TO_SP(time_scale); +-#undef SPS_TO_SP +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType, +- sizeof(seq_param), &seq_param)) +- return false; +- +- VAEncPictureParameterBufferH264 pic_param; +- memset(&pic_param, 0, sizeof(pic_param)); +- +- pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id(); +- pic_param.CurrPic.TopFieldOrderCnt = current_pic_->top_field_order_cnt; +- pic_param.CurrPic.BottomFieldOrderCnt = current_pic_->bottom_field_order_cnt; +- pic_param.CurrPic.flags = 0; +- +- for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i) +- InitVAPicture(&pic_param.ReferenceFrames[i]); +- +- DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames)); +- RefPicList::const_iterator iter = ref_pic_list0_.begin(); +- for (size_t i = 0; +- i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end(); +- ++iter, ++i) { +- pic_param.ReferenceFrames[i].picture_id = (*iter)->id(); +- pic_param.ReferenceFrames[i].flags = 0; +- } +- +- pic_param.coded_buf = current_encode_job_->coded_buffer; +- pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; +- pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id; +- pic_param.frame_num = current_pic_->frame_num; +- pic_param.pic_init_qp = qp_; +- pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1; +- pic_param.pic_fields.bits.idr_pic_flag = current_pic_->idr; +- pic_param.pic_fields.bits.reference_pic_flag = current_pic_->ref; +-#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a; +- PPS_TO_PP_PF(entropy_coding_mode_flag); +- PPS_TO_PP_PF(transform_8x8_mode_flag); +- PPS_TO_PP_PF(deblocking_filter_control_present_flag); +-#undef PPS_TO_PP_PF +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, +- sizeof(pic_param), &pic_param)) +- return false; +- +- VAEncSliceParameterBufferH264 slice_param; +- memset(&slice_param, 0, sizeof(slice_param)); +- +- slice_param.num_macroblocks = mb_width_ * mb_height_; +- slice_param.macroblock_info = VA_INVALID_ID; +- slice_param.slice_type = current_pic_->type; +- slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; +- slice_param.idr_pic_id = idr_pic_id_; +- slice_param.pic_order_cnt_lsb = current_pic_->pic_order_cnt_lsb; +- slice_param.num_ref_idx_active_override_flag = true; +- +- for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) +- InitVAPicture(&slice_param.RefPicList0[i]); +- +- for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i) +- InitVAPicture(&slice_param.RefPicList1[i]); +- +- DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0)); +- iter = ref_pic_list0_.begin(); +- for (size_t i = 0; +- i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end(); +- ++iter, ++i) { +- InitVAPicture(&slice_param.RefPicList0[i]); +- slice_param.RefPicList0[i].picture_id = (*iter)->id(); +- slice_param.RefPicList0[i].flags = 0; +- } +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, +- sizeof(slice_param), &slice_param)) +- return false; +- +- VAEncMiscParameterRateControl rate_control_param; +- memset(&rate_control_param, 0, sizeof(rate_control_param)); +- rate_control_param.bits_per_second = bitrate_; +- rate_control_param.target_percentage = 90; +- rate_control_param.window_size = kCPBWindowSizeMs; +- rate_control_param.initial_qp = qp_; +- rate_control_param.rc_flags.bits.disable_frame_skip = true; +- +- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( +- VAEncMiscParameterTypeRateControl, sizeof(rate_control_param), +- &rate_control_param)) +- return false; +- +- VAEncMiscParameterFrameRate framerate_param; +- memset(&framerate_param, 0, sizeof(framerate_param)); +- framerate_param.framerate = framerate_; +- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( +- VAEncMiscParameterTypeFrameRate, sizeof(framerate_param), +- &framerate_param)) +- return false; +- +- VAEncMiscParameterHRD hrd_param; +- memset(&hrd_param, 0, sizeof(hrd_param)); +- hrd_param.buffer_size = cpb_size_; +- hrd_param.initial_buffer_fullness = cpb_size_ / 2; +- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( +- VAEncMiscParameterTypeHRD, sizeof(hrd_param), &hrd_param)) +- return false; +- +- return true; +-} +- +-bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() { +- DCHECK(current_pic_); +- if (current_pic_->type != H264SliceHeader::kISlice) +- return true; +- +- // Submit SPS. +- VAEncPackedHeaderParameterBuffer par_buffer; +- memset(&par_buffer, 0, sizeof(par_buffer)); +- par_buffer.type = VAEncPackedHeaderSequence; +- par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8; +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, +- sizeof(par_buffer), &par_buffer)) +- return false; +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, +- packed_sps_.BytesInBuffer(), +- packed_sps_.data())) +- return false; +- +- // Submit PPS. +- memset(&par_buffer, 0, sizeof(par_buffer)); +- par_buffer.type = VAEncPackedHeaderPicture; +- par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8; +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, +- sizeof(par_buffer), &par_buffer)) +- return false; +- +- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, +- packed_pps_.BytesInBuffer(), +- packed_pps_.data())) +- return false; +- +- return true; +-} +- +-bool VaapiVideoEncodeAccelerator::ExecuteEncode() { +- DCHECK(current_pic_); +- DVLOGF(4) << "Encoding frame_num: " << current_pic_->frame_num; +- return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( +- current_encode_job_->input_surface->id()); +-} +- +-bool VaapiVideoEncodeAccelerator::UploadFrame( +- const scoped_refptr& frame) { +- return vaapi_wrapper_->UploadVideoFrameToSurface( +- frame, current_encode_job_->input_surface->id()); +-} +- +-void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() { +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- +- if (state_ != kEncoding) +- return; +- +- while (!submitted_encode_jobs_.empty()) { +- linked_ptr encode_job = submitted_encode_jobs_.front(); +- // An null job indicates a flush command. +- if (encode_job == nullptr) { +- submitted_encode_jobs_.pop(); +- DVLOGF(2) << "FlushDone"; +- DCHECK(flush_callback_); +- child_task_runner_->PostTask( +- FROM_HERE, base::BindOnce(std::move(flush_callback_), true)); +- continue; +- } +- +- if (available_bitstream_buffers_.empty()) +- break; +- auto buffer = available_bitstream_buffers_.front(); +- +- available_bitstream_buffers_.pop(); +- submitted_encode_jobs_.pop(); +- +- uint8_t* target_data = reinterpret_cast(buffer->shm->memory()); +- +- size_t data_size = 0; +- if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer( +- encode_job->coded_buffer, encode_job->input_surface->id(), +- target_data, buffer->shm->size(), &data_size)) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer"); +- return; +- } +- +- DVLOGF(4) << "Returning bitstream buffer " +- << (encode_job->keyframe ? "(keyframe)" : "") +- << " id: " << buffer->id << " size: " << data_size; +- +- child_task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&Client::BitstreamBufferReady, client_, buffer->id, +- data_size, encode_job->keyframe, encode_job->timestamp)); +- break; +- } +-} +- +-void VaapiVideoEncodeAccelerator::Encode(const scoped_refptr& frame, +- bool force_keyframe) { +- DVLOGF(4) << "Frame timestamp: " << frame->timestamp().InMilliseconds() +- << " force_keyframe: " << force_keyframe; +- DCHECK(child_task_runner_->BelongsToCurrentThread()); +- +- encoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask, +- base::Unretained(this), frame, force_keyframe)); +-} +- +-bool VaapiVideoEncodeAccelerator::PrepareNextJob(base::TimeDelta timestamp) { +- if (available_va_surface_ids_.size() < kMinSurfacesToEncode) +- return false; +- +- DCHECK(!current_encode_job_); +- current_encode_job_.reset(new EncodeJob()); +- +- if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_, +- ¤t_encode_job_->coded_buffer)) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer"); +- return false; +- } +- +- current_encode_job_->timestamp = timestamp; +- +- current_encode_job_->input_surface = new VASurface( +- available_va_surface_ids_.back(), coded_size_, +- vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); +- available_va_surface_ids_.pop_back(); +- +- current_encode_job_->recon_surface = new VASurface( +- available_va_surface_ids_.back(), coded_size_, +- vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); +- available_va_surface_ids_.pop_back(); +- +- // Reference surfaces are needed until the job is done, but they get +- // removed from ref_pic_list0_ when it's full at the end of job submission. +- // Keep refs to them along with the job and only release after sync. +- current_encode_job_->reference_surfaces = ref_pic_list0_; +- +- return true; +-} +- +-void VaapiVideoEncodeAccelerator::EncodeTask( +- const scoped_refptr& frame, +- bool force_keyframe) { +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- DCHECK_NE(state_, kUninitialized); +- +- encoder_input_queue_.push( +- make_linked_ptr(new InputFrameRef(frame, force_keyframe))); +- EncodeFrameTask(); +-} +- +-void VaapiVideoEncodeAccelerator::EncodeFrameTask() { +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- +- if (state_ != kEncoding || encoder_input_queue_.empty()) +- return; +- +- if (!PrepareNextJob(encoder_input_queue_.front()->frame->timestamp())) { +- DVLOGF(4) << "Not ready for next frame yet"; +- return; +- } +- +- linked_ptr frame_ref = encoder_input_queue_.front(); +- encoder_input_queue_.pop(); +- +- if (!UploadFrame(frame_ref->frame)) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW."); +- return; +- } +- +- BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_); +- encoding_parameters_changed_ = false; +- +- if (!SubmitFrameParameters()) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters."); +- return; +- } +- +- if (!SubmitHeadersIfNeeded()) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers."); +- return; +- } +- +- if (!ExecuteEncode()) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW."); +- return; +- } +- +- EndFrame(); +- TryToReturnBitstreamBuffer(); +-} +- +-void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer( +- const BitstreamBuffer& buffer) { +- DVLOGF(4) << "id: " << buffer.id(); +- DCHECK(child_task_runner_->BelongsToCurrentThread()); +- +- if (buffer.size() < output_buffer_byte_size_) { +- NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small"); +- return; +- } +- +- std::unique_ptr shm( +- new SharedMemoryRegion(buffer, false)); +- if (!shm->Map()) { +- NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory."); +- return; +- } +- +- std::unique_ptr buffer_ref( +- new BitstreamBufferRef(buffer.id(), std::move(shm))); +- +- encoder_thread_task_runner_->PostTask( +- FROM_HERE, +- base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask, +- base::Unretained(this), base::Passed(&buffer_ref))); +-} +- +-void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask( +- std::unique_ptr buffer_ref) { +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- DCHECK_NE(state_, kUninitialized); +- +- available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release())); +- TryToReturnBitstreamBuffer(); +-} +- +-void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange( +- uint32_t bitrate, +- uint32_t framerate) { +- VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; +- DCHECK(child_task_runner_->BelongsToCurrentThread()); +- +- encoder_thread_task_runner_->PostTask( +- FROM_HERE, +- base::Bind( +- &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask, +- base::Unretained(this), bitrate, framerate)); +-} +- +-void VaapiVideoEncodeAccelerator::UpdateRates(uint32_t bitrate, +- uint32_t framerate) { +- if (encoder_thread_.IsRunning()) +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- DCHECK_NE(bitrate, 0u); +- DCHECK_NE(framerate, 0u); +- bitrate_ = bitrate; +- framerate_ = framerate; +- cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000; +-} +- +-void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask( +- uint32_t bitrate, +- uint32_t framerate) { +- VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- DCHECK_NE(state_, kUninitialized); +- +- // This is a workaround to zero being temporarily, as part of the initial +- // setup, provided by the webrtc video encode and a zero bitrate and +- // framerate not being accepted by VAAPI +- // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps +- // it could be pulled up to RTCVideoEncoder +- if (bitrate < 1) +- bitrate = 1; +- if (framerate < 1) +- framerate = 1; +- +- if (bitrate_ == bitrate && framerate_ == framerate) +- return; +- +- UpdateRates(bitrate, framerate); +- +- UpdateSPS(); +- GeneratePackedSPS(); +- +- // Submit new parameters along with next frame that will be processed. +- encoding_parameters_changed_ = true; +-} +- +-void VaapiVideoEncodeAccelerator::Flush(FlushCallback flush_callback) { +- DVLOGF(2); +- DCHECK(child_task_runner_->BelongsToCurrentThread()); +- if (flush_callback_) { +- NOTIFY_ERROR(kIllegalStateError, "There is a pending flush"); +- std::move(flush_callback).Run(false); +- return; +- } +- flush_callback_ = std::move(flush_callback); +- encoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::FlushTask, +- base::Unretained(this))); +-} +- +-void VaapiVideoEncodeAccelerator::FlushTask() { +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- +- // Insert an null job to indicate a flush command. +- submitted_encode_jobs_.push(linked_ptr(nullptr)); +- TryToReturnBitstreamBuffer(); +-} +- +-void VaapiVideoEncodeAccelerator::Destroy() { +- DCHECK(child_task_runner_->BelongsToCurrentThread()); +- +- // Can't call client anymore after Destroy() returns. +- client_ptr_factory_.reset(); +- weak_this_ptr_factory_.InvalidateWeakPtrs(); +- +- // Early-exit encoder tasks if they are running and join the thread. +- if (encoder_thread_.IsRunning()) { +- encoder_thread_.task_runner()->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask, +- base::Unretained(this))); +- encoder_thread_.Stop(); +- } +- +- if (flush_callback_) +- std::move(flush_callback_).Run(false); +- +- delete this; +-} +- +-void VaapiVideoEncodeAccelerator::DestroyTask() { +- VLOGF(2); +- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); +- SetState(kError); +-} +- +-void VaapiVideoEncodeAccelerator::UpdateSPS() { +- memset(¤t_sps_, 0, sizeof(H264SPS)); +- +- // Spec A.2 and A.3. +- switch (profile_) { +- case H264PROFILE_BASELINE: +- // Due to crbug.com/345569, we don't distinguish between constrained +- // and non-constrained baseline profiles. Since many codecs can't do +- // non-constrained, and constrained is usually what we mean (and it's a +- // subset of non-constrained), default to it. +- current_sps_.profile_idc = H264SPS::kProfileIDCBaseline; +- current_sps_.constraint_set0_flag = true; +- break; +- case H264PROFILE_MAIN: +- current_sps_.profile_idc = H264SPS::kProfileIDCMain; +- current_sps_.constraint_set1_flag = true; +- break; +- case H264PROFILE_HIGH: +- current_sps_.profile_idc = H264SPS::kProfileIDCHigh; +- break; +- default: +- NOTIMPLEMENTED(); +- return; +- } +- +- current_sps_.level_idc = kDefaultLevelIDC; +- current_sps_.seq_parameter_set_id = 0; +- current_sps_.chroma_format_idc = kChromaFormatIDC; +- +- DCHECK_GE(idr_period_, 1u << 4); +- current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4; +- current_sps_.pic_order_cnt_type = 0; +- current_sps_.log2_max_pic_order_cnt_lsb_minus4 = +- Log2OfPowerOf2(idr_period_ * 2) - 4; +- current_sps_.max_num_ref_frames = max_ref_idx_l0_size_; +- +- current_sps_.frame_mbs_only_flag = true; +- +- DCHECK_GT(mb_width_, 0u); +- DCHECK_GT(mb_height_, 0u); +- current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1; +- DCHECK(current_sps_.frame_mbs_only_flag); +- current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1; +- +- if (visible_size_ != coded_size_) { +- // Visible size differs from coded size, fill crop information. +- current_sps_.frame_cropping_flag = true; +- DCHECK(!current_sps_.separate_colour_plane_flag); +- // Spec table 6-1. Only 4:2:0 for now. +- DCHECK_EQ(current_sps_.chroma_format_idc, 1); +- // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0. +- const unsigned int crop_unit_x = 2; +- const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag); +- current_sps_.frame_crop_left_offset = 0; +- current_sps_.frame_crop_right_offset = +- (coded_size_.width() - visible_size_.width()) / crop_unit_x; +- current_sps_.frame_crop_top_offset = 0; +- current_sps_.frame_crop_bottom_offset = +- (coded_size_.height() - visible_size_.height()) / crop_unit_y; +- } +- +- current_sps_.vui_parameters_present_flag = true; +- current_sps_.timing_info_present_flag = true; +- current_sps_.num_units_in_tick = 1; +- current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec. +- current_sps_.fixed_frame_rate_flag = true; +- +- current_sps_.nal_hrd_parameters_present_flag = true; +- // H.264 spec ch. E.2.2. +- current_sps_.cpb_cnt_minus1 = 0; +- current_sps_.bit_rate_scale = kBitRateScale; +- current_sps_.cpb_size_scale = kCPBSizeScale; +- current_sps_.bit_rate_value_minus1[0] = +- (bitrate_ >> (kBitRateScale + H264SPS::kBitRateScaleConstantTerm)) - 1; +- current_sps_.cpb_size_value_minus1[0] = +- (cpb_size_ >> (kCPBSizeScale + H264SPS::kCPBSizeScaleConstantTerm)) - 1; +- current_sps_.cbr_flag[0] = true; +- current_sps_.initial_cpb_removal_delay_length_minus_1 = +- H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; +- current_sps_.cpb_removal_delay_length_minus1 = +- H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; +- current_sps_.dpb_output_delay_length_minus1 = +- H264SPS::kDefaultDPBOutputDelayLength - 1; +- current_sps_.time_offset_length = H264SPS::kDefaultTimeOffsetLength; +- current_sps_.low_delay_hrd_flag = false; +-} +- +-void VaapiVideoEncodeAccelerator::GeneratePackedSPS() { +- packed_sps_.Reset(); +- +- packed_sps_.BeginNALU(H264NALU::kSPS, 3); +- +- packed_sps_.AppendBits(8, current_sps_.profile_idc); +- packed_sps_.AppendBool(current_sps_.constraint_set0_flag); +- packed_sps_.AppendBool(current_sps_.constraint_set1_flag); +- packed_sps_.AppendBool(current_sps_.constraint_set2_flag); +- packed_sps_.AppendBool(current_sps_.constraint_set3_flag); +- packed_sps_.AppendBool(current_sps_.constraint_set4_flag); +- packed_sps_.AppendBool(current_sps_.constraint_set5_flag); +- packed_sps_.AppendBits(2, 0); // reserved_zero_2bits +- packed_sps_.AppendBits(8, current_sps_.level_idc); +- packed_sps_.AppendUE(current_sps_.seq_parameter_set_id); +- +- if (current_sps_.profile_idc == H264SPS::kProfileIDCHigh) { +- packed_sps_.AppendUE(current_sps_.chroma_format_idc); +- if (current_sps_.chroma_format_idc == 3) +- packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag); +- packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8); +- packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8); +- packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag); +- packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag); +- CHECK(!current_sps_.seq_scaling_matrix_present_flag); +- } +- +- packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4); +- packed_sps_.AppendUE(current_sps_.pic_order_cnt_type); +- if (current_sps_.pic_order_cnt_type == 0) +- packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4); +- else if (current_sps_.pic_order_cnt_type == 1) { +- CHECK(1); +- } +- +- packed_sps_.AppendUE(current_sps_.max_num_ref_frames); +- packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag); +- packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1); +- packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1); +- +- packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag); +- if (!current_sps_.frame_mbs_only_flag) +- packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag); +- +- packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag); +- +- packed_sps_.AppendBool(current_sps_.frame_cropping_flag); +- if (current_sps_.frame_cropping_flag) { +- packed_sps_.AppendUE(current_sps_.frame_crop_left_offset); +- packed_sps_.AppendUE(current_sps_.frame_crop_right_offset); +- packed_sps_.AppendUE(current_sps_.frame_crop_top_offset); +- packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset); +- } +- +- packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag); +- if (current_sps_.vui_parameters_present_flag) { +- packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag +- packed_sps_.AppendBool(false); // overscan_info_present_flag +- packed_sps_.AppendBool(false); // video_signal_type_present_flag +- packed_sps_.AppendBool(false); // chroma_loc_info_present_flag +- +- packed_sps_.AppendBool(current_sps_.timing_info_present_flag); +- if (current_sps_.timing_info_present_flag) { +- packed_sps_.AppendBits(32, current_sps_.num_units_in_tick); +- packed_sps_.AppendBits(32, current_sps_.time_scale); +- packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag); +- } +- +- packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag); +- if (current_sps_.nal_hrd_parameters_present_flag) { +- packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1); +- packed_sps_.AppendBits(4, current_sps_.bit_rate_scale); +- packed_sps_.AppendBits(4, current_sps_.cpb_size_scale); +- CHECK_LT(base::checked_cast(current_sps_.cpb_cnt_minus1), +- arraysize(current_sps_.bit_rate_value_minus1)); +- for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) { +- packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]); +- packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]); +- packed_sps_.AppendBool(current_sps_.cbr_flag[i]); +- } +- packed_sps_.AppendBits( +- 5, current_sps_.initial_cpb_removal_delay_length_minus_1); +- packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1); +- packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1); +- packed_sps_.AppendBits(5, current_sps_.time_offset_length); +- } +- +- packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag +- if (current_sps_.nal_hrd_parameters_present_flag) +- packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag); +- +- packed_sps_.AppendBool(false); // pic_struct_present_flag +- packed_sps_.AppendBool(true); // bitstream_restriction_flag +- +- packed_sps_.AppendBool(false); // motion_vectors_over_pic_boundaries_flag +- packed_sps_.AppendUE(2); // max_bytes_per_pic_denom +- packed_sps_.AppendUE(1); // max_bits_per_mb_denom +- packed_sps_.AppendUE(16); // log2_max_mv_length_horizontal +- packed_sps_.AppendUE(16); // log2_max_mv_length_vertical +- +- // Explicitly set max_num_reorder_frames to 0 to allow the decoder to +- // output pictures early. +- packed_sps_.AppendUE(0); // max_num_reorder_frames +- +- // The value of max_dec_frame_buffering shall be greater than or equal to +- // max_num_ref_frames. +- const unsigned int max_dec_frame_buffering = +- current_sps_.max_num_ref_frames; +- packed_sps_.AppendUE(max_dec_frame_buffering); +- } +- +- packed_sps_.FinishNALU(); +-} +- +-void VaapiVideoEncodeAccelerator::UpdatePPS() { +- memset(¤t_pps_, 0, sizeof(H264PPS)); +- +- current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id; +- current_pps_.pic_parameter_set_id = 0; +- +- current_pps_.entropy_coding_mode_flag = +- current_sps_.profile_idc >= H264SPS::kProfileIDCMain; +- +- CHECK_GT(max_ref_idx_l0_size_, 0u); +- current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1; +- current_pps_.num_ref_idx_l1_default_active_minus1 = 0; +- DCHECK_LE(qp_, 51u); +- current_pps_.pic_init_qp_minus26 = qp_ - 26; +- current_pps_.deblocking_filter_control_present_flag = true; +- current_pps_.transform_8x8_mode_flag = +- (current_sps_.profile_idc == H264SPS::kProfileIDCHigh); +-} +- +-void VaapiVideoEncodeAccelerator::GeneratePackedPPS() { +- packed_pps_.Reset(); +- +- packed_pps_.BeginNALU(H264NALU::kPPS, 3); +- +- packed_pps_.AppendUE(current_pps_.pic_parameter_set_id); +- packed_pps_.AppendUE(current_pps_.seq_parameter_set_id); +- packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag); +- packed_pps_.AppendBool( +- current_pps_.bottom_field_pic_order_in_frame_present_flag); +- CHECK_EQ(current_pps_.num_slice_groups_minus1, 0); +- packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1); +- +- packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1); +- packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1); +- +- packed_pps_.AppendBool(current_pps_.weighted_pred_flag); +- packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc); +- +- packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26); +- packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26); +- packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset); +- +- packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag); +- packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag); +- packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag); +- +- packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag); +- packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag); +- DCHECK(!current_pps_.pic_scaling_matrix_present_flag); +- packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset); +- +- packed_pps_.FinishNALU(); +-} +- +-void VaapiVideoEncodeAccelerator::SetState(State state) { +- // Only touch state on encoder thread, unless it's not running. +- if (encoder_thread_.IsRunning() && +- !encoder_thread_task_runner_->BelongsToCurrentThread()) { +- encoder_thread_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::SetState, +- base::Unretained(this), state)); +- return; +- } +- +- VLOGF(2) << "setting state to: " << state; +- state_ = state; +-} +- +-void VaapiVideoEncodeAccelerator::NotifyError(Error error) { +- if (!child_task_runner_->BelongsToCurrentThread()) { +- child_task_runner_->PostTask( +- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::NotifyError, +- weak_this_, error)); +- return; +- } +- +- if (client_) { +- client_->NotifyError(error); +- client_ptr_factory_.reset(); +- } +-} +- +-VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob() +- : coded_buffer(VA_INVALID_ID), keyframe(false) {} +- +-VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {} +- +-} // namespace media +--- a/media/gpu/vaapi_video_encode_accelerator.h ++++ /dev/null +@@ -1,275 +0,0 @@ +-// Copyright 2014 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#ifndef MEDIA_GPU_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ +-#define MEDIA_GPU_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ +- +-#include +-#include +- +-#include +-#include +- +-#include "base/containers/queue.h" +-#include "base/macros.h" +-#include "base/memory/linked_ptr.h" +-#include "base/threading/thread.h" +-#include "media/filters/h264_bitstream_buffer.h" +-#include "media/gpu/h264_dpb.h" +-#include "media/gpu/media_gpu_export.h" +-#include "media/gpu/va_surface.h" +-#include "media/gpu/vaapi_wrapper.h" +-#include "media/video/video_encode_accelerator.h" +- +-namespace media { +- +-// A VideoEncodeAccelerator implementation that uses VA-API +-// (http://www.freedesktop.org/wiki/Software/vaapi) for HW-accelerated +-// video encode. +-class MEDIA_GPU_EXPORT VaapiVideoEncodeAccelerator +- : public VideoEncodeAccelerator { +- public: +- VaapiVideoEncodeAccelerator(); +- ~VaapiVideoEncodeAccelerator() override; +- +- // VideoEncodeAccelerator implementation. +- VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles() override; +- bool Initialize(VideoPixelFormat format, +- const gfx::Size& input_visible_size, +- VideoCodecProfile output_profile, +- uint32_t initial_bitrate, +- Client* client) override; +- void Encode(const scoped_refptr& frame, +- bool force_keyframe) override; +- void UseOutputBitstreamBuffer(const BitstreamBuffer& buffer) override; +- void RequestEncodingParametersChange(uint32_t bitrate, +- uint32_t framerate) override; +- void Destroy() override; +- void Flush(FlushCallback flush_callback) override; +- +- private: +- // Reference picture list. +- typedef std::list> RefPicList; +- +- // Encode job for one frame. Created when an input frame is awaiting and +- // enough resources are available to proceed. Once the job is prepared and +- // submitted to the hardware, it awaits on the submitted_encode_jobs_ queue +- // for an output bitstream buffer to become available. Once one is ready, +- // the encoded bytes are downloaded to it and job resources are released +- // and become available for reuse. +- struct EncodeJob { +- // Input surface for video frame data. +- scoped_refptr input_surface; +- // Surface for a reconstructed picture, which is used for reference +- // for subsequent frames. +- scoped_refptr recon_surface; +- // Buffer that will contain output bitstream for this frame. +- VABufferID coded_buffer; +- // Reference surfaces required to encode this picture. We keep references +- // to them here, because we may discard some of them from ref_pic_list* +- // before the HW job is done. +- RefPicList reference_surfaces; +- // True if this job will produce a keyframe. Used to report +- // to BitstreamBufferReady(). +- bool keyframe; +- // Source timestamp. +- base::TimeDelta timestamp; +- +- EncodeJob(); +- ~EncodeJob(); +- }; +- +- // Encoder state. +- enum State { +- kUninitialized, +- kEncoding, +- kError, +- }; +- +- // Holds input frames coming from the client ready to be encoded. +- struct InputFrameRef; +- // Holds output buffers coming from the client ready to be filled. +- struct BitstreamBufferRef; +- +- // Tasks for each of the VEA interface calls to be executed on the +- // encoder thread. +- void InitializeTask(); +- void EncodeTask(const scoped_refptr& frame, bool force_keyframe); +- void UseOutputBitstreamBufferTask( +- std::unique_ptr buffer_ref); +- void RequestEncodingParametersChangeTask(uint32_t bitrate, +- uint32_t framerate); +- void DestroyTask(); +- void FlushTask(); +- +- // Prepare and schedule an encode job if we have an input to encode +- // and enough resources to proceed. +- void EncodeFrameTask(); +- +- // Fill current_sps_/current_pps_ with current values. +- void UpdateSPS(); +- void UpdatePPS(); +- void UpdateRates(uint32_t bitrate, uint32_t framerate); +- +- // Generate packed SPS and PPS in packed_sps_/packed_pps_, using +- // values in current_sps_/current_pps_. +- void GeneratePackedSPS(); +- void GeneratePackedPPS(); +- +- // Check if we have sufficient resources for a new encode job, claim them and +- // fill current_encode_job_ with them. +- // Return false if we cannot start a new job yet, true otherwise. +- bool PrepareNextJob(base::TimeDelta timestamp); +- +- // Begin a new frame, making it a keyframe if |force_keyframe| is true, +- // updating current_pic_. +- void BeginFrame(bool force_keyframe); +- +- // End current frame, updating reference picture lists and storing current +- // job in the jobs awaiting completion on submitted_encode_jobs_. +- void EndFrame(); +- +- // Submit parameters for the current frame to the hardware. +- bool SubmitFrameParameters(); +- // Submit keyframe headers to the hardware if the current frame is a keyframe. +- bool SubmitHeadersIfNeeded(); +- +- // Upload image data from |frame| to the input surface for current job. +- bool UploadFrame(const scoped_refptr& frame); +- +- // Execute encode in hardware. This does not block and will return before +- // the job is finished. +- bool ExecuteEncode(); +- +- // Callback that returns a no longer used VASurfaceID to +- // available_va_surface_ids_ for reuse. +- void RecycleVASurfaceID(VASurfaceID va_surface_id); +- +- // Tries to return a bitstream buffer if both a submitted job awaits to +- // be completed and we have bitstream buffers from the client available +- // to download the encoded data to. +- void TryToReturnBitstreamBuffer(); +- +- // Puts the encoder into en error state and notifies client about the error. +- void NotifyError(Error error); +- +- // Sets the encoder state on the correct thread. +- void SetState(State state); +- +- // VaapiWrapper is the owner of all HW resources (surfaces and buffers) +- // and will free them on destruction. +- scoped_refptr vaapi_wrapper_; +- +- // Input profile and sizes. +- VideoCodecProfile profile_; +- gfx::Size visible_size_; +- gfx::Size coded_size_; // Macroblock-aligned. +- // Width/height in macroblocks. +- unsigned int mb_width_; +- unsigned int mb_height_; +- +- // Maximum size of the reference list 0. +- unsigned int max_ref_idx_l0_size_; +- +- // Initial QP. +- unsigned int qp_; +- +- // IDR frame period. +- unsigned int idr_period_; +- // I frame period. +- unsigned int i_period_; +- // IP period, i.e. how often do we need to have either an I or a P frame in +- // the stream. Period of 1 means we can have no B frames. +- unsigned int ip_period_; +- +- // Size in bytes required for input bitstream buffers. +- size_t output_buffer_byte_size_; +- +- // All of the members below must be accessed on the encoder_thread_, +- // while it is running. +- +- // Encoder state. Encode tasks will only run in kEncoding state. +- State state_; +- +- // frame_num to be used for the next frame. +- unsigned int frame_num_; +- // idr_pic_id to be used for the next frame. +- unsigned int idr_pic_id_; +- +- // Current bitrate in bps. +- unsigned int bitrate_; +- // Current fps. +- unsigned int framerate_; +- // CPB size in bits, i.e. bitrate in kbps * window size in ms/1000. +- unsigned int cpb_size_; +- // True if the parameters have changed and we need to submit a keyframe +- // with updated parameters. +- bool encoding_parameters_changed_; +- +- // Job currently being prepared for encode. +- std::unique_ptr current_encode_job_; +- +- // Current SPS, PPS and their packed versions. Packed versions are their NALUs +- // in AnnexB format *without* emulation prevention three-byte sequences +- // (those will be added by the driver). +- H264SPS current_sps_; +- H264BitstreamBuffer packed_sps_; +- H264PPS current_pps_; +- H264BitstreamBuffer packed_pps_; +- +- // Picture currently being prepared for encode. +- scoped_refptr current_pic_; +- +- // VA surfaces available for reuse. +- std::vector available_va_surface_ids_; +- +- // VA buffers for coded frames. +- std::vector available_va_buffer_ids_; +- +- // Currently active reference surfaces. +- RefPicList ref_pic_list0_; +- +- // Callback via which finished VA surfaces are returned to us. +- VASurface::ReleaseCB va_surface_release_cb_; +- +- // VideoFrames passed from the client, waiting to be encoded. +- base::queue> encoder_input_queue_; +- +- // BitstreamBuffers mapped, ready to be filled. +- base::queue> available_bitstream_buffers_; +- +- // Jobs submitted for encode, awaiting bitstream buffers to become available. +- // A pending flush command, indicated by a null job, will be also put in the +- // queue. +- base::queue> submitted_encode_jobs_; +- +- // Encoder thread. All tasks are executed on it. +- base::Thread encoder_thread_; +- scoped_refptr encoder_thread_task_runner_; +- +- const scoped_refptr child_task_runner_; +- +- // To expose client callbacks from VideoEncodeAccelerator. +- // NOTE: all calls to these objects *MUST* be executed on +- // child_task_runner_. +- std::unique_ptr> client_ptr_factory_; +- base::WeakPtr client_; +- +- // WeakPtr to post from the encoder thread back to the ChildThread, as it may +- // outlive this. Posting from the ChildThread using base::Unretained(this) +- // to the encoder thread is safe, because |this| always outlives the encoder +- // thread (it's a member of this class). +- base::WeakPtr weak_this_; +- +- // The completion callback of the Flush() function. +- FlushCallback flush_callback_; +- +- base::WeakPtrFactory weak_this_ptr_factory_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiVideoEncodeAccelerator); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ +--- a/media/gpu/vaapi_wrapper.cc ++++ /dev/null +@@ -1,1372 +0,0 @@ +-// Copyright 2013 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +- +-#include "media/gpu/vaapi_wrapper.h" +- +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-#include "base/bind.h" +-#include "base/callback_helpers.h" +-#include "base/environment.h" +-#include "base/logging.h" +-#include "base/macros.h" +-#include "base/numerics/safe_conversions.h" +-#include "base/stl_util.h" +-#include "base/sys_info.h" +-#include "build/build_config.h" +- +-// Auto-generated for dlopen libva libraries +-#include "media/gpu/vaapi/va_stubs.h" +- +-#include "media/gpu/vaapi/vaapi_picture.h" +-#include "third_party/libyuv/include/libyuv.h" +-#include "ui/gfx/buffer_format_util.h" +-#include "ui/gfx/native_pixmap.h" +-#include "ui/gl/gl_bindings.h" +-#include "ui/gl/gl_implementation.h" +- +-#if defined(USE_X11) +-#include +-#include "ui/gfx/x/x11_types.h" // nogncheck +-#endif +- +-#if defined(USE_OZONE) +-#include "ui/ozone/public/ozone_platform.h" +-#include "ui/ozone/public/surface_factory_ozone.h" +-#endif +- +-using media_gpu_vaapi::kModuleVa; +-using media_gpu_vaapi::kModuleVa_drm; +-#if defined(USE_X11) +-using media_gpu_vaapi::kModuleVa_x11; +-#endif +-using media_gpu_vaapi::InitializeStubs; +-using media_gpu_vaapi::StubPathMap; +- +-#define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ +- do { \ +- LOG(ERROR) << err_msg << " VA error: " << vaErrorStr(va_error); \ +- report_error_to_uma_cb_.Run(); \ +- } while (0) +- +-#define VA_LOG_ON_ERROR(va_error, err_msg) \ +- do { \ +- if ((va_error) != VA_STATUS_SUCCESS) \ +- LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ +- } while (0) +- +-#define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ +- do { \ +- if ((va_error) != VA_STATUS_SUCCESS) { \ +- LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ +- return (ret); \ +- } \ +- } while (0) +- +-namespace { +- +-uint32_t BufferFormatToVAFourCC(gfx::BufferFormat fmt) { +- switch (fmt) { +- case gfx::BufferFormat::BGRX_8888: +- return VA_FOURCC_BGRX; +- case gfx::BufferFormat::BGRA_8888: +- return VA_FOURCC_BGRA; +- case gfx::BufferFormat::RGBX_8888: +- return VA_FOURCC_RGBX; +- case gfx::BufferFormat::UYVY_422: +- return VA_FOURCC_UYVY; +- case gfx::BufferFormat::YVU_420: +- return VA_FOURCC_YV12; +- default: +- NOTREACHED(); +- return 0; +- } +-} +- +-uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt) { +- switch (fmt) { +- case gfx::BufferFormat::UYVY_422: +- return VA_RT_FORMAT_YUV422; +- case gfx::BufferFormat::BGRX_8888: +- case gfx::BufferFormat::BGRA_8888: +- case gfx::BufferFormat::RGBX_8888: +- return VA_RT_FORMAT_RGB32; +- case gfx::BufferFormat::YVU_420: +- return VA_RT_FORMAT_YUV420; +- default: +- NOTREACHED(); +- return 0; +- } +-} +- +-} // namespace +- +-namespace media { +- +-namespace { +- +-// Maximum framerate of encoded profile. This value is an arbitary limit +-// and not taken from HW documentation. +-const int kMaxEncoderFramerate = 30; +- +-// Attributes required for encode. This only applies to video encode, not JPEG +-// encode. +-static const VAConfigAttrib kVideoEncodeVAConfigAttribs[] = { +- {VAConfigAttribRateControl, VA_RC_CBR}, +- {VAConfigAttribEncPackedHeaders, +- VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE}, +-}; +- +-// A map between VideoCodecProfile and VAProfile. +-static const struct { +- VideoCodecProfile profile; +- VAProfile va_profile; +-} kProfileMap[] = { +- {H264PROFILE_BASELINE, VAProfileH264Baseline}, +- {H264PROFILE_MAIN, VAProfileH264Main}, +- // TODO(posciak): See if we can/want to support other variants of +- // H264PROFILE_HIGH*. +- {H264PROFILE_HIGH, VAProfileH264High}, +- {VP8PROFILE_ANY, VAProfileVP8Version0_3}, +- {VP9PROFILE_PROFILE0, VAProfileVP9Profile0}, +- {VP9PROFILE_PROFILE1, VAProfileVP9Profile1}, +- {VP9PROFILE_PROFILE2, VAProfileVP9Profile2}, +- {VP9PROFILE_PROFILE3, VAProfileVP9Profile3}, +-}; +- +-// This class is a wrapper around its |va_display_| (and its associated +-// |va_lock_|) to guarantee mutual exclusion and singleton behaviour. +-class VADisplayState { +- public: +- static VADisplayState* Get(); +- +- // Initialize static data before sandbox is enabled. +- static void PreSandboxInitialization(); +- +- VADisplayState(); +- ~VADisplayState() = delete; +- +- // |va_lock_| must be held on entry. +- bool Initialize(); +- void Deinitialize(VAStatus* status); +- +- base::Lock* va_lock() { return &va_lock_; } +- VADisplay va_display() const { return va_display_; } +- +- void SetDrmFd(base::PlatformFile fd) { drm_fd_.reset(HANDLE_EINTR(dup(fd))); } +- +- private: +- // Returns false on init failure. +- static bool PostSandboxInitialization(); +- +- // Protected by |va_lock_|. +- int refcount_; +- +- // Libva is not thread safe, so we have to do locking for it ourselves. +- // This lock is to be taken for the duration of all VA-API calls and for +- // the entire job submission sequence in ExecuteAndDestroyPendingBuffers(). +- base::Lock va_lock_; +- +- // Drm fd used to obtain access to the driver interface by VA. +- base::ScopedFD drm_fd_; +- +- // The VADisplay handle. +- VADisplay va_display_; +- +- // True if vaInitialize() has been called successfully. +- bool va_initialized_; +-}; +- +-// static +-VADisplayState* VADisplayState::Get() { +- static VADisplayState* display_state = new VADisplayState(); +- return display_state; +-} +- +-// static +-void VADisplayState::PreSandboxInitialization() { +- const char kDriRenderNode0Path[] = "/dev/dri/renderD128"; +- base::File drm_file = base::File( +- base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path), +- base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); +- if (drm_file.IsValid()) +- VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile()); +-} +- +-// static +-bool VADisplayState::PostSandboxInitialization() { +- const std::string va_suffix(std::to_string(VA_MAJOR_VERSION + 1)); +- StubPathMap paths; +- +- paths[kModuleVa].push_back(std::string("libva.so.") + va_suffix); +- paths[kModuleVa_drm].push_back(std::string("libva-drm.so.") + va_suffix); +-#if defined(USE_X11) +- // libva-x11 does not exist on libva >= 2 +- if (VA_MAJOR_VERSION == 0) +- paths[kModuleVa_x11].push_back("libva-x11.so.1"); +-#endif +- +- const bool success = InitializeStubs(paths); +- if (!success) { +- static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; +-#if defined(OS_CHROMEOS) +- // When Chrome runs on Linux with target_os="chromeos", do not log error +- // message without VAAPI libraries. +- LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS()) << kErrorMsg; +-#else +- DVLOG(1) << kErrorMsg; +-#endif +- } +- return success; +-} +- +-VADisplayState::VADisplayState() +- : refcount_(0), va_display_(nullptr), va_initialized_(false) {} +- +-bool VADisplayState::Initialize() { +- va_lock_.AssertAcquired(); +- +- static bool result = PostSandboxInitialization(); +- if (!result) +- return false; +- +- if (refcount_++ > 0) +- return true; +- +- switch (gl::GetGLImplementation()) { +- case gl::kGLImplementationEGLGLES2: +- va_display_ = vaGetDisplayDRM(drm_fd_.get()); +- break; +- case gl::kGLImplementationDesktopGL: +-#if defined(USE_X11) +- va_display_ = vaGetDisplay(gfx::GetXDisplay()); +-#else +- LOG(WARNING) << "HW video decode acceleration not available without " +- "DesktopGL (GLX)."; +-#endif // USE_X11 +- break; +- // Cannot infer platform from GL, try all available displays +- case gl::kGLImplementationNone: +-#if defined(USE_X11) +- va_display_ = vaGetDisplay(gfx::GetXDisplay()); +- if (vaDisplayIsValid(va_display_)) +- break; +-#endif // USE_X11 +- va_display_ = vaGetDisplayDRM(drm_fd_.get()); +- break; +- +- default: +- LOG(WARNING) << "HW video decode acceleration not available for " +- << gl::GetGLImplementationName(gl::GetGLImplementation()); +- return false; +- } +- +- if (!vaDisplayIsValid(va_display_)) { +- LOG(ERROR) << "Could not get a valid VA display"; +- return false; +- } +- +- // Set VA logging level to enable error messages, unless already set +- constexpr char libva_log_level_env[] = "LIBVA_MESSAGING_LEVEL"; +- std::unique_ptr env(base::Environment::Create()); +- if (!env->HasVar(libva_log_level_env)) +- env->SetVar(libva_log_level_env, "1"); +- +- // The VAAPI version. +- int major_version, minor_version; +- VAStatus va_res = vaInitialize(va_display_, &major_version, &minor_version); +- if (va_res != VA_STATUS_SUCCESS) { +- LOG(ERROR) << "vaInitialize failed: " << vaErrorStr(va_res); +- return false; +- } +- +- va_initialized_ = true; +- DVLOG(1) << "VAAPI version: " << major_version << "." << minor_version; +- +- if (major_version != VA_MAJOR_VERSION || minor_version != VA_MINOR_VERSION) { +- LOG(ERROR) << "This build of Chromium requires VA-API version " +- << VA_MAJOR_VERSION << "." << VA_MINOR_VERSION +- << ", system version: " << major_version << "." << minor_version; +- return false; +- } +- return true; +-} +- +-void VADisplayState::Deinitialize(VAStatus* status) { +- va_lock_.AssertAcquired(); +- if (--refcount_ > 0) +- return; +- +- // Must check if vaInitialize completed successfully, to work around a bug in +- // libva. The bug was fixed upstream: +- // http://lists.freedesktop.org/archives/libva/2013-July/001807.html +- // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once +- // the fix has rolled out sufficiently. +- if (va_initialized_ && va_display_) +- *status = vaTerminate(va_display_); +- va_initialized_ = false; +- va_display_ = nullptr; +-} +- +-static std::vector GetRequiredAttribs( +- VaapiWrapper::CodecMode mode, +- VAProfile profile) { +- std::vector required_attribs; +- // VAConfigAttribRTFormat is common to both encode and decode |mode|s. +- if (profile == VAProfileVP9Profile2 || profile == VAProfileVP9Profile3) { +- required_attribs.push_back( +- {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420_10BPP}); +- } else { +- required_attribs.push_back({VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}); +- } +- if (mode == VaapiWrapper::kEncode && profile != VAProfileJPEGBaseline) { +- required_attribs.insert( +- required_attribs.end(), kVideoEncodeVAConfigAttribs, +- kVideoEncodeVAConfigAttribs + arraysize(kVideoEncodeVAConfigAttribs)); +- } +- return required_attribs; +-} +- +-static VAEntrypoint GetVaEntryPoint(VaapiWrapper::CodecMode mode, +- VAProfile profile) { +- switch (mode) { +- case VaapiWrapper::kDecode: +- return VAEntrypointVLD; +- case VaapiWrapper::kEncode: +- if (profile == VAProfileJPEGBaseline) +- return VAEntrypointEncPicture; +- else +- return VAEntrypointEncSlice; +- case VaapiWrapper::kCodecModeMax: +- NOTREACHED(); +- return VAEntrypointVLD; +- } +-} +- +-// This class encapsulates reading and giving access to the list of supported +-// ProfileInfo entries, in a singleton way. +-class VASupportedProfiles { +- public: +- struct ProfileInfo { +- VAProfile va_profile; +- gfx::Size max_resolution; +- }; +- static VASupportedProfiles* Get(); +- +- std::vector GetSupportedProfileInfosForCodecMode( +- VaapiWrapper::CodecMode mode); +- +- bool IsProfileSupported(VaapiWrapper::CodecMode mode, VAProfile va_profile); +- +- private: +- VASupportedProfiles(); +- ~VASupportedProfiles() = default; +- +- bool GetSupportedVAProfiles(std::vector* profiles); +- +- // Gets supported profile infos for |mode|. +- std::vector GetSupportedProfileInfosForCodecModeInternal( +- VaapiWrapper::CodecMode mode); +- +- // |va_lock_| must be held on entry in the following _Locked methods. +- +- // Checks if |va_profile| supports |entrypoint| or not. +- bool IsEntrypointSupported_Locked(VAProfile va_profile, +- VAEntrypoint entrypoint); +- // Returns true if |va_profile| for |entrypoint| with |required_attribs| is +- // supported. +- bool AreAttribsSupported_Locked( +- VAProfile va_profile, +- VAEntrypoint entrypoint, +- const std::vector& required_attribs); +- // Gets maximum resolution for |va_profile| and |entrypoint| with +- // |required_attribs|. If return value is true, |resolution| is the maximum +- // resolution. +- bool GetMaxResolution_Locked(VAProfile va_profile, +- VAEntrypoint entrypoint, +- std::vector& required_attribs, +- gfx::Size* resolution); +- +- std::vector supported_profiles_[VaapiWrapper::kCodecModeMax]; +- +- // Pointer to VADisplayState's members |va_lock_| and its |va_display_|. +- base::Lock* va_lock_; +- VADisplay va_display_; +- +- const base::Closure report_error_to_uma_cb_; +-}; +- +-// static +-VASupportedProfiles* VASupportedProfiles::Get() { +- static VASupportedProfiles* profile_infos = new VASupportedProfiles(); +- return profile_infos; +-} +- +-std::vector +-VASupportedProfiles::GetSupportedProfileInfosForCodecMode( +- VaapiWrapper::CodecMode mode) { +- return supported_profiles_[mode]; +-} +- +-bool VASupportedProfiles::IsProfileSupported(VaapiWrapper::CodecMode mode, +- VAProfile va_profile) { +- for (const auto& profile : supported_profiles_[mode]) { +- if (profile.va_profile == va_profile) +- return true; +- } +- return false; +-} +- +-VASupportedProfiles::VASupportedProfiles() +- : va_lock_(VADisplayState::Get()->va_lock()), +- va_display_(nullptr), +- report_error_to_uma_cb_(base::Bind(&base::DoNothing)) { +- static_assert(arraysize(supported_profiles_) == VaapiWrapper::kCodecModeMax, +- "The array size of supported profile is incorrect."); +- { +- base::AutoLock auto_lock(*va_lock_); +- if (!VADisplayState::Get()->Initialize()) +- return; +- } +- +- va_display_ = VADisplayState::Get()->va_display(); +- DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; +- +- for (size_t i = 0; i < VaapiWrapper::kCodecModeMax; ++i) { +- supported_profiles_[i] = GetSupportedProfileInfosForCodecModeInternal( +- static_cast(i)); +- } +- +- { +- base::AutoLock auto_lock(*va_lock_); +- VAStatus va_res = VA_STATUS_SUCCESS; +- VADisplayState::Get()->Deinitialize(&va_res); +- VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); +- va_display_ = nullptr; +- } +-} +- +-std::vector +-VASupportedProfiles::GetSupportedProfileInfosForCodecModeInternal( +- VaapiWrapper::CodecMode mode) { +- std::vector supported_profile_infos; +- std::vector va_profiles; +- if (!GetSupportedVAProfiles(&va_profiles)) +- return supported_profile_infos; +- +- base::AutoLock auto_lock(*va_lock_); +- for (const auto& va_profile : va_profiles) { +- VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); +- std::vector required_attribs = +- GetRequiredAttribs(mode, va_profile); +- if (!IsEntrypointSupported_Locked(va_profile, entrypoint)) +- continue; +- if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs)) +- continue; +- ProfileInfo profile_info; +- if (!GetMaxResolution_Locked(va_profile, entrypoint, required_attribs, +- &profile_info.max_resolution)) { +- LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile +- << " and entrypoint " << entrypoint; +- continue; +- } +- profile_info.va_profile = va_profile; +- supported_profile_infos.push_back(profile_info); +- } +- return supported_profile_infos; +-} +- +-bool VASupportedProfiles::GetSupportedVAProfiles( +- std::vector* profiles) { +- base::AutoLock auto_lock(*va_lock_); +- // Query the driver for supported profiles. +- const int max_profiles = vaMaxNumProfiles(va_display_); +- std::vector supported_profiles( +- base::checked_cast(max_profiles)); +- +- int num_supported_profiles; +- VAStatus va_res = vaQueryConfigProfiles(va_display_, &supported_profiles[0], +- &num_supported_profiles); +- VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false); +- if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) { +- LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_supported_profiles; +- return false; +- } +- +- supported_profiles.resize(base::checked_cast(num_supported_profiles)); +- *profiles = supported_profiles; +- return true; +-} +- +-bool VASupportedProfiles::IsEntrypointSupported_Locked( +- VAProfile va_profile, +- VAEntrypoint entrypoint) { +- va_lock_->AssertAcquired(); +- // Query the driver for supported entrypoints. +- int max_entrypoints = vaMaxNumEntrypoints(va_display_); +- std::vector supported_entrypoints( +- base::checked_cast(max_entrypoints)); +- +- int num_supported_entrypoints; +- VAStatus va_res = vaQueryConfigEntrypoints(va_display_, va_profile, +- &supported_entrypoints[0], +- &num_supported_entrypoints); +- VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false); +- if (num_supported_entrypoints < 0 || +- num_supported_entrypoints > max_entrypoints) { +- LOG(ERROR) << "vaQueryConfigEntrypoints returned: " +- << num_supported_entrypoints; +- return false; +- } +- +- return base::ContainsValue(supported_entrypoints, entrypoint); +-} +- +-bool VASupportedProfiles::AreAttribsSupported_Locked( +- VAProfile va_profile, +- VAEntrypoint entrypoint, +- const std::vector& required_attribs) { +- va_lock_->AssertAcquired(); +- // Query the driver for required attributes. +- std::vector attribs = required_attribs; +- for (size_t i = 0; i < required_attribs.size(); ++i) +- attribs[i].value = 0; +- +- VAStatus va_res = vaGetConfigAttributes(va_display_, va_profile, entrypoint, +- &attribs[0], attribs.size()); +- VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); +- +- for (size_t i = 0; i < required_attribs.size(); ++i) { +- if (attribs[i].type != required_attribs[i].type || +- (attribs[i].value & required_attribs[i].value) != +- required_attribs[i].value) { +- DVLOG(1) << "Unsupported value " << required_attribs[i].value +- << " for attribute type " << required_attribs[i].type; +- return false; +- } +- } +- return true; +-} +- +-bool VASupportedProfiles::GetMaxResolution_Locked( +- VAProfile va_profile, +- VAEntrypoint entrypoint, +- std::vector& required_attribs, +- gfx::Size* resolution) { +- va_lock_->AssertAcquired(); +- VAConfigID va_config_id; +- VAStatus va_res = +- vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], +- required_attribs.size(), &va_config_id); +- VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); +- +- // Calls vaQuerySurfaceAttributes twice. The first time is to get the number +- // of attributes to prepare the space and the second time is to get all +- // attributes. +- unsigned int num_attribs; +- va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, nullptr, +- &num_attribs); +- VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); +- if (!num_attribs) +- return false; +- +- std::vector attrib_list( +- base::checked_cast(num_attribs)); +- +- va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, &attrib_list[0], +- &num_attribs); +- VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); +- +- resolution->SetSize(0, 0); +- for (const auto& attrib : attrib_list) { +- if (attrib.type == VASurfaceAttribMaxWidth) +- resolution->set_width(attrib.value.value.i); +- else if (attrib.type == VASurfaceAttribMaxHeight) +- resolution->set_height(attrib.value.value.i); +- } +- if (resolution->IsEmpty()) { +- LOG(ERROR) << "Wrong codec resolution: " << resolution->ToString(); +- return false; +- } +- return true; +-} +- +-// Maps VideoCodecProfile enum values to VaProfile values. This function +-// includes a workaround for https://crbug.com/345569: if va_profile is h264 +-// baseline and it is not supported, we try constrained baseline. +-VAProfile ProfileToVAProfile(VideoCodecProfile profile, +- VaapiWrapper::CodecMode mode) { +- VAProfile va_profile = VAProfileNone; +- for (size_t i = 0; i < arraysize(kProfileMap); ++i) { +- if (kProfileMap[i].profile == profile) { +- va_profile = kProfileMap[i].va_profile; +- break; +- } +- } +- if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile) && +- va_profile == VAProfileH264Baseline) { +- // https://crbug.com/345569: ProfileIDToVideoCodecProfile() currently strips +- // the information whether the profile is constrained or not, so we have no +- // way to know here. Try for baseline first, but if it is not supported, +- // try constrained baseline and hope this is what it actually is +- // (which in practice is true for a great majority of cases). +- if (VASupportedProfiles::Get()->IsProfileSupported( +- mode, VAProfileH264ConstrainedBaseline)) { +- va_profile = VAProfileH264ConstrainedBaseline; +- DVLOG(1) << "Fall back to constrained baseline profile."; +- } +- } +- return va_profile; +-} +- +-void DestroyVAImage(VADisplay va_display, VAImage image) { +- if (image.image_id != VA_INVALID_ID) +- vaDestroyImage(va_display, image.image_id); +-} +- +-} // namespace +- +-VaapiWrapper::VaapiWrapper() +- : va_surface_format_(0), +- va_display_(NULL), +- va_config_id_(VA_INVALID_ID), +- va_context_id_(VA_INVALID_ID), +- va_vpp_config_id_(VA_INVALID_ID), +- va_vpp_context_id_(VA_INVALID_ID), +- va_vpp_buffer_id_(VA_INVALID_ID) { +- va_lock_ = VADisplayState::Get()->va_lock(); +-} +- +-VaapiWrapper::~VaapiWrapper() { +- DestroyPendingBuffers(); +- DestroyCodedBuffers(); +- DestroySurfaces(); +- DeinitializeVpp(); +- Deinitialize(); +-} +- +-// static +-scoped_refptr VaapiWrapper::Create( +- CodecMode mode, +- VAProfile va_profile, +- const base::Closure& report_error_to_uma_cb) { +- if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile)) { +- DVLOG(1) << "Unsupported va_profile: " << va_profile; +- return nullptr; +- } +- +- scoped_refptr vaapi_wrapper(new VaapiWrapper()); +- if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) { +- if (vaapi_wrapper->Initialize(mode, va_profile)) +- return vaapi_wrapper; +- } +- LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " << va_profile; +- return nullptr; +-} +- +-// static +-scoped_refptr VaapiWrapper::CreateForVideoCodec( +- CodecMode mode, +- VideoCodecProfile profile, +- const base::Closure& report_error_to_uma_cb) { +- VAProfile va_profile = ProfileToVAProfile(profile, mode); +- return Create(mode, va_profile, report_error_to_uma_cb); +-} +- +-// static +-VideoEncodeAccelerator::SupportedProfiles +-VaapiWrapper::GetSupportedEncodeProfiles() { +- VideoEncodeAccelerator::SupportedProfiles profiles; +- std::vector encode_profile_infos = +- VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kEncode); +- +- for (size_t i = 0; i < arraysize(kProfileMap); ++i) { +- VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode); +- if (va_profile == VAProfileNone) +- continue; +- for (const auto& profile_info : encode_profile_infos) { +- if (profile_info.va_profile == va_profile) { +- VideoEncodeAccelerator::SupportedProfile profile; +- profile.profile = kProfileMap[i].profile; +- profile.max_resolution = profile_info.max_resolution; +- profile.max_framerate_numerator = kMaxEncoderFramerate; +- profile.max_framerate_denominator = 1; +- profiles.push_back(profile); +- break; +- } +- } +- } +- return profiles; +-} +- +-// static +-VideoDecodeAccelerator::SupportedProfiles +-VaapiWrapper::GetSupportedDecodeProfiles() { +- VideoDecodeAccelerator::SupportedProfiles profiles; +- std::vector decode_profile_infos = +- VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kDecode); +- +- for (size_t i = 0; i < arraysize(kProfileMap); ++i) { +- VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kDecode); +- if (va_profile == VAProfileNone) +- continue; +- for (const auto& profile_info : decode_profile_infos) { +- if (profile_info.va_profile == va_profile) { +- VideoDecodeAccelerator::SupportedProfile profile; +- profile.profile = kProfileMap[i].profile; +- profile.max_resolution = profile_info.max_resolution; +- profile.min_resolution.SetSize(16, 16); +- profiles.push_back(profile); +- break; +- } +- } +- } +- return profiles; +-} +- +-// static +-bool VaapiWrapper::IsJpegDecodeSupported() { +- return VASupportedProfiles::Get()->IsProfileSupported(kDecode, +- VAProfileJPEGBaseline); +-} +- +-// static +-bool VaapiWrapper::IsJpegEncodeSupported() { +- return VASupportedProfiles::Get()->IsProfileSupported(kEncode, +- VAProfileJPEGBaseline); +-} +- +-void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { +- base::AutoLock auto_lock(*va_lock_); +- VADisplayAttribute item = {VADisplayAttribRenderMode, +- 1, // At least support '_LOCAL_OVERLAY'. +- -1, // The maximum possible support 'ALL'. +- VA_RENDER_MODE_LOCAL_GPU, +- VA_DISPLAY_ATTRIB_SETTABLE}; +- +- VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1); +- if (va_res != VA_STATUS_SUCCESS) +- DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; +-} +- +-bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) { +- report_error_to_uma_cb_ = report_error_to_uma_cb; +- { +- base::AutoLock auto_lock(*va_lock_); +- if (!VADisplayState::Get()->Initialize()) +- return false; +- } +- +- va_display_ = VADisplayState::Get()->va_display(); +- DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; +- return true; +-} +- +-bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) { +- TryToSetVADisplayAttributeToLocalGPU(); +- +- VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); +- std::vector required_attribs = +- GetRequiredAttribs(mode, va_profile); +- base::AutoLock auto_lock(*va_lock_); +- VAStatus va_res = +- vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], +- required_attribs.size(), &va_config_id_); +- VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); +- +- return true; +-} +- +-void VaapiWrapper::Deinitialize() { +- base::AutoLock auto_lock(*va_lock_); +- +- if (va_config_id_ != VA_INVALID_ID) { +- VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); +- VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); +- } +- +- VAStatus va_res = VA_STATUS_SUCCESS; +- VADisplayState::Get()->Deinitialize(&va_res); +- VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); +- +- va_config_id_ = VA_INVALID_ID; +- va_display_ = NULL; +-} +- +-bool VaapiWrapper::CreateSurfaces(unsigned int va_format, +- const gfx::Size& size, +- size_t num_surfaces, +- std::vector* va_surfaces) { +- base::AutoLock auto_lock(*va_lock_); +- DVLOG(2) << "Creating " << num_surfaces << " surfaces"; +- +- DCHECK(va_surfaces->empty()); +- DCHECK(va_surface_ids_.empty()); +- DCHECK_EQ(va_surface_format_, 0u); +- va_surface_ids_.resize(num_surfaces); +- +- // Allocate surfaces in driver. +- VAStatus va_res = +- vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), +- &va_surface_ids_[0], va_surface_ids_.size(), NULL, 0); +- +- VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); +- if (va_res != VA_STATUS_SUCCESS) { +- va_surface_ids_.clear(); +- return false; +- } +- +- // And create a context associated with them. +- va_res = vaCreateContext(va_display_, va_config_id_, size.width(), +- size.height(), VA_PROGRESSIVE, &va_surface_ids_[0], +- va_surface_ids_.size(), &va_context_id_); +- +- VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); +- if (va_res != VA_STATUS_SUCCESS) { +- DestroySurfaces_Locked(); +- return false; +- } +- +- *va_surfaces = va_surface_ids_; +- va_surface_format_ = va_format; +- return true; +-} +- +-void VaapiWrapper::DestroySurfaces() { +- base::AutoLock auto_lock(*va_lock_); +- DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; +- +- DestroySurfaces_Locked(); +-} +- +-void VaapiWrapper::DestroySurfaces_Locked() { +- va_lock_->AssertAcquired(); +- +- if (va_context_id_ != VA_INVALID_ID) { +- VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); +- VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); +- } +- +- if (!va_surface_ids_.empty()) { +- VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0], +- va_surface_ids_.size()); +- VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); +- } +- +- va_surface_ids_.clear(); +- va_context_id_ = VA_INVALID_ID; +- va_surface_format_ = 0; +-} +- +-scoped_refptr VaapiWrapper::CreateUnownedSurface( +- unsigned int va_format, +- const gfx::Size& size, +- const std::vector& va_attribs) { +- base::AutoLock auto_lock(*va_lock_); +- +- std::vector attribs(va_attribs); +- VASurfaceID va_surface_id; +- VAStatus va_res = +- vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), +- &va_surface_id, 1, &attribs[0], attribs.size()); +- +- scoped_refptr va_surface; +- VA_SUCCESS_OR_RETURN(va_res, "Failed to create unowned VASurface", +- va_surface); +- +- // This is safe to use Unretained() here, because the VDA takes care +- // of the destruction order. All the surfaces will be destroyed +- // before VaapiWrapper. +- va_surface = new VASurface( +- va_surface_id, size, va_format, +- base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this))); +- +- return va_surface; +-} +- +-scoped_refptr VaapiWrapper::CreateVASurfaceForPixmap( +- const scoped_refptr& pixmap) { +- // Create a VASurface for a NativePixmap by importing the underlying dmabufs. +- VASurfaceAttribExternalBuffers va_attrib_extbuf; +- memset(&va_attrib_extbuf, 0, sizeof(va_attrib_extbuf)); +- +- va_attrib_extbuf.pixel_format = +- BufferFormatToVAFourCC(pixmap->GetBufferFormat()); +- gfx::Size size = pixmap->GetBufferSize(); +- va_attrib_extbuf.width = size.width(); +- va_attrib_extbuf.height = size.height(); +- +- size_t num_fds = pixmap->GetDmaBufFdCount(); +- size_t num_planes = +- gfx::NumberOfPlanesForBufferFormat(pixmap->GetBufferFormat()); +- if (num_fds == 0 || num_fds > num_planes) { +- LOG(ERROR) << "Invalid number of dmabuf fds: " << num_fds +- << " , planes: " << num_planes; +- return nullptr; +- } +- +- for (size_t i = 0; i < num_planes; ++i) { +- va_attrib_extbuf.pitches[i] = pixmap->GetDmaBufPitch(i); +- va_attrib_extbuf.offsets[i] = pixmap->GetDmaBufOffset(i); +- DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i] +- << " offset: " << va_attrib_extbuf.offsets[i]; +- } +- va_attrib_extbuf.num_planes = num_planes; +- +- std::vector fds(num_fds); +- for (size_t i = 0; i < num_fds; ++i) { +- int dmabuf_fd = pixmap->GetDmaBufFd(i); +- if (dmabuf_fd < 0) { +- LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap"; +- return nullptr; +- } +- fds[i] = dmabuf_fd; +- } +- va_attrib_extbuf.buffers = fds.data(); +- va_attrib_extbuf.num_buffers = fds.size(); +- +- va_attrib_extbuf.flags = 0; +- va_attrib_extbuf.private_data = NULL; +- +- std::vector va_attribs(2); +- +- va_attribs[0].type = VASurfaceAttribMemoryType; +- va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; +- va_attribs[0].value.type = VAGenericValueTypeInteger; +- va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; +- +- va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; +- va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; +- va_attribs[1].value.type = VAGenericValueTypePointer; +- va_attribs[1].value.value.p = &va_attrib_extbuf; +- +- scoped_refptr va_surface = CreateUnownedSurface( +- BufferFormatToVARTFormat(pixmap->GetBufferFormat()), size, va_attribs); +- if (!va_surface) { +- LOG(ERROR) << "Failed to create VASurface for an Ozone NativePixmap"; +- return nullptr; +- } +- +- return va_surface; +-} +- +-void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id) { +- base::AutoLock auto_lock(*va_lock_); +- +- VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1); +- VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces on surface failed"); +-} +- +-bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, +- size_t size, +- void* buffer) { +- base::AutoLock auto_lock(*va_lock_); +- +- VABufferID buffer_id; +- VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, va_buffer_type, +- size, 1, buffer, &buffer_id); +- VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); +- +- switch (va_buffer_type) { +- case VASliceParameterBufferType: +- case VASliceDataBufferType: +- case VAEncSliceParameterBufferType: +- pending_slice_bufs_.push_back(buffer_id); +- break; +- +- default: +- pending_va_bufs_.push_back(buffer_id); +- break; +- } +- +- return true; +-} +- +-bool VaapiWrapper::SubmitVAEncMiscParamBuffer( +- VAEncMiscParameterType misc_param_type, +- size_t size, +- void* buffer) { +- base::AutoLock auto_lock(*va_lock_); +- +- VABufferID buffer_id; +- VAStatus va_res = vaCreateBuffer( +- va_display_, va_context_id_, VAEncMiscParameterBufferType, +- sizeof(VAEncMiscParameterBuffer) + size, 1, NULL, &buffer_id); +- VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); +- +- void* data_ptr = NULL; +- va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr); +- VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); +- if (va_res != VA_STATUS_SUCCESS) { +- vaDestroyBuffer(va_display_, buffer_id); +- return false; +- } +- +- DCHECK(data_ptr); +- +- VAEncMiscParameterBuffer* misc_param = +- reinterpret_cast(data_ptr); +- misc_param->type = misc_param_type; +- memcpy(misc_param->data, buffer, size); +- va_res = vaUnmapBuffer(va_display_, buffer_id); +- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); +- +- pending_va_bufs_.push_back(buffer_id); +- return true; +-} +- +-void VaapiWrapper::DestroyPendingBuffers() { +- base::AutoLock auto_lock(*va_lock_); +- +- for (const auto& pending_va_buf : pending_va_bufs_) { +- VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf); +- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); +- } +- +- for (const auto& pending_slice_buf : pending_slice_bufs_) { +- VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf); +- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); +- } +- +- pending_va_bufs_.clear(); +- pending_slice_bufs_.clear(); +-} +- +-bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) { +- base::AutoLock auto_lock(*va_lock_); +- VAStatus va_res = +- vaCreateBuffer(va_display_, va_context_id_, VAEncCodedBufferType, size, 1, +- NULL, buffer_id); +- VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false); +- +- const auto is_new_entry = coded_buffers_.insert(*buffer_id).second; +- DCHECK(is_new_entry); +- return true; +-} +- +-void VaapiWrapper::DestroyCodedBuffers() { +- base::AutoLock auto_lock(*va_lock_); +- +- for (std::set::const_iterator iter = coded_buffers_.begin(); +- iter != coded_buffers_.end(); ++iter) { +- VAStatus va_res = vaDestroyBuffer(va_display_, *iter); +- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); +- } +- +- coded_buffers_.clear(); +-} +- +-bool VaapiWrapper::Execute(VASurfaceID va_surface_id) { +- base::AutoLock auto_lock(*va_lock_); +- +- DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); +- DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); +- DVLOG(4) << "Target VA surface " << va_surface_id; +- +- // Get ready to execute for given surface. +- VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, va_surface_id); +- VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); +- +- if (pending_va_bufs_.size() > 0) { +- // Commit parameter and slice buffers. +- va_res = vaRenderPicture(va_display_, va_context_id_, &pending_va_bufs_[0], +- pending_va_bufs_.size()); +- VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); +- } +- +- if (pending_slice_bufs_.size() > 0) { +- va_res = +- vaRenderPicture(va_display_, va_context_id_, &pending_slice_bufs_[0], +- pending_slice_bufs_.size()); +- VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); +- } +- +- // Instruct HW codec to start processing committed buffers. +- // Does not block and the job is not finished after this returns. +- va_res = vaEndPicture(va_display_, va_context_id_); +- VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); +- +- return true; +-} +- +-bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { +- bool result = Execute(va_surface_id); +- DestroyPendingBuffers(); +- return result; +-} +- +-#if defined(USE_X11) +-bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, +- Pixmap x_pixmap, +- gfx::Size dest_size) { +- base::AutoLock auto_lock(*va_lock_); +- +- VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); +- VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); +- +- // Put the data into an X Pixmap. +- va_res = vaPutSurface(va_display_, +- va_surface_id, +- x_pixmap, +- 0, 0, dest_size.width(), dest_size.height(), +- 0, 0, dest_size.width(), dest_size.height(), +- NULL, 0, 0); +- VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false); +- return true; +-} +-#endif // USE_X11 +- +-bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id, +- VAImageFormat* format, +- const gfx::Size& size, +- VAImage* image, +- void** mem) { +- base::AutoLock auto_lock(*va_lock_); +- +- VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); +- VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); +- +- va_res = +- vaCreateImage(va_display_, format, size.width(), size.height(), image); +- VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false); +- +- va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(), +- size.height(), image->image_id); +- VA_LOG_ON_ERROR(va_res, "vaGetImage failed"); +- +- if (va_res == VA_STATUS_SUCCESS) { +- // Map the VAImage into memory +- va_res = vaMapBuffer(va_display_, image->buf, mem); +- VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); +- } +- +- if (va_res != VA_STATUS_SUCCESS) { +- va_res = vaDestroyImage(va_display_, image->image_id); +- VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); +- return false; +- } +- +- return true; +-} +- +-void VaapiWrapper::ReturnVaImage(VAImage* image) { +- base::AutoLock auto_lock(*va_lock_); +- +- VAStatus va_res = vaUnmapBuffer(va_display_, image->buf); +- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); +- +- va_res = vaDestroyImage(va_display_, image->image_id); +- VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); +-} +- +-bool VaapiWrapper::UploadVideoFrameToSurface( +- const scoped_refptr& frame, +- VASurfaceID va_surface_id) { +- base::AutoLock auto_lock(*va_lock_); +- +- VAImage image; +- VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image); +- VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false); +- base::ScopedClosureRunner vaimage_deleter( +- base::Bind(&DestroyVAImage, va_display_, image)); +- +- if (image.format.fourcc != VA_FOURCC_NV12) { +- LOG(ERROR) << "Unsupported image format: " << image.format.fourcc; +- return false; +- } +- +- if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) { +- LOG(ERROR) << "Buffer too small to fit the frame."; +- return false; +- } +- +- void* image_ptr = NULL; +- va_res = vaMapBuffer(va_display_, image.buf, &image_ptr); +- VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); +- DCHECK(image_ptr); +- +- int ret = 0; +- { +- base::AutoUnlock auto_unlock(*va_lock_); +- ret = libyuv::I420ToNV12( +- frame->data(VideoFrame::kYPlane), frame->stride(VideoFrame::kYPlane), +- frame->data(VideoFrame::kUPlane), frame->stride(VideoFrame::kUPlane), +- frame->data(VideoFrame::kVPlane), frame->stride(VideoFrame::kVPlane), +- static_cast(image_ptr) + image.offsets[0], image.pitches[0], +- static_cast(image_ptr) + image.offsets[1], image.pitches[1], +- image.width, image.height); +- } +- +- va_res = vaUnmapBuffer(va_display_, image.buf); +- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); +- +- return ret == 0; +-} +- +-bool VaapiWrapper::DownloadFromCodedBuffer(VABufferID buffer_id, +- VASurfaceID sync_surface_id, +- uint8_t* target_ptr, +- size_t target_size, +- size_t* coded_data_size) { +- base::AutoLock auto_lock(*va_lock_); +- +- VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id); +- VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); +- +- VACodedBufferSegment* buffer_segment = NULL; +- va_res = vaMapBuffer(va_display_, buffer_id, +- reinterpret_cast(&buffer_segment)); +- VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); +- DCHECK(target_ptr); +- +- { +- base::AutoUnlock auto_unlock(*va_lock_); +- *coded_data_size = 0; +- +- while (buffer_segment) { +- DCHECK(buffer_segment->buf); +- +- if (buffer_segment->size > target_size) { +- LOG(ERROR) << "Insufficient output buffer size"; +- break; +- } +- +- memcpy(target_ptr, buffer_segment->buf, buffer_segment->size); +- +- target_ptr += buffer_segment->size; +- *coded_data_size += buffer_segment->size; +- target_size -= buffer_segment->size; +- +- buffer_segment = +- reinterpret_cast(buffer_segment->next); +- } +- } +- +- va_res = vaUnmapBuffer(va_display_, buffer_id); +- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); +- return buffer_segment == NULL; +-} +- +-bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id, +- VASurfaceID sync_surface_id, +- uint8_t* target_ptr, +- size_t target_size, +- size_t* coded_data_size) { +- bool result = DownloadFromCodedBuffer(buffer_id, sync_surface_id, target_ptr, +- target_size, coded_data_size); +- +- VAStatus va_res = vaDestroyBuffer(va_display_, buffer_id); +- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); +- const auto was_found = coded_buffers_.erase(buffer_id); +- DCHECK(was_found); +- +- return result; +-} +- +-bool VaapiWrapper::BlitSurface( +- const scoped_refptr& va_surface_src, +- const scoped_refptr& va_surface_dest) { +- base::AutoLock auto_lock(*va_lock_); +- +- // Initialize the post processing engine if not already done. +- if (va_vpp_buffer_id_ == VA_INVALID_ID) { +- if (!InitializeVpp_Locked()) +- return false; +- } +- +- VAProcPipelineParameterBuffer* pipeline_param; +- VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_, va_vpp_buffer_id_, +- reinterpret_cast(&pipeline_param)), +- "Couldn't map vpp buffer", false); +- +- memset(pipeline_param, 0, sizeof *pipeline_param); +- const gfx::Size src_size = va_surface_src->size(); +- const gfx::Size dest_size = va_surface_dest->size(); +- +- VARectangle input_region; +- input_region.x = input_region.y = 0; +- input_region.width = src_size.width(); +- input_region.height = src_size.height(); +- pipeline_param->surface_region = &input_region; +- pipeline_param->surface = va_surface_src->id(); +- pipeline_param->surface_color_standard = VAProcColorStandardNone; +- +- VARectangle output_region; +- output_region.x = output_region.y = 0; +- output_region.width = dest_size.width(); +- output_region.height = dest_size.height(); +- pipeline_param->output_region = &output_region; +- pipeline_param->output_background_color = 0xff000000; +- pipeline_param->output_color_standard = VAProcColorStandardNone; +- pipeline_param->filter_flags = VA_FILTER_SCALING_DEFAULT; +- +- VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_, va_vpp_buffer_id_), +- "Couldn't unmap vpp buffer", false); +- +- VA_SUCCESS_OR_RETURN( +- vaBeginPicture(va_display_, va_vpp_context_id_, va_surface_dest->id()), +- "Couldn't begin picture", false); +- +- VA_SUCCESS_OR_RETURN( +- vaRenderPicture(va_display_, va_vpp_context_id_, &va_vpp_buffer_id_, 1), +- "Couldn't render picture", false); +- +- VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_, va_vpp_context_id_), +- "Couldn't end picture", false); +- +- return true; +-} +- +-bool VaapiWrapper::InitializeVpp_Locked() { +- va_lock_->AssertAcquired(); +- +- VA_SUCCESS_OR_RETURN( +- vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0, +- &va_vpp_config_id_), +- "Couldn't create config", false); +- +- // The size of the picture for the context is irrelevant in the case +- // of the VPP, just passing 1x1. +- VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_, va_vpp_config_id_, 1, 1, 0, +- NULL, 0, &va_vpp_context_id_), +- "Couldn't create context", false); +- +- VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_, va_vpp_context_id_, +- VAProcPipelineParameterBufferType, +- sizeof(VAProcPipelineParameterBuffer), 1, +- NULL, &va_vpp_buffer_id_), +- "Couldn't create buffer", false); +- +- return true; +-} +- +-void VaapiWrapper::DeinitializeVpp() { +- base::AutoLock auto_lock(*va_lock_); +- +- if (va_vpp_buffer_id_ != VA_INVALID_ID) { +- vaDestroyBuffer(va_display_, va_vpp_buffer_id_); +- va_vpp_buffer_id_ = VA_INVALID_ID; +- } +- if (va_vpp_context_id_ != VA_INVALID_ID) { +- vaDestroyContext(va_display_, va_vpp_context_id_); +- va_vpp_context_id_ = VA_INVALID_ID; +- } +- if (va_vpp_config_id_ != VA_INVALID_ID) { +- vaDestroyConfig(va_display_, va_vpp_config_id_); +- va_vpp_config_id_ = VA_INVALID_ID; +- } +-} +- +-// static +-void VaapiWrapper::PreSandboxInitialization() { +- VADisplayState::PreSandboxInitialization(); +-} +- +-} // namespace media +--- a/media/gpu/vaapi_wrapper.h ++++ /dev/null +@@ -1,288 +0,0 @@ +-// Copyright 2013 The Chromium Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style license that can be +-// found in the LICENSE file. +-// +-// This file contains an implementation of VaapiWrapper, used by +-// VaapiVideoDecodeAccelerator and VaapiH264Decoder for decode, +-// and VaapiVideoEncodeAccelerator for encode, to interface +-// with libva (VA-API library for hardware video codec). +- +-#ifndef MEDIA_GPU_VAAPI_WRAPPER_H_ +-#define MEDIA_GPU_VAAPI_WRAPPER_H_ +- +-#include +-#include +- +-#include +-#include +- +-#include +- +-#include "base/files/file.h" +-#include "base/macros.h" +-#include "base/memory/ref_counted.h" +-#include "base/synchronization/lock.h" +-#include "media/base/video_decoder_config.h" +-#include "media/base/video_frame.h" +-#include "media/gpu/media_gpu_export.h" +-#include "media/gpu/va_surface.h" +-#include "media/video/jpeg_decode_accelerator.h" +-#include "media/video/video_decode_accelerator.h" +-#include "media/video/video_encode_accelerator.h" +-#include "ui/gfx/geometry/size.h" +- +-#if defined(USE_X11) +-#include "ui/gfx/x/x11.h" +-#endif // USE_X11 +- +-namespace gfx { +-class NativePixmap; +-} +- +-namespace media { +- +-// This class handles VA-API calls and ensures proper locking of VA-API calls +-// to libva, the userspace shim to the HW codec driver. libva is not +-// thread-safe, so we have to perform locking ourselves. This class is fully +-// synchronous and its methods can be called from any thread and may wait on +-// the va_lock_ while other, concurrent calls run. +-// +-// This class is responsible for managing VAAPI connection, contexts and state. +-// It is also responsible for managing and freeing VABuffers (not VASurfaces), +-// which are used to queue parameters and slice data to the HW codec, +-// as well as underlying memory for VASurfaces themselves. +-class MEDIA_GPU_EXPORT VaapiWrapper +- : public base::RefCountedThreadSafe { +- public: +- enum CodecMode { +- kDecode, +- kEncode, +- kCodecModeMax, +- }; +- +- // Return an instance of VaapiWrapper initialized for |va_profile| and +- // |mode|. |report_error_to_uma_cb| will be called independently from +- // reporting errors to clients via method return values. +- static scoped_refptr Create( +- CodecMode mode, +- VAProfile va_profile, +- const base::Closure& report_error_to_uma_cb); +- +- // Create VaapiWrapper for VideoCodecProfile. It maps VideoCodecProfile +- // |profile| to VAProfile. +- // |report_error_to_uma_cb| will be called independently from reporting +- // errors to clients via method return values. +- static scoped_refptr CreateForVideoCodec( +- CodecMode mode, +- VideoCodecProfile profile, +- const base::Closure& report_error_to_uma_cb); +- +- // Return the supported video encode profiles. +- static VideoEncodeAccelerator::SupportedProfiles GetSupportedEncodeProfiles(); +- +- // Return the supported video decode profiles. +- static VideoDecodeAccelerator::SupportedProfiles GetSupportedDecodeProfiles(); +- +- // Return true when JPEG decode is supported. +- static bool IsJpegDecodeSupported(); +- +- // Return true when JPEG encode is supported. +- static bool IsJpegEncodeSupported(); +- +- // Create |num_surfaces| backing surfaces in driver for VASurfaces of +- // |va_format|, each of size |size|. Returns true when successful, with the +- // created IDs in |va_surfaces| to be managed and later wrapped in +- // VASurfaces. +- // The client must DestroySurfaces() each time before calling this method +- // again to free the allocated surfaces first, but is not required to do so +- // at destruction time, as this will be done automatically from +- // the destructor. +- virtual bool CreateSurfaces(unsigned int va_format, +- const gfx::Size& size, +- size_t num_surfaces, +- std::vector* va_surfaces); +- +- // Free all memory allocated in CreateSurfaces. +- virtual void DestroySurfaces(); +- +- // Create a VASurface of |va_format|, |size| and using |va_attribs| +- // attributes. The ownership of the surface is transferred to the +- // caller. It differs from surfaces created using CreateSurfaces(), +- // where VaapiWrapper is the owner of the surfaces. +- scoped_refptr CreateUnownedSurface( +- unsigned int va_format, +- const gfx::Size& size, +- const std::vector& va_attribs); +- +- // Create a VASurface for |pixmap|. The ownership of the surface is +- // transferred to the caller. It differs from surfaces created using +- // CreateSurfaces(), where VaapiWrapper is the owner of the surfaces. +- scoped_refptr CreateVASurfaceForPixmap( +- const scoped_refptr& pixmap); +- +- // Submit parameters or slice data of |va_buffer_type|, copying them from +- // |buffer| of size |size|, into HW codec. The data in |buffer| is no +- // longer needed and can be freed after this method returns. +- // Data submitted via this method awaits in the HW codec until +- // ExecuteAndDestroyPendingBuffers() is called to execute or +- // DestroyPendingBuffers() is used to cancel a pending job. +- bool SubmitBuffer(VABufferType va_buffer_type, size_t size, void* buffer); +- +- // Submit a VAEncMiscParameterBuffer of given |misc_param_type|, copying its +- // data from |buffer| of size |size|, into HW codec. The data in |buffer| is +- // no longer needed and can be freed after this method returns. +- // Data submitted via this method awaits in the HW codec until +- // ExecuteAndDestroyPendingBuffers() is called to execute or +- // DestroyPendingBuffers() is used to cancel a pending job. +- bool SubmitVAEncMiscParamBuffer(VAEncMiscParameterType misc_param_type, +- size_t size, +- void* buffer); +- +- // Cancel and destroy all buffers queued to the HW codec via SubmitBuffer(). +- // Useful when a pending job is to be cancelled (on reset or error). +- void DestroyPendingBuffers(); +- +- // Execute job in hardware on target |va_surface_id| and destroy pending +- // buffers. Return false if Execute() fails. +- bool ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id); +- +-#if defined(USE_X11) +- // Put data from |va_surface_id| into |x_pixmap| of size +- // |dest_size|, converting/scaling to it. +- bool PutSurfaceIntoPixmap(VASurfaceID va_surface_id, +- Pixmap x_pixmap, +- gfx::Size dest_size); +-#endif // USE_X11 +- +- // Get a VAImage from a VASurface |va_surface_id| and map it into memory with +- // given |format| and |size|. The output is |image| and the mapped memory is +- // |mem|. If |format| doesn't equal to the internal format, the underlying +- // implementation will do format conversion if supported. |size| should be +- // smaller than or equal to the surface. If |size| is smaller, the image will +- // be cropped. The VAImage should be released using the ReturnVaImage +- // function. Returns true when successful. +- bool GetVaImage(VASurfaceID va_surface_id, +- VAImageFormat* format, +- const gfx::Size& size, +- VAImage* image, +- void** mem); +- +- // Release the VAImage (and the associated memory mapping) obtained from +- // GetVaImage(). +- void ReturnVaImage(VAImage* image); +- +- // Upload contents of |frame| into |va_surface_id| for encode. +- bool UploadVideoFrameToSurface(const scoped_refptr& frame, +- VASurfaceID va_surface_id); +- +- // Create a buffer of |size| bytes to be used as encode output. +- bool CreateCodedBuffer(size_t size, VABufferID* buffer_id); +- +- // Download the contents of the buffer with given |buffer_id| into a buffer of +- // size |target_size|, pointed to by |target_ptr|. The number of bytes +- // downloaded will be returned in |coded_data_size|. |sync_surface_id| will +- // be used as a sync point, i.e. it will have to become idle before starting +- // the download. |sync_surface_id| should be the source surface passed +- // to the encode job. +- bool DownloadFromCodedBuffer(VABufferID buffer_id, +- VASurfaceID sync_surface_id, +- uint8_t* target_ptr, +- size_t target_size, +- size_t* coded_data_size); +- +- // See DownloadFromCodedBuffer() for details. After downloading, it deletes +- // the VA buffer with |buffer_id|. +- bool DownloadAndDestroyCodedBuffer(VABufferID buffer_id, +- VASurfaceID sync_surface_id, +- uint8_t* target_ptr, +- size_t target_size, +- size_t* coded_data_size); +- +- // Destroy all previously-allocated (and not yet destroyed) coded buffers. +- void DestroyCodedBuffers(); +- +- // Blits a VASurface |va_surface_src| into another VASurface +- // |va_surface_dest| applying pixel format conversion and scaling +- // if needed. +- bool BlitSurface(const scoped_refptr& va_surface_src, +- const scoped_refptr& va_surface_dest); +- +- // Initialize static data before sandbox is enabled. +- static void PreSandboxInitialization(); +- +- // Get the created surfaces format. +- unsigned int va_surface_format() const { return va_surface_format_; } +- +- protected: +- VaapiWrapper(); +- virtual ~VaapiWrapper(); +- +- private: +- friend class base::RefCountedThreadSafe; +- +- bool Initialize(CodecMode mode, VAProfile va_profile); +- void Deinitialize(); +- bool VaInitialize(const base::Closure& report_error_to_uma_cb); +- +- // Free all memory allocated in CreateSurfaces. +- void DestroySurfaces_Locked(); +- // Destroys a |va_surface| created using CreateUnownedSurface. +- void DestroyUnownedSurface(VASurfaceID va_surface_id); +- +- // Initialize the video post processing context with the |size| of +- // the input pictures to be processed. +- bool InitializeVpp_Locked(); +- +- // Deinitialize the video post processing context. +- void DeinitializeVpp(); +- +- // Execute pending job in hardware and destroy pending buffers. Return false +- // if vaapi driver refuses to accept parameter or slice buffers submitted +- // by client, or if execution fails in hardware. +- bool Execute(VASurfaceID va_surface_id); +- +- // Attempt to set render mode to "render to texture.". Failure is non-fatal. +- void TryToSetVADisplayAttributeToLocalGPU(); +- +- // Pointer to VADisplayState's member |va_lock_|. Guaranteed to be valid for +- // the lifetime of VaapiWrapper. +- base::Lock* va_lock_; +- +- // Allocated ids for VASurfaces. +- std::vector va_surface_ids_; +- +- // VA format of surfaces with va_surface_ids_. +- unsigned int va_surface_format_; +- +- // VA handles. +- // All valid after successful Initialize() and until Deinitialize(). +- VADisplay va_display_; +- VAConfigID va_config_id_; +- // Created for the current set of va_surface_ids_ in CreateSurfaces() and +- // valid until DestroySurfaces(). +- VAContextID va_context_id_; +- +- // Data queued up for HW codec, to be committed on next execution. +- std::vector pending_slice_bufs_; +- std::vector pending_va_bufs_; +- +- // Bitstream buffers for encode. +- std::set coded_buffers_; +- +- // Called to report codec errors to UMA. Errors to clients are reported via +- // return values from public methods. +- base::Closure report_error_to_uma_cb_; +- +- // VPP (Video Post Processing) context, this is used to convert +- // pictures used by the decoder to RGBA pictures usable by GL or the +- // display hardware. +- VAConfigID va_vpp_config_id_; +- VAContextID va_vpp_context_id_; +- VABufferID va_vpp_buffer_id_; +- +- DISALLOW_COPY_AND_ASSIGN(VaapiWrapper); +-}; +- +-} // namespace media +- +-#endif // MEDIA_GPU_VAAPI_WRAPPER_H_ +--- a/media/gpu/video_decode_accelerator_unittest.cc ++++ b/media/gpu/video_decode_accelerator_unittest.cc +@@ -75,7 +75,7 @@ + #include "media/gpu/dxva_video_decode_accelerator_win.h" + #endif // defined(OS_WIN) + #if BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #endif // BUILDFLAG(USE_VAAPI) + + #if defined(OS_CHROMEOS) +--- a/media/gpu/video_encode_accelerator_unittest.cc ++++ b/media/gpu/video_encode_accelerator_unittest.cc +@@ -56,7 +56,7 @@ + #include "testing/gtest/include/gtest/gtest.h" + + #if BUILDFLAG(USE_VAAPI) +-#include "media/gpu/vaapi_wrapper.h" ++#include "media/gpu/vaapi/vaapi_wrapper.h" + #elif defined(OS_WIN) + #include "media/gpu/media_foundation_video_encode_accelerator_win.h" + #endif diff --git a/chromium-vaapi-r15.patch b/chromium-vaapi-r16.patch similarity index 76% rename from chromium-vaapi-r15.patch rename to chromium-vaapi-r16.patch index b8dff36..0020ea7 100644 --- a/chromium-vaapi-r15.patch +++ b/chromium-vaapi-r16.patch @@ -36,13 +36,11 @@ Change-Id: Ifbbf5c9e5221a8b5733fc6d4d0cf984a1f103171 Signed-off-by: Daniel Charles --- -diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc -index 3555293..4c3115f 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc -@@ -1246,12 +1246,14 @@ - flag_descriptions::kUiPartialSwapDescription, kOsAll, - SINGLE_DISABLE_VALUE_TYPE(switches::kUIDisablePartialSwap)}, +@@ -1249,12 +1249,14 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kEnablePreventLayerSquashingDescription, kOsAll, + FEATURE_VALUE_TYPE(features::kEnablePreventLayerSquashing)}, #if BUILDFLAG(ENABLE_WEBRTC) +#if !defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) {"disable-webrtc-hw-decoding", flag_descriptions::kWebrtcHwDecodingName, @@ -55,9 +53,9 @@ index 3555293..4c3115f 100644 {"enable-webrtc-hw-h264-encoding", flag_descriptions::kWebrtcHwH264EncodingName, flag_descriptions::kWebrtcHwH264EncodingDescription, kOsAndroid | kOsCrOS, -@@ -1557,6 +1559,13 @@ - flag_descriptions::kSpuriousPowerButtonLidAngleChangeDescription, kOsCrOS, - MULTI_VALUE_TYPE(kSpuriousPowerButtonLidAngleChangeChoices)}, +@@ -1535,6 +1537,13 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kShowTouchHudDescription, kOsAll, + SINGLE_VALUE_TYPE(ash::switches::kAshTouchHud)}, #endif // OS_CHROMEOS +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) + { @@ -69,7 +67,7 @@ index 3555293..4c3115f 100644 { "disable-accelerated-video-decode", flag_descriptions::kAcceleratedVideoDecodeName, -@@ -1564,6 +1573,7 @@ +@@ -1542,6 +1551,7 @@ const FeatureEntry kFeatureEntries[] = { kOsMac | kOsWin | kOsCrOS | kOsAndroid, SINGLE_DISABLE_VALUE_TYPE(switches::kDisableAcceleratedVideoDecode), }, @@ -77,9 +75,9 @@ index 3555293..4c3115f 100644 {"mojo-video-encode-accelerator", flag_descriptions::kMojoVideoEncodeAcceleratorName, flag_descriptions::kMojoVideoEncodeAcceleratorDescription, -@@ -2229,12 +2239,17 @@ - FEATURE_VALUE_TYPE(features::kWebVrVsyncAlign)}, - #endif // OS_ANDROID +@@ -2270,12 +2280,17 @@ const FeatureEntry kFeatureEntries[] = { + FEATURE_VALUE_TYPE(features::kOpenVR)}, + #endif // ENABLE_OPENVR #endif // ENABLE_VR -#if defined(OS_CHROMEOS) +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) @@ -97,8 +95,6 @@ index 3555293..4c3115f 100644 {"v8-cache-options", flag_descriptions::kV8CacheOptionsName, flag_descriptions::kV8CacheOptionsDescription, kOsAll, MULTI_VALUE_TYPE(kV8CacheOptionsChoices)}, -diff --git a/chrome/browser/chromeos/login/chrome_restart_request.cc b/chrome/browser/chromeos/login/chrome_restart_request.cc -index 3815e79..d5d3712 100644 --- a/chrome/browser/chromeos/login/chrome_restart_request.cc +++ b/chrome/browser/chromeos/login/chrome_restart_request.cc @@ -19,6 +19,7 @@ @@ -109,7 +105,7 @@ index 3815e79..d5d3712 100644 #include "cc/base/switches.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/boot_times_recorder.h" -@@ -79,8 +80,13 @@ +@@ -84,8 +85,13 @@ void DeriveCommandLine(const GURL& start ::switches::kDisable2dCanvasImageChromium, ::switches::kDisableAccelerated2dCanvas, ::switches::kDisableAcceleratedJpegDecoding, @@ -123,7 +119,7 @@ index 3815e79..d5d3712 100644 ::switches::kDisableBlinkFeatures, ::switches::kDisableCastStreamingHWEncoding, ::switches::kDisableDistanceFieldText, -@@ -166,7 +172,7 @@ +@@ -166,7 +172,7 @@ void DeriveCommandLine(const GURL& start ::switches::kDisableWebGLImageChromium, ::switches::kEnableWebGLImageChromium, ::switches::kEnableWebVR, @@ -132,11 +128,9 @@ index 3815e79..d5d3712 100644 ::switches::kDisableWebRtcHWDecoding, ::switches::kDisableWebRtcHWEncoding, #endif -diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc -index 74e6bc7..ab184a5 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc -@@ -14,6 +14,13 @@ +@@ -19,6 +19,13 @@ const char kAccelerated2dCanvasDescripti "Enables the use of the GPU to perform 2d canvas rendering instead of " "using software rendering."; @@ -150,7 +144,7 @@ index 74e6bc7..ab184a5 100644 const char kAcceleratedVideoDecodeName[] = "Hardware-accelerated video decode"; const char kAcceleratedVideoDecodeDescription[] = "Hardware-accelerated video decode where available."; -@@ -1478,6 +1485,7 @@ +@@ -1572,6 +1579,7 @@ const char kWebrtcEchoCanceller3Name[] = const char kWebrtcEchoCanceller3Description[] = "Experimental WebRTC echo canceller (AEC3)."; @@ -158,7 +152,7 @@ index 74e6bc7..ab184a5 100644 const char kWebrtcHwDecodingName[] = "WebRTC hardware video decoding"; const char kWebrtcHwDecodingDescription[] = "Support in WebRTC for decoding video streams using platform hardware."; -@@ -1485,6 +1493,7 @@ +@@ -1579,6 +1587,7 @@ const char kWebrtcHwDecodingDescription[ const char kWebrtcHwEncodingName[] = "WebRTC hardware video encoding"; const char kWebrtcHwEncodingDescription[] = "Support in WebRTC for encoding video streams using platform hardware."; @@ -166,7 +160,7 @@ index 74e6bc7..ab184a5 100644 const char kWebrtcHwH264EncodingName[] = "WebRTC hardware h264 video encoding"; const char kWebrtcHwH264EncodingDescription[] = -@@ -2283,14 +2292,16 @@ +@@ -2390,14 +2399,16 @@ const char kTranslateNewUxDescription[] // Chrome OS ------------------------------------------------------------------- @@ -184,11 +178,9 @@ index 74e6bc7..ab184a5 100644 const char kAllowTouchpadThreeFingerClickName[] = "Touchpad three-finger-click"; const char kAllowTouchpadThreeFingerClickDescription[] = "Enables touchpad three-finger-click as middle button."; -diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h -index 54a4950..5ace5e0 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h -@@ -37,6 +37,10 @@ +@@ -37,6 +37,10 @@ namespace flag_descriptions { extern const char kAccelerated2dCanvasName[]; extern const char kAccelerated2dCanvasDescription[]; @@ -199,7 +191,7 @@ index 54a4950..5ace5e0 100644 extern const char kAcceleratedVideoDecodeName[]; extern const char kAcceleratedVideoDecodeDescription[]; -@@ -1401,13 +1405,17 @@ +@@ -1480,13 +1484,17 @@ extern const char kPermissionPromptPersi #endif // defined(OS_MACOSX) @@ -220,11 +212,9 @@ index 54a4950..5ace5e0 100644 extern const char kAllowTouchpadThreeFingerClickName[]; extern const char kAllowTouchpadThreeFingerClickDescription[]; -diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc -index 122282d..bc102a3 100644 --- a/content/browser/gpu/compositor_util.cc +++ b/content/browser/gpu/compositor_util.cc -@@ -105,7 +105,11 @@ +@@ -103,7 +103,11 @@ const GpuFeatureInfo GetGpuFeatureInfo(s {"video_decode", manager->IsFeatureBlacklisted( gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE), @@ -236,23 +226,9 @@ index 122282d..bc102a3 100644 "Accelerated video decode has been disabled, either via blacklist," " about:flags or the command line.", true}, -@@ -113,7 +117,11 @@ - {"video_encode", - manager->IsFeatureBlacklisted( - gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_ENCODE), -+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -+ !command_line.HasSwitch(switches::kEnableAcceleratedVideo), -+#else - command_line.HasSwitch(switches::kDisableWebRtcHWEncoding), -+#endif - "Accelerated video encode has been disabled, either via blacklist," - " about:flags or the command line.", - true}, -diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc -index 51aa672..963da85 100644 --- a/content/browser/gpu/gpu_data_manager_impl_private.cc +++ b/content/browser/gpu/gpu_data_manager_impl_private.cc -@@ -717,7 +717,11 @@ +@@ -709,7 +709,11 @@ void GpuDataManagerImplPrivate::AppendRe DCHECK(command_line); if (ShouldDisableAcceleratedVideoDecode(command_line)) @@ -261,24 +237,12 @@ index 51aa672..963da85 100644 +#else command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode); +#endif + } - #if defined(USE_AURA) - if (!CanUseGpuBrowserCompositor()) -@@ -748,7 +752,11 @@ - } - - if (ShouldDisableAcceleratedVideoDecode(command_line)) { -+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -+ command_line->AppendSwitch(switches::kEnableAcceleratedVideo); -+#else - command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode); -+#endif + void GpuDataManagerImplPrivate::AppendGpuCommandLine( +@@ -810,7 +814,12 @@ void GpuDataManagerImplPrivate::UpdateRe + prefs->accelerated_2d_canvas_enabled = false; } - - #if defined(USE_OZONE) -@@ -826,7 +834,12 @@ - const base::CommandLine* command_line = - base::CommandLine::ForCurrentProcess(); if (!ShouldDisableAcceleratedVideoDecode(command_line) && - !command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) { +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) @@ -290,10 +254,10 @@ index 51aa672..963da85 100644 prefs->pepper_accelerated_video_decode_enabled = true; } } -@@ -1016,7 +1029,13 @@ - // to resolve crbug/442039 has been collected. - const std::string group_name = base::FieldTrialList::FindFullName( - "DisableAcceleratedVideoDecode"); +@@ -959,7 +968,13 @@ bool GpuDataManagerImplPrivate::UpdateAc + + bool GpuDataManagerImplPrivate::ShouldDisableAcceleratedVideoDecode( + const base::CommandLine* command_line) const { - if (command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) { + if ( +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) @@ -305,36 +269,31 @@ index 51aa672..963da85 100644 // It was already disabled on the command line. return false; } -diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc -index 95d4dff..9b26d3f 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc -@@ -111,7 +111,11 @@ - - // Command-line switches to propagate to the GPU process. - static const char* const kSwitchNames[] = { +@@ -114,13 +114,18 @@ static const char* const kSwitchNames[] + service_manager::switches::kDisableSeccompFilterSandbox, + service_manager::switches::kGpuSandboxAllowSysVShm, + service_manager::switches::kGpuSandboxFailuresFatal, +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) + switches::kEnableAcceleratedVideo, +#else - switches::kDisableAcceleratedVideoDecode, ++ switches::kDisableAcceleratedVideoDecode, +#endif switches::kDisableBreakpad, switches::kDisableGpuRasterization, switches::kDisableGpuSandbox, -@@ -120,7 +124,7 @@ + switches::kDisableGLExtensions, switches::kDisableLogging, - switches::kDisableSeccompFilterSandbox, switches::kDisableShaderNameHashing, -#if BUILDFLAG(ENABLE_WEBRTC) +#if BUILDFLAG(ENABLE_WEBRTC) && !defined(OS_LINUX) switches::kDisableWebRtcHWEncoding, #endif #if defined(OS_WIN) -diff --git a/content/browser/renderer_host/media/video_capture_browsertest.cc b/content/browser/renderer_host/media/video_capture_browsertest.cc -index 8ca0ad0..8c489d0 100644 --- a/content/browser/renderer_host/media/video_capture_browsertest.cc +++ b/content/browser/renderer_host/media/video_capture_browsertest.cc -@@ -155,8 +155,13 @@ +@@ -164,8 +164,13 @@ class VideoCaptureBrowserTest : public C base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kUseFakeJpegDecodeAccelerator); } else { @@ -346,13 +305,11 @@ index 8ca0ad0..8c489d0 100644 switches::kDisableAcceleratedMjpegDecode); +#endif } - if (params_.use_mojo_service) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( -diff --git a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc -index 34afec23..ad77490 100644 + } + --- a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc +++ b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc -@@ -56,15 +56,21 @@ +@@ -64,15 +64,21 @@ void VideoCaptureGpuJpegDecoder::Initial bool is_platform_supported = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseFakeJpegDecodeAccelerator); @@ -378,11 +335,9 @@ index 34afec23..ad77490 100644 decoder_status_ = FAILED; RecordInitDecodeUMA_Locked(); return; -diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc -index fbe4d21..1be295e 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -2506,7 +2506,11 @@ +@@ -2526,7 +2526,11 @@ void RenderProcessHostImpl::PropagateBro switches::kDefaultTileHeight, switches::kDisable2dCanvasImageChromium, switches::kDisableAcceleratedJpegDecoding, @@ -394,7 +349,7 @@ index fbe4d21..1be295e 100644 switches::kDisableBackgroundTimerThrottling, switches::kDisableBreakpad, switches::kDisableBrowserSideNavigation, -@@ -2660,8 +2664,10 @@ +@@ -2674,8 +2678,10 @@ void RenderProcessHostImpl::PropagateBro switches::kDisableMojoRenderer, #endif #if BUILDFLAG(ENABLE_WEBRTC) @@ -405,11 +360,9 @@ index fbe4d21..1be295e 100644 switches::kEnableWebRtcSrtpAesGcm, switches::kEnableWebRtcSrtpEncryptedHeaders, switches::kEnableWebRtcStunOrigin, -diff --git a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc -index 3d815a2..2c96048 100644 --- a/content/browser/webrtc/webrtc_media_recorder_browsertest.cc +++ b/content/browser/webrtc/webrtc_media_recorder_browsertest.cc -@@ -58,7 +58,12 @@ +@@ -58,7 +58,12 @@ class WebRtcMediaRecorderTest return; // This flag is also used for encoding, https://crbug.com/616640. base::CommandLine::ForCurrentProcess()->AppendSwitch( @@ -423,11 +376,9 @@ index 3d815a2..2c96048 100644 } private: -diff --git a/content/gpu/BUILD.gn b/content/gpu/BUILD.gn -index a5424bb..b68b802 100644 --- a/content/gpu/BUILD.gn +++ b/content/gpu/BUILD.gn -@@ -45,7 +45,6 @@ +@@ -48,7 +48,6 @@ target(link_target_type, "gpu_sources") ] configs += [ "//content:content_implementation" ] @@ -435,20 +386,18 @@ index a5424bb..b68b802 100644 deps = [ "//base", "//base/third_party/dynamic_annotations", -@@ -114,4 +113,8 @@ - if (enable_vulkan) { - deps += [ "//gpu/vulkan" ] +@@ -124,4 +123,8 @@ target(link_target_type, "gpu_sources") + if (is_desktop_linux && (!is_chromecast || is_cast_desktop_build)) { + configs += [ "//build/config/linux/dri" ] } + + if (is_desktop_linux) { + public_configs = [ "//media/gpu:libva_config" ] + } } -diff --git a/content/gpu/gpu_main.cc b/content/gpu/gpu_main.cc -index 556cf00..730f4ae 100644 --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc -@@ -254,7 +254,7 @@ +@@ -271,7 +271,7 @@ int GpuMain(const MainFunctionParams& pa // Initializes StatisticsRecorder which tracks UMA histograms. base::StatisticsRecorder::Initialize(); @@ -457,7 +406,7 @@ index 556cf00..730f4ae 100644 // Set thread priority before sandbox initialization. base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); #endif -@@ -283,7 +283,7 @@ +@@ -300,7 +300,7 @@ int GpuMain(const MainFunctionParams& pa GetContentClient()->SetGpuInfo(gpu_init->gpu_info()); base::ThreadPriority io_thread_priority = base::ThreadPriority::NORMAL; @@ -466,8 +415,6 @@ index 556cf00..730f4ae 100644 io_thread_priority = base::ThreadPriority::DISPLAY; #endif -diff --git a/content/public/browser/gpu_utils.cc b/content/public/browser/gpu_utils.cc -index 6aafb06..d4ddd8d 100644 --- a/content/public/browser/gpu_utils.cc +++ b/content/public/browser/gpu_utils.cc @@ -7,6 +7,7 @@ @@ -478,9 +425,9 @@ index 6aafb06..d4ddd8d 100644 #include "content/browser/gpu/gpu_process_host.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" -@@ -57,12 +58,19 @@ - gpu_preferences.ui_prioritize_in_gpu_process = - command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess); +@@ -55,12 +56,19 @@ const gpu::GpuPreferences GetGpuPreferen + gpu_preferences.in_process_gpu = + command_line->HasSwitch(switches::kInProcessGPU); gpu_preferences.disable_accelerated_video_decode = +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) + !command_line->HasSwitch(switches::kEnableAcceleratedVideo); @@ -500,11 +447,9 @@ index 6aafb06..d4ddd8d 100644 gpu_preferences.disable_web_rtc_hw_encoding = command_line->HasSwitch(switches::kDisableWebRtcHWEncoding); #endif -diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc -index 4e699d1..3a7a2b7 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc -@@ -89,12 +89,21 @@ +@@ -78,12 +78,21 @@ const char kDisable3DAPIs[] // Disable gpu-accelerated 2d canvas. const char kDisableAccelerated2dCanvas[] = "disable-accelerated-2d-canvas"; @@ -527,7 +472,7 @@ index 4e699d1..3a7a2b7 100644 // Disables hardware acceleration of video decode, where available. const char kDisableAcceleratedVideoDecode[] = "disable-accelerated-video-decode"; -@@ -932,11 +941,13 @@ +@@ -903,11 +912,13 @@ const char kZygoteProcess[] // ignores this switch on its stable and beta channels. const char kDisableWebRtcEncryption[] = "disable-webrtc-encryption"; @@ -541,11 +486,9 @@ index 4e699d1..3a7a2b7 100644 // Enables negotiation of GCM cipher suites from RFC 7714 for SRTP in WebRTC. // See https://tools.ietf.org/html/rfc7714 for further information. -diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h -index 2704924..67d00bb 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h -@@ -35,7 +35,11 @@ +@@ -33,7 +33,11 @@ CONTENT_EXPORT extern const char kDisabl CONTENT_EXPORT extern const char kDisable3DAPIs[]; CONTENT_EXPORT extern const char kDisableAccelerated2dCanvas[]; CONTENT_EXPORT extern const char kDisableAcceleratedJpegDecoding[]; @@ -557,7 +500,7 @@ index 2704924..67d00bb 100644 CONTENT_EXPORT extern const char kDisableAcceleratedVideoDecode[]; CONTENT_EXPORT extern const char kDisableAudioSupportForDesktopShare[]; extern const char kDisableBackingStoreLimit[]; -@@ -109,6 +113,9 @@ +@@ -107,6 +111,9 @@ CONTENT_EXPORT extern const char kDisabl CONTENT_EXPORT extern const char kDomAutomationController[]; extern const char kDisable2dCanvasClipAntialiasing[]; CONTENT_EXPORT extern const char kDumpBlinkRuntimeCallStats[]; @@ -567,7 +510,7 @@ index 2704924..67d00bb 100644 CONTENT_EXPORT extern const char kEnableAggressiveDOMStorageFlushing[]; CONTENT_EXPORT extern const char kEnableAutomation[]; CONTENT_EXPORT extern const char kEnablePreferCompositingToLCDText[]; -@@ -256,8 +263,10 @@ +@@ -248,8 +255,10 @@ CONTENT_EXPORT extern const char kZygote #if BUILDFLAG(ENABLE_WEBRTC) CONTENT_EXPORT extern const char kDisableWebRtcEncryption[]; @@ -578,11 +521,9 @@ index 2704924..67d00bb 100644 CONTENT_EXPORT extern const char kEnableWebRtcSrtpAesGcm[]; CONTENT_EXPORT extern const char kEnableWebRtcSrtpEncryptedHeaders[]; CONTENT_EXPORT extern const char kEnableWebRtcStunOrigin[]; -diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc -index 017056c..825bdce 100644 --- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc +++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc -@@ -239,10 +239,19 @@ +@@ -243,10 +243,19 @@ void PeerConnectionDependencyFactory::In const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (gpu_factories && gpu_factories->IsGpuVideoAcceleratorEnabled()) { @@ -603,11 +544,9 @@ index 017056c..825bdce 100644 encoder_factory.reset(new RTCVideoEncoderFactory(gpu_factories)); } } -diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc -index dc01117..2f9ee03 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc -@@ -1456,7 +1456,11 @@ +@@ -1495,7 +1495,11 @@ media::GpuVideoAcceleratorFactories* Ren scoped_refptr media_task_runner = GetMediaThreadTaskRunner(); const bool enable_video_accelerator = @@ -617,26 +556,11 @@ index dc01117..2f9ee03 100644 !cmd_line->HasSwitch(switches::kDisableAcceleratedVideoDecode); +#endif const bool enable_gpu_memory_buffer_video_frames = + !is_gpu_compositing_disabled_ && #if defined(OS_MACOSX) || defined(OS_LINUX) - !cmd_line->HasSwitch(switches::kDisableGpuMemoryBufferVideoFrames) && -diff --git a/gpu/command_buffer/service/gpu_preferences.h b/gpu/command_buffer/service/gpu_preferences.h -index b37c2cc..a721eb7 100644 ---- a/gpu/command_buffer/service/gpu_preferences.h -+++ b/gpu/command_buffer/service/gpu_preferences.h -@@ -50,7 +50,7 @@ - // Disables hardware acceleration of video decode, where available. - bool disable_accelerated_video_decode = false; - --#if defined(OS_CHROMEOS) -+#if defined(OS_CHROMEOS) || defined(OS_LINUX) - // Disables VA-API accelerated video encode. - bool disable_vaapi_accelerated_video_encode = false; - #endif -diff --git a/gpu/config/software_rendering_list.json b/gpu/config/software_rendering_list.json -index 1289a55..22e11e5 100644 --- a/gpu/config/software_rendering_list.json +++ b/gpu/config/software_rendering_list.json -@@ -374,17 +374,6 @@ +@@ -373,17 +373,6 @@ ] }, { @@ -654,24 +578,9 @@ index 1289a55..22e11e5 100644 "id": 50, "description": "Disable VMware software renderer on older Mesa", "cr_bugs": [145531, 332596, 571899, 629434], -diff --git a/media/filters/BUILD.gn b/media/filters/BUILD.gn -index 490722c..1cda2fb 100644 ---- a/media/filters/BUILD.gn -+++ b/media/filters/BUILD.gn -@@ -189,7 +189,7 @@ - deps += [ "//media/base/android" ] - } - -- if (current_cpu != "arm" && is_chromeos) { -+ if (current_cpu != "arm" && (is_chromeos || is_desktop_linux)) { - sources += [ - "h264_bitstream_buffer.cc", - "h264_bitstream_buffer.h", -diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn -index 729c6fa..4878251 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn -@@ -24,6 +24,14 @@ +@@ -25,6 +25,14 @@ if (is_mac) { import("//build/config/mac/mac_sdk.gni") } @@ -685,22 +594,21 @@ index 729c6fa..4878251 100644 + if (use_vaapi) { action("libva_generate_stubs") { - extra_header = "va_stub_header.fragment" -@@ -334,6 +342,9 @@ - "vaapi_drm_picture.h", - ] + extra_header = "vaapi/va_stub_header.fragment" +@@ -356,6 +364,10 @@ component("gpu") { + if (use_ozone) { + deps += [ "//ui/ozone" ] } ++ + if (is_desktop_linux) { + public_configs = [ ":libva_config" ] + } } if (is_win) { -diff --git a/media/gpu/gpu_video_decode_accelerator_factory.cc b/media/gpu/gpu_video_decode_accelerator_factory.cc -index 2cee490..de02414 100644 --- a/media/gpu/gpu_video_decode_accelerator_factory.cc +++ b/media/gpu/gpu_video_decode_accelerator_factory.cc -@@ -87,6 +87,7 @@ +@@ -87,6 +87,7 @@ GpuVideoDecodeAcceleratorFactory::GetDec // profile (instead of calculating a superset). // TODO(posciak,henryhsu): improve this so that we choose a superset of // resolutions and other supported profile parameters. @@ -708,15 +616,3 @@ index 2cee490..de02414 100644 #if defined(OS_WIN) capabilities.supported_profiles = DXVAVideoDecodeAccelerator::GetSupportedProfiles(gpu_preferences, -diff --git a/media/gpu/vaapi_wrapper.cc b/media/gpu/vaapi_wrapper.cc -index 1e72893..6495642 100644 ---- a/media/gpu/vaapi_wrapper.cc -+++ b/media/gpu/vaapi_wrapper.cc -@@ -1117,6 +1117,7 @@ - if (drm_file.IsValid()) - GetDisplayState()->SetDrmFd(drm_file.GetPlatformFile()); - #endif -+ GetProfileInfos(); // dlopen all necessary libraries - } - - // static diff --git a/chromium-vaapi-rgbx.patch b/chromium-vaapi-rgbx.patch new file mode 100644 index 0000000..f6e1deb --- /dev/null +++ b/chromium-vaapi-rgbx.patch @@ -0,0 +1,35 @@ +From 51357dc19efbf30328ca05655fbf69886f6e9113 Mon Sep 17 00:00:00 2001 +From: Julien Isorce +Date: Tue, 05 Dec 2017 23:39:45 +0000 +Subject: [PATCH] Allow RGBX for VaapiTFPPicture + +Fixes regression on non-ozone platforms and introduced by + 73d609f366ba6a86324048bbad81527f76d237b5 + https://chromium-review.googlesource.com/787290 + +Note that the format is only used for sanity check. All the logic is +done automatically in the vaapi driver's implementation of vaPutSurface. + +Bug: 785201 +Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel +Change-Id: Idc0bdf184874bf2c238e28da6f42f4e3572e9743 +Reviewed-on: https://chromium-review.googlesource.com/807928 +Reviewed-by: Dan Sanders +Commit-Queue: Julien Isorce +Cr-Commit-Position: refs/heads/master@{#521897} +--- + +diff --git a/media/gpu/vaapi/vaapi_tfp_picture.cc b/media/gpu/vaapi/vaapi_tfp_picture.cc +index e9eecce..bd7823c 100644 +--- a/media/gpu/vaapi/vaapi_tfp_picture.cc ++++ b/media/gpu/vaapi/vaapi_tfp_picture.cc +@@ -72,7 +72,8 @@ + bool VaapiTFPPicture::Allocate(gfx::BufferFormat format) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (format != gfx::BufferFormat::BGRX_8888 && +- format != gfx::BufferFormat::BGRA_8888) { ++ format != gfx::BufferFormat::BGRA_8888 && ++ format != gfx::BufferFormat::RGBX_8888) { + DLOG(ERROR) << "Unsupported format"; + return false; + } From 14e7d294b290fc31b918737c98a57f9cab634364 Mon Sep 17 00:00:00 2001 From: xsmile Date: Wed, 31 Jan 2018 13:52:18 +0100 Subject: [PATCH 6/9] add patch to build without ReportingService (#140) Allow building with the setting enable_reporting=false to exclude the ReportingService. --- 0002-fix-building-without-reporting.patch | 112 ++++++++++++++++++++++ PKGBUILD | 8 +- 2 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 0002-fix-building-without-reporting.patch diff --git a/0002-fix-building-without-reporting.patch b/0002-fix-building-without-reporting.patch new file mode 100644 index 0000000..2e3ead3 --- /dev/null +++ b/0002-fix-building-without-reporting.patch @@ -0,0 +1,112 @@ +# Fix building with enable_reporting=false +# In profile_impl_io_data.h/cc and reporting_service_proxy.cc the devs forgot to check if reporting was enabled before using a reporting feature +# In printer_handler.cc, due to some combination of build flags used in the ungoogled-chromium build, MakeUnique was not pulled in + +--- a/chrome/browser/ui/webui/print_preview/printer_handler.cc ++++ b/chrome/browser/ui/webui/print_preview/printer_handler.cc +@@ -8,6 +8,7 @@ + #include "chrome/browser/ui/webui/print_preview/extension_printer_handler.h" + #include "chrome/browser/ui/webui/print_preview/pdf_printer_handler.h" + #include "chrome/common/features.h" ++#include "base/memory/ptr_util.h" + + #if BUILDFLAG(ENABLE_SERVICE_DISCOVERY) + #include "chrome/browser/ui/webui/print_preview/privet_printer_handler.h" +--- a/content/browser/net/reporting_service_proxy.cc ++++ b/content/browser/net/reporting_service_proxy.cc +@@ -97,6 +97,7 @@ class ReportingServiceProxyImpl : public + const std::string& group, + const std::string& type, + std::unique_ptr body) { ++#if BUILDFLAG(ENABLE_REPORTING) + net::URLRequestContext* request_context = + request_context_getter_->GetURLRequestContext(); + if (!request_context) { +@@ -112,6 +113,7 @@ class ReportingServiceProxyImpl : public + } + + reporting_service->QueueReport(url, group, type, std::move(body)); ++#endif + } + + scoped_refptr request_context_getter_; +--- a/chrome/browser/profiles/profile_impl_io_data.h ++++ b/chrome/browser/profiles/profile_impl_io_data.h +@@ -182,6 +182,7 @@ class ProfileImplIOData : public Profile + const StoragePartitionDescriptor& partition_descriptor) const override; + chrome_browser_net::Predictor* GetPredictor() override; + ++#if BUILDFLAG(ENABLE_REPORTING) + // Returns a net::ReportingService, if reporting should be enabled. Otherwise, + // returns nullptr. + // TODO(mmenke): Remove once URLRequestContextBuilders are always used to +@@ -192,6 +193,7 @@ class ProfileImplIOData : public Profile + // Returns a net::ReportingPolicy, if reporting should be enabled. Otherwise, + // returns nullptr. + static std::unique_ptr MaybeCreateReportingPolicy(); ++#endif + + // Lazy initialization params. + mutable std::unique_ptr lazy_params_; +--- a/chrome/browser/profiles/profile_impl_io_data.cc ++++ b/chrome/browser/profiles/profile_impl_io_data.cc +@@ -472,7 +472,9 @@ void ProfileImplIOData::InitializeIntern + builder, std::move(request_interceptors), + std::move(profile_params->protocol_handler_interceptor)); + ++#if BUILDFLAG(ENABLE_REPORTING) + builder->set_reporting_policy(MaybeCreateReportingPolicy()); ++#endif + } + + void ProfileImplIOData::OnMainRequestContextCreated( +@@ -608,7 +610,9 @@ net::URLRequestContext* ProfileImplIODat + context->host_resolver())); + context->SetJobFactory(std::move(top_job_factory)); + ++#if BUILDFLAG(ENABLE_REPORTING) + context->SetReportingService(MaybeCreateReportingService(context)); ++#endif + + return context; + } +@@ -698,6 +702,7 @@ chrome_browser_net::Predictor* ProfileIm + return predictor_.get(); + } + ++#if BUILDFLAG(ENABLE_REPORTING) + std::unique_ptr + ProfileImplIOData::MaybeCreateReportingService( + net::URLRequestContext* url_request_context) const { +@@ -716,3 +721,4 @@ ProfileImplIOData::MaybeCreateReportingP + + return base::MakeUnique(); + } ++#endif +--- a/chrome/browser/profiles/profile_io_data.cc ++++ b/chrome/browser/profiles/profile_io_data.cc +@@ -638,7 +638,9 @@ + void ProfileIOData::AppRequestContext::SetReportingService( + std::unique_ptr reporting_service) { + reporting_service_ = std::move(reporting_service); ++#if BUILDFLAG(ENABLE_REPORTING) + set_reporting_service(reporting_service_.get()); ++#endif + } + + ProfileIOData::AppRequestContext::~AppRequestContext() { +--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc ++++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc +@@ -252,10 +252,12 @@ + const base::Callback& origin_filter) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + ++#if BUILDFLAG(ENABLE_REPORTING) + net::ReportingService* service = + context->GetURLRequestContext()->reporting_service(); + if (service) + service->RemoveBrowsingData(data_type_mask, origin_filter); ++#endif + } + + #if defined(OS_ANDROID) diff --git a/PKGBUILD b/PKGBUILD index 57a146e..dffaf08 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -45,6 +45,7 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-r16.patch # Inox patchset https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0001-fix-building-without-safebrowsing.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0002-fix-building-without-reporting.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0003-disable-autofill-download-manager.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0004-disable-google-url-tracker.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0005-disable-default-extensions.patch @@ -65,8 +66,6 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0020-launcher-branding.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0021-disable-rlz.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/9000-disable-metrics.patch) - - sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' '4dc3428f2c927955d9ae117f2fb24d098cc6dd67adb760ac9c82b522ec8b0587' 'a86a8ec67aed5a94557257b9826c5b8fe37005e8376e75986fee77acd066539a' @@ -90,6 +89,7 @@ sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' '0a9186ab591773f8fb6cbc908f9bbf4bc1508f1095b6c1cd7479aac945045373' 'b82047df666e6bbf66e0c0911d20c5001bd1100fd08adafa92cac5f02a887a01' 'd1e112adb135a823907aae33b189cb775d48e6afa785a26a452fc833824cd2e8' + '300a7f3f0aba2037d159e5815f5cb3f2699c0ac26cbbb61209bbf01ac1eb2efb' '605cca8be9828a29cc96d473847eef9452d572fe6a56dacd96426a202310ba58' 'fb91a7e30e2615e4eb0626b0fdcf97b92d4a727a52023730f408b02fee436c8d' '6ba0ad7d91b2f3bbf03fc4a3236a04310a0c57505e1688c7e11ace9dcea1dded' @@ -193,6 +193,7 @@ prepare() { msg2 'Applying Inox patchset' # Apply patches to fix building patch -Np1 -i ../0001-fix-building-without-safebrowsing.patch + patch -Np1 -i ../0002-fix-building-without-reporting.patch # Apply Inox patches patch -Np1 -i ../0003-disable-autofill-download-manager.patch @@ -268,8 +269,6 @@ build() { export AR=llvm-ar export NM=llvm-nm - # TODO: enable_mdns=false (linker error) - # TODO: enable_reporting=false (compiler error) local _flags=( 'custom_toolchain="//build/toolchain/linux/unbundle:default"' 'host_toolchain="//build/toolchain/linux/unbundle:default"' @@ -302,6 +301,7 @@ build() { 'enable_nacl_nonsfi=false' 'enable_remoting=false' 'enable_google_now=false' + 'enable_reporting=false' 'safe_browsing_mode=0' ) From a53c943d1d4f2d47f9ea677232cd98521ee98392 Mon Sep 17 00:00:00 2001 From: xsmile Date: Mon, 5 Feb 2018 18:13:34 +0100 Subject: [PATCH 7/9] 64.0.3282.140: Update (#141) --- PKGBUILD | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index dffaf08..c490251 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -5,7 +5,7 @@ # Contributor: Daniel J Griffiths pkgname=inox -pkgver=64.0.3282.119 +pkgver=64.0.3282.140 pkgrel=1 _launcher_ver=5 pkgdesc="Chromium Spin-off to enhance privacy by disabling data transmission to Google" @@ -66,9 +66,9 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0020-launcher-branding.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0021-disable-rlz.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/9000-disable-metrics.patch) -sha256sums=('342ea80a925d85f5155b2b423a0d3cbcf2ee5729bf107c601d7d902315d03127' +sha256sums=('146afbab37982c52251e5c71b6e19e6e7053b527217fe1da9966c794478c29ce' '4dc3428f2c927955d9ae117f2fb24d098cc6dd67adb760ac9c82b522ec8b0587' - 'a86a8ec67aed5a94557257b9826c5b8fe37005e8376e75986fee77acd066539a' + 'cc3a328836af87f3a262ac7a7bc848e0f3a4b2f9f0346ef76b9b059c6f6d32bc' '71471fa4690894420f9e04a2e9a622af620d92ac2714a35f9a4c4e90fa3968dd' '4a533acefbbc1567b0d74a1c0903e9179b8c59c1beabe748850795815366e509' '7b88830c5e0e9819f514ad68aae885d427541a907e25607e47dee1b0f38975fd' @@ -272,7 +272,6 @@ build() { local _flags=( 'custom_toolchain="//build/toolchain/linux/unbundle:default"' 'host_toolchain="//build/toolchain/linux/unbundle:default"' - 'use_lld=false' 'clang_use_chrome_plugins=false' 'symbol_level=0' 'is_debug=false' From ac06c5c1075d4d577e57ac67367dd30facb373ac Mon Sep 17 00:00:00 2001 From: xsmile Date: Thu, 15 Mar 2018 17:13:28 +0100 Subject: [PATCH 8/9] Update to 65.0.3325.146 (#145) * PKGBUILD: upstream changes * re-add lld dependency * 64.0.3282.186: Update * 65.0.3325.146: Update Inox patches * 65.0.3325.146: Update build patches --- 0001-fix-building-without-safebrowsing.patch | 327 +- 0002-fix-building-without-reporting.patch | 22 +- 0003-disable-autofill-download-manager.patch | 44 +- 0005-disable-default-extensions.patch | 6 +- 0006-modify-default-prefs.patch | 12 +- 0008-restore-classic-ntp.patch | 4 +- 0011-add-duckduckgo-search-engine.patch | 2 +- 0012-branding.patch | 8 +- 0013-disable-missing-key-warning.patch | 8 +- 0017-disable-new-avatar-menu.patch | 2 +- 9000-disable-metrics.patch | 22 +- PKGBUILD | 91 +- chromium-exclude_unwind_tables.patch | 33 - chromium-libva-r2.patch | 35 - chromium-math.h-r0.patch | 29 + chromium-memcpy-r0.patch | 34 - chromium-omnibox-unescape-fragment.patch | 437 - chromium-stdint.patch | 21 + ...mUTF8-for-UnicodeString-construction.patch | 67 - ...nit.patch => chromium-vaapi-init-r16.patch | 78 +- chromium-vaapi-move.patch | 15235 ---------------- chromium-vaapi-r16.patch | 206 +- chromium-vaapi-rgbx.patch | 35 - 23 files changed, 384 insertions(+), 16374 deletions(-) delete mode 100644 chromium-exclude_unwind_tables.patch delete mode 100644 chromium-libva-r2.patch create mode 100644 chromium-math.h-r0.patch delete mode 100644 chromium-memcpy-r0.patch delete mode 100644 chromium-omnibox-unescape-fragment.patch create mode 100644 chromium-stdint.patch delete mode 100644 chromium-use-fromUTF8-for-UnicodeString-construction.patch rename chromium-vaapi-init.patch => chromium-vaapi-init-r16.patch (77%) delete mode 100644 chromium-vaapi-move.patch delete mode 100644 chromium-vaapi-rgbx.patch diff --git a/0001-fix-building-without-safebrowsing.patch b/0001-fix-building-without-safebrowsing.patch index 0273505..3c0fbb3 100644 --- a/0001-fix-building-without-safebrowsing.patch +++ b/0001-fix-building-without-safebrowsing.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc -@@ -639,6 +639,7 @@ void SetApplicationLocaleOnIOThread(cons +@@ -645,6 +645,7 @@ void SetApplicationLocaleOnIOThread(cons g_io_thread_application_locale.Get() = locale; } @@ -8,7 +8,7 @@ // An implementation of the SSLCertReporter interface used by // SSLErrorHandler. Uses CertificateReportingService to send reports. The // service handles queueing and re-sending of failed reports. Each certificate -@@ -654,7 +655,6 @@ class CertificateReportingServiceCertRep +@@ -660,7 +661,6 @@ class CertificateReportingServiceCertRep // SSLCertReporter implementation void ReportInvalidCertificateChain( const std::string& serialized_report) override { @@ -16,7 +16,7 @@ } private: -@@ -662,6 +662,7 @@ class CertificateReportingServiceCertRep +@@ -668,6 +668,7 @@ class CertificateReportingServiceCertRep DISALLOW_COPY_AND_ASSIGN(CertificateReportingServiceCertReporter); }; @@ -24,7 +24,7 @@ #if defined(OS_ANDROID) float GetDeviceScaleAdjustment() { -@@ -1752,7 +1753,7 @@ void ChromeContentBrowserClient::AppendE +@@ -1707,7 +1708,7 @@ void ChromeContentBrowserClient::AppendE // Disable client-side phishing detection in the renderer if it is // disabled in the Profile preferences or the browser process. if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled) || @@ -33,16 +33,16 @@ command_line->AppendSwitch( switches::kDisableClientSidePhishingDetection); } -@@ -2361,7 +2362,7 @@ void ChromeContentBrowserClient::AllowCe +@@ -2304,7 +2305,7 @@ void ChromeContentBrowserClient::AllowCe SSLErrorHandler::HandleSSLError( - web_contents, cert_error, ssl_info, request_url, strict_enforcement, + web_contents, cert_error, ssl_info, request_url, expired_previous_decision, - std::make_unique(web_contents), + nullptr, callback, SSLErrorHandler::BlockingPageReadyCallback()); } -@@ -2551,8 +2552,6 @@ bool ChromeContentBrowserClient::CanCrea +@@ -2535,8 +2536,6 @@ bool ChromeContentBrowserClient::CanCrea void ChromeContentBrowserClient::ResourceDispatcherHostCreated() { DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -69,7 +69,7 @@ #if defined(OS_WIN) if (base::FeatureList::IsEnabled(features::kModuleDatabase)) { // Add the ModuleEventSink interface. This is the interface used by renderer -@@ -3508,7 +3496,7 @@ ChromeContentBrowserClient::CreateThrott +@@ -3491,7 +3479,7 @@ ChromeContentBrowserClient::CreateThrott switches::kCommittedInterstitials)) { throttles.push_back(std::make_unique( handle, @@ -78,7 +78,7 @@ base::Bind(&SSLErrorHandler::HandleSSLError))); } -@@ -3697,12 +3685,6 @@ ChromeContentBrowserClient::CreateURLLoa +@@ -3684,12 +3672,6 @@ ChromeContentBrowserClient::CreateURLLoa std::vector> result; @@ -88,10 +88,10 @@ - if (safe_browsing_throttle) - result.push_back(std::move(safe_browsing_throttle)); - - return result; - } - -@@ -3781,18 +3763,3 @@ void ChromeContentBrowserClient::SetDefa + ChromeNavigationUIData* chrome_navigation_ui_data = + static_cast(navigation_ui_data); + if (chrome_navigation_ui_data && +@@ -3872,18 +3854,3 @@ void ChromeContentBrowserClient::SetDefa const storage::QuotaSettings* settings) { g_default_quota_settings = settings; } @@ -112,7 +112,7 @@ -} --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc -@@ -494,18 +494,6 @@ ProfileImpl::ProfileImpl( +@@ -480,18 +480,6 @@ ProfileImpl::ProfileImpl( create_mode == CREATE_MODE_SYNCHRONOUS); #endif @@ -131,7 +131,7 @@ content::BrowserContext::Initialize(this, path_); { -@@ -514,7 +502,7 @@ ProfileImpl::ProfileImpl( +@@ -500,7 +488,7 @@ ProfileImpl::ProfileImpl( ->CreateDelegate(); delegate->InitPrefRegistry(pref_registry_.get()); prefs_ = chrome_prefs::CreateProfilePrefs( @@ -142,7 +142,7 @@ GetIOTaskRunner(), std::move(delegate)); --- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc +++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc -@@ -406,8 +406,7 @@ void NotifyUIThreadOfRequestComplete( +@@ -417,8 +417,7 @@ void NotifyUIThreadOfRequestComplete( } // namespace ChromeResourceDispatcherHostDelegate::ChromeResourceDispatcherHostDelegate() @@ -152,7 +152,7 @@ #if BUILDFLAG(ENABLE_EXTENSIONS) , user_script_listener_(new extensions::UserScriptListener()) #endif -@@ -457,8 +456,6 @@ void ChromeResourceDispatcherHostDelegat +@@ -468,8 +467,6 @@ void ChromeResourceDispatcherHostDelegat content::AppCacheService* appcache_service, ResourceType resource_type, std::vector>* throttles) { @@ -161,7 +161,7 @@ ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context); client_hints::RequestBeginning(request, io_data->GetCookieSettings()); -@@ -644,13 +641,13 @@ void ChromeResourceDispatcherHostDelegat +@@ -642,13 +639,13 @@ void ChromeResourceDispatcherHostDelegat content::ResourceThrottle* first_throttle = NULL; #if defined(OS_ANDROID) first_throttle = DataReductionProxyResourceThrottle::MaybeCreate( @@ -179,7 +179,7 @@ --- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc +++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc -@@ -248,6 +248,7 @@ BadClockBlockingPage* CreateBadClockBloc +@@ -250,6 +250,7 @@ BadClockBlockingPage* CreateBadClockBloc base::Callback()); } @@ -187,7 +187,7 @@ safe_browsing::SafeBrowsingBlockingPage* CreateSafeBrowsingBlockingPage( content::WebContents* web_contents) { safe_browsing::SBThreatType threat_type = -@@ -303,7 +304,9 @@ safe_browsing::SafeBrowsingBlockingPage* +@@ -305,7 +306,9 @@ safe_browsing::SafeBrowsingBlockingPage* g_browser_process->safe_browsing_service()->ui_manager().get(), web_contents, main_frame_url, resource); } @@ -197,7 +197,7 @@ TestSafeBrowsingBlockingPageQuiet* CreateSafeBrowsingQuietBlockingPage( content::WebContents* web_contents) { safe_browsing::SBThreatType threat_type = -@@ -351,6 +354,7 @@ TestSafeBrowsingBlockingPageQuiet* Creat +@@ -353,6 +356,7 @@ TestSafeBrowsingBlockingPageQuiet* Creat g_browser_process->safe_browsing_service()->ui_manager().get(), web_contents, main_frame_url, resource, is_giant_webview); } @@ -205,7 +205,7 @@ #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) CaptivePortalBlockingPage* CreateCaptivePortalBlockingPage( -@@ -455,8 +459,6 @@ void InterstitialHTMLSource::StartDataRe +@@ -457,8 +461,6 @@ void InterstitialHTMLSource::StartDataRe CreateSSLBlockingPage(web_contents, true /* is superfish */)); } else if (path_without_query == "/mitm-software-ssl") { interstitial_delegate.reset(CreateMITMSoftwareBlockingPage(web_contents)); @@ -214,7 +214,7 @@ } else if (path_without_query == "/clock") { interstitial_delegate.reset(CreateBadClockBlockingPage(web_contents)); #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) -@@ -467,11 +469,6 @@ void InterstitialHTMLSource::StartDataRe +@@ -469,11 +471,6 @@ void InterstitialHTMLSource::StartDataRe if (path_without_query == "/supervised_user") { html = GetSupervisedUserInterstitialHTML(path); @@ -334,7 +334,7 @@ } void ChromeDownloadManagerDelegate::Shutdown() { -@@ -556,16 +549,6 @@ download::InProgressCache* ChromeDownloa +@@ -555,16 +548,6 @@ download::InProgressCache* ChromeDownloa void ChromeDownloadManagerDelegate::SanitizeSavePackageResourceName( base::FilePath* filename) { @@ -353,7 +353,7 @@ void ChromeDownloadManagerDelegate::OpenDownloadUsingPlatformHandler( --- a/chrome/browser/browser_process_impl.cc +++ b/chrome/browser/browser_process_impl.cc -@@ -71,7 +71,6 @@ +@@ -70,7 +70,6 @@ #include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/printing/print_preview_dialog_controller.h" #include "chrome/browser/profiles/profile_manager.h" @@ -361,15 +361,7 @@ #include "chrome/browser/shell_integration.h" #include "chrome/browser/status_icons/status_tray.h" #include "chrome/browser/ui/browser_dialogs.h" -@@ -220,7 +219,6 @@ BrowserProcessImpl::BrowserProcessImpl( - created_icon_manager_(false), - created_notification_ui_manager_(false), - created_notification_bridge_(false), -- created_safe_browsing_service_(false), - created_subresource_filter_ruleset_service_(false), - created_optimization_guide_service_(false), - shutting_down_(false), -@@ -309,8 +307,6 @@ void BrowserProcessImpl::StartTearDown() +@@ -295,8 +294,6 @@ void BrowserProcessImpl::StartTearDown() // that URLFetcher operation before going away.) metrics_services_manager_.reset(); intranet_redirect_detector_.reset(); @@ -378,7 +370,7 @@ network_time_tracker_.reset(); #if BUILDFLAG(ENABLE_PLUGINS) plugins_resource_service_.reset(); -@@ -922,22 +918,6 @@ StatusTray* BrowserProcessImpl::status_t +@@ -911,22 +908,6 @@ StatusTray* BrowserProcessImpl::status_t return status_tray_.get(); } @@ -401,7 +393,7 @@ subresource_filter::ContentRulesetService* BrowserProcessImpl::subresource_filter_ruleset_service() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -@@ -1230,16 +1210,6 @@ void BrowserProcessImpl::CreateBackgroun +@@ -1219,16 +1200,6 @@ void BrowserProcessImpl::CreateBackgroun #endif } @@ -420,7 +412,7 @@ created_subresource_filter_ruleset_service_ = true; --- a/chrome/browser/browser_process_impl.h +++ b/chrome/browser/browser_process_impl.h -@@ -134,9 +134,6 @@ class BrowserProcessImpl : public Browse +@@ -136,9 +136,6 @@ class BrowserProcessImpl : public Browse void set_background_mode_manager_for_test( std::unique_ptr manager) override; StatusTray* status_tray() override; @@ -430,19 +422,19 @@ subresource_filter::ContentRulesetService* subresource_filter_ruleset_service() override; optimization_guide::OptimizationGuideService* optimization_guide_service() -@@ -269,9 +266,6 @@ class BrowserProcessImpl : public Browse +@@ -274,9 +271,6 @@ class BrowserProcessImpl : public Browse std::unique_ptr background_mode_manager_; #endif -- bool created_safe_browsing_service_; +- bool created_safe_browsing_service_ = false; - scoped_refptr safe_browsing_service_; - - bool created_subresource_filter_ruleset_service_; + bool created_subresource_filter_ruleset_service_ = false; std::unique_ptr subresource_filter_ruleset_service_; --- a/chrome/browser/browser_process.h +++ b/chrome/browser/browser_process.h -@@ -46,10 +46,6 @@ namespace content { +@@ -48,10 +48,6 @@ namespace content { class NetworkConnectionTracker; } @@ -453,7 +445,7 @@ namespace subresource_filter { class ContentRulesetService; } -@@ -126,10 +122,6 @@ namespace resource_coordinator { +@@ -128,10 +124,6 @@ namespace resource_coordinator { class TabManager; } @@ -464,7 +456,7 @@ // NOT THREAD SAFE, call only from the main thread. // These functions shouldn't return NULL unless otherwise noted. class BrowserProcess { -@@ -250,14 +242,6 @@ class BrowserProcess { +@@ -254,14 +246,6 @@ class BrowserProcess { // on this platform (or this is a unit test). virtual StatusTray* status_tray() = 0; @@ -529,7 +521,7 @@ #include "chrome/browser/download/download_history.h" #include "chrome/browser/download/download_item_model.h" #include "chrome/browser/download/download_prefs.h" -@@ -194,34 +193,6 @@ void MdDownloadsDOMHandler::HandleDrag(c +@@ -195,34 +194,6 @@ void MdDownloadsDOMHandler::HandleDrag(c void MdDownloadsDOMHandler::HandleSaveDangerous(const base::ListValue* args) { CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SAVE_DANGEROUS); @@ -564,7 +556,7 @@ } void MdDownloadsDOMHandler::HandleDiscardDangerous( -@@ -323,12 +294,6 @@ void MdDownloadsDOMHandler::RemoveDownlo +@@ -324,12 +295,6 @@ void MdDownloadsDOMHandler::RemoveDownlo IdSet ids; for (auto* download : to_remove) { @@ -577,7 +569,7 @@ DownloadItemModel item_model(download); if (!item_model.ShouldShowInShelf() || download->GetState() == content::DownloadItem::IN_PROGRESS) { -@@ -381,33 +346,6 @@ void MdDownloadsDOMHandler::FinalizeRemo +@@ -382,33 +347,6 @@ void MdDownloadsDOMHandler::FinalizeRemo } } @@ -740,7 +732,7 @@ extension.erase(0, 1); --- a/chrome/browser/component_updater/file_type_policies_component_installer.cc +++ b/chrome/browser/component_updater/file_type_policies_component_installer.cc -@@ -38,20 +38,6 @@ const uint8_t kPublicKeySHA256[32] = { +@@ -38,20 +38,6 @@ const uint8_t kFileTypePoliciesPublicKey const char kFileTypePoliciesManifestName[] = "File Type Policies"; void LoadFileTypesFromDisk(const base::FilePath& pb_path) { @@ -796,7 +788,7 @@ void DownloadTargetDeterminer::OnDownloadDestroyed( --- a/chrome/browser/permissions/permission_uma_util.cc +++ b/chrome/browser/permissions/permission_uma_util.cc -@@ -498,8 +498,6 @@ void PermissionUmaUtil::RecordPermission +@@ -423,8 +423,6 @@ void PermissionUmaUtil::RecordPermission requesting_origin, permission, action, source_ui, gesture_type, autoblocker->GetDismissCount(requesting_origin, permission), autoblocker->GetIgnoreCount(requesting_origin, permission)); @@ -819,7 +811,7 @@ return !download_item_->IsDone(); --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn -@@ -2349,8 +2349,6 @@ split_static_library("browser") { +@@ -2388,8 +2388,6 @@ split_static_library("browser") { "download/download_commands.h", "download/download_crx_util.cc", "download/download_crx_util.h", @@ -830,7 +822,7 @@ "download/download_dir_util.cc", --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn -@@ -2047,7 +2047,6 @@ split_static_library("ui") { +@@ -2070,7 +2070,6 @@ split_static_library("ui") { "cocoa/dialog_text_field_editor.mm", "cocoa/download/background_theme.h", "cocoa/download/background_theme.mm", @@ -838,7 +830,7 @@ "cocoa/download/download_item_button.h", "cocoa/download/download_item_button.mm", "cocoa/download/download_item_cell.h", -@@ -2707,7 +2706,6 @@ split_static_library("ui") { +@@ -2723,7 +2722,6 @@ split_static_library("ui") { "views/cookie_info_view.h", "views/device_chooser_content_view.cc", "views/device_chooser_content_view.h", @@ -848,7 +840,7 @@ "views/exclusive_access_bubble_views.cc", --- a/chrome/browser/ssl/security_state_tab_helper.cc +++ b/chrome/browser/ssl/security_state_tab_helper.cc -@@ -180,6 +180,7 @@ bool SecurityStateTabHelper::UsedPolicyI +@@ -208,6 +208,7 @@ bool SecurityStateTabHelper::UsedPolicyI security_state::MaliciousContentStatus SecurityStateTabHelper::GetMaliciousContentStatus() const { @@ -856,7 +848,7 @@ content::NavigationEntry* entry = web_contents()->GetController().GetVisibleEntry(); if (!entry) -@@ -235,6 +236,7 @@ SecurityStateTabHelper::GetMaliciousCont +@@ -263,6 +264,7 @@ SecurityStateTabHelper::GetMaliciousCont break; } } @@ -866,7 +858,19 @@ --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc -@@ -720,40 +720,6 @@ void ChromeBrowsingDataRemoverDelegate:: +@@ -265,11 +265,6 @@ void ClearNetworkErrorLoggingOnIOThread( + net::URLRequestContextGetter* context, + const base::RepeatingCallback& origin_filter) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); +- +- net::NetworkErrorLoggingDelegate* delegate = +- context->GetURLRequestContext()->network_error_logging_delegate(); +- if (delegate) +- delegate->RemoveBrowsingData(origin_filter); + } + + #if defined(OS_ANDROID) +@@ -745,40 +740,6 @@ void ChromeBrowsingDataRemoverDelegate:: CONTENT_SETTINGS_TYPE_CLIENT_HINTS, base::Time(), base::Bind(&WebsiteSettingsFilterAdapter, filter)); @@ -934,7 +938,7 @@ // Whether a web notification should be displayed when chrome is in full // screen mode. -@@ -516,13 +516,6 @@ PlatformNotificationServiceImpl::CreateN +@@ -512,13 +512,6 @@ PlatformNotificationServiceImpl::CreateN notification.set_type(message_center::NOTIFICATION_TYPE_IMAGE); notification.set_image( gfx::Image::CreateFrom1xBitmap(notification_resources.image)); @@ -1012,7 +1016,7 @@ #include "chrome/browser/sync/chrome_sync_client.h" #include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/translate/translate_ranker_metrics_provider.h" -@@ -699,9 +698,6 @@ void ChromeMetricsServiceClient::Registe +@@ -685,9 +684,6 @@ void ChromeMetricsServiceClient::Registe metrics_service_->RegisterMetricsProvider( base::MakeUnique()); @@ -1038,7 +1042,7 @@ // WebContentsObserver implementation. Sets a flag so that when the database --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc -@@ -214,7 +214,6 @@ EnsureBrowserContextKeyedServiceFactorie +@@ -217,7 +217,6 @@ EnsureBrowserContextKeyedServiceFactorie #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) CaptivePortalServiceFactory::GetInstance(); #endif @@ -1048,48 +1052,30 @@ #endif --- a/chrome/browser/ssl/captive_portal_blocking_page.cc +++ b/chrome/browser/ssl/captive_portal_blocking_page.cc -@@ -85,13 +85,6 @@ CaptivePortalBlockingPage::CaptivePortal - login_url_(login_url), - ssl_info_(ssl_info), - callback_(callback) { -- if (ssl_cert_reporter) { -- cert_report_helper_.reset(new CertReportHelper( -- std::move(ssl_cert_reporter), web_contents, request_url, ssl_info, -- certificate_reporting::ErrorReport::INTERSTITIAL_CAPTIVE_PORTAL, false, -- base::Time::Now(), nullptr)); -- } -- - captive_portal::CaptivePortalMetrics::LogCaptivePortalBlockingPageEvent( - captive_portal::CaptivePortalMetrics::SHOW_ALL); - } -@@ -209,10 +202,7 @@ void CaptivePortalBlockingPage::Populate +@@ -206,10 +206,7 @@ void CaptivePortalBlockingPage::Populate load_time_data->SetString("explanationParagraph", base::string16()); load_time_data->SetString("finalParagraph", base::string16()); -- if (cert_report_helper_) -- cert_report_helper_->PopulateExtendedReportingOption(load_time_data); +- if (cert_report_helper()) +- cert_report_helper()->PopulateExtendedReportingOption(load_time_data); - else - load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false); + load_time_data->SetBoolean(security_interstitials::kDisplayCheckBox, false); } void CaptivePortalBlockingPage::CommandReceived(const std::string& command) { -@@ -287,12 +277,6 @@ void CaptivePortalBlockingPage::OnProcee - - void CaptivePortalBlockingPage::OnDontProceed() { - UpdateMetricsAfterSecurityInterstitial(); -- if (cert_report_helper_) { -- // Finish collecting information about invalid certificates, if the -- // user opted in to. -- cert_report_helper_->FinishCertCollection( -- certificate_reporting::ErrorReport::USER_DID_NOT_PROCEED); -- } - - // Need to explicity deny the certificate via the callback, otherwise memory - // is leaked. +@@ -224,8 +221,6 @@ void CaptivePortalBlockingPage::CommandR + security_interstitials::SecurityInterstitialCommand cmd = + static_cast( + command_num); +- cert_report_helper()->HandleReportingCommands(cmd, +- controller()->GetPrefService()); + switch (cmd) { + case security_interstitials::CMD_OPEN_LOGIN: + captive_portal::CaptivePortalMetrics::LogCaptivePortalBlockingPageEvent( --- a/chrome/browser/ssl/cert_report_helper.cc +++ b/chrome/browser/ssl/cert_report_helper.cc -@@ -153,8 +153,6 @@ void CertReportHelper::FinishCertCollect +@@ -183,8 +183,6 @@ void CertReportHelper::FinishCertCollect LOG(ERROR) << "Failed to serialize certificate report."; return; } @@ -1097,10 +1083,10 @@ - ssl_cert_reporter_->ReportInvalidCertificateChain(serialized_report); } - void CertReportHelper::SetSSLCertReporterForTesting( + bool CertReportHelper::ShouldShowCertificateReporterCheckbox() { --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc -@@ -288,11 +288,6 @@ void TabHelpers::AttachTabHelpers(WebCon +@@ -292,11 +292,6 @@ void TabHelpers::AttachTabHelpers(WebCon new ChromePDFWebContentsHelperClient())); PluginObserver::CreateForWebContents(web_contents); SadTabHelper::CreateForWebContents(web_contents); @@ -1111,7 +1097,7 @@ - profile, web_contents); SearchTabHelper::CreateForWebContents(web_contents); if (base::FeatureList::IsEnabled(features::kTabMetricsLogging)) - TabActivityWatcher::WatchWebContents(web_contents); + resource_coordinator::TabActivityWatcher::WatchWebContents(web_contents); --- a/chrome/browser/permissions/permission_decision_auto_blocker.cc +++ b/chrome/browser/permissions/permission_decision_auto_blocker.cc @@ -18,7 +18,6 @@ @@ -1175,7 +1161,7 @@ void ChromeSubresourceFilterClient::OnReloadRequested() { --- a/chrome/browser/extensions/webstore_inline_installer.cc +++ b/chrome/browser/extensions/webstore_inline_installer.cc -@@ -115,85 +115,10 @@ bool WebstoreInlineInstaller::IsRequesto +@@ -116,7 +116,7 @@ bool WebstoreInlineInstaller::IsRequesto } bool WebstoreInlineInstaller::SafeBrowsingNavigationEventsEnabled() const { @@ -1183,85 +1169,39 @@ + return false; } + std::string WebstoreInlineInstaller::GetPostData( +@@ -248,6 +248,7 @@ void WebstoreInlineInstaller::WebContent + } + std::string WebstoreInlineInstaller::GetJsonPostData() { -- // web_contents() might return null during tab destruction. This object would -- // also be destroyed shortly thereafter but check to be on the safe side. -- if (!web_contents()) -- return std::string(); -- -- // Report extra data only when SafeBrowsing is enabled for the current -- // profile. -- if (!profile()->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) -- return std::string(); -- -- auto redirect_chain = base::MakeUnique(); -- -- if (SafeBrowsingNavigationEventsEnabled()) { -- // If we have it, use the new referrer checker. -- safe_browsing::SafeBrowsingService* safe_browsing_service = -- g_browser_process->safe_browsing_service(); -- // May be null in some tests. -- if (!safe_browsing_service) -- return std::string(); -- -- scoped_refptr -- navigation_observer_manager = -- safe_browsing_service->navigation_observer_manager(); -- // This may be null if the navigation observer manager feature is -- // disabled by experiment. -- if (!navigation_observer_manager) -- return std::string(); -- -- ReferrerChain referrer_chain; -- SafeBrowsingNavigationObserverManager::AttributionResult result = -- navigation_observer_manager->IdentifyReferrerChainByWebContents( -- web_contents(), kExtensionReferrerUserGestureLimit, -- &referrer_chain); -- if (result != -- SafeBrowsingNavigationObserverManager::NAVIGATION_EVENT_NOT_FOUND) { -- // For now the CWS post data is JSON encoded. Consider moving it to a -- // proto. -- for (const auto& referrer_chain_entry : referrer_chain) { -- // Referrer chain entries are a list of URLs in reverse chronological -- // order, so the final URL is the last thing in the list and the initial -- // landing page is the first thing in the list. -- // Furthermore each entry may contain a series of server redirects -- // stored in the same order. -- redirect_chain->AppendString(referrer_chain_entry.url()); -- for (const auto& server_side_redirect : -- referrer_chain_entry.server_redirect_chain()) { -- redirect_chain->AppendString(server_side_redirect.url()); -- } -- } -- } -- } else { -- content::NavigationController& navigation_controller = -- web_contents()->GetController(); -- content::NavigationEntry* navigation_entry = -- navigation_controller.GetLastCommittedEntry(); -- if (navigation_entry) { -- const std::vector& redirect_urls = -- navigation_entry->GetRedirectChain(); -- for (const GURL& url : redirect_urls) { -- redirect_chain->AppendString(url.spec()); -- } -- } -- } -- -- if (!redirect_chain->empty()) { -- base::DictionaryValue dictionary; -- dictionary.SetString("id", id()); -- dictionary.SetString("referrer", requestor_url_.spec()); -- dictionary.Set("redirect_chain", std::move(redirect_chain)); -- -- std::string json; -- base::JSONWriter::Write(dictionary, &json); -- return json; -- } -- ++#if 0 + auto redirect_chain = base::MakeUnique(); + + if (SafeBrowsingNavigationEventsEnabled()) { +@@ -299,11 +300,13 @@ std::string WebstoreInlineInstaller::Get + base::JSONWriter::Write(dictionary, &json); + return json; + } ++#endif + return std::string(); } + std::string WebstoreInlineInstaller::GetProtoPostData() { ++#if 0 + if (!SafeBrowsingNavigationEventsEnabled()) + return std::string(); + +@@ -330,6 +333,9 @@ std::string WebstoreInlineInstaller::Get + recent_navigations_to_collect); + + return request.SerializeAsString(); ++#endif ++ ++ return std::string(); + } + + // static --- a/chrome/browser/safe_browsing/BUILD.gn +++ b/chrome/browser/safe_browsing/BUILD.gn @@ -12,6 +12,11 @@ proto_library("chunk_proto") { @@ -1286,7 +1226,7 @@ # "Safe Browsing Basic" files used for safe browsing in full mode --- a/chrome/browser/net/system_network_context_manager.cc +++ b/chrome/browser/net/system_network_context_manager.cc -@@ -50,17 +50,11 @@ content::mojom::NetworkContextParamsPtr +@@ -32,18 +32,12 @@ namespace { // Called on IOThread to disable QUIC for HttpNetworkSessions not using the // network service. Note that re-enabling QUIC dynamically is not supported for // simpliciy and requires a browser restart. @@ -1296,7 +1236,8 @@ +void DisableQuicOnIOThread(IOThread* io_thread) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - // Disable QUIC for HttpNetworkSessions using IOThread's NetworkService. + if (!base::FeatureList::IsEnabled(features::kNetworkService)) + content::GetNetworkServiceImpl()->DisableQuic(); io_thread->DisableQuic(); - - // Safebrowsing isn't yet using the IOThread's NetworkService, so must be @@ -1305,7 +1246,7 @@ } } // namespace -@@ -132,11 +126,7 @@ void SystemNetworkContextManager::Disabl +@@ -122,13 +116,9 @@ void SystemNetworkContextManager::Disabl if (!io_thread) return; @@ -1318,9 +1259,11 @@ - base::Unretained(safe_browsing_service))); + base::BindOnce(&DisableQuicOnIOThread, io_thread)); } + + void SystemNetworkContextManager::FlushProxyConfigMonitorForTesting() { --- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc +++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc -@@ -405,9 +405,8 @@ void AddBluetoothStrings(content::WebUID +@@ -427,9 +427,8 @@ void AddBluetoothStrings(content::WebUID #endif void AddChangePasswordStrings(content::WebUIDataSource* html_source) { @@ -1340,18 +1283,17 @@ #include "chrome/renderer/prerender/prerenderer_client.h" -#include "chrome/renderer/safe_browsing/phishing_classifier_delegate.h" #include "chrome/renderer/tts_dispatcher.h" + #include "chrome/renderer/url_loader_throttle_provider_impl.h" #include "chrome/renderer/worker_content_settings_client.h" - #include "components/autofill/content/renderer/autofill_agent.h" -@@ -79,8 +78,6 @@ +@@ -79,7 +78,6 @@ + #include "components/error_page/common/localized_error.h" #include "components/network_hints/renderer/prescient_networking_dispatcher.h" - #include "components/password_manager/content/renderer/credential_manager_client.h" #include "components/pdf/renderer/pepper_pdf_host.h" --#include "components/safe_browsing/renderer/renderer_url_loader_throttle.h" -#include "components/safe_browsing/renderer/threat_dom_details.h" #include "components/safe_browsing/renderer/websocket_sb_handshake_throttle.h" #include "components/spellcheck/spellcheck_build_features.h" #include "components/startup_metric_utils/common/startup_metric.mojom.h" -@@ -449,9 +446,6 @@ void ChromeContentRendererClient::Render +@@ -448,9 +446,6 @@ void ChromeContentRendererClient::Render thread->AddObserver(spellcheck_.get()); } #endif @@ -1361,7 +1303,7 @@ prerender_dispatcher_.reset(new prerender::PrerenderDispatcher()); subresource_filter_ruleset_dealer_.reset( new subresource_filter::UnverifiedRulesetDealer()); -@@ -566,10 +560,6 @@ void ChromeContentRendererClient::Render +@@ -564,10 +559,6 @@ void ChromeContentRendererClient::Render new nacl::NaClHelper(render_frame); #endif @@ -1372,24 +1314,7 @@ #if BUILDFLAG(ENABLE_PRINTING) new printing::PrintRenderFrameHelper( render_frame, base::MakeUnique()); -@@ -1316,16 +1306,6 @@ bool ChromeContentRendererClient::WillSe - const blink::WebURL& url, - std::vector>* throttles, - GURL* new_url) { -- if (base::FeatureList::IsEnabled(features::kNetworkService)) { -- InitSafeBrowsingIfNecessary(); -- RenderFrame* render_frame = content::RenderFrame::FromWebFrame(frame); -- int render_frame_id = -- render_frame ? render_frame->GetRoutingID() : MSG_ROUTING_NONE; -- throttles->push_back( -- base::MakeUnique( -- safe_browsing_.get(), render_frame_id)); -- } -- - // Check whether the request should be allowed. If not allowed, we reset the - // URL to something invalid to prevent the request and cause an error. - #if BUILDFLAG(ENABLE_EXTENSIONS) -@@ -1423,9 +1403,7 @@ bool ChromeContentRendererClient::IsExte +@@ -1394,9 +1385,7 @@ bool ChromeContentRendererClient::IsExte std::unique_ptr ChromeContentRendererClient::CreateWebSocketHandshakeThrottle() { @@ -1400,7 +1325,7 @@ } std::unique_ptr -@@ -1717,13 +1695,6 @@ ChromeContentRendererClient::GetTaskSche +@@ -1688,13 +1677,6 @@ ChromeContentRendererClient::GetTaskSche GetRendererTaskSchedulerInitParamsFromCommandLine(); } @@ -1435,17 +1360,17 @@ namespace subresource_filter { class UnverifiedRulesetDealer; } -@@ -255,9 +250,6 @@ class ChromeContentRendererClient +@@ -263,9 +258,6 @@ class ChromeContentRendererClient void GetInterface(const std::string& name, mojo::ScopedMessagePipeHandle request_handle) override; - // Initialises |safe_browsing_| if it is not already initialised. - void InitSafeBrowsingIfNecessary(); - - void GetNavigationErrorStringsInternal( - content::RenderFrame* render_frame, - const blink::WebURLRequest& failed_request, -@@ -289,12 +281,9 @@ class ChromeContentRendererClient + void PrepareErrorPageInternal(content::RenderFrame* render_frame, + const blink::WebURLRequest& failed_request, + const error_page::Error& error, +@@ -296,12 +288,9 @@ class ChromeContentRendererClient ChromeKeySystemsProvider key_systems_provider_; diff --git a/0002-fix-building-without-reporting.patch b/0002-fix-building-without-reporting.patch index 2e3ead3..4e8bd63 100644 --- a/0002-fix-building-without-reporting.patch +++ b/0002-fix-building-without-reporting.patch @@ -14,7 +14,7 @@ #include "chrome/browser/ui/webui/print_preview/privet_printer_handler.h" --- a/content/browser/net/reporting_service_proxy.cc +++ b/content/browser/net/reporting_service_proxy.cc -@@ -97,6 +97,7 @@ class ReportingServiceProxyImpl : public +@@ -102,6 +102,7 @@ class ReportingServiceProxyImpl : public const std::string& group, const std::string& type, std::unique_ptr body) { @@ -22,7 +22,7 @@ net::URLRequestContext* request_context = request_context_getter_->GetURLRequestContext(); if (!request_context) { -@@ -112,6 +113,7 @@ class ReportingServiceProxyImpl : public +@@ -117,6 +118,7 @@ class ReportingServiceProxyImpl : public } reporting_service->QueueReport(url, group, type, std::move(body)); @@ -32,7 +32,7 @@ scoped_refptr request_context_getter_; --- a/chrome/browser/profiles/profile_impl_io_data.h +++ b/chrome/browser/profiles/profile_impl_io_data.h -@@ -182,6 +182,7 @@ class ProfileImplIOData : public Profile +@@ -177,6 +177,7 @@ class ProfileImplIOData : public Profile const StoragePartitionDescriptor& partition_descriptor) const override; chrome_browser_net::Predictor* GetPredictor() override; @@ -40,7 +40,7 @@ // Returns a net::ReportingService, if reporting should be enabled. Otherwise, // returns nullptr. // TODO(mmenke): Remove once URLRequestContextBuilders are always used to -@@ -192,6 +193,7 @@ class ProfileImplIOData : public Profile +@@ -187,6 +188,7 @@ class ProfileImplIOData : public Profile // Returns a net::ReportingPolicy, if reporting should be enabled. Otherwise, // returns nullptr. static std::unique_ptr MaybeCreateReportingPolicy(); @@ -50,7 +50,7 @@ mutable std::unique_ptr lazy_params_; --- a/chrome/browser/profiles/profile_impl_io_data.cc +++ b/chrome/browser/profiles/profile_impl_io_data.cc -@@ -472,7 +472,9 @@ void ProfileImplIOData::InitializeIntern +@@ -480,7 +480,9 @@ void ProfileImplIOData::InitializeIntern builder, std::move(request_interceptors), std::move(profile_params->protocol_handler_interceptor)); @@ -60,7 +60,7 @@ } void ProfileImplIOData::OnMainRequestContextCreated( -@@ -608,7 +610,9 @@ net::URLRequestContext* ProfileImplIODat +@@ -615,7 +617,9 @@ net::URLRequestContext* ProfileImplIODat context->host_resolver())); context->SetJobFactory(std::move(top_job_factory)); @@ -70,7 +70,7 @@ return context; } -@@ -698,6 +702,7 @@ chrome_browser_net::Predictor* ProfileIm +@@ -705,6 +709,7 @@ chrome_browser_net::Predictor* ProfileIm return predictor_.get(); } @@ -78,14 +78,14 @@ std::unique_ptr ProfileImplIOData::MaybeCreateReportingService( net::URLRequestContext* url_request_context) const { -@@ -716,3 +721,4 @@ ProfileImplIOData::MaybeCreateReportingP +@@ -723,3 +728,4 @@ ProfileImplIOData::MaybeCreateReportingP return base::MakeUnique(); } +#endif --- a/chrome/browser/profiles/profile_io_data.cc +++ b/chrome/browser/profiles/profile_io_data.cc -@@ -638,7 +638,9 @@ +@@ -637,7 +637,9 @@ void ProfileIOData::AppRequestContext::S void ProfileIOData::AppRequestContext::SetReportingService( std::unique_ptr reporting_service) { reporting_service_ = std::move(reporting_service); @@ -97,7 +97,7 @@ ProfileIOData::AppRequestContext::~AppRequestContext() { --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc -@@ -252,10 +252,12 @@ +@@ -255,10 +255,12 @@ void ClearReportingCacheOnIOThread( const base::Callback& origin_filter) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -109,4 +109,4 @@ +#endif } - #if defined(OS_ANDROID) + void ClearNetworkErrorLoggingOnIOThread( diff --git a/0003-disable-autofill-download-manager.patch b/0003-disable-autofill-download-manager.patch index 6fddd30..30b051f 100644 --- a/0003-disable-autofill-download-manager.patch +++ b/0003-disable-autofill-download-manager.patch @@ -10,41 +10,19 @@ } std::ostream& operator<<(std::ostream& out, -@@ -299,37 +298,6 @@ bool AutofillDownloadManager::StartUploa +@@ -299,6 +298,7 @@ bool AutofillDownloadManager::StartUploa bool AutofillDownloadManager::StartRequest( const FormRequestData& request_data) { -- net::URLRequestContextGetter* request_context = -- driver_->GetURLRequestContext(); -- DCHECK(request_context); -- GURL request_url = GetRequestUrl(request_data.request_type); -- -- // Id is ignored for regular chrome, in unit test id's for fake fetcher -- // factory will be 0, 1, 2, ... -- std::unique_ptr owned_fetcher = net::URLFetcher::Create( -- fetcher_id_for_unittest_++, request_url, net::URLFetcher::POST, this, -- GetNetworkTrafficAnnotation(request_data.request_type)); -- net::URLFetcher* fetcher = owned_fetcher.get(); -- data_use_measurement::DataUseUserData::AttachToFetcher( -- fetcher, data_use_measurement::DataUseUserData::AUTOFILL); -- url_fetchers_[fetcher] = -- std::make_pair(std::move(owned_fetcher), request_data); -- fetcher->SetAutomaticallyRetryOn5xx(false); -- fetcher->SetRequestContext(request_context); -- fetcher->SetUploadData("text/proto", request_data.payload); -- fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES | -- net::LOAD_DO_NOT_SEND_COOKIES); -- // Add Chrome experiment state to the request headers. -- net::HttpRequestHeaders headers; -- // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does -- // not affect transmission of experiments coming from the variations server. -- bool is_signed_in = false; -- variations::AppendVariationHeaders(fetcher->GetOriginalURL(), -- driver_->IsIncognito(), false, -- is_signed_in, &headers); -- fetcher->SetExtraRequestHeaders(headers.ToString()); -- fetcher->Start(); -- ++#if 0 + net::URLRequestContextGetter* request_context = + driver_->GetURLRequestContext(); + DCHECK(request_context); +@@ -330,6 +330,7 @@ bool AutofillDownloadManager::StartReque + variations::SignedIn::kNo, &headers); + fetcher->SetExtraRequestHeaders(headers.ToString()); + fetcher->Start(); ++#endif + return true; } - diff --git a/0005-disable-default-extensions.patch b/0005-disable-default-extensions.patch index 8a73898..c67de54 100644 --- a/0005-disable-default-extensions.patch +++ b/0005-disable-default-extensions.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/extensions/component_loader.cc +++ b/chrome/browser/extensions/component_loader.cc -@@ -422,11 +422,6 @@ void ComponentLoader::AddWebStoreApp() { +@@ -421,11 +421,6 @@ void ComponentLoader::AddWebStoreApp() { if (!IsNormalSession()) return; #endif @@ -12,7 +12,7 @@ } scoped_refptr ComponentLoader::CreateExtension( -@@ -481,11 +476,6 @@ void ComponentLoader::AddDefaultComponen +@@ -480,11 +475,6 @@ void ComponentLoader::AddDefaultComponen Add(IDR_BOOKMARKS_MANIFEST, base::FilePath(FILE_PATH_LITERAL("bookmark_manager"))); } @@ -24,7 +24,7 @@ #endif // defined(OS_CHROMEOS) if (!skip_session_components) { -@@ -561,13 +551,6 @@ void ComponentLoader::AddDefaultComponen +@@ -560,13 +550,6 @@ void ComponentLoader::AddDefaultComponen AddHangoutServicesExtension(); AddImageLoaderExtension(); diff --git a/0006-modify-default-prefs.patch b/0006-modify-default-prefs.patch index 57610f4..14071b2 100644 --- a/0006-modify-default-prefs.patch +++ b/0006-modify-default-prefs.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc -@@ -879,7 +879,7 @@ void ChromeContentBrowserClient::Registe +@@ -859,7 +859,7 @@ void ChromeContentBrowserClient::Registe void ChromeContentBrowserClient::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref(prefs::kDisable3DAPIs, false); @@ -82,7 +82,7 @@ --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc -@@ -217,7 +217,7 @@ void AutofillManager::RegisterProfilePre +@@ -216,7 +216,7 @@ void AutofillManager::RegisterProfilePre registry->RegisterIntegerPref( prefs::kAutofillCreditCardSigninPromoImpressionCount, 0); registry->RegisterBooleanPref( @@ -104,7 +104,7 @@ --- a/chrome/browser/io_thread.cc +++ b/chrome/browser/io_thread.cc -@@ -612,7 +612,7 @@ void IOThread::RegisterPrefs(PrefRegistr +@@ -620,7 +620,7 @@ void IOThread::RegisterPrefs(PrefRegistr std::string()); registry->RegisterBooleanPref(prefs::kEnableReferrers, true); data_reduction_proxy::RegisterPrefs(registry); @@ -145,7 +145,7 @@ prefs::kShowManagedBookmarksInBookmarkBar, --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc -@@ -136,7 +136,7 @@ const char Profile::kNoHostedDomainFound +@@ -138,7 +138,7 @@ const char Profile::kNoHostedDomainFound void Profile::RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( prefs::kSearchSuggestEnabled, @@ -167,7 +167,7 @@ prefs::kCloudPrintXmppPingEnabled, false); --- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc +++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui.cc -@@ -143,10 +143,6 @@ void LocalDiscoveryUI::RegisterProfilePr +@@ -144,10 +144,6 @@ void LocalDiscoveryUI::RegisterProfilePr user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( prefs::kLocalDiscoveryNotificationsEnabled, @@ -211,7 +211,7 @@ false); --- a/components/password_manager/core/browser/password_manager.cc +++ b/components/password_manager/core/browser/password_manager.cc -@@ -235,10 +235,10 @@ PasswordFormManager* FindMatchedManager( +@@ -236,10 +236,10 @@ PasswordFormManager* FindMatchedManager( void PasswordManager::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { registry->RegisterBooleanPref( diff --git a/0008-restore-classic-ntp.patch b/0008-restore-classic-ntp.patch index cefdb67..624f2d9 100644 --- a/0008-restore-classic-ntp.patch +++ b/0008-restore-classic-ntp.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/search/search.cc +++ b/chrome/browser/search/search.cc -@@ -147,29 +147,7 @@ struct NewTabURLDetails { +@@ -187,29 +187,7 @@ struct NewTabURLDetails { const GURL local_url(chrome::kChromeSearchLocalNtpUrl); @@ -33,7 +33,7 @@ const GURL url; --- a/components/ntp_snippets/features.cc +++ b/components/ntp_snippets/features.cc -@@ -38,16 +38,16 @@ const base::Feature* const kAllFeatures[ +@@ -37,16 +37,16 @@ const base::Feature* const kAllFeatures[ nullptr}; const base::Feature kArticleSuggestionsFeature{ diff --git a/0011-add-duckduckgo-search-engine.patch b/0011-add-duckduckgo-search-engine.patch index 4dff65d..f246b54 100644 --- a/0011-add-duckduckgo-search-engine.patch +++ b/0011-add-duckduckgo-search-engine.patch @@ -30,7 +30,7 @@ --- a/components/search_engines/template_url_prepopulate_data.cc +++ b/components/search_engines/template_url_prepopulate_data.cc -@@ -50,548 +50,548 @@ namespace { +@@ -51,548 +51,548 @@ namespace { // Default (for countries with no better engine set) const PrepopulatedEngine* const engines_default[] = { diff --git a/0012-branding.patch b/0012-branding.patch index 47cf085..e9559a7 100644 --- a/0012-branding.patch +++ b/0012-branding.patch @@ -55,7 +55,7 @@ -@@ -275,7 +275,7 @@ If you update this file, be sure also to +@@ -266,7 +266,7 @@ If you update this file, be sure also to @@ -64,7 +64,7 @@ -@@ -609,7 +609,7 @@ Chromium is unable to recover your setti +@@ -595,7 +595,7 @@ Chromium is unable to recover your setti @@ -73,7 +73,7 @@ Update &Chromium -@@ -617,7 +617,7 @@ Chromium is unable to recover your setti +@@ -603,7 +603,7 @@ Chromium is unable to recover your setti @@ -82,7 +82,7 @@ Update &Chromium -@@ -634,7 +634,7 @@ Chromium is unable to recover your setti +@@ -620,7 +620,7 @@ Chromium is unable to recover your setti diff --git a/0013-disable-missing-key-warning.patch b/0013-disable-missing-key-warning.patch index cdf7e19..bec3f05 100644 --- a/0013-disable-missing-key-warning.patch +++ b/0013-disable-missing-key-warning.patch @@ -1,10 +1,10 @@ --- a/chrome/browser/ui/startup/google_api_keys_infobar_delegate.cc +++ b/chrome/browser/ui/startup/google_api_keys_infobar_delegate.cc -@@ -15,6 +15,7 @@ +@@ -14,6 +14,7 @@ // static void GoogleApiKeysInfoBarDelegate::Create(InfoBarService* infobar_service) { + return; - if (google_apis::HasKeysConfigured()) - return; - + infobar_service->AddInfoBar(infobar_service->CreateConfirmInfoBar( + std::unique_ptr( + new GoogleApiKeysInfoBarDelegate()))); diff --git a/0017-disable-new-avatar-menu.patch b/0017-disable-new-avatar-menu.patch index 52f6bf9..fe05949 100644 --- a/0017-disable-new-avatar-menu.patch +++ b/0017-disable-new-avatar-menu.patch @@ -1,6 +1,6 @@ --- a/components/signin/core/browser/signin_manager.cc +++ b/components/signin/core/browser/signin_manager.cc -@@ -299,7 +299,7 @@ void SigninManager::OnGoogleServicesUser +@@ -302,7 +302,7 @@ void SigninManager::OnGoogleServicesUser } bool SigninManager::IsSigninAllowed() const { diff --git a/9000-disable-metrics.patch b/9000-disable-metrics.patch index b3adcfd..c27a1fc 100644 --- a/9000-disable-metrics.patch +++ b/9000-disable-metrics.patch @@ -1,6 +1,6 @@ --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc -@@ -1211,6 +1211,11 @@ const FeatureEntry::FeatureVariation kSp +@@ -1221,6 +1221,11 @@ const FeatureEntry::FeatureVariation kWe // // When adding a new choice, add it to the end of the list. const FeatureEntry kFeatureEntries[] = { @@ -52,7 +52,7 @@ HistogramBase::HistogramBase(const char* name) --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc -@@ -124,7 +124,8 @@ class Histogram::Factory { +@@ -123,7 +123,8 @@ class Histogram::Factory { minimum_(minimum), maximum_(maximum), bucket_count_(bucket_count), @@ -62,7 +62,7 @@ // Create a BucketRanges structure appropriate for this histogram. virtual BucketRanges* CreateRanges() { -@@ -154,13 +155,18 @@ class Histogram::Factory { +@@ -153,13 +154,18 @@ class Histogram::Factory { HistogramBase::Sample maximum_; uint32_t bucket_count_; int32_t flags_; @@ -82,7 +82,7 @@ if (!histogram) { // To avoid racy destruction at shutdown, the following will be leaked. const BucketRanges* created_ranges = CreateRanges(); -@@ -191,7 +197,7 @@ HistogramBase* Histogram::Factory::Build +@@ -190,7 +196,7 @@ HistogramBase* Histogram::Factory::Build if (allocator) { tentative_histogram = allocator->AllocateHistogram( histogram_type_, @@ -91,7 +91,7 @@ minimum_, maximum_, registered_ranges, -@@ -226,6 +232,10 @@ HistogramBase* Histogram::Factory::Build +@@ -225,6 +231,10 @@ HistogramBase* Histogram::Factory::Build } } @@ -102,7 +102,7 @@ CHECK_EQ(histogram_type_, histogram->GetHistogramType()) << name_; if (bucket_count_ != 0 && !histogram->HasConstructionArguments(minimum_, maximum_, bucket_count_)) { -@@ -462,10 +472,16 @@ bool Histogram::HasConstructionArguments +@@ -461,10 +471,16 @@ bool Histogram::HasConstructionArguments } void Histogram::Add(int value) { @@ -119,7 +119,7 @@ DCHECK_EQ(0, ranges(0)); DCHECK_EQ(kSampleType_MAX, ranges(bucket_count())); -@@ -520,10 +536,16 @@ std::unique_ptr Histog +@@ -519,10 +535,16 @@ std::unique_ptr Histog } void Histogram::AddSamples(const HistogramSamples& samples) { @@ -279,7 +279,7 @@ --- a/chrome/browser/ui/tab_helpers.cc +++ b/chrome/browser/ui/tab_helpers.cc -@@ -204,8 +204,10 @@ void TabHelpers::AttachTabHelpers(WebCon +@@ -207,8 +207,10 @@ void TabHelpers::AttachTabHelpers(WebCon ChromeTranslateClient::CreateForWebContents(web_contents); ClientHintsObserver::CreateForWebContents(web_contents); CoreTabHelper::CreateForWebContents(web_contents); @@ -292,7 +292,7 @@ ExternalProtocolObserver::CreateForWebContents(web_contents); favicon::CreateContentFaviconDriverForWebContents(web_contents); FindTabHelper::CreateForWebContents(web_contents); -@@ -220,14 +222,20 @@ void TabHelpers::AttachTabHelpers(WebCon +@@ -223,14 +225,20 @@ void TabHelpers::AttachTabHelpers(WebCon HistoryTabHelper::CreateForWebContents(web_contents); InfoBarService::CreateForWebContents(web_contents); InstallableManager::CreateForWebContents(web_contents); @@ -317,7 +317,7 @@ PDFPluginPlaceholderObserver::CreateForWebContents(web_contents); PermissionRequestManager::CreateForWebContents(web_contents); // The PopupBlockerTabHelper has an implicit dependency on -@@ -252,7 +260,9 @@ void TabHelpers::AttachTabHelpers(WebCon +@@ -255,7 +263,9 @@ void TabHelpers::AttachTabHelpers(WebCon // is taken over by ChromeContentSettingsClient. http://crbug.com/387075 TabSpecificContentSettings::CreateForWebContents(web_contents); TabUIHelper::CreateForWebContents(web_contents); @@ -328,7 +328,7 @@ vr::VrTabHelper::CreateForWebContents(web_contents); // NO! Do not just add your tab helper here. This is a large alphabetized -@@ -302,7 +312,8 @@ void TabHelpers::AttachTabHelpers(WebCon +@@ -306,7 +316,8 @@ void TabHelpers::AttachTabHelpers(WebCon #if defined(OS_WIN) || defined(OS_MACOSX) || \ (defined(OS_LINUX) && !defined(OS_CHROMEOS)) diff --git a/PKGBUILD b/PKGBUILD index c490251..9874129 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -5,7 +5,7 @@ # Contributor: Daniel J Griffiths pkgname=inox -pkgver=64.0.3282.140 +pkgver=65.0.3325.146 pkgrel=1 _launcher_ver=5 pkgdesc="Chromium Spin-off to enhance privacy by disabling data transmission to Google" @@ -16,7 +16,7 @@ depends=('gtk3' 'nss' 'alsa-lib' 'xdg-utils' 'libxss' 'libcups' 'libgcrypt' 'ttf-font' 'systemd' 'dbus' 'libpulse' 'pciutils' 'json-glib' 'desktop-file-utils' 'hicolor-icon-theme') makedepends=('python2' 'gperf' 'yasm' 'mesa' 'ninja' 'nodejs' 'git' 'libva' - 'clang' 'llvm') + 'clang' 'llvm' 'lld') optdepends=('pepper-flash: support for Flash content' 'kdialog: needed for file dialogs in KDE' 'gnome-keyring: for storing passwords in GNOME keyring' @@ -30,18 +30,14 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom chromium-$pkgver.txt::https://chromium.googlesource.com/chromium/src.git/+/$pkgver?format=TEXT https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/product_logo_{16,22,24,32,48,64,128,256}.png # Patches from Arch Linux - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-use-fromUTF8-for-UnicodeString-construction.patch - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-exclude_unwind_tables.patch - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-omnibox-unescape-fragment.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-skia-harmony.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-widevine.patch # Patches from Gentoo https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-clang-r2.patch - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-memcpy-r0.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-math.h-r0.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-stdint.patch # Misc - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-move.patch - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-init.patch - https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-rgbx.patch + https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-init-r16.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/chromium-vaapi-r16.patch # Inox patchset https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0001-fix-building-without-safebrowsing.patch @@ -66,9 +62,9 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0020-launcher-branding.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0021-disable-rlz.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/9000-disable-metrics.patch) -sha256sums=('146afbab37982c52251e5c71b6e19e6e7053b527217fe1da9966c794478c29ce' +sha256sums=('cb4f2f3f5a3344f7c452b61f8086d4b4e56af6f5bc34309c3ede8be6b4ab81a3' '4dc3428f2c927955d9ae117f2fb24d098cc6dd67adb760ac9c82b522ec8b0587' - 'cc3a328836af87f3a262ac7a7bc848e0f3a4b2f9f0346ef76b9b059c6f6d32bc' + 'adfeb830af4c9b55c4a6481ef245e82ad2b9fc3cfe0fe339b30baa8573f701e7' '71471fa4690894420f9e04a2e9a622af620d92ac2714a35f9a4c4e90fa3968dd' '4a533acefbbc1567b0d74a1c0903e9179b8c59c1beabe748850795815366e509' '7b88830c5e0e9819f514ad68aae885d427541a907e25607e47dee1b0f38975fd' @@ -77,53 +73,49 @@ sha256sums=('146afbab37982c52251e5c71b6e19e6e7053b527217fe1da9966c794478c29ce' '53a1e8da18069eb4d6ab3af9c923c22a0f020241a4839c3140e3601052ddf6ff' '896993987d4ef9f0ac7db454f288117316c2c80ed0b6764019afd760db222dad' '3df9b3bbdc07fde63d9e400954dcc6ab6e0e5454f0ef6447570eef0549337354' - 'f846218089a7b095d275e9cb3b74b28586d72f2137968c8c4e09b6f8232d694b' - '9478f1ec1a3c53425306cf41c2d0555c215a4f106955d9d6adfff38044530ce8' - '814eb2cecb10cb697e24036b08aac41e88d0e38971741f9e946200764e2401ae' 'feca54ab09ac0fc9d0626770a6b899a6ac5a12173c7d0c1005bc3964ec83e7b3' 'd6fdcb922e5a7fbe15759d39ccc8ea4225821c44d98054ce0f23f9d1f00c9808' '4495e8b29dae242c79ffe4beefc5171eb3c7aacb7e9aebfd2d4d69b9d8c958d3' - 'f6227987c30f8b8a1e0cb5f3863698543a890e6f4bd20ff9384735e1122e66da' - '1336c9a790c0bd7fa8cc00d0c58d6f6374cc311beb1a9db0a1696f4ddb21bfde' - '8a81a14af625c8b79006d1b9b4321d5487bc2e56a3fb3a677f9a8dab369be7af' - '0a9186ab591773f8fb6cbc908f9bbf4bc1508f1095b6c1cd7479aac945045373' - 'b82047df666e6bbf66e0c0911d20c5001bd1100fd08adafa92cac5f02a887a01' - 'd1e112adb135a823907aae33b189cb775d48e6afa785a26a452fc833824cd2e8' - '300a7f3f0aba2037d159e5815f5cb3f2699c0ac26cbbb61209bbf01ac1eb2efb' - '605cca8be9828a29cc96d473847eef9452d572fe6a56dacd96426a202310ba58' + 'fe0ab86aa5b0072db730eccda3e1582ebed4af25815bfd49fe0da24cf63ca902' + 'c00d2506f1078b38a8ebec474a7318e76a61db1298afb40088a34210f137210f' + 'acae2de43c123f19523c4fca3af19c671acbe76f76bd40e285fe3b08cddb7044' + '5bc4f5dc5e9c8d71cf273338f65e82efcebc902e645affb35659532dcf9ad1af' + 'ae84e4eed9969057dd9d3ecff040e02480993c17ff05e9674d5e24e398704a60' + 'b7261e4063b0532139b72f20675bb58c743d95022c7368019deb012b2c777f83' + 'ef9000eaaa5c0a28f0e4cb31139c7f2b454192532d8469329f5f6f95c21769e0' 'fb91a7e30e2615e4eb0626b0fdcf97b92d4a727a52023730f408b02fee436c8d' - '6ba0ad7d91b2f3bbf03fc4a3236a04310a0c57505e1688c7e11ace9dcea1dded' - '2e8ba84204840f5f57b68456a70895c7ab07286efb4b165911e3f0c8072ded62' + '72cd12b9064573fc1c7d88d606985ef180efdcec3815b0185c6038ec8216a471' + '034cce5fb219a293284a4c01f7e4d7ad6ab262eb6fb9065dc65a4b18e885da7f' '7781ecd43e3c28f7d1e9158e043d6f98a190b5ee3c2c5ebe91644ea27e0b42ee' - 'daf9dcbeed8c6cd5d0012086680e8ee252121f56d96624d920d5b46d300a4052' + 'a5dde3ff6cd4adeef7eb099839d8d17a3bfeb89ec6bb66eba3e89d741743c9c0' 'cf050473adae5b83680346b369997b5ead55dce282515f185e4096c5ed39f11d' '3190a507dfa00e863a0e622b5738db5cf19947f696ac7a790f427510cc15d1e1' - '476593cf1e3bbf2539732567a70b0acea14033370317baf868f3d9701e4a1d5d' - '0b7332739e7f5eabb54213449fabed35e98d46c334a9e15398582659755a89c3' - 'c79f12e444d2c7b9b61de8d6698033cc8a84bb35f949908b3a366105367237b0' + '6fdea7a737959b226165dc3b6dd347de1e09e6e237acc444116df007ba0a7c57' + '6427fea42b1cc6cf9aaae883c75c2209360344125827e1d6b15666faaf3c10a9' + '60ecb418ff8728f67ac9617216f68dcc1ba0fa4d4e47e2da1fc4e63b5c91bfea' 'f80106b8127b60a62c006653154a26ebe68dd4aec5c551bae5321fa4e5ccef3f' '795686bf0dd7bfac0f596155be8fc7ed3f6294a6b764f793cd1614085562ce38' '5dc10c49cfc3ea65505e07366420eda0fc4878d0b0cebbfbcd8ad7daa88b3ded' - 'a1a5cb2c68abb02e7cdad3108a5a4d00beac86ae9341df98eb20495fcc400d45' + 'e407da0596e044971631c0883a83bd75665535f1b913df32ba08ca8cd5d4b16e' 'cb2bd17fbbd9184f15eb24d3b23deca92d06cb4b9ec31bd6944504e130d69ff8' 'c17556772059a64873ddac383f2976e3befb5c07c3019b641c989ffb5683c4cd' '80d2974001708c288a54c24e1dc896ef25916552b740765f6066a244c05ffcd5' 'dbe942b1eaba525ca6b81d398462a70360fc2043cbfe5d4105657c3bd721e592' - '52412cb1da3169f246bb99f1299c91e1da3c14ca23876475918f534bb887f8c4') + '8ff834ed3f34fbbc969d2ec0abb3010033d1f3a3aa2db9fc81608e955a7d561c') # Possible replacements are listed in build/linux/unbundle/replace_gn_files.py # Keys are the names in the above script; values are the dependencies in Arch readonly -A _system_libs=( #[ffmpeg]=ffmpeg # https://crbug.com/731766 [flac]=flac - #[fontconfig]=fontconfig # Enable for M65 - #[freetype]=freetype2 # Using 'use_system_freetype=true' until M65 - #[harfbuzz-ng]=harfbuzz # Using 'use_system_harfbuzz=true' until M65 + [fontconfig]=fontconfig + [freetype]=freetype2 + [harfbuzz-ng]=harfbuzz [icu]=icu [libdrm]= [libjpeg]=libjpeg #[libpng]=libpng # https://crbug.com/752403#c10 - #[libvpx]=libvpx # https://bugs.gentoo.org/611394 + [libvpx]=libvpx [libwebp]=libwebp #[libxml]=libxml2 # https://crbug.com/736026 [libxslt]=libxslt @@ -136,10 +128,8 @@ readonly -A _system_libs=( readonly _unwanted_bundled_libs=( ${!_system_libs[@]} ${_system_libs[libjpeg]+libjpeg_turbo} - freetype - harfbuzz-ng ) -depends+=(${_system_libs[@]} freetype2 harfbuzz) +depends+=(${_system_libs[@]}) prepare() { cd "$srcdir/chromium-$pkgver" @@ -160,21 +150,13 @@ prepare() { sed "s/@WIDEVINE_VERSION@/Pinkie Pie/" ../chromium-widevine.patch | patch -Np1 - # https://chromium-review.googlesource.com/c/chromium/src/+/712575 - patch -Np1 -i ../chromium-exclude_unwind_tables.patch - - # https://crbug.com/772655 - patch -Np1 -i ../chromium-use-fromUTF8-for-UnicodeString-construction.patch - - # https://crbug.com/789163 - patch -Np1 -i ../chromium-omnibox-unescape-fragment.patch - # https://crbug.com/skia/6663#c10 patch -Np4 -i ../chromium-skia-harmony.patch # Fixes from Gentoo - patch -Np1 -i ../chromium-memcpy-r0.patch patch -Np1 -i ../chromium-clang-r2.patch + patch -Np1 -i ../chromium-math.h-r0.patch + patch -Np1 -i ../chromium-stdint.patch # Remove compiler flags not supported by our system clang sed -i \ @@ -185,9 +167,7 @@ prepare() { build/config/compiler/BUILD.gn msg2 'Applying VA-API patches' - patch -Np1 -i ../chromium-vaapi-move.patch - patch -Np1 -i ../chromium-vaapi-init.patch - patch -Np1 -i ../chromium-vaapi-rgbx.patch + patch -Np1 -i ../chromium-vaapi-init-r16.patch patch -Np1 -i ../chromium-vaapi-r16.patch msg2 'Applying Inox patchset' @@ -236,7 +216,7 @@ prepare() { \! -path "*third_party/$_lib/chromium/*" \ \! -path "*third_party/$_lib/google/*" \ \! -path './base/third_party/icu/*' \ - \! -path './third_party/freetype/src/src/psnames/pstables.h' \ + \! -path './third_party/pdfium/third_party/freetype/include/pstables.h' \ \! -path './third_party/yasm/run_yasm.py' \ \! -regex '.*\.\(gn\|gni\|isolate\)' \ -delete @@ -282,16 +262,11 @@ build() { 'ffmpeg_branding="Chrome"' 'proprietary_codecs=true' 'link_pulseaudio=true' - 'use_system_freetype=true' - 'use_system_harfbuzz=true' - 'use_gconf=false' 'use_gnome_keyring=false' 'use_gold=false' 'use_sysroot=false' 'linux_use_bundled_binutils=false' 'use_custom_libcxx=false' - 'use_system_libjpeg=true' - 'use_system_libpng=true' 'use_vaapi=true' 'enable_hangout_services_extension=false' 'enable_widevine=true' @@ -305,7 +280,9 @@ build() { ) if check_option strip y; then - _flags+=('exclude_unwind_tables=true') + CFLAGS+=' -fno-unwind-tables -fno-asynchronous-unwind-tables' + CXXFLAGS+=' -fno-unwind-tables -fno-asynchronous-unwind-tables' + CPPFLAGS+=' -DNO_UNWIND_TABLES' fi msg2 'Building GN' diff --git a/chromium-exclude_unwind_tables.patch b/chromium-exclude_unwind_tables.patch deleted file mode 100644 index 1961f15..0000000 --- a/chromium-exclude_unwind_tables.patch +++ /dev/null @@ -1,33 +0,0 @@ -diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni -index ad40fd9..50e19a4 100644 ---- a/build/config/compiler/compiler.gni -+++ b/build/config/compiler/compiler.gni -@@ -68,19 +68,19 @@ - - # Whether or not we should use position independent code. - use_pic = true -+ -+ # Exclude unwind tables for official builds as unwinding can be done from -+ # stack dumps produced by Crashpad at a later time "offline" in the crash -+ # server. For unofficial (e.g. development) builds and non-Chrome branded -+ # (e.g. Cronet which doesn't use Crashpad, crbug.com/479283) builds it's -+ # useful to be able to unwind at runtime. -+ exclude_unwind_tables = -+ (is_chrome_branded && is_official_build) || -+ (is_chromecast && !is_cast_desktop_build && !is_debug && !is_fuchsia) - } - - assert(!is_cfi || use_thin_lto, "CFI requires ThinLTO") - --# Exclude unwind tables for official builds as unwinding can be done from stack --# dumps produced by Crashpad at a later time "offline" in the crash server. --# For unofficial (e.g. development) builds and non-Chrome branded (e.g. Cronet --# which doesn't use Crashpad, crbug.com/479283) builds it's useful to be able --# to unwind at runtime. --exclude_unwind_tables = -- (is_chrome_branded && is_official_build) || -- (is_chromecast && !is_cast_desktop_build && !is_debug && !is_fuchsia) -- - # If true, optimize for size. Does not affect windows builds. - # Linux & Mac favor speed over size. - # TODO(brettw) it's weird that Mac and desktop Linux are different. We should diff --git a/chromium-libva-r2.patch b/chromium-libva-r2.patch deleted file mode 100644 index 855f85d..0000000 --- a/chromium-libva-r2.patch +++ /dev/null @@ -1,35 +0,0 @@ ---- a/content/common/sandbox_linux/bpf_gpu_policy_linux.cc -+++ b/content/common/sandbox_linux/bpf_gpu_policy_linux.cc -@@ -313,11 +313,11 @@ bool GpuProcessPolicy::PreSandboxHook() - dlopen(I965DrvVideoPath, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); - if (I965HybridDrvVideoPath) - dlopen(I965HybridDrvVideoPath, RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); -- dlopen("libva.so.1", RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); -+ dlopen("libva.so.2", RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); - #if defined(USE_OZONE) -- dlopen("libva-drm.so.1", RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); -+ dlopen("libva-drm.so.2", RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); - #elif defined(USE_X11) -- dlopen("libva-x11.so.1", RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); -+ dlopen("libva-x11.so.2", RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE); - #endif - } - } ---- a/media/gpu/vaapi_wrapper.cc -+++ b/media/gpu/vaapi_wrapper.cc -@@ -1124,12 +1124,12 @@ void VaapiWrapper::PreSandboxInitializat - bool VaapiWrapper::PostSandboxInitialization() { - StubPathMap paths; - -- paths[kModuleVa].push_back("libva.so.1"); -+ paths[kModuleVa].push_back("libva.so.2"); - - #if defined(USE_X11) -- paths[kModuleVa_x11].push_back("libva-x11.so.1"); -+ paths[kModuleVa_x11].push_back("libva-x11.so.2"); - #elif defined(USE_OZONE) -- paths[kModuleVa_drm].push_back("libva-drm.so.1"); -+ paths[kModuleVa_drm].push_back("libva-drm.so.2"); - #endif - - return InitializeStubs(paths); diff --git a/chromium-math.h-r0.patch b/chromium-math.h-r0.patch new file mode 100644 index 0000000..6c7c747 --- /dev/null +++ b/chromium-math.h-r0.patch @@ -0,0 +1,29 @@ +From 9f63f94a11abc34d40ede8b8712fa15b5844a8c0 Mon Sep 17 00:00:00 2001 +From: Tom Anderson +Date: Sat, 27 Jan 2018 20:03:37 +0000 +Subject: [PATCH] Fix build with glibc 2.27 + +BUG=806340 +TBR=hamelphi@chromium.org + +Change-Id: Ib4e5091212d874d9ad88f3e9a1fdfee3ed7e0d5e +Reviewed-on: https://chromium-review.googlesource.com/890059 +Reviewed-by: Thomas Anderson +Reviewed-by: Philippe Hamel +Commit-Queue: Thomas Anderson +Cr-Commit-Position: refs/heads/master@{#532249} +--- + +diff --git a/components/assist_ranker/ranker_example_util.cc b/components/assist_ranker/ranker_example_util.cc +index 54d4dbd..ceedd8f 100644 +--- a/components/assist_ranker/ranker_example_util.cc ++++ b/components/assist_ranker/ranker_example_util.cc +@@ -2,6 +2,8 @@ + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++ + #include "components/assist_ranker/ranker_example_util.h" + #include "base/bit_cast.h" + #include "base/format_macros.h" diff --git a/chromium-memcpy-r0.patch b/chromium-memcpy-r0.patch deleted file mode 100644 index c8b2e19..0000000 --- a/chromium-memcpy-r0.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 4942f56ceb6d60d6f54ebca8e6eba8ba01c278e8 Mon Sep 17 00:00:00 2001 -From: Tomas Popela -Date: Thu, 7 Dec 2017 22:33:34 +0000 -Subject: [PATCH] memcpy used without including string.h - -Compiling Chromium with Clang 4.0.1 and using libstdc++ will fail on using -memcpy without including string.h. - -Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel -Change-Id: Idced1d5de3baf6b520d4a2d61774120642ead1a8 -Reviewed-on: https://chromium-review.googlesource.com/813737 -Reviewed-by: Thomas Anderson -Reviewed-by: vmpstr -Commit-Queue: Thomas Anderson -Cr-Commit-Position: refs/heads/master@{#522579} ---- - cc/paint/raw_memory_transfer_cache_entry.cc | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/cc/paint/raw_memory_transfer_cache_entry.cc b/cc/paint/raw_memory_transfer_cache_entry.cc -index 9e4660c685ee..95ad50b1a338 100644 ---- a/cc/paint/raw_memory_transfer_cache_entry.cc -+++ b/cc/paint/raw_memory_transfer_cache_entry.cc -@@ -4,6 +4,8 @@ - - #include "cc/paint/raw_memory_transfer_cache_entry.h" - -+#include -+ - namespace cc { - - ClientRawMemoryTransferCacheEntry::ClientRawMemoryTransferCacheEntry( --- -2.15.1 diff --git a/chromium-omnibox-unescape-fragment.patch b/chromium-omnibox-unescape-fragment.patch deleted file mode 100644 index 59ab040..0000000 --- a/chromium-omnibox-unescape-fragment.patch +++ /dev/null @@ -1,437 +0,0 @@ -commit 03f9a90d8a783f9d1a94935ac298338a1e694380 -Author: Eric Lawrence -Date: Sat Dec 16 04:48:11 2017 +0000 - - Reland of 'Unescape fragment for display in Omnibox' - - The original landing broke EGTests for iOS Simulator and Device which - were not run by the CQ. This change includes updated EGTests. - - TBR=pkasting@chromium.org - - Bug: 789163, 643458 - Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs - Change-Id: Ie30afcb7bcba5affc7266d72a10b7f8dc0074314 - Reviewed-on: https://chromium-review.googlesource.com/830093 - Reviewed-by: Eric Lawrence - Reviewed-by: Peter Kasting - Reviewed-by: Eugene But - Commit-Queue: Eric Lawrence - Cr-Commit-Position: refs/heads/master@{#524591} - -diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc -index 6e5dff47f7ec..24f1056d49d5 100644 ---- a/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc -+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_browsertest.cc -@@ -462,3 +462,13 @@ IN_PROC_BROWSER_TEST_F(OmniboxViewViewsTest, TextElideStatus) { - EXPECT_EQ(omnibox_view_views->GetRenderText()->elide_behavior(), - gfx::NO_ELIDE); - } -+ -+IN_PROC_BROWSER_TEST_F(OmniboxViewViewsTest, FragmentUnescapedForDisplay) { -+ OmniboxView* view = nullptr; -+ ASSERT_NO_FATAL_FAILURE(GetOmniboxViewForBrowser(browser(), &view)); -+ ui_test_utils::NavigateToURL(browser(), -+ GURL("https://www.google.com/#%E2%98%83")); -+ -+ EXPECT_EQ(view->GetText(), -+ base::UTF8ToUTF16("https://www.google.com/#\u2603")); -+} -diff --git a/components/url_formatter/elide_url_unittest.cc b/components/url_formatter/elide_url_unittest.cc -index 5e0384af72a2..d403ce065f45 100644 ---- a/components/url_formatter/elide_url_unittest.cc -+++ b/components/url_formatter/elide_url_unittest.cc -@@ -347,15 +347,16 @@ TEST(TextEliderTest, TestElisionSpecialCases) { - kEllipsisStr + "berkeley.edu:4430/" + kEllipsisStr + "/arbitfilename"}, - - // Unescaping. -- {"http://www/%E4%BD%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0", -- "www/\xe4\xbd\xa0\xe5\xa5\xbd?q=\xe4\xbd\xa0\xe5\xa5\xbd#" + -+ {"http://www/%E4%BD%A0%E5%A5%BD?" -+ "q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0\xe4\xbd\xa0\xe4\xbd\xa0", -+ "www/\xe4\xbd\xa0\xe5\xa5\xbd?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0" + - kEllipsisStr}, - - // Invalid unescaping for path. The ref will always be valid UTF-8. We - // don't bother to do too many edge cases, since these are handled by the - // escaper unittest. - {"http://www/%E4%A0%E5%A5%BD?q=%E4%BD%A0%E5%A5%BD#\xe4\xbd\xa0", -- "www/%E4%A0%E5%A5%BD?q=\xe4\xbd\xa0\xe5\xa5\xbd#" + kEllipsisStr}, -+ "www/%E4%A0%E5%A5%BD?q=\xe4\xbd\xa0\xe5\xa5\xbd#\xe4\xbd\xa0"}, - }; - - RunElisionTest(testcases); -diff --git a/components/url_formatter/url_formatter.cc b/components/url_formatter/url_formatter.cc -index d5c0d314a5de..c422e3d98a72 100644 ---- a/components/url_formatter/url_formatter.cc -+++ b/components/url_formatter/url_formatter.cc -@@ -586,11 +586,10 @@ base::string16 FormatUrlWithAdjustments( - NonHostComponentTransform(unescape_rules), - &url_string, &new_parsed->query, adjustments); - -- // Ref. This is valid, unescaped UTF-8, so we can just convert. - if (parsed.ref.is_valid()) - url_string.push_back('#'); - AppendFormattedComponent(spec, parsed.ref, -- NonHostComponentTransform(net::UnescapeRule::NONE), -+ NonHostComponentTransform(unescape_rules), - &url_string, &new_parsed->ref, adjustments); - } - -diff --git a/components/url_formatter/url_formatter_unittest.cc b/components/url_formatter/url_formatter_unittest.cc -index 6fd3ece50f7d..5aaf31105a43 100644 ---- a/components/url_formatter/url_formatter_unittest.cc -+++ b/components/url_formatter/url_formatter_unittest.cc -@@ -828,7 +828,7 @@ TEST(UrlFormatterTest, FormatUrl) { - - {"With a port number and a reference", - "http://www.google.com:8080/#\xE3\x82\xB0", default_format_type, -- net::UnescapeRule::NORMAL, L"http://www.google.com:8080/#%E3%82%B0", 7}, -+ net::UnescapeRule::NORMAL, L"http://www.google.com:8080/#\x30B0", 7}, - - // -------- IDN tests -------- - {"Japanese IDN with ja", "http://xn--l8jvb1ey91xtjb.jp", -@@ -1119,7 +1119,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) { - kFormatUrlOmitNothing, net::UnescapeRule::NORMAL, &parsed, - nullptr, nullptr); - EXPECT_EQ(WideToUTF16(L"http://\x30B0:\x30FC@\x30B0\x30FC\x30B0\x30EB.jp:8080" -- L"/\x30B0/?q=\x30B0#%E3%82%B0"), -+ L"/\x30B0/?q=\x30B0#\x30B0"), - formatted); - EXPECT_EQ(WideToUTF16(L"\x30B0"), - formatted.substr(parsed.username.begin, parsed.username.len)); -@@ -1133,7 +1133,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) { - formatted.substr(parsed.path.begin, parsed.path.len)); - EXPECT_EQ(WideToUTF16(L"q=\x30B0"), - formatted.substr(parsed.query.begin, parsed.query.len)); -- EXPECT_EQ(WideToUTF16(L"%E3%82%B0"), -+ EXPECT_EQ(WideToUTF16(L"\x30B0"), - formatted.substr(parsed.ref.begin, parsed.ref.len)); - - // Omit_username_password + unescape case. -@@ -1143,7 +1143,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) { - kFormatUrlOmitUsernamePassword, net::UnescapeRule::NORMAL, - &parsed, nullptr, nullptr); - EXPECT_EQ(WideToUTF16(L"http://\x30B0\x30FC\x30B0\x30EB.jp:8080" -- L"/\x30B0/?q=\x30B0#%E3%82%B0"), -+ L"/\x30B0/?q=\x30B0#\x30B0"), - formatted); - EXPECT_FALSE(parsed.username.is_valid()); - EXPECT_FALSE(parsed.password.is_valid()); -@@ -1155,7 +1155,7 @@ TEST(UrlFormatterTest, FormatUrlParsed) { - formatted.substr(parsed.path.begin, parsed.path.len)); - EXPECT_EQ(WideToUTF16(L"q=\x30B0"), - formatted.substr(parsed.query.begin, parsed.query.len)); -- EXPECT_EQ(WideToUTF16(L"%E3%82%B0"), -+ EXPECT_EQ(WideToUTF16(L"\x30B0"), - formatted.substr(parsed.ref.begin, parsed.ref.len)); - - // View-source case. -@@ -1389,9 +1389,12 @@ TEST(UrlFormatterTest, FormatUrlWithOffsets) { - kFormatUrlOmitNothing, net::UnescapeRule::SPACES, unescape_offsets); - - const size_t ref_offsets[] = { -- 0, 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}; -+ 0, 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, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, -+ 32, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, kNpos, 33}; -+ - // Unescape to "http://www.google.com/foo.html#\x30B0\x30B0z". - CheckAdjustedOffsets("http://www.google.com/foo.html#%E3%82%B0%E3%82%B0z", - kFormatUrlOmitNothing, net::UnescapeRule::NORMAL, -diff --git a/ios/chrome/browser/web/push_and_replace_state_navigation_egtest.mm b/ios/chrome/browser/web/push_and_replace_state_navigation_egtest.mm -index b67f2a08d0a0..431ad368f07d 100644 ---- a/ios/chrome/browser/web/push_and_replace_state_navigation_egtest.mm -+++ b/ios/chrome/browser/web/push_and_replace_state_navigation_egtest.mm -@@ -59,40 +59,42 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - // Push 3 URLs. Verify that the URL changed and the status was updated. - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateHashWithObject"]; - [self assertStatusText:@"pushStateHashWithObject" -- withURL:pushStateHashWithObjectURL -+ withOmniboxText:pushStateHashWithObjectURL.GetContent() - pageLoaded:NO]; - - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateRootPath"]; - [self assertStatusText:@"pushStateRootPath" -- withURL:pushStateRootPathURL -+ withOmniboxText:pushStateRootPathURL.GetContent() - pageLoaded:NO]; - - [ChromeEarlGrey tapWebViewElementWithID:@"pushStatePathSpace"]; - [self assertStatusText:@"pushStatePathSpace" -- withURL:pushStatePathSpaceURL -+ withOmniboxText:pushStatePathSpaceURL.GetContent() - pageLoaded:NO]; - - // Go back and check that the page doesn't load and the status text is updated - // by the popstate event. - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [self assertStatusText:@"pushStateRootPath" -- withURL:pushStateRootPathURL -+ withOmniboxText:pushStateRootPathURL.GetContent() - pageLoaded:NO]; - - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [self assertStatusText:@"pushStateHashWithObject" -- withURL:pushStateHashWithObjectURL -+ withOmniboxText:pushStateHashWithObjectURL.GetContent() - pageLoaded:NO]; - - [ChromeEarlGrey tapWebViewElementWithID:@"goBack"]; - const GURL historyTestURL = web::test::HttpServer::MakeUrl(kHistoryTestUrl); -- [self assertStatusText:NULL withURL:historyTestURL pageLoaded:NO]; -+ [self assertStatusText:nil -+ withOmniboxText:historyTestURL.GetContent() -+ pageLoaded:NO]; - - // Go forward 2 pages and check that the page doesn't load and the status text - // is updated by the popstate event. - [ChromeEarlGrey tapWebViewElementWithID:@"goForward2"]; - [self assertStatusText:@"pushStateRootPath" -- withURL:pushStateRootPathURL -+ withOmniboxText:pushStateRootPathURL.GetContent() - pageLoaded:NO]; - } - -@@ -108,7 +110,7 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - web::test::HttpServer::MakeUrl(kReplaceStateHashWithObjectURL); - [ChromeEarlGrey tapWebViewElementWithID:@"replaceStateHashWithObject"]; - [self assertStatusText:@"replaceStateHashWithObject" -- withURL:replaceStateHashWithObjectURL -+ withOmniboxText:replaceStateHashWithObjectURL.GetContent() - pageLoaded:NO]; - - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; -@@ -119,7 +121,7 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - [[EarlGrey selectElementWithMatcher:ForwardButton()] - performAction:grey_tap()]; - [self assertStatusText:@"replaceStateHashWithObject" -- withURL:replaceStateHashWithObjectURL -+ withOmniboxText:replaceStateHashWithObjectURL.GetContent() - pageLoaded:YES]; - - // Push URL then replace it. Do this twice. -@@ -127,44 +129,44 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - web::test::HttpServer::MakeUrl(kPushStateHashStringURL); - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateHashString"]; - [self assertStatusText:@"pushStateHashString" -- withURL:pushStateHashStringURL -+ withOmniboxText:pushStateHashStringURL.GetContent() - pageLoaded:NO]; - - const GURL replaceStateHashStringURL = - web::test::HttpServer::MakeUrl(kReplaceStateHashStringURL); - [ChromeEarlGrey tapWebViewElementWithID:@"replaceStateHashString"]; - [self assertStatusText:@"replaceStateHashString" -- withURL:replaceStateHashStringURL -+ withOmniboxText:replaceStateHashStringURL.GetContent() - pageLoaded:NO]; - - const GURL pushStatePathURL = - web::test::HttpServer::MakeUrl(kPushStatePathURL); - [ChromeEarlGrey tapWebViewElementWithID:@"pushStatePath"]; - [self assertStatusText:@"pushStatePath" -- withURL:pushStatePathURL -+ withOmniboxText:pushStatePathURL.GetContent() - pageLoaded:NO]; - - const GURL replaceStateRootPathSpaceURL = - web::test::HttpServer::MakeUrl(kReplaceStateRootPathSpaceURL); - [ChromeEarlGrey tapWebViewElementWithID:@"replaceStateRootPathSpace"]; - [self assertStatusText:@"replaceStateRootPathSpace" -- withURL:replaceStateRootPathSpaceURL -+ withOmniboxText:replaceStateRootPathSpaceURL.GetContent() - pageLoaded:NO]; - - // Go back and check URLs. - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [self assertStatusText:@"replaceStateHashString" -- withURL:replaceStateHashStringURL -+ withOmniboxText:replaceStateHashStringURL.GetContent() - pageLoaded:NO]; - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [self assertStatusText:@"replaceStateHashWithObject" -- withURL:replaceStateHashWithObjectURL -+ withOmniboxText:replaceStateHashWithObjectURL.GetContent() - pageLoaded:NO]; - - // Go forward and check URL. - [ChromeEarlGrey tapWebViewElementWithID:@"goForward2"]; - [self assertStatusText:@"replaceStateRootPathSpace" -- withURL:replaceStateRootPathSpaceURL -+ withOmniboxText:replaceStateRootPathSpaceURL.GetContent() - pageLoaded:NO]; - } - -@@ -180,11 +182,11 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - web::test::HttpServer::MakeUrl(kPushStateHashStringURL); - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateHashString"]; - [self assertStatusText:@"pushStateHashString" -- withURL:pushStateHashStringURL -+ withOmniboxText:pushStateHashStringURL.GetContent() - pageLoaded:NO]; - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateHashString"]; - [self assertStatusText:@"pushStateHashString" -- withURL:pushStateHashStringURL -+ withOmniboxText:pushStateHashStringURL.GetContent() - pageLoaded:NO]; - - // Load a non-pushed URL. -@@ -194,7 +196,7 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - [ChromeEarlGrey loadURL:historyTestURL]; - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateHashString"]; - [self assertStatusText:@"pushStateHashString" -- withURL:pushStateHashStringURL -+ withOmniboxText:pushStateHashStringURL.GetContent() - pageLoaded:NO]; - - // At this point the history looks like this: -@@ -202,38 +204,54 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - - // Go back (to second history.html) and verify page did not load. - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; -- [self assertStatusText:nil withURL:historyTestURL pageLoaded:NO]; -+ [self assertStatusText:nil -+ withOmniboxText:historyTestURL.GetContent() -+ pageLoaded:NO]; - - // Go back twice (to second #string) and verify page did load. - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; -- [self assertStatusText:nil withURL:pushStateHashStringURL pageLoaded:YES]; -+ [self assertStatusText:nil -+ withOmniboxText:pushStateHashStringURL.GetContent() -+ pageLoaded:YES]; - - // Go back once (to first #string) and verify page did not load. - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [self assertStatusText:@"pushStateHashString" -- withURL:pushStateHashStringURL -+ withOmniboxText:pushStateHashStringURL.GetContent() - pageLoaded:NO]; - - // Go forward 4 entries at once (to third #string) and verify page did load. - [ChromeEarlGrey tapWebViewElementWithID:@"goForward4"]; - -- [self assertStatusText:nil withURL:pushStateHashStringURL pageLoaded:YES]; -+ [self assertStatusText:nil -+ withOmniboxText:pushStateHashStringURL.GetContent() -+ pageLoaded:YES]; - - // Go back 4 entries at once (to first #string) and verify page did load. - [ChromeEarlGrey tapWebViewElementWithID:@"goBack4"]; - -- [self assertStatusText:NULL withURL:pushStateHashStringURL pageLoaded:YES]; -+ [self assertStatusText:nil -+ withOmniboxText:pushStateHashStringURL.GetContent() -+ pageLoaded:YES]; - } - - // Tests calling pushState with unicode characters. - - (void)testHtml5HistoryPushUnicodeCharacters { -- const GURL pushStateUnicodeURLEncoded = web::test::HttpServer::MakeUrl( -- "http://ios/testing/data/http_server_files/" -- "history.html#unicode%E1%84%91"); -- const GURL pushStateUnicode2URLEncoded = web::test::HttpServer::MakeUrl( -- "http://ios/testing/data/http_server_files/" -- "history.html#unicode2%E2%88%A2"); -+ // The GURL object %-escapes Unicode characters in the URL's fragment, -+ // but the omnibox decodes them back to Unicode for display. -+ std::string pushStateUnicode = -+ web::test::HttpServer::MakeUrl( -+ "http://ios/testing/data/http_server_files/" -+ "history.html#unicode") -+ .GetContent() + -+ "\xe1\x84\x91"; -+ std::string pushStateUnicode2 = -+ web::test::HttpServer::MakeUrl( -+ "http://ios/testing/data/http_server_files/" -+ "history.html#unicode2") -+ .GetContent() + -+ "\xe2\x88\xa2"; - const char pushStateUnicodeLabel[] = "Action: pushStateUnicodeᄑ"; - NSString* pushStateUnicodeStatus = @"pushStateUnicodeᄑ"; - const char pushStateUnicode2Label[] = "Action: pushStateUnicode2∢"; -@@ -242,21 +260,16 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - web::test::SetUpFileBasedHttpServer(); - [ChromeEarlGrey loadURL:web::test::HttpServer::MakeUrl(kHistoryTestUrl)]; - -- // TODO(crbug.com/643458): The fact that the URL shows %-escaped is due to -- // NSURL escaping to make UIWebView/JS happy. See if it's possible to -- // represent differently such that it displays unescaped. - // Do 2 push states with unicode characters. - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateUnicode"]; - [[EarlGrey -- selectElementWithMatcher:chrome_test_util::OmniboxText( -- pushStateUnicodeURLEncoded.GetContent())] -+ selectElementWithMatcher:chrome_test_util::OmniboxText(pushStateUnicode)] - assertWithMatcher:grey_notNil()]; - [ChromeEarlGrey waitForWebViewContainingText:pushStateUnicodeLabel]; - - [ChromeEarlGrey tapWebViewElementWithID:@"pushStateUnicode2"]; - [[EarlGrey -- selectElementWithMatcher:chrome_test_util::OmniboxText( -- pushStateUnicode2URLEncoded.GetContent())] -+ selectElementWithMatcher:chrome_test_util::OmniboxText(pushStateUnicode2)] - assertWithMatcher:grey_notNil()]; - [ChromeEarlGrey waitForWebViewContainingText:pushStateUnicode2Label]; - -@@ -266,18 +279,18 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - [ChromeEarlGrey tapWebViewElementWithID:@"pushStatePath"]; - - [self assertStatusText:@"pushStatePath" -- withURL:pushStatePathURL -+ withOmniboxText:pushStatePathURL.GetContent() - pageLoaded:NO]; - - // Go back and check the unicode in the URL and status. - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [self assertStatusText:pushStateUnicode2Status -- withURL:pushStateUnicode2URLEncoded -+ withOmniboxText:pushStateUnicode2 - pageLoaded:NO]; - - [[EarlGrey selectElementWithMatcher:BackButton()] performAction:grey_tap()]; - [self assertStatusText:pushStateUnicodeStatus -- withURL:pushStateUnicodeURLEncoded -+ withOmniboxText:pushStateUnicode - pageLoaded:NO]; - } - -@@ -321,10 +334,11 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - - #pragma mark - Utility methods - --// Assert that status text |status| is displayed in the webview, that "onloaded" --// text is displayed if pageLoaded is YES, and that the URL is as expected. -+// Assert that status text |status|, if non-nil, is displayed in the webview, -+// that the omnibox text is as expected, and that "onload" text is displayed if -+// pageLoaded is YES. - - (void)assertStatusText:(NSString*)status -- withURL:(const GURL&)urlToVerify -+ withOmniboxText:(const std::string&)omniboxText - pageLoaded:(BOOL)pageLoaded { - if (pageLoaded) { - [ChromeEarlGrey waitForWebViewContainingText:"onload"]; -@@ -332,14 +346,14 @@ const char* kReplaceStateRootPathSpaceURL = "http://ios/rep lace"; - [ChromeEarlGrey waitForWebViewNotContainingText:"onload"]; - } - -- if (status != NULL) { -+ if (status != nil) { - NSString* statusLabel = [NSString stringWithFormat:@"Action: %@", status]; - [ChromeEarlGrey - waitForWebViewContainingText:base::SysNSStringToUTF8(statusLabel)]; - } - -- [[EarlGrey selectElementWithMatcher:chrome_test_util::OmniboxText( -- urlToVerify.GetContent())] -+ [[EarlGrey -+ selectElementWithMatcher:chrome_test_util::OmniboxText(omniboxText)] - assertWithMatcher:grey_notNil()]; - } - diff --git a/chromium-stdint.patch b/chromium-stdint.patch new file mode 100644 index 0000000..8774439 --- /dev/null +++ b/chromium-stdint.patch @@ -0,0 +1,21 @@ +From 0235c2b657d936f3cdb09053776e5929fc84704b Mon Sep 17 00:00:00 2001 +From: Tomas Popela +Date: Wed, 31 Jan 2018 18:57:07 +0000 +Subject: [PATCH] Add missing stdint include + +diff --git a/chrome/browser/vr/sample_queue.cc b/chrome/browser/vr/sample_queue.cc +index c2ca777ce90c..53cb3aab1576 100644 +--- a/chrome/browser/vr/sample_queue.cc ++++ b/chrome/browser/vr/sample_queue.cc +@@ -2,6 +2,8 @@ + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++#include ++ + #include "chrome/browser/vr/sample_queue.h" + + namespace vr { +-- +2.16.2 + diff --git a/chromium-use-fromUTF8-for-UnicodeString-construction.patch b/chromium-use-fromUTF8-for-UnicodeString-construction.patch deleted file mode 100644 index 796390d..0000000 --- a/chromium-use-fromUTF8-for-UnicodeString-construction.patch +++ /dev/null @@ -1,67 +0,0 @@ -From e58fa0ba66272c5f28828b15d06c7e42a9882b3b Mon Sep 17 00:00:00 2001 -From: Jungshik Shin -Date: Sat, 16 Dec 2017 04:19:27 +0000 -Subject: [PATCH] Use fromUTF8() for UnicodeString construction from UTF-8 - -Chrome's copy of ICU is built with U_CHARSET_IS_UTF8=1 so that |char *| -buffer is treated as UTF-8 when constructing UnicodeString() regardless -of the default encoding of the current locale on Linux or non-Unicode code -page on Windows. - -However, some Linux distros do not set U_CHARSET_IS_UTF=1 when building -ICU and Chromium build with system_icu crashes when Chromium is run in -non-UTF-8 locale (e.g. 'C'). - -To make Chromium work in a non-UTF-8 locale (which is pretty rare these -days), use 'icu::UnicodeString::fromUTF8(StringPiece)' instead of -'icu::UnicodeString(const char*)'. - -Bug: 772655 -Test: components_unittests --gtest_filter=*IDN* -Test: Chromium built with system_icu does not crash in C locale. -Change-Id: I0daa284ec06b8e83814fc70eb8e9e5c96444ebfa -Reviewed-on: https://chromium-review.googlesource.com/831247 -Reviewed-by: Peter Kasting -Commit-Queue: Jungshik Shin -Cr-Commit-Position: refs/heads/master@{#524586} ---- - components/url_formatter/idn_spoof_checker.cc | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/components/url_formatter/idn_spoof_checker.cc b/components/url_formatter/idn_spoof_checker.cc -index a88c5e8f8331..aee748d8a4d5 100644 ---- a/components/url_formatter/idn_spoof_checker.cc -+++ b/components/url_formatter/idn_spoof_checker.cc -@@ -110,8 +110,8 @@ IDNSpoofChecker::IDNSpoofChecker() { - - // These Cyrillic letters look like Latin. A domain label entirely made of - // these letters is blocked as a simplified whole-script-spoofable. -- cyrillic_letters_latin_alike_ = -- icu::UnicodeSet(icu::UnicodeString("[асԁеһіјӏорԛѕԝхуъЬҽпгѵѡ]"), status); -+ cyrillic_letters_latin_alike_ = icu::UnicodeSet( -+ icu::UnicodeString::fromUTF8("[асԁеһіјӏорԛѕԝхуъЬҽпгѵѡ]"), status); - cyrillic_letters_latin_alike_.freeze(); - - cyrillic_letters_ = -@@ -141,8 +141,8 @@ IDNSpoofChecker::IDNSpoofChecker() { - UParseError parse_error; - diacritic_remover_.reset(icu::Transliterator::createFromRules( - UNICODE_STRING_SIMPLE("DropAcc"), -- icu::UnicodeString("::NFD; ::[:Nonspacing Mark:] Remove; ::NFC;" -- " ł > l; ø > o; đ > d;"), -+ icu::UnicodeString::fromUTF8("::NFD; ::[:Nonspacing Mark:] Remove; ::NFC;" -+ " ł > l; ø > o; đ > d;"), - UTRANS_FORWARD, parse_error, status)); - - // Supplement the Unicode confusable list by the following mapping. -@@ -158,7 +158,7 @@ IDNSpoofChecker::IDNSpoofChecker() { - // - U+0D1F (ട) => s - extra_confusable_mapper_.reset(icu::Transliterator::createFromRules( - UNICODE_STRING_SIMPLE("ExtraConf"), -- icu::UnicodeString( -+ icu::UnicodeString::fromUTF8( - "ӏ > l; [кĸκ] > k; п > n; [ƅь] > b; в > b; м > m; н > h; " - "т > t; [шщ] > w; ട > s;"), - UTRANS_FORWARD, parse_error, status)); --- -2.15.1 diff --git a/chromium-vaapi-init.patch b/chromium-vaapi-init-r16.patch similarity index 77% rename from chromium-vaapi-init.patch rename to chromium-vaapi-init-r16.patch index 0cd4557..8b49d2a 100644 --- a/chromium-vaapi-init.patch +++ b/chromium-vaapi-init-r16.patch @@ -1,6 +1,6 @@ -From a57aa298ed3c3b76056d6e24fb09db05dca2f465 Mon Sep 17 00:00:00 2001 +From 281edc278272f0650fc190c8539d443ac59157bc Mon Sep 17 00:00:00 2001 From: Daniel Charles -Date: Thu, 25 Jan 2018 13:01:37 -0800 +Date: Thu, 08 Feb 2018 02:36:51 +0000 Subject: [PATCH] vaapi initialization: move it to vaapi_wrapper vaapi loading of libraries happens on the Pre and Post Sandbox @@ -10,18 +10,28 @@ and i965_drv_video shared libraries to vaapi_wrapper. When calling PreSandbox function in vaapi_wrapper libva will open i965_drv_video shared library and both will be available for use -BUG=chromium:785117 -TEST="video initialization of h/w dec/enc, VAVDA/VAVEA/VAJDA subjective" -TEST="testing and include unittests and autotests" +BUG=785117 +TEST=video initialization of h/w dec/enc, VAVDA/VAVEA/VAJDA subjective + testing and include unittests and autotests Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel Change-Id: I862bb49f1167d7437e80387882cb9081ad53f54b Signed-off-by: Daniel Charles +Reviewed-on: https://chromium-review.googlesource.com/666138 +Commit-Queue: Miguel Casas +Reviewed-by: Kenneth Russell +Reviewed-by: Antoine Labour +Reviewed-by: Dongseong Hwang +Reviewed-by: Pawel Osciak +Reviewed-by: Jorge Lucangeli Obes +Cr-Commit-Position: refs/heads/master@{#535274} --- +diff --git a/content/gpu/gpu_sandbox_hook_linux.cc b/content/gpu/gpu_sandbox_hook_linux.cc +index e69e23e..5c4013c 100644 --- a/content/gpu/gpu_sandbox_hook_linux.cc +++ b/content/gpu/gpu_sandbox_hook_linux.cc -@@ -28,10 +28,6 @@ +@@ -29,10 +29,6 @@ #include "services/service_manager/sandbox/linux/bpf_gpu_policy_linux.h" #include "services/service_manager/sandbox/linux/sandbox_linux.h" @@ -32,7 +42,7 @@ Signed-off-by: Daniel Charles using sandbox::bpf_dsl::Policy; using sandbox::syscall_broker::BrokerFilePermission; using sandbox::syscall_broker::BrokerProcess; -@@ -47,22 +43,6 @@ inline bool IsChromeOS() { +@@ -48,22 +44,6 @@ #endif } @@ -55,7 +65,7 @@ Signed-off-by: Daniel Charles inline bool IsArchitectureArm() { #if defined(ARCH_CPU_ARM_FAMILY) return true; -@@ -87,14 +67,6 @@ inline bool UseLibV4L2() { +@@ -88,14 +68,6 @@ #endif } @@ -70,7 +80,7 @@ Signed-off-by: Daniel Charles constexpr int dlopen_flag = RTLD_NOW | RTLD_GLOBAL | RTLD_NODELETE; void AddV4L2GpuWhitelist( -@@ -259,51 +231,6 @@ void LoadV4L2Libraries() { +@@ -270,50 +242,6 @@ } } @@ -79,8 +89,7 @@ Signed-off-by: Daniel Charles - if (IsArchitectureX86_64() || IsArchitectureI386()) { - // Accelerated video dlopen()'s some shared objects - // inside the sandbox, so preload them now. -- if (options.vaapi_accelerated_video_encode_enabled || -- options.accelerated_video_decode_enabled) { +- if (IsAcceleratedVideoEnabled(options)) { - if (IsLibVAVersion2()) { - if (IsArchitectureX86_64()) { - dlopen("/usr/lib64/va/drivers/i965_drv_video.so", dlopen_flag); @@ -122,7 +131,7 @@ Signed-off-by: Daniel Charles bool LoadLibrariesForGpu( const service_manager::SandboxSeccompBPF::Options& options) { if (IsChromeOS()) { -@@ -316,7 +243,6 @@ bool LoadLibrariesForGpu( +@@ -326,7 +254,6 @@ if (options.use_amd_specific_policies) return LoadAmdGpuLibraries(); } @@ -130,9 +139,23 @@ Signed-off-by: Daniel Charles return true; } +diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc +index bc3d735..7421a74 100644 --- a/media/gpu/vaapi/vaapi_wrapper.cc +++ b/media/gpu/vaapi/vaapi_wrapper.cc -@@ -163,9 +163,6 @@ class VADisplayState { +@@ -48,6 +48,11 @@ + using media_gpu_vaapi::kModuleVa_x11; + #endif + using media_gpu_vaapi::InitializeStubs; ++using media_gpu_vaapi::IsVaInitialized; ++#if defined(USE_X11) ++using media_gpu_vaapi::IsVa_x11Initialized; ++#endif ++using media_gpu_vaapi::IsVa_drmInitialized; + using media_gpu_vaapi::StubPathMap; + + #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ +@@ -166,9 +171,6 @@ void SetDrmFd(base::PlatformFile fd) { drm_fd_.reset(HANDLE_EINTR(dup(fd))); } private: @@ -142,7 +165,7 @@ Signed-off-by: Daniel Charles // Protected by |va_lock_|. int refcount_; -@@ -200,43 +197,12 @@ void VADisplayState::PreSandboxInitializ +@@ -203,41 +205,17 @@ VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile()); } @@ -181,12 +204,15 @@ Signed-off-by: Daniel Charles - static bool result = PostSandboxInitialization(); - if (!result) -- return false; -- - if (refcount_++ > 0) - return true; ++ if (!IsVaInitialized() || ++#if defined(USE_X11) ++ !IsVa_x11Initialized() || ++#endif ++ !IsVa_drmInitialized()) + return false; -@@ -1367,6 +1333,34 @@ void VaapiWrapper::DeinitializeVpp() { + if (refcount_++ > 0) +@@ -1169,6 +1147,38 @@ // static void VaapiWrapper::PreSandboxInitialization() { VADisplayState::PreSandboxInitialization(); @@ -200,6 +226,10 @@ Signed-off-by: Daniel Charles + paths[kModuleVa_x11].push_back(std::string("libva-x11.so.") + va_suffix); +#endif + ++ // InitializeStubs dlopen() VA-API libraries ++ // libva.so ++ // libva-x11.so (X11) ++ // libva-drm.so (X11 and Ozone). + static bool result = InitializeStubs(paths); + if (!result) { + static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; @@ -211,13 +241,13 @@ Signed-off-by: Daniel Charles + DVLOG(1) << kErrorMsg; +#endif + } -+ // next command will dlopen all necessary libraries for -+ // va-api to function properly, to know: -+ // libva.so ++ ++ // VASupportedProfiles::Get creates VADisplayState and in so doing ++ // driver associated libraries are dlopen(), to know: + // i965_drv_video.so + // hybrid_drv_video.so (platforms that support it) -+ // libva-x11.so (X11) or libva-drm.so (Ozone). ++ // libcmrt.so (platforms that support it) + VASupportedProfiles::Get(); } - } // namespace media + VaapiWrapper::VaapiWrapper() diff --git a/chromium-vaapi-move.patch b/chromium-vaapi-move.patch deleted file mode 100644 index 1eb8fad..0000000 --- a/chromium-vaapi-move.patch +++ /dev/null @@ -1,15235 +0,0 @@ -From 42e955c9b3030121dd945e103a39381a92221bdb Mon Sep 17 00:00:00 2001 -From: Miguel Casas -Date: Mon, 04 Dec 2017 14:18:12 +0000 -Subject: [PATCH] vaapi: move vaapi* files to //media/gpu/vaapi/ folder - -This CL moves the (remaining) vaapi related files from -//media/gpu to the existing//media/gpu/vaapi. - -Fully automatic: - -$ git mv media/gpu/vaapi_* media/gpu/vaapi/ -$ git mv media/gpu/va_s* media/gpu/vaapi/ -$ tools/git/mass-rename.p - -TBR= sadrul@chromium.org, avi@chromium.org for -the two first files that just get an include path udpate. - -Bug: none -Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel -Change-Id: Ia1dcbdef3695bae5879d0951fc78cf6dcef3325f -Reviewed-on: https://chromium-review.googlesource.com/801636 -Commit-Queue: Miguel Casas -Reviewed-by: Pawel Osciak -Reviewed-by: Dale Curtis -Cr-Commit-Position: refs/heads/master@{#521330} ---- - ---- a/components/viz/service/main/viz_main_impl.cc -+++ b/components/viz/service/main/viz_main_impl.cc -@@ -29,7 +29,7 @@ - #include "services/service_manager/public/cpp/connector.h" - - #if defined(OS_CHROMEOS) && BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #endif - - namespace { ---- a/content/gpu/gpu_main.cc -+++ b/content/gpu/gpu_main.cc -@@ -95,7 +95,7 @@ - #endif - - #if BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #endif - - namespace content { ---- a/media/gpu/BUILD.gn -+++ b/media/gpu/BUILD.gn -@@ -310,26 +310,26 @@ component("gpu") { - - if (use_vaapi) { - sources += [ -- "va_surface.cc", -- "va_surface.h", -- "vaapi/vaapi_picture_factory.cc", -- "vaapi/vaapi_picture_factory.h", -+ "vaapi/va_surface.cc", -+ "vaapi/va_surface.h", -+ "vaapi/vaapi_jpeg_decode_accelerator.cc", -+ "vaapi/vaapi_jpeg_decode_accelerator.h", -+ "vaapi/vaapi_jpeg_decoder.cc", -+ "vaapi/vaapi_jpeg_decoder.h", -+ "vaapi/vaapi_jpeg_encode_accelerator.cc", -+ "vaapi/vaapi_jpeg_encode_accelerator.h", -+ "vaapi/vaapi_jpeg_encoder.cc", -+ "vaapi/vaapi_jpeg_encoder.h", - "vaapi/vaapi_picture.cc", - "vaapi/vaapi_picture.h", -- "vaapi_jpeg_decode_accelerator.cc", -- "vaapi_jpeg_decode_accelerator.h", -- "vaapi_jpeg_decoder.cc", -- "vaapi_jpeg_decoder.h", -- "vaapi_jpeg_encode_accelerator.cc", -- "vaapi_jpeg_encode_accelerator.h", -- "vaapi_jpeg_encoder.cc", -- "vaapi_jpeg_encoder.h", -- "vaapi_video_decode_accelerator.cc", -- "vaapi_video_decode_accelerator.h", -- "vaapi_video_encode_accelerator.cc", -- "vaapi_video_encode_accelerator.h", -- "vaapi_wrapper.cc", -- "vaapi_wrapper.h", -+ "vaapi/vaapi_picture_factory.cc", -+ "vaapi/vaapi_picture_factory.h", -+ "vaapi/vaapi_video_decode_accelerator.cc", -+ "vaapi/vaapi_video_decode_accelerator.h", -+ "vaapi/vaapi_video_encode_accelerator.cc", -+ "vaapi/vaapi_video_encode_accelerator.h", -+ "vaapi/vaapi_wrapper.cc", -+ "vaapi/vaapi_wrapper.h", - ] + get_target_outputs(":libva_generate_stubs") - configs += [ "//third_party/libyuv:libyuv_config" ] - deps += [ -@@ -596,7 +596,7 @@ source_set("unit_tests") { - ] - - if (use_vaapi) { -- sources += [ "vaapi_video_decode_accelerator_unittest.cc" ] -+ sources += [ "vaapi/vaapi_video_decode_accelerator_unittest.cc" ] - deps += [ - ":gpu", - "//base/test:test_support", ---- a/media/gpu/gpu_jpeg_decode_accelerator_factory.cc -+++ b/media/gpu/gpu_jpeg_decode_accelerator_factory.cc -@@ -17,7 +17,7 @@ - #endif - - #if BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_jpeg_decode_accelerator.h" -+#include "media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h" - #endif - - #if defined(USE_V4L2_JDA) ---- a/media/gpu/gpu_video_decode_accelerator_factory.cc -+++ b/media/gpu/gpu_video_decode_accelerator_factory.cc -@@ -34,7 +34,7 @@ - #include "media/gpu/android/device_info.h" - #endif - #if BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_video_decode_accelerator.h" -+#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" - #include "ui/gl/gl_implementation.h" - #endif - ---- a/media/gpu/gpu_video_encode_accelerator_factory.cc -+++ b/media/gpu/gpu_video_encode_accelerator_factory.cc -@@ -24,7 +24,7 @@ - #include "media/gpu/media_foundation_video_encode_accelerator_win.h" - #endif - #if BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_video_encode_accelerator.h" -+#include "media/gpu/vaapi/vaapi_video_encode_accelerator.h" - #endif - - namespace media { ---- a/media/gpu/jpeg_decode_accelerator_unittest.cc -+++ b/media/gpu/jpeg_decode_accelerator_unittest.cc -@@ -35,7 +35,7 @@ - #include "ui/gfx/codec/jpeg_codec.h" - - #if BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #endif - - namespace media { ---- a/media/gpu/va_surface.cc -+++ /dev/null -@@ -1,24 +0,0 @@ --// Copyright 2017 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/va_surface.h" -- --namespace media { -- --VASurface::VASurface(VASurfaceID va_surface_id, -- const gfx::Size& size, -- unsigned int format, -- const ReleaseCB& release_cb) -- : va_surface_id_(va_surface_id), -- size_(size), -- format_(format), -- release_cb_(release_cb) { -- DCHECK(!release_cb_.is_null()); --} -- --VASurface::~VASurface() { -- release_cb_.Run(va_surface_id_); --} -- --} // namespace media ---- a/media/gpu/va_surface.h -+++ /dev/null -@@ -1,117 +0,0 @@ --// Copyright 2013 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. --// --// This file contains the definition of VASurface class, used for decoding by --// VaapiVideoDecodeAccelerator and VaapiH264Decoder. -- --#ifndef MEDIA_GPU_VA_SURFACE_H_ --#define MEDIA_GPU_VA_SURFACE_H_ -- --#include -- --#include "base/callback.h" --#include "base/macros.h" --#include "base/memory/ref_counted.h" --#include "media/gpu/media_gpu_export.h" --#include "ui/gfx/geometry/size.h" -- --namespace media { -- --// A VA-API-specific decode surface used by VaapiH264Decoder to decode into --// and use as reference for decoding other surfaces. It is also handed by the --// decoder to VaapiVideoDecodeAccelerator when the contents of the surface are --// ready and should be displayed. VAVDA converts the surface contents into an --// X/Drm Pixmap bound to a texture for display and releases its reference to it. --// Decoder releases its references to the surface when it's done decoding and --// using it as reference. Note that a surface may still be used for reference --// after it's been sent to output and also after it is no longer used by VAVDA. --// Thus, the surface can be in use by both VAVDA and the Decoder at the same --// time, or by either of them, with the restriction that VAVDA will never get --// the surface until the contents are ready, and it is guaranteed that the --// contents will not change after that. --// When both the decoder and VAVDA release their references to the surface, --// it is freed and the release callback is executed to put the surface back --// into available surfaces pool, which is managed externally. --// --// VASurfaceID is allocated in VaapiWrapper. --// | --// +----->| --// | v --// | VASurfaceID is put onto VaapiVideoDecodeAccelerator::available_va_surfaces_ --// | | list. --// | v --// | VASurfaceID is taken off of the VVDA:available_va_surfaces_ when --// | | VaapiH264Decoder requests more output surfaces, is wrapped into --// | | a VASurface and passed to VaapiH264Decoder. --// | v --// | VASurface is put onto VaapiH264Decoder::available_va_surfaces_, keeping --// | | the only reference to it until it's needed for decoding. --// | v --// | VaapiH264Decoder starts decoding a new frame. It takes a VASurface off of --// | | VHD::available_va_surfaces_ and assigns it to a DecodeSurface, --// | | which now keeps the only reference. --// | v --// | DecodeSurface is used for decoding, putting data into associated VASurface. --// | | --// | |--------------------------------------------------+ --// | | | --// | v v --// | DecodeSurface is to be output. VaapiH264Decoder uses the --// | VaapiH264Decoder passes the associated DecodeSurface and associated --// | VASurface to VaapiVideoDecodeAccelerator, VASurface as reference for --// | which stores it (taking a ref) on decoding more frames. --// | pending_output_cbs_ queue until an output | --// | VaapiPicture becomes available. v --// | | Once the DecodeSurface is not --// | | needed as reference anymore, --// | v it is released, releasing the --// | A VaapiPicture becomes available after associated VASurface reference. --// | the client of VVDA returns | --// | a PictureBuffer associated with it. VVDA | --// | puts the contents of the VASurface into | --// | it and releases the reference to VASurface. | --// | | | --// | '---------------------------------------' --// | | --// | v --// | Neither VVDA nor VHD hold a reference to VASurface. VASurface is released, --// | ReleaseCB gets called in its destructor, which puts the associated --// | VASurfaceID back onto VVDA::available_va_surfaces_. --// | | --// '-------------------------------------| --// | --// v --// VaapiWrapper frees VASurfaceID. --// --class MEDIA_GPU_EXPORT VASurface -- : public base::RefCountedThreadSafe { -- public: -- // Provided by user, will be called when all references to the surface -- // are released. -- using ReleaseCB = base::Callback; -- -- VASurface(VASurfaceID va_surface_id, -- const gfx::Size& size, -- unsigned int format, -- const ReleaseCB& release_cb); -- -- VASurfaceID id() const { return va_surface_id_; } -- const gfx::Size& size() const { return size_; } -- unsigned int format() const { return format_; } -- -- private: -- friend class base::RefCountedThreadSafe; -- ~VASurface(); -- -- const VASurfaceID va_surface_id_; -- const gfx::Size size_; -- const unsigned int format_; -- const ReleaseCB release_cb_; -- -- DISALLOW_COPY_AND_ASSIGN(VASurface); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VA_SURFACE_H_ ---- /dev/null -+++ b/media/gpu/vaapi/va_surface.cc -@@ -0,0 +1,24 @@ -+// Copyright 2017 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/va_surface.h" -+ -+namespace media { -+ -+VASurface::VASurface(VASurfaceID va_surface_id, -+ const gfx::Size& size, -+ unsigned int format, -+ const ReleaseCB& release_cb) -+ : va_surface_id_(va_surface_id), -+ size_(size), -+ format_(format), -+ release_cb_(release_cb) { -+ DCHECK(!release_cb_.is_null()); -+} -+ -+VASurface::~VASurface() { -+ release_cb_.Run(va_surface_id_); -+} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/va_surface.h -@@ -0,0 +1,117 @@ -+// Copyright 2013 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+// -+// This file contains the definition of VASurface class, used for decoding by -+// VaapiVideoDecodeAccelerator and VaapiH264Decoder. -+ -+#ifndef MEDIA_GPU_VAAPI_VA_SURFACE_H_ -+#define MEDIA_GPU_VAAPI_VA_SURFACE_H_ -+ -+#include -+ -+#include "base/callback.h" -+#include "base/macros.h" -+#include "base/memory/ref_counted.h" -+#include "media/gpu/media_gpu_export.h" -+#include "ui/gfx/geometry/size.h" -+ -+namespace media { -+ -+// A VA-API-specific decode surface used by VaapiH264Decoder to decode into -+// and use as reference for decoding other surfaces. It is also handed by the -+// decoder to VaapiVideoDecodeAccelerator when the contents of the surface are -+// ready and should be displayed. VAVDA converts the surface contents into an -+// X/Drm Pixmap bound to a texture for display and releases its reference to it. -+// Decoder releases its references to the surface when it's done decoding and -+// using it as reference. Note that a surface may still be used for reference -+// after it's been sent to output and also after it is no longer used by VAVDA. -+// Thus, the surface can be in use by both VAVDA and the Decoder at the same -+// time, or by either of them, with the restriction that VAVDA will never get -+// the surface until the contents are ready, and it is guaranteed that the -+// contents will not change after that. -+// When both the decoder and VAVDA release their references to the surface, -+// it is freed and the release callback is executed to put the surface back -+// into available surfaces pool, which is managed externally. -+// -+// VASurfaceID is allocated in VaapiWrapper. -+// | -+// +----->| -+// | v -+// | VASurfaceID is put onto VaapiVideoDecodeAccelerator::available_va_surfaces_ -+// | | list. -+// | v -+// | VASurfaceID is taken off of the VVDA:available_va_surfaces_ when -+// | | VaapiH264Decoder requests more output surfaces, is wrapped into -+// | | a VASurface and passed to VaapiH264Decoder. -+// | v -+// | VASurface is put onto VaapiH264Decoder::available_va_surfaces_, keeping -+// | | the only reference to it until it's needed for decoding. -+// | v -+// | VaapiH264Decoder starts decoding a new frame. It takes a VASurface off of -+// | | VHD::available_va_surfaces_ and assigns it to a DecodeSurface, -+// | | which now keeps the only reference. -+// | v -+// | DecodeSurface is used for decoding, putting data into associated VASurface. -+// | | -+// | |--------------------------------------------------+ -+// | | | -+// | v v -+// | DecodeSurface is to be output. VaapiH264Decoder uses the -+// | VaapiH264Decoder passes the associated DecodeSurface and associated -+// | VASurface to VaapiVideoDecodeAccelerator, VASurface as reference for -+// | which stores it (taking a ref) on decoding more frames. -+// | pending_output_cbs_ queue until an output | -+// | VaapiPicture becomes available. v -+// | | Once the DecodeSurface is not -+// | | needed as reference anymore, -+// | v it is released, releasing the -+// | A VaapiPicture becomes available after associated VASurface reference. -+// | the client of VVDA returns | -+// | a PictureBuffer associated with it. VVDA | -+// | puts the contents of the VASurface into | -+// | it and releases the reference to VASurface. | -+// | | | -+// | '---------------------------------------' -+// | | -+// | v -+// | Neither VVDA nor VHD hold a reference to VASurface. VASurface is released, -+// | ReleaseCB gets called in its destructor, which puts the associated -+// | VASurfaceID back onto VVDA::available_va_surfaces_. -+// | | -+// '-------------------------------------| -+// | -+// v -+// VaapiWrapper frees VASurfaceID. -+// -+class MEDIA_GPU_EXPORT VASurface -+ : public base::RefCountedThreadSafe { -+ public: -+ // Provided by user, will be called when all references to the surface -+ // are released. -+ using ReleaseCB = base::Callback; -+ -+ VASurface(VASurfaceID va_surface_id, -+ const gfx::Size& size, -+ unsigned int format, -+ const ReleaseCB& release_cb); -+ -+ VASurfaceID id() const { return va_surface_id_; } -+ const gfx::Size& size() const { return size_; } -+ unsigned int format() const { return format_; } -+ -+ private: -+ friend class base::RefCountedThreadSafe; -+ ~VASurface(); -+ -+ const VASurfaceID va_surface_id_; -+ const gfx::Size size_; -+ const unsigned int format_; -+ const ReleaseCB release_cb_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VASurface); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VA_SURFACE_H_ ---- a/media/gpu/vaapi/vaapi_drm_picture.cc -+++ b/media/gpu/vaapi/vaapi_drm_picture.cc -@@ -5,8 +5,8 @@ - #include "media/gpu/vaapi/vaapi_drm_picture.h" - - #include "base/file_descriptor_posix.h" --#include "media/gpu/va_surface.h" --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/va_surface.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #include "ui/gfx/gpu_memory_buffer.h" - #include "ui/gfx/native_pixmap.h" - #include "ui/gl/gl_bindings.h" ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.cc -@@ -0,0 +1,325 @@ -+// Copyright 2015 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h" -+ -+#include -+#include -+ -+#include -+#include -+ -+#include "base/bind.h" -+#include "base/logging.h" -+#include "base/metrics/histogram_macros.h" -+#include "base/threading/thread_task_runner_handle.h" -+#include "base/trace_event/trace_event.h" -+#include "gpu/ipc/service/gpu_channel.h" -+#include "media/base/video_frame.h" -+#include "media/filters/jpeg_parser.h" -+#include "media/gpu/shared_memory_region.h" -+#include "media/gpu/vaapi/vaapi_picture.h" -+#include "third_party/libyuv/include/libyuv.h" -+ -+#define VLOGF(level) VLOG(level) << __func__ << "(): " -+#define DVLOGF(level) DVLOG(level) << __func__ << "(): " -+ -+namespace media { -+ -+namespace { -+// UMA errors that the VaapiJpegDecodeAccelerator class reports. -+enum VAJDADecoderFailure { -+ VAAPI_ERROR = 0, -+ VAJDA_DECODER_FAILURES_MAX, -+}; -+ -+static void ReportToUMA(VAJDADecoderFailure failure) { -+ UMA_HISTOGRAM_ENUMERATION("Media.VAJDA.DecoderFailure", failure, -+ VAJDA_DECODER_FAILURES_MAX + 1); -+} -+ -+static unsigned int VaSurfaceFormatForJpeg( -+ const JpegFrameHeader& frame_header) { -+ // The range of sampling factor is [1, 4]. Pack them into integer to make the -+ // matching code simpler. For example, 0x211 means the sampling factor are 2, -+ // 1, 1 for 3 components. -+ unsigned int h = 0, v = 0; -+ for (int i = 0; i < frame_header.num_components; i++) { -+ DCHECK_LE(frame_header.components[i].horizontal_sampling_factor, 4); -+ DCHECK_LE(frame_header.components[i].vertical_sampling_factor, 4); -+ h = h << 4 | frame_header.components[i].horizontal_sampling_factor; -+ v = v << 4 | frame_header.components[i].vertical_sampling_factor; -+ } -+ -+ switch (frame_header.num_components) { -+ case 1: // Grey image -+ return VA_RT_FORMAT_YUV400; -+ -+ case 3: // Y Cb Cr color image -+ // See https://en.wikipedia.org/wiki/Chroma_subsampling for the -+ // definition of these numbers. -+ if (h == 0x211 && v == 0x211) -+ return VA_RT_FORMAT_YUV420; -+ -+ if (h == 0x211 && v == 0x111) -+ return VA_RT_FORMAT_YUV422; -+ -+ if (h == 0x111 && v == 0x111) -+ return VA_RT_FORMAT_YUV444; -+ -+ if (h == 0x411 && v == 0x111) -+ return VA_RT_FORMAT_YUV411; -+ } -+ VLOGF(1) << "Unsupported sampling factor: num_components=" -+ << frame_header.num_components << ", h=" << std::hex << h -+ << ", v=" << v; -+ -+ return 0; -+} -+ -+} // namespace -+ -+VaapiJpegDecodeAccelerator::DecodeRequest::DecodeRequest( -+ int32_t bitstream_buffer_id, -+ std::unique_ptr shm, -+ const scoped_refptr& video_frame) -+ : bitstream_buffer_id(bitstream_buffer_id), -+ shm(std::move(shm)), -+ video_frame(video_frame) {} -+ -+VaapiJpegDecodeAccelerator::DecodeRequest::~DecodeRequest() {} -+ -+void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id, -+ Error error) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ VLOGF(1) << "Notifying of error " << error; -+ DCHECK(client_); -+ client_->NotifyError(bitstream_buffer_id, error); -+} -+ -+void VaapiJpegDecodeAccelerator::NotifyErrorFromDecoderThread( -+ int32_t bitstream_buffer_id, -+ Error error) { -+ DCHECK(decoder_task_runner_->BelongsToCurrentThread()); -+ task_runner_->PostTask(FROM_HERE, -+ base::Bind(&VaapiJpegDecodeAccelerator::NotifyError, -+ weak_this_, bitstream_buffer_id, error)); -+} -+ -+void VaapiJpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ client_->VideoFrameReady(bitstream_buffer_id); -+} -+ -+VaapiJpegDecodeAccelerator::VaapiJpegDecodeAccelerator( -+ const scoped_refptr& io_task_runner) -+ : task_runner_(base::ThreadTaskRunnerHandle::Get()), -+ io_task_runner_(io_task_runner), -+ decoder_thread_("VaapiJpegDecoderThread"), -+ va_surface_id_(VA_INVALID_SURFACE), -+ weak_this_factory_(this) { -+ weak_this_ = weak_this_factory_.GetWeakPtr(); -+} -+ -+VaapiJpegDecodeAccelerator::~VaapiJpegDecodeAccelerator() { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ VLOGF(2) << "Destroying VaapiJpegDecodeAccelerator"; -+ -+ weak_this_factory_.InvalidateWeakPtrs(); -+ decoder_thread_.Stop(); -+} -+ -+bool VaapiJpegDecodeAccelerator::Initialize(Client* client) { -+ VLOGF(2); -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ client_ = client; -+ -+ vaapi_wrapper_ = -+ VaapiWrapper::Create(VaapiWrapper::kDecode, VAProfileJPEGBaseline, -+ base::Bind(&ReportToUMA, VAAPI_ERROR)); -+ -+ if (!vaapi_wrapper_.get()) { -+ VLOGF(1) << "Failed initializing VAAPI"; -+ return false; -+ } -+ -+ if (!decoder_thread_.Start()) { -+ VLOGF(1) << "Failed to start decoding thread."; -+ return false; -+ } -+ decoder_task_runner_ = decoder_thread_.task_runner(); -+ -+ return true; -+} -+ -+bool VaapiJpegDecodeAccelerator::OutputPicture( -+ VASurfaceID va_surface_id, -+ int32_t input_buffer_id, -+ const scoped_refptr& video_frame) { -+ DCHECK(decoder_task_runner_->BelongsToCurrentThread()); -+ -+ TRACE_EVENT1("jpeg", "VaapiJpegDecodeAccelerator::OutputPicture", -+ "input_buffer_id", input_buffer_id); -+ -+ DVLOGF(4) << "Outputting VASurface " << va_surface_id -+ << " into video_frame associated with input buffer id " -+ << input_buffer_id; -+ -+ VAImage image; -+ VAImageFormat format; -+ const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); -+ memset(&image, 0, sizeof(image)); -+ memset(&format, 0, sizeof(format)); -+ format.fourcc = kI420Fourcc; -+ format.byte_order = VA_LSB_FIRST; -+ format.bits_per_pixel = 12; // 12 for I420 -+ -+ uint8_t* mem = nullptr; -+ gfx::Size coded_size = video_frame->coded_size(); -+ if (!vaapi_wrapper_->GetVaImage(va_surface_id, &format, coded_size, &image, -+ reinterpret_cast(&mem))) { -+ VLOGF(1) << "Cannot get VAImage"; -+ return false; -+ } -+ -+ // Copy image content from VAImage to VideoFrame. -+ // The component order of VAImage I420 are Y, U, and V. -+ DCHECK_EQ(image.num_planes, 3u); -+ DCHECK_GE(image.width, coded_size.width()); -+ DCHECK_GE(image.height, coded_size.height()); -+ const uint8_t* src_y = mem + image.offsets[0]; -+ const uint8_t* src_u = mem + image.offsets[1]; -+ const uint8_t* src_v = mem + image.offsets[2]; -+ size_t src_y_stride = image.pitches[0]; -+ size_t src_u_stride = image.pitches[1]; -+ size_t src_v_stride = image.pitches[2]; -+ uint8_t* dst_y = video_frame->data(VideoFrame::kYPlane); -+ uint8_t* dst_u = video_frame->data(VideoFrame::kUPlane); -+ uint8_t* dst_v = video_frame->data(VideoFrame::kVPlane); -+ size_t dst_y_stride = video_frame->stride(VideoFrame::kYPlane); -+ size_t dst_u_stride = video_frame->stride(VideoFrame::kUPlane); -+ size_t dst_v_stride = video_frame->stride(VideoFrame::kVPlane); -+ -+ if (libyuv::I420Copy(src_y, src_y_stride, // Y -+ src_u, src_u_stride, // U -+ src_v, src_v_stride, // V -+ dst_y, dst_y_stride, // Y -+ dst_u, dst_u_stride, // U -+ dst_v, dst_v_stride, // V -+ coded_size.width(), coded_size.height())) { -+ VLOGF(1) << "I420Copy failed"; -+ return false; -+ } -+ -+ vaapi_wrapper_->ReturnVaImage(&image); -+ -+ task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::VideoFrameReady, -+ weak_this_, input_buffer_id)); -+ -+ return true; -+} -+ -+void VaapiJpegDecodeAccelerator::DecodeTask( -+ const std::unique_ptr& request) { -+ DVLOGF(4); -+ DCHECK(decoder_task_runner_->BelongsToCurrentThread()); -+ TRACE_EVENT0("jpeg", "DecodeTask"); -+ -+ JpegParseResult parse_result; -+ if (!ParseJpegPicture( -+ reinterpret_cast(request->shm->memory()), -+ request->shm->size(), &parse_result)) { -+ VLOGF(1) << "ParseJpegPicture failed"; -+ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -+ PARSE_JPEG_FAILED); -+ return; -+ } -+ -+ unsigned int new_va_rt_format = -+ VaSurfaceFormatForJpeg(parse_result.frame_header); -+ if (!new_va_rt_format) { -+ VLOGF(1) << "Unsupported subsampling"; -+ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -+ UNSUPPORTED_JPEG); -+ return; -+ } -+ -+ // Reuse VASurface if size doesn't change. -+ gfx::Size new_coded_size(parse_result.frame_header.coded_width, -+ parse_result.frame_header.coded_height); -+ if (new_coded_size != coded_size_ || va_surface_id_ == VA_INVALID_SURFACE || -+ new_va_rt_format != va_rt_format_) { -+ vaapi_wrapper_->DestroySurfaces(); -+ va_surface_id_ = VA_INVALID_SURFACE; -+ va_rt_format_ = new_va_rt_format; -+ -+ std::vector va_surfaces; -+ if (!vaapi_wrapper_->CreateSurfaces(va_rt_format_, new_coded_size, 1, -+ &va_surfaces)) { -+ VLOGF(1) << "Create VA surface failed"; -+ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -+ PLATFORM_FAILURE); -+ return; -+ } -+ va_surface_id_ = va_surfaces[0]; -+ coded_size_ = new_coded_size; -+ } -+ -+ if (!VaapiJpegDecoder::Decode(vaapi_wrapper_.get(), parse_result, -+ va_surface_id_)) { -+ VLOGF(1) << "Decode JPEG failed"; -+ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -+ PLATFORM_FAILURE); -+ return; -+ } -+ -+ if (!OutputPicture(va_surface_id_, request->bitstream_buffer_id, -+ request->video_frame)) { -+ VLOGF(1) << "Output picture failed"; -+ NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -+ PLATFORM_FAILURE); -+ return; -+ } -+} -+ -+void VaapiJpegDecodeAccelerator::Decode( -+ const BitstreamBuffer& bitstream_buffer, -+ const scoped_refptr& video_frame) { -+ DCHECK(io_task_runner_->BelongsToCurrentThread()); -+ TRACE_EVENT1("jpeg", "Decode", "input_id", bitstream_buffer.id()); -+ -+ DVLOGF(4) << "Mapping new input buffer id: " << bitstream_buffer.id() -+ << " size: " << bitstream_buffer.size(); -+ -+ // SharedMemoryRegion will take over the |bitstream_buffer.handle()|. -+ std::unique_ptr shm( -+ new SharedMemoryRegion(bitstream_buffer, true)); -+ -+ if (bitstream_buffer.id() < 0) { -+ VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); -+ NotifyErrorFromDecoderThread(bitstream_buffer.id(), INVALID_ARGUMENT); -+ return; -+ } -+ -+ if (!shm->Map()) { -+ VLOGF(1) << "Failed to map input buffer"; -+ NotifyErrorFromDecoderThread(bitstream_buffer.id(), UNREADABLE_INPUT); -+ return; -+ } -+ -+ std::unique_ptr request( -+ new DecodeRequest(bitstream_buffer.id(), std::move(shm), video_frame)); -+ -+ decoder_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::DecodeTask, -+ base::Unretained(this), base::Passed(&request))); -+} -+ -+bool VaapiJpegDecodeAccelerator::IsSupported() { -+ return VaapiWrapper::IsJpegDecodeSupported(); -+} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_decode_accelerator.h -@@ -0,0 +1,121 @@ -+// Copyright 2015 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODE_ACCELERATOR_H_ -+#define MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODE_ACCELERATOR_H_ -+ -+#include -+ -+#include -+ -+#include "base/macros.h" -+#include "base/memory/linked_ptr.h" -+#include "base/memory/weak_ptr.h" -+#include "base/single_thread_task_runner.h" -+#include "base/synchronization/lock.h" -+#include "base/threading/thread.h" -+#include "media/base/bitstream_buffer.h" -+#include "media/gpu/media_gpu_export.h" -+#include "media/gpu/shared_memory_region.h" -+#include "media/gpu/vaapi/vaapi_jpeg_decoder.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+#include "media/video/jpeg_decode_accelerator.h" -+ -+namespace media { -+ -+// Class to provide JPEG decode acceleration for Intel systems with hardware -+// support for it, and on which libva is available. -+// Decoding tasks are performed in a separate decoding thread. -+// -+// Threading/life-cycle: this object is created & destroyed on the GPU -+// ChildThread. A few methods on it are called on the decoder thread which is -+// stopped during |this->Destroy()|, so any tasks posted to the decoder thread -+// can assume |*this| is still alive. See |weak_this_| below for more details. -+class MEDIA_GPU_EXPORT VaapiJpegDecodeAccelerator -+ : public JpegDecodeAccelerator { -+ public: -+ VaapiJpegDecodeAccelerator( -+ const scoped_refptr& io_task_runner); -+ ~VaapiJpegDecodeAccelerator() override; -+ -+ // JpegDecodeAccelerator implementation. -+ bool Initialize(JpegDecodeAccelerator::Client* client) override; -+ void Decode(const BitstreamBuffer& bitstream_buffer, -+ const scoped_refptr& video_frame) override; -+ bool IsSupported() override; -+ -+ private: -+ // An input buffer and the corresponding output video frame awaiting -+ // consumption, provided by the client. -+ struct DecodeRequest { -+ DecodeRequest(int32_t bitstream_buffer_id, -+ std::unique_ptr shm, -+ const scoped_refptr& video_frame); -+ ~DecodeRequest(); -+ -+ int32_t bitstream_buffer_id; -+ std::unique_ptr shm; -+ scoped_refptr video_frame; -+ }; -+ -+ // Notifies the client that an error has occurred and decoding cannot -+ // continue. -+ void NotifyError(int32_t bitstream_buffer_id, Error error); -+ void NotifyErrorFromDecoderThread(int32_t bitstream_buffer_id, Error error); -+ void VideoFrameReady(int32_t bitstream_buffer_id); -+ -+ // Processes one decode |request|. -+ void DecodeTask(const std::unique_ptr& request); -+ -+ // Puts contents of |va_surface| into given |video_frame|, releases the -+ // surface and passes the |input_buffer_id| of the resulting picture to -+ // client for output. -+ bool OutputPicture(VASurfaceID va_surface_id, -+ int32_t input_buffer_id, -+ const scoped_refptr& video_frame); -+ -+ // ChildThread's task runner. -+ scoped_refptr task_runner_; -+ -+ // GPU IO task runner. -+ scoped_refptr io_task_runner_; -+ -+ // The client of this class. -+ Client* client_; -+ -+ // WeakPtr<> pointing to |this| for use in posting tasks from the decoder -+ // thread back to the ChildThread. Because the decoder thread is a member of -+ // this class, any task running on the decoder thread is guaranteed that this -+ // object is still alive. As a result, tasks posted from ChildThread to -+ // decoder thread should use base::Unretained(this), and tasks posted from the -+ // decoder thread to the ChildThread should use |weak_this_|. -+ base::WeakPtr weak_this_; -+ -+ scoped_refptr vaapi_wrapper_; -+ -+ // Comes after vaapi_wrapper_ to ensure its destructor is executed before -+ // |vaapi_wrapper_| is destroyed. -+ std::unique_ptr decoder_; -+ base::Thread decoder_thread_; -+ // Use this to post tasks to |decoder_thread_| instead of -+ // |decoder_thread_.task_runner()| because the latter will be NULL once -+ // |decoder_thread_.Stop()| returns. -+ scoped_refptr decoder_task_runner_; -+ -+ // The current VA surface for decoding. -+ VASurfaceID va_surface_id_; -+ // The coded size associated with |va_surface_id_|. -+ gfx::Size coded_size_; -+ // The VA RT format associated with |va_surface_id_|. -+ unsigned int va_rt_format_; -+ -+ // The WeakPtrFactory for |weak_this_|. -+ base::WeakPtrFactory weak_this_factory_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecodeAccelerator); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODE_ACCELERATOR_H_ ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_decoder.cc -@@ -0,0 +1,229 @@ -+// Copyright 2015 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_jpeg_decoder.h" -+ -+#include -+#include -+ -+#include "base/logging.h" -+#include "base/macros.h" -+#include "media/filters/jpeg_parser.h" -+ -+namespace media { -+ -+// VAAPI only support subset of JPEG profiles. This function determines a given -+// parsed JPEG result is supported or not. -+static bool IsVaapiSupportedJpeg(const JpegParseResult& jpeg) { -+ if (jpeg.frame_header.visible_width < 1 || -+ jpeg.frame_header.visible_height < 1) { -+ DLOG(ERROR) << "width(" << jpeg.frame_header.visible_width -+ << ") and height(" << jpeg.frame_header.visible_height -+ << ") should be at least 1"; -+ return false; -+ } -+ -+ // Size 64k*64k is the maximum in the JPEG standard. VAAPI doesn't support -+ // resolutions larger than 16k*16k. -+ const int kMaxDimension = 16384; -+ if (jpeg.frame_header.coded_width > kMaxDimension || -+ jpeg.frame_header.coded_height > kMaxDimension) { -+ DLOG(ERROR) << "VAAPI doesn't support size(" -+ << jpeg.frame_header.coded_width << "*" -+ << jpeg.frame_header.coded_height << ") larger than " -+ << kMaxDimension << "*" << kMaxDimension; -+ return false; -+ } -+ -+ if (jpeg.frame_header.num_components != 3) { -+ DLOG(ERROR) << "VAAPI doesn't support num_components(" -+ << static_cast(jpeg.frame_header.num_components) -+ << ") != 3"; -+ return false; -+ } -+ -+ if (jpeg.frame_header.components[0].horizontal_sampling_factor < -+ jpeg.frame_header.components[1].horizontal_sampling_factor || -+ jpeg.frame_header.components[0].horizontal_sampling_factor < -+ jpeg.frame_header.components[2].horizontal_sampling_factor) { -+ DLOG(ERROR) << "VAAPI doesn't supports horizontal sampling factor of Y" -+ << " smaller than Cb and Cr"; -+ return false; -+ } -+ -+ if (jpeg.frame_header.components[0].vertical_sampling_factor < -+ jpeg.frame_header.components[1].vertical_sampling_factor || -+ jpeg.frame_header.components[0].vertical_sampling_factor < -+ jpeg.frame_header.components[2].vertical_sampling_factor) { -+ DLOG(ERROR) << "VAAPI doesn't supports vertical sampling factor of Y" -+ << " smaller than Cb and Cr"; -+ return false; -+ } -+ -+ return true; -+} -+ -+static void FillPictureParameters( -+ const JpegFrameHeader& frame_header, -+ VAPictureParameterBufferJPEGBaseline* pic_param) { -+ memset(pic_param, 0, sizeof(*pic_param)); -+ pic_param->picture_width = frame_header.coded_width; -+ pic_param->picture_height = frame_header.coded_height; -+ pic_param->num_components = frame_header.num_components; -+ -+ for (int i = 0; i < pic_param->num_components; i++) { -+ pic_param->components[i].component_id = frame_header.components[i].id; -+ pic_param->components[i].h_sampling_factor = -+ frame_header.components[i].horizontal_sampling_factor; -+ pic_param->components[i].v_sampling_factor = -+ frame_header.components[i].vertical_sampling_factor; -+ pic_param->components[i].quantiser_table_selector = -+ frame_header.components[i].quantization_table_selector; -+ } -+} -+ -+static void FillIQMatrix(const JpegQuantizationTable* q_table, -+ VAIQMatrixBufferJPEGBaseline* iq_matrix) { -+ memset(iq_matrix, 0, sizeof(*iq_matrix)); -+ static_assert(kJpegMaxQuantizationTableNum == -+ arraysize(iq_matrix->load_quantiser_table), -+ "max number of quantization table mismatched"); -+ for (size_t i = 0; i < kJpegMaxQuantizationTableNum; i++) { -+ if (!q_table[i].valid) -+ continue; -+ iq_matrix->load_quantiser_table[i] = 1; -+ static_assert( -+ arraysize(iq_matrix->quantiser_table[i]) == arraysize(q_table[i].value), -+ "number of quantization entries mismatched"); -+ for (size_t j = 0; j < arraysize(q_table[i].value); j++) -+ iq_matrix->quantiser_table[i][j] = q_table[i].value[j]; -+ } -+} -+ -+static void FillHuffmanTable(const JpegHuffmanTable* dc_table, -+ const JpegHuffmanTable* ac_table, -+ VAHuffmanTableBufferJPEGBaseline* huffman_table) { -+ memset(huffman_table, 0, sizeof(*huffman_table)); -+ // Use default huffman tables if not specified in header. -+ bool has_huffman_table = false; -+ for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { -+ if (dc_table[i].valid || ac_table[i].valid) { -+ has_huffman_table = true; -+ break; -+ } -+ } -+ if (!has_huffman_table) { -+ dc_table = kDefaultDcTable; -+ ac_table = kDefaultAcTable; -+ } -+ -+ static_assert(kJpegMaxHuffmanTableNumBaseline == -+ arraysize(huffman_table->load_huffman_table), -+ "max number of huffman table mismatched"); -+ static_assert(sizeof(huffman_table->huffman_table[0].num_dc_codes) == -+ sizeof(dc_table[0].code_length), -+ "size of huffman table code length mismatch"); -+ static_assert(sizeof(huffman_table->huffman_table[0].dc_values[0]) == -+ sizeof(dc_table[0].code_value[0]), -+ "size of huffman table code value mismatch"); -+ for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { -+ if (!dc_table[i].valid || !ac_table[i].valid) -+ continue; -+ huffman_table->load_huffman_table[i] = 1; -+ -+ memcpy(huffman_table->huffman_table[i].num_dc_codes, -+ dc_table[i].code_length, -+ sizeof(huffman_table->huffman_table[i].num_dc_codes)); -+ memcpy(huffman_table->huffman_table[i].dc_values, dc_table[i].code_value, -+ sizeof(huffman_table->huffman_table[i].dc_values)); -+ memcpy(huffman_table->huffman_table[i].num_ac_codes, -+ ac_table[i].code_length, -+ sizeof(huffman_table->huffman_table[i].num_ac_codes)); -+ memcpy(huffman_table->huffman_table[i].ac_values, ac_table[i].code_value, -+ sizeof(huffman_table->huffman_table[i].ac_values)); -+ } -+} -+ -+static void FillSliceParameters( -+ const JpegParseResult& parse_result, -+ VASliceParameterBufferJPEGBaseline* slice_param) { -+ memset(slice_param, 0, sizeof(*slice_param)); -+ slice_param->slice_data_size = parse_result.data_size; -+ slice_param->slice_data_offset = 0; -+ slice_param->slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -+ slice_param->slice_horizontal_position = 0; -+ slice_param->slice_vertical_position = 0; -+ slice_param->num_components = parse_result.scan.num_components; -+ for (int i = 0; i < slice_param->num_components; i++) { -+ slice_param->components[i].component_selector = -+ parse_result.scan.components[i].component_selector; -+ slice_param->components[i].dc_table_selector = -+ parse_result.scan.components[i].dc_selector; -+ slice_param->components[i].ac_table_selector = -+ parse_result.scan.components[i].ac_selector; -+ } -+ slice_param->restart_interval = parse_result.restart_interval; -+ -+ // Cast to int to prevent overflow. -+ int max_h_factor = -+ parse_result.frame_header.components[0].horizontal_sampling_factor; -+ int max_v_factor = -+ parse_result.frame_header.components[0].vertical_sampling_factor; -+ int mcu_cols = parse_result.frame_header.coded_width / (max_h_factor * 8); -+ DCHECK_GT(mcu_cols, 0); -+ int mcu_rows = parse_result.frame_header.coded_height / (max_v_factor * 8); -+ DCHECK_GT(mcu_rows, 0); -+ slice_param->num_mcus = mcu_rows * mcu_cols; -+} -+ -+// static -+bool VaapiJpegDecoder::Decode(VaapiWrapper* vaapi_wrapper, -+ const JpegParseResult& parse_result, -+ VASurfaceID va_surface) { -+ DCHECK_NE(va_surface, VA_INVALID_SURFACE); -+ if (!IsVaapiSupportedJpeg(parse_result)) -+ return false; -+ -+ // Set picture parameters. -+ VAPictureParameterBufferJPEGBaseline pic_param; -+ FillPictureParameters(parse_result.frame_header, &pic_param); -+ if (!vaapi_wrapper->SubmitBuffer(VAPictureParameterBufferType, -+ sizeof(pic_param), &pic_param)) -+ return false; -+ -+ // Set quantization table. -+ VAIQMatrixBufferJPEGBaseline iq_matrix; -+ FillIQMatrix(parse_result.q_table, &iq_matrix); -+ if (!vaapi_wrapper->SubmitBuffer(VAIQMatrixBufferType, sizeof(iq_matrix), -+ &iq_matrix)) -+ return false; -+ -+ // Set huffman table. -+ VAHuffmanTableBufferJPEGBaseline huffman_table; -+ FillHuffmanTable(parse_result.dc_table, parse_result.ac_table, -+ &huffman_table); -+ if (!vaapi_wrapper->SubmitBuffer(VAHuffmanTableBufferType, -+ sizeof(huffman_table), &huffman_table)) -+ return false; -+ -+ // Set slice parameters. -+ VASliceParameterBufferJPEGBaseline slice_param; -+ FillSliceParameters(parse_result, &slice_param); -+ if (!vaapi_wrapper->SubmitBuffer(VASliceParameterBufferType, -+ sizeof(slice_param), &slice_param)) -+ return false; -+ -+ // Set scan data. -+ if (!vaapi_wrapper->SubmitBuffer(VASliceDataBufferType, -+ parse_result.data_size, -+ const_cast(parse_result.data))) -+ return false; -+ -+ if (!vaapi_wrapper->ExecuteAndDestroyPendingBuffers(va_surface)) -+ return false; -+ -+ return true; -+} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_decoder.h -@@ -0,0 +1,43 @@ -+// Copyright 2015 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODER_H_ -+#define MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODER_H_ -+ -+#include "base/macros.h" -+#include "media/gpu/media_gpu_export.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+ -+namespace media { -+ -+struct JpegParseResult; -+ -+// A JPEG decoder that utilizes VA-API hardware video decode acceleration on -+// Intel systems. Provides functionality to allow plugging VAAPI HW -+// acceleration into the JpegDecodeAccelerator framework. -+// -+// Clients of this class are expected to manage VA surfaces created via -+// VaapiWrapper, parse JPEG picture via ParseJpegPicture, and then pass -+// them to this class. -+class MEDIA_GPU_EXPORT VaapiJpegDecoder { -+ public: -+ // Decode a JPEG picture. It will fill VA-API parameters and call -+ // corresponding VA-API methods according to parsed JPEG result -+ // |parse_result|. Decoded data will be outputted to the given |va_surface|. -+ // Return false on failure. -+ // |vaapi_wrapper| should be initialized in kDecode mode with -+ // VAProfileJPEGBaseline profile. -+ // |va_surface| should be created with size at least as large as the picture -+ // size. -+ static bool Decode(VaapiWrapper* vaapi_wrapper, -+ const JpegParseResult& parse_result, -+ VASurfaceID va_surface); -+ -+ private: -+ DISALLOW_IMPLICIT_CONSTRUCTORS(VaapiJpegDecoder); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_DECODER_H_ ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc -@@ -0,0 +1,139 @@ -+// Copyright 2015 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include -+#include -+ -+#include -+ -+// This has to be included first. -+// See http://code.google.com/p/googletest/issues/detail?id=371 -+#include "testing/gtest/include/gtest/gtest.h" -+ -+#include "base/at_exit.h" -+#include "base/bind.h" -+#include "base/files/file_util.h" -+#include "base/logging.h" -+#include "base/md5.h" -+#include "base/path_service.h" -+#include "base/strings/string_piece.h" -+#include "media/base/test_data_util.h" -+#include "media/base/video_frame.h" -+#include "media/filters/jpeg_parser.h" -+#include "media/gpu/vaapi/vaapi_jpeg_decoder.h" -+ -+namespace media { -+namespace { -+ -+const char* kTestFilename = "pixel-1280x720.jpg"; -+const char* kExpectedMd5Sum = "6e9e1716073c9a9a1282e3f0e0dab743"; -+ -+void LogOnError() { -+ LOG(FATAL) << "Oh noes! Decoder failed"; -+} -+ -+class VaapiJpegDecoderTest : public ::testing::Test { -+ protected: -+ VaapiJpegDecoderTest() {} -+ -+ void SetUp() override { -+ base::Closure report_error_cb = base::Bind(&LogOnError); -+ wrapper_ = VaapiWrapper::Create(VaapiWrapper::kDecode, -+ VAProfileJPEGBaseline, report_error_cb); -+ ASSERT_TRUE(wrapper_); -+ -+ base::FilePath input_file = GetTestDataFilePath(kTestFilename); -+ -+ ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data_)) -+ << "failed to read input data from " << input_file.value(); -+ } -+ -+ void TearDown() override { wrapper_ = nullptr; } -+ -+ bool VerifyDecode(const JpegParseResult& parse_result, -+ const std::string& md5sum); -+ -+ protected: -+ scoped_refptr wrapper_; -+ std::string jpeg_data_; -+}; -+ -+bool VaapiJpegDecoderTest::VerifyDecode(const JpegParseResult& parse_result, -+ const std::string& expected_md5sum) { -+ gfx::Size size(parse_result.frame_header.coded_width, -+ parse_result.frame_header.coded_height); -+ -+ std::vector va_surfaces; -+ if (!wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)) -+ return false; -+ -+ if (!VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])) { -+ LOG(ERROR) << "Decode failed"; -+ return false; -+ } -+ -+ VAImage image; -+ VAImageFormat format; -+ const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); -+ memset(&image, 0, sizeof(image)); -+ memset(&format, 0, sizeof(format)); -+ format.fourcc = kI420Fourcc; -+ format.byte_order = VA_LSB_FIRST; -+ format.bits_per_pixel = 12; // 12 for I420 -+ -+ void* mem; -+ if (!wrapper_->GetVaImage(va_surfaces[0], &format, size, &image, &mem)) { -+ LOG(ERROR) << "Cannot get VAImage"; -+ return false; -+ } -+ EXPECT_EQ(kI420Fourcc, image.format.fourcc); -+ -+ base::StringPiece result(reinterpret_cast(mem), -+ VideoFrame::AllocationSize(PIXEL_FORMAT_I420, size)); -+ EXPECT_EQ(expected_md5sum, base::MD5String(result)); -+ -+ wrapper_->ReturnVaImage(&image); -+ -+ return true; -+} -+ -+TEST_F(VaapiJpegDecoderTest, DecodeSuccess) { -+ JpegParseResult parse_result; -+ ASSERT_TRUE( -+ ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), -+ jpeg_data_.size(), &parse_result)); -+ -+ EXPECT_TRUE(VerifyDecode(parse_result, kExpectedMd5Sum)); -+} -+ -+TEST_F(VaapiJpegDecoderTest, DecodeFail) { -+ JpegParseResult parse_result; -+ ASSERT_TRUE( -+ ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), -+ jpeg_data_.size(), &parse_result)); -+ -+ // Not supported by VAAPI. -+ parse_result.frame_header.num_components = 1; -+ parse_result.scan.num_components = 1; -+ -+ gfx::Size size(parse_result.frame_header.coded_width, -+ parse_result.frame_header.coded_height); -+ -+ std::vector va_surfaces; -+ ASSERT_TRUE( -+ wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)); -+ -+ EXPECT_FALSE( -+ VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])); -+} -+ -+} // namespace -+} // namespace media -+ -+int main(int argc, char** argv) { -+ testing::InitGoogleTest(&argc, argv); -+ base::AtExitManager exit_manager; -+ media::VaapiWrapper::PreSandboxInitialization(); -+ return RUN_ALL_TESTS(); -+} ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.cc -@@ -0,0 +1,268 @@ -+// Copyright 2017 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h" -+ -+#include -+ -+#include -+#include -+ -+#include "base/bind.h" -+#include "base/logging.h" -+#include "base/metrics/histogram_macros.h" -+#include "base/sequence_checker.h" -+#include "base/task_scheduler/post_task.h" -+#include "base/threading/thread_task_runner_handle.h" -+#include "base/trace_event/trace_event.h" -+#include "media/base/bind_to_current_loop.h" -+#include "media/base/video_frame.h" -+#include "media/gpu/vaapi/vaapi_jpeg_encoder.h" -+ -+namespace media { -+ -+namespace { -+ -+// UMA results that the VaapiJpegEncodeAccelerator class reports. -+// These values are persisted to logs, and should therefore never be renumbered -+// nor reused. -+enum VAJEAEncoderResult { -+ VAAPI_SUCCESS = 0, -+ VAAPI_ERROR, -+ VAJEA_ENCODER_RESULT_MAX = VAAPI_ERROR, -+}; -+ -+static void ReportToUMA(VAJEAEncoderResult result) { -+ UMA_HISTOGRAM_ENUMERATION("Media.VAJEA.EncoderResult", result, -+ VAJEAEncoderResult::VAJEA_ENCODER_RESULT_MAX + 1); -+} -+} // namespace -+ -+VaapiJpegEncodeAccelerator::EncodeRequest::EncodeRequest( -+ scoped_refptr video_frame, -+ std::unique_ptr shm, -+ int quality) -+ : video_frame(std::move(video_frame)), -+ shm(std::move(shm)), -+ quality(quality) {} -+ -+VaapiJpegEncodeAccelerator::EncodeRequest::~EncodeRequest() {} -+ -+class VaapiJpegEncodeAccelerator::Encoder { -+ public: -+ Encoder(scoped_refptr vaapi_wrapper, -+ base::RepeatingCallback video_frame_ready_cb, -+ base::RepeatingCallback notify_error_cb); -+ ~Encoder(); -+ -+ // Processes one encode |request|. -+ void EncodeTask(std::unique_ptr request); -+ -+ private: -+ // |cached_output_buffer_id_| is the last allocated VABuffer during -+ // EncodeTask() and |cached_output_buffer_size_| is the size of it. -+ // If the next call to EncodeTask() does not require a buffer bigger than -+ // |cached_output_buffer_size_|, |cached_output_buffer_id_| will be reused. -+ size_t cached_output_buffer_size_; -+ VABufferID cached_output_buffer_id_; -+ -+ std::unique_ptr jpeg_encoder_; -+ scoped_refptr vaapi_wrapper_; -+ -+ base::RepeatingCallback video_frame_ready_cb_; -+ base::RepeatingCallback notify_error_cb_; -+ -+ SEQUENCE_CHECKER(sequence_checker_); -+ -+ DISALLOW_COPY_AND_ASSIGN(Encoder); -+}; -+ -+VaapiJpegEncodeAccelerator::Encoder::Encoder( -+ scoped_refptr vaapi_wrapper, -+ base::RepeatingCallback video_frame_ready_cb, -+ base::RepeatingCallback notify_error_cb) -+ : cached_output_buffer_size_(0), -+ jpeg_encoder_(new VaapiJpegEncoder(vaapi_wrapper)), -+ vaapi_wrapper_(std::move(vaapi_wrapper)), -+ video_frame_ready_cb_(std::move(video_frame_ready_cb)), -+ notify_error_cb_(std::move(notify_error_cb)) { -+ DETACH_FROM_SEQUENCE(sequence_checker_); -+} -+ -+VaapiJpegEncodeAccelerator::Encoder::~Encoder() { -+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -+} -+ -+void VaapiJpegEncodeAccelerator::Encoder::EncodeTask( -+ std::unique_ptr request) { -+ TRACE_EVENT0("jpeg", "EncodeTask"); -+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -+ -+ const int video_frame_id = request->video_frame->unique_id(); -+ gfx::Size input_size = request->video_frame->coded_size(); -+ std::vector va_surfaces; -+ if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, input_size, 1, -+ &va_surfaces)) { -+ VLOG(1) << "Failed to create VA surface"; -+ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -+ return; -+ } -+ VASurfaceID va_surface_id = va_surfaces[0]; -+ -+ if (!vaapi_wrapper_->UploadVideoFrameToSurface(request->video_frame, -+ va_surface_id)) { -+ VLOG(1) << "Failed to upload video frame to VA surface"; -+ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -+ return; -+ } -+ -+ // Create output buffer for encoding result. -+ size_t max_coded_buffer_size = -+ VaapiJpegEncoder::GetMaxCodedBufferSize(input_size); -+ if (max_coded_buffer_size > cached_output_buffer_size_) { -+ vaapi_wrapper_->DestroyCodedBuffers(); -+ cached_output_buffer_size_ = 0; -+ -+ VABufferID output_buffer_id; -+ if (!vaapi_wrapper_->CreateCodedBuffer(max_coded_buffer_size, -+ &output_buffer_id)) { -+ VLOG(1) << "Failed to create VA buffer for encoding output"; -+ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -+ return; -+ } -+ cached_output_buffer_size_ = max_coded_buffer_size; -+ cached_output_buffer_id_ = output_buffer_id; -+ } -+ -+ if (!jpeg_encoder_->Encode(input_size, request->quality, va_surface_id, -+ cached_output_buffer_id_)) { -+ VLOG(1) << "Encode JPEG failed"; -+ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -+ return; -+ } -+ -+ // Get the encoded output. DownloadFromCodedBuffer() is a blocking call. It -+ // would wait until encoding is finished. -+ size_t encoded_size = 0; -+ if (!vaapi_wrapper_->DownloadFromCodedBuffer( -+ cached_output_buffer_id_, va_surface_id, -+ static_cast(request->shm->memory()), request->shm->size(), -+ &encoded_size)) { -+ VLOG(1) << "Failed to retrieve output image from VA coded buffer"; -+ notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -+ } -+ -+ video_frame_ready_cb_.Run(request->video_frame->unique_id(), encoded_size); -+} -+ -+VaapiJpegEncodeAccelerator::VaapiJpegEncodeAccelerator( -+ scoped_refptr io_task_runner) -+ : task_runner_(base::ThreadTaskRunnerHandle::Get()), -+ io_task_runner_(std::move(io_task_runner)), -+ weak_this_factory_(this) { -+ weak_this_ = weak_this_factory_.GetWeakPtr(); -+} -+ -+VaapiJpegEncodeAccelerator::~VaapiJpegEncodeAccelerator() { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ DVLOG(1) << "Destroying VaapiJpegEncodeAccelerator"; -+ -+ weak_this_factory_.InvalidateWeakPtrs(); -+ encoder_task_runner_->DeleteSoon(FROM_HERE, std::move(encoder_)); -+} -+ -+void VaapiJpegEncodeAccelerator::NotifyError(int video_frame_id, -+ Status status) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ DLOG(ERROR) << "Notifying error: " << status; -+ DCHECK(client_); -+ client_->NotifyError(video_frame_id, status); -+} -+ -+void VaapiJpegEncodeAccelerator::VideoFrameReady(int video_frame_id, -+ size_t encoded_picture_size) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ ReportToUMA(VAJEAEncoderResult::VAAPI_SUCCESS); -+ -+ client_->VideoFrameReady(video_frame_id, encoded_picture_size); -+} -+ -+JpegEncodeAccelerator::Status VaapiJpegEncodeAccelerator::Initialize( -+ JpegEncodeAccelerator::Client* client) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ if (!VaapiWrapper::IsJpegEncodeSupported()) { -+ return HW_JPEG_ENCODE_NOT_SUPPORTED; -+ } -+ -+ client_ = client; -+ scoped_refptr vaapi_wrapper = VaapiWrapper::Create( -+ VaapiWrapper::kEncode, VAProfileJPEGBaseline, -+ base::Bind(&ReportToUMA, VAJEAEncoderResult::VAAPI_ERROR)); -+ -+ if (!vaapi_wrapper) { -+ VLOG(1) << "Failed initializing VAAPI"; -+ return PLATFORM_FAILURE; -+ } -+ -+ encoder_task_runner_ = base::CreateSingleThreadTaskRunnerWithTraits( -+ {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); -+ if (!encoder_task_runner_) { -+ VLOG(1) << "Failed to create encoder task runner."; -+ return THREAD_CREATION_FAILED; -+ } -+ -+ encoder_ = std::make_unique( -+ std::move(vaapi_wrapper), -+ BindToCurrentLoop(base::BindRepeating( -+ &VaapiJpegEncodeAccelerator::VideoFrameReady, weak_this_)), -+ BindToCurrentLoop(base::BindRepeating( -+ &VaapiJpegEncodeAccelerator::NotifyError, weak_this_))); -+ -+ return ENCODE_OK; -+} -+ -+size_t VaapiJpegEncodeAccelerator::GetMaxCodedBufferSize( -+ const gfx::Size& picture_size) { -+ return VaapiJpegEncoder::GetMaxCodedBufferSize(picture_size); -+} -+ -+void VaapiJpegEncodeAccelerator::Encode( -+ scoped_refptr video_frame, -+ int quality, -+ const BitstreamBuffer& bitstream_buffer) { -+ DCHECK(io_task_runner_->BelongsToCurrentThread()); -+ -+ int video_frame_id = video_frame->unique_id(); -+ TRACE_EVENT1("jpeg", "Encode", "input_id", video_frame_id); -+ -+ // TODO(shenghao): support other YUV formats. -+ if (video_frame->format() != VideoPixelFormat::PIXEL_FORMAT_I420) { -+ VLOG(1) << "Unsupported input format: " << video_frame->format(); -+ task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, -+ weak_this_, video_frame_id, INVALID_ARGUMENT)); -+ return; -+ } -+ -+ // SharedMemoryRegion will take ownership of the |bitstream_buffer.handle()|. -+ auto shm = std::make_unique(bitstream_buffer, false); -+ if (!shm->Map()) { -+ VLOG(1) << "Failed to map output buffer"; -+ task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, weak_this_, -+ video_frame_id, INACCESSIBLE_OUTPUT_BUFFER)); -+ return; -+ } -+ -+ auto request = std::make_unique(std::move(video_frame), -+ std::move(shm), quality); -+ encoder_task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiJpegEncodeAccelerator::Encoder::EncodeTask, -+ base::Unretained(encoder_.get()), base::Passed(&request))); -+} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_encode_accelerator.h -@@ -0,0 +1,96 @@ -+// Copyright 2017 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ -+#define MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ -+ -+#include -+ -+#include "base/macros.h" -+#include "base/memory/weak_ptr.h" -+#include "base/single_thread_task_runner.h" -+#include "media/base/bitstream_buffer.h" -+#include "media/gpu/media_gpu_export.h" -+#include "media/gpu/shared_memory_region.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+#include "media/video/jpeg_encode_accelerator.h" -+ -+namespace media { -+ -+// Class to provide JPEG encode acceleration for Intel systems with hardware -+// support for it, and on which libva is available. -+// Encoding tasks are performed in a separate encoding thread. -+// -+// Threading/life-cycle: this object is created & destroyed on the GPU -+// ChildThread. Methods in nested class Encoder are called on the encoder -+// thread which is stopped during destructor, so the callbacks bound with -+// a weak this can be run on the encoder thread because it can assume -+// VaapiJpegEncodeAccelerator is still alive. -+class MEDIA_GPU_EXPORT VaapiJpegEncodeAccelerator -+ : public JpegEncodeAccelerator { -+ public: -+ explicit VaapiJpegEncodeAccelerator( -+ scoped_refptr io_task_runner); -+ ~VaapiJpegEncodeAccelerator() override; -+ -+ // JpegEncodeAccelerator implementation. -+ Status Initialize(JpegEncodeAccelerator::Client* client) override; -+ size_t GetMaxCodedBufferSize(const gfx::Size& picture_size) override; -+ -+ // Currently only I420 format is supported for |video_frame|. -+ void Encode(scoped_refptr video_frame, -+ int quality, -+ const BitstreamBuffer& bitstream_buffer) override; -+ -+ private: -+ // An input video frame and the corresponding output buffer awaiting -+ // consumption, provided by the client. -+ struct EncodeRequest { -+ EncodeRequest(scoped_refptr video_frame, -+ std::unique_ptr shm, -+ int quality); -+ ~EncodeRequest(); -+ -+ scoped_refptr video_frame; -+ std::unique_ptr shm; -+ int quality; -+ -+ DISALLOW_COPY_AND_ASSIGN(EncodeRequest); -+ }; -+ -+ // The Encoder class is a collection of methods that run on -+ // |encoder_task_runner_|. -+ class Encoder; -+ -+ // Notifies the client that an error has occurred and encoding cannot -+ // continue. -+ void NotifyError(int video_frame_id, Status status); -+ -+ void VideoFrameReady(int video_frame_id, size_t encoded_picture_size); -+ -+ // ChildThread's task runner. -+ scoped_refptr task_runner_; -+ -+ // GPU IO task runner. -+ scoped_refptr io_task_runner_; -+ -+ // The client of this class. -+ Client* client_; -+ -+ // Use this to post tasks to encoder thread. -+ scoped_refptr encoder_task_runner_; -+ -+ std::unique_ptr encoder_; -+ -+ // |weak_this_| is used to post tasks from |encoder_task_runner_| to -+ // |task_runner_|. -+ base::WeakPtr weak_this_; -+ base::WeakPtrFactory weak_this_factory_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncodeAccelerator); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_encoder.cc -@@ -0,0 +1,427 @@ -+// Copyright 2017 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_jpeg_encoder.h" -+ -+#include -+#include -+#include -+ -+#include "base/logging.h" -+#include "base/macros.h" -+#include "base/numerics/safe_conversions.h" -+#include "media/filters/jpeg_parser.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+ -+#define ARRAY_MEMCPY_CHECKED(to, from) \ -+ do { \ -+ static_assert(sizeof(to) == sizeof(from), \ -+ #from " and " #to " arrays must be of same size"); \ -+ memcpy(to, from, sizeof(to)); \ -+ } while (0) -+ -+namespace media { -+ -+namespace { -+ -+// JPEG header only uses 2 bytes to represent width and height. -+const int kMaxDimension = 65535; -+const size_t kDctSize2 = 64; -+const size_t kNumDcRunSizeBits = 16; -+const size_t kNumAcRunSizeBits = 16; -+const size_t kNumDcCodeWordsHuffVal = 12; -+const size_t kNumAcCodeWordsHuffVal = 162; -+const size_t kJpegHeaderSize = 83 + (kDctSize2 * 2) + (kNumDcRunSizeBits * 2) + -+ (kNumDcCodeWordsHuffVal * 2) + -+ (kNumAcRunSizeBits * 2) + -+ (kNumAcCodeWordsHuffVal * 2); -+ -+const uint8_t kZigZag8x8[64] = { -+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, -+ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, -+ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, -+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; -+ -+const JpegQuantizationTable kDefaultQuantTable[2] = { -+ // Table K.1 Luminance quantization table values. -+ { -+ true, -+ {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, -+ 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, -+ 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, -+ 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}, -+ }, -+ // Table K.2 Chrominance quantization table values. -+ { -+ true, -+ {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, -+ 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, -+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, -+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, -+ }, -+}; -+ -+using JPEGHeader = uint8_t[kJpegHeaderSize]; -+ -+void FillPictureParameters(const gfx::Size& input_size, -+ int quality, -+ VABufferID output_buffer_id, -+ VAEncPictureParameterBufferJPEG* pic_param) { -+ pic_param->picture_width = input_size.width(); -+ pic_param->picture_height = input_size.height(); -+ pic_param->num_components = 3; -+ -+ // Output buffer. -+ pic_param->coded_buf = output_buffer_id; -+ pic_param->quality = quality; -+ // Profile = Baseline. -+ pic_param->pic_flags.bits.profile = 0; -+ // Sequential encoding. -+ pic_param->pic_flags.bits.progressive = 0; -+ // Uses Huffman coding. -+ pic_param->pic_flags.bits.huffman = 1; -+ // Input format is interleaved (YUV). -+ pic_param->pic_flags.bits.interleaved = 0; -+ // Non-differential Encoding. -+ pic_param->pic_flags.bits.differential = 0; -+ // Only 8 bit sample depth is currently supported. -+ pic_param->sample_bit_depth = 8; -+ pic_param->num_scan = 1; -+} -+ -+void FillQMatrix(VAQMatrixBufferJPEG* q_matrix) { -+ // Fill the raw, unscaled quantization tables for libva. The VAAPI driver is -+ // responsible for scaling the quantization tables based on picture -+ // parameter quality. -+ const JpegQuantizationTable& luminance = kDefaultQuantTable[0]; -+ static_assert( -+ arraysize(luminance.value) == arraysize(q_matrix->lum_quantiser_matrix), -+ "Luminance quantization table size mismatch."); -+ static_assert(arraysize(kZigZag8x8) == arraysize(luminance.value), -+ "Luminance quantization table size mismatch."); -+ q_matrix->load_lum_quantiser_matrix = 1; -+ for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { -+ q_matrix->lum_quantiser_matrix[i] = luminance.value[kZigZag8x8[i]]; -+ } -+ -+ const JpegQuantizationTable& chrominance = kDefaultQuantTable[1]; -+ static_assert(arraysize(chrominance.value) == -+ arraysize(q_matrix->chroma_quantiser_matrix), -+ "Chrominance quantization table size mismatch."); -+ static_assert(arraysize(kZigZag8x8) == arraysize(chrominance.value), -+ "Chrominance quantization table size mismatch."); -+ q_matrix->load_chroma_quantiser_matrix = 1; -+ for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { -+ q_matrix->chroma_quantiser_matrix[i] = chrominance.value[kZigZag8x8[i]]; -+ } -+} -+ -+void FillHuffmanTableParameters( -+ VAHuffmanTableBufferJPEGBaseline* huff_table_param) { -+ static_assert(arraysize(kDefaultDcTable) == arraysize(kDefaultAcTable), -+ "DC table and AC table size mismatch."); -+ static_assert( -+ arraysize(kDefaultDcTable) == arraysize(huff_table_param->huffman_table), -+ "DC table and destination table size mismatch."); -+ -+ for (size_t i = 0; i < arraysize(kDefaultDcTable); ++i) { -+ const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; -+ const JpegHuffmanTable& acTable = kDefaultAcTable[i]; -+ huff_table_param->load_huffman_table[i] = true; -+ -+ // Load DC Table. -+ ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_dc_codes, -+ dcTable.code_length); -+ // |code_values| of JpegHuffmanTable needs to hold DC and AC code values -+ // so it has different size than -+ // |huff_table_param->huffman_table[i].dc_values|. Therefore we can't use -+ // ARRAY_MEMCPY_CHECKED() here. -+ static_assert(arraysize(huff_table_param->huffman_table[i].dc_values) <= -+ arraysize(dcTable.code_value), -+ "DC table code value array too small."); -+ memcpy(huff_table_param->huffman_table[i].dc_values, &dcTable.code_value[0], -+ sizeof(huff_table_param->huffman_table[i].dc_values)); -+ -+ // Load AC Table. -+ ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_ac_codes, -+ acTable.code_length); -+ ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].ac_values, -+ acTable.code_value); -+ -+ memset(huff_table_param->huffman_table[i].pad, 0, -+ sizeof(huff_table_param->huffman_table[i].pad)); -+ } -+} -+ -+void FillSliceParameters(VAEncSliceParameterBufferJPEG* slice_param) { -+ slice_param->restart_interval = 0; -+ slice_param->num_components = 3; -+ -+ slice_param->components[0].component_selector = 1; -+ slice_param->components[0].dc_table_selector = 0; -+ slice_param->components[0].ac_table_selector = 0; -+ -+ slice_param->components[1].component_selector = 2; -+ slice_param->components[1].dc_table_selector = 1; -+ slice_param->components[1].ac_table_selector = 1; -+ -+ slice_param->components[2].component_selector = 3; -+ slice_param->components[2].dc_table_selector = 1; -+ slice_param->components[2].ac_table_selector = 1; -+} -+ -+size_t FillJpegHeader(const gfx::Size& input_size, -+ int quality, -+ JPEGHeader& header) { -+ unsigned int width = input_size.width(); -+ unsigned int height = input_size.height(); -+ -+ size_t idx = 0; -+ -+ // Start Of Input. -+ static const uint8_t kSOI[] = {0xFF, JPEG_SOI}; -+ memcpy(header, kSOI, sizeof(kSOI)); -+ idx += sizeof(kSOI); -+ -+ // Application Segment - JFIF standard 1.01. -+ // TODO(shenghao): Use Exif (JPEG_APP1) instead. -+ static const uint8_t kAppSegment[] = { -+ 0xFF, JPEG_APP0, 0x00, -+ 0x10, // Segment length:16 (2-byte). -+ 0x4A, // J -+ 0x46, // F -+ 0x49, // I -+ 0x46, // F -+ 0x00, // 0 -+ 0x01, // Major version. -+ 0x01, // Minor version. -+ 0x01, // Density units 0:no units, 1:pixels per inch, -+ // 2: pixels per cm. -+ 0x00, -+ 0x48, // X density (2-byte). -+ 0x00, -+ 0x48, // Y density (2-byte). -+ 0x00, // Thumbnail width. -+ 0x00 // Thumbnail height. -+ }; -+ memcpy(header + idx, kAppSegment, sizeof(kAppSegment)); -+ idx += sizeof(kAppSegment); -+ -+ if (quality <= 0) { -+ quality = 1; -+ } -+ -+ // Normalize quality factor. -+ // Unlike VAQMatrixBufferJPEG, we have to scale quantization table in JPEG -+ // header by ourselves. -+ uint32_t quality_normalized = base::saturated_cast( -+ (quality < 50) ? (5000 / quality) : (200 - (quality * 2))); -+ -+ // Quantization Tables. -+ for (size_t i = 0; i < 2; ++i) { -+ const uint8_t kQuantSegment[] = { -+ 0xFF, JPEG_DQT, 0x00, -+ 0x03 + kDctSize2, // Segment length:67 (2-byte). -+ static_cast(i) // Precision (4-bit high) = 0, -+ // Index (4-bit low) = i. -+ }; -+ memcpy(header + idx, kQuantSegment, sizeof(kQuantSegment)); -+ idx += sizeof(kQuantSegment); -+ -+ const JpegQuantizationTable& quant_table = kDefaultQuantTable[i]; -+ for (size_t j = 0; j < kDctSize2; ++j) { -+ uint32_t scaled_quant_value = -+ (quant_table.value[kZigZag8x8[j]] * quality_normalized) / 100; -+ scaled_quant_value = std::min(255u, std::max(1u, scaled_quant_value)); -+ header[idx++] = static_cast(scaled_quant_value); -+ } -+ } -+ -+ // Start of Frame - Baseline. -+ const uint8_t kStartOfFrame[] = { -+ 0xFF, -+ JPEG_SOF0, // Baseline. -+ 0x00, -+ 0x11, // Segment length:17 (2-byte). -+ 8, // Data precision. -+ static_cast((height >> 8) & 0xFF), -+ static_cast(height & 0xFF), -+ static_cast((width >> 8) & 0xFF), -+ static_cast(width & 0xFF), -+ 0x03, // Number of Components. -+ }; -+ memcpy(header + idx, kStartOfFrame, sizeof(kStartOfFrame)); -+ idx += sizeof(kStartOfFrame); -+ for (uint8_t i = 0; i < 3; ++i) { -+ // These are the values for U and V planes. -+ uint8_t h_sample_factor = 1; -+ uint8_t v_sample_factor = 1; -+ uint8_t quant_table_number = 1; -+ if (!i) { -+ // These are the values for Y plane. -+ h_sample_factor = 2; -+ v_sample_factor = 2; -+ quant_table_number = 0; -+ } -+ -+ header[idx++] = i + 1; -+ // Horizontal Sample Factor (4-bit high), -+ // Vertical Sample Factor (4-bit low). -+ header[idx++] = (h_sample_factor << 4) | v_sample_factor; -+ header[idx++] = quant_table_number; -+ } -+ -+ static const uint8_t kDcSegment[] = { -+ 0xFF, JPEG_DHT, 0x00, -+ 0x1F, // Segment length:31 (2-byte). -+ }; -+ static const uint8_t kAcSegment[] = { -+ 0xFF, JPEG_DHT, 0x00, -+ 0xB5, // Segment length:181 (2-byte). -+ }; -+ -+ // Huffman Tables. -+ for (size_t i = 0; i < 2; ++i) { -+ // DC Table. -+ memcpy(header + idx, kDcSegment, sizeof(kDcSegment)); -+ idx += sizeof(kDcSegment); -+ -+ // Type (4-bit high) = 0:DC, Index (4-bit low). -+ header[idx++] = static_cast(i); -+ -+ const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; -+ for (size_t j = 0; j < kNumDcRunSizeBits; ++j) -+ header[idx++] = dcTable.code_length[j]; -+ for (size_t j = 0; j < kNumDcCodeWordsHuffVal; ++j) -+ header[idx++] = dcTable.code_value[j]; -+ -+ // AC Table. -+ memcpy(header + idx, kAcSegment, sizeof(kAcSegment)); -+ idx += sizeof(kAcSegment); -+ -+ // Type (4-bit high) = 1:AC, Index (4-bit low). -+ header[idx++] = 0x10 | static_cast(i); -+ -+ const JpegHuffmanTable& acTable = kDefaultAcTable[i]; -+ for (size_t j = 0; j < kNumAcRunSizeBits; ++j) -+ header[idx++] = acTable.code_length[j]; -+ for (size_t j = 0; j < kNumAcCodeWordsHuffVal; ++j) -+ header[idx++] = acTable.code_value[j]; -+ } -+ -+ // Start of Scan. -+ static const uint8_t kStartOfScan[] = { -+ 0xFF, JPEG_SOS, 0x00, -+ 0x0C, // Segment Length:12 (2-byte). -+ 0x03 // Number of components in scan. -+ }; -+ memcpy(header + idx, kStartOfScan, sizeof(kStartOfScan)); -+ idx += sizeof(kStartOfScan); -+ -+ for (uint8_t i = 0; i < 3; ++i) { -+ uint8_t dc_table_number = 1; -+ uint8_t ac_table_number = 1; -+ if (!i) { -+ dc_table_number = 0; -+ ac_table_number = 0; -+ } -+ -+ header[idx++] = i + 1; -+ // DC Table Selector (4-bit high), AC Table Selector (4-bit low). -+ header[idx++] = (dc_table_number << 4) | ac_table_number; -+ } -+ header[idx++] = 0x00; // 0 for Baseline. -+ header[idx++] = 0x3F; // 63 for Baseline. -+ header[idx++] = 0x00; // 0 for Baseline. -+ -+ return idx << 3; -+} -+ -+} // namespace -+ -+VaapiJpegEncoder::VaapiJpegEncoder(scoped_refptr vaapi_wrapper) -+ : vaapi_wrapper_(vaapi_wrapper), -+ q_matrix_cached_(nullptr), -+ huff_table_param_cached_(nullptr), -+ slice_param_cached_(nullptr) {} -+ -+VaapiJpegEncoder::~VaapiJpegEncoder() {} -+ -+size_t VaapiJpegEncoder::GetMaxCodedBufferSize(const gfx::Size& size) { -+ return size.GetArea() * 3 / 2 + kJpegHeaderSize; -+} -+ -+bool VaapiJpegEncoder::Encode(const gfx::Size& input_size, -+ int quality, -+ VASurfaceID surface_id, -+ VABufferID output_buffer_id) { -+ DCHECK_NE(surface_id, VA_INVALID_SURFACE); -+ -+ if (input_size.width() > kMaxDimension || -+ input_size.height() > kMaxDimension) { -+ return false; -+ } -+ -+ // Set picture parameters. -+ VAEncPictureParameterBufferJPEG pic_param; -+ FillPictureParameters(input_size, quality, output_buffer_id, &pic_param); -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, -+ sizeof(pic_param), &pic_param)) { -+ return false; -+ } -+ -+ if (!q_matrix_cached_) { -+ q_matrix_cached_.reset(new VAQMatrixBufferJPEG()); -+ FillQMatrix(q_matrix_cached_.get()); -+ } -+ if (!vaapi_wrapper_->SubmitBuffer(VAQMatrixBufferType, -+ sizeof(*q_matrix_cached_), -+ q_matrix_cached_.get())) { -+ return false; -+ } -+ -+ if (!huff_table_param_cached_) { -+ huff_table_param_cached_.reset(new VAHuffmanTableBufferJPEGBaseline()); -+ FillHuffmanTableParameters(huff_table_param_cached_.get()); -+ } -+ if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType, -+ sizeof(*huff_table_param_cached_), -+ huff_table_param_cached_.get())) { -+ return false; -+ } -+ -+ // Set slice parameters. -+ if (!slice_param_cached_) { -+ slice_param_cached_.reset(new VAEncSliceParameterBufferJPEG()); -+ FillSliceParameters(slice_param_cached_.get()); -+ } -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, -+ sizeof(*slice_param_cached_), -+ slice_param_cached_.get())) { -+ return false; -+ } -+ -+ JPEGHeader header_data; -+ size_t length_in_bits = FillJpegHeader(input_size, quality, header_data); -+ -+ VAEncPackedHeaderParameterBuffer header_param; -+ memset(&header_param, 0, sizeof(header_param)); -+ header_param.type = VAEncPackedHeaderRawData; -+ header_param.bit_length = length_in_bits; -+ header_param.has_emulation_bytes = 0; -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, -+ sizeof(header_param), &header_param)) { -+ return false; -+ } -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, -+ (length_in_bits + 7) / 8, header_data)) { -+ return false; -+ } -+ -+ // Submit the |surface_id| which contains input YUV frame and begin encoding. -+ return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(surface_id); -+} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_jpeg_encoder.h -@@ -0,0 +1,65 @@ -+// Copyright 2017 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifndef MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODER_H_ -+#define MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODER_H_ -+ -+#include -+#include -+ -+#include "base/macros.h" -+#include "base/memory/scoped_refptr.h" -+#include "media/gpu/media_gpu_export.h" -+#include "ui/gfx/geometry/size.h" -+ -+namespace media { -+ -+class VaapiWrapper; -+ -+// A collection of methods that utilize VA-API hardware video encode -+// acceleration on Intel systems. Provides functionality to allow plugging VAAPI -+// HW acceleration into the JpegEncodeAccelerator framework. -+// -+// Clients are expected to manage VA surfaces and VA buffers created via -+// VaapiWrapper, and pass them to this class. -+class MEDIA_GPU_EXPORT VaapiJpegEncoder { -+ public: -+ // |vaapi_wrapper| should be initialized in VaapiWrapper::kEncode -+ // mode with VAProfileJPEGBaseline profile. -+ explicit VaapiJpegEncoder(scoped_refptr vaapi_wrapper); -+ ~VaapiJpegEncoder(); -+ -+ // Encode a JPEG picture. It will fill VA-API parameters and call -+ // corresponding VA-API methods according to |input_size|. -+ // |quality| is the JPEG image quality -+ // |surface_id| is the VA surface that contains input image. -+ // |output_buffer_id| is the ID of VA buffer that encoded image will be -+ // stored. The size of it should be at least as large as -+ // GetMaxCodedBufferSize(). -+ // Return false on failure. -+ bool Encode(const gfx::Size& input_size, -+ int quality, -+ VASurfaceID surface_id, -+ VABufferID output_buffer_id); -+ -+ // Gets the maximum possible encoded result size. -+ // |size| is the dimension of the YUV image to be encoded. -+ static size_t GetMaxCodedBufferSize(const gfx::Size& size); -+ -+ private: -+ scoped_refptr vaapi_wrapper_; -+ -+ // |q_matrix_cached_|, |huff_table_param_cached_| and |slice_param_cached_| -+ // are created when Encode() is called the first time. After that, they will -+ // directly be used for all the subsequent Encode() calls. -+ std::unique_ptr q_matrix_cached_; -+ std::unique_ptr huff_table_param_cached_; -+ std::unique_ptr slice_param_cached_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncoder); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VAAPI_JPEG_ENCODER_H_ ---- a/media/gpu/vaapi/vaapi_picture.cc -+++ b/media/gpu/vaapi/vaapi_picture.cc -@@ -4,7 +4,7 @@ - - #include "media/gpu/vaapi/vaapi_picture.h" - --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #include "ui/gl/gl_bindings.h" - #include "ui/gl/gl_implementation.h" - ---- a/media/gpu/vaapi/vaapi_picture_factory.cc -+++ b/media/gpu/vaapi/vaapi_picture_factory.cc -@@ -4,7 +4,7 @@ - - #include "media/gpu/vaapi/vaapi_picture_factory.h" - --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #include "ui/gl/gl_bindings.h" - - #include "media/gpu/vaapi/vaapi_drm_picture.h" ---- a/media/gpu/vaapi/vaapi_tfp_picture.cc -+++ b/media/gpu/vaapi/vaapi_tfp_picture.cc -@@ -4,8 +4,8 @@ - - #include "media/gpu/vaapi/vaapi_tfp_picture.h" - --#include "media/gpu/va_surface.h" --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/va_surface.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #include "ui/gfx/x/x11_types.h" - #include "ui/gl/gl_bindings.h" - #include "ui/gl/gl_image_glx.h" ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc -@@ -0,0 +1,1871 @@ -+// Copyright (c) 2012 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" -+ -+#include -+ -+#include -+ -+#include -+ -+#include "base/bind.h" -+#include "base/files/scoped_file.h" -+#include "base/logging.h" -+#include "base/macros.h" -+#include "base/metrics/histogram_macros.h" -+#include "base/stl_util.h" -+#include "base/strings/string_util.h" -+#include "base/synchronization/waitable_event.h" -+#include "base/threading/thread_task_runner_handle.h" -+#include "base/trace_event/trace_event.h" -+#include "gpu/ipc/service/gpu_channel.h" -+#include "media/base/bind_to_current_loop.h" -+#include "media/gpu/accelerated_video_decoder.h" -+#include "media/gpu/format_utils.h" -+#include "media/gpu/h264_decoder.h" -+#include "media/gpu/vaapi/vaapi_picture.h" -+#include "media/gpu/vp8_decoder.h" -+#include "media/gpu/vp9_decoder.h" -+#include "media/video/picture.h" -+#include "ui/gl/gl_image.h" -+ -+#define DVLOGF(level) DVLOG(level) << __func__ << "(): " -+#define VLOGF(level) VLOG(level) << __func__ << "(): " -+ -+namespace media { -+ -+namespace { -+// UMA errors that the VaapiVideoDecodeAccelerator class reports. -+enum VAVDADecoderFailure { -+ VAAPI_ERROR = 0, -+ VAVDA_DECODER_FAILURES_MAX, -+}; -+// from ITU-T REC H.264 spec -+// section 8.5.6 -+// "Inverse scanning process for 4x4 transform coefficients and scaling lists" -+static const int kZigzagScan4x4[16] = {0, 1, 4, 8, 5, 2, 3, 6, -+ 9, 12, 13, 10, 7, 11, 14, 15}; -+ -+// section 8.5.7 -+// "Inverse scanning process for 8x8 transform coefficients and scaling lists" -+static const uint8_t kZigzagScan8x8[64] = { -+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, -+ 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, -+ 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, -+ 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; -+ -+// Returns the preferred VA_RT_FORMAT for the given |profile|. -+unsigned int GetVaFormatForVideoCodecProfile(VideoCodecProfile profile) { -+ if (profile == VP9PROFILE_PROFILE2 || profile == VP9PROFILE_PROFILE3) -+ return VA_RT_FORMAT_YUV420_10BPP; -+ return VA_RT_FORMAT_YUV420; -+} -+ -+} // namespace -+ -+static void ReportToUMA(VAVDADecoderFailure failure) { -+ UMA_HISTOGRAM_ENUMERATION("Media.VAVDA.DecoderFailure", failure, -+ VAVDA_DECODER_FAILURES_MAX + 1); -+} -+ -+#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ -+ do { \ -+ if (!(result)) { \ -+ VLOGF(1) << log; \ -+ NotifyError(error_code); \ -+ return ret; \ -+ } \ -+ } while (0) -+ -+class VaapiVideoDecodeAccelerator::VaapiDecodeSurface -+ : public base::RefCountedThreadSafe { -+ public: -+ VaapiDecodeSurface(int32_t bitstream_id, -+ const scoped_refptr& va_surface); -+ -+ int32_t bitstream_id() const { return bitstream_id_; } -+ scoped_refptr va_surface() { return va_surface_; } -+ gfx::Rect visible_rect() const { return visible_rect_; } -+ -+ void set_visible_rect(const gfx::Rect& visible_rect) { -+ visible_rect_ = visible_rect; -+ } -+ -+ private: -+ friend class base::RefCountedThreadSafe; -+ ~VaapiDecodeSurface(); -+ -+ const int32_t bitstream_id_; -+ const scoped_refptr va_surface_; -+ gfx::Rect visible_rect_; -+}; -+ -+VaapiVideoDecodeAccelerator::VaapiDecodeSurface::VaapiDecodeSurface( -+ int32_t bitstream_id, -+ const scoped_refptr& va_surface) -+ : bitstream_id_(bitstream_id), va_surface_(va_surface) {} -+ -+VaapiVideoDecodeAccelerator::VaapiDecodeSurface::~VaapiDecodeSurface() {} -+ -+class VaapiH264Picture : public H264Picture { -+ public: -+ explicit VaapiH264Picture( -+ scoped_refptr surface) -+ : dec_surface_(surface) {} -+ -+ VaapiH264Picture* AsVaapiH264Picture() override { return this; } -+ scoped_refptr dec_surface() { -+ return dec_surface_; -+ } -+ -+ private: -+ ~VaapiH264Picture() override {} -+ -+ scoped_refptr dec_surface_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiH264Picture); -+}; -+ -+class VaapiVideoDecodeAccelerator::VaapiH264Accelerator -+ : public H264Decoder::H264Accelerator { -+ public: -+ VaapiH264Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, -+ VaapiWrapper* vaapi_wrapper); -+ ~VaapiH264Accelerator() override; -+ -+ // H264Decoder::H264Accelerator implementation. -+ scoped_refptr CreateH264Picture() override; -+ -+ bool SubmitFrameMetadata(const H264SPS* sps, -+ const H264PPS* pps, -+ const H264DPB& dpb, -+ const H264Picture::Vector& ref_pic_listp0, -+ const H264Picture::Vector& ref_pic_listb0, -+ const H264Picture::Vector& ref_pic_listb1, -+ const scoped_refptr& pic) override; -+ -+ bool SubmitSlice(const H264PPS* pps, -+ const H264SliceHeader* slice_hdr, -+ const H264Picture::Vector& ref_pic_list0, -+ const H264Picture::Vector& ref_pic_list1, -+ const scoped_refptr& pic, -+ const uint8_t* data, -+ size_t size) override; -+ -+ bool SubmitDecode(const scoped_refptr& pic) override; -+ bool OutputPicture(const scoped_refptr& pic) override; -+ -+ void Reset() override; -+ -+ private: -+ scoped_refptr H264PictureToVaapiDecodeSurface( -+ const scoped_refptr& pic); -+ -+ void FillVAPicture(VAPictureH264* va_pic, scoped_refptr pic); -+ int FillVARefFramesFromDPB(const H264DPB& dpb, -+ VAPictureH264* va_pics, -+ int num_pics); -+ -+ VaapiWrapper* vaapi_wrapper_; -+ VaapiVideoDecodeAccelerator* vaapi_dec_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiH264Accelerator); -+}; -+ -+class VaapiVP8Picture : public VP8Picture { -+ public: -+ explicit VaapiVP8Picture( -+ scoped_refptr surface) -+ : dec_surface_(surface) {} -+ -+ VaapiVP8Picture* AsVaapiVP8Picture() override { return this; } -+ scoped_refptr dec_surface() { -+ return dec_surface_; -+ } -+ -+ private: -+ ~VaapiVP8Picture() override {} -+ -+ scoped_refptr dec_surface_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVP8Picture); -+}; -+ -+class VaapiVideoDecodeAccelerator::VaapiVP8Accelerator -+ : public VP8Decoder::VP8Accelerator { -+ public: -+ VaapiVP8Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, -+ VaapiWrapper* vaapi_wrapper); -+ ~VaapiVP8Accelerator() override; -+ -+ // VP8Decoder::VP8Accelerator implementation. -+ scoped_refptr CreateVP8Picture() override; -+ -+ bool SubmitDecode(const scoped_refptr& pic, -+ const Vp8FrameHeader* frame_hdr, -+ const scoped_refptr& last_frame, -+ const scoped_refptr& golden_frame, -+ const scoped_refptr& alt_frame) override; -+ -+ bool OutputPicture(const scoped_refptr& pic) override; -+ -+ private: -+ scoped_refptr VP8PictureToVaapiDecodeSurface( -+ const scoped_refptr& pic); -+ -+ VaapiWrapper* vaapi_wrapper_; -+ VaapiVideoDecodeAccelerator* vaapi_dec_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVP8Accelerator); -+}; -+ -+class VaapiVP9Picture : public VP9Picture { -+ public: -+ explicit VaapiVP9Picture( -+ scoped_refptr surface) -+ : dec_surface_(surface) {} -+ -+ VaapiVP9Picture* AsVaapiVP9Picture() override { return this; } -+ scoped_refptr dec_surface() { -+ return dec_surface_; -+ } -+ -+ private: -+ ~VaapiVP9Picture() override {} -+ -+ scoped_refptr dec_surface_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVP9Picture); -+}; -+ -+class VaapiVideoDecodeAccelerator::VaapiVP9Accelerator -+ : public VP9Decoder::VP9Accelerator { -+ public: -+ VaapiVP9Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, -+ VaapiWrapper* vaapi_wrapper); -+ ~VaapiVP9Accelerator() override; -+ -+ // VP9Decoder::VP9Accelerator implementation. -+ scoped_refptr CreateVP9Picture() override; -+ -+ bool SubmitDecode(const scoped_refptr& pic, -+ const Vp9SegmentationParams& seg, -+ const Vp9LoopFilterParams& lf, -+ const std::vector>& ref_pictures, -+ const base::Closure& done_cb) override; -+ -+ bool OutputPicture(const scoped_refptr& pic) override; -+ -+ bool IsFrameContextRequired() const override { return false; } -+ -+ bool GetFrameContext(const scoped_refptr& pic, -+ Vp9FrameContext* frame_ctx) override; -+ -+ private: -+ scoped_refptr VP9PictureToVaapiDecodeSurface( -+ const scoped_refptr& pic); -+ -+ VaapiWrapper* vaapi_wrapper_; -+ VaapiVideoDecodeAccelerator* vaapi_dec_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVP9Accelerator); -+}; -+ -+class VaapiVideoDecodeAccelerator::InputBuffer { -+ public: -+ InputBuffer() = default; -+ InputBuffer(uint32_t id, -+ std::unique_ptr shm, -+ base::OnceCallback release_cb) -+ : id_(id), shm_(std::move(shm)), release_cb_(std::move(release_cb)) {} -+ ~InputBuffer() { -+ VLOGF(4) << "id = " << id_; -+ if (release_cb_) -+ std::move(release_cb_).Run(id_); -+ } -+ -+ // Indicates this is a dummy buffer for flush request. -+ bool IsFlushRequest() const { return shm_ == nullptr; } -+ int32_t id() const { return id_; } -+ SharedMemoryRegion* shm() const { return shm_.get(); } -+ -+ private: -+ const int32_t id_ = -1; -+ const std::unique_ptr shm_; -+ base::OnceCallback release_cb_; -+ -+ DISALLOW_COPY_AND_ASSIGN(InputBuffer); -+}; -+ -+void VaapiVideoDecodeAccelerator::NotifyError(Error error) { -+ if (!task_runner_->BelongsToCurrentThread()) { -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ task_runner_->PostTask(FROM_HERE, -+ base::Bind(&VaapiVideoDecodeAccelerator::NotifyError, -+ weak_this_, error)); -+ return; -+ } -+ -+ // Post Cleanup() as a task so we don't recursively acquire lock_. -+ task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::Cleanup, weak_this_)); -+ -+ VLOGF(1) << "Notifying of error " << error; -+ if (client_) { -+ client_->NotifyError(error); -+ client_ptr_factory_.reset(); -+ } -+} -+ -+VaapiPicture* VaapiVideoDecodeAccelerator::PictureById( -+ int32_t picture_buffer_id) { -+ Pictures::iterator it = pictures_.find(picture_buffer_id); -+ if (it == pictures_.end()) { -+ VLOGF(4) << "Picture id " << picture_buffer_id << " does not exist"; -+ return NULL; -+ } -+ -+ return it->second.get(); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( -+ const MakeGLContextCurrentCallback& make_context_current_cb, -+ const BindGLImageCallback& bind_image_cb) -+ : state_(kUninitialized), -+ input_ready_(&lock_), -+ vaapi_picture_factory_(new VaapiPictureFactory()), -+ surfaces_available_(&lock_), -+ task_runner_(base::ThreadTaskRunnerHandle::Get()), -+ decoder_thread_("VaapiDecoderThread"), -+ num_frames_at_client_(0), -+ finish_flush_pending_(false), -+ awaiting_va_surfaces_recycle_(false), -+ requested_num_pics_(0), -+ output_format_(gfx::BufferFormat::BGRX_8888), -+ profile_(VIDEO_CODEC_PROFILE_UNKNOWN), -+ make_context_current_cb_(make_context_current_cb), -+ bind_image_cb_(bind_image_cb), -+ weak_this_factory_(this) { -+ weak_this_ = weak_this_factory_.GetWeakPtr(); -+ va_surface_release_cb_ = BindToCurrentLoop( -+ base::Bind(&VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_)); -+} -+ -+VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+} -+ -+bool VaapiVideoDecodeAccelerator::Initialize(const Config& config, -+ Client* client) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ if (config.is_encrypted()) { -+ NOTREACHED() << "Encrypted streams are not supported for this VDA"; -+ return false; -+ } -+ -+ switch (config.output_mode) { -+ case Config::OutputMode::ALLOCATE: -+ output_format_ = vaapi_picture_factory_->GetBufferFormatForAllocateMode(); -+ break; -+ -+ case Config::OutputMode::IMPORT: -+ output_format_ = vaapi_picture_factory_->GetBufferFormatForImportMode(); -+ break; -+ -+ default: -+ NOTREACHED() << "Only ALLOCATE and IMPORT OutputModes are supported"; -+ return false; -+ } -+ -+ client_ptr_factory_.reset(new base::WeakPtrFactory(client)); -+ client_ = client_ptr_factory_->GetWeakPtr(); -+ -+ VideoCodecProfile profile = config.profile; -+ -+ base::AutoLock auto_lock(lock_); -+ DCHECK_EQ(state_, kUninitialized); -+ VLOGF(2) << "Initializing VAVDA, profile: " << GetProfileName(profile); -+ -+ vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec( -+ VaapiWrapper::kDecode, profile, base::Bind(&ReportToUMA, VAAPI_ERROR)); -+ -+ if (!vaapi_wrapper_.get()) { -+ VLOGF(1) << "Failed initializing VAAPI for profile " -+ << GetProfileName(profile); -+ return false; -+ } -+ -+ if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { -+ h264_accelerator_.reset( -+ new VaapiH264Accelerator(this, vaapi_wrapper_.get())); -+ decoder_.reset(new H264Decoder(h264_accelerator_.get())); -+ } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { -+ vp8_accelerator_.reset(new VaapiVP8Accelerator(this, vaapi_wrapper_.get())); -+ decoder_.reset(new VP8Decoder(vp8_accelerator_.get())); -+ } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) { -+ vp9_accelerator_.reset(new VaapiVP9Accelerator(this, vaapi_wrapper_.get())); -+ decoder_.reset(new VP9Decoder(vp9_accelerator_.get())); -+ } else { -+ VLOGF(1) << "Unsupported profile " << GetProfileName(profile); -+ return false; -+ } -+ profile_ = profile; -+ -+ CHECK(decoder_thread_.Start()); -+ decoder_thread_task_runner_ = decoder_thread_.task_runner(); -+ -+ state_ = kIdle; -+ output_mode_ = config.output_mode; -+ return true; -+} -+ -+void VaapiVideoDecodeAccelerator::OutputPicture( -+ const scoped_refptr& va_surface, -+ int32_t input_id, -+ gfx::Rect visible_rect, -+ VaapiPicture* picture) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ int32_t output_id = picture->picture_buffer_id(); -+ -+ VLOGF(4) << "Outputting VASurface " << va_surface->id() -+ << " into pixmap bound to picture buffer id " << output_id; -+ { -+ TRACE_EVENT2("Video Decoder", "VAVDA::DownloadFromSurface", "input_id", -+ input_id, "output_id", output_id); -+ RETURN_AND_NOTIFY_ON_FAILURE(picture->DownloadFromSurface(va_surface), -+ "Failed putting surface into pixmap", -+ PLATFORM_FAILURE, ); -+ } -+ // Notify the client a picture is ready to be displayed. -+ ++num_frames_at_client_; -+ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -+ VLOGF(4) << "Notifying output picture id " << output_id << " for input " -+ << input_id -+ << " is ready. visible rect: " << visible_rect.ToString(); -+ if (client_) { -+ // TODO(hubbe): Use the correct color space. http://crbug.com/647725 -+ client_->PictureReady(Picture(output_id, input_id, visible_rect, -+ gfx::ColorSpace(), picture->AllowOverlay())); -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::TryOutputSurface() { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ // Handle Destroy() arriving while pictures are queued for output. -+ if (!client_) -+ return; -+ -+ if (pending_output_cbs_.empty() || output_buffers_.empty()) -+ return; -+ -+ OutputCB output_cb = pending_output_cbs_.front(); -+ pending_output_cbs_.pop(); -+ -+ VaapiPicture* picture = PictureById(output_buffers_.front()); -+ DCHECK(picture); -+ output_buffers_.pop(); -+ -+ output_cb.Run(picture); -+ -+ if (finish_flush_pending_ && pending_output_cbs_.empty()) -+ FinishFlush(); -+} -+ -+void VaapiVideoDecodeAccelerator::QueueInputBuffer( -+ const BitstreamBuffer& bitstream_buffer) { -+ VLOGF(4) << "Queueing new input buffer id: " << bitstream_buffer.id() -+ << " size: " << (int)bitstream_buffer.size(); -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ TRACE_EVENT1("Video Decoder", "QueueInputBuffer", "input_id", -+ bitstream_buffer.id()); -+ -+ base::AutoLock auto_lock(lock_); -+ if (bitstream_buffer.size() == 0) { -+ DCHECK(!base::SharedMemory::IsHandleValid(bitstream_buffer.handle())); -+ // Dummy buffer for flush. -+ auto flush_buffer = base::MakeUnique(); -+ DCHECK(flush_buffer->IsFlushRequest()); -+ input_buffers_.push(std::move(flush_buffer)); -+ } else { -+ std::unique_ptr shm( -+ new SharedMemoryRegion(bitstream_buffer, true)); -+ RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(), "Failed to map input buffer", -+ UNREADABLE_INPUT, ); -+ -+ auto input_buffer = base::MakeUnique( -+ bitstream_buffer.id(), std::move(shm), -+ BindToCurrentLoop( -+ base::Bind(&Client::NotifyEndOfBitstreamBuffer, client_))); -+ input_buffers_.push(std::move(input_buffer)); -+ -+ TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); -+ } -+ -+ input_ready_.Signal(); -+ -+ switch (state_) { -+ case kIdle: -+ state_ = kDecoding; -+ decoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ break; -+ -+ case kDecoding: -+ // Decoder already running. -+ break; -+ -+ case kResetting: -+ // When resetting, allow accumulating bitstream buffers, so that -+ // the client can queue after-seek-buffers while we are finishing with -+ // the before-seek one. -+ break; -+ -+ default: -+ VLOGF(1) << "Decode/Flush request from client in invalid state: " -+ << state_; -+ NotifyError(PLATFORM_FAILURE); -+ break; -+ } -+} -+ -+bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ lock_.AssertAcquired(); -+ -+ if (curr_input_buffer_.get()) -+ return true; -+ -+ // Will only wait if it is expected that in current state new buffers will -+ // be queued from the client via Decode(). The state can change during wait. -+ while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { -+ input_ready_.Wait(); -+ } -+ -+ // We could have got woken up in a different state or never got to sleep -+ // due to current state. -+ if (state_ != kDecoding && state_ != kIdle) -+ return false; -+ -+ DCHECK(!input_buffers_.empty()); -+ curr_input_buffer_ = std::move(input_buffers_.front()); -+ input_buffers_.pop(); -+ -+ if (curr_input_buffer_->IsFlushRequest()) { -+ VLOGF(4) << "New flush buffer"; -+ return true; -+ } -+ -+ VLOGF(4) << "New current input buffer, id: " << curr_input_buffer_->id() -+ << " size: " << curr_input_buffer_->shm()->size() << "B"; -+ decoder_->SetStream( -+ static_cast(curr_input_buffer_->shm()->memory()), -+ curr_input_buffer_->shm()->size()); -+ -+ return true; -+} -+ -+void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ lock_.AssertAcquired(); -+ DCHECK(curr_input_buffer_.get()); -+ curr_input_buffer_.reset(); -+ -+ TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); -+} -+ -+// TODO(posciak): refactor the whole class to remove sleeping in wait for -+// surfaces, and reschedule DecodeTask instead. -+bool VaapiVideoDecodeAccelerator::WaitForSurfaces_Locked() { -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ lock_.AssertAcquired(); -+ -+ while (available_va_surfaces_.empty() && -+ (state_ == kDecoding || state_ == kIdle)) { -+ surfaces_available_.Wait(); -+ } -+ -+ return state_ == kDecoding || state_ == kIdle; -+} -+ -+void VaapiVideoDecodeAccelerator::DecodeTask() { -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ base::AutoLock auto_lock(lock_); -+ -+ if (state_ != kDecoding) -+ return; -+ -+ // Main decode task. -+ VLOGF(4) << "Decode task"; -+ -+ // Try to decode what stream data is (still) in the decoder until we run out -+ // of it. -+ while (GetInputBuffer_Locked()) { -+ DCHECK(curr_input_buffer_.get()); -+ -+ if (curr_input_buffer_->IsFlushRequest()) { -+ FlushTask(); -+ break; -+ } -+ -+ AcceleratedVideoDecoder::DecodeResult res; -+ { -+ // We are OK releasing the lock here, as decoder never calls our methods -+ // directly and we will reacquire the lock before looking at state again. -+ // This is the main decode function of the decoder and while keeping -+ // the lock for its duration would be fine, it would defeat the purpose -+ // of having a separate decoder thread. -+ base::AutoUnlock auto_unlock(lock_); -+ TRACE_EVENT0("Video Decoder", "VAVDA::Decode"); -+ res = decoder_->Decode(); -+ } -+ -+ switch (res) { -+ case AcceleratedVideoDecoder::kAllocateNewSurfaces: -+ VLOGF(2) << "Decoder requesting a new set of surfaces"; -+ task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, -+ weak_this_, decoder_->GetRequiredNumOfPictures(), -+ decoder_->GetPicSize())); -+ // We'll get rescheduled once ProvidePictureBuffers() finishes. -+ return; -+ -+ case AcceleratedVideoDecoder::kRanOutOfStreamData: -+ ReturnCurrInputBuffer_Locked(); -+ break; -+ -+ case AcceleratedVideoDecoder::kRanOutOfSurfaces: -+ // No more output buffers in the decoder, try getting more or go to -+ // sleep waiting for them. -+ if (!WaitForSurfaces_Locked()) -+ return; -+ -+ break; -+ -+ case AcceleratedVideoDecoder::kNeedContextUpdate: -+ // This should not happen as we return false from -+ // IsFrameContextRequired(). -+ NOTREACHED() << "Context updates not supported"; -+ return; -+ -+ case AcceleratedVideoDecoder::kDecodeError: -+ RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", -+ PLATFORM_FAILURE, ); -+ return; -+ } -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, -+ gfx::Size size) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ DCHECK(!awaiting_va_surfaces_recycle_); -+ -+ // At this point decoder has stopped running and has already posted onto our -+ // loop any remaining output request callbacks, which executed before we got -+ // here. Some of them might have been pended though, because we might not -+ // have had enough TFPictures to output surfaces to. Initiate a wait cycle, -+ // which will wait for client to return enough PictureBuffers to us, so that -+ // we can finish all pending output callbacks, releasing associated surfaces. -+ VLOGF(2) << "Initiating surface set change"; -+ awaiting_va_surfaces_recycle_ = true; -+ -+ requested_num_pics_ = num_pics; -+ requested_pic_size_ = size; -+ -+ TryFinishSurfaceSetChange(); -+} -+ -+void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ if (!awaiting_va_surfaces_recycle_) -+ return; -+ -+ if (!pending_output_cbs_.empty() || -+ pictures_.size() != available_va_surfaces_.size()) { -+ // Either: -+ // 1. Not all pending pending output callbacks have been executed yet. -+ // Wait for the client to return enough pictures and retry later. -+ // 2. The above happened and all surface release callbacks have been posted -+ // as the result, but not all have executed yet. Post ourselves after them -+ // to let them release surfaces. -+ DVLOGF(2) << "Awaiting pending output/surface release callbacks to finish"; -+ task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, -+ weak_this_)); -+ return; -+ } -+ -+ // All surfaces released, destroy them and dismiss all PictureBuffers. -+ awaiting_va_surfaces_recycle_ = false; -+ available_va_surfaces_.clear(); -+ vaapi_wrapper_->DestroySurfaces(); -+ -+ for (Pictures::iterator iter = pictures_.begin(); iter != pictures_.end(); -+ ++iter) { -+ VLOGF(2) << "Dismissing picture id: " << iter->first; -+ if (client_) -+ client_->DismissPictureBuffer(iter->first); -+ } -+ pictures_.clear(); -+ -+ // And ask for a new set as requested. -+ VLOGF(2) << "Requesting " << requested_num_pics_ -+ << " pictures of size: " << requested_pic_size_.ToString(); -+ -+ VideoPixelFormat format = GfxBufferFormatToVideoPixelFormat(output_format_); -+ task_runner_->PostTask( -+ FROM_HERE, base::Bind(&Client::ProvidePictureBuffers, client_, -+ requested_num_pics_, format, 1, requested_pic_size_, -+ vaapi_picture_factory_->GetGLTextureTarget())); -+} -+ -+void VaapiVideoDecodeAccelerator::Decode( -+ const BitstreamBuffer& bitstream_buffer) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", -+ bitstream_buffer.id()); -+ -+ if (bitstream_buffer.id() < 0) { -+ if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) -+ base::SharedMemory::CloseHandle(bitstream_buffer.handle()); -+ VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); -+ NotifyError(INVALID_ARGUMENT); -+ return; -+ } -+ -+ // Skip empty buffers. VaapiVDA uses empty buffer as dummy buffer for flush -+ // internally. -+ if (bitstream_buffer.size() == 0) { -+ if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) -+ base::SharedMemory::CloseHandle(bitstream_buffer.handle()); -+ if (client_) -+ client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id()); -+ return; -+ } -+ -+ QueueInputBuffer(bitstream_buffer); -+} -+ -+void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( -+ VASurfaceID va_surface_id) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ base::AutoLock auto_lock(lock_); -+ -+ available_va_surfaces_.push_back(va_surface_id); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::AssignPictureBuffers( -+ const std::vector& buffers) { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ base::AutoLock auto_lock(lock_); -+ DCHECK(pictures_.empty()); -+ -+ while (!output_buffers_.empty()) -+ output_buffers_.pop(); -+ -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ buffers.size() >= requested_num_pics_, -+ "Got an invalid number of picture buffers. (Got " << buffers.size() -+ << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); -+ DCHECK(requested_pic_size_ == buffers[0].size()); -+ -+ const unsigned int va_format = GetVaFormatForVideoCodecProfile(profile_); -+ std::vector va_surface_ids; -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ vaapi_wrapper_->CreateSurfaces(va_format, requested_pic_size_, -+ buffers.size(), &va_surface_ids), -+ "Failed creating VA Surfaces", PLATFORM_FAILURE, ); -+ DCHECK_EQ(va_surface_ids.size(), buffers.size()); -+ -+ for (size_t i = 0; i < buffers.size(); ++i) { -+ uint32_t client_id = !buffers[i].client_texture_ids().empty() -+ ? buffers[i].client_texture_ids()[0] -+ : 0; -+ uint32_t service_id = !buffers[i].service_texture_ids().empty() -+ ? buffers[i].service_texture_ids()[0] -+ : 0; -+ -+ std::unique_ptr picture(vaapi_picture_factory_->Create( -+ vaapi_wrapper_, make_context_current_cb_, bind_image_cb_, -+ buffers[i].id(), requested_pic_size_, service_id, client_id)); -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ picture.get(), "Failed creating a VaapiPicture", PLATFORM_FAILURE, ); -+ -+ if (output_mode_ == Config::OutputMode::ALLOCATE) { -+ RETURN_AND_NOTIFY_ON_FAILURE( -+ picture->Allocate(output_format_), -+ "Failed to allocate memory for a VaapiPicture", PLATFORM_FAILURE, ); -+ output_buffers_.push(buffers[i].id()); -+ } -+ bool inserted = -+ pictures_.insert(std::make_pair(buffers[i].id(), std::move(picture))) -+ .second; -+ DCHECK(inserted); -+ -+ available_va_surfaces_.push_back(va_surface_ids[i]); -+ surfaces_available_.Signal(); -+ } -+ -+ // Resume DecodeTask if it is still in decoding state. -+ if (state_ == kDecoding) { -+ decoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ } -+} -+ -+#if defined(USE_OZONE) -+static void CloseGpuMemoryBufferHandle( -+ const gfx::GpuMemoryBufferHandle& handle) { -+ for (const auto& fd : handle.native_pixmap_handle.fds) { -+ // Close the fd by wrapping it in a ScopedFD and letting -+ // it fall out of scope. -+ base::ScopedFD scoped_fd(fd.fd); -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::ImportBufferForPicture( -+ int32_t picture_buffer_id, -+ const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) { -+ VLOGF(2) << "Importing picture id: " << picture_buffer_id; -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ if (output_mode_ != Config::OutputMode::IMPORT) { -+ CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); -+ VLOGF(1) << "Cannot import in non-import mode"; -+ NotifyError(INVALID_ARGUMENT); -+ return; -+ } -+ -+ VaapiPicture* picture = PictureById(picture_buffer_id); -+ if (!picture) { -+ CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); -+ -+ // It's possible that we've already posted a DismissPictureBuffer for this -+ // picture, but it has not yet executed when this ImportBufferForPicture -+ // was posted to us by the client. In that case just ignore this (we've -+ // already dismissed it and accounted for that). -+ VLOGF(3) << "got picture id=" << picture_buffer_id -+ << " not in use (anymore?)."; -+ return; -+ } -+ -+ if (!picture->ImportGpuMemoryBufferHandle(output_format_, -+ gpu_memory_buffer_handle)) { -+ // ImportGpuMemoryBufferHandle will close the handles even on failure, so -+ // we don't need to do this ourselves. -+ VLOGF(1) << "Failed to import GpuMemoryBufferHandle"; -+ NotifyError(PLATFORM_FAILURE); -+ return; -+ } -+ -+ ReusePictureBuffer(picture_buffer_id); -+} -+#endif -+ -+void VaapiVideoDecodeAccelerator::ReusePictureBuffer( -+ int32_t picture_buffer_id) { -+ VLOGF(4) << "picture id=" << picture_buffer_id; -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", -+ picture_buffer_id); -+ -+ if (!PictureById(picture_buffer_id)) { -+ // It's possible that we've already posted a DismissPictureBuffer for this -+ // picture, but it has not yet executed when this ReusePictureBuffer -+ // was posted to us by the client. In that case just ignore this (we've -+ // already dismissed it and accounted for that). -+ VLOGF(3) << "got picture id=" << picture_buffer_id -+ << " not in use (anymore?)."; -+ return; -+ } -+ -+ --num_frames_at_client_; -+ TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -+ -+ output_buffers_.push(picture_buffer_id); -+ TryOutputSurface(); -+} -+ -+void VaapiVideoDecodeAccelerator::FlushTask() { -+ VLOGF(2); -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ DCHECK(curr_input_buffer_.get() && curr_input_buffer_->IsFlushRequest()); -+ -+ curr_input_buffer_.reset(); -+ -+ // First flush all the pictures that haven't been outputted, notifying the -+ // client to output them. -+ bool res = decoder_->Flush(); -+ RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", -+ PLATFORM_FAILURE, ); -+ -+ // Put the decoder in idle state, ready to resume. -+ decoder_->Reset(); -+ -+ task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); -+} -+ -+void VaapiVideoDecodeAccelerator::Flush() { -+ VLOGF(2) << "Got flush request"; -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ // Queue a dummy buffer, which means flush. -+ QueueInputBuffer(media::BitstreamBuffer()); -+} -+ -+void VaapiVideoDecodeAccelerator::FinishFlush() { -+ VLOGF(2); -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ finish_flush_pending_ = false; -+ -+ base::AutoLock auto_lock(lock_); -+ if (state_ != kDecoding) { -+ DCHECK(state_ == kDestroying || state_ == kResetting) << state_; -+ return; -+ } -+ -+ // Still waiting for textures from client to finish outputting all pending -+ // frames. Try again later. -+ if (!pending_output_cbs_.empty()) { -+ finish_flush_pending_ = true; -+ return; -+ } -+ -+ // Resume decoding if necessary. -+ if (input_buffers_.empty()) { -+ state_ = kIdle; -+ } else { -+ decoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ } -+ -+ task_runner_->PostTask(FROM_HERE, -+ base::Bind(&Client::NotifyFlushDone, client_)); -+} -+ -+void VaapiVideoDecodeAccelerator::ResetTask() { -+ VLOGF(2); -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ -+ // All the decoding tasks from before the reset request from client are done -+ // by now, as this task was scheduled after them and client is expected not -+ // to call Decode() after Reset() and before NotifyResetDone. -+ decoder_->Reset(); -+ -+ base::AutoLock auto_lock(lock_); -+ -+ // Return current input buffer, if present. -+ if (curr_input_buffer_.get()) -+ ReturnCurrInputBuffer_Locked(); -+ -+ // And let client know that we are done with reset. -+ task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -+} -+ -+void VaapiVideoDecodeAccelerator::Reset() { -+ VLOGF(2) << "Got reset request"; -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ // This will make any new decode tasks exit early. -+ base::AutoLock auto_lock(lock_); -+ state_ = kResetting; -+ finish_flush_pending_ = false; -+ -+ // Drop all remaining input buffers, if present. -+ while (!input_buffers_.empty()) -+ input_buffers_.pop(); -+ TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); -+ -+ decoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, -+ base::Unretained(this))); -+ -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+} -+ -+void VaapiVideoDecodeAccelerator::FinishReset() { -+ VLOGF(2); -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ base::AutoLock auto_lock(lock_); -+ -+ if (state_ != kResetting) { -+ DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; -+ return; // We could've gotten destroyed already. -+ } -+ -+ // Drop pending outputs. -+ while (!pending_output_cbs_.empty()) -+ pending_output_cbs_.pop(); -+ -+ if (awaiting_va_surfaces_recycle_) { -+ // Decoder requested a new surface set while we were waiting for it to -+ // finish the last DecodeTask, running at the time of Reset(). -+ // Let the surface set change finish first before resetting. -+ task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -+ return; -+ } -+ -+ state_ = kIdle; -+ -+ task_runner_->PostTask(FROM_HERE, -+ base::Bind(&Client::NotifyResetDone, client_)); -+ -+ // The client might have given us new buffers via Decode() while we were -+ // resetting and might be waiting for our move, and not call Decode() anymore -+ // until we return something. Post a DecodeTask() so that we won't -+ // sleep forever waiting for Decode() in that case. Having two of them -+ // in the pipe is harmless, the additional one will return as soon as it sees -+ // that we are back in kDecoding state. -+ if (!input_buffers_.empty()) { -+ state_ = kDecoding; -+ decoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -+ base::Unretained(this))); -+ } -+} -+ -+void VaapiVideoDecodeAccelerator::Cleanup() { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ -+ base::AutoLock auto_lock(lock_); -+ if (state_ == kUninitialized || state_ == kDestroying) -+ return; -+ -+ VLOGF(2) << "Destroying VAVDA"; -+ state_ = kDestroying; -+ -+ client_ptr_factory_.reset(); -+ weak_this_factory_.InvalidateWeakPtrs(); -+ -+ // Signal all potential waiters on the decoder_thread_, let them early-exit, -+ // as we've just moved to the kDestroying state, and wait for all tasks -+ // to finish. -+ input_ready_.Signal(); -+ surfaces_available_.Signal(); -+ { -+ base::AutoUnlock auto_unlock(lock_); -+ decoder_thread_.Stop(); -+ } -+ -+ state_ = kUninitialized; -+} -+ -+void VaapiVideoDecodeAccelerator::Destroy() { -+ DCHECK(task_runner_->BelongsToCurrentThread()); -+ Cleanup(); -+ delete this; -+} -+ -+bool VaapiVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( -+ const base::WeakPtr& decode_client, -+ const scoped_refptr& decode_task_runner) { -+ return false; -+} -+ -+bool VaapiVideoDecodeAccelerator::DecodeSurface( -+ const scoped_refptr& dec_surface) { -+ const bool result = vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( -+ dec_surface->va_surface()->id()); -+ if (!result) -+ VLOGF(1) << "Failed decoding picture"; -+ return result; -+} -+ -+void VaapiVideoDecodeAccelerator::SurfaceReady( -+ const scoped_refptr& dec_surface) { -+ if (!task_runner_->BelongsToCurrentThread()) { -+ task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::SurfaceReady, -+ weak_this_, dec_surface)); -+ return; -+ } -+ -+ DCHECK(!awaiting_va_surfaces_recycle_); -+ -+ { -+ base::AutoLock auto_lock(lock_); -+ // Drop any requests to output if we are resetting or being destroyed. -+ if (state_ == kResetting || state_ == kDestroying) -+ return; -+ } -+ -+ pending_output_cbs_.push( -+ base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, weak_this_, -+ dec_surface->va_surface(), dec_surface->bitstream_id(), -+ dec_surface->visible_rect())); -+ -+ TryOutputSurface(); -+} -+ -+scoped_refptr -+VaapiVideoDecodeAccelerator::CreateSurface() { -+ DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -+ base::AutoLock auto_lock(lock_); -+ -+ if (available_va_surfaces_.empty()) -+ return nullptr; -+ -+ DCHECK(!awaiting_va_surfaces_recycle_); -+ scoped_refptr va_surface(new VASurface( -+ available_va_surfaces_.front(), requested_pic_size_, -+ vaapi_wrapper_->va_surface_format(), va_surface_release_cb_)); -+ available_va_surfaces_.pop_front(); -+ -+ return new VaapiDecodeSurface(curr_input_buffer_->id(), va_surface); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiH264Accelerator::VaapiH264Accelerator( -+ VaapiVideoDecodeAccelerator* vaapi_dec, -+ VaapiWrapper* vaapi_wrapper) -+ : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { -+ DCHECK(vaapi_wrapper_); -+ DCHECK(vaapi_dec_); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiH264Accelerator::~VaapiH264Accelerator() {} -+ -+scoped_refptr -+VaapiVideoDecodeAccelerator::VaapiH264Accelerator::CreateH264Picture() { -+ scoped_refptr va_surface = vaapi_dec_->CreateSurface(); -+ if (!va_surface) -+ return nullptr; -+ -+ return new VaapiH264Picture(std::move(va_surface)); -+} -+ -+// Fill |va_pic| with default/neutral values. -+static void InitVAPicture(VAPictureH264* va_pic) { -+ memset(va_pic, 0, sizeof(*va_pic)); -+ va_pic->picture_id = VA_INVALID_ID; -+ va_pic->flags = VA_PICTURE_H264_INVALID; -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitFrameMetadata( -+ const H264SPS* sps, -+ const H264PPS* pps, -+ const H264DPB& dpb, -+ const H264Picture::Vector& ref_pic_listp0, -+ const H264Picture::Vector& ref_pic_listb0, -+ const H264Picture::Vector& ref_pic_listb1, -+ const scoped_refptr& pic) { -+ VAPictureParameterBufferH264 pic_param; -+ memset(&pic_param, 0, sizeof(pic_param)); -+ -+#define FROM_SPS_TO_PP(a) pic_param.a = sps->a -+#define FROM_SPS_TO_PP2(a, b) pic_param.b = sps->a -+ FROM_SPS_TO_PP2(pic_width_in_mbs_minus1, picture_width_in_mbs_minus1); -+ // This assumes non-interlaced video -+ FROM_SPS_TO_PP2(pic_height_in_map_units_minus1, picture_height_in_mbs_minus1); -+ FROM_SPS_TO_PP(bit_depth_luma_minus8); -+ FROM_SPS_TO_PP(bit_depth_chroma_minus8); -+#undef FROM_SPS_TO_PP -+#undef FROM_SPS_TO_PP2 -+ -+#define FROM_SPS_TO_PP_SF(a) pic_param.seq_fields.bits.a = sps->a -+#define FROM_SPS_TO_PP_SF2(a, b) pic_param.seq_fields.bits.b = sps->a -+ FROM_SPS_TO_PP_SF(chroma_format_idc); -+ FROM_SPS_TO_PP_SF2(separate_colour_plane_flag, -+ residual_colour_transform_flag); -+ FROM_SPS_TO_PP_SF(gaps_in_frame_num_value_allowed_flag); -+ FROM_SPS_TO_PP_SF(frame_mbs_only_flag); -+ FROM_SPS_TO_PP_SF(mb_adaptive_frame_field_flag); -+ FROM_SPS_TO_PP_SF(direct_8x8_inference_flag); -+ pic_param.seq_fields.bits.MinLumaBiPredSize8x8 = (sps->level_idc >= 31); -+ FROM_SPS_TO_PP_SF(log2_max_frame_num_minus4); -+ FROM_SPS_TO_PP_SF(pic_order_cnt_type); -+ FROM_SPS_TO_PP_SF(log2_max_pic_order_cnt_lsb_minus4); -+ FROM_SPS_TO_PP_SF(delta_pic_order_always_zero_flag); -+#undef FROM_SPS_TO_PP_SF -+#undef FROM_SPS_TO_PP_SF2 -+ -+#define FROM_PPS_TO_PP(a) pic_param.a = pps->a -+ FROM_PPS_TO_PP(pic_init_qp_minus26); -+ FROM_PPS_TO_PP(pic_init_qs_minus26); -+ FROM_PPS_TO_PP(chroma_qp_index_offset); -+ FROM_PPS_TO_PP(second_chroma_qp_index_offset); -+#undef FROM_PPS_TO_PP -+ -+#define FROM_PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = pps->a -+#define FROM_PPS_TO_PP_PF2(a, b) pic_param.pic_fields.bits.b = pps->a -+ FROM_PPS_TO_PP_PF(entropy_coding_mode_flag); -+ FROM_PPS_TO_PP_PF(weighted_pred_flag); -+ FROM_PPS_TO_PP_PF(weighted_bipred_idc); -+ FROM_PPS_TO_PP_PF(transform_8x8_mode_flag); -+ -+ pic_param.pic_fields.bits.field_pic_flag = 0; -+ FROM_PPS_TO_PP_PF(constrained_intra_pred_flag); -+ FROM_PPS_TO_PP_PF2(bottom_field_pic_order_in_frame_present_flag, -+ pic_order_present_flag); -+ FROM_PPS_TO_PP_PF(deblocking_filter_control_present_flag); -+ FROM_PPS_TO_PP_PF(redundant_pic_cnt_present_flag); -+ pic_param.pic_fields.bits.reference_pic_flag = pic->ref; -+#undef FROM_PPS_TO_PP_PF -+#undef FROM_PPS_TO_PP_PF2 -+ -+ pic_param.frame_num = pic->frame_num; -+ -+ InitVAPicture(&pic_param.CurrPic); -+ FillVAPicture(&pic_param.CurrPic, pic); -+ -+ // Init reference pictures' array. -+ for (int i = 0; i < 16; ++i) -+ InitVAPicture(&pic_param.ReferenceFrames[i]); -+ -+ // And fill it with picture info from DPB. -+ FillVARefFramesFromDPB(dpb, pic_param.ReferenceFrames, -+ arraysize(pic_param.ReferenceFrames)); -+ -+ pic_param.num_ref_frames = sps->max_num_ref_frames; -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, -+ sizeof(pic_param), &pic_param)) -+ return false; -+ -+ VAIQMatrixBufferH264 iq_matrix_buf; -+ memset(&iq_matrix_buf, 0, sizeof(iq_matrix_buf)); -+ -+ if (pps->pic_scaling_matrix_present_flag) { -+ for (int i = 0; i < 6; ++i) { -+ for (int j = 0; j < 16; ++j) -+ iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = -+ pps->scaling_list4x4[i][j]; -+ } -+ -+ for (int i = 0; i < 2; ++i) { -+ for (int j = 0; j < 64; ++j) -+ iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = -+ pps->scaling_list8x8[i][j]; -+ } -+ } else { -+ for (int i = 0; i < 6; ++i) { -+ for (int j = 0; j < 16; ++j) -+ iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = -+ sps->scaling_list4x4[i][j]; -+ } -+ -+ for (int i = 0; i < 2; ++i) { -+ for (int j = 0; j < 64; ++j) -+ iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = -+ sps->scaling_list8x8[i][j]; -+ } -+ } -+ -+ return vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, -+ sizeof(iq_matrix_buf), &iq_matrix_buf); -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitSlice( -+ const H264PPS* pps, -+ const H264SliceHeader* slice_hdr, -+ const H264Picture::Vector& ref_pic_list0, -+ const H264Picture::Vector& ref_pic_list1, -+ const scoped_refptr& pic, -+ const uint8_t* data, -+ size_t size) { -+ VASliceParameterBufferH264 slice_param; -+ memset(&slice_param, 0, sizeof(slice_param)); -+ -+ slice_param.slice_data_size = slice_hdr->nalu_size; -+ slice_param.slice_data_offset = 0; -+ slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -+ slice_param.slice_data_bit_offset = slice_hdr->header_bit_size; -+ -+#define SHDRToSP(a) slice_param.a = slice_hdr->a -+ SHDRToSP(first_mb_in_slice); -+ slice_param.slice_type = slice_hdr->slice_type % 5; -+ SHDRToSP(direct_spatial_mv_pred_flag); -+ -+ // TODO posciak: make sure parser sets those even when override flags -+ // in slice header is off. -+ SHDRToSP(num_ref_idx_l0_active_minus1); -+ SHDRToSP(num_ref_idx_l1_active_minus1); -+ SHDRToSP(cabac_init_idc); -+ SHDRToSP(slice_qp_delta); -+ SHDRToSP(disable_deblocking_filter_idc); -+ SHDRToSP(slice_alpha_c0_offset_div2); -+ SHDRToSP(slice_beta_offset_div2); -+ -+ if (((slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) && -+ pps->weighted_pred_flag) || -+ (slice_hdr->IsBSlice() && pps->weighted_bipred_idc == 1)) { -+ SHDRToSP(luma_log2_weight_denom); -+ SHDRToSP(chroma_log2_weight_denom); -+ -+ SHDRToSP(luma_weight_l0_flag); -+ SHDRToSP(luma_weight_l1_flag); -+ -+ SHDRToSP(chroma_weight_l0_flag); -+ SHDRToSP(chroma_weight_l1_flag); -+ -+ for (int i = 0; i <= slice_param.num_ref_idx_l0_active_minus1; ++i) { -+ slice_param.luma_weight_l0[i] = -+ slice_hdr->pred_weight_table_l0.luma_weight[i]; -+ slice_param.luma_offset_l0[i] = -+ slice_hdr->pred_weight_table_l0.luma_offset[i]; -+ -+ for (int j = 0; j < 2; ++j) { -+ slice_param.chroma_weight_l0[i][j] = -+ slice_hdr->pred_weight_table_l0.chroma_weight[i][j]; -+ slice_param.chroma_offset_l0[i][j] = -+ slice_hdr->pred_weight_table_l0.chroma_offset[i][j]; -+ } -+ } -+ -+ if (slice_hdr->IsBSlice()) { -+ for (int i = 0; i <= slice_param.num_ref_idx_l1_active_minus1; ++i) { -+ slice_param.luma_weight_l1[i] = -+ slice_hdr->pred_weight_table_l1.luma_weight[i]; -+ slice_param.luma_offset_l1[i] = -+ slice_hdr->pred_weight_table_l1.luma_offset[i]; -+ -+ for (int j = 0; j < 2; ++j) { -+ slice_param.chroma_weight_l1[i][j] = -+ slice_hdr->pred_weight_table_l1.chroma_weight[i][j]; -+ slice_param.chroma_offset_l1[i][j] = -+ slice_hdr->pred_weight_table_l1.chroma_offset[i][j]; -+ } -+ } -+ } -+ } -+ -+ static_assert( -+ arraysize(slice_param.RefPicList0) == arraysize(slice_param.RefPicList1), -+ "Invalid RefPicList sizes"); -+ -+ for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) { -+ InitVAPicture(&slice_param.RefPicList0[i]); -+ InitVAPicture(&slice_param.RefPicList1[i]); -+ } -+ -+ for (size_t i = 0; -+ i < ref_pic_list0.size() && i < arraysize(slice_param.RefPicList0); -+ ++i) { -+ if (ref_pic_list0[i]) -+ FillVAPicture(&slice_param.RefPicList0[i], ref_pic_list0[i]); -+ } -+ for (size_t i = 0; -+ i < ref_pic_list1.size() && i < arraysize(slice_param.RefPicList1); -+ ++i) { -+ if (ref_pic_list1[i]) -+ FillVAPicture(&slice_param.RefPicList1[i], ref_pic_list1[i]); -+ } -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, -+ sizeof(slice_param), &slice_param)) -+ return false; -+ -+ // Can't help it, blame libva... -+ void* non_const_ptr = const_cast(data); -+ return vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, size, -+ non_const_ptr); -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitDecode( -+ const scoped_refptr& pic) { -+ VLOGF(4) << "Decoding POC " << pic->pic_order_cnt; -+ scoped_refptr dec_surface = -+ H264PictureToVaapiDecodeSurface(pic); -+ -+ return vaapi_dec_->DecodeSurface(dec_surface); -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::OutputPicture( -+ const scoped_refptr& pic) { -+ scoped_refptr dec_surface = -+ H264PictureToVaapiDecodeSurface(pic); -+ dec_surface->set_visible_rect(pic->visible_rect); -+ vaapi_dec_->SurfaceReady(dec_surface); -+ -+ return true; -+} -+ -+void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::Reset() { -+ vaapi_wrapper_->DestroyPendingBuffers(); -+} -+ -+scoped_refptr -+VaapiVideoDecodeAccelerator::VaapiH264Accelerator:: -+ H264PictureToVaapiDecodeSurface(const scoped_refptr& pic) { -+ VaapiH264Picture* vaapi_pic = pic->AsVaapiH264Picture(); -+ CHECK(vaapi_pic); -+ return vaapi_pic->dec_surface(); -+} -+ -+void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVAPicture( -+ VAPictureH264* va_pic, -+ scoped_refptr pic) { -+ VASurfaceID va_surface_id = VA_INVALID_SURFACE; -+ -+ if (!pic->nonexisting) { -+ scoped_refptr dec_surface = -+ H264PictureToVaapiDecodeSurface(pic); -+ va_surface_id = dec_surface->va_surface()->id(); -+ } -+ -+ va_pic->picture_id = va_surface_id; -+ va_pic->frame_idx = pic->frame_num; -+ va_pic->flags = 0; -+ -+ switch (pic->field) { -+ case H264Picture::FIELD_NONE: -+ break; -+ case H264Picture::FIELD_TOP: -+ va_pic->flags |= VA_PICTURE_H264_TOP_FIELD; -+ break; -+ case H264Picture::FIELD_BOTTOM: -+ va_pic->flags |= VA_PICTURE_H264_BOTTOM_FIELD; -+ break; -+ } -+ -+ if (pic->ref) { -+ va_pic->flags |= pic->long_term ? VA_PICTURE_H264_LONG_TERM_REFERENCE -+ : VA_PICTURE_H264_SHORT_TERM_REFERENCE; -+ } -+ -+ va_pic->TopFieldOrderCnt = pic->top_field_order_cnt; -+ va_pic->BottomFieldOrderCnt = pic->bottom_field_order_cnt; -+} -+ -+int VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVARefFramesFromDPB( -+ const H264DPB& dpb, -+ VAPictureH264* va_pics, -+ int num_pics) { -+ H264Picture::Vector::const_reverse_iterator rit; -+ int i; -+ -+ // Return reference frames in reverse order of insertion. -+ // Libva does not document this, but other implementations (e.g. mplayer) -+ // do it this way as well. -+ for (rit = dpb.rbegin(), i = 0; rit != dpb.rend() && i < num_pics; ++rit) { -+ if ((*rit)->ref) -+ FillVAPicture(&va_pics[i++], *rit); -+ } -+ -+ return i; -+} -+ -+VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::VaapiVP8Accelerator( -+ VaapiVideoDecodeAccelerator* vaapi_dec, -+ VaapiWrapper* vaapi_wrapper) -+ : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { -+ DCHECK(vaapi_wrapper_); -+ DCHECK(vaapi_dec_); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::~VaapiVP8Accelerator() {} -+ -+scoped_refptr -+VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::CreateVP8Picture() { -+ scoped_refptr va_surface = vaapi_dec_->CreateSurface(); -+ if (!va_surface) -+ return nullptr; -+ -+ return new VaapiVP8Picture(std::move(va_surface)); -+} -+ -+#define ARRAY_MEMCPY_CHECKED(to, from) \ -+ do { \ -+ static_assert(sizeof(to) == sizeof(from), \ -+ #from " and " #to " arrays must be of same size"); \ -+ memcpy(to, from, sizeof(to)); \ -+ } while (0) -+ -+bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::SubmitDecode( -+ const scoped_refptr& pic, -+ const Vp8FrameHeader* frame_hdr, -+ const scoped_refptr& last_frame, -+ const scoped_refptr& golden_frame, -+ const scoped_refptr& alt_frame) { -+ VAIQMatrixBufferVP8 iq_matrix_buf; -+ memset(&iq_matrix_buf, 0, sizeof(VAIQMatrixBufferVP8)); -+ -+ const Vp8SegmentationHeader& sgmnt_hdr = frame_hdr->segmentation_hdr; -+ const Vp8QuantizationHeader& quant_hdr = frame_hdr->quantization_hdr; -+ static_assert(arraysize(iq_matrix_buf.quantization_index) == kMaxMBSegments, -+ "incorrect quantization matrix size"); -+ for (size_t i = 0; i < kMaxMBSegments; ++i) { -+ int q = quant_hdr.y_ac_qi; -+ -+ if (sgmnt_hdr.segmentation_enabled) { -+ if (sgmnt_hdr.segment_feature_mode == -+ Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) -+ q = sgmnt_hdr.quantizer_update_value[i]; -+ else -+ q += sgmnt_hdr.quantizer_update_value[i]; -+ } -+ -+#define CLAMP_Q(q) std::min(std::max(q, 0), 127) -+ static_assert(arraysize(iq_matrix_buf.quantization_index[i]) == 6, -+ "incorrect quantization matrix size"); -+ iq_matrix_buf.quantization_index[i][0] = CLAMP_Q(q); -+ iq_matrix_buf.quantization_index[i][1] = CLAMP_Q(q + quant_hdr.y_dc_delta); -+ iq_matrix_buf.quantization_index[i][2] = CLAMP_Q(q + quant_hdr.y2_dc_delta); -+ iq_matrix_buf.quantization_index[i][3] = CLAMP_Q(q + quant_hdr.y2_ac_delta); -+ iq_matrix_buf.quantization_index[i][4] = CLAMP_Q(q + quant_hdr.uv_dc_delta); -+ iq_matrix_buf.quantization_index[i][5] = CLAMP_Q(q + quant_hdr.uv_ac_delta); -+#undef CLAMP_Q -+ } -+ -+ if (!vaapi_wrapper_->SubmitBuffer( -+ VAIQMatrixBufferType, sizeof(VAIQMatrixBufferVP8), &iq_matrix_buf)) -+ return false; -+ -+ VAProbabilityDataBufferVP8 prob_buf; -+ memset(&prob_buf, 0, sizeof(VAProbabilityDataBufferVP8)); -+ -+ const Vp8EntropyHeader& entr_hdr = frame_hdr->entropy_hdr; -+ ARRAY_MEMCPY_CHECKED(prob_buf.dct_coeff_probs, entr_hdr.coeff_probs); -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAProbabilityBufferType, -+ sizeof(VAProbabilityDataBufferVP8), -+ &prob_buf)) -+ return false; -+ -+ VAPictureParameterBufferVP8 pic_param; -+ memset(&pic_param, 0, sizeof(VAPictureParameterBufferVP8)); -+ pic_param.frame_width = frame_hdr->width; -+ pic_param.frame_height = frame_hdr->height; -+ -+ if (last_frame) { -+ scoped_refptr last_frame_surface = -+ VP8PictureToVaapiDecodeSurface(last_frame); -+ pic_param.last_ref_frame = last_frame_surface->va_surface()->id(); -+ } else { -+ pic_param.last_ref_frame = VA_INVALID_SURFACE; -+ } -+ -+ if (golden_frame) { -+ scoped_refptr golden_frame_surface = -+ VP8PictureToVaapiDecodeSurface(golden_frame); -+ pic_param.golden_ref_frame = golden_frame_surface->va_surface()->id(); -+ } else { -+ pic_param.golden_ref_frame = VA_INVALID_SURFACE; -+ } -+ -+ if (alt_frame) { -+ scoped_refptr alt_frame_surface = -+ VP8PictureToVaapiDecodeSurface(alt_frame); -+ pic_param.alt_ref_frame = alt_frame_surface->va_surface()->id(); -+ } else { -+ pic_param.alt_ref_frame = VA_INVALID_SURFACE; -+ } -+ -+ pic_param.out_of_loop_frame = VA_INVALID_SURFACE; -+ -+ const Vp8LoopFilterHeader& lf_hdr = frame_hdr->loopfilter_hdr; -+ -+#define FHDR_TO_PP_PF(a, b) pic_param.pic_fields.bits.a = (b) -+ FHDR_TO_PP_PF(key_frame, frame_hdr->IsKeyframe() ? 0 : 1); -+ FHDR_TO_PP_PF(version, frame_hdr->version); -+ FHDR_TO_PP_PF(segmentation_enabled, sgmnt_hdr.segmentation_enabled); -+ FHDR_TO_PP_PF(update_mb_segmentation_map, -+ sgmnt_hdr.update_mb_segmentation_map); -+ FHDR_TO_PP_PF(update_segment_feature_data, -+ sgmnt_hdr.update_segment_feature_data); -+ FHDR_TO_PP_PF(filter_type, lf_hdr.type); -+ FHDR_TO_PP_PF(sharpness_level, lf_hdr.sharpness_level); -+ FHDR_TO_PP_PF(loop_filter_adj_enable, lf_hdr.loop_filter_adj_enable); -+ FHDR_TO_PP_PF(mode_ref_lf_delta_update, lf_hdr.mode_ref_lf_delta_update); -+ FHDR_TO_PP_PF(sign_bias_golden, frame_hdr->sign_bias_golden); -+ FHDR_TO_PP_PF(sign_bias_alternate, frame_hdr->sign_bias_alternate); -+ FHDR_TO_PP_PF(mb_no_coeff_skip, frame_hdr->mb_no_skip_coeff); -+ FHDR_TO_PP_PF(loop_filter_disable, lf_hdr.level == 0); -+#undef FHDR_TO_PP_PF -+ -+ ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, sgmnt_hdr.segment_prob); -+ -+ static_assert(arraysize(sgmnt_hdr.lf_update_value) == -+ arraysize(pic_param.loop_filter_level), -+ "loop filter level arrays mismatch"); -+ for (size_t i = 0; i < arraysize(sgmnt_hdr.lf_update_value); ++i) { -+ int lf_level = lf_hdr.level; -+ if (sgmnt_hdr.segmentation_enabled) { -+ if (sgmnt_hdr.segment_feature_mode == -+ Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) -+ lf_level = sgmnt_hdr.lf_update_value[i]; -+ else -+ lf_level += sgmnt_hdr.lf_update_value[i]; -+ } -+ -+ // Clamp to [0..63] range. -+ lf_level = std::min(std::max(lf_level, 0), 63); -+ pic_param.loop_filter_level[i] = lf_level; -+ } -+ -+ static_assert( -+ arraysize(lf_hdr.ref_frame_delta) == -+ arraysize(pic_param.loop_filter_deltas_ref_frame) && -+ arraysize(lf_hdr.mb_mode_delta) == -+ arraysize(pic_param.loop_filter_deltas_mode) && -+ arraysize(lf_hdr.ref_frame_delta) == arraysize(lf_hdr.mb_mode_delta), -+ "loop filter deltas arrays size mismatch"); -+ for (size_t i = 0; i < arraysize(lf_hdr.ref_frame_delta); ++i) { -+ pic_param.loop_filter_deltas_ref_frame[i] = lf_hdr.ref_frame_delta[i]; -+ pic_param.loop_filter_deltas_mode[i] = lf_hdr.mb_mode_delta[i]; -+ } -+ -+#define FHDR_TO_PP(a) pic_param.a = frame_hdr->a -+ FHDR_TO_PP(prob_skip_false); -+ FHDR_TO_PP(prob_intra); -+ FHDR_TO_PP(prob_last); -+ FHDR_TO_PP(prob_gf); -+#undef FHDR_TO_PP -+ -+ ARRAY_MEMCPY_CHECKED(pic_param.y_mode_probs, entr_hdr.y_mode_probs); -+ ARRAY_MEMCPY_CHECKED(pic_param.uv_mode_probs, entr_hdr.uv_mode_probs); -+ ARRAY_MEMCPY_CHECKED(pic_param.mv_probs, entr_hdr.mv_probs); -+ -+ pic_param.bool_coder_ctx.range = frame_hdr->bool_dec_range; -+ pic_param.bool_coder_ctx.value = frame_hdr->bool_dec_value; -+ pic_param.bool_coder_ctx.count = frame_hdr->bool_dec_count; -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, -+ sizeof(pic_param), &pic_param)) -+ return false; -+ -+ VASliceParameterBufferVP8 slice_param; -+ memset(&slice_param, 0, sizeof(slice_param)); -+ slice_param.slice_data_size = frame_hdr->frame_size; -+ slice_param.slice_data_offset = frame_hdr->first_part_offset; -+ slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -+ slice_param.macroblock_offset = frame_hdr->macroblock_bit_offset; -+ // Number of DCT partitions plus control partition. -+ slice_param.num_of_partitions = frame_hdr->num_of_dct_partitions + 1; -+ -+ // Per VAAPI, this size only includes the size of the macroblock data in -+ // the first partition (in bytes), so we have to subtract the header size. -+ slice_param.partition_size[0] = -+ frame_hdr->first_part_size - ((frame_hdr->macroblock_bit_offset + 7) / 8); -+ -+ for (size_t i = 0; i < frame_hdr->num_of_dct_partitions; ++i) -+ slice_param.partition_size[i + 1] = frame_hdr->dct_partition_sizes[i]; -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, -+ sizeof(VASliceParameterBufferVP8), -+ &slice_param)) -+ return false; -+ -+ void* non_const_ptr = const_cast(frame_hdr->data); -+ if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, -+ frame_hdr->frame_size, non_const_ptr)) -+ return false; -+ -+ scoped_refptr dec_surface = -+ VP8PictureToVaapiDecodeSurface(pic); -+ -+ return vaapi_dec_->DecodeSurface(dec_surface); -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::OutputPicture( -+ const scoped_refptr& pic) { -+ scoped_refptr dec_surface = -+ VP8PictureToVaapiDecodeSurface(pic); -+ dec_surface->set_visible_rect(pic->visible_rect); -+ vaapi_dec_->SurfaceReady(dec_surface); -+ return true; -+} -+ -+scoped_refptr -+VaapiVideoDecodeAccelerator::VaapiVP8Accelerator:: -+ VP8PictureToVaapiDecodeSurface(const scoped_refptr& pic) { -+ VaapiVP8Picture* vaapi_pic = pic->AsVaapiVP8Picture(); -+ CHECK(vaapi_pic); -+ return vaapi_pic->dec_surface(); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::VaapiVP9Accelerator( -+ VaapiVideoDecodeAccelerator* vaapi_dec, -+ VaapiWrapper* vaapi_wrapper) -+ : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { -+ DCHECK(vaapi_wrapper_); -+ DCHECK(vaapi_dec_); -+} -+ -+VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::~VaapiVP9Accelerator() {} -+ -+scoped_refptr -+VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::CreateVP9Picture() { -+ scoped_refptr va_surface = vaapi_dec_->CreateSurface(); -+ if (!va_surface) -+ return nullptr; -+ -+ return new VaapiVP9Picture(std::move(va_surface)); -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::SubmitDecode( -+ const scoped_refptr& pic, -+ const Vp9SegmentationParams& seg, -+ const Vp9LoopFilterParams& lf, -+ const std::vector>& ref_pictures, -+ const base::Closure& done_cb) { -+ // |done_cb| should be null as we return false from IsFrameContextRequired(). -+ DCHECK(done_cb.is_null()); -+ -+ VADecPictureParameterBufferVP9 pic_param; -+ memset(&pic_param, 0, sizeof(pic_param)); -+ -+ const Vp9FrameHeader* frame_hdr = pic->frame_hdr.get(); -+ DCHECK(frame_hdr); -+ -+ pic_param.frame_width = base::checked_cast(frame_hdr->frame_width); -+ pic_param.frame_height = -+ base::checked_cast(frame_hdr->frame_height); -+ -+ CHECK_EQ(ref_pictures.size(), arraysize(pic_param.reference_frames)); -+ for (size_t i = 0; i < arraysize(pic_param.reference_frames); ++i) { -+ VASurfaceID va_surface_id; -+ if (ref_pictures[i]) { -+ scoped_refptr surface = -+ VP9PictureToVaapiDecodeSurface(ref_pictures[i]); -+ va_surface_id = surface->va_surface()->id(); -+ } else { -+ va_surface_id = VA_INVALID_SURFACE; -+ } -+ -+ pic_param.reference_frames[i] = va_surface_id; -+ } -+ -+#define FHDR_TO_PP_PF1(a) pic_param.pic_fields.bits.a = frame_hdr->a -+#define FHDR_TO_PP_PF2(a, b) pic_param.pic_fields.bits.a = b -+ FHDR_TO_PP_PF2(subsampling_x, frame_hdr->subsampling_x == 1); -+ FHDR_TO_PP_PF2(subsampling_y, frame_hdr->subsampling_y == 1); -+ FHDR_TO_PP_PF2(frame_type, frame_hdr->IsKeyframe() ? 0 : 1); -+ FHDR_TO_PP_PF1(show_frame); -+ FHDR_TO_PP_PF1(error_resilient_mode); -+ FHDR_TO_PP_PF1(intra_only); -+ FHDR_TO_PP_PF1(allow_high_precision_mv); -+ FHDR_TO_PP_PF2(mcomp_filter_type, frame_hdr->interpolation_filter); -+ FHDR_TO_PP_PF1(frame_parallel_decoding_mode); -+ FHDR_TO_PP_PF1(reset_frame_context); -+ FHDR_TO_PP_PF1(refresh_frame_context); -+ FHDR_TO_PP_PF2(frame_context_idx, frame_hdr->frame_context_idx_to_save_probs); -+ FHDR_TO_PP_PF2(segmentation_enabled, seg.enabled); -+ FHDR_TO_PP_PF2(segmentation_temporal_update, seg.temporal_update); -+ FHDR_TO_PP_PF2(segmentation_update_map, seg.update_map); -+ FHDR_TO_PP_PF2(last_ref_frame, frame_hdr->ref_frame_idx[0]); -+ FHDR_TO_PP_PF2(last_ref_frame_sign_bias, -+ frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_LAST]); -+ FHDR_TO_PP_PF2(golden_ref_frame, frame_hdr->ref_frame_idx[1]); -+ FHDR_TO_PP_PF2(golden_ref_frame_sign_bias, -+ frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_GOLDEN]); -+ FHDR_TO_PP_PF2(alt_ref_frame, frame_hdr->ref_frame_idx[2]); -+ FHDR_TO_PP_PF2(alt_ref_frame_sign_bias, -+ frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_ALTREF]); -+ FHDR_TO_PP_PF2(lossless_flag, frame_hdr->quant_params.IsLossless()); -+#undef FHDR_TO_PP_PF2 -+#undef FHDR_TO_PP_PF1 -+ -+ pic_param.filter_level = lf.level; -+ pic_param.sharpness_level = lf.sharpness; -+ pic_param.log2_tile_rows = frame_hdr->tile_rows_log2; -+ pic_param.log2_tile_columns = frame_hdr->tile_cols_log2; -+ pic_param.frame_header_length_in_bytes = frame_hdr->uncompressed_header_size; -+ pic_param.first_partition_size = frame_hdr->header_size_in_bytes; -+ -+ ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, seg.tree_probs); -+ ARRAY_MEMCPY_CHECKED(pic_param.segment_pred_probs, seg.pred_probs); -+ -+ pic_param.profile = frame_hdr->profile; -+ pic_param.bit_depth = frame_hdr->bit_depth; -+ DCHECK((pic_param.profile == 0 && pic_param.bit_depth == 8) || -+ (pic_param.profile == 2 && pic_param.bit_depth == 10)); -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, -+ sizeof(pic_param), &pic_param)) -+ return false; -+ -+ VASliceParameterBufferVP9 slice_param; -+ memset(&slice_param, 0, sizeof(slice_param)); -+ slice_param.slice_data_size = frame_hdr->frame_size; -+ slice_param.slice_data_offset = 0; -+ slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -+ -+ static_assert(arraysize(Vp9SegmentationParams::feature_enabled) == -+ arraysize(slice_param.seg_param), -+ "seg_param array of incorrect size"); -+ for (size_t i = 0; i < arraysize(slice_param.seg_param); ++i) { -+ VASegmentParameterVP9& seg_param = slice_param.seg_param[i]; -+#define SEG_TO_SP_SF(a, b) seg_param.segment_flags.fields.a = b -+ SEG_TO_SP_SF( -+ segment_reference_enabled, -+ seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); -+ SEG_TO_SP_SF(segment_reference, -+ seg.FeatureData(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); -+ SEG_TO_SP_SF(segment_reference_skipped, -+ seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_SKIP)); -+#undef SEG_TO_SP_SF -+ -+ ARRAY_MEMCPY_CHECKED(seg_param.filter_level, lf.lvl[i]); -+ -+ seg_param.luma_dc_quant_scale = seg.y_dequant[i][0]; -+ seg_param.luma_ac_quant_scale = seg.y_dequant[i][1]; -+ seg_param.chroma_dc_quant_scale = seg.uv_dequant[i][0]; -+ seg_param.chroma_ac_quant_scale = seg.uv_dequant[i][1]; -+ } -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, -+ sizeof(slice_param), &slice_param)) -+ return false; -+ -+ void* non_const_ptr = const_cast(frame_hdr->data); -+ if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, -+ frame_hdr->frame_size, non_const_ptr)) -+ return false; -+ -+ scoped_refptr dec_surface = -+ VP9PictureToVaapiDecodeSurface(pic); -+ -+ return vaapi_dec_->DecodeSurface(dec_surface); -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::OutputPicture( -+ const scoped_refptr& pic) { -+ scoped_refptr dec_surface = -+ VP9PictureToVaapiDecodeSurface(pic); -+ dec_surface->set_visible_rect(pic->visible_rect); -+ vaapi_dec_->SurfaceReady(dec_surface); -+ return true; -+} -+ -+bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::GetFrameContext( -+ const scoped_refptr& pic, -+ Vp9FrameContext* frame_ctx) { -+ NOTIMPLEMENTED() << "Frame context update not supported"; -+ return false; -+} -+ -+scoped_refptr -+VaapiVideoDecodeAccelerator::VaapiVP9Accelerator:: -+ VP9PictureToVaapiDecodeSurface(const scoped_refptr& pic) { -+ VaapiVP9Picture* vaapi_pic = pic->AsVaapiVP9Picture(); -+ CHECK(vaapi_pic); -+ return vaapi_pic->dec_surface(); -+} -+ -+// static -+VideoDecodeAccelerator::SupportedProfiles -+VaapiVideoDecodeAccelerator::GetSupportedProfiles() { -+ return VaapiWrapper::GetSupportedDecodeProfiles(); -+} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.h -@@ -0,0 +1,325 @@ -+// Copyright (c) 2012 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+// -+// This file contains an implementation of VideoDecoderAccelerator -+// that utilizes hardware video decoder present on Intel CPUs. -+ -+#ifndef MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -+#define MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "base/containers/queue.h" -+#include "base/logging.h" -+#include "base/macros.h" -+#include "base/memory/linked_ptr.h" -+#include "base/memory/ref_counted.h" -+#include "base/memory/weak_ptr.h" -+#include "base/single_thread_task_runner.h" -+#include "base/synchronization/condition_variable.h" -+#include "base/synchronization/lock.h" -+#include "base/threading/thread.h" -+#include "media/base/bitstream_buffer.h" -+#include "media/gpu/gpu_video_decode_accelerator_helpers.h" -+#include "media/gpu/media_gpu_export.h" -+#include "media/gpu/shared_memory_region.h" -+#include "media/gpu/vaapi/vaapi_picture_factory.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+#include "media/video/picture.h" -+#include "media/video/video_decode_accelerator.h" -+ -+namespace gl { -+class GLImage; -+} -+ -+namespace media { -+ -+class AcceleratedVideoDecoder; -+class VaapiPicture; -+ -+// Class to provide video decode acceleration for Intel systems with hardware -+// support for it, and on which libva is available. -+// Decoding tasks are performed in a separate decoding thread. -+// -+// Threading/life-cycle: this object is created & destroyed on the GPU -+// ChildThread. A few methods on it are called on the decoder thread which is -+// stopped during |this->Destroy()|, so any tasks posted to the decoder thread -+// can assume |*this| is still alive. See |weak_this_| below for more details. -+class MEDIA_GPU_EXPORT VaapiVideoDecodeAccelerator -+ : public VideoDecodeAccelerator { -+ public: -+ // Wrapper of a VASurface with id and visible area. -+ class VaapiDecodeSurface; -+ -+ VaapiVideoDecodeAccelerator( -+ const MakeGLContextCurrentCallback& make_context_current_cb, -+ const BindGLImageCallback& bind_image_cb); -+ -+ ~VaapiVideoDecodeAccelerator() override; -+ -+ // VideoDecodeAccelerator implementation. -+ bool Initialize(const Config& config, Client* client) override; -+ void Decode(const BitstreamBuffer& bitstream_buffer) override; -+ void AssignPictureBuffers(const std::vector& buffers) override; -+#if defined(USE_OZONE) -+ void ImportBufferForPicture( -+ int32_t picture_buffer_id, -+ const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override; -+#endif -+ void ReusePictureBuffer(int32_t picture_buffer_id) override; -+ void Flush() override; -+ void Reset() override; -+ void Destroy() override; -+ bool TryToSetupDecodeOnSeparateThread( -+ const base::WeakPtr& decode_client, -+ const scoped_refptr& decode_task_runner) -+ override; -+ -+ static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(); -+ -+ private: -+ friend class VaapiVideoDecodeAcceleratorTest; -+ class VaapiH264Accelerator; -+ class VaapiVP8Accelerator; -+ class VaapiVP9Accelerator; -+ -+ // An input buffer with id provided by the client and awaiting consumption. -+ class InputBuffer; -+ -+ // Notify the client that an error has occurred and decoding cannot continue. -+ void NotifyError(Error error); -+ -+ // Queue a input buffer for decode. -+ void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer); -+ -+ // Get a new input buffer from the queue and set it up in decoder. This will -+ // sleep if no input buffers are available. Return true if a new buffer has -+ // been set up, false if an early exit has been requested (due to initiated -+ // reset/flush/destroy). -+ bool GetInputBuffer_Locked(); -+ -+ // Signal the client that the current buffer has been read and can be -+ // returned. Will also release the mapping. -+ void ReturnCurrInputBuffer_Locked(); -+ -+ // Wait for more surfaces to become available. Return true once they do or -+ // false if an early exit has been requested (due to an initiated -+ // reset/flush/destroy). -+ bool WaitForSurfaces_Locked(); -+ -+ // Continue decoding given input buffers and sleep waiting for input/output -+ // as needed. Will exit if a new set of surfaces or reset/flush/destroy -+ // is requested. -+ void DecodeTask(); -+ -+ // Scheduled after receiving a flush request and executed after the current -+ // decoding task finishes decoding pending inputs. Makes the decoder return -+ // all remaining output pictures and puts it in an idle state, ready -+ // to resume if needed and schedules a FinishFlush. -+ void FlushTask(); -+ -+ // Scheduled by the FlushTask after decoder is flushed to put VAVDA into idle -+ // state and notify the client that flushing has been finished. -+ void FinishFlush(); -+ -+ // Scheduled after receiving a reset request and executed after the current -+ // decoding task finishes decoding the current frame. Puts the decoder into -+ // an idle state, ready to resume if needed, discarding decoded but not yet -+ // outputted pictures (decoder keeps ownership of their associated picture -+ // buffers). Schedules a FinishReset afterwards. -+ void ResetTask(); -+ -+ // Scheduled by ResetTask after it's done putting VAVDA into an idle state. -+ // Drops remaining input buffers and notifies the client that reset has been -+ // finished. -+ void FinishReset(); -+ -+ // Helper for Destroy(), doing all the actual work except for deleting self. -+ void Cleanup(); -+ -+ // Get a usable framebuffer configuration for use in binding textures -+ // or return false on failure. -+ bool InitializeFBConfig(); -+ -+ // Callback to be executed once we have a |va_surface| to be output and -+ // an available |picture| to use for output. -+ // Puts contents of |va_surface| into given |picture|, releases the surface -+ // and passes the resulting picture to client to output the given -+ // |visible_rect| part of it. -+ void OutputPicture(const scoped_refptr& va_surface, -+ int32_t input_id, -+ gfx::Rect visible_rect, -+ VaapiPicture* picture); -+ -+ // Try to OutputPicture() if we have both a ready surface and picture. -+ void TryOutputSurface(); -+ -+ // Called when a VASurface is no longer in use by the decoder or is not being -+ // synced/waiting to be synced to a picture. Returns it to available surfaces -+ // pool. -+ void RecycleVASurfaceID(VASurfaceID va_surface_id); -+ -+ // Initiate wait cycle for surfaces to be released before we release them -+ // and allocate new ones, as requested by the decoder. -+ void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size); -+ -+ // Check if the surfaces have been released or post ourselves for later. -+ void TryFinishSurfaceSetChange(); -+ -+ // -+ // Below methods are used by accelerator implementations. -+ // -+ // Decode of |dec_surface| is ready to be submitted and all codec-specific -+ // settings are set in hardware. -+ bool DecodeSurface(const scoped_refptr& dec_surface); -+ -+ // |dec_surface| is ready to be outputted once decode is finished. -+ // This can be called before decode is actually done in hardware, and this -+ // method is responsible for maintaining the ordering, i.e. the surfaces have -+ // to be outputted in the same order as SurfaceReady is called. -+ // On Intel, we don't have to explicitly maintain the ordering however, as the -+ // driver will maintain ordering, as well as dependencies, and will process -+ // each submitted command in order, and run each command only if its -+ // dependencies are ready. -+ void SurfaceReady(const scoped_refptr& dec_surface); -+ -+ // Return a new VaapiDecodeSurface for decoding into, or nullptr if not -+ // available. -+ scoped_refptr CreateSurface(); -+ -+ // VAVDA state. -+ enum State { -+ // Initialize() not called yet or failed. -+ kUninitialized, -+ // DecodeTask running. -+ kDecoding, -+ // Resetting, waiting for decoder to finish current task and cleanup. -+ kResetting, -+ // Idle, decoder in state ready to start/resume decoding. -+ kIdle, -+ // Destroying, waiting for the decoder to finish current task. -+ kDestroying, -+ }; -+ -+ // Protects input buffer and surface queues and state_. -+ base::Lock lock_; -+ State state_; -+ Config::OutputMode output_mode_; -+ -+ // Queue of available InputBuffers (picture_buffer_ids). -+ base::queue> input_buffers_; -+ // Signalled when input buffers are queued onto |input_buffers_| queue. -+ base::ConditionVariable input_ready_; -+ -+ // Current input buffer at decoder. -+ std::unique_ptr curr_input_buffer_; -+ -+ // Queue for incoming output buffers (texture ids). -+ using OutputBuffers = base::queue; -+ OutputBuffers output_buffers_; -+ -+ std::unique_ptr vaapi_picture_factory_; -+ -+ scoped_refptr vaapi_wrapper_; -+ -+ // All allocated Pictures, regardless of their current state. Pictures are -+ // allocated once using |create_vaapi_picture_callback_| and destroyed at the -+ // end of decode. Comes after |vaapi_wrapper_| to ensure all pictures are -+ // destroyed before said |vaapi_wrapper_| is destroyed. -+ using Pictures = std::map>; -+ Pictures pictures_; -+ -+ // Return a VaapiPicture associated with given client-provided id. -+ VaapiPicture* PictureById(int32_t picture_buffer_id); -+ -+ // VA Surfaces no longer in use that can be passed back to the decoder for -+ // reuse, once it requests them. -+ std::list available_va_surfaces_; -+ // Signalled when output surfaces are queued onto the available_va_surfaces_ -+ // queue. -+ base::ConditionVariable surfaces_available_; -+ -+ // Pending output requests from the decoder. When it indicates that we should -+ // output a surface and we have an available Picture (i.e. texture) ready -+ // to use, we'll execute the callback passing the Picture. The callback -+ // will put the contents of the surface into the picture and return it to -+ // the client, releasing the surface as well. -+ // If we don't have any available Pictures at the time when the decoder -+ // requests output, we'll store the request on pending_output_cbs_ queue for -+ // later and run it once the client gives us more textures -+ // via ReusePictureBuffer(). -+ using OutputCB = base::Callback; -+ base::queue pending_output_cbs_; -+ -+ // ChildThread's task runner. -+ scoped_refptr task_runner_; -+ -+ // WeakPtr<> pointing to |this| for use in posting tasks from the decoder -+ // thread back to the ChildThread. Because the decoder thread is a member of -+ // this class, any task running on the decoder thread is guaranteed that this -+ // object is still alive. As a result, tasks posted from ChildThread to -+ // decoder thread should use base::Unretained(this), and tasks posted from the -+ // decoder thread to the ChildThread should use |weak_this_|. -+ base::WeakPtr weak_this_; -+ -+ // Callback used when creating VASurface objects. -+ VASurface::ReleaseCB va_surface_release_cb_; -+ -+ // To expose client callbacks from VideoDecodeAccelerator. -+ // NOTE: all calls to these objects *MUST* be executed on task_runner_. -+ std::unique_ptr> client_ptr_factory_; -+ base::WeakPtr client_; -+ -+ // Accelerators come after vaapi_wrapper_ to ensure they are destroyed first. -+ std::unique_ptr h264_accelerator_; -+ std::unique_ptr vp8_accelerator_; -+ std::unique_ptr vp9_accelerator_; -+ // After *_accelerator_ to ensure correct destruction order. -+ std::unique_ptr decoder_; -+ -+ base::Thread decoder_thread_; -+ // Use this to post tasks to |decoder_thread_| instead of -+ // |decoder_thread_.message_loop()| because the latter will be NULL once -+ // |decoder_thread_.Stop()| returns. -+ scoped_refptr decoder_thread_task_runner_; -+ -+ int num_frames_at_client_; -+ -+ // Whether we are waiting for any pending_output_cbs_ to be run before -+ // NotifyingFlushDone. -+ bool finish_flush_pending_; -+ -+ // Decoder requested a new surface set and we are waiting for all the surfaces -+ // to be returned before we can free them. -+ bool awaiting_va_surfaces_recycle_; -+ -+ // Last requested number/resolution of output picture buffers and their -+ // format. -+ size_t requested_num_pics_; -+ gfx::Size requested_pic_size_; -+ gfx::BufferFormat output_format_; -+ VideoCodecProfile profile_; -+ -+ // Callback to make GL context current. -+ MakeGLContextCurrentCallback make_context_current_cb_; -+ -+ // Callback to bind a GLImage to a given texture. -+ BindGLImageCallback bind_image_cb_; -+ -+ // The WeakPtrFactory for |weak_this_|. -+ base::WeakPtrFactory weak_this_factory_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc -@@ -0,0 +1,367 @@ -+// Copyright 2017 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_video_decode_accelerator.h" -+ -+#include "base/bind.h" -+#include "base/memory/ptr_util.h" -+#include "base/run_loop.h" -+#include "base/test/scoped_task_environment.h" -+#include "media/gpu/accelerated_video_decoder.h" -+#include "media/gpu/format_utils.h" -+#include "media/gpu/vaapi/vaapi_picture.h" -+#include "media/gpu/vaapi/vaapi_picture_factory.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+#include "testing/gmock/include/gmock/gmock.h" -+#include "testing/gtest/include/gtest/gtest.h" -+ -+using ::testing::_; -+using ::testing::DoAll; -+using ::testing::Invoke; -+using ::testing::Return; -+using ::testing::TestWithParam; -+using ::testing::ValuesIn; -+using ::testing::WithArgs; -+ -+namespace media { -+ -+namespace { -+ -+ACTION_P(RunClosure, closure) { -+ closure.Run(); -+} -+ -+constexpr VideoCodecProfile kCodecProfiles[] = {H264PROFILE_MIN, VP8PROFILE_MIN, -+ VP9PROFILE_MIN}; -+constexpr int kBitstreamId = 123; -+constexpr size_t kInputSize = 256; -+ -+} // namespace -+ -+class MockAcceleratedVideoDecoder : public AcceleratedVideoDecoder { -+ public: -+ MockAcceleratedVideoDecoder() = default; -+ ~MockAcceleratedVideoDecoder() override = default; -+ -+ MOCK_METHOD2(SetStream, void(const uint8_t* ptr, size_t size)); -+ MOCK_METHOD0(Flush, bool()); -+ MOCK_METHOD0(Reset, void()); -+ MOCK_METHOD0(Decode, DecodeResult()); -+ MOCK_CONST_METHOD0(GetPicSize, gfx::Size()); -+ MOCK_CONST_METHOD0(GetRequiredNumOfPictures, size_t()); -+}; -+ -+class MockVaapiWrapper : public VaapiWrapper { -+ public: -+ MockVaapiWrapper() = default; -+ MOCK_METHOD4( -+ CreateSurfaces, -+ bool(unsigned int, const gfx::Size&, size_t, std::vector*)); -+ MOCK_METHOD0(DestroySurfaces, void()); -+ -+ private: -+ ~MockVaapiWrapper() override = default; -+}; -+ -+class MockVaapiPicture : public VaapiPicture { -+ public: -+ MockVaapiPicture(const scoped_refptr& vaapi_wrapper, -+ const MakeGLContextCurrentCallback& make_context_current_cb, -+ const BindGLImageCallback& bind_image_cb, -+ int32_t picture_buffer_id, -+ const gfx::Size& size, -+ uint32_t texture_id, -+ uint32_t client_texture_id) -+ : VaapiPicture(vaapi_wrapper, -+ make_context_current_cb, -+ bind_image_cb, -+ picture_buffer_id, -+ size, -+ texture_id, -+ client_texture_id) {} -+ ~MockVaapiPicture() override = default; -+ -+ // VaapiPicture implementation. -+ bool Allocate(gfx::BufferFormat format) override { return true; } -+ bool ImportGpuMemoryBufferHandle( -+ gfx::BufferFormat format, -+ const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override { -+ return true; -+ } -+ bool DownloadFromSurface( -+ const scoped_refptr& va_surface) override { -+ return true; -+ } -+ bool AllowOverlay() const override { return false; } -+}; -+ -+class MockVaapiPictureFactory : public VaapiPictureFactory { -+ public: -+ MockVaapiPictureFactory() = default; -+ ~MockVaapiPictureFactory() override = default; -+ -+ MOCK_METHOD2(MockCreateVaapiPicture, void(VaapiWrapper*, const gfx::Size&)); -+ std::unique_ptr Create( -+ const scoped_refptr& vaapi_wrapper, -+ const MakeGLContextCurrentCallback& make_context_current_cb, -+ const BindGLImageCallback& bind_image_cb, -+ int32_t picture_buffer_id, -+ const gfx::Size& size, -+ uint32_t texture_id, -+ uint32_t client_texture_id) override { -+ MockCreateVaapiPicture(vaapi_wrapper.get(), size); -+ return std::make_unique( -+ vaapi_wrapper, make_context_current_cb, bind_image_cb, -+ picture_buffer_id, size, texture_id, client_texture_id); -+ } -+}; -+ -+class VaapiVideoDecodeAcceleratorTest : public TestWithParam, -+ public VideoDecodeAccelerator::Client { -+ public: -+ VaapiVideoDecodeAcceleratorTest() -+ : vda_(base::Bind([] { return true; }), -+ base::Bind([](uint32_t client_texture_id, -+ uint32_t texture_target, -+ const scoped_refptr& image, -+ bool can_bind_to_sampler) { return true; })), -+ decoder_thread_("VaapiVideoDecodeAcceleratorTestThread"), -+ mock_decoder_(new MockAcceleratedVideoDecoder), -+ mock_vaapi_picture_factory_(new MockVaapiPictureFactory()), -+ mock_vaapi_wrapper_(new MockVaapiWrapper()), -+ weak_ptr_factory_(this) { -+ decoder_thread_.Start(); -+ -+ // Don't want to go through a vda_->Initialize() because it binds too many -+ // items of the environment. Instead, just start the decoder thread. -+ vda_.decoder_thread_task_runner_ = decoder_thread_.task_runner(); -+ -+ // Plug in all the mocks and ourselves as the |client_|. -+ vda_.decoder_.reset(mock_decoder_); -+ vda_.client_ = weak_ptr_factory_.GetWeakPtr(); -+ vda_.vaapi_wrapper_ = mock_vaapi_wrapper_; -+ vda_.vaapi_picture_factory_.reset(mock_vaapi_picture_factory_); -+ -+ vda_.state_ = VaapiVideoDecodeAccelerator::kIdle; -+ } -+ ~VaapiVideoDecodeAcceleratorTest() {} -+ -+ void SetUp() override { -+ in_shm_.reset(new base::SharedMemory); -+ ASSERT_TRUE(in_shm_->CreateAndMapAnonymous(kInputSize)); -+ } -+ -+ void SetVdaStateToUnitialized() { -+ vda_.state_ = VaapiVideoDecodeAccelerator::kUninitialized; -+ } -+ -+ void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer) { -+ vda_.QueueInputBuffer(bitstream_buffer); -+ } -+ -+ void AssignPictureBuffers(const std::vector& picture_buffers) { -+ vda_.AssignPictureBuffers(picture_buffers); -+ } -+ -+ // Reset epilogue, needed to get |vda_| worker thread out of its Wait(). -+ void ResetSequence() { -+ base::RunLoop run_loop; -+ base::Closure quit_closure = run_loop.QuitClosure(); -+ EXPECT_CALL(*mock_decoder_, Reset()); -+ EXPECT_CALL(*this, NotifyResetDone()).WillOnce(RunClosure(quit_closure)); -+ vda_.Reset(); -+ run_loop.Run(); -+ } -+ -+ // VideoDecodeAccelerator::Client methods. -+ MOCK_METHOD1(NotifyInitializationComplete, void(bool)); -+ MOCK_METHOD5( -+ ProvidePictureBuffers, -+ void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t)); -+ MOCK_METHOD1(DismissPictureBuffer, void(int32_t)); -+ MOCK_METHOD1(PictureReady, void(const Picture&)); -+ MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t)); -+ MOCK_METHOD0(NotifyFlushDone, void()); -+ MOCK_METHOD0(NotifyResetDone, void()); -+ MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error)); -+ -+ base::test::ScopedTaskEnvironment scoped_task_environment_; -+ -+ // The class under test and a worker thread for it. -+ VaapiVideoDecodeAccelerator vda_; -+ base::Thread decoder_thread_; -+ -+ // Ownership passed to |vda_|, but we retain a pointer to it for MOCK checks. -+ MockAcceleratedVideoDecoder* mock_decoder_; -+ MockVaapiPictureFactory* mock_vaapi_picture_factory_; -+ -+ scoped_refptr mock_vaapi_wrapper_; -+ -+ std::unique_ptr in_shm_; -+ -+ private: -+ base::WeakPtrFactory weak_ptr_factory_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAcceleratorTest); -+}; -+ -+// This test checks that QueueInputBuffer() fails when state is kUnitialized. -+TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndError) { -+ SetVdaStateToUnitialized(); -+ -+ base::SharedMemoryHandle handle; -+ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -+ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -+ -+ EXPECT_CALL(*this, -+ NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)); -+ QueueInputBuffer(bitstream_buffer); -+} -+ -+// Verifies that Decode() returning kDecodeError ends up pinging NotifyError(). -+TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeError) { -+ base::SharedMemoryHandle handle; -+ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -+ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -+ -+ base::RunLoop run_loop; -+ base::Closure quit_closure = run_loop.QuitClosure(); -+ EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); -+ EXPECT_CALL(*mock_decoder_, Decode()) -+ .WillOnce(Return(AcceleratedVideoDecoder::kDecodeError)); -+ EXPECT_CALL(*this, NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)) -+ .WillOnce(RunClosure(quit_closure)); -+ -+ QueueInputBuffer(bitstream_buffer); -+ run_loop.Run(); -+} -+ -+// Tests usual startup sequence: a BitstreamBuffer is enqueued for decode, -+// |vda_| asks for PictureBuffers, that we provide, and then the same Decode() -+// is tried again. -+TEST_P(VaapiVideoDecodeAcceleratorTest, -+ QueueInputBufferAndAssignPictureBuffersAndDecode) { -+ // Try and QueueInputBuffer(), |vda_| will ping us to ProvidePictureBuffers(). -+ const uint32_t kNumPictures = 2; -+ const gfx::Size kPictureSize(64, 48); -+ { -+ base::SharedMemoryHandle handle; -+ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -+ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -+ -+ base::RunLoop run_loop; -+ base::Closure quit_closure = run_loop.QuitClosure(); -+ EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); -+ EXPECT_CALL(*mock_decoder_, Decode()) -+ .WillOnce(Return(AcceleratedVideoDecoder::kAllocateNewSurfaces)); -+ -+ EXPECT_CALL(*mock_decoder_, GetRequiredNumOfPictures()) -+ .WillOnce(Return(kNumPictures)); -+ EXPECT_CALL(*mock_decoder_, GetPicSize()).WillOnce(Return(kPictureSize)); -+ EXPECT_CALL(*mock_vaapi_wrapper_, DestroySurfaces()); -+ -+ EXPECT_CALL(*this, -+ ProvidePictureBuffers(kNumPictures, _, 1, kPictureSize, _)) -+ .WillOnce(RunClosure(quit_closure)); -+ -+ QueueInputBuffer(bitstream_buffer); -+ run_loop.Run(); -+ } -+ // AssignPictureBuffers() accordingly and expect another go at Decode(). -+ { -+ base::RunLoop run_loop; -+ base::Closure quit_closure = run_loop.QuitClosure(); -+ -+ const std::vector kPictureBuffers( -+ {{2, kPictureSize}, {3, kPictureSize}}); -+ EXPECT_EQ(kPictureBuffers.size(), kNumPictures); -+ -+ EXPECT_CALL(*mock_vaapi_wrapper_, -+ CreateSurfaces(_, kPictureSize, kNumPictures, _)) -+ .WillOnce(DoAll( -+ WithArgs<3>(Invoke([](std::vector* va_surface_ids) { -+ va_surface_ids->resize(kNumPictures); -+ })), -+ Return(true))); -+ EXPECT_CALL(*mock_vaapi_picture_factory_, -+ MockCreateVaapiPicture(mock_vaapi_wrapper_.get(), kPictureSize)) -+ .Times(2); -+ -+ EXPECT_CALL(*mock_decoder_, Decode()) -+ .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); -+ EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) -+ .WillOnce(RunClosure(quit_closure)); -+ -+ AssignPictureBuffers(kPictureBuffers); -+ run_loop.Run(); -+ } -+ -+ ResetSequence(); -+} -+ -+// Verifies that Decode() replying kRanOutOfStreamData (to signal it's finished) -+// rolls to a NotifyEndOfBitstreamBuffer(). -+TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeFinished) { -+ base::SharedMemoryHandle handle; -+ handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -+ BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -+ -+ { -+ base::RunLoop run_loop; -+ base::Closure quit_closure = run_loop.QuitClosure(); -+ EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); -+ EXPECT_CALL(*mock_decoder_, Decode()) -+ .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); -+ EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) -+ .WillOnce(RunClosure(quit_closure)); -+ -+ QueueInputBuffer(bitstream_buffer); -+ run_loop.Run(); -+ } -+ -+ ResetSequence(); -+} -+ -+// Verify that it is possible to select DRM(egl) and TFP(glx) at runtime. -+TEST_P(VaapiVideoDecodeAcceleratorTest, SupportedPlatforms) { -+ EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationNone, -+ mock_vaapi_picture_factory_->GetVaapiImplementation( -+ gl::kGLImplementationNone)); -+ EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationDrm, -+ mock_vaapi_picture_factory_->GetVaapiImplementation( -+ gl::kGLImplementationEGLGLES2)); -+ -+#if defined(USE_X11) -+ EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationX11, -+ mock_vaapi_picture_factory_->GetVaapiImplementation( -+ gl::kGLImplementationDesktopGL)); -+#endif -+} -+ -+// Verifies the expected buffer format for each output mode. -+TEST_P(VaapiVideoDecodeAcceleratorTest, PictureBufferFormat) { -+ gfx::BufferFormat allocate_format = -+ mock_vaapi_picture_factory_->GetBufferFormatForAllocateMode(); -+ gfx::BufferFormat import_format = -+ mock_vaapi_picture_factory_->GetBufferFormatForImportMode(); -+ -+#if defined(USE_OZONE) -+ EXPECT_EQ(gfx::BufferFormat::BGRX_8888, allocate_format); -+#else -+ EXPECT_EQ(gfx::BufferFormat::RGBX_8888, allocate_format); -+#endif // USE_OZONE -+ -+ EXPECT_EQ(gfx::BufferFormat::YVU_420, import_format); -+ -+ EXPECT_EQ(PIXEL_FORMAT_XRGB, -+ GfxBufferFormatToVideoPixelFormat(allocate_format)); -+ EXPECT_EQ(PIXEL_FORMAT_YV12, -+ GfxBufferFormatToVideoPixelFormat(import_format)); -+} -+ -+INSTANTIATE_TEST_CASE_P(/* No prefix. */, -+ VaapiVideoDecodeAcceleratorTest, -+ ValuesIn(kCodecProfiles)); -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc -@@ -0,0 +1,1102 @@ -+// Copyright 2014 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_video_encode_accelerator.h" -+ -+#include -+ -+#include -+#include -+ -+#include -+ -+#include "base/bind.h" -+#include "base/callback.h" -+#include "base/macros.h" -+#include "base/metrics/histogram_macros.h" -+#include "base/numerics/safe_conversions.h" -+#include "base/single_thread_task_runner.h" -+#include "base/threading/thread_task_runner_handle.h" -+#include "media/base/bind_to_current_loop.h" -+#include "media/gpu/h264_dpb.h" -+#include "media/gpu/shared_memory_region.h" -+ -+#define VLOGF(level) VLOG(level) << __func__ << "(): " -+#define DVLOGF(level) DVLOG(level) << __func__ << "(): " -+ -+#define NOTIFY_ERROR(error, msg) \ -+ do { \ -+ SetState(kError); \ -+ VLOGF(1) << msg; \ -+ VLOGF(1) << "Calling NotifyError(" << error << ")"; \ -+ NotifyError(error); \ -+ } while (0) -+ -+namespace media { -+ -+namespace { -+// Need 2 surfaces for each frame: one for input data and one for -+// reconstructed picture, which is later used for reference. -+const size_t kMinSurfacesToEncode = 2; -+ -+// Subjectively chosen. -+const size_t kNumInputBuffers = 4; -+const size_t kMaxNumReferenceFrames = 4; -+ -+// TODO(owenlin): Adjust the value after b/71367113 is fixed. -+const size_t kExtraOutputBufferSize = 32768; // bytes -+ -+// We need up to kMaxNumReferenceFrames surfaces for reference, plus one -+// for input and one for encode (which will be added to the set of reference -+// frames for subsequent frames). Actual execution of HW encode is done -+// in parallel, and we want to process more frames in the meantime. -+// To have kNumInputBuffers in flight, we need a full set of reference + -+// encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and -+// (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames -+// in flight. -+const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode + -+ kMinSurfacesToEncode * (kNumInputBuffers - 1); -+ -+// An IDR every 2048 frames, an I frame every 256 and no B frames. -+// We choose IDR period to equal MaxFrameNum so it must be a power of 2. -+const int kIDRPeriod = 2048; -+const int kIPeriod = 256; -+const int kIPPeriod = 1; -+ -+const int kDefaultFramerate = 30; -+ -+// HRD parameters (ch. E.2.2 in spec). -+const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters. -+const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters. -+ -+const int kDefaultQP = 26; -+// All Intel codecs can do at least 4.1. -+const int kDefaultLevelIDC = 41; -+const int kChromaFormatIDC = 1; // 4:2:0 -+ -+// Arbitrarily chosen bitrate window size for rate control, in ms. -+const int kCPBWindowSizeMs = 1500; -+ -+// UMA errors that the VaapiVideoEncodeAccelerator class reports. -+enum VAVEAEncoderFailure { -+ VAAPI_ERROR = 0, -+ VAVEA_ENCODER_FAILURES_MAX, -+}; -+} -+ -+// Round |value| up to |alignment|, which must be a power of 2. -+static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) { -+ // Check that |alignment| is a power of 2. -+ DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); -+ return ((value + (alignment - 1)) & ~(alignment - 1)); -+} -+ -+static void ReportToUMA(VAVEAEncoderFailure failure) { -+ UMA_HISTOGRAM_ENUMERATION("Media.VAVEA.EncoderFailure", failure, -+ VAVEA_ENCODER_FAILURES_MAX + 1); -+} -+ -+struct VaapiVideoEncodeAccelerator::InputFrameRef { -+ InputFrameRef(const scoped_refptr& frame, bool force_keyframe) -+ : frame(frame), force_keyframe(force_keyframe) {} -+ const scoped_refptr frame; -+ const bool force_keyframe; -+}; -+ -+struct VaapiVideoEncodeAccelerator::BitstreamBufferRef { -+ BitstreamBufferRef(int32_t id, std::unique_ptr shm) -+ : id(id), shm(std::move(shm)) {} -+ const int32_t id; -+ const std::unique_ptr shm; -+}; -+ -+VideoEncodeAccelerator::SupportedProfiles -+VaapiVideoEncodeAccelerator::GetSupportedProfiles() { -+ return VaapiWrapper::GetSupportedEncodeProfiles(); -+} -+ -+static unsigned int Log2OfPowerOf2(unsigned int x) { -+ CHECK_GT(x, 0u); -+ DCHECK_EQ(x & (x - 1), 0u); -+ -+ int log = 0; -+ while (x > 1) { -+ x >>= 1; -+ ++log; -+ } -+ return log; -+} -+ -+VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator() -+ : profile_(VIDEO_CODEC_PROFILE_UNKNOWN), -+ mb_width_(0), -+ mb_height_(0), -+ output_buffer_byte_size_(0), -+ state_(kUninitialized), -+ frame_num_(0), -+ idr_pic_id_(0), -+ bitrate_(0), -+ framerate_(0), -+ cpb_size_(0), -+ encoding_parameters_changed_(false), -+ encoder_thread_("VAVEAEncoderThread"), -+ child_task_runner_(base::ThreadTaskRunnerHandle::Get()), -+ weak_this_ptr_factory_(this) { -+ VLOGF(2); -+ weak_this_ = weak_this_ptr_factory_.GetWeakPtr(); -+ max_ref_idx_l0_size_ = kMaxNumReferenceFrames; -+ qp_ = kDefaultQP; -+ idr_period_ = kIDRPeriod; -+ i_period_ = kIPeriod; -+ ip_period_ = kIPPeriod; -+} -+ -+VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() { -+ VLOGF(2); -+ DCHECK(child_task_runner_->BelongsToCurrentThread()); -+ DCHECK(!encoder_thread_.IsRunning()); -+} -+ -+bool VaapiVideoEncodeAccelerator::Initialize( -+ VideoPixelFormat format, -+ const gfx::Size& input_visible_size, -+ VideoCodecProfile output_profile, -+ uint32_t initial_bitrate, -+ Client* client) { -+ DCHECK(child_task_runner_->BelongsToCurrentThread()); -+ DCHECK(!encoder_thread_.IsRunning()); -+ DCHECK_EQ(state_, kUninitialized); -+ -+ VLOGF(2) << "Initializing VAVEA, input_format: " -+ << VideoPixelFormatToString(format) -+ << ", input_visible_size: " << input_visible_size.ToString() -+ << ", output_profile: " << GetProfileName(output_profile) -+ << ", initial_bitrate: " << initial_bitrate; -+ -+ client_ptr_factory_.reset(new base::WeakPtrFactory(client)); -+ client_ = client_ptr_factory_->GetWeakPtr(); -+ -+ const SupportedProfiles& profiles = GetSupportedProfiles(); -+ auto profile = find_if(profiles.begin(), profiles.end(), -+ [output_profile](const SupportedProfile& profile) { -+ return profile.profile == output_profile; -+ }); -+ if (profile == profiles.end()) { -+ VLOGF(1) << "Unsupported output profile " << GetProfileName(output_profile); -+ return false; -+ } -+ if (input_visible_size.width() > profile->max_resolution.width() || -+ input_visible_size.height() > profile->max_resolution.height()) { -+ VLOGF(1) << "Input size too big: " << input_visible_size.ToString() -+ << ", max supported size: " << profile->max_resolution.ToString(); -+ return false; -+ } -+ -+ if (format != PIXEL_FORMAT_I420) { -+ VLOGF(1) << "Unsupported input format: " -+ << VideoPixelFormatToString(format); -+ return false; -+ } -+ -+ profile_ = output_profile; -+ visible_size_ = input_visible_size; -+ // 4:2:0 format has to be 2-aligned. -+ DCHECK_EQ(visible_size_.width() % 2, 0); -+ DCHECK_EQ(visible_size_.height() % 2, 0); -+ coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16), -+ RoundUpToPowerOf2(visible_size_.height(), 16)); -+ mb_width_ = coded_size_.width() / 16; -+ mb_height_ = coded_size_.height() / 16; -+ output_buffer_byte_size_ = coded_size_.GetArea() + kExtraOutputBufferSize; -+ -+ UpdateRates(initial_bitrate, kDefaultFramerate); -+ -+ vaapi_wrapper_ = -+ VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile, -+ base::Bind(&ReportToUMA, VAAPI_ERROR)); -+ if (!vaapi_wrapper_.get()) { -+ VLOGF(1) << "Failed initializing VAAPI for profile " -+ << GetProfileName(output_profile); -+ return false; -+ } -+ -+ if (!encoder_thread_.Start()) { -+ VLOGF(1) << "Failed to start encoder thread"; -+ return false; -+ } -+ encoder_thread_task_runner_ = encoder_thread_.task_runner(); -+ -+ // Finish the remaining initialization on the encoder thread. -+ encoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask, -+ base::Unretained(this))); -+ -+ return true; -+} -+ -+void VaapiVideoEncodeAccelerator::InitializeTask() { -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ DCHECK_EQ(state_, kUninitialized); -+ VLOGF(2); -+ -+ va_surface_release_cb_ = BindToCurrentLoop( -+ base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID, -+ base::Unretained(this))); -+ -+ if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, coded_size_, -+ kNumSurfaces, -+ &available_va_surface_ids_)) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces"); -+ return; -+ } -+ -+ UpdateSPS(); -+ GeneratePackedSPS(); -+ -+ UpdatePPS(); -+ GeneratePackedPPS(); -+ -+ child_task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers, -+ coded_size_, output_buffer_byte_size_)); -+ -+ SetState(kEncoding); -+} -+ -+void VaapiVideoEncodeAccelerator::RecycleVASurfaceID( -+ VASurfaceID va_surface_id) { -+ DVLOGF(4) << "va_surface_id: " << va_surface_id; -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ -+ available_va_surface_ids_.push_back(va_surface_id); -+ EncodeFrameTask(); -+} -+ -+void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) { -+ current_pic_ = new H264Picture(); -+ -+ // If the current picture is an IDR picture, frame_num shall be equal to 0. -+ if (force_keyframe) -+ frame_num_ = 0; -+ -+ current_pic_->frame_num = frame_num_++; -+ frame_num_ %= idr_period_; -+ -+ if (current_pic_->frame_num == 0) { -+ current_pic_->idr = true; -+ // H264 spec mandates idr_pic_id to differ between two consecutive IDRs. -+ idr_pic_id_ ^= 1; -+ ref_pic_list0_.clear(); -+ } -+ -+ if (current_pic_->frame_num % i_period_ == 0) -+ current_pic_->type = H264SliceHeader::kISlice; -+ else -+ current_pic_->type = H264SliceHeader::kPSlice; -+ -+ if (current_pic_->type != H264SliceHeader::kBSlice) -+ current_pic_->ref = true; -+ -+ current_pic_->pic_order_cnt = current_pic_->frame_num * 2; -+ current_pic_->top_field_order_cnt = current_pic_->pic_order_cnt; -+ current_pic_->pic_order_cnt_lsb = current_pic_->pic_order_cnt; -+ -+ current_encode_job_->keyframe = current_pic_->idr; -+ -+ DVLOGF(4) << "Starting a new frame, type: " << current_pic_->type -+ << (force_keyframe ? " (forced keyframe)" : "") -+ << " frame_num: " << current_pic_->frame_num -+ << " POC: " << current_pic_->pic_order_cnt; -+} -+ -+void VaapiVideoEncodeAccelerator::EndFrame() { -+ DCHECK(current_pic_); -+ // Store the picture on the list of reference pictures and keep the list -+ // below maximum size, dropping oldest references. -+ if (current_pic_->ref) -+ ref_pic_list0_.push_front(current_encode_job_->recon_surface); -+ size_t max_num_ref_frames = -+ base::checked_cast(current_sps_.max_num_ref_frames); -+ while (ref_pic_list0_.size() > max_num_ref_frames) -+ ref_pic_list0_.pop_back(); -+ -+ submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release())); -+} -+ -+static void InitVAPicture(VAPictureH264* va_pic) { -+ memset(va_pic, 0, sizeof(*va_pic)); -+ va_pic->picture_id = VA_INVALID_ID; -+ va_pic->flags = VA_PICTURE_H264_INVALID; -+} -+ -+bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() { -+ DCHECK(current_pic_); -+ VAEncSequenceParameterBufferH264 seq_param; -+ memset(&seq_param, 0, sizeof(seq_param)); -+ -+#define SPS_TO_SP(a) seq_param.a = current_sps_.a; -+ SPS_TO_SP(seq_parameter_set_id); -+ SPS_TO_SP(level_idc); -+ -+ seq_param.intra_period = i_period_; -+ seq_param.intra_idr_period = idr_period_; -+ seq_param.ip_period = ip_period_; -+ seq_param.bits_per_second = bitrate_; -+ -+ SPS_TO_SP(max_num_ref_frames); -+ seq_param.picture_width_in_mbs = mb_width_; -+ seq_param.picture_height_in_mbs = mb_height_; -+ -+#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a; -+ SPS_TO_SP_FS(chroma_format_idc); -+ SPS_TO_SP_FS(frame_mbs_only_flag); -+ SPS_TO_SP_FS(log2_max_frame_num_minus4); -+ SPS_TO_SP_FS(pic_order_cnt_type); -+ SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4); -+#undef SPS_TO_SP_FS -+ -+ SPS_TO_SP(bit_depth_luma_minus8); -+ SPS_TO_SP(bit_depth_chroma_minus8); -+ -+ SPS_TO_SP(frame_cropping_flag); -+ if (current_sps_.frame_cropping_flag) { -+ SPS_TO_SP(frame_crop_left_offset); -+ SPS_TO_SP(frame_crop_right_offset); -+ SPS_TO_SP(frame_crop_top_offset); -+ SPS_TO_SP(frame_crop_bottom_offset); -+ } -+ -+ SPS_TO_SP(vui_parameters_present_flag); -+#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a; -+ SPS_TO_SP_VF(timing_info_present_flag); -+#undef SPS_TO_SP_VF -+ SPS_TO_SP(num_units_in_tick); -+ SPS_TO_SP(time_scale); -+#undef SPS_TO_SP -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType, -+ sizeof(seq_param), &seq_param)) -+ return false; -+ -+ VAEncPictureParameterBufferH264 pic_param; -+ memset(&pic_param, 0, sizeof(pic_param)); -+ -+ pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id(); -+ pic_param.CurrPic.TopFieldOrderCnt = current_pic_->top_field_order_cnt; -+ pic_param.CurrPic.BottomFieldOrderCnt = current_pic_->bottom_field_order_cnt; -+ pic_param.CurrPic.flags = 0; -+ -+ for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i) -+ InitVAPicture(&pic_param.ReferenceFrames[i]); -+ -+ DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames)); -+ RefPicList::const_iterator iter = ref_pic_list0_.begin(); -+ for (size_t i = 0; -+ i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end(); -+ ++iter, ++i) { -+ pic_param.ReferenceFrames[i].picture_id = (*iter)->id(); -+ pic_param.ReferenceFrames[i].flags = 0; -+ } -+ -+ pic_param.coded_buf = current_encode_job_->coded_buffer; -+ pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; -+ pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id; -+ pic_param.frame_num = current_pic_->frame_num; -+ pic_param.pic_init_qp = qp_; -+ pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1; -+ pic_param.pic_fields.bits.idr_pic_flag = current_pic_->idr; -+ pic_param.pic_fields.bits.reference_pic_flag = current_pic_->ref; -+#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a; -+ PPS_TO_PP_PF(entropy_coding_mode_flag); -+ PPS_TO_PP_PF(transform_8x8_mode_flag); -+ PPS_TO_PP_PF(deblocking_filter_control_present_flag); -+#undef PPS_TO_PP_PF -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, -+ sizeof(pic_param), &pic_param)) -+ return false; -+ -+ VAEncSliceParameterBufferH264 slice_param; -+ memset(&slice_param, 0, sizeof(slice_param)); -+ -+ slice_param.num_macroblocks = mb_width_ * mb_height_; -+ slice_param.macroblock_info = VA_INVALID_ID; -+ slice_param.slice_type = current_pic_->type; -+ slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; -+ slice_param.idr_pic_id = idr_pic_id_; -+ slice_param.pic_order_cnt_lsb = current_pic_->pic_order_cnt_lsb; -+ slice_param.num_ref_idx_active_override_flag = true; -+ -+ for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) -+ InitVAPicture(&slice_param.RefPicList0[i]); -+ -+ for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i) -+ InitVAPicture(&slice_param.RefPicList1[i]); -+ -+ DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0)); -+ iter = ref_pic_list0_.begin(); -+ for (size_t i = 0; -+ i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end(); -+ ++iter, ++i) { -+ InitVAPicture(&slice_param.RefPicList0[i]); -+ slice_param.RefPicList0[i].picture_id = (*iter)->id(); -+ slice_param.RefPicList0[i].flags = 0; -+ } -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, -+ sizeof(slice_param), &slice_param)) -+ return false; -+ -+ VAEncMiscParameterRateControl rate_control_param; -+ memset(&rate_control_param, 0, sizeof(rate_control_param)); -+ rate_control_param.bits_per_second = bitrate_; -+ rate_control_param.target_percentage = 90; -+ rate_control_param.window_size = kCPBWindowSizeMs; -+ rate_control_param.initial_qp = qp_; -+ rate_control_param.rc_flags.bits.disable_frame_skip = true; -+ -+ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( -+ VAEncMiscParameterTypeRateControl, sizeof(rate_control_param), -+ &rate_control_param)) -+ return false; -+ -+ VAEncMiscParameterFrameRate framerate_param; -+ memset(&framerate_param, 0, sizeof(framerate_param)); -+ framerate_param.framerate = framerate_; -+ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( -+ VAEncMiscParameterTypeFrameRate, sizeof(framerate_param), -+ &framerate_param)) -+ return false; -+ -+ VAEncMiscParameterHRD hrd_param; -+ memset(&hrd_param, 0, sizeof(hrd_param)); -+ hrd_param.buffer_size = cpb_size_; -+ hrd_param.initial_buffer_fullness = cpb_size_ / 2; -+ if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( -+ VAEncMiscParameterTypeHRD, sizeof(hrd_param), &hrd_param)) -+ return false; -+ -+ return true; -+} -+ -+bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() { -+ DCHECK(current_pic_); -+ if (current_pic_->type != H264SliceHeader::kISlice) -+ return true; -+ -+ // Submit SPS. -+ VAEncPackedHeaderParameterBuffer par_buffer; -+ memset(&par_buffer, 0, sizeof(par_buffer)); -+ par_buffer.type = VAEncPackedHeaderSequence; -+ par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8; -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, -+ sizeof(par_buffer), &par_buffer)) -+ return false; -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, -+ packed_sps_.BytesInBuffer(), -+ packed_sps_.data())) -+ return false; -+ -+ // Submit PPS. -+ memset(&par_buffer, 0, sizeof(par_buffer)); -+ par_buffer.type = VAEncPackedHeaderPicture; -+ par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8; -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, -+ sizeof(par_buffer), &par_buffer)) -+ return false; -+ -+ if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, -+ packed_pps_.BytesInBuffer(), -+ packed_pps_.data())) -+ return false; -+ -+ return true; -+} -+ -+bool VaapiVideoEncodeAccelerator::ExecuteEncode() { -+ DCHECK(current_pic_); -+ DVLOGF(4) << "Encoding frame_num: " << current_pic_->frame_num; -+ return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( -+ current_encode_job_->input_surface->id()); -+} -+ -+bool VaapiVideoEncodeAccelerator::UploadFrame( -+ const scoped_refptr& frame) { -+ return vaapi_wrapper_->UploadVideoFrameToSurface( -+ frame, current_encode_job_->input_surface->id()); -+} -+ -+void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() { -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ -+ if (state_ != kEncoding) -+ return; -+ -+ while (!submitted_encode_jobs_.empty()) { -+ linked_ptr encode_job = submitted_encode_jobs_.front(); -+ // An null job indicates a flush command. -+ if (encode_job == nullptr) { -+ submitted_encode_jobs_.pop(); -+ DVLOGF(2) << "FlushDone"; -+ DCHECK(flush_callback_); -+ child_task_runner_->PostTask( -+ FROM_HERE, base::BindOnce(std::move(flush_callback_), true)); -+ continue; -+ } -+ -+ if (available_bitstream_buffers_.empty()) -+ break; -+ auto buffer = available_bitstream_buffers_.front(); -+ -+ available_bitstream_buffers_.pop(); -+ submitted_encode_jobs_.pop(); -+ -+ uint8_t* target_data = reinterpret_cast(buffer->shm->memory()); -+ -+ size_t data_size = 0; -+ if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer( -+ encode_job->coded_buffer, encode_job->input_surface->id(), -+ target_data, buffer->shm->size(), &data_size)) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer"); -+ return; -+ } -+ -+ DVLOGF(4) << "Returning bitstream buffer " -+ << (encode_job->keyframe ? "(keyframe)" : "") -+ << " id: " << buffer->id << " size: " << data_size; -+ -+ child_task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&Client::BitstreamBufferReady, client_, buffer->id, -+ data_size, encode_job->keyframe, encode_job->timestamp)); -+ break; -+ } -+} -+ -+void VaapiVideoEncodeAccelerator::Encode(const scoped_refptr& frame, -+ bool force_keyframe) { -+ DVLOGF(4) << "Frame timestamp: " << frame->timestamp().InMilliseconds() -+ << " force_keyframe: " << force_keyframe; -+ DCHECK(child_task_runner_->BelongsToCurrentThread()); -+ -+ encoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask, -+ base::Unretained(this), frame, force_keyframe)); -+} -+ -+bool VaapiVideoEncodeAccelerator::PrepareNextJob(base::TimeDelta timestamp) { -+ if (available_va_surface_ids_.size() < kMinSurfacesToEncode) -+ return false; -+ -+ DCHECK(!current_encode_job_); -+ current_encode_job_.reset(new EncodeJob()); -+ -+ if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_, -+ ¤t_encode_job_->coded_buffer)) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer"); -+ return false; -+ } -+ -+ current_encode_job_->timestamp = timestamp; -+ -+ current_encode_job_->input_surface = new VASurface( -+ available_va_surface_ids_.back(), coded_size_, -+ vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); -+ available_va_surface_ids_.pop_back(); -+ -+ current_encode_job_->recon_surface = new VASurface( -+ available_va_surface_ids_.back(), coded_size_, -+ vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); -+ available_va_surface_ids_.pop_back(); -+ -+ // Reference surfaces are needed until the job is done, but they get -+ // removed from ref_pic_list0_ when it's full at the end of job submission. -+ // Keep refs to them along with the job and only release after sync. -+ current_encode_job_->reference_surfaces = ref_pic_list0_; -+ -+ return true; -+} -+ -+void VaapiVideoEncodeAccelerator::EncodeTask( -+ const scoped_refptr& frame, -+ bool force_keyframe) { -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ DCHECK_NE(state_, kUninitialized); -+ -+ encoder_input_queue_.push( -+ make_linked_ptr(new InputFrameRef(frame, force_keyframe))); -+ EncodeFrameTask(); -+} -+ -+void VaapiVideoEncodeAccelerator::EncodeFrameTask() { -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ -+ if (state_ != kEncoding || encoder_input_queue_.empty()) -+ return; -+ -+ if (!PrepareNextJob(encoder_input_queue_.front()->frame->timestamp())) { -+ DVLOGF(4) << "Not ready for next frame yet"; -+ return; -+ } -+ -+ linked_ptr frame_ref = encoder_input_queue_.front(); -+ encoder_input_queue_.pop(); -+ -+ if (!UploadFrame(frame_ref->frame)) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW."); -+ return; -+ } -+ -+ BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_); -+ encoding_parameters_changed_ = false; -+ -+ if (!SubmitFrameParameters()) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters."); -+ return; -+ } -+ -+ if (!SubmitHeadersIfNeeded()) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers."); -+ return; -+ } -+ -+ if (!ExecuteEncode()) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW."); -+ return; -+ } -+ -+ EndFrame(); -+ TryToReturnBitstreamBuffer(); -+} -+ -+void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer( -+ const BitstreamBuffer& buffer) { -+ DVLOGF(4) << "id: " << buffer.id(); -+ DCHECK(child_task_runner_->BelongsToCurrentThread()); -+ -+ if (buffer.size() < output_buffer_byte_size_) { -+ NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small"); -+ return; -+ } -+ -+ std::unique_ptr shm( -+ new SharedMemoryRegion(buffer, false)); -+ if (!shm->Map()) { -+ NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory."); -+ return; -+ } -+ -+ std::unique_ptr buffer_ref( -+ new BitstreamBufferRef(buffer.id(), std::move(shm))); -+ -+ encoder_thread_task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask, -+ base::Unretained(this), base::Passed(&buffer_ref))); -+} -+ -+void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask( -+ std::unique_ptr buffer_ref) { -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ DCHECK_NE(state_, kUninitialized); -+ -+ available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release())); -+ TryToReturnBitstreamBuffer(); -+} -+ -+void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange( -+ uint32_t bitrate, -+ uint32_t framerate) { -+ VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; -+ DCHECK(child_task_runner_->BelongsToCurrentThread()); -+ -+ encoder_thread_task_runner_->PostTask( -+ FROM_HERE, -+ base::Bind( -+ &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask, -+ base::Unretained(this), bitrate, framerate)); -+} -+ -+void VaapiVideoEncodeAccelerator::UpdateRates(uint32_t bitrate, -+ uint32_t framerate) { -+ if (encoder_thread_.IsRunning()) -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ DCHECK_NE(bitrate, 0u); -+ DCHECK_NE(framerate, 0u); -+ bitrate_ = bitrate; -+ framerate_ = framerate; -+ cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000; -+} -+ -+void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask( -+ uint32_t bitrate, -+ uint32_t framerate) { -+ VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ DCHECK_NE(state_, kUninitialized); -+ -+ // This is a workaround to zero being temporarily, as part of the initial -+ // setup, provided by the webrtc video encode and a zero bitrate and -+ // framerate not being accepted by VAAPI -+ // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps -+ // it could be pulled up to RTCVideoEncoder -+ if (bitrate < 1) -+ bitrate = 1; -+ if (framerate < 1) -+ framerate = 1; -+ -+ if (bitrate_ == bitrate && framerate_ == framerate) -+ return; -+ -+ UpdateRates(bitrate, framerate); -+ -+ UpdateSPS(); -+ GeneratePackedSPS(); -+ -+ // Submit new parameters along with next frame that will be processed. -+ encoding_parameters_changed_ = true; -+} -+ -+void VaapiVideoEncodeAccelerator::Flush(FlushCallback flush_callback) { -+ DVLOGF(2); -+ DCHECK(child_task_runner_->BelongsToCurrentThread()); -+ if (flush_callback_) { -+ NOTIFY_ERROR(kIllegalStateError, "There is a pending flush"); -+ std::move(flush_callback).Run(false); -+ return; -+ } -+ flush_callback_ = std::move(flush_callback); -+ encoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::FlushTask, -+ base::Unretained(this))); -+} -+ -+void VaapiVideoEncodeAccelerator::FlushTask() { -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ -+ // Insert an null job to indicate a flush command. -+ submitted_encode_jobs_.push(linked_ptr(nullptr)); -+ TryToReturnBitstreamBuffer(); -+} -+ -+void VaapiVideoEncodeAccelerator::Destroy() { -+ DCHECK(child_task_runner_->BelongsToCurrentThread()); -+ -+ // Can't call client anymore after Destroy() returns. -+ client_ptr_factory_.reset(); -+ weak_this_ptr_factory_.InvalidateWeakPtrs(); -+ -+ // Early-exit encoder tasks if they are running and join the thread. -+ if (encoder_thread_.IsRunning()) { -+ encoder_thread_.task_runner()->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask, -+ base::Unretained(this))); -+ encoder_thread_.Stop(); -+ } -+ -+ if (flush_callback_) -+ std::move(flush_callback_).Run(false); -+ -+ delete this; -+} -+ -+void VaapiVideoEncodeAccelerator::DestroyTask() { -+ VLOGF(2); -+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -+ SetState(kError); -+} -+ -+void VaapiVideoEncodeAccelerator::UpdateSPS() { -+ memset(¤t_sps_, 0, sizeof(H264SPS)); -+ -+ // Spec A.2 and A.3. -+ switch (profile_) { -+ case H264PROFILE_BASELINE: -+ // Due to crbug.com/345569, we don't distinguish between constrained -+ // and non-constrained baseline profiles. Since many codecs can't do -+ // non-constrained, and constrained is usually what we mean (and it's a -+ // subset of non-constrained), default to it. -+ current_sps_.profile_idc = H264SPS::kProfileIDCBaseline; -+ current_sps_.constraint_set0_flag = true; -+ break; -+ case H264PROFILE_MAIN: -+ current_sps_.profile_idc = H264SPS::kProfileIDCMain; -+ current_sps_.constraint_set1_flag = true; -+ break; -+ case H264PROFILE_HIGH: -+ current_sps_.profile_idc = H264SPS::kProfileIDCHigh; -+ break; -+ default: -+ NOTIMPLEMENTED(); -+ return; -+ } -+ -+ current_sps_.level_idc = kDefaultLevelIDC; -+ current_sps_.seq_parameter_set_id = 0; -+ current_sps_.chroma_format_idc = kChromaFormatIDC; -+ -+ DCHECK_GE(idr_period_, 1u << 4); -+ current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4; -+ current_sps_.pic_order_cnt_type = 0; -+ current_sps_.log2_max_pic_order_cnt_lsb_minus4 = -+ Log2OfPowerOf2(idr_period_ * 2) - 4; -+ current_sps_.max_num_ref_frames = max_ref_idx_l0_size_; -+ -+ current_sps_.frame_mbs_only_flag = true; -+ -+ DCHECK_GT(mb_width_, 0u); -+ DCHECK_GT(mb_height_, 0u); -+ current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1; -+ DCHECK(current_sps_.frame_mbs_only_flag); -+ current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1; -+ -+ if (visible_size_ != coded_size_) { -+ // Visible size differs from coded size, fill crop information. -+ current_sps_.frame_cropping_flag = true; -+ DCHECK(!current_sps_.separate_colour_plane_flag); -+ // Spec table 6-1. Only 4:2:0 for now. -+ DCHECK_EQ(current_sps_.chroma_format_idc, 1); -+ // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0. -+ const unsigned int crop_unit_x = 2; -+ const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag); -+ current_sps_.frame_crop_left_offset = 0; -+ current_sps_.frame_crop_right_offset = -+ (coded_size_.width() - visible_size_.width()) / crop_unit_x; -+ current_sps_.frame_crop_top_offset = 0; -+ current_sps_.frame_crop_bottom_offset = -+ (coded_size_.height() - visible_size_.height()) / crop_unit_y; -+ } -+ -+ current_sps_.vui_parameters_present_flag = true; -+ current_sps_.timing_info_present_flag = true; -+ current_sps_.num_units_in_tick = 1; -+ current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec. -+ current_sps_.fixed_frame_rate_flag = true; -+ -+ current_sps_.nal_hrd_parameters_present_flag = true; -+ // H.264 spec ch. E.2.2. -+ current_sps_.cpb_cnt_minus1 = 0; -+ current_sps_.bit_rate_scale = kBitRateScale; -+ current_sps_.cpb_size_scale = kCPBSizeScale; -+ current_sps_.bit_rate_value_minus1[0] = -+ (bitrate_ >> (kBitRateScale + H264SPS::kBitRateScaleConstantTerm)) - 1; -+ current_sps_.cpb_size_value_minus1[0] = -+ (cpb_size_ >> (kCPBSizeScale + H264SPS::kCPBSizeScaleConstantTerm)) - 1; -+ current_sps_.cbr_flag[0] = true; -+ current_sps_.initial_cpb_removal_delay_length_minus_1 = -+ H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; -+ current_sps_.cpb_removal_delay_length_minus1 = -+ H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; -+ current_sps_.dpb_output_delay_length_minus1 = -+ H264SPS::kDefaultDPBOutputDelayLength - 1; -+ current_sps_.time_offset_length = H264SPS::kDefaultTimeOffsetLength; -+ current_sps_.low_delay_hrd_flag = false; -+} -+ -+void VaapiVideoEncodeAccelerator::GeneratePackedSPS() { -+ packed_sps_.Reset(); -+ -+ packed_sps_.BeginNALU(H264NALU::kSPS, 3); -+ -+ packed_sps_.AppendBits(8, current_sps_.profile_idc); -+ packed_sps_.AppendBool(current_sps_.constraint_set0_flag); -+ packed_sps_.AppendBool(current_sps_.constraint_set1_flag); -+ packed_sps_.AppendBool(current_sps_.constraint_set2_flag); -+ packed_sps_.AppendBool(current_sps_.constraint_set3_flag); -+ packed_sps_.AppendBool(current_sps_.constraint_set4_flag); -+ packed_sps_.AppendBool(current_sps_.constraint_set5_flag); -+ packed_sps_.AppendBits(2, 0); // reserved_zero_2bits -+ packed_sps_.AppendBits(8, current_sps_.level_idc); -+ packed_sps_.AppendUE(current_sps_.seq_parameter_set_id); -+ -+ if (current_sps_.profile_idc == H264SPS::kProfileIDCHigh) { -+ packed_sps_.AppendUE(current_sps_.chroma_format_idc); -+ if (current_sps_.chroma_format_idc == 3) -+ packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag); -+ packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8); -+ packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8); -+ packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag); -+ packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag); -+ CHECK(!current_sps_.seq_scaling_matrix_present_flag); -+ } -+ -+ packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4); -+ packed_sps_.AppendUE(current_sps_.pic_order_cnt_type); -+ if (current_sps_.pic_order_cnt_type == 0) -+ packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4); -+ else if (current_sps_.pic_order_cnt_type == 1) { -+ CHECK(1); -+ } -+ -+ packed_sps_.AppendUE(current_sps_.max_num_ref_frames); -+ packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag); -+ packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1); -+ packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1); -+ -+ packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag); -+ if (!current_sps_.frame_mbs_only_flag) -+ packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag); -+ -+ packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag); -+ -+ packed_sps_.AppendBool(current_sps_.frame_cropping_flag); -+ if (current_sps_.frame_cropping_flag) { -+ packed_sps_.AppendUE(current_sps_.frame_crop_left_offset); -+ packed_sps_.AppendUE(current_sps_.frame_crop_right_offset); -+ packed_sps_.AppendUE(current_sps_.frame_crop_top_offset); -+ packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset); -+ } -+ -+ packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag); -+ if (current_sps_.vui_parameters_present_flag) { -+ packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag -+ packed_sps_.AppendBool(false); // overscan_info_present_flag -+ packed_sps_.AppendBool(false); // video_signal_type_present_flag -+ packed_sps_.AppendBool(false); // chroma_loc_info_present_flag -+ -+ packed_sps_.AppendBool(current_sps_.timing_info_present_flag); -+ if (current_sps_.timing_info_present_flag) { -+ packed_sps_.AppendBits(32, current_sps_.num_units_in_tick); -+ packed_sps_.AppendBits(32, current_sps_.time_scale); -+ packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag); -+ } -+ -+ packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag); -+ if (current_sps_.nal_hrd_parameters_present_flag) { -+ packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1); -+ packed_sps_.AppendBits(4, current_sps_.bit_rate_scale); -+ packed_sps_.AppendBits(4, current_sps_.cpb_size_scale); -+ CHECK_LT(base::checked_cast(current_sps_.cpb_cnt_minus1), -+ arraysize(current_sps_.bit_rate_value_minus1)); -+ for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) { -+ packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]); -+ packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]); -+ packed_sps_.AppendBool(current_sps_.cbr_flag[i]); -+ } -+ packed_sps_.AppendBits( -+ 5, current_sps_.initial_cpb_removal_delay_length_minus_1); -+ packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1); -+ packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1); -+ packed_sps_.AppendBits(5, current_sps_.time_offset_length); -+ } -+ -+ packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag -+ if (current_sps_.nal_hrd_parameters_present_flag) -+ packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag); -+ -+ packed_sps_.AppendBool(false); // pic_struct_present_flag -+ packed_sps_.AppendBool(true); // bitstream_restriction_flag -+ -+ packed_sps_.AppendBool(false); // motion_vectors_over_pic_boundaries_flag -+ packed_sps_.AppendUE(2); // max_bytes_per_pic_denom -+ packed_sps_.AppendUE(1); // max_bits_per_mb_denom -+ packed_sps_.AppendUE(16); // log2_max_mv_length_horizontal -+ packed_sps_.AppendUE(16); // log2_max_mv_length_vertical -+ -+ // Explicitly set max_num_reorder_frames to 0 to allow the decoder to -+ // output pictures early. -+ packed_sps_.AppendUE(0); // max_num_reorder_frames -+ -+ // The value of max_dec_frame_buffering shall be greater than or equal to -+ // max_num_ref_frames. -+ const unsigned int max_dec_frame_buffering = -+ current_sps_.max_num_ref_frames; -+ packed_sps_.AppendUE(max_dec_frame_buffering); -+ } -+ -+ packed_sps_.FinishNALU(); -+} -+ -+void VaapiVideoEncodeAccelerator::UpdatePPS() { -+ memset(¤t_pps_, 0, sizeof(H264PPS)); -+ -+ current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id; -+ current_pps_.pic_parameter_set_id = 0; -+ -+ current_pps_.entropy_coding_mode_flag = -+ current_sps_.profile_idc >= H264SPS::kProfileIDCMain; -+ -+ CHECK_GT(max_ref_idx_l0_size_, 0u); -+ current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1; -+ current_pps_.num_ref_idx_l1_default_active_minus1 = 0; -+ DCHECK_LE(qp_, 51u); -+ current_pps_.pic_init_qp_minus26 = qp_ - 26; -+ current_pps_.deblocking_filter_control_present_flag = true; -+ current_pps_.transform_8x8_mode_flag = -+ (current_sps_.profile_idc == H264SPS::kProfileIDCHigh); -+} -+ -+void VaapiVideoEncodeAccelerator::GeneratePackedPPS() { -+ packed_pps_.Reset(); -+ -+ packed_pps_.BeginNALU(H264NALU::kPPS, 3); -+ -+ packed_pps_.AppendUE(current_pps_.pic_parameter_set_id); -+ packed_pps_.AppendUE(current_pps_.seq_parameter_set_id); -+ packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag); -+ packed_pps_.AppendBool( -+ current_pps_.bottom_field_pic_order_in_frame_present_flag); -+ CHECK_EQ(current_pps_.num_slice_groups_minus1, 0); -+ packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1); -+ -+ packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1); -+ packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1); -+ -+ packed_pps_.AppendBool(current_pps_.weighted_pred_flag); -+ packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc); -+ -+ packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26); -+ packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26); -+ packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset); -+ -+ packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag); -+ packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag); -+ packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag); -+ -+ packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag); -+ packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag); -+ DCHECK(!current_pps_.pic_scaling_matrix_present_flag); -+ packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset); -+ -+ packed_pps_.FinishNALU(); -+} -+ -+void VaapiVideoEncodeAccelerator::SetState(State state) { -+ // Only touch state on encoder thread, unless it's not running. -+ if (encoder_thread_.IsRunning() && -+ !encoder_thread_task_runner_->BelongsToCurrentThread()) { -+ encoder_thread_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::SetState, -+ base::Unretained(this), state)); -+ return; -+ } -+ -+ VLOGF(2) << "setting state to: " << state; -+ state_ = state; -+} -+ -+void VaapiVideoEncodeAccelerator::NotifyError(Error error) { -+ if (!child_task_runner_->BelongsToCurrentThread()) { -+ child_task_runner_->PostTask( -+ FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::NotifyError, -+ weak_this_, error)); -+ return; -+ } -+ -+ if (client_) { -+ client_->NotifyError(error); -+ client_ptr_factory_.reset(); -+ } -+} -+ -+VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob() -+ : coded_buffer(VA_INVALID_ID), keyframe(false) {} -+ -+VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.h -@@ -0,0 +1,275 @@ -+// Copyright 2014 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#ifndef MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ -+#define MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include "base/containers/queue.h" -+#include "base/macros.h" -+#include "base/memory/linked_ptr.h" -+#include "base/threading/thread.h" -+#include "media/filters/h264_bitstream_buffer.h" -+#include "media/gpu/h264_dpb.h" -+#include "media/gpu/media_gpu_export.h" -+#include "media/gpu/vaapi/va_surface.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+#include "media/video/video_encode_accelerator.h" -+ -+namespace media { -+ -+// A VideoEncodeAccelerator implementation that uses VA-API -+// (http://www.freedesktop.org/wiki/Software/vaapi) for HW-accelerated -+// video encode. -+class MEDIA_GPU_EXPORT VaapiVideoEncodeAccelerator -+ : public VideoEncodeAccelerator { -+ public: -+ VaapiVideoEncodeAccelerator(); -+ ~VaapiVideoEncodeAccelerator() override; -+ -+ // VideoEncodeAccelerator implementation. -+ VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles() override; -+ bool Initialize(VideoPixelFormat format, -+ const gfx::Size& input_visible_size, -+ VideoCodecProfile output_profile, -+ uint32_t initial_bitrate, -+ Client* client) override; -+ void Encode(const scoped_refptr& frame, -+ bool force_keyframe) override; -+ void UseOutputBitstreamBuffer(const BitstreamBuffer& buffer) override; -+ void RequestEncodingParametersChange(uint32_t bitrate, -+ uint32_t framerate) override; -+ void Destroy() override; -+ void Flush(FlushCallback flush_callback) override; -+ -+ private: -+ // Reference picture list. -+ typedef std::list> RefPicList; -+ -+ // Encode job for one frame. Created when an input frame is awaiting and -+ // enough resources are available to proceed. Once the job is prepared and -+ // submitted to the hardware, it awaits on the submitted_encode_jobs_ queue -+ // for an output bitstream buffer to become available. Once one is ready, -+ // the encoded bytes are downloaded to it and job resources are released -+ // and become available for reuse. -+ struct EncodeJob { -+ // Input surface for video frame data. -+ scoped_refptr input_surface; -+ // Surface for a reconstructed picture, which is used for reference -+ // for subsequent frames. -+ scoped_refptr recon_surface; -+ // Buffer that will contain output bitstream for this frame. -+ VABufferID coded_buffer; -+ // Reference surfaces required to encode this picture. We keep references -+ // to them here, because we may discard some of them from ref_pic_list* -+ // before the HW job is done. -+ RefPicList reference_surfaces; -+ // True if this job will produce a keyframe. Used to report -+ // to BitstreamBufferReady(). -+ bool keyframe; -+ // Source timestamp. -+ base::TimeDelta timestamp; -+ -+ EncodeJob(); -+ ~EncodeJob(); -+ }; -+ -+ // Encoder state. -+ enum State { -+ kUninitialized, -+ kEncoding, -+ kError, -+ }; -+ -+ // Holds input frames coming from the client ready to be encoded. -+ struct InputFrameRef; -+ // Holds output buffers coming from the client ready to be filled. -+ struct BitstreamBufferRef; -+ -+ // Tasks for each of the VEA interface calls to be executed on the -+ // encoder thread. -+ void InitializeTask(); -+ void EncodeTask(const scoped_refptr& frame, bool force_keyframe); -+ void UseOutputBitstreamBufferTask( -+ std::unique_ptr buffer_ref); -+ void RequestEncodingParametersChangeTask(uint32_t bitrate, -+ uint32_t framerate); -+ void DestroyTask(); -+ void FlushTask(); -+ -+ // Prepare and schedule an encode job if we have an input to encode -+ // and enough resources to proceed. -+ void EncodeFrameTask(); -+ -+ // Fill current_sps_/current_pps_ with current values. -+ void UpdateSPS(); -+ void UpdatePPS(); -+ void UpdateRates(uint32_t bitrate, uint32_t framerate); -+ -+ // Generate packed SPS and PPS in packed_sps_/packed_pps_, using -+ // values in current_sps_/current_pps_. -+ void GeneratePackedSPS(); -+ void GeneratePackedPPS(); -+ -+ // Check if we have sufficient resources for a new encode job, claim them and -+ // fill current_encode_job_ with them. -+ // Return false if we cannot start a new job yet, true otherwise. -+ bool PrepareNextJob(base::TimeDelta timestamp); -+ -+ // Begin a new frame, making it a keyframe if |force_keyframe| is true, -+ // updating current_pic_. -+ void BeginFrame(bool force_keyframe); -+ -+ // End current frame, updating reference picture lists and storing current -+ // job in the jobs awaiting completion on submitted_encode_jobs_. -+ void EndFrame(); -+ -+ // Submit parameters for the current frame to the hardware. -+ bool SubmitFrameParameters(); -+ // Submit keyframe headers to the hardware if the current frame is a keyframe. -+ bool SubmitHeadersIfNeeded(); -+ -+ // Upload image data from |frame| to the input surface for current job. -+ bool UploadFrame(const scoped_refptr& frame); -+ -+ // Execute encode in hardware. This does not block and will return before -+ // the job is finished. -+ bool ExecuteEncode(); -+ -+ // Callback that returns a no longer used VASurfaceID to -+ // available_va_surface_ids_ for reuse. -+ void RecycleVASurfaceID(VASurfaceID va_surface_id); -+ -+ // Tries to return a bitstream buffer if both a submitted job awaits to -+ // be completed and we have bitstream buffers from the client available -+ // to download the encoded data to. -+ void TryToReturnBitstreamBuffer(); -+ -+ // Puts the encoder into en error state and notifies client about the error. -+ void NotifyError(Error error); -+ -+ // Sets the encoder state on the correct thread. -+ void SetState(State state); -+ -+ // VaapiWrapper is the owner of all HW resources (surfaces and buffers) -+ // and will free them on destruction. -+ scoped_refptr vaapi_wrapper_; -+ -+ // Input profile and sizes. -+ VideoCodecProfile profile_; -+ gfx::Size visible_size_; -+ gfx::Size coded_size_; // Macroblock-aligned. -+ // Width/height in macroblocks. -+ unsigned int mb_width_; -+ unsigned int mb_height_; -+ -+ // Maximum size of the reference list 0. -+ unsigned int max_ref_idx_l0_size_; -+ -+ // Initial QP. -+ unsigned int qp_; -+ -+ // IDR frame period. -+ unsigned int idr_period_; -+ // I frame period. -+ unsigned int i_period_; -+ // IP period, i.e. how often do we need to have either an I or a P frame in -+ // the stream. Period of 1 means we can have no B frames. -+ unsigned int ip_period_; -+ -+ // Size in bytes required for input bitstream buffers. -+ size_t output_buffer_byte_size_; -+ -+ // All of the members below must be accessed on the encoder_thread_, -+ // while it is running. -+ -+ // Encoder state. Encode tasks will only run in kEncoding state. -+ State state_; -+ -+ // frame_num to be used for the next frame. -+ unsigned int frame_num_; -+ // idr_pic_id to be used for the next frame. -+ unsigned int idr_pic_id_; -+ -+ // Current bitrate in bps. -+ unsigned int bitrate_; -+ // Current fps. -+ unsigned int framerate_; -+ // CPB size in bits, i.e. bitrate in kbps * window size in ms/1000. -+ unsigned int cpb_size_; -+ // True if the parameters have changed and we need to submit a keyframe -+ // with updated parameters. -+ bool encoding_parameters_changed_; -+ -+ // Job currently being prepared for encode. -+ std::unique_ptr current_encode_job_; -+ -+ // Current SPS, PPS and their packed versions. Packed versions are their NALUs -+ // in AnnexB format *without* emulation prevention three-byte sequences -+ // (those will be added by the driver). -+ H264SPS current_sps_; -+ H264BitstreamBuffer packed_sps_; -+ H264PPS current_pps_; -+ H264BitstreamBuffer packed_pps_; -+ -+ // Picture currently being prepared for encode. -+ scoped_refptr current_pic_; -+ -+ // VA surfaces available for reuse. -+ std::vector available_va_surface_ids_; -+ -+ // VA buffers for coded frames. -+ std::vector available_va_buffer_ids_; -+ -+ // Currently active reference surfaces. -+ RefPicList ref_pic_list0_; -+ -+ // Callback via which finished VA surfaces are returned to us. -+ VASurface::ReleaseCB va_surface_release_cb_; -+ -+ // VideoFrames passed from the client, waiting to be encoded. -+ base::queue> encoder_input_queue_; -+ -+ // BitstreamBuffers mapped, ready to be filled. -+ base::queue> available_bitstream_buffers_; -+ -+ // Jobs submitted for encode, awaiting bitstream buffers to become available. -+ // A pending flush command, indicated by a null job, will be also put in the -+ // queue. -+ base::queue> submitted_encode_jobs_; -+ -+ // Encoder thread. All tasks are executed on it. -+ base::Thread encoder_thread_; -+ scoped_refptr encoder_thread_task_runner_; -+ -+ const scoped_refptr child_task_runner_; -+ -+ // To expose client callbacks from VideoEncodeAccelerator. -+ // NOTE: all calls to these objects *MUST* be executed on -+ // child_task_runner_. -+ std::unique_ptr> client_ptr_factory_; -+ base::WeakPtr client_; -+ -+ // WeakPtr to post from the encoder thread back to the ChildThread, as it may -+ // outlive this. Posting from the ChildThread using base::Unretained(this) -+ // to the encoder thread is safe, because |this| always outlives the encoder -+ // thread (it's a member of this class). -+ base::WeakPtr weak_this_; -+ -+ // The completion callback of the Flush() function. -+ FlushCallback flush_callback_; -+ -+ base::WeakPtrFactory weak_this_ptr_factory_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiVideoEncodeAccelerator); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_wrapper.cc -@@ -0,0 +1,1372 @@ -+// Copyright 2013 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+ -+#include "media/gpu/vaapi/vaapi_wrapper.h" -+ -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include "base/bind.h" -+#include "base/callback_helpers.h" -+#include "base/environment.h" -+#include "base/logging.h" -+#include "base/macros.h" -+#include "base/numerics/safe_conversions.h" -+#include "base/stl_util.h" -+#include "base/sys_info.h" -+#include "build/build_config.h" -+ -+// Auto-generated for dlopen libva libraries -+#include "media/gpu/vaapi/va_stubs.h" -+ -+#include "media/gpu/vaapi/vaapi_picture.h" -+#include "third_party/libyuv/include/libyuv.h" -+#include "ui/gfx/buffer_format_util.h" -+#include "ui/gfx/native_pixmap.h" -+#include "ui/gl/gl_bindings.h" -+#include "ui/gl/gl_implementation.h" -+ -+#if defined(USE_X11) -+#include -+#include "ui/gfx/x/x11_types.h" // nogncheck -+#endif -+ -+#if defined(USE_OZONE) -+#include "ui/ozone/public/ozone_platform.h" -+#include "ui/ozone/public/surface_factory_ozone.h" -+#endif -+ -+using media_gpu_vaapi::kModuleVa; -+using media_gpu_vaapi::kModuleVa_drm; -+#if defined(USE_X11) -+using media_gpu_vaapi::kModuleVa_x11; -+#endif -+using media_gpu_vaapi::InitializeStubs; -+using media_gpu_vaapi::StubPathMap; -+ -+#define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ -+ do { \ -+ LOG(ERROR) << err_msg << " VA error: " << vaErrorStr(va_error); \ -+ report_error_to_uma_cb_.Run(); \ -+ } while (0) -+ -+#define VA_LOG_ON_ERROR(va_error, err_msg) \ -+ do { \ -+ if ((va_error) != VA_STATUS_SUCCESS) \ -+ LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ -+ } while (0) -+ -+#define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ -+ do { \ -+ if ((va_error) != VA_STATUS_SUCCESS) { \ -+ LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ -+ return (ret); \ -+ } \ -+ } while (0) -+ -+namespace { -+ -+uint32_t BufferFormatToVAFourCC(gfx::BufferFormat fmt) { -+ switch (fmt) { -+ case gfx::BufferFormat::BGRX_8888: -+ return VA_FOURCC_BGRX; -+ case gfx::BufferFormat::BGRA_8888: -+ return VA_FOURCC_BGRA; -+ case gfx::BufferFormat::RGBX_8888: -+ return VA_FOURCC_RGBX; -+ case gfx::BufferFormat::UYVY_422: -+ return VA_FOURCC_UYVY; -+ case gfx::BufferFormat::YVU_420: -+ return VA_FOURCC_YV12; -+ default: -+ NOTREACHED(); -+ return 0; -+ } -+} -+ -+uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt) { -+ switch (fmt) { -+ case gfx::BufferFormat::UYVY_422: -+ return VA_RT_FORMAT_YUV422; -+ case gfx::BufferFormat::BGRX_8888: -+ case gfx::BufferFormat::BGRA_8888: -+ case gfx::BufferFormat::RGBX_8888: -+ return VA_RT_FORMAT_RGB32; -+ case gfx::BufferFormat::YVU_420: -+ return VA_RT_FORMAT_YUV420; -+ default: -+ NOTREACHED(); -+ return 0; -+ } -+} -+ -+} // namespace -+ -+namespace media { -+ -+namespace { -+ -+// Maximum framerate of encoded profile. This value is an arbitary limit -+// and not taken from HW documentation. -+const int kMaxEncoderFramerate = 30; -+ -+// Attributes required for encode. This only applies to video encode, not JPEG -+// encode. -+static const VAConfigAttrib kVideoEncodeVAConfigAttribs[] = { -+ {VAConfigAttribRateControl, VA_RC_CBR}, -+ {VAConfigAttribEncPackedHeaders, -+ VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE}, -+}; -+ -+// A map between VideoCodecProfile and VAProfile. -+static const struct { -+ VideoCodecProfile profile; -+ VAProfile va_profile; -+} kProfileMap[] = { -+ {H264PROFILE_BASELINE, VAProfileH264Baseline}, -+ {H264PROFILE_MAIN, VAProfileH264Main}, -+ // TODO(posciak): See if we can/want to support other variants of -+ // H264PROFILE_HIGH*. -+ {H264PROFILE_HIGH, VAProfileH264High}, -+ {VP8PROFILE_ANY, VAProfileVP8Version0_3}, -+ {VP9PROFILE_PROFILE0, VAProfileVP9Profile0}, -+ {VP9PROFILE_PROFILE1, VAProfileVP9Profile1}, -+ {VP9PROFILE_PROFILE2, VAProfileVP9Profile2}, -+ {VP9PROFILE_PROFILE3, VAProfileVP9Profile3}, -+}; -+ -+// This class is a wrapper around its |va_display_| (and its associated -+// |va_lock_|) to guarantee mutual exclusion and singleton behaviour. -+class VADisplayState { -+ public: -+ static VADisplayState* Get(); -+ -+ // Initialize static data before sandbox is enabled. -+ static void PreSandboxInitialization(); -+ -+ VADisplayState(); -+ ~VADisplayState() = delete; -+ -+ // |va_lock_| must be held on entry. -+ bool Initialize(); -+ void Deinitialize(VAStatus* status); -+ -+ base::Lock* va_lock() { return &va_lock_; } -+ VADisplay va_display() const { return va_display_; } -+ -+ void SetDrmFd(base::PlatformFile fd) { drm_fd_.reset(HANDLE_EINTR(dup(fd))); } -+ -+ private: -+ // Returns false on init failure. -+ static bool PostSandboxInitialization(); -+ -+ // Protected by |va_lock_|. -+ int refcount_; -+ -+ // Libva is not thread safe, so we have to do locking for it ourselves. -+ // This lock is to be taken for the duration of all VA-API calls and for -+ // the entire job submission sequence in ExecuteAndDestroyPendingBuffers(). -+ base::Lock va_lock_; -+ -+ // Drm fd used to obtain access to the driver interface by VA. -+ base::ScopedFD drm_fd_; -+ -+ // The VADisplay handle. -+ VADisplay va_display_; -+ -+ // True if vaInitialize() has been called successfully. -+ bool va_initialized_; -+}; -+ -+// static -+VADisplayState* VADisplayState::Get() { -+ static VADisplayState* display_state = new VADisplayState(); -+ return display_state; -+} -+ -+// static -+void VADisplayState::PreSandboxInitialization() { -+ const char kDriRenderNode0Path[] = "/dev/dri/renderD128"; -+ base::File drm_file = base::File( -+ base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path), -+ base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); -+ if (drm_file.IsValid()) -+ VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile()); -+} -+ -+// static -+bool VADisplayState::PostSandboxInitialization() { -+ const std::string va_suffix(std::to_string(VA_MAJOR_VERSION + 1)); -+ StubPathMap paths; -+ -+ paths[kModuleVa].push_back(std::string("libva.so.") + va_suffix); -+ paths[kModuleVa_drm].push_back(std::string("libva-drm.so.") + va_suffix); -+#if defined(USE_X11) -+ // libva-x11 does not exist on libva >= 2 -+ if (VA_MAJOR_VERSION == 0) -+ paths[kModuleVa_x11].push_back("libva-x11.so.1"); -+#endif -+ -+ const bool success = InitializeStubs(paths); -+ if (!success) { -+ static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; -+#if defined(OS_CHROMEOS) -+ // When Chrome runs on Linux with target_os="chromeos", do not log error -+ // message without VAAPI libraries. -+ LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS()) << kErrorMsg; -+#else -+ DVLOG(1) << kErrorMsg; -+#endif -+ } -+ return success; -+} -+ -+VADisplayState::VADisplayState() -+ : refcount_(0), va_display_(nullptr), va_initialized_(false) {} -+ -+bool VADisplayState::Initialize() { -+ va_lock_.AssertAcquired(); -+ -+ static bool result = PostSandboxInitialization(); -+ if (!result) -+ return false; -+ -+ if (refcount_++ > 0) -+ return true; -+ -+ switch (gl::GetGLImplementation()) { -+ case gl::kGLImplementationEGLGLES2: -+ va_display_ = vaGetDisplayDRM(drm_fd_.get()); -+ break; -+ case gl::kGLImplementationDesktopGL: -+#if defined(USE_X11) -+ va_display_ = vaGetDisplay(gfx::GetXDisplay()); -+#else -+ LOG(WARNING) << "HW video decode acceleration not available without " -+ "DesktopGL (GLX)."; -+#endif // USE_X11 -+ break; -+ // Cannot infer platform from GL, try all available displays -+ case gl::kGLImplementationNone: -+#if defined(USE_X11) -+ va_display_ = vaGetDisplay(gfx::GetXDisplay()); -+ if (vaDisplayIsValid(va_display_)) -+ break; -+#endif // USE_X11 -+ va_display_ = vaGetDisplayDRM(drm_fd_.get()); -+ break; -+ -+ default: -+ LOG(WARNING) << "HW video decode acceleration not available for " -+ << gl::GetGLImplementationName(gl::GetGLImplementation()); -+ return false; -+ } -+ -+ if (!vaDisplayIsValid(va_display_)) { -+ LOG(ERROR) << "Could not get a valid VA display"; -+ return false; -+ } -+ -+ // Set VA logging level to enable error messages, unless already set -+ constexpr char libva_log_level_env[] = "LIBVA_MESSAGING_LEVEL"; -+ std::unique_ptr env(base::Environment::Create()); -+ if (!env->HasVar(libva_log_level_env)) -+ env->SetVar(libva_log_level_env, "1"); -+ -+ // The VAAPI version. -+ int major_version, minor_version; -+ VAStatus va_res = vaInitialize(va_display_, &major_version, &minor_version); -+ if (va_res != VA_STATUS_SUCCESS) { -+ LOG(ERROR) << "vaInitialize failed: " << vaErrorStr(va_res); -+ return false; -+ } -+ -+ va_initialized_ = true; -+ DVLOG(1) << "VAAPI version: " << major_version << "." << minor_version; -+ -+ if (major_version != VA_MAJOR_VERSION || minor_version != VA_MINOR_VERSION) { -+ LOG(ERROR) << "This build of Chromium requires VA-API version " -+ << VA_MAJOR_VERSION << "." << VA_MINOR_VERSION -+ << ", system version: " << major_version << "." << minor_version; -+ return false; -+ } -+ return true; -+} -+ -+void VADisplayState::Deinitialize(VAStatus* status) { -+ va_lock_.AssertAcquired(); -+ if (--refcount_ > 0) -+ return; -+ -+ // Must check if vaInitialize completed successfully, to work around a bug in -+ // libva. The bug was fixed upstream: -+ // http://lists.freedesktop.org/archives/libva/2013-July/001807.html -+ // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once -+ // the fix has rolled out sufficiently. -+ if (va_initialized_ && va_display_) -+ *status = vaTerminate(va_display_); -+ va_initialized_ = false; -+ va_display_ = nullptr; -+} -+ -+static std::vector GetRequiredAttribs( -+ VaapiWrapper::CodecMode mode, -+ VAProfile profile) { -+ std::vector required_attribs; -+ // VAConfigAttribRTFormat is common to both encode and decode |mode|s. -+ if (profile == VAProfileVP9Profile2 || profile == VAProfileVP9Profile3) { -+ required_attribs.push_back( -+ {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420_10BPP}); -+ } else { -+ required_attribs.push_back({VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}); -+ } -+ if (mode == VaapiWrapper::kEncode && profile != VAProfileJPEGBaseline) { -+ required_attribs.insert( -+ required_attribs.end(), kVideoEncodeVAConfigAttribs, -+ kVideoEncodeVAConfigAttribs + arraysize(kVideoEncodeVAConfigAttribs)); -+ } -+ return required_attribs; -+} -+ -+static VAEntrypoint GetVaEntryPoint(VaapiWrapper::CodecMode mode, -+ VAProfile profile) { -+ switch (mode) { -+ case VaapiWrapper::kDecode: -+ return VAEntrypointVLD; -+ case VaapiWrapper::kEncode: -+ if (profile == VAProfileJPEGBaseline) -+ return VAEntrypointEncPicture; -+ else -+ return VAEntrypointEncSlice; -+ case VaapiWrapper::kCodecModeMax: -+ NOTREACHED(); -+ return VAEntrypointVLD; -+ } -+} -+ -+// This class encapsulates reading and giving access to the list of supported -+// ProfileInfo entries, in a singleton way. -+class VASupportedProfiles { -+ public: -+ struct ProfileInfo { -+ VAProfile va_profile; -+ gfx::Size max_resolution; -+ }; -+ static VASupportedProfiles* Get(); -+ -+ std::vector GetSupportedProfileInfosForCodecMode( -+ VaapiWrapper::CodecMode mode); -+ -+ bool IsProfileSupported(VaapiWrapper::CodecMode mode, VAProfile va_profile); -+ -+ private: -+ VASupportedProfiles(); -+ ~VASupportedProfiles() = default; -+ -+ bool GetSupportedVAProfiles(std::vector* profiles); -+ -+ // Gets supported profile infos for |mode|. -+ std::vector GetSupportedProfileInfosForCodecModeInternal( -+ VaapiWrapper::CodecMode mode); -+ -+ // |va_lock_| must be held on entry in the following _Locked methods. -+ -+ // Checks if |va_profile| supports |entrypoint| or not. -+ bool IsEntrypointSupported_Locked(VAProfile va_profile, -+ VAEntrypoint entrypoint); -+ // Returns true if |va_profile| for |entrypoint| with |required_attribs| is -+ // supported. -+ bool AreAttribsSupported_Locked( -+ VAProfile va_profile, -+ VAEntrypoint entrypoint, -+ const std::vector& required_attribs); -+ // Gets maximum resolution for |va_profile| and |entrypoint| with -+ // |required_attribs|. If return value is true, |resolution| is the maximum -+ // resolution. -+ bool GetMaxResolution_Locked(VAProfile va_profile, -+ VAEntrypoint entrypoint, -+ std::vector& required_attribs, -+ gfx::Size* resolution); -+ -+ std::vector supported_profiles_[VaapiWrapper::kCodecModeMax]; -+ -+ // Pointer to VADisplayState's members |va_lock_| and its |va_display_|. -+ base::Lock* va_lock_; -+ VADisplay va_display_; -+ -+ const base::Closure report_error_to_uma_cb_; -+}; -+ -+// static -+VASupportedProfiles* VASupportedProfiles::Get() { -+ static VASupportedProfiles* profile_infos = new VASupportedProfiles(); -+ return profile_infos; -+} -+ -+std::vector -+VASupportedProfiles::GetSupportedProfileInfosForCodecMode( -+ VaapiWrapper::CodecMode mode) { -+ return supported_profiles_[mode]; -+} -+ -+bool VASupportedProfiles::IsProfileSupported(VaapiWrapper::CodecMode mode, -+ VAProfile va_profile) { -+ for (const auto& profile : supported_profiles_[mode]) { -+ if (profile.va_profile == va_profile) -+ return true; -+ } -+ return false; -+} -+ -+VASupportedProfiles::VASupportedProfiles() -+ : va_lock_(VADisplayState::Get()->va_lock()), -+ va_display_(nullptr), -+ report_error_to_uma_cb_(base::Bind(&base::DoNothing)) { -+ static_assert(arraysize(supported_profiles_) == VaapiWrapper::kCodecModeMax, -+ "The array size of supported profile is incorrect."); -+ { -+ base::AutoLock auto_lock(*va_lock_); -+ if (!VADisplayState::Get()->Initialize()) -+ return; -+ } -+ -+ va_display_ = VADisplayState::Get()->va_display(); -+ DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; -+ -+ for (size_t i = 0; i < VaapiWrapper::kCodecModeMax; ++i) { -+ supported_profiles_[i] = GetSupportedProfileInfosForCodecModeInternal( -+ static_cast(i)); -+ } -+ -+ { -+ base::AutoLock auto_lock(*va_lock_); -+ VAStatus va_res = VA_STATUS_SUCCESS; -+ VADisplayState::Get()->Deinitialize(&va_res); -+ VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); -+ va_display_ = nullptr; -+ } -+} -+ -+std::vector -+VASupportedProfiles::GetSupportedProfileInfosForCodecModeInternal( -+ VaapiWrapper::CodecMode mode) { -+ std::vector supported_profile_infos; -+ std::vector va_profiles; -+ if (!GetSupportedVAProfiles(&va_profiles)) -+ return supported_profile_infos; -+ -+ base::AutoLock auto_lock(*va_lock_); -+ for (const auto& va_profile : va_profiles) { -+ VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); -+ std::vector required_attribs = -+ GetRequiredAttribs(mode, va_profile); -+ if (!IsEntrypointSupported_Locked(va_profile, entrypoint)) -+ continue; -+ if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs)) -+ continue; -+ ProfileInfo profile_info; -+ if (!GetMaxResolution_Locked(va_profile, entrypoint, required_attribs, -+ &profile_info.max_resolution)) { -+ LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile -+ << " and entrypoint " << entrypoint; -+ continue; -+ } -+ profile_info.va_profile = va_profile; -+ supported_profile_infos.push_back(profile_info); -+ } -+ return supported_profile_infos; -+} -+ -+bool VASupportedProfiles::GetSupportedVAProfiles( -+ std::vector* profiles) { -+ base::AutoLock auto_lock(*va_lock_); -+ // Query the driver for supported profiles. -+ const int max_profiles = vaMaxNumProfiles(va_display_); -+ std::vector supported_profiles( -+ base::checked_cast(max_profiles)); -+ -+ int num_supported_profiles; -+ VAStatus va_res = vaQueryConfigProfiles(va_display_, &supported_profiles[0], -+ &num_supported_profiles); -+ VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false); -+ if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) { -+ LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_supported_profiles; -+ return false; -+ } -+ -+ supported_profiles.resize(base::checked_cast(num_supported_profiles)); -+ *profiles = supported_profiles; -+ return true; -+} -+ -+bool VASupportedProfiles::IsEntrypointSupported_Locked( -+ VAProfile va_profile, -+ VAEntrypoint entrypoint) { -+ va_lock_->AssertAcquired(); -+ // Query the driver for supported entrypoints. -+ int max_entrypoints = vaMaxNumEntrypoints(va_display_); -+ std::vector supported_entrypoints( -+ base::checked_cast(max_entrypoints)); -+ -+ int num_supported_entrypoints; -+ VAStatus va_res = vaQueryConfigEntrypoints(va_display_, va_profile, -+ &supported_entrypoints[0], -+ &num_supported_entrypoints); -+ VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false); -+ if (num_supported_entrypoints < 0 || -+ num_supported_entrypoints > max_entrypoints) { -+ LOG(ERROR) << "vaQueryConfigEntrypoints returned: " -+ << num_supported_entrypoints; -+ return false; -+ } -+ -+ return base::ContainsValue(supported_entrypoints, entrypoint); -+} -+ -+bool VASupportedProfiles::AreAttribsSupported_Locked( -+ VAProfile va_profile, -+ VAEntrypoint entrypoint, -+ const std::vector& required_attribs) { -+ va_lock_->AssertAcquired(); -+ // Query the driver for required attributes. -+ std::vector attribs = required_attribs; -+ for (size_t i = 0; i < required_attribs.size(); ++i) -+ attribs[i].value = 0; -+ -+ VAStatus va_res = vaGetConfigAttributes(va_display_, va_profile, entrypoint, -+ &attribs[0], attribs.size()); -+ VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); -+ -+ for (size_t i = 0; i < required_attribs.size(); ++i) { -+ if (attribs[i].type != required_attribs[i].type || -+ (attribs[i].value & required_attribs[i].value) != -+ required_attribs[i].value) { -+ DVLOG(1) << "Unsupported value " << required_attribs[i].value -+ << " for attribute type " << required_attribs[i].type; -+ return false; -+ } -+ } -+ return true; -+} -+ -+bool VASupportedProfiles::GetMaxResolution_Locked( -+ VAProfile va_profile, -+ VAEntrypoint entrypoint, -+ std::vector& required_attribs, -+ gfx::Size* resolution) { -+ va_lock_->AssertAcquired(); -+ VAConfigID va_config_id; -+ VAStatus va_res = -+ vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], -+ required_attribs.size(), &va_config_id); -+ VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); -+ -+ // Calls vaQuerySurfaceAttributes twice. The first time is to get the number -+ // of attributes to prepare the space and the second time is to get all -+ // attributes. -+ unsigned int num_attribs; -+ va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, nullptr, -+ &num_attribs); -+ VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); -+ if (!num_attribs) -+ return false; -+ -+ std::vector attrib_list( -+ base::checked_cast(num_attribs)); -+ -+ va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, &attrib_list[0], -+ &num_attribs); -+ VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); -+ -+ resolution->SetSize(0, 0); -+ for (const auto& attrib : attrib_list) { -+ if (attrib.type == VASurfaceAttribMaxWidth) -+ resolution->set_width(attrib.value.value.i); -+ else if (attrib.type == VASurfaceAttribMaxHeight) -+ resolution->set_height(attrib.value.value.i); -+ } -+ if (resolution->IsEmpty()) { -+ LOG(ERROR) << "Wrong codec resolution: " << resolution->ToString(); -+ return false; -+ } -+ return true; -+} -+ -+// Maps VideoCodecProfile enum values to VaProfile values. This function -+// includes a workaround for https://crbug.com/345569: if va_profile is h264 -+// baseline and it is not supported, we try constrained baseline. -+VAProfile ProfileToVAProfile(VideoCodecProfile profile, -+ VaapiWrapper::CodecMode mode) { -+ VAProfile va_profile = VAProfileNone; -+ for (size_t i = 0; i < arraysize(kProfileMap); ++i) { -+ if (kProfileMap[i].profile == profile) { -+ va_profile = kProfileMap[i].va_profile; -+ break; -+ } -+ } -+ if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile) && -+ va_profile == VAProfileH264Baseline) { -+ // https://crbug.com/345569: ProfileIDToVideoCodecProfile() currently strips -+ // the information whether the profile is constrained or not, so we have no -+ // way to know here. Try for baseline first, but if it is not supported, -+ // try constrained baseline and hope this is what it actually is -+ // (which in practice is true for a great majority of cases). -+ if (VASupportedProfiles::Get()->IsProfileSupported( -+ mode, VAProfileH264ConstrainedBaseline)) { -+ va_profile = VAProfileH264ConstrainedBaseline; -+ DVLOG(1) << "Fall back to constrained baseline profile."; -+ } -+ } -+ return va_profile; -+} -+ -+void DestroyVAImage(VADisplay va_display, VAImage image) { -+ if (image.image_id != VA_INVALID_ID) -+ vaDestroyImage(va_display, image.image_id); -+} -+ -+} // namespace -+ -+VaapiWrapper::VaapiWrapper() -+ : va_surface_format_(0), -+ va_display_(NULL), -+ va_config_id_(VA_INVALID_ID), -+ va_context_id_(VA_INVALID_ID), -+ va_vpp_config_id_(VA_INVALID_ID), -+ va_vpp_context_id_(VA_INVALID_ID), -+ va_vpp_buffer_id_(VA_INVALID_ID) { -+ va_lock_ = VADisplayState::Get()->va_lock(); -+} -+ -+VaapiWrapper::~VaapiWrapper() { -+ DestroyPendingBuffers(); -+ DestroyCodedBuffers(); -+ DestroySurfaces(); -+ DeinitializeVpp(); -+ Deinitialize(); -+} -+ -+// static -+scoped_refptr VaapiWrapper::Create( -+ CodecMode mode, -+ VAProfile va_profile, -+ const base::Closure& report_error_to_uma_cb) { -+ if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile)) { -+ DVLOG(1) << "Unsupported va_profile: " << va_profile; -+ return nullptr; -+ } -+ -+ scoped_refptr vaapi_wrapper(new VaapiWrapper()); -+ if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) { -+ if (vaapi_wrapper->Initialize(mode, va_profile)) -+ return vaapi_wrapper; -+ } -+ LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " << va_profile; -+ return nullptr; -+} -+ -+// static -+scoped_refptr VaapiWrapper::CreateForVideoCodec( -+ CodecMode mode, -+ VideoCodecProfile profile, -+ const base::Closure& report_error_to_uma_cb) { -+ VAProfile va_profile = ProfileToVAProfile(profile, mode); -+ return Create(mode, va_profile, report_error_to_uma_cb); -+} -+ -+// static -+VideoEncodeAccelerator::SupportedProfiles -+VaapiWrapper::GetSupportedEncodeProfiles() { -+ VideoEncodeAccelerator::SupportedProfiles profiles; -+ std::vector encode_profile_infos = -+ VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kEncode); -+ -+ for (size_t i = 0; i < arraysize(kProfileMap); ++i) { -+ VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode); -+ if (va_profile == VAProfileNone) -+ continue; -+ for (const auto& profile_info : encode_profile_infos) { -+ if (profile_info.va_profile == va_profile) { -+ VideoEncodeAccelerator::SupportedProfile profile; -+ profile.profile = kProfileMap[i].profile; -+ profile.max_resolution = profile_info.max_resolution; -+ profile.max_framerate_numerator = kMaxEncoderFramerate; -+ profile.max_framerate_denominator = 1; -+ profiles.push_back(profile); -+ break; -+ } -+ } -+ } -+ return profiles; -+} -+ -+// static -+VideoDecodeAccelerator::SupportedProfiles -+VaapiWrapper::GetSupportedDecodeProfiles() { -+ VideoDecodeAccelerator::SupportedProfiles profiles; -+ std::vector decode_profile_infos = -+ VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kDecode); -+ -+ for (size_t i = 0; i < arraysize(kProfileMap); ++i) { -+ VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kDecode); -+ if (va_profile == VAProfileNone) -+ continue; -+ for (const auto& profile_info : decode_profile_infos) { -+ if (profile_info.va_profile == va_profile) { -+ VideoDecodeAccelerator::SupportedProfile profile; -+ profile.profile = kProfileMap[i].profile; -+ profile.max_resolution = profile_info.max_resolution; -+ profile.min_resolution.SetSize(16, 16); -+ profiles.push_back(profile); -+ break; -+ } -+ } -+ } -+ return profiles; -+} -+ -+// static -+bool VaapiWrapper::IsJpegDecodeSupported() { -+ return VASupportedProfiles::Get()->IsProfileSupported(kDecode, -+ VAProfileJPEGBaseline); -+} -+ -+// static -+bool VaapiWrapper::IsJpegEncodeSupported() { -+ return VASupportedProfiles::Get()->IsProfileSupported(kEncode, -+ VAProfileJPEGBaseline); -+} -+ -+void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { -+ base::AutoLock auto_lock(*va_lock_); -+ VADisplayAttribute item = {VADisplayAttribRenderMode, -+ 1, // At least support '_LOCAL_OVERLAY'. -+ -1, // The maximum possible support 'ALL'. -+ VA_RENDER_MODE_LOCAL_GPU, -+ VA_DISPLAY_ATTRIB_SETTABLE}; -+ -+ VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1); -+ if (va_res != VA_STATUS_SUCCESS) -+ DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; -+} -+ -+bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) { -+ report_error_to_uma_cb_ = report_error_to_uma_cb; -+ { -+ base::AutoLock auto_lock(*va_lock_); -+ if (!VADisplayState::Get()->Initialize()) -+ return false; -+ } -+ -+ va_display_ = VADisplayState::Get()->va_display(); -+ DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; -+ return true; -+} -+ -+bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) { -+ TryToSetVADisplayAttributeToLocalGPU(); -+ -+ VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); -+ std::vector required_attribs = -+ GetRequiredAttribs(mode, va_profile); -+ base::AutoLock auto_lock(*va_lock_); -+ VAStatus va_res = -+ vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], -+ required_attribs.size(), &va_config_id_); -+ VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); -+ -+ return true; -+} -+ -+void VaapiWrapper::Deinitialize() { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ if (va_config_id_ != VA_INVALID_ID) { -+ VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); -+ } -+ -+ VAStatus va_res = VA_STATUS_SUCCESS; -+ VADisplayState::Get()->Deinitialize(&va_res); -+ VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); -+ -+ va_config_id_ = VA_INVALID_ID; -+ va_display_ = NULL; -+} -+ -+bool VaapiWrapper::CreateSurfaces(unsigned int va_format, -+ const gfx::Size& size, -+ size_t num_surfaces, -+ std::vector* va_surfaces) { -+ base::AutoLock auto_lock(*va_lock_); -+ DVLOG(2) << "Creating " << num_surfaces << " surfaces"; -+ -+ DCHECK(va_surfaces->empty()); -+ DCHECK(va_surface_ids_.empty()); -+ DCHECK_EQ(va_surface_format_, 0u); -+ va_surface_ids_.resize(num_surfaces); -+ -+ // Allocate surfaces in driver. -+ VAStatus va_res = -+ vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), -+ &va_surface_ids_[0], va_surface_ids_.size(), NULL, 0); -+ -+ VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); -+ if (va_res != VA_STATUS_SUCCESS) { -+ va_surface_ids_.clear(); -+ return false; -+ } -+ -+ // And create a context associated with them. -+ va_res = vaCreateContext(va_display_, va_config_id_, size.width(), -+ size.height(), VA_PROGRESSIVE, &va_surface_ids_[0], -+ va_surface_ids_.size(), &va_context_id_); -+ -+ VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); -+ if (va_res != VA_STATUS_SUCCESS) { -+ DestroySurfaces_Locked(); -+ return false; -+ } -+ -+ *va_surfaces = va_surface_ids_; -+ va_surface_format_ = va_format; -+ return true; -+} -+ -+void VaapiWrapper::DestroySurfaces() { -+ base::AutoLock auto_lock(*va_lock_); -+ DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; -+ -+ DestroySurfaces_Locked(); -+} -+ -+void VaapiWrapper::DestroySurfaces_Locked() { -+ va_lock_->AssertAcquired(); -+ -+ if (va_context_id_ != VA_INVALID_ID) { -+ VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); -+ } -+ -+ if (!va_surface_ids_.empty()) { -+ VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0], -+ va_surface_ids_.size()); -+ VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); -+ } -+ -+ va_surface_ids_.clear(); -+ va_context_id_ = VA_INVALID_ID; -+ va_surface_format_ = 0; -+} -+ -+scoped_refptr VaapiWrapper::CreateUnownedSurface( -+ unsigned int va_format, -+ const gfx::Size& size, -+ const std::vector& va_attribs) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ std::vector attribs(va_attribs); -+ VASurfaceID va_surface_id; -+ VAStatus va_res = -+ vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), -+ &va_surface_id, 1, &attribs[0], attribs.size()); -+ -+ scoped_refptr va_surface; -+ VA_SUCCESS_OR_RETURN(va_res, "Failed to create unowned VASurface", -+ va_surface); -+ -+ // This is safe to use Unretained() here, because the VDA takes care -+ // of the destruction order. All the surfaces will be destroyed -+ // before VaapiWrapper. -+ va_surface = new VASurface( -+ va_surface_id, size, va_format, -+ base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this))); -+ -+ return va_surface; -+} -+ -+scoped_refptr VaapiWrapper::CreateVASurfaceForPixmap( -+ const scoped_refptr& pixmap) { -+ // Create a VASurface for a NativePixmap by importing the underlying dmabufs. -+ VASurfaceAttribExternalBuffers va_attrib_extbuf; -+ memset(&va_attrib_extbuf, 0, sizeof(va_attrib_extbuf)); -+ -+ va_attrib_extbuf.pixel_format = -+ BufferFormatToVAFourCC(pixmap->GetBufferFormat()); -+ gfx::Size size = pixmap->GetBufferSize(); -+ va_attrib_extbuf.width = size.width(); -+ va_attrib_extbuf.height = size.height(); -+ -+ size_t num_fds = pixmap->GetDmaBufFdCount(); -+ size_t num_planes = -+ gfx::NumberOfPlanesForBufferFormat(pixmap->GetBufferFormat()); -+ if (num_fds == 0 || num_fds > num_planes) { -+ LOG(ERROR) << "Invalid number of dmabuf fds: " << num_fds -+ << " , planes: " << num_planes; -+ return nullptr; -+ } -+ -+ for (size_t i = 0; i < num_planes; ++i) { -+ va_attrib_extbuf.pitches[i] = pixmap->GetDmaBufPitch(i); -+ va_attrib_extbuf.offsets[i] = pixmap->GetDmaBufOffset(i); -+ DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i] -+ << " offset: " << va_attrib_extbuf.offsets[i]; -+ } -+ va_attrib_extbuf.num_planes = num_planes; -+ -+ std::vector fds(num_fds); -+ for (size_t i = 0; i < num_fds; ++i) { -+ int dmabuf_fd = pixmap->GetDmaBufFd(i); -+ if (dmabuf_fd < 0) { -+ LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap"; -+ return nullptr; -+ } -+ fds[i] = dmabuf_fd; -+ } -+ va_attrib_extbuf.buffers = fds.data(); -+ va_attrib_extbuf.num_buffers = fds.size(); -+ -+ va_attrib_extbuf.flags = 0; -+ va_attrib_extbuf.private_data = NULL; -+ -+ std::vector va_attribs(2); -+ -+ va_attribs[0].type = VASurfaceAttribMemoryType; -+ va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; -+ va_attribs[0].value.type = VAGenericValueTypeInteger; -+ va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; -+ -+ va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; -+ va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; -+ va_attribs[1].value.type = VAGenericValueTypePointer; -+ va_attribs[1].value.value.p = &va_attrib_extbuf; -+ -+ scoped_refptr va_surface = CreateUnownedSurface( -+ BufferFormatToVARTFormat(pixmap->GetBufferFormat()), size, va_attribs); -+ if (!va_surface) { -+ LOG(ERROR) << "Failed to create VASurface for an Ozone NativePixmap"; -+ return nullptr; -+ } -+ -+ return va_surface; -+} -+ -+void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1); -+ VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces on surface failed"); -+} -+ -+bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, -+ size_t size, -+ void* buffer) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VABufferID buffer_id; -+ VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, va_buffer_type, -+ size, 1, buffer, &buffer_id); -+ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); -+ -+ switch (va_buffer_type) { -+ case VASliceParameterBufferType: -+ case VASliceDataBufferType: -+ case VAEncSliceParameterBufferType: -+ pending_slice_bufs_.push_back(buffer_id); -+ break; -+ -+ default: -+ pending_va_bufs_.push_back(buffer_id); -+ break; -+ } -+ -+ return true; -+} -+ -+bool VaapiWrapper::SubmitVAEncMiscParamBuffer( -+ VAEncMiscParameterType misc_param_type, -+ size_t size, -+ void* buffer) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VABufferID buffer_id; -+ VAStatus va_res = vaCreateBuffer( -+ va_display_, va_context_id_, VAEncMiscParameterBufferType, -+ sizeof(VAEncMiscParameterBuffer) + size, 1, NULL, &buffer_id); -+ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); -+ -+ void* data_ptr = NULL; -+ va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr); -+ VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); -+ if (va_res != VA_STATUS_SUCCESS) { -+ vaDestroyBuffer(va_display_, buffer_id); -+ return false; -+ } -+ -+ DCHECK(data_ptr); -+ -+ VAEncMiscParameterBuffer* misc_param = -+ reinterpret_cast(data_ptr); -+ misc_param->type = misc_param_type; -+ memcpy(misc_param->data, buffer, size); -+ va_res = vaUnmapBuffer(va_display_, buffer_id); -+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -+ -+ pending_va_bufs_.push_back(buffer_id); -+ return true; -+} -+ -+void VaapiWrapper::DestroyPendingBuffers() { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ for (const auto& pending_va_buf : pending_va_bufs_) { -+ VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -+ } -+ -+ for (const auto& pending_slice_buf : pending_slice_bufs_) { -+ VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -+ } -+ -+ pending_va_bufs_.clear(); -+ pending_slice_bufs_.clear(); -+} -+ -+bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) { -+ base::AutoLock auto_lock(*va_lock_); -+ VAStatus va_res = -+ vaCreateBuffer(va_display_, va_context_id_, VAEncCodedBufferType, size, 1, -+ NULL, buffer_id); -+ VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false); -+ -+ const auto is_new_entry = coded_buffers_.insert(*buffer_id).second; -+ DCHECK(is_new_entry); -+ return true; -+} -+ -+void VaapiWrapper::DestroyCodedBuffers() { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ for (std::set::const_iterator iter = coded_buffers_.begin(); -+ iter != coded_buffers_.end(); ++iter) { -+ VAStatus va_res = vaDestroyBuffer(va_display_, *iter); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -+ } -+ -+ coded_buffers_.clear(); -+} -+ -+bool VaapiWrapper::Execute(VASurfaceID va_surface_id) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); -+ DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); -+ DVLOG(4) << "Target VA surface " << va_surface_id; -+ -+ // Get ready to execute for given surface. -+ VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, va_surface_id); -+ VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); -+ -+ if (pending_va_bufs_.size() > 0) { -+ // Commit parameter and slice buffers. -+ va_res = vaRenderPicture(va_display_, va_context_id_, &pending_va_bufs_[0], -+ pending_va_bufs_.size()); -+ VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); -+ } -+ -+ if (pending_slice_bufs_.size() > 0) { -+ va_res = -+ vaRenderPicture(va_display_, va_context_id_, &pending_slice_bufs_[0], -+ pending_slice_bufs_.size()); -+ VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); -+ } -+ -+ // Instruct HW codec to start processing committed buffers. -+ // Does not block and the job is not finished after this returns. -+ va_res = vaEndPicture(va_display_, va_context_id_); -+ VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); -+ -+ return true; -+} -+ -+bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { -+ bool result = Execute(va_surface_id); -+ DestroyPendingBuffers(); -+ return result; -+} -+ -+#if defined(USE_X11) -+bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, -+ Pixmap x_pixmap, -+ gfx::Size dest_size) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); -+ VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); -+ -+ // Put the data into an X Pixmap. -+ va_res = vaPutSurface(va_display_, -+ va_surface_id, -+ x_pixmap, -+ 0, 0, dest_size.width(), dest_size.height(), -+ 0, 0, dest_size.width(), dest_size.height(), -+ NULL, 0, 0); -+ VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false); -+ return true; -+} -+#endif // USE_X11 -+ -+bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id, -+ VAImageFormat* format, -+ const gfx::Size& size, -+ VAImage* image, -+ void** mem) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); -+ VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); -+ -+ va_res = -+ vaCreateImage(va_display_, format, size.width(), size.height(), image); -+ VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false); -+ -+ va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(), -+ size.height(), image->image_id); -+ VA_LOG_ON_ERROR(va_res, "vaGetImage failed"); -+ -+ if (va_res == VA_STATUS_SUCCESS) { -+ // Map the VAImage into memory -+ va_res = vaMapBuffer(va_display_, image->buf, mem); -+ VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); -+ } -+ -+ if (va_res != VA_STATUS_SUCCESS) { -+ va_res = vaDestroyImage(va_display_, image->image_id); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); -+ return false; -+ } -+ -+ return true; -+} -+ -+void VaapiWrapper::ReturnVaImage(VAImage* image) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VAStatus va_res = vaUnmapBuffer(va_display_, image->buf); -+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -+ -+ va_res = vaDestroyImage(va_display_, image->image_id); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); -+} -+ -+bool VaapiWrapper::UploadVideoFrameToSurface( -+ const scoped_refptr& frame, -+ VASurfaceID va_surface_id) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VAImage image; -+ VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image); -+ VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false); -+ base::ScopedClosureRunner vaimage_deleter( -+ base::Bind(&DestroyVAImage, va_display_, image)); -+ -+ if (image.format.fourcc != VA_FOURCC_NV12) { -+ LOG(ERROR) << "Unsupported image format: " << image.format.fourcc; -+ return false; -+ } -+ -+ if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) { -+ LOG(ERROR) << "Buffer too small to fit the frame."; -+ return false; -+ } -+ -+ void* image_ptr = NULL; -+ va_res = vaMapBuffer(va_display_, image.buf, &image_ptr); -+ VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); -+ DCHECK(image_ptr); -+ -+ int ret = 0; -+ { -+ base::AutoUnlock auto_unlock(*va_lock_); -+ ret = libyuv::I420ToNV12( -+ frame->data(VideoFrame::kYPlane), frame->stride(VideoFrame::kYPlane), -+ frame->data(VideoFrame::kUPlane), frame->stride(VideoFrame::kUPlane), -+ frame->data(VideoFrame::kVPlane), frame->stride(VideoFrame::kVPlane), -+ static_cast(image_ptr) + image.offsets[0], image.pitches[0], -+ static_cast(image_ptr) + image.offsets[1], image.pitches[1], -+ image.width, image.height); -+ } -+ -+ va_res = vaUnmapBuffer(va_display_, image.buf); -+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -+ -+ return ret == 0; -+} -+ -+bool VaapiWrapper::DownloadFromCodedBuffer(VABufferID buffer_id, -+ VASurfaceID sync_surface_id, -+ uint8_t* target_ptr, -+ size_t target_size, -+ size_t* coded_data_size) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id); -+ VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); -+ -+ VACodedBufferSegment* buffer_segment = NULL; -+ va_res = vaMapBuffer(va_display_, buffer_id, -+ reinterpret_cast(&buffer_segment)); -+ VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); -+ DCHECK(target_ptr); -+ -+ { -+ base::AutoUnlock auto_unlock(*va_lock_); -+ *coded_data_size = 0; -+ -+ while (buffer_segment) { -+ DCHECK(buffer_segment->buf); -+ -+ if (buffer_segment->size > target_size) { -+ LOG(ERROR) << "Insufficient output buffer size"; -+ break; -+ } -+ -+ memcpy(target_ptr, buffer_segment->buf, buffer_segment->size); -+ -+ target_ptr += buffer_segment->size; -+ *coded_data_size += buffer_segment->size; -+ target_size -= buffer_segment->size; -+ -+ buffer_segment = -+ reinterpret_cast(buffer_segment->next); -+ } -+ } -+ -+ va_res = vaUnmapBuffer(va_display_, buffer_id); -+ VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -+ return buffer_segment == NULL; -+} -+ -+bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id, -+ VASurfaceID sync_surface_id, -+ uint8_t* target_ptr, -+ size_t target_size, -+ size_t* coded_data_size) { -+ bool result = DownloadFromCodedBuffer(buffer_id, sync_surface_id, target_ptr, -+ target_size, coded_data_size); -+ -+ VAStatus va_res = vaDestroyBuffer(va_display_, buffer_id); -+ VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -+ const auto was_found = coded_buffers_.erase(buffer_id); -+ DCHECK(was_found); -+ -+ return result; -+} -+ -+bool VaapiWrapper::BlitSurface( -+ const scoped_refptr& va_surface_src, -+ const scoped_refptr& va_surface_dest) { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ // Initialize the post processing engine if not already done. -+ if (va_vpp_buffer_id_ == VA_INVALID_ID) { -+ if (!InitializeVpp_Locked()) -+ return false; -+ } -+ -+ VAProcPipelineParameterBuffer* pipeline_param; -+ VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_, va_vpp_buffer_id_, -+ reinterpret_cast(&pipeline_param)), -+ "Couldn't map vpp buffer", false); -+ -+ memset(pipeline_param, 0, sizeof *pipeline_param); -+ const gfx::Size src_size = va_surface_src->size(); -+ const gfx::Size dest_size = va_surface_dest->size(); -+ -+ VARectangle input_region; -+ input_region.x = input_region.y = 0; -+ input_region.width = src_size.width(); -+ input_region.height = src_size.height(); -+ pipeline_param->surface_region = &input_region; -+ pipeline_param->surface = va_surface_src->id(); -+ pipeline_param->surface_color_standard = VAProcColorStandardNone; -+ -+ VARectangle output_region; -+ output_region.x = output_region.y = 0; -+ output_region.width = dest_size.width(); -+ output_region.height = dest_size.height(); -+ pipeline_param->output_region = &output_region; -+ pipeline_param->output_background_color = 0xff000000; -+ pipeline_param->output_color_standard = VAProcColorStandardNone; -+ pipeline_param->filter_flags = VA_FILTER_SCALING_DEFAULT; -+ -+ VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_, va_vpp_buffer_id_), -+ "Couldn't unmap vpp buffer", false); -+ -+ VA_SUCCESS_OR_RETURN( -+ vaBeginPicture(va_display_, va_vpp_context_id_, va_surface_dest->id()), -+ "Couldn't begin picture", false); -+ -+ VA_SUCCESS_OR_RETURN( -+ vaRenderPicture(va_display_, va_vpp_context_id_, &va_vpp_buffer_id_, 1), -+ "Couldn't render picture", false); -+ -+ VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_, va_vpp_context_id_), -+ "Couldn't end picture", false); -+ -+ return true; -+} -+ -+bool VaapiWrapper::InitializeVpp_Locked() { -+ va_lock_->AssertAcquired(); -+ -+ VA_SUCCESS_OR_RETURN( -+ vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0, -+ &va_vpp_config_id_), -+ "Couldn't create config", false); -+ -+ // The size of the picture for the context is irrelevant in the case -+ // of the VPP, just passing 1x1. -+ VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_, va_vpp_config_id_, 1, 1, 0, -+ NULL, 0, &va_vpp_context_id_), -+ "Couldn't create context", false); -+ -+ VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_, va_vpp_context_id_, -+ VAProcPipelineParameterBufferType, -+ sizeof(VAProcPipelineParameterBuffer), 1, -+ NULL, &va_vpp_buffer_id_), -+ "Couldn't create buffer", false); -+ -+ return true; -+} -+ -+void VaapiWrapper::DeinitializeVpp() { -+ base::AutoLock auto_lock(*va_lock_); -+ -+ if (va_vpp_buffer_id_ != VA_INVALID_ID) { -+ vaDestroyBuffer(va_display_, va_vpp_buffer_id_); -+ va_vpp_buffer_id_ = VA_INVALID_ID; -+ } -+ if (va_vpp_context_id_ != VA_INVALID_ID) { -+ vaDestroyContext(va_display_, va_vpp_context_id_); -+ va_vpp_context_id_ = VA_INVALID_ID; -+ } -+ if (va_vpp_config_id_ != VA_INVALID_ID) { -+ vaDestroyConfig(va_display_, va_vpp_config_id_); -+ va_vpp_config_id_ = VA_INVALID_ID; -+ } -+} -+ -+// static -+void VaapiWrapper::PreSandboxInitialization() { -+ VADisplayState::PreSandboxInitialization(); -+} -+ -+} // namespace media ---- /dev/null -+++ b/media/gpu/vaapi/vaapi_wrapper.h -@@ -0,0 +1,288 @@ -+// Copyright 2013 The Chromium Authors. All rights reserved. -+// Use of this source code is governed by a BSD-style license that can be -+// found in the LICENSE file. -+// -+// This file contains an implementation of VaapiWrapper, used by -+// VaapiVideoDecodeAccelerator and VaapiH264Decoder for decode, -+// and VaapiVideoEncodeAccelerator for encode, to interface -+// with libva (VA-API library for hardware video codec). -+ -+#ifndef MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ -+#define MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include "base/files/file.h" -+#include "base/macros.h" -+#include "base/memory/ref_counted.h" -+#include "base/synchronization/lock.h" -+#include "media/base/video_decoder_config.h" -+#include "media/base/video_frame.h" -+#include "media/gpu/media_gpu_export.h" -+#include "media/gpu/vaapi/va_surface.h" -+#include "media/video/jpeg_decode_accelerator.h" -+#include "media/video/video_decode_accelerator.h" -+#include "media/video/video_encode_accelerator.h" -+#include "ui/gfx/geometry/size.h" -+ -+#if defined(USE_X11) -+#include "ui/gfx/x/x11.h" -+#endif // USE_X11 -+ -+namespace gfx { -+class NativePixmap; -+} -+ -+namespace media { -+ -+// This class handles VA-API calls and ensures proper locking of VA-API calls -+// to libva, the userspace shim to the HW codec driver. libva is not -+// thread-safe, so we have to perform locking ourselves. This class is fully -+// synchronous and its methods can be called from any thread and may wait on -+// the va_lock_ while other, concurrent calls run. -+// -+// This class is responsible for managing VAAPI connection, contexts and state. -+// It is also responsible for managing and freeing VABuffers (not VASurfaces), -+// which are used to queue parameters and slice data to the HW codec, -+// as well as underlying memory for VASurfaces themselves. -+class MEDIA_GPU_EXPORT VaapiWrapper -+ : public base::RefCountedThreadSafe { -+ public: -+ enum CodecMode { -+ kDecode, -+ kEncode, -+ kCodecModeMax, -+ }; -+ -+ // Return an instance of VaapiWrapper initialized for |va_profile| and -+ // |mode|. |report_error_to_uma_cb| will be called independently from -+ // reporting errors to clients via method return values. -+ static scoped_refptr Create( -+ CodecMode mode, -+ VAProfile va_profile, -+ const base::Closure& report_error_to_uma_cb); -+ -+ // Create VaapiWrapper for VideoCodecProfile. It maps VideoCodecProfile -+ // |profile| to VAProfile. -+ // |report_error_to_uma_cb| will be called independently from reporting -+ // errors to clients via method return values. -+ static scoped_refptr CreateForVideoCodec( -+ CodecMode mode, -+ VideoCodecProfile profile, -+ const base::Closure& report_error_to_uma_cb); -+ -+ // Return the supported video encode profiles. -+ static VideoEncodeAccelerator::SupportedProfiles GetSupportedEncodeProfiles(); -+ -+ // Return the supported video decode profiles. -+ static VideoDecodeAccelerator::SupportedProfiles GetSupportedDecodeProfiles(); -+ -+ // Return true when JPEG decode is supported. -+ static bool IsJpegDecodeSupported(); -+ -+ // Return true when JPEG encode is supported. -+ static bool IsJpegEncodeSupported(); -+ -+ // Create |num_surfaces| backing surfaces in driver for VASurfaces of -+ // |va_format|, each of size |size|. Returns true when successful, with the -+ // created IDs in |va_surfaces| to be managed and later wrapped in -+ // VASurfaces. -+ // The client must DestroySurfaces() each time before calling this method -+ // again to free the allocated surfaces first, but is not required to do so -+ // at destruction time, as this will be done automatically from -+ // the destructor. -+ virtual bool CreateSurfaces(unsigned int va_format, -+ const gfx::Size& size, -+ size_t num_surfaces, -+ std::vector* va_surfaces); -+ -+ // Free all memory allocated in CreateSurfaces. -+ virtual void DestroySurfaces(); -+ -+ // Create a VASurface of |va_format|, |size| and using |va_attribs| -+ // attributes. The ownership of the surface is transferred to the -+ // caller. It differs from surfaces created using CreateSurfaces(), -+ // where VaapiWrapper is the owner of the surfaces. -+ scoped_refptr CreateUnownedSurface( -+ unsigned int va_format, -+ const gfx::Size& size, -+ const std::vector& va_attribs); -+ -+ // Create a VASurface for |pixmap|. The ownership of the surface is -+ // transferred to the caller. It differs from surfaces created using -+ // CreateSurfaces(), where VaapiWrapper is the owner of the surfaces. -+ scoped_refptr CreateVASurfaceForPixmap( -+ const scoped_refptr& pixmap); -+ -+ // Submit parameters or slice data of |va_buffer_type|, copying them from -+ // |buffer| of size |size|, into HW codec. The data in |buffer| is no -+ // longer needed and can be freed after this method returns. -+ // Data submitted via this method awaits in the HW codec until -+ // ExecuteAndDestroyPendingBuffers() is called to execute or -+ // DestroyPendingBuffers() is used to cancel a pending job. -+ bool SubmitBuffer(VABufferType va_buffer_type, size_t size, void* buffer); -+ -+ // Submit a VAEncMiscParameterBuffer of given |misc_param_type|, copying its -+ // data from |buffer| of size |size|, into HW codec. The data in |buffer| is -+ // no longer needed and can be freed after this method returns. -+ // Data submitted via this method awaits in the HW codec until -+ // ExecuteAndDestroyPendingBuffers() is called to execute or -+ // DestroyPendingBuffers() is used to cancel a pending job. -+ bool SubmitVAEncMiscParamBuffer(VAEncMiscParameterType misc_param_type, -+ size_t size, -+ void* buffer); -+ -+ // Cancel and destroy all buffers queued to the HW codec via SubmitBuffer(). -+ // Useful when a pending job is to be cancelled (on reset or error). -+ void DestroyPendingBuffers(); -+ -+ // Execute job in hardware on target |va_surface_id| and destroy pending -+ // buffers. Return false if Execute() fails. -+ bool ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id); -+ -+#if defined(USE_X11) -+ // Put data from |va_surface_id| into |x_pixmap| of size -+ // |dest_size|, converting/scaling to it. -+ bool PutSurfaceIntoPixmap(VASurfaceID va_surface_id, -+ Pixmap x_pixmap, -+ gfx::Size dest_size); -+#endif // USE_X11 -+ -+ // Get a VAImage from a VASurface |va_surface_id| and map it into memory with -+ // given |format| and |size|. The output is |image| and the mapped memory is -+ // |mem|. If |format| doesn't equal to the internal format, the underlying -+ // implementation will do format conversion if supported. |size| should be -+ // smaller than or equal to the surface. If |size| is smaller, the image will -+ // be cropped. The VAImage should be released using the ReturnVaImage -+ // function. Returns true when successful. -+ bool GetVaImage(VASurfaceID va_surface_id, -+ VAImageFormat* format, -+ const gfx::Size& size, -+ VAImage* image, -+ void** mem); -+ -+ // Release the VAImage (and the associated memory mapping) obtained from -+ // GetVaImage(). -+ void ReturnVaImage(VAImage* image); -+ -+ // Upload contents of |frame| into |va_surface_id| for encode. -+ bool UploadVideoFrameToSurface(const scoped_refptr& frame, -+ VASurfaceID va_surface_id); -+ -+ // Create a buffer of |size| bytes to be used as encode output. -+ bool CreateCodedBuffer(size_t size, VABufferID* buffer_id); -+ -+ // Download the contents of the buffer with given |buffer_id| into a buffer of -+ // size |target_size|, pointed to by |target_ptr|. The number of bytes -+ // downloaded will be returned in |coded_data_size|. |sync_surface_id| will -+ // be used as a sync point, i.e. it will have to become idle before starting -+ // the download. |sync_surface_id| should be the source surface passed -+ // to the encode job. -+ bool DownloadFromCodedBuffer(VABufferID buffer_id, -+ VASurfaceID sync_surface_id, -+ uint8_t* target_ptr, -+ size_t target_size, -+ size_t* coded_data_size); -+ -+ // See DownloadFromCodedBuffer() for details. After downloading, it deletes -+ // the VA buffer with |buffer_id|. -+ bool DownloadAndDestroyCodedBuffer(VABufferID buffer_id, -+ VASurfaceID sync_surface_id, -+ uint8_t* target_ptr, -+ size_t target_size, -+ size_t* coded_data_size); -+ -+ // Destroy all previously-allocated (and not yet destroyed) coded buffers. -+ void DestroyCodedBuffers(); -+ -+ // Blits a VASurface |va_surface_src| into another VASurface -+ // |va_surface_dest| applying pixel format conversion and scaling -+ // if needed. -+ bool BlitSurface(const scoped_refptr& va_surface_src, -+ const scoped_refptr& va_surface_dest); -+ -+ // Initialize static data before sandbox is enabled. -+ static void PreSandboxInitialization(); -+ -+ // Get the created surfaces format. -+ unsigned int va_surface_format() const { return va_surface_format_; } -+ -+ protected: -+ VaapiWrapper(); -+ virtual ~VaapiWrapper(); -+ -+ private: -+ friend class base::RefCountedThreadSafe; -+ -+ bool Initialize(CodecMode mode, VAProfile va_profile); -+ void Deinitialize(); -+ bool VaInitialize(const base::Closure& report_error_to_uma_cb); -+ -+ // Free all memory allocated in CreateSurfaces. -+ void DestroySurfaces_Locked(); -+ // Destroys a |va_surface| created using CreateUnownedSurface. -+ void DestroyUnownedSurface(VASurfaceID va_surface_id); -+ -+ // Initialize the video post processing context with the |size| of -+ // the input pictures to be processed. -+ bool InitializeVpp_Locked(); -+ -+ // Deinitialize the video post processing context. -+ void DeinitializeVpp(); -+ -+ // Execute pending job in hardware and destroy pending buffers. Return false -+ // if vaapi driver refuses to accept parameter or slice buffers submitted -+ // by client, or if execution fails in hardware. -+ bool Execute(VASurfaceID va_surface_id); -+ -+ // Attempt to set render mode to "render to texture.". Failure is non-fatal. -+ void TryToSetVADisplayAttributeToLocalGPU(); -+ -+ // Pointer to VADisplayState's member |va_lock_|. Guaranteed to be valid for -+ // the lifetime of VaapiWrapper. -+ base::Lock* va_lock_; -+ -+ // Allocated ids for VASurfaces. -+ std::vector va_surface_ids_; -+ -+ // VA format of surfaces with va_surface_ids_. -+ unsigned int va_surface_format_; -+ -+ // VA handles. -+ // All valid after successful Initialize() and until Deinitialize(). -+ VADisplay va_display_; -+ VAConfigID va_config_id_; -+ // Created for the current set of va_surface_ids_ in CreateSurfaces() and -+ // valid until DestroySurfaces(). -+ VAContextID va_context_id_; -+ -+ // Data queued up for HW codec, to be committed on next execution. -+ std::vector pending_slice_bufs_; -+ std::vector pending_va_bufs_; -+ -+ // Bitstream buffers for encode. -+ std::set coded_buffers_; -+ -+ // Called to report codec errors to UMA. Errors to clients are reported via -+ // return values from public methods. -+ base::Closure report_error_to_uma_cb_; -+ -+ // VPP (Video Post Processing) context, this is used to convert -+ // pictures used by the decoder to RGBA pictures usable by GL or the -+ // display hardware. -+ VAConfigID va_vpp_config_id_; -+ VAContextID va_vpp_context_id_; -+ VABufferID va_vpp_buffer_id_; -+ -+ DISALLOW_COPY_AND_ASSIGN(VaapiWrapper); -+}; -+ -+} // namespace media -+ -+#endif // MEDIA_GPU_VAAPI_VAAPI_WRAPPER_H_ ---- a/media/gpu/vaapi_jpeg_decode_accelerator.cc -+++ /dev/null -@@ -1,325 +0,0 @@ --// Copyright 2015 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_jpeg_decode_accelerator.h" -- --#include --#include -- --#include --#include -- --#include "base/bind.h" --#include "base/logging.h" --#include "base/metrics/histogram_macros.h" --#include "base/threading/thread_task_runner_handle.h" --#include "base/trace_event/trace_event.h" --#include "gpu/ipc/service/gpu_channel.h" --#include "media/base/video_frame.h" --#include "media/filters/jpeg_parser.h" --#include "media/gpu/shared_memory_region.h" --#include "media/gpu/vaapi/vaapi_picture.h" --#include "third_party/libyuv/include/libyuv.h" -- --#define VLOGF(level) VLOG(level) << __func__ << "(): " --#define DVLOGF(level) DVLOG(level) << __func__ << "(): " -- --namespace media { -- --namespace { --// UMA errors that the VaapiJpegDecodeAccelerator class reports. --enum VAJDADecoderFailure { -- VAAPI_ERROR = 0, -- VAJDA_DECODER_FAILURES_MAX, --}; -- --static void ReportToUMA(VAJDADecoderFailure failure) { -- UMA_HISTOGRAM_ENUMERATION("Media.VAJDA.DecoderFailure", failure, -- VAJDA_DECODER_FAILURES_MAX + 1); --} -- --static unsigned int VaSurfaceFormatForJpeg( -- const JpegFrameHeader& frame_header) { -- // The range of sampling factor is [1, 4]. Pack them into integer to make the -- // matching code simpler. For example, 0x211 means the sampling factor are 2, -- // 1, 1 for 3 components. -- unsigned int h = 0, v = 0; -- for (int i = 0; i < frame_header.num_components; i++) { -- DCHECK_LE(frame_header.components[i].horizontal_sampling_factor, 4); -- DCHECK_LE(frame_header.components[i].vertical_sampling_factor, 4); -- h = h << 4 | frame_header.components[i].horizontal_sampling_factor; -- v = v << 4 | frame_header.components[i].vertical_sampling_factor; -- } -- -- switch (frame_header.num_components) { -- case 1: // Grey image -- return VA_RT_FORMAT_YUV400; -- -- case 3: // Y Cb Cr color image -- // See https://en.wikipedia.org/wiki/Chroma_subsampling for the -- // definition of these numbers. -- if (h == 0x211 && v == 0x211) -- return VA_RT_FORMAT_YUV420; -- -- if (h == 0x211 && v == 0x111) -- return VA_RT_FORMAT_YUV422; -- -- if (h == 0x111 && v == 0x111) -- return VA_RT_FORMAT_YUV444; -- -- if (h == 0x411 && v == 0x111) -- return VA_RT_FORMAT_YUV411; -- } -- VLOGF(1) << "Unsupported sampling factor: num_components=" -- << frame_header.num_components << ", h=" << std::hex << h -- << ", v=" << v; -- -- return 0; --} -- --} // namespace -- --VaapiJpegDecodeAccelerator::DecodeRequest::DecodeRequest( -- int32_t bitstream_buffer_id, -- std::unique_ptr shm, -- const scoped_refptr& video_frame) -- : bitstream_buffer_id(bitstream_buffer_id), -- shm(std::move(shm)), -- video_frame(video_frame) {} -- --VaapiJpegDecodeAccelerator::DecodeRequest::~DecodeRequest() {} -- --void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id, -- Error error) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- VLOGF(1) << "Notifying of error " << error; -- DCHECK(client_); -- client_->NotifyError(bitstream_buffer_id, error); --} -- --void VaapiJpegDecodeAccelerator::NotifyErrorFromDecoderThread( -- int32_t bitstream_buffer_id, -- Error error) { -- DCHECK(decoder_task_runner_->BelongsToCurrentThread()); -- task_runner_->PostTask(FROM_HERE, -- base::Bind(&VaapiJpegDecodeAccelerator::NotifyError, -- weak_this_, bitstream_buffer_id, error)); --} -- --void VaapiJpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- client_->VideoFrameReady(bitstream_buffer_id); --} -- --VaapiJpegDecodeAccelerator::VaapiJpegDecodeAccelerator( -- const scoped_refptr& io_task_runner) -- : task_runner_(base::ThreadTaskRunnerHandle::Get()), -- io_task_runner_(io_task_runner), -- decoder_thread_("VaapiJpegDecoderThread"), -- va_surface_id_(VA_INVALID_SURFACE), -- weak_this_factory_(this) { -- weak_this_ = weak_this_factory_.GetWeakPtr(); --} -- --VaapiJpegDecodeAccelerator::~VaapiJpegDecodeAccelerator() { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- VLOGF(2) << "Destroying VaapiJpegDecodeAccelerator"; -- -- weak_this_factory_.InvalidateWeakPtrs(); -- decoder_thread_.Stop(); --} -- --bool VaapiJpegDecodeAccelerator::Initialize(Client* client) { -- VLOGF(2); -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- client_ = client; -- -- vaapi_wrapper_ = -- VaapiWrapper::Create(VaapiWrapper::kDecode, VAProfileJPEGBaseline, -- base::Bind(&ReportToUMA, VAAPI_ERROR)); -- -- if (!vaapi_wrapper_.get()) { -- VLOGF(1) << "Failed initializing VAAPI"; -- return false; -- } -- -- if (!decoder_thread_.Start()) { -- VLOGF(1) << "Failed to start decoding thread."; -- return false; -- } -- decoder_task_runner_ = decoder_thread_.task_runner(); -- -- return true; --} -- --bool VaapiJpegDecodeAccelerator::OutputPicture( -- VASurfaceID va_surface_id, -- int32_t input_buffer_id, -- const scoped_refptr& video_frame) { -- DCHECK(decoder_task_runner_->BelongsToCurrentThread()); -- -- TRACE_EVENT1("jpeg", "VaapiJpegDecodeAccelerator::OutputPicture", -- "input_buffer_id", input_buffer_id); -- -- DVLOGF(4) << "Outputting VASurface " << va_surface_id -- << " into video_frame associated with input buffer id " -- << input_buffer_id; -- -- VAImage image; -- VAImageFormat format; -- const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); -- memset(&image, 0, sizeof(image)); -- memset(&format, 0, sizeof(format)); -- format.fourcc = kI420Fourcc; -- format.byte_order = VA_LSB_FIRST; -- format.bits_per_pixel = 12; // 12 for I420 -- -- uint8_t* mem = nullptr; -- gfx::Size coded_size = video_frame->coded_size(); -- if (!vaapi_wrapper_->GetVaImage(va_surface_id, &format, coded_size, &image, -- reinterpret_cast(&mem))) { -- VLOGF(1) << "Cannot get VAImage"; -- return false; -- } -- -- // Copy image content from VAImage to VideoFrame. -- // The component order of VAImage I420 are Y, U, and V. -- DCHECK_EQ(image.num_planes, 3u); -- DCHECK_GE(image.width, coded_size.width()); -- DCHECK_GE(image.height, coded_size.height()); -- const uint8_t* src_y = mem + image.offsets[0]; -- const uint8_t* src_u = mem + image.offsets[1]; -- const uint8_t* src_v = mem + image.offsets[2]; -- size_t src_y_stride = image.pitches[0]; -- size_t src_u_stride = image.pitches[1]; -- size_t src_v_stride = image.pitches[2]; -- uint8_t* dst_y = video_frame->data(VideoFrame::kYPlane); -- uint8_t* dst_u = video_frame->data(VideoFrame::kUPlane); -- uint8_t* dst_v = video_frame->data(VideoFrame::kVPlane); -- size_t dst_y_stride = video_frame->stride(VideoFrame::kYPlane); -- size_t dst_u_stride = video_frame->stride(VideoFrame::kUPlane); -- size_t dst_v_stride = video_frame->stride(VideoFrame::kVPlane); -- -- if (libyuv::I420Copy(src_y, src_y_stride, // Y -- src_u, src_u_stride, // U -- src_v, src_v_stride, // V -- dst_y, dst_y_stride, // Y -- dst_u, dst_u_stride, // U -- dst_v, dst_v_stride, // V -- coded_size.width(), coded_size.height())) { -- VLOGF(1) << "I420Copy failed"; -- return false; -- } -- -- vaapi_wrapper_->ReturnVaImage(&image); -- -- task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::VideoFrameReady, -- weak_this_, input_buffer_id)); -- -- return true; --} -- --void VaapiJpegDecodeAccelerator::DecodeTask( -- const std::unique_ptr& request) { -- DVLOGF(4); -- DCHECK(decoder_task_runner_->BelongsToCurrentThread()); -- TRACE_EVENT0("jpeg", "DecodeTask"); -- -- JpegParseResult parse_result; -- if (!ParseJpegPicture( -- reinterpret_cast(request->shm->memory()), -- request->shm->size(), &parse_result)) { -- VLOGF(1) << "ParseJpegPicture failed"; -- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -- PARSE_JPEG_FAILED); -- return; -- } -- -- unsigned int new_va_rt_format = -- VaSurfaceFormatForJpeg(parse_result.frame_header); -- if (!new_va_rt_format) { -- VLOGF(1) << "Unsupported subsampling"; -- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -- UNSUPPORTED_JPEG); -- return; -- } -- -- // Reuse VASurface if size doesn't change. -- gfx::Size new_coded_size(parse_result.frame_header.coded_width, -- parse_result.frame_header.coded_height); -- if (new_coded_size != coded_size_ || va_surface_id_ == VA_INVALID_SURFACE || -- new_va_rt_format != va_rt_format_) { -- vaapi_wrapper_->DestroySurfaces(); -- va_surface_id_ = VA_INVALID_SURFACE; -- va_rt_format_ = new_va_rt_format; -- -- std::vector va_surfaces; -- if (!vaapi_wrapper_->CreateSurfaces(va_rt_format_, new_coded_size, 1, -- &va_surfaces)) { -- VLOGF(1) << "Create VA surface failed"; -- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -- PLATFORM_FAILURE); -- return; -- } -- va_surface_id_ = va_surfaces[0]; -- coded_size_ = new_coded_size; -- } -- -- if (!VaapiJpegDecoder::Decode(vaapi_wrapper_.get(), parse_result, -- va_surface_id_)) { -- VLOGF(1) << "Decode JPEG failed"; -- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -- PLATFORM_FAILURE); -- return; -- } -- -- if (!OutputPicture(va_surface_id_, request->bitstream_buffer_id, -- request->video_frame)) { -- VLOGF(1) << "Output picture failed"; -- NotifyErrorFromDecoderThread(request->bitstream_buffer_id, -- PLATFORM_FAILURE); -- return; -- } --} -- --void VaapiJpegDecodeAccelerator::Decode( -- const BitstreamBuffer& bitstream_buffer, -- const scoped_refptr& video_frame) { -- DCHECK(io_task_runner_->BelongsToCurrentThread()); -- TRACE_EVENT1("jpeg", "Decode", "input_id", bitstream_buffer.id()); -- -- DVLOGF(4) << "Mapping new input buffer id: " << bitstream_buffer.id() -- << " size: " << bitstream_buffer.size(); -- -- // SharedMemoryRegion will take over the |bitstream_buffer.handle()|. -- std::unique_ptr shm( -- new SharedMemoryRegion(bitstream_buffer, true)); -- -- if (bitstream_buffer.id() < 0) { -- VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); -- NotifyErrorFromDecoderThread(bitstream_buffer.id(), INVALID_ARGUMENT); -- return; -- } -- -- if (!shm->Map()) { -- VLOGF(1) << "Failed to map input buffer"; -- NotifyErrorFromDecoderThread(bitstream_buffer.id(), UNREADABLE_INPUT); -- return; -- } -- -- std::unique_ptr request( -- new DecodeRequest(bitstream_buffer.id(), std::move(shm), video_frame)); -- -- decoder_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiJpegDecodeAccelerator::DecodeTask, -- base::Unretained(this), base::Passed(&request))); --} -- --bool VaapiJpegDecodeAccelerator::IsSupported() { -- return VaapiWrapper::IsJpegDecodeSupported(); --} -- --} // namespace media ---- a/media/gpu/vaapi_jpeg_decode_accelerator.h -+++ /dev/null -@@ -1,121 +0,0 @@ --// Copyright 2015 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#ifndef MEDIA_GPU_VAAPI_JPEG_DECODE_ACCELERATOR_H_ --#define MEDIA_GPU_VAAPI_JPEG_DECODE_ACCELERATOR_H_ -- --#include -- --#include -- --#include "base/macros.h" --#include "base/memory/linked_ptr.h" --#include "base/memory/weak_ptr.h" --#include "base/single_thread_task_runner.h" --#include "base/synchronization/lock.h" --#include "base/threading/thread.h" --#include "media/base/bitstream_buffer.h" --#include "media/gpu/media_gpu_export.h" --#include "media/gpu/shared_memory_region.h" --#include "media/gpu/vaapi_jpeg_decoder.h" --#include "media/gpu/vaapi_wrapper.h" --#include "media/video/jpeg_decode_accelerator.h" -- --namespace media { -- --// Class to provide JPEG decode acceleration for Intel systems with hardware --// support for it, and on which libva is available. --// Decoding tasks are performed in a separate decoding thread. --// --// Threading/life-cycle: this object is created & destroyed on the GPU --// ChildThread. A few methods on it are called on the decoder thread which is --// stopped during |this->Destroy()|, so any tasks posted to the decoder thread --// can assume |*this| is still alive. See |weak_this_| below for more details. --class MEDIA_GPU_EXPORT VaapiJpegDecodeAccelerator -- : public JpegDecodeAccelerator { -- public: -- VaapiJpegDecodeAccelerator( -- const scoped_refptr& io_task_runner); -- ~VaapiJpegDecodeAccelerator() override; -- -- // JpegDecodeAccelerator implementation. -- bool Initialize(JpegDecodeAccelerator::Client* client) override; -- void Decode(const BitstreamBuffer& bitstream_buffer, -- const scoped_refptr& video_frame) override; -- bool IsSupported() override; -- -- private: -- // An input buffer and the corresponding output video frame awaiting -- // consumption, provided by the client. -- struct DecodeRequest { -- DecodeRequest(int32_t bitstream_buffer_id, -- std::unique_ptr shm, -- const scoped_refptr& video_frame); -- ~DecodeRequest(); -- -- int32_t bitstream_buffer_id; -- std::unique_ptr shm; -- scoped_refptr video_frame; -- }; -- -- // Notifies the client that an error has occurred and decoding cannot -- // continue. -- void NotifyError(int32_t bitstream_buffer_id, Error error); -- void NotifyErrorFromDecoderThread(int32_t bitstream_buffer_id, Error error); -- void VideoFrameReady(int32_t bitstream_buffer_id); -- -- // Processes one decode |request|. -- void DecodeTask(const std::unique_ptr& request); -- -- // Puts contents of |va_surface| into given |video_frame|, releases the -- // surface and passes the |input_buffer_id| of the resulting picture to -- // client for output. -- bool OutputPicture(VASurfaceID va_surface_id, -- int32_t input_buffer_id, -- const scoped_refptr& video_frame); -- -- // ChildThread's task runner. -- scoped_refptr task_runner_; -- -- // GPU IO task runner. -- scoped_refptr io_task_runner_; -- -- // The client of this class. -- Client* client_; -- -- // WeakPtr<> pointing to |this| for use in posting tasks from the decoder -- // thread back to the ChildThread. Because the decoder thread is a member of -- // this class, any task running on the decoder thread is guaranteed that this -- // object is still alive. As a result, tasks posted from ChildThread to -- // decoder thread should use base::Unretained(this), and tasks posted from the -- // decoder thread to the ChildThread should use |weak_this_|. -- base::WeakPtr weak_this_; -- -- scoped_refptr vaapi_wrapper_; -- -- // Comes after vaapi_wrapper_ to ensure its destructor is executed before -- // |vaapi_wrapper_| is destroyed. -- std::unique_ptr decoder_; -- base::Thread decoder_thread_; -- // Use this to post tasks to |decoder_thread_| instead of -- // |decoder_thread_.task_runner()| because the latter will be NULL once -- // |decoder_thread_.Stop()| returns. -- scoped_refptr decoder_task_runner_; -- -- // The current VA surface for decoding. -- VASurfaceID va_surface_id_; -- // The coded size associated with |va_surface_id_|. -- gfx::Size coded_size_; -- // The VA RT format associated with |va_surface_id_|. -- unsigned int va_rt_format_; -- -- // The WeakPtrFactory for |weak_this_|. -- base::WeakPtrFactory weak_this_factory_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiJpegDecodeAccelerator); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VAAPI_JPEG_DECODE_ACCELERATOR_H_ ---- a/media/gpu/vaapi_jpeg_decoder.cc -+++ /dev/null -@@ -1,229 +0,0 @@ --// Copyright 2015 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_jpeg_decoder.h" -- --#include --#include -- --#include "base/logging.h" --#include "base/macros.h" --#include "media/filters/jpeg_parser.h" -- --namespace media { -- --// VAAPI only support subset of JPEG profiles. This function determines a given --// parsed JPEG result is supported or not. --static bool IsVaapiSupportedJpeg(const JpegParseResult& jpeg) { -- if (jpeg.frame_header.visible_width < 1 || -- jpeg.frame_header.visible_height < 1) { -- DLOG(ERROR) << "width(" << jpeg.frame_header.visible_width -- << ") and height(" << jpeg.frame_header.visible_height -- << ") should be at least 1"; -- return false; -- } -- -- // Size 64k*64k is the maximum in the JPEG standard. VAAPI doesn't support -- // resolutions larger than 16k*16k. -- const int kMaxDimension = 16384; -- if (jpeg.frame_header.coded_width > kMaxDimension || -- jpeg.frame_header.coded_height > kMaxDimension) { -- DLOG(ERROR) << "VAAPI doesn't support size(" -- << jpeg.frame_header.coded_width << "*" -- << jpeg.frame_header.coded_height << ") larger than " -- << kMaxDimension << "*" << kMaxDimension; -- return false; -- } -- -- if (jpeg.frame_header.num_components != 3) { -- DLOG(ERROR) << "VAAPI doesn't support num_components(" -- << static_cast(jpeg.frame_header.num_components) -- << ") != 3"; -- return false; -- } -- -- if (jpeg.frame_header.components[0].horizontal_sampling_factor < -- jpeg.frame_header.components[1].horizontal_sampling_factor || -- jpeg.frame_header.components[0].horizontal_sampling_factor < -- jpeg.frame_header.components[2].horizontal_sampling_factor) { -- DLOG(ERROR) << "VAAPI doesn't supports horizontal sampling factor of Y" -- << " smaller than Cb and Cr"; -- return false; -- } -- -- if (jpeg.frame_header.components[0].vertical_sampling_factor < -- jpeg.frame_header.components[1].vertical_sampling_factor || -- jpeg.frame_header.components[0].vertical_sampling_factor < -- jpeg.frame_header.components[2].vertical_sampling_factor) { -- DLOG(ERROR) << "VAAPI doesn't supports vertical sampling factor of Y" -- << " smaller than Cb and Cr"; -- return false; -- } -- -- return true; --} -- --static void FillPictureParameters( -- const JpegFrameHeader& frame_header, -- VAPictureParameterBufferJPEGBaseline* pic_param) { -- memset(pic_param, 0, sizeof(*pic_param)); -- pic_param->picture_width = frame_header.coded_width; -- pic_param->picture_height = frame_header.coded_height; -- pic_param->num_components = frame_header.num_components; -- -- for (int i = 0; i < pic_param->num_components; i++) { -- pic_param->components[i].component_id = frame_header.components[i].id; -- pic_param->components[i].h_sampling_factor = -- frame_header.components[i].horizontal_sampling_factor; -- pic_param->components[i].v_sampling_factor = -- frame_header.components[i].vertical_sampling_factor; -- pic_param->components[i].quantiser_table_selector = -- frame_header.components[i].quantization_table_selector; -- } --} -- --static void FillIQMatrix(const JpegQuantizationTable* q_table, -- VAIQMatrixBufferJPEGBaseline* iq_matrix) { -- memset(iq_matrix, 0, sizeof(*iq_matrix)); -- static_assert(kJpegMaxQuantizationTableNum == -- arraysize(iq_matrix->load_quantiser_table), -- "max number of quantization table mismatched"); -- for (size_t i = 0; i < kJpegMaxQuantizationTableNum; i++) { -- if (!q_table[i].valid) -- continue; -- iq_matrix->load_quantiser_table[i] = 1; -- static_assert( -- arraysize(iq_matrix->quantiser_table[i]) == arraysize(q_table[i].value), -- "number of quantization entries mismatched"); -- for (size_t j = 0; j < arraysize(q_table[i].value); j++) -- iq_matrix->quantiser_table[i][j] = q_table[i].value[j]; -- } --} -- --static void FillHuffmanTable(const JpegHuffmanTable* dc_table, -- const JpegHuffmanTable* ac_table, -- VAHuffmanTableBufferJPEGBaseline* huffman_table) { -- memset(huffman_table, 0, sizeof(*huffman_table)); -- // Use default huffman tables if not specified in header. -- bool has_huffman_table = false; -- for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { -- if (dc_table[i].valid || ac_table[i].valid) { -- has_huffman_table = true; -- break; -- } -- } -- if (!has_huffman_table) { -- dc_table = kDefaultDcTable; -- ac_table = kDefaultAcTable; -- } -- -- static_assert(kJpegMaxHuffmanTableNumBaseline == -- arraysize(huffman_table->load_huffman_table), -- "max number of huffman table mismatched"); -- static_assert(sizeof(huffman_table->huffman_table[0].num_dc_codes) == -- sizeof(dc_table[0].code_length), -- "size of huffman table code length mismatch"); -- static_assert(sizeof(huffman_table->huffman_table[0].dc_values[0]) == -- sizeof(dc_table[0].code_value[0]), -- "size of huffman table code value mismatch"); -- for (size_t i = 0; i < kJpegMaxHuffmanTableNumBaseline; i++) { -- if (!dc_table[i].valid || !ac_table[i].valid) -- continue; -- huffman_table->load_huffman_table[i] = 1; -- -- memcpy(huffman_table->huffman_table[i].num_dc_codes, -- dc_table[i].code_length, -- sizeof(huffman_table->huffman_table[i].num_dc_codes)); -- memcpy(huffman_table->huffman_table[i].dc_values, dc_table[i].code_value, -- sizeof(huffman_table->huffman_table[i].dc_values)); -- memcpy(huffman_table->huffman_table[i].num_ac_codes, -- ac_table[i].code_length, -- sizeof(huffman_table->huffman_table[i].num_ac_codes)); -- memcpy(huffman_table->huffman_table[i].ac_values, ac_table[i].code_value, -- sizeof(huffman_table->huffman_table[i].ac_values)); -- } --} -- --static void FillSliceParameters( -- const JpegParseResult& parse_result, -- VASliceParameterBufferJPEGBaseline* slice_param) { -- memset(slice_param, 0, sizeof(*slice_param)); -- slice_param->slice_data_size = parse_result.data_size; -- slice_param->slice_data_offset = 0; -- slice_param->slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -- slice_param->slice_horizontal_position = 0; -- slice_param->slice_vertical_position = 0; -- slice_param->num_components = parse_result.scan.num_components; -- for (int i = 0; i < slice_param->num_components; i++) { -- slice_param->components[i].component_selector = -- parse_result.scan.components[i].component_selector; -- slice_param->components[i].dc_table_selector = -- parse_result.scan.components[i].dc_selector; -- slice_param->components[i].ac_table_selector = -- parse_result.scan.components[i].ac_selector; -- } -- slice_param->restart_interval = parse_result.restart_interval; -- -- // Cast to int to prevent overflow. -- int max_h_factor = -- parse_result.frame_header.components[0].horizontal_sampling_factor; -- int max_v_factor = -- parse_result.frame_header.components[0].vertical_sampling_factor; -- int mcu_cols = parse_result.frame_header.coded_width / (max_h_factor * 8); -- DCHECK_GT(mcu_cols, 0); -- int mcu_rows = parse_result.frame_header.coded_height / (max_v_factor * 8); -- DCHECK_GT(mcu_rows, 0); -- slice_param->num_mcus = mcu_rows * mcu_cols; --} -- --// static --bool VaapiJpegDecoder::Decode(VaapiWrapper* vaapi_wrapper, -- const JpegParseResult& parse_result, -- VASurfaceID va_surface) { -- DCHECK_NE(va_surface, VA_INVALID_SURFACE); -- if (!IsVaapiSupportedJpeg(parse_result)) -- return false; -- -- // Set picture parameters. -- VAPictureParameterBufferJPEGBaseline pic_param; -- FillPictureParameters(parse_result.frame_header, &pic_param); -- if (!vaapi_wrapper->SubmitBuffer(VAPictureParameterBufferType, -- sizeof(pic_param), &pic_param)) -- return false; -- -- // Set quantization table. -- VAIQMatrixBufferJPEGBaseline iq_matrix; -- FillIQMatrix(parse_result.q_table, &iq_matrix); -- if (!vaapi_wrapper->SubmitBuffer(VAIQMatrixBufferType, sizeof(iq_matrix), -- &iq_matrix)) -- return false; -- -- // Set huffman table. -- VAHuffmanTableBufferJPEGBaseline huffman_table; -- FillHuffmanTable(parse_result.dc_table, parse_result.ac_table, -- &huffman_table); -- if (!vaapi_wrapper->SubmitBuffer(VAHuffmanTableBufferType, -- sizeof(huffman_table), &huffman_table)) -- return false; -- -- // Set slice parameters. -- VASliceParameterBufferJPEGBaseline slice_param; -- FillSliceParameters(parse_result, &slice_param); -- if (!vaapi_wrapper->SubmitBuffer(VASliceParameterBufferType, -- sizeof(slice_param), &slice_param)) -- return false; -- -- // Set scan data. -- if (!vaapi_wrapper->SubmitBuffer(VASliceDataBufferType, -- parse_result.data_size, -- const_cast(parse_result.data))) -- return false; -- -- if (!vaapi_wrapper->ExecuteAndDestroyPendingBuffers(va_surface)) -- return false; -- -- return true; --} -- --} // namespace media ---- a/media/gpu/vaapi_jpeg_decoder.h -+++ /dev/null -@@ -1,43 +0,0 @@ --// Copyright 2015 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#ifndef MEDIA_GPU_VAAPI_JPEG_DECODER_H_ --#define MEDIA_GPU_VAAPI_JPEG_DECODER_H_ -- --#include "base/macros.h" --#include "media/gpu/media_gpu_export.h" --#include "media/gpu/vaapi_wrapper.h" -- --namespace media { -- --struct JpegParseResult; -- --// A JPEG decoder that utilizes VA-API hardware video decode acceleration on --// Intel systems. Provides functionality to allow plugging VAAPI HW --// acceleration into the JpegDecodeAccelerator framework. --// --// Clients of this class are expected to manage VA surfaces created via --// VaapiWrapper, parse JPEG picture via ParseJpegPicture, and then pass --// them to this class. --class MEDIA_GPU_EXPORT VaapiJpegDecoder { -- public: -- // Decode a JPEG picture. It will fill VA-API parameters and call -- // corresponding VA-API methods according to parsed JPEG result -- // |parse_result|. Decoded data will be outputted to the given |va_surface|. -- // Return false on failure. -- // |vaapi_wrapper| should be initialized in kDecode mode with -- // VAProfileJPEGBaseline profile. -- // |va_surface| should be created with size at least as large as the picture -- // size. -- static bool Decode(VaapiWrapper* vaapi_wrapper, -- const JpegParseResult& parse_result, -- VASurfaceID va_surface); -- -- private: -- DISALLOW_IMPLICIT_CONSTRUCTORS(VaapiJpegDecoder); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VAAPI_JPEG_DECODER_H_ ---- a/media/gpu/vaapi_jpeg_decoder_unittest.cc -+++ /dev/null -@@ -1,139 +0,0 @@ --// Copyright 2015 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include --#include -- --#include -- --// This has to be included first. --// See http://code.google.com/p/googletest/issues/detail?id=371 --#include "testing/gtest/include/gtest/gtest.h" -- --#include "base/at_exit.h" --#include "base/bind.h" --#include "base/files/file_util.h" --#include "base/logging.h" --#include "base/md5.h" --#include "base/path_service.h" --#include "base/strings/string_piece.h" --#include "media/base/test_data_util.h" --#include "media/base/video_frame.h" --#include "media/filters/jpeg_parser.h" --#include "media/gpu/vaapi_jpeg_decoder.h" -- --namespace media { --namespace { -- --const char* kTestFilename = "pixel-1280x720.jpg"; --const char* kExpectedMd5Sum = "6e9e1716073c9a9a1282e3f0e0dab743"; -- --void LogOnError() { -- LOG(FATAL) << "Oh noes! Decoder failed"; --} -- --class VaapiJpegDecoderTest : public ::testing::Test { -- protected: -- VaapiJpegDecoderTest() {} -- -- void SetUp() override { -- base::Closure report_error_cb = base::Bind(&LogOnError); -- wrapper_ = VaapiWrapper::Create(VaapiWrapper::kDecode, -- VAProfileJPEGBaseline, report_error_cb); -- ASSERT_TRUE(wrapper_); -- -- base::FilePath input_file = GetTestDataFilePath(kTestFilename); -- -- ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data_)) -- << "failed to read input data from " << input_file.value(); -- } -- -- void TearDown() override { wrapper_ = nullptr; } -- -- bool VerifyDecode(const JpegParseResult& parse_result, -- const std::string& md5sum); -- -- protected: -- scoped_refptr wrapper_; -- std::string jpeg_data_; --}; -- --bool VaapiJpegDecoderTest::VerifyDecode(const JpegParseResult& parse_result, -- const std::string& expected_md5sum) { -- gfx::Size size(parse_result.frame_header.coded_width, -- parse_result.frame_header.coded_height); -- -- std::vector va_surfaces; -- if (!wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)) -- return false; -- -- if (!VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])) { -- LOG(ERROR) << "Decode failed"; -- return false; -- } -- -- VAImage image; -- VAImageFormat format; -- const uint32_t kI420Fourcc = VA_FOURCC('I', '4', '2', '0'); -- memset(&image, 0, sizeof(image)); -- memset(&format, 0, sizeof(format)); -- format.fourcc = kI420Fourcc; -- format.byte_order = VA_LSB_FIRST; -- format.bits_per_pixel = 12; // 12 for I420 -- -- void* mem; -- if (!wrapper_->GetVaImage(va_surfaces[0], &format, size, &image, &mem)) { -- LOG(ERROR) << "Cannot get VAImage"; -- return false; -- } -- EXPECT_EQ(kI420Fourcc, image.format.fourcc); -- -- base::StringPiece result(reinterpret_cast(mem), -- VideoFrame::AllocationSize(PIXEL_FORMAT_I420, size)); -- EXPECT_EQ(expected_md5sum, base::MD5String(result)); -- -- wrapper_->ReturnVaImage(&image); -- -- return true; --} -- --TEST_F(VaapiJpegDecoderTest, DecodeSuccess) { -- JpegParseResult parse_result; -- ASSERT_TRUE( -- ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), -- jpeg_data_.size(), &parse_result)); -- -- EXPECT_TRUE(VerifyDecode(parse_result, kExpectedMd5Sum)); --} -- --TEST_F(VaapiJpegDecoderTest, DecodeFail) { -- JpegParseResult parse_result; -- ASSERT_TRUE( -- ParseJpegPicture(reinterpret_cast(jpeg_data_.data()), -- jpeg_data_.size(), &parse_result)); -- -- // Not supported by VAAPI. -- parse_result.frame_header.num_components = 1; -- parse_result.scan.num_components = 1; -- -- gfx::Size size(parse_result.frame_header.coded_width, -- parse_result.frame_header.coded_height); -- -- std::vector va_surfaces; -- ASSERT_TRUE( -- wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, size, 1, &va_surfaces)); -- -- EXPECT_FALSE( -- VaapiJpegDecoder::Decode(wrapper_.get(), parse_result, va_surfaces[0])); --} -- --} // namespace --} // namespace media -- --int main(int argc, char** argv) { -- testing::InitGoogleTest(&argc, argv); -- base::AtExitManager exit_manager; -- media::VaapiWrapper::PreSandboxInitialization(); -- return RUN_ALL_TESTS(); --} ---- a/media/gpu/vaapi_jpeg_encode_accelerator.cc -+++ /dev/null -@@ -1,268 +0,0 @@ --// Copyright 2017 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_jpeg_encode_accelerator.h" -- --#include -- --#include --#include -- --#include "base/bind.h" --#include "base/logging.h" --#include "base/metrics/histogram_macros.h" --#include "base/sequence_checker.h" --#include "base/task_scheduler/post_task.h" --#include "base/threading/thread_task_runner_handle.h" --#include "base/trace_event/trace_event.h" --#include "media/base/bind_to_current_loop.h" --#include "media/base/video_frame.h" --#include "media/gpu/vaapi_jpeg_encoder.h" -- --namespace media { -- --namespace { -- --// UMA results that the VaapiJpegEncodeAccelerator class reports. --// These values are persisted to logs, and should therefore never be renumbered --// nor reused. --enum VAJEAEncoderResult { -- VAAPI_SUCCESS = 0, -- VAAPI_ERROR, -- VAJEA_ENCODER_RESULT_MAX = VAAPI_ERROR, --}; -- --static void ReportToUMA(VAJEAEncoderResult result) { -- UMA_HISTOGRAM_ENUMERATION("Media.VAJEA.EncoderResult", result, -- VAJEAEncoderResult::VAJEA_ENCODER_RESULT_MAX + 1); --} --} // namespace -- --VaapiJpegEncodeAccelerator::EncodeRequest::EncodeRequest( -- scoped_refptr video_frame, -- std::unique_ptr shm, -- int quality) -- : video_frame(std::move(video_frame)), -- shm(std::move(shm)), -- quality(quality) {} -- --VaapiJpegEncodeAccelerator::EncodeRequest::~EncodeRequest() {} -- --class VaapiJpegEncodeAccelerator::Encoder { -- public: -- Encoder(scoped_refptr vaapi_wrapper, -- base::RepeatingCallback video_frame_ready_cb, -- base::RepeatingCallback notify_error_cb); -- ~Encoder(); -- -- // Processes one encode |request|. -- void EncodeTask(std::unique_ptr request); -- -- private: -- // |cached_output_buffer_id_| is the last allocated VABuffer during -- // EncodeTask() and |cached_output_buffer_size_| is the size of it. -- // If the next call to EncodeTask() does not require a buffer bigger than -- // |cached_output_buffer_size_|, |cached_output_buffer_id_| will be reused. -- size_t cached_output_buffer_size_; -- VABufferID cached_output_buffer_id_; -- -- std::unique_ptr jpeg_encoder_; -- scoped_refptr vaapi_wrapper_; -- -- base::RepeatingCallback video_frame_ready_cb_; -- base::RepeatingCallback notify_error_cb_; -- -- SEQUENCE_CHECKER(sequence_checker_); -- -- DISALLOW_COPY_AND_ASSIGN(Encoder); --}; -- --VaapiJpegEncodeAccelerator::Encoder::Encoder( -- scoped_refptr vaapi_wrapper, -- base::RepeatingCallback video_frame_ready_cb, -- base::RepeatingCallback notify_error_cb) -- : cached_output_buffer_size_(0), -- jpeg_encoder_(new VaapiJpegEncoder(vaapi_wrapper)), -- vaapi_wrapper_(std::move(vaapi_wrapper)), -- video_frame_ready_cb_(std::move(video_frame_ready_cb)), -- notify_error_cb_(std::move(notify_error_cb)) { -- DETACH_FROM_SEQUENCE(sequence_checker_); --} -- --VaapiJpegEncodeAccelerator::Encoder::~Encoder() { -- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); --} -- --void VaapiJpegEncodeAccelerator::Encoder::EncodeTask( -- std::unique_ptr request) { -- TRACE_EVENT0("jpeg", "EncodeTask"); -- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -- -- const int video_frame_id = request->video_frame->unique_id(); -- gfx::Size input_size = request->video_frame->coded_size(); -- std::vector va_surfaces; -- if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, input_size, 1, -- &va_surfaces)) { -- VLOG(1) << "Failed to create VA surface"; -- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -- return; -- } -- VASurfaceID va_surface_id = va_surfaces[0]; -- -- if (!vaapi_wrapper_->UploadVideoFrameToSurface(request->video_frame, -- va_surface_id)) { -- VLOG(1) << "Failed to upload video frame to VA surface"; -- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -- return; -- } -- -- // Create output buffer for encoding result. -- size_t max_coded_buffer_size = -- VaapiJpegEncoder::GetMaxCodedBufferSize(input_size); -- if (max_coded_buffer_size > cached_output_buffer_size_) { -- vaapi_wrapper_->DestroyCodedBuffers(); -- cached_output_buffer_size_ = 0; -- -- VABufferID output_buffer_id; -- if (!vaapi_wrapper_->CreateCodedBuffer(max_coded_buffer_size, -- &output_buffer_id)) { -- VLOG(1) << "Failed to create VA buffer for encoding output"; -- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -- return; -- } -- cached_output_buffer_size_ = max_coded_buffer_size; -- cached_output_buffer_id_ = output_buffer_id; -- } -- -- if (!jpeg_encoder_->Encode(input_size, request->quality, va_surface_id, -- cached_output_buffer_id_)) { -- VLOG(1) << "Encode JPEG failed"; -- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -- return; -- } -- -- // Get the encoded output. DownloadFromCodedBuffer() is a blocking call. It -- // would wait until encoding is finished. -- size_t encoded_size = 0; -- if (!vaapi_wrapper_->DownloadFromCodedBuffer( -- cached_output_buffer_id_, va_surface_id, -- static_cast(request->shm->memory()), request->shm->size(), -- &encoded_size)) { -- VLOG(1) << "Failed to retrieve output image from VA coded buffer"; -- notify_error_cb_.Run(video_frame_id, PLATFORM_FAILURE); -- } -- -- video_frame_ready_cb_.Run(request->video_frame->unique_id(), encoded_size); --} -- --VaapiJpegEncodeAccelerator::VaapiJpegEncodeAccelerator( -- scoped_refptr io_task_runner) -- : task_runner_(base::ThreadTaskRunnerHandle::Get()), -- io_task_runner_(std::move(io_task_runner)), -- weak_this_factory_(this) { -- weak_this_ = weak_this_factory_.GetWeakPtr(); --} -- --VaapiJpegEncodeAccelerator::~VaapiJpegEncodeAccelerator() { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- DVLOG(1) << "Destroying VaapiJpegEncodeAccelerator"; -- -- weak_this_factory_.InvalidateWeakPtrs(); -- encoder_task_runner_->DeleteSoon(FROM_HERE, std::move(encoder_)); --} -- --void VaapiJpegEncodeAccelerator::NotifyError(int video_frame_id, -- Status status) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- DLOG(ERROR) << "Notifying error: " << status; -- DCHECK(client_); -- client_->NotifyError(video_frame_id, status); --} -- --void VaapiJpegEncodeAccelerator::VideoFrameReady(int video_frame_id, -- size_t encoded_picture_size) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- ReportToUMA(VAJEAEncoderResult::VAAPI_SUCCESS); -- -- client_->VideoFrameReady(video_frame_id, encoded_picture_size); --} -- --JpegEncodeAccelerator::Status VaapiJpegEncodeAccelerator::Initialize( -- JpegEncodeAccelerator::Client* client) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- if (!VaapiWrapper::IsJpegEncodeSupported()) { -- return HW_JPEG_ENCODE_NOT_SUPPORTED; -- } -- -- client_ = client; -- scoped_refptr vaapi_wrapper = VaapiWrapper::Create( -- VaapiWrapper::kEncode, VAProfileJPEGBaseline, -- base::Bind(&ReportToUMA, VAJEAEncoderResult::VAAPI_ERROR)); -- -- if (!vaapi_wrapper) { -- VLOG(1) << "Failed initializing VAAPI"; -- return PLATFORM_FAILURE; -- } -- -- encoder_task_runner_ = base::CreateSingleThreadTaskRunnerWithTraits( -- {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); -- if (!encoder_task_runner_) { -- VLOG(1) << "Failed to create encoder task runner."; -- return THREAD_CREATION_FAILED; -- } -- -- encoder_ = std::make_unique( -- std::move(vaapi_wrapper), -- BindToCurrentLoop(base::BindRepeating( -- &VaapiJpegEncodeAccelerator::VideoFrameReady, weak_this_)), -- BindToCurrentLoop(base::BindRepeating( -- &VaapiJpegEncodeAccelerator::NotifyError, weak_this_))); -- -- return ENCODE_OK; --} -- --size_t VaapiJpegEncodeAccelerator::GetMaxCodedBufferSize( -- const gfx::Size& picture_size) { -- return VaapiJpegEncoder::GetMaxCodedBufferSize(picture_size); --} -- --void VaapiJpegEncodeAccelerator::Encode( -- scoped_refptr video_frame, -- int quality, -- const BitstreamBuffer& bitstream_buffer) { -- DCHECK(io_task_runner_->BelongsToCurrentThread()); -- -- int video_frame_id = video_frame->unique_id(); -- TRACE_EVENT1("jpeg", "Encode", "input_id", video_frame_id); -- -- // TODO(shenghao): support other YUV formats. -- if (video_frame->format() != VideoPixelFormat::PIXEL_FORMAT_I420) { -- VLOG(1) << "Unsupported input format: " << video_frame->format(); -- task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, -- weak_this_, video_frame_id, INVALID_ARGUMENT)); -- return; -- } -- -- // SharedMemoryRegion will take ownership of the |bitstream_buffer.handle()|. -- auto shm = std::make_unique(bitstream_buffer, false); -- if (!shm->Map()) { -- VLOG(1) << "Failed to map output buffer"; -- task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiJpegEncodeAccelerator::NotifyError, weak_this_, -- video_frame_id, INACCESSIBLE_OUTPUT_BUFFER)); -- return; -- } -- -- auto request = std::make_unique(std::move(video_frame), -- std::move(shm), quality); -- encoder_task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiJpegEncodeAccelerator::Encoder::EncodeTask, -- base::Unretained(encoder_.get()), base::Passed(&request))); --} -- --} // namespace media ---- a/media/gpu/vaapi_jpeg_encode_accelerator.h -+++ /dev/null -@@ -1,96 +0,0 @@ --// Copyright 2017 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#ifndef MEDIA_GPU_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ --#define MEDIA_GPU_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ -- --#include -- --#include "base/macros.h" --#include "base/memory/weak_ptr.h" --#include "base/single_thread_task_runner.h" --#include "media/base/bitstream_buffer.h" --#include "media/gpu/media_gpu_export.h" --#include "media/gpu/shared_memory_region.h" --#include "media/gpu/vaapi_wrapper.h" --#include "media/video/jpeg_encode_accelerator.h" -- --namespace media { -- --// Class to provide JPEG encode acceleration for Intel systems with hardware --// support for it, and on which libva is available. --// Encoding tasks are performed in a separate encoding thread. --// --// Threading/life-cycle: this object is created & destroyed on the GPU --// ChildThread. Methods in nested class Encoder are called on the encoder --// thread which is stopped during destructor, so the callbacks bound with --// a weak this can be run on the encoder thread because it can assume --// VaapiJpegEncodeAccelerator is still alive. --class MEDIA_GPU_EXPORT VaapiJpegEncodeAccelerator -- : public JpegEncodeAccelerator { -- public: -- explicit VaapiJpegEncodeAccelerator( -- scoped_refptr io_task_runner); -- ~VaapiJpegEncodeAccelerator() override; -- -- // JpegEncodeAccelerator implementation. -- Status Initialize(JpegEncodeAccelerator::Client* client) override; -- size_t GetMaxCodedBufferSize(const gfx::Size& picture_size) override; -- -- // Currently only I420 format is supported for |video_frame|. -- void Encode(scoped_refptr video_frame, -- int quality, -- const BitstreamBuffer& bitstream_buffer) override; -- -- private: -- // An input video frame and the corresponding output buffer awaiting -- // consumption, provided by the client. -- struct EncodeRequest { -- EncodeRequest(scoped_refptr video_frame, -- std::unique_ptr shm, -- int quality); -- ~EncodeRequest(); -- -- scoped_refptr video_frame; -- std::unique_ptr shm; -- int quality; -- -- DISALLOW_COPY_AND_ASSIGN(EncodeRequest); -- }; -- -- // The Encoder class is a collection of methods that run on -- // |encoder_task_runner_|. -- class Encoder; -- -- // Notifies the client that an error has occurred and encoding cannot -- // continue. -- void NotifyError(int video_frame_id, Status status); -- -- void VideoFrameReady(int video_frame_id, size_t encoded_picture_size); -- -- // ChildThread's task runner. -- scoped_refptr task_runner_; -- -- // GPU IO task runner. -- scoped_refptr io_task_runner_; -- -- // The client of this class. -- Client* client_; -- -- // Use this to post tasks to encoder thread. -- scoped_refptr encoder_task_runner_; -- -- std::unique_ptr encoder_; -- -- // |weak_this_| is used to post tasks from |encoder_task_runner_| to -- // |task_runner_|. -- base::WeakPtr weak_this_; -- base::WeakPtrFactory weak_this_factory_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncodeAccelerator); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VAAPI_JPEG_ENCODE_ACCELERATOR_H_ ---- a/media/gpu/vaapi_jpeg_encoder.cc -+++ /dev/null -@@ -1,427 +0,0 @@ --// Copyright 2017 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_jpeg_encoder.h" -- --#include --#include --#include -- --#include "base/logging.h" --#include "base/macros.h" --#include "base/numerics/safe_conversions.h" --#include "media/filters/jpeg_parser.h" --#include "media/gpu/vaapi_wrapper.h" -- --#define ARRAY_MEMCPY_CHECKED(to, from) \ -- do { \ -- static_assert(sizeof(to) == sizeof(from), \ -- #from " and " #to " arrays must be of same size"); \ -- memcpy(to, from, sizeof(to)); \ -- } while (0) -- --namespace media { -- --namespace { -- --// JPEG header only uses 2 bytes to represent width and height. --const int kMaxDimension = 65535; --const size_t kDctSize2 = 64; --const size_t kNumDcRunSizeBits = 16; --const size_t kNumAcRunSizeBits = 16; --const size_t kNumDcCodeWordsHuffVal = 12; --const size_t kNumAcCodeWordsHuffVal = 162; --const size_t kJpegHeaderSize = 83 + (kDctSize2 * 2) + (kNumDcRunSizeBits * 2) + -- (kNumDcCodeWordsHuffVal * 2) + -- (kNumAcRunSizeBits * 2) + -- (kNumAcCodeWordsHuffVal * 2); -- --const uint8_t kZigZag8x8[64] = { -- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, -- 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, -- 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, -- 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; -- --const JpegQuantizationTable kDefaultQuantTable[2] = { -- // Table K.1 Luminance quantization table values. -- { -- true, -- {16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, -- 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62, -- 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, -- 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99}, -- }, -- // Table K.2 Chrominance quantization table values. -- { -- true, -- {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, -- 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99, -- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, -- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}, -- }, --}; -- --using JPEGHeader = uint8_t[kJpegHeaderSize]; -- --void FillPictureParameters(const gfx::Size& input_size, -- int quality, -- VABufferID output_buffer_id, -- VAEncPictureParameterBufferJPEG* pic_param) { -- pic_param->picture_width = input_size.width(); -- pic_param->picture_height = input_size.height(); -- pic_param->num_components = 3; -- -- // Output buffer. -- pic_param->coded_buf = output_buffer_id; -- pic_param->quality = quality; -- // Profile = Baseline. -- pic_param->pic_flags.bits.profile = 0; -- // Sequential encoding. -- pic_param->pic_flags.bits.progressive = 0; -- // Uses Huffman coding. -- pic_param->pic_flags.bits.huffman = 1; -- // Input format is interleaved (YUV). -- pic_param->pic_flags.bits.interleaved = 0; -- // Non-differential Encoding. -- pic_param->pic_flags.bits.differential = 0; -- // Only 8 bit sample depth is currently supported. -- pic_param->sample_bit_depth = 8; -- pic_param->num_scan = 1; --} -- --void FillQMatrix(VAQMatrixBufferJPEG* q_matrix) { -- // Fill the raw, unscaled quantization tables for libva. The VAAPI driver is -- // responsible for scaling the quantization tables based on picture -- // parameter quality. -- const JpegQuantizationTable& luminance = kDefaultQuantTable[0]; -- static_assert( -- arraysize(luminance.value) == arraysize(q_matrix->lum_quantiser_matrix), -- "Luminance quantization table size mismatch."); -- static_assert(arraysize(kZigZag8x8) == arraysize(luminance.value), -- "Luminance quantization table size mismatch."); -- q_matrix->load_lum_quantiser_matrix = 1; -- for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { -- q_matrix->lum_quantiser_matrix[i] = luminance.value[kZigZag8x8[i]]; -- } -- -- const JpegQuantizationTable& chrominance = kDefaultQuantTable[1]; -- static_assert(arraysize(chrominance.value) == -- arraysize(q_matrix->chroma_quantiser_matrix), -- "Chrominance quantization table size mismatch."); -- static_assert(arraysize(kZigZag8x8) == arraysize(chrominance.value), -- "Chrominance quantization table size mismatch."); -- q_matrix->load_chroma_quantiser_matrix = 1; -- for (size_t i = 0; i < arraysize(kZigZag8x8); i++) { -- q_matrix->chroma_quantiser_matrix[i] = chrominance.value[kZigZag8x8[i]]; -- } --} -- --void FillHuffmanTableParameters( -- VAHuffmanTableBufferJPEGBaseline* huff_table_param) { -- static_assert(arraysize(kDefaultDcTable) == arraysize(kDefaultAcTable), -- "DC table and AC table size mismatch."); -- static_assert( -- arraysize(kDefaultDcTable) == arraysize(huff_table_param->huffman_table), -- "DC table and destination table size mismatch."); -- -- for (size_t i = 0; i < arraysize(kDefaultDcTable); ++i) { -- const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; -- const JpegHuffmanTable& acTable = kDefaultAcTable[i]; -- huff_table_param->load_huffman_table[i] = true; -- -- // Load DC Table. -- ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_dc_codes, -- dcTable.code_length); -- // |code_values| of JpegHuffmanTable needs to hold DC and AC code values -- // so it has different size than -- // |huff_table_param->huffman_table[i].dc_values|. Therefore we can't use -- // ARRAY_MEMCPY_CHECKED() here. -- static_assert(arraysize(huff_table_param->huffman_table[i].dc_values) <= -- arraysize(dcTable.code_value), -- "DC table code value array too small."); -- memcpy(huff_table_param->huffman_table[i].dc_values, &dcTable.code_value[0], -- sizeof(huff_table_param->huffman_table[i].dc_values)); -- -- // Load AC Table. -- ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].num_ac_codes, -- acTable.code_length); -- ARRAY_MEMCPY_CHECKED(huff_table_param->huffman_table[i].ac_values, -- acTable.code_value); -- -- memset(huff_table_param->huffman_table[i].pad, 0, -- sizeof(huff_table_param->huffman_table[i].pad)); -- } --} -- --void FillSliceParameters(VAEncSliceParameterBufferJPEG* slice_param) { -- slice_param->restart_interval = 0; -- slice_param->num_components = 3; -- -- slice_param->components[0].component_selector = 1; -- slice_param->components[0].dc_table_selector = 0; -- slice_param->components[0].ac_table_selector = 0; -- -- slice_param->components[1].component_selector = 2; -- slice_param->components[1].dc_table_selector = 1; -- slice_param->components[1].ac_table_selector = 1; -- -- slice_param->components[2].component_selector = 3; -- slice_param->components[2].dc_table_selector = 1; -- slice_param->components[2].ac_table_selector = 1; --} -- --size_t FillJpegHeader(const gfx::Size& input_size, -- int quality, -- JPEGHeader& header) { -- unsigned int width = input_size.width(); -- unsigned int height = input_size.height(); -- -- size_t idx = 0; -- -- // Start Of Input. -- static const uint8_t kSOI[] = {0xFF, JPEG_SOI}; -- memcpy(header, kSOI, sizeof(kSOI)); -- idx += sizeof(kSOI); -- -- // Application Segment - JFIF standard 1.01. -- // TODO(shenghao): Use Exif (JPEG_APP1) instead. -- static const uint8_t kAppSegment[] = { -- 0xFF, JPEG_APP0, 0x00, -- 0x10, // Segment length:16 (2-byte). -- 0x4A, // J -- 0x46, // F -- 0x49, // I -- 0x46, // F -- 0x00, // 0 -- 0x01, // Major version. -- 0x01, // Minor version. -- 0x01, // Density units 0:no units, 1:pixels per inch, -- // 2: pixels per cm. -- 0x00, -- 0x48, // X density (2-byte). -- 0x00, -- 0x48, // Y density (2-byte). -- 0x00, // Thumbnail width. -- 0x00 // Thumbnail height. -- }; -- memcpy(header + idx, kAppSegment, sizeof(kAppSegment)); -- idx += sizeof(kAppSegment); -- -- if (quality <= 0) { -- quality = 1; -- } -- -- // Normalize quality factor. -- // Unlike VAQMatrixBufferJPEG, we have to scale quantization table in JPEG -- // header by ourselves. -- uint32_t quality_normalized = base::saturated_cast( -- (quality < 50) ? (5000 / quality) : (200 - (quality * 2))); -- -- // Quantization Tables. -- for (size_t i = 0; i < 2; ++i) { -- const uint8_t kQuantSegment[] = { -- 0xFF, JPEG_DQT, 0x00, -- 0x03 + kDctSize2, // Segment length:67 (2-byte). -- static_cast(i) // Precision (4-bit high) = 0, -- // Index (4-bit low) = i. -- }; -- memcpy(header + idx, kQuantSegment, sizeof(kQuantSegment)); -- idx += sizeof(kQuantSegment); -- -- const JpegQuantizationTable& quant_table = kDefaultQuantTable[i]; -- for (size_t j = 0; j < kDctSize2; ++j) { -- uint32_t scaled_quant_value = -- (quant_table.value[kZigZag8x8[j]] * quality_normalized) / 100; -- scaled_quant_value = std::min(255u, std::max(1u, scaled_quant_value)); -- header[idx++] = static_cast(scaled_quant_value); -- } -- } -- -- // Start of Frame - Baseline. -- const uint8_t kStartOfFrame[] = { -- 0xFF, -- JPEG_SOF0, // Baseline. -- 0x00, -- 0x11, // Segment length:17 (2-byte). -- 8, // Data precision. -- static_cast((height >> 8) & 0xFF), -- static_cast(height & 0xFF), -- static_cast((width >> 8) & 0xFF), -- static_cast(width & 0xFF), -- 0x03, // Number of Components. -- }; -- memcpy(header + idx, kStartOfFrame, sizeof(kStartOfFrame)); -- idx += sizeof(kStartOfFrame); -- for (uint8_t i = 0; i < 3; ++i) { -- // These are the values for U and V planes. -- uint8_t h_sample_factor = 1; -- uint8_t v_sample_factor = 1; -- uint8_t quant_table_number = 1; -- if (!i) { -- // These are the values for Y plane. -- h_sample_factor = 2; -- v_sample_factor = 2; -- quant_table_number = 0; -- } -- -- header[idx++] = i + 1; -- // Horizontal Sample Factor (4-bit high), -- // Vertical Sample Factor (4-bit low). -- header[idx++] = (h_sample_factor << 4) | v_sample_factor; -- header[idx++] = quant_table_number; -- } -- -- static const uint8_t kDcSegment[] = { -- 0xFF, JPEG_DHT, 0x00, -- 0x1F, // Segment length:31 (2-byte). -- }; -- static const uint8_t kAcSegment[] = { -- 0xFF, JPEG_DHT, 0x00, -- 0xB5, // Segment length:181 (2-byte). -- }; -- -- // Huffman Tables. -- for (size_t i = 0; i < 2; ++i) { -- // DC Table. -- memcpy(header + idx, kDcSegment, sizeof(kDcSegment)); -- idx += sizeof(kDcSegment); -- -- // Type (4-bit high) = 0:DC, Index (4-bit low). -- header[idx++] = static_cast(i); -- -- const JpegHuffmanTable& dcTable = kDefaultDcTable[i]; -- for (size_t j = 0; j < kNumDcRunSizeBits; ++j) -- header[idx++] = dcTable.code_length[j]; -- for (size_t j = 0; j < kNumDcCodeWordsHuffVal; ++j) -- header[idx++] = dcTable.code_value[j]; -- -- // AC Table. -- memcpy(header + idx, kAcSegment, sizeof(kAcSegment)); -- idx += sizeof(kAcSegment); -- -- // Type (4-bit high) = 1:AC, Index (4-bit low). -- header[idx++] = 0x10 | static_cast(i); -- -- const JpegHuffmanTable& acTable = kDefaultAcTable[i]; -- for (size_t j = 0; j < kNumAcRunSizeBits; ++j) -- header[idx++] = acTable.code_length[j]; -- for (size_t j = 0; j < kNumAcCodeWordsHuffVal; ++j) -- header[idx++] = acTable.code_value[j]; -- } -- -- // Start of Scan. -- static const uint8_t kStartOfScan[] = { -- 0xFF, JPEG_SOS, 0x00, -- 0x0C, // Segment Length:12 (2-byte). -- 0x03 // Number of components in scan. -- }; -- memcpy(header + idx, kStartOfScan, sizeof(kStartOfScan)); -- idx += sizeof(kStartOfScan); -- -- for (uint8_t i = 0; i < 3; ++i) { -- uint8_t dc_table_number = 1; -- uint8_t ac_table_number = 1; -- if (!i) { -- dc_table_number = 0; -- ac_table_number = 0; -- } -- -- header[idx++] = i + 1; -- // DC Table Selector (4-bit high), AC Table Selector (4-bit low). -- header[idx++] = (dc_table_number << 4) | ac_table_number; -- } -- header[idx++] = 0x00; // 0 for Baseline. -- header[idx++] = 0x3F; // 63 for Baseline. -- header[idx++] = 0x00; // 0 for Baseline. -- -- return idx << 3; --} -- --} // namespace -- --VaapiJpegEncoder::VaapiJpegEncoder(scoped_refptr vaapi_wrapper) -- : vaapi_wrapper_(vaapi_wrapper), -- q_matrix_cached_(nullptr), -- huff_table_param_cached_(nullptr), -- slice_param_cached_(nullptr) {} -- --VaapiJpegEncoder::~VaapiJpegEncoder() {} -- --size_t VaapiJpegEncoder::GetMaxCodedBufferSize(const gfx::Size& size) { -- return size.GetArea() * 3 / 2 + kJpegHeaderSize; --} -- --bool VaapiJpegEncoder::Encode(const gfx::Size& input_size, -- int quality, -- VASurfaceID surface_id, -- VABufferID output_buffer_id) { -- DCHECK_NE(surface_id, VA_INVALID_SURFACE); -- -- if (input_size.width() > kMaxDimension || -- input_size.height() > kMaxDimension) { -- return false; -- } -- -- // Set picture parameters. -- VAEncPictureParameterBufferJPEG pic_param; -- FillPictureParameters(input_size, quality, output_buffer_id, &pic_param); -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, -- sizeof(pic_param), &pic_param)) { -- return false; -- } -- -- if (!q_matrix_cached_) { -- q_matrix_cached_.reset(new VAQMatrixBufferJPEG()); -- FillQMatrix(q_matrix_cached_.get()); -- } -- if (!vaapi_wrapper_->SubmitBuffer(VAQMatrixBufferType, -- sizeof(*q_matrix_cached_), -- q_matrix_cached_.get())) { -- return false; -- } -- -- if (!huff_table_param_cached_) { -- huff_table_param_cached_.reset(new VAHuffmanTableBufferJPEGBaseline()); -- FillHuffmanTableParameters(huff_table_param_cached_.get()); -- } -- if (!vaapi_wrapper_->SubmitBuffer(VAHuffmanTableBufferType, -- sizeof(*huff_table_param_cached_), -- huff_table_param_cached_.get())) { -- return false; -- } -- -- // Set slice parameters. -- if (!slice_param_cached_) { -- slice_param_cached_.reset(new VAEncSliceParameterBufferJPEG()); -- FillSliceParameters(slice_param_cached_.get()); -- } -- if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, -- sizeof(*slice_param_cached_), -- slice_param_cached_.get())) { -- return false; -- } -- -- JPEGHeader header_data; -- size_t length_in_bits = FillJpegHeader(input_size, quality, header_data); -- -- VAEncPackedHeaderParameterBuffer header_param; -- memset(&header_param, 0, sizeof(header_param)); -- header_param.type = VAEncPackedHeaderRawData; -- header_param.bit_length = length_in_bits; -- header_param.has_emulation_bytes = 0; -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, -- sizeof(header_param), &header_param)) { -- return false; -- } -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, -- (length_in_bits + 7) / 8, header_data)) { -- return false; -- } -- -- // Submit the |surface_id| which contains input YUV frame and begin encoding. -- return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers(surface_id); --} -- --} // namespace media ---- a/media/gpu/vaapi_jpeg_encoder.h -+++ /dev/null -@@ -1,65 +0,0 @@ --// Copyright 2017 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#ifndef MEDIA_GPU_VAAPI_JPEG_ENCODER_H_ --#define MEDIA_GPU_VAAPI_JPEG_ENCODER_H_ -- --#include --#include -- --#include "base/macros.h" --#include "base/memory/scoped_refptr.h" --#include "media/gpu/media_gpu_export.h" --#include "ui/gfx/geometry/size.h" -- --namespace media { -- --class VaapiWrapper; -- --// A collection of methods that utilize VA-API hardware video encode --// acceleration on Intel systems. Provides functionality to allow plugging VAAPI --// HW acceleration into the JpegEncodeAccelerator framework. --// --// Clients are expected to manage VA surfaces and VA buffers created via --// VaapiWrapper, and pass them to this class. --class MEDIA_GPU_EXPORT VaapiJpegEncoder { -- public: -- // |vaapi_wrapper| should be initialized in VaapiWrapper::kEncode -- // mode with VAProfileJPEGBaseline profile. -- explicit VaapiJpegEncoder(scoped_refptr vaapi_wrapper); -- ~VaapiJpegEncoder(); -- -- // Encode a JPEG picture. It will fill VA-API parameters and call -- // corresponding VA-API methods according to |input_size|. -- // |quality| is the JPEG image quality -- // |surface_id| is the VA surface that contains input image. -- // |output_buffer_id| is the ID of VA buffer that encoded image will be -- // stored. The size of it should be at least as large as -- // GetMaxCodedBufferSize(). -- // Return false on failure. -- bool Encode(const gfx::Size& input_size, -- int quality, -- VASurfaceID surface_id, -- VABufferID output_buffer_id); -- -- // Gets the maximum possible encoded result size. -- // |size| is the dimension of the YUV image to be encoded. -- static size_t GetMaxCodedBufferSize(const gfx::Size& size); -- -- private: -- scoped_refptr vaapi_wrapper_; -- -- // |q_matrix_cached_|, |huff_table_param_cached_| and |slice_param_cached_| -- // are created when Encode() is called the first time. After that, they will -- // directly be used for all the subsequent Encode() calls. -- std::unique_ptr q_matrix_cached_; -- std::unique_ptr huff_table_param_cached_; -- std::unique_ptr slice_param_cached_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiJpegEncoder); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VAAPI_JPEG_ENCODER_H_ ---- a/media/gpu/vaapi_video_decode_accelerator.cc -+++ /dev/null -@@ -1,1871 +0,0 @@ --// Copyright (c) 2012 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_video_decode_accelerator.h" -- --#include -- --#include -- --#include -- --#include "base/bind.h" --#include "base/files/scoped_file.h" --#include "base/logging.h" --#include "base/macros.h" --#include "base/metrics/histogram_macros.h" --#include "base/stl_util.h" --#include "base/strings/string_util.h" --#include "base/synchronization/waitable_event.h" --#include "base/threading/thread_task_runner_handle.h" --#include "base/trace_event/trace_event.h" --#include "gpu/ipc/service/gpu_channel.h" --#include "media/base/bind_to_current_loop.h" --#include "media/gpu/accelerated_video_decoder.h" --#include "media/gpu/format_utils.h" --#include "media/gpu/h264_decoder.h" --#include "media/gpu/vaapi/vaapi_picture.h" --#include "media/gpu/vp8_decoder.h" --#include "media/gpu/vp9_decoder.h" --#include "media/video/picture.h" --#include "ui/gl/gl_image.h" -- --#define DVLOGF(level) DVLOG(level) << __func__ << "(): " --#define VLOGF(level) VLOG(level) << __func__ << "(): " -- --namespace media { -- --namespace { --// UMA errors that the VaapiVideoDecodeAccelerator class reports. --enum VAVDADecoderFailure { -- VAAPI_ERROR = 0, -- VAVDA_DECODER_FAILURES_MAX, --}; --// from ITU-T REC H.264 spec --// section 8.5.6 --// "Inverse scanning process for 4x4 transform coefficients and scaling lists" --static const int kZigzagScan4x4[16] = {0, 1, 4, 8, 5, 2, 3, 6, -- 9, 12, 13, 10, 7, 11, 14, 15}; -- --// section 8.5.7 --// "Inverse scanning process for 8x8 transform coefficients and scaling lists" --static const uint8_t kZigzagScan8x8[64] = { -- 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, -- 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, -- 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, -- 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63}; -- --// Returns the preferred VA_RT_FORMAT for the given |profile|. --unsigned int GetVaFormatForVideoCodecProfile(VideoCodecProfile profile) { -- if (profile == VP9PROFILE_PROFILE2 || profile == VP9PROFILE_PROFILE3) -- return VA_RT_FORMAT_YUV420_10BPP; -- return VA_RT_FORMAT_YUV420; --} -- --} // namespace -- --static void ReportToUMA(VAVDADecoderFailure failure) { -- UMA_HISTOGRAM_ENUMERATION("Media.VAVDA.DecoderFailure", failure, -- VAVDA_DECODER_FAILURES_MAX + 1); --} -- --#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ -- do { \ -- if (!(result)) { \ -- VLOGF(1) << log; \ -- NotifyError(error_code); \ -- return ret; \ -- } \ -- } while (0) -- --class VaapiVideoDecodeAccelerator::VaapiDecodeSurface -- : public base::RefCountedThreadSafe { -- public: -- VaapiDecodeSurface(int32_t bitstream_id, -- const scoped_refptr& va_surface); -- -- int32_t bitstream_id() const { return bitstream_id_; } -- scoped_refptr va_surface() { return va_surface_; } -- gfx::Rect visible_rect() const { return visible_rect_; } -- -- void set_visible_rect(const gfx::Rect& visible_rect) { -- visible_rect_ = visible_rect; -- } -- -- private: -- friend class base::RefCountedThreadSafe; -- ~VaapiDecodeSurface(); -- -- const int32_t bitstream_id_; -- const scoped_refptr va_surface_; -- gfx::Rect visible_rect_; --}; -- --VaapiVideoDecodeAccelerator::VaapiDecodeSurface::VaapiDecodeSurface( -- int32_t bitstream_id, -- const scoped_refptr& va_surface) -- : bitstream_id_(bitstream_id), va_surface_(va_surface) {} -- --VaapiVideoDecodeAccelerator::VaapiDecodeSurface::~VaapiDecodeSurface() {} -- --class VaapiH264Picture : public H264Picture { -- public: -- explicit VaapiH264Picture( -- scoped_refptr surface) -- : dec_surface_(surface) {} -- -- VaapiH264Picture* AsVaapiH264Picture() override { return this; } -- scoped_refptr dec_surface() { -- return dec_surface_; -- } -- -- private: -- ~VaapiH264Picture() override {} -- -- scoped_refptr dec_surface_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiH264Picture); --}; -- --class VaapiVideoDecodeAccelerator::VaapiH264Accelerator -- : public H264Decoder::H264Accelerator { -- public: -- VaapiH264Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, -- VaapiWrapper* vaapi_wrapper); -- ~VaapiH264Accelerator() override; -- -- // H264Decoder::H264Accelerator implementation. -- scoped_refptr CreateH264Picture() override; -- -- bool SubmitFrameMetadata(const H264SPS* sps, -- const H264PPS* pps, -- const H264DPB& dpb, -- const H264Picture::Vector& ref_pic_listp0, -- const H264Picture::Vector& ref_pic_listb0, -- const H264Picture::Vector& ref_pic_listb1, -- const scoped_refptr& pic) override; -- -- bool SubmitSlice(const H264PPS* pps, -- const H264SliceHeader* slice_hdr, -- const H264Picture::Vector& ref_pic_list0, -- const H264Picture::Vector& ref_pic_list1, -- const scoped_refptr& pic, -- const uint8_t* data, -- size_t size) override; -- -- bool SubmitDecode(const scoped_refptr& pic) override; -- bool OutputPicture(const scoped_refptr& pic) override; -- -- void Reset() override; -- -- private: -- scoped_refptr H264PictureToVaapiDecodeSurface( -- const scoped_refptr& pic); -- -- void FillVAPicture(VAPictureH264* va_pic, scoped_refptr pic); -- int FillVARefFramesFromDPB(const H264DPB& dpb, -- VAPictureH264* va_pics, -- int num_pics); -- -- VaapiWrapper* vaapi_wrapper_; -- VaapiVideoDecodeAccelerator* vaapi_dec_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiH264Accelerator); --}; -- --class VaapiVP8Picture : public VP8Picture { -- public: -- explicit VaapiVP8Picture( -- scoped_refptr surface) -- : dec_surface_(surface) {} -- -- VaapiVP8Picture* AsVaapiVP8Picture() override { return this; } -- scoped_refptr dec_surface() { -- return dec_surface_; -- } -- -- private: -- ~VaapiVP8Picture() override {} -- -- scoped_refptr dec_surface_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiVP8Picture); --}; -- --class VaapiVideoDecodeAccelerator::VaapiVP8Accelerator -- : public VP8Decoder::VP8Accelerator { -- public: -- VaapiVP8Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, -- VaapiWrapper* vaapi_wrapper); -- ~VaapiVP8Accelerator() override; -- -- // VP8Decoder::VP8Accelerator implementation. -- scoped_refptr CreateVP8Picture() override; -- -- bool SubmitDecode(const scoped_refptr& pic, -- const Vp8FrameHeader* frame_hdr, -- const scoped_refptr& last_frame, -- const scoped_refptr& golden_frame, -- const scoped_refptr& alt_frame) override; -- -- bool OutputPicture(const scoped_refptr& pic) override; -- -- private: -- scoped_refptr VP8PictureToVaapiDecodeSurface( -- const scoped_refptr& pic); -- -- VaapiWrapper* vaapi_wrapper_; -- VaapiVideoDecodeAccelerator* vaapi_dec_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiVP8Accelerator); --}; -- --class VaapiVP9Picture : public VP9Picture { -- public: -- explicit VaapiVP9Picture( -- scoped_refptr surface) -- : dec_surface_(surface) {} -- -- VaapiVP9Picture* AsVaapiVP9Picture() override { return this; } -- scoped_refptr dec_surface() { -- return dec_surface_; -- } -- -- private: -- ~VaapiVP9Picture() override {} -- -- scoped_refptr dec_surface_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiVP9Picture); --}; -- --class VaapiVideoDecodeAccelerator::VaapiVP9Accelerator -- : public VP9Decoder::VP9Accelerator { -- public: -- VaapiVP9Accelerator(VaapiVideoDecodeAccelerator* vaapi_dec, -- VaapiWrapper* vaapi_wrapper); -- ~VaapiVP9Accelerator() override; -- -- // VP9Decoder::VP9Accelerator implementation. -- scoped_refptr CreateVP9Picture() override; -- -- bool SubmitDecode(const scoped_refptr& pic, -- const Vp9SegmentationParams& seg, -- const Vp9LoopFilterParams& lf, -- const std::vector>& ref_pictures, -- const base::Closure& done_cb) override; -- -- bool OutputPicture(const scoped_refptr& pic) override; -- -- bool IsFrameContextRequired() const override { return false; } -- -- bool GetFrameContext(const scoped_refptr& pic, -- Vp9FrameContext* frame_ctx) override; -- -- private: -- scoped_refptr VP9PictureToVaapiDecodeSurface( -- const scoped_refptr& pic); -- -- VaapiWrapper* vaapi_wrapper_; -- VaapiVideoDecodeAccelerator* vaapi_dec_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiVP9Accelerator); --}; -- --class VaapiVideoDecodeAccelerator::InputBuffer { -- public: -- InputBuffer() = default; -- InputBuffer(uint32_t id, -- std::unique_ptr shm, -- base::OnceCallback release_cb) -- : id_(id), shm_(std::move(shm)), release_cb_(std::move(release_cb)) {} -- ~InputBuffer() { -- VLOGF(4) << "id = " << id_; -- if (release_cb_) -- std::move(release_cb_).Run(id_); -- } -- -- // Indicates this is a dummy buffer for flush request. -- bool IsFlushRequest() const { return shm_ == nullptr; } -- int32_t id() const { return id_; } -- SharedMemoryRegion* shm() const { return shm_.get(); } -- -- private: -- const int32_t id_ = -1; -- const std::unique_ptr shm_; -- base::OnceCallback release_cb_; -- -- DISALLOW_COPY_AND_ASSIGN(InputBuffer); --}; -- --void VaapiVideoDecodeAccelerator::NotifyError(Error error) { -- if (!task_runner_->BelongsToCurrentThread()) { -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- task_runner_->PostTask(FROM_HERE, -- base::Bind(&VaapiVideoDecodeAccelerator::NotifyError, -- weak_this_, error)); -- return; -- } -- -- // Post Cleanup() as a task so we don't recursively acquire lock_. -- task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::Cleanup, weak_this_)); -- -- VLOGF(1) << "Notifying of error " << error; -- if (client_) { -- client_->NotifyError(error); -- client_ptr_factory_.reset(); -- } --} -- --VaapiPicture* VaapiVideoDecodeAccelerator::PictureById( -- int32_t picture_buffer_id) { -- Pictures::iterator it = pictures_.find(picture_buffer_id); -- if (it == pictures_.end()) { -- VLOGF(4) << "Picture id " << picture_buffer_id << " does not exist"; -- return NULL; -- } -- -- return it->second.get(); --} -- --VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator( -- const MakeGLContextCurrentCallback& make_context_current_cb, -- const BindGLImageCallback& bind_image_cb) -- : state_(kUninitialized), -- input_ready_(&lock_), -- vaapi_picture_factory_(new VaapiPictureFactory()), -- surfaces_available_(&lock_), -- task_runner_(base::ThreadTaskRunnerHandle::Get()), -- decoder_thread_("VaapiDecoderThread"), -- num_frames_at_client_(0), -- finish_flush_pending_(false), -- awaiting_va_surfaces_recycle_(false), -- requested_num_pics_(0), -- output_format_(gfx::BufferFormat::BGRX_8888), -- profile_(VIDEO_CODEC_PROFILE_UNKNOWN), -- make_context_current_cb_(make_context_current_cb), -- bind_image_cb_(bind_image_cb), -- weak_this_factory_(this) { -- weak_this_ = weak_this_factory_.GetWeakPtr(); -- va_surface_release_cb_ = BindToCurrentLoop( -- base::Bind(&VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_)); --} -- --VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() { -- DCHECK(task_runner_->BelongsToCurrentThread()); --} -- --bool VaapiVideoDecodeAccelerator::Initialize(const Config& config, -- Client* client) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- if (config.is_encrypted()) { -- NOTREACHED() << "Encrypted streams are not supported for this VDA"; -- return false; -- } -- -- switch (config.output_mode) { -- case Config::OutputMode::ALLOCATE: -- output_format_ = vaapi_picture_factory_->GetBufferFormatForAllocateMode(); -- break; -- -- case Config::OutputMode::IMPORT: -- output_format_ = vaapi_picture_factory_->GetBufferFormatForImportMode(); -- break; -- -- default: -- NOTREACHED() << "Only ALLOCATE and IMPORT OutputModes are supported"; -- return false; -- } -- -- client_ptr_factory_.reset(new base::WeakPtrFactory(client)); -- client_ = client_ptr_factory_->GetWeakPtr(); -- -- VideoCodecProfile profile = config.profile; -- -- base::AutoLock auto_lock(lock_); -- DCHECK_EQ(state_, kUninitialized); -- VLOGF(2) << "Initializing VAVDA, profile: " << GetProfileName(profile); -- -- vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec( -- VaapiWrapper::kDecode, profile, base::Bind(&ReportToUMA, VAAPI_ERROR)); -- -- if (!vaapi_wrapper_.get()) { -- VLOGF(1) << "Failed initializing VAAPI for profile " -- << GetProfileName(profile); -- return false; -- } -- -- if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { -- h264_accelerator_.reset( -- new VaapiH264Accelerator(this, vaapi_wrapper_.get())); -- decoder_.reset(new H264Decoder(h264_accelerator_.get())); -- } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { -- vp8_accelerator_.reset(new VaapiVP8Accelerator(this, vaapi_wrapper_.get())); -- decoder_.reset(new VP8Decoder(vp8_accelerator_.get())); -- } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) { -- vp9_accelerator_.reset(new VaapiVP9Accelerator(this, vaapi_wrapper_.get())); -- decoder_.reset(new VP9Decoder(vp9_accelerator_.get())); -- } else { -- VLOGF(1) << "Unsupported profile " << GetProfileName(profile); -- return false; -- } -- profile_ = profile; -- -- CHECK(decoder_thread_.Start()); -- decoder_thread_task_runner_ = decoder_thread_.task_runner(); -- -- state_ = kIdle; -- output_mode_ = config.output_mode; -- return true; --} -- --void VaapiVideoDecodeAccelerator::OutputPicture( -- const scoped_refptr& va_surface, -- int32_t input_id, -- gfx::Rect visible_rect, -- VaapiPicture* picture) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- int32_t output_id = picture->picture_buffer_id(); -- -- VLOGF(4) << "Outputting VASurface " << va_surface->id() -- << " into pixmap bound to picture buffer id " << output_id; -- { -- TRACE_EVENT2("Video Decoder", "VAVDA::DownloadFromSurface", "input_id", -- input_id, "output_id", output_id); -- RETURN_AND_NOTIFY_ON_FAILURE(picture->DownloadFromSurface(va_surface), -- "Failed putting surface into pixmap", -- PLATFORM_FAILURE, ); -- } -- // Notify the client a picture is ready to be displayed. -- ++num_frames_at_client_; -- TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -- VLOGF(4) << "Notifying output picture id " << output_id << " for input " -- << input_id -- << " is ready. visible rect: " << visible_rect.ToString(); -- if (client_) { -- // TODO(hubbe): Use the correct color space. http://crbug.com/647725 -- client_->PictureReady(Picture(output_id, input_id, visible_rect, -- gfx::ColorSpace(), picture->AllowOverlay())); -- } --} -- --void VaapiVideoDecodeAccelerator::TryOutputSurface() { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- // Handle Destroy() arriving while pictures are queued for output. -- if (!client_) -- return; -- -- if (pending_output_cbs_.empty() || output_buffers_.empty()) -- return; -- -- OutputCB output_cb = pending_output_cbs_.front(); -- pending_output_cbs_.pop(); -- -- VaapiPicture* picture = PictureById(output_buffers_.front()); -- DCHECK(picture); -- output_buffers_.pop(); -- -- output_cb.Run(picture); -- -- if (finish_flush_pending_ && pending_output_cbs_.empty()) -- FinishFlush(); --} -- --void VaapiVideoDecodeAccelerator::QueueInputBuffer( -- const BitstreamBuffer& bitstream_buffer) { -- VLOGF(4) << "Queueing new input buffer id: " << bitstream_buffer.id() -- << " size: " << (int)bitstream_buffer.size(); -- DCHECK(task_runner_->BelongsToCurrentThread()); -- TRACE_EVENT1("Video Decoder", "QueueInputBuffer", "input_id", -- bitstream_buffer.id()); -- -- base::AutoLock auto_lock(lock_); -- if (bitstream_buffer.size() == 0) { -- DCHECK(!base::SharedMemory::IsHandleValid(bitstream_buffer.handle())); -- // Dummy buffer for flush. -- auto flush_buffer = base::MakeUnique(); -- DCHECK(flush_buffer->IsFlushRequest()); -- input_buffers_.push(std::move(flush_buffer)); -- } else { -- std::unique_ptr shm( -- new SharedMemoryRegion(bitstream_buffer, true)); -- RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(), "Failed to map input buffer", -- UNREADABLE_INPUT, ); -- -- auto input_buffer = base::MakeUnique( -- bitstream_buffer.id(), std::move(shm), -- BindToCurrentLoop( -- base::Bind(&Client::NotifyEndOfBitstreamBuffer, client_))); -- input_buffers_.push(std::move(input_buffer)); -- -- TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); -- } -- -- input_ready_.Signal(); -- -- switch (state_) { -- case kIdle: -- state_ = kDecoding; -- decoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -- base::Unretained(this))); -- break; -- -- case kDecoding: -- // Decoder already running. -- break; -- -- case kResetting: -- // When resetting, allow accumulating bitstream buffers, so that -- // the client can queue after-seek-buffers while we are finishing with -- // the before-seek one. -- break; -- -- default: -- VLOGF(1) << "Decode/Flush request from client in invalid state: " -- << state_; -- NotifyError(PLATFORM_FAILURE); -- break; -- } --} -- --bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() { -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- lock_.AssertAcquired(); -- -- if (curr_input_buffer_.get()) -- return true; -- -- // Will only wait if it is expected that in current state new buffers will -- // be queued from the client via Decode(). The state can change during wait. -- while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) { -- input_ready_.Wait(); -- } -- -- // We could have got woken up in a different state or never got to sleep -- // due to current state. -- if (state_ != kDecoding && state_ != kIdle) -- return false; -- -- DCHECK(!input_buffers_.empty()); -- curr_input_buffer_ = std::move(input_buffers_.front()); -- input_buffers_.pop(); -- -- if (curr_input_buffer_->IsFlushRequest()) { -- VLOGF(4) << "New flush buffer"; -- return true; -- } -- -- VLOGF(4) << "New current input buffer, id: " << curr_input_buffer_->id() -- << " size: " << curr_input_buffer_->shm()->size() << "B"; -- decoder_->SetStream( -- static_cast(curr_input_buffer_->shm()->memory()), -- curr_input_buffer_->shm()->size()); -- -- return true; --} -- --void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() { -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- lock_.AssertAcquired(); -- DCHECK(curr_input_buffer_.get()); -- curr_input_buffer_.reset(); -- -- TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); --} -- --// TODO(posciak): refactor the whole class to remove sleeping in wait for --// surfaces, and reschedule DecodeTask instead. --bool VaapiVideoDecodeAccelerator::WaitForSurfaces_Locked() { -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- lock_.AssertAcquired(); -- -- while (available_va_surfaces_.empty() && -- (state_ == kDecoding || state_ == kIdle)) { -- surfaces_available_.Wait(); -- } -- -- return state_ == kDecoding || state_ == kIdle; --} -- --void VaapiVideoDecodeAccelerator::DecodeTask() { -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- base::AutoLock auto_lock(lock_); -- -- if (state_ != kDecoding) -- return; -- -- // Main decode task. -- VLOGF(4) << "Decode task"; -- -- // Try to decode what stream data is (still) in the decoder until we run out -- // of it. -- while (GetInputBuffer_Locked()) { -- DCHECK(curr_input_buffer_.get()); -- -- if (curr_input_buffer_->IsFlushRequest()) { -- FlushTask(); -- break; -- } -- -- AcceleratedVideoDecoder::DecodeResult res; -- { -- // We are OK releasing the lock here, as decoder never calls our methods -- // directly and we will reacquire the lock before looking at state again. -- // This is the main decode function of the decoder and while keeping -- // the lock for its duration would be fine, it would defeat the purpose -- // of having a separate decoder thread. -- base::AutoUnlock auto_unlock(lock_); -- TRACE_EVENT0("Video Decoder", "VAVDA::Decode"); -- res = decoder_->Decode(); -- } -- -- switch (res) { -- case AcceleratedVideoDecoder::kAllocateNewSurfaces: -- VLOGF(2) << "Decoder requesting a new set of surfaces"; -- task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, -- weak_this_, decoder_->GetRequiredNumOfPictures(), -- decoder_->GetPicSize())); -- // We'll get rescheduled once ProvidePictureBuffers() finishes. -- return; -- -- case AcceleratedVideoDecoder::kRanOutOfStreamData: -- ReturnCurrInputBuffer_Locked(); -- break; -- -- case AcceleratedVideoDecoder::kRanOutOfSurfaces: -- // No more output buffers in the decoder, try getting more or go to -- // sleep waiting for them. -- if (!WaitForSurfaces_Locked()) -- return; -- -- break; -- -- case AcceleratedVideoDecoder::kNeedContextUpdate: -- // This should not happen as we return false from -- // IsFrameContextRequired(). -- NOTREACHED() << "Context updates not supported"; -- return; -- -- case AcceleratedVideoDecoder::kDecodeError: -- RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream", -- PLATFORM_FAILURE, ); -- return; -- } -- } --} -- --void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics, -- gfx::Size size) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- DCHECK(!awaiting_va_surfaces_recycle_); -- -- // At this point decoder has stopped running and has already posted onto our -- // loop any remaining output request callbacks, which executed before we got -- // here. Some of them might have been pended though, because we might not -- // have had enough TFPictures to output surfaces to. Initiate a wait cycle, -- // which will wait for client to return enough PictureBuffers to us, so that -- // we can finish all pending output callbacks, releasing associated surfaces. -- VLOGF(2) << "Initiating surface set change"; -- awaiting_va_surfaces_recycle_ = true; -- -- requested_num_pics_ = num_pics; -- requested_pic_size_ = size; -- -- TryFinishSurfaceSetChange(); --} -- --void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- if (!awaiting_va_surfaces_recycle_) -- return; -- -- if (!pending_output_cbs_.empty() || -- pictures_.size() != available_va_surfaces_.size()) { -- // Either: -- // 1. Not all pending pending output callbacks have been executed yet. -- // Wait for the client to return enough pictures and retry later. -- // 2. The above happened and all surface release callbacks have been posted -- // as the result, but not all have executed yet. Post ourselves after them -- // to let them release surfaces. -- DVLOGF(2) << "Awaiting pending output/surface release callbacks to finish"; -- task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, -- weak_this_)); -- return; -- } -- -- // All surfaces released, destroy them and dismiss all PictureBuffers. -- awaiting_va_surfaces_recycle_ = false; -- available_va_surfaces_.clear(); -- vaapi_wrapper_->DestroySurfaces(); -- -- for (Pictures::iterator iter = pictures_.begin(); iter != pictures_.end(); -- ++iter) { -- VLOGF(2) << "Dismissing picture id: " << iter->first; -- if (client_) -- client_->DismissPictureBuffer(iter->first); -- } -- pictures_.clear(); -- -- // And ask for a new set as requested. -- VLOGF(2) << "Requesting " << requested_num_pics_ -- << " pictures of size: " << requested_pic_size_.ToString(); -- -- VideoPixelFormat format = GfxBufferFormatToVideoPixelFormat(output_format_); -- task_runner_->PostTask( -- FROM_HERE, base::Bind(&Client::ProvidePictureBuffers, client_, -- requested_num_pics_, format, 1, requested_pic_size_, -- vaapi_picture_factory_->GetGLTextureTarget())); --} -- --void VaapiVideoDecodeAccelerator::Decode( -- const BitstreamBuffer& bitstream_buffer) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id", -- bitstream_buffer.id()); -- -- if (bitstream_buffer.id() < 0) { -- if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) -- base::SharedMemory::CloseHandle(bitstream_buffer.handle()); -- VLOGF(1) << "Invalid bitstream_buffer, id: " << bitstream_buffer.id(); -- NotifyError(INVALID_ARGUMENT); -- return; -- } -- -- // Skip empty buffers. VaapiVDA uses empty buffer as dummy buffer for flush -- // internally. -- if (bitstream_buffer.size() == 0) { -- if (base::SharedMemory::IsHandleValid(bitstream_buffer.handle())) -- base::SharedMemory::CloseHandle(bitstream_buffer.handle()); -- if (client_) -- client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id()); -- return; -- } -- -- QueueInputBuffer(bitstream_buffer); --} -- --void VaapiVideoDecodeAccelerator::RecycleVASurfaceID( -- VASurfaceID va_surface_id) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- base::AutoLock auto_lock(lock_); -- -- available_va_surfaces_.push_back(va_surface_id); -- surfaces_available_.Signal(); --} -- --void VaapiVideoDecodeAccelerator::AssignPictureBuffers( -- const std::vector& buffers) { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- base::AutoLock auto_lock(lock_); -- DCHECK(pictures_.empty()); -- -- while (!output_buffers_.empty()) -- output_buffers_.pop(); -- -- RETURN_AND_NOTIFY_ON_FAILURE( -- buffers.size() >= requested_num_pics_, -- "Got an invalid number of picture buffers. (Got " << buffers.size() -- << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, ); -- DCHECK(requested_pic_size_ == buffers[0].size()); -- -- const unsigned int va_format = GetVaFormatForVideoCodecProfile(profile_); -- std::vector va_surface_ids; -- RETURN_AND_NOTIFY_ON_FAILURE( -- vaapi_wrapper_->CreateSurfaces(va_format, requested_pic_size_, -- buffers.size(), &va_surface_ids), -- "Failed creating VA Surfaces", PLATFORM_FAILURE, ); -- DCHECK_EQ(va_surface_ids.size(), buffers.size()); -- -- for (size_t i = 0; i < buffers.size(); ++i) { -- uint32_t client_id = !buffers[i].client_texture_ids().empty() -- ? buffers[i].client_texture_ids()[0] -- : 0; -- uint32_t service_id = !buffers[i].service_texture_ids().empty() -- ? buffers[i].service_texture_ids()[0] -- : 0; -- -- std::unique_ptr picture(vaapi_picture_factory_->Create( -- vaapi_wrapper_, make_context_current_cb_, bind_image_cb_, -- buffers[i].id(), requested_pic_size_, service_id, client_id)); -- RETURN_AND_NOTIFY_ON_FAILURE( -- picture.get(), "Failed creating a VaapiPicture", PLATFORM_FAILURE, ); -- -- if (output_mode_ == Config::OutputMode::ALLOCATE) { -- RETURN_AND_NOTIFY_ON_FAILURE( -- picture->Allocate(output_format_), -- "Failed to allocate memory for a VaapiPicture", PLATFORM_FAILURE, ); -- output_buffers_.push(buffers[i].id()); -- } -- bool inserted = -- pictures_.insert(std::make_pair(buffers[i].id(), std::move(picture))) -- .second; -- DCHECK(inserted); -- -- available_va_surfaces_.push_back(va_surface_ids[i]); -- surfaces_available_.Signal(); -- } -- -- // Resume DecodeTask if it is still in decoding state. -- if (state_ == kDecoding) { -- decoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -- base::Unretained(this))); -- } --} -- --#if defined(USE_OZONE) --static void CloseGpuMemoryBufferHandle( -- const gfx::GpuMemoryBufferHandle& handle) { -- for (const auto& fd : handle.native_pixmap_handle.fds) { -- // Close the fd by wrapping it in a ScopedFD and letting -- // it fall out of scope. -- base::ScopedFD scoped_fd(fd.fd); -- } --} -- --void VaapiVideoDecodeAccelerator::ImportBufferForPicture( -- int32_t picture_buffer_id, -- const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) { -- VLOGF(2) << "Importing picture id: " << picture_buffer_id; -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- if (output_mode_ != Config::OutputMode::IMPORT) { -- CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); -- VLOGF(1) << "Cannot import in non-import mode"; -- NotifyError(INVALID_ARGUMENT); -- return; -- } -- -- VaapiPicture* picture = PictureById(picture_buffer_id); -- if (!picture) { -- CloseGpuMemoryBufferHandle(gpu_memory_buffer_handle); -- -- // It's possible that we've already posted a DismissPictureBuffer for this -- // picture, but it has not yet executed when this ImportBufferForPicture -- // was posted to us by the client. In that case just ignore this (we've -- // already dismissed it and accounted for that). -- VLOGF(3) << "got picture id=" << picture_buffer_id -- << " not in use (anymore?)."; -- return; -- } -- -- if (!picture->ImportGpuMemoryBufferHandle(output_format_, -- gpu_memory_buffer_handle)) { -- // ImportGpuMemoryBufferHandle will close the handles even on failure, so -- // we don't need to do this ourselves. -- VLOGF(1) << "Failed to import GpuMemoryBufferHandle"; -- NotifyError(PLATFORM_FAILURE); -- return; -- } -- -- ReusePictureBuffer(picture_buffer_id); --} --#endif -- --void VaapiVideoDecodeAccelerator::ReusePictureBuffer( -- int32_t picture_buffer_id) { -- VLOGF(4) << "picture id=" << picture_buffer_id; -- DCHECK(task_runner_->BelongsToCurrentThread()); -- TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id", -- picture_buffer_id); -- -- if (!PictureById(picture_buffer_id)) { -- // It's possible that we've already posted a DismissPictureBuffer for this -- // picture, but it has not yet executed when this ReusePictureBuffer -- // was posted to us by the client. In that case just ignore this (we've -- // already dismissed it and accounted for that). -- VLOGF(3) << "got picture id=" << picture_buffer_id -- << " not in use (anymore?)."; -- return; -- } -- -- --num_frames_at_client_; -- TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); -- -- output_buffers_.push(picture_buffer_id); -- TryOutputSurface(); --} -- --void VaapiVideoDecodeAccelerator::FlushTask() { -- VLOGF(2); -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- DCHECK(curr_input_buffer_.get() && curr_input_buffer_->IsFlushRequest()); -- -- curr_input_buffer_.reset(); -- -- // First flush all the pictures that haven't been outputted, notifying the -- // client to output them. -- bool res = decoder_->Flush(); -- RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.", -- PLATFORM_FAILURE, ); -- -- // Put the decoder in idle state, ready to resume. -- decoder_->Reset(); -- -- task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiVideoDecodeAccelerator::FinishFlush, weak_this_)); --} -- --void VaapiVideoDecodeAccelerator::Flush() { -- VLOGF(2) << "Got flush request"; -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- // Queue a dummy buffer, which means flush. -- QueueInputBuffer(media::BitstreamBuffer()); --} -- --void VaapiVideoDecodeAccelerator::FinishFlush() { -- VLOGF(2); -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- finish_flush_pending_ = false; -- -- base::AutoLock auto_lock(lock_); -- if (state_ != kDecoding) { -- DCHECK(state_ == kDestroying || state_ == kResetting) << state_; -- return; -- } -- -- // Still waiting for textures from client to finish outputting all pending -- // frames. Try again later. -- if (!pending_output_cbs_.empty()) { -- finish_flush_pending_ = true; -- return; -- } -- -- // Resume decoding if necessary. -- if (input_buffers_.empty()) { -- state_ = kIdle; -- } else { -- decoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -- base::Unretained(this))); -- } -- -- task_runner_->PostTask(FROM_HERE, -- base::Bind(&Client::NotifyFlushDone, client_)); --} -- --void VaapiVideoDecodeAccelerator::ResetTask() { -- VLOGF(2); -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- -- // All the decoding tasks from before the reset request from client are done -- // by now, as this task was scheduled after them and client is expected not -- // to call Decode() after Reset() and before NotifyResetDone. -- decoder_->Reset(); -- -- base::AutoLock auto_lock(lock_); -- -- // Return current input buffer, if present. -- if (curr_input_buffer_.get()) -- ReturnCurrInputBuffer_Locked(); -- -- // And let client know that we are done with reset. -- task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); --} -- --void VaapiVideoDecodeAccelerator::Reset() { -- VLOGF(2) << "Got reset request"; -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- // This will make any new decode tasks exit early. -- base::AutoLock auto_lock(lock_); -- state_ = kResetting; -- finish_flush_pending_ = false; -- -- // Drop all remaining input buffers, if present. -- while (!input_buffers_.empty()) -- input_buffers_.pop(); -- TRACE_COUNTER1("Video Decoder", "Input buffers", input_buffers_.size()); -- -- decoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, -- base::Unretained(this))); -- -- input_ready_.Signal(); -- surfaces_available_.Signal(); --} -- --void VaapiVideoDecodeAccelerator::FinishReset() { -- VLOGF(2); -- DCHECK(task_runner_->BelongsToCurrentThread()); -- base::AutoLock auto_lock(lock_); -- -- if (state_ != kResetting) { -- DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_; -- return; // We could've gotten destroyed already. -- } -- -- // Drop pending outputs. -- while (!pending_output_cbs_.empty()) -- pending_output_cbs_.pop(); -- -- if (awaiting_va_surfaces_recycle_) { -- // Decoder requested a new surface set while we were waiting for it to -- // finish the last DecodeTask, running at the time of Reset(). -- // Let the surface set change finish first before resetting. -- task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiVideoDecodeAccelerator::FinishReset, weak_this_)); -- return; -- } -- -- state_ = kIdle; -- -- task_runner_->PostTask(FROM_HERE, -- base::Bind(&Client::NotifyResetDone, client_)); -- -- // The client might have given us new buffers via Decode() while we were -- // resetting and might be waiting for our move, and not call Decode() anymore -- // until we return something. Post a DecodeTask() so that we won't -- // sleep forever waiting for Decode() in that case. Having two of them -- // in the pipe is harmless, the additional one will return as soon as it sees -- // that we are back in kDecoding state. -- if (!input_buffers_.empty()) { -- state_ = kDecoding; -- decoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, -- base::Unretained(this))); -- } --} -- --void VaapiVideoDecodeAccelerator::Cleanup() { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- -- base::AutoLock auto_lock(lock_); -- if (state_ == kUninitialized || state_ == kDestroying) -- return; -- -- VLOGF(2) << "Destroying VAVDA"; -- state_ = kDestroying; -- -- client_ptr_factory_.reset(); -- weak_this_factory_.InvalidateWeakPtrs(); -- -- // Signal all potential waiters on the decoder_thread_, let them early-exit, -- // as we've just moved to the kDestroying state, and wait for all tasks -- // to finish. -- input_ready_.Signal(); -- surfaces_available_.Signal(); -- { -- base::AutoUnlock auto_unlock(lock_); -- decoder_thread_.Stop(); -- } -- -- state_ = kUninitialized; --} -- --void VaapiVideoDecodeAccelerator::Destroy() { -- DCHECK(task_runner_->BelongsToCurrentThread()); -- Cleanup(); -- delete this; --} -- --bool VaapiVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( -- const base::WeakPtr& decode_client, -- const scoped_refptr& decode_task_runner) { -- return false; --} -- --bool VaapiVideoDecodeAccelerator::DecodeSurface( -- const scoped_refptr& dec_surface) { -- const bool result = vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( -- dec_surface->va_surface()->id()); -- if (!result) -- VLOGF(1) << "Failed decoding picture"; -- return result; --} -- --void VaapiVideoDecodeAccelerator::SurfaceReady( -- const scoped_refptr& dec_surface) { -- if (!task_runner_->BelongsToCurrentThread()) { -- task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoDecodeAccelerator::SurfaceReady, -- weak_this_, dec_surface)); -- return; -- } -- -- DCHECK(!awaiting_va_surfaces_recycle_); -- -- { -- base::AutoLock auto_lock(lock_); -- // Drop any requests to output if we are resetting or being destroyed. -- if (state_ == kResetting || state_ == kDestroying) -- return; -- } -- -- pending_output_cbs_.push( -- base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture, weak_this_, -- dec_surface->va_surface(), dec_surface->bitstream_id(), -- dec_surface->visible_rect())); -- -- TryOutputSurface(); --} -- --scoped_refptr --VaapiVideoDecodeAccelerator::CreateSurface() { -- DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); -- base::AutoLock auto_lock(lock_); -- -- if (available_va_surfaces_.empty()) -- return nullptr; -- -- DCHECK(!awaiting_va_surfaces_recycle_); -- scoped_refptr va_surface(new VASurface( -- available_va_surfaces_.front(), requested_pic_size_, -- vaapi_wrapper_->va_surface_format(), va_surface_release_cb_)); -- available_va_surfaces_.pop_front(); -- -- return new VaapiDecodeSurface(curr_input_buffer_->id(), va_surface); --} -- --VaapiVideoDecodeAccelerator::VaapiH264Accelerator::VaapiH264Accelerator( -- VaapiVideoDecodeAccelerator* vaapi_dec, -- VaapiWrapper* vaapi_wrapper) -- : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { -- DCHECK(vaapi_wrapper_); -- DCHECK(vaapi_dec_); --} -- --VaapiVideoDecodeAccelerator::VaapiH264Accelerator::~VaapiH264Accelerator() {} -- --scoped_refptr --VaapiVideoDecodeAccelerator::VaapiH264Accelerator::CreateH264Picture() { -- scoped_refptr va_surface = vaapi_dec_->CreateSurface(); -- if (!va_surface) -- return nullptr; -- -- return new VaapiH264Picture(std::move(va_surface)); --} -- --// Fill |va_pic| with default/neutral values. --static void InitVAPicture(VAPictureH264* va_pic) { -- memset(va_pic, 0, sizeof(*va_pic)); -- va_pic->picture_id = VA_INVALID_ID; -- va_pic->flags = VA_PICTURE_H264_INVALID; --} -- --bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitFrameMetadata( -- const H264SPS* sps, -- const H264PPS* pps, -- const H264DPB& dpb, -- const H264Picture::Vector& ref_pic_listp0, -- const H264Picture::Vector& ref_pic_listb0, -- const H264Picture::Vector& ref_pic_listb1, -- const scoped_refptr& pic) { -- VAPictureParameterBufferH264 pic_param; -- memset(&pic_param, 0, sizeof(pic_param)); -- --#define FROM_SPS_TO_PP(a) pic_param.a = sps->a --#define FROM_SPS_TO_PP2(a, b) pic_param.b = sps->a -- FROM_SPS_TO_PP2(pic_width_in_mbs_minus1, picture_width_in_mbs_minus1); -- // This assumes non-interlaced video -- FROM_SPS_TO_PP2(pic_height_in_map_units_minus1, picture_height_in_mbs_minus1); -- FROM_SPS_TO_PP(bit_depth_luma_minus8); -- FROM_SPS_TO_PP(bit_depth_chroma_minus8); --#undef FROM_SPS_TO_PP --#undef FROM_SPS_TO_PP2 -- --#define FROM_SPS_TO_PP_SF(a) pic_param.seq_fields.bits.a = sps->a --#define FROM_SPS_TO_PP_SF2(a, b) pic_param.seq_fields.bits.b = sps->a -- FROM_SPS_TO_PP_SF(chroma_format_idc); -- FROM_SPS_TO_PP_SF2(separate_colour_plane_flag, -- residual_colour_transform_flag); -- FROM_SPS_TO_PP_SF(gaps_in_frame_num_value_allowed_flag); -- FROM_SPS_TO_PP_SF(frame_mbs_only_flag); -- FROM_SPS_TO_PP_SF(mb_adaptive_frame_field_flag); -- FROM_SPS_TO_PP_SF(direct_8x8_inference_flag); -- pic_param.seq_fields.bits.MinLumaBiPredSize8x8 = (sps->level_idc >= 31); -- FROM_SPS_TO_PP_SF(log2_max_frame_num_minus4); -- FROM_SPS_TO_PP_SF(pic_order_cnt_type); -- FROM_SPS_TO_PP_SF(log2_max_pic_order_cnt_lsb_minus4); -- FROM_SPS_TO_PP_SF(delta_pic_order_always_zero_flag); --#undef FROM_SPS_TO_PP_SF --#undef FROM_SPS_TO_PP_SF2 -- --#define FROM_PPS_TO_PP(a) pic_param.a = pps->a -- FROM_PPS_TO_PP(pic_init_qp_minus26); -- FROM_PPS_TO_PP(pic_init_qs_minus26); -- FROM_PPS_TO_PP(chroma_qp_index_offset); -- FROM_PPS_TO_PP(second_chroma_qp_index_offset); --#undef FROM_PPS_TO_PP -- --#define FROM_PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = pps->a --#define FROM_PPS_TO_PP_PF2(a, b) pic_param.pic_fields.bits.b = pps->a -- FROM_PPS_TO_PP_PF(entropy_coding_mode_flag); -- FROM_PPS_TO_PP_PF(weighted_pred_flag); -- FROM_PPS_TO_PP_PF(weighted_bipred_idc); -- FROM_PPS_TO_PP_PF(transform_8x8_mode_flag); -- -- pic_param.pic_fields.bits.field_pic_flag = 0; -- FROM_PPS_TO_PP_PF(constrained_intra_pred_flag); -- FROM_PPS_TO_PP_PF2(bottom_field_pic_order_in_frame_present_flag, -- pic_order_present_flag); -- FROM_PPS_TO_PP_PF(deblocking_filter_control_present_flag); -- FROM_PPS_TO_PP_PF(redundant_pic_cnt_present_flag); -- pic_param.pic_fields.bits.reference_pic_flag = pic->ref; --#undef FROM_PPS_TO_PP_PF --#undef FROM_PPS_TO_PP_PF2 -- -- pic_param.frame_num = pic->frame_num; -- -- InitVAPicture(&pic_param.CurrPic); -- FillVAPicture(&pic_param.CurrPic, pic); -- -- // Init reference pictures' array. -- for (int i = 0; i < 16; ++i) -- InitVAPicture(&pic_param.ReferenceFrames[i]); -- -- // And fill it with picture info from DPB. -- FillVARefFramesFromDPB(dpb, pic_param.ReferenceFrames, -- arraysize(pic_param.ReferenceFrames)); -- -- pic_param.num_ref_frames = sps->max_num_ref_frames; -- -- if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, -- sizeof(pic_param), &pic_param)) -- return false; -- -- VAIQMatrixBufferH264 iq_matrix_buf; -- memset(&iq_matrix_buf, 0, sizeof(iq_matrix_buf)); -- -- if (pps->pic_scaling_matrix_present_flag) { -- for (int i = 0; i < 6; ++i) { -- for (int j = 0; j < 16; ++j) -- iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = -- pps->scaling_list4x4[i][j]; -- } -- -- for (int i = 0; i < 2; ++i) { -- for (int j = 0; j < 64; ++j) -- iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = -- pps->scaling_list8x8[i][j]; -- } -- } else { -- for (int i = 0; i < 6; ++i) { -- for (int j = 0; j < 16; ++j) -- iq_matrix_buf.ScalingList4x4[i][kZigzagScan4x4[j]] = -- sps->scaling_list4x4[i][j]; -- } -- -- for (int i = 0; i < 2; ++i) { -- for (int j = 0; j < 64; ++j) -- iq_matrix_buf.ScalingList8x8[i][kZigzagScan8x8[j]] = -- sps->scaling_list8x8[i][j]; -- } -- } -- -- return vaapi_wrapper_->SubmitBuffer(VAIQMatrixBufferType, -- sizeof(iq_matrix_buf), &iq_matrix_buf); --} -- --bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitSlice( -- const H264PPS* pps, -- const H264SliceHeader* slice_hdr, -- const H264Picture::Vector& ref_pic_list0, -- const H264Picture::Vector& ref_pic_list1, -- const scoped_refptr& pic, -- const uint8_t* data, -- size_t size) { -- VASliceParameterBufferH264 slice_param; -- memset(&slice_param, 0, sizeof(slice_param)); -- -- slice_param.slice_data_size = slice_hdr->nalu_size; -- slice_param.slice_data_offset = 0; -- slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -- slice_param.slice_data_bit_offset = slice_hdr->header_bit_size; -- --#define SHDRToSP(a) slice_param.a = slice_hdr->a -- SHDRToSP(first_mb_in_slice); -- slice_param.slice_type = slice_hdr->slice_type % 5; -- SHDRToSP(direct_spatial_mv_pred_flag); -- -- // TODO posciak: make sure parser sets those even when override flags -- // in slice header is off. -- SHDRToSP(num_ref_idx_l0_active_minus1); -- SHDRToSP(num_ref_idx_l1_active_minus1); -- SHDRToSP(cabac_init_idc); -- SHDRToSP(slice_qp_delta); -- SHDRToSP(disable_deblocking_filter_idc); -- SHDRToSP(slice_alpha_c0_offset_div2); -- SHDRToSP(slice_beta_offset_div2); -- -- if (((slice_hdr->IsPSlice() || slice_hdr->IsSPSlice()) && -- pps->weighted_pred_flag) || -- (slice_hdr->IsBSlice() && pps->weighted_bipred_idc == 1)) { -- SHDRToSP(luma_log2_weight_denom); -- SHDRToSP(chroma_log2_weight_denom); -- -- SHDRToSP(luma_weight_l0_flag); -- SHDRToSP(luma_weight_l1_flag); -- -- SHDRToSP(chroma_weight_l0_flag); -- SHDRToSP(chroma_weight_l1_flag); -- -- for (int i = 0; i <= slice_param.num_ref_idx_l0_active_minus1; ++i) { -- slice_param.luma_weight_l0[i] = -- slice_hdr->pred_weight_table_l0.luma_weight[i]; -- slice_param.luma_offset_l0[i] = -- slice_hdr->pred_weight_table_l0.luma_offset[i]; -- -- for (int j = 0; j < 2; ++j) { -- slice_param.chroma_weight_l0[i][j] = -- slice_hdr->pred_weight_table_l0.chroma_weight[i][j]; -- slice_param.chroma_offset_l0[i][j] = -- slice_hdr->pred_weight_table_l0.chroma_offset[i][j]; -- } -- } -- -- if (slice_hdr->IsBSlice()) { -- for (int i = 0; i <= slice_param.num_ref_idx_l1_active_minus1; ++i) { -- slice_param.luma_weight_l1[i] = -- slice_hdr->pred_weight_table_l1.luma_weight[i]; -- slice_param.luma_offset_l1[i] = -- slice_hdr->pred_weight_table_l1.luma_offset[i]; -- -- for (int j = 0; j < 2; ++j) { -- slice_param.chroma_weight_l1[i][j] = -- slice_hdr->pred_weight_table_l1.chroma_weight[i][j]; -- slice_param.chroma_offset_l1[i][j] = -- slice_hdr->pred_weight_table_l1.chroma_offset[i][j]; -- } -- } -- } -- } -- -- static_assert( -- arraysize(slice_param.RefPicList0) == arraysize(slice_param.RefPicList1), -- "Invalid RefPicList sizes"); -- -- for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) { -- InitVAPicture(&slice_param.RefPicList0[i]); -- InitVAPicture(&slice_param.RefPicList1[i]); -- } -- -- for (size_t i = 0; -- i < ref_pic_list0.size() && i < arraysize(slice_param.RefPicList0); -- ++i) { -- if (ref_pic_list0[i]) -- FillVAPicture(&slice_param.RefPicList0[i], ref_pic_list0[i]); -- } -- for (size_t i = 0; -- i < ref_pic_list1.size() && i < arraysize(slice_param.RefPicList1); -- ++i) { -- if (ref_pic_list1[i]) -- FillVAPicture(&slice_param.RefPicList1[i], ref_pic_list1[i]); -- } -- -- if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, -- sizeof(slice_param), &slice_param)) -- return false; -- -- // Can't help it, blame libva... -- void* non_const_ptr = const_cast(data); -- return vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, size, -- non_const_ptr); --} -- --bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitDecode( -- const scoped_refptr& pic) { -- VLOGF(4) << "Decoding POC " << pic->pic_order_cnt; -- scoped_refptr dec_surface = -- H264PictureToVaapiDecodeSurface(pic); -- -- return vaapi_dec_->DecodeSurface(dec_surface); --} -- --bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::OutputPicture( -- const scoped_refptr& pic) { -- scoped_refptr dec_surface = -- H264PictureToVaapiDecodeSurface(pic); -- dec_surface->set_visible_rect(pic->visible_rect); -- vaapi_dec_->SurfaceReady(dec_surface); -- -- return true; --} -- --void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::Reset() { -- vaapi_wrapper_->DestroyPendingBuffers(); --} -- --scoped_refptr --VaapiVideoDecodeAccelerator::VaapiH264Accelerator:: -- H264PictureToVaapiDecodeSurface(const scoped_refptr& pic) { -- VaapiH264Picture* vaapi_pic = pic->AsVaapiH264Picture(); -- CHECK(vaapi_pic); -- return vaapi_pic->dec_surface(); --} -- --void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVAPicture( -- VAPictureH264* va_pic, -- scoped_refptr pic) { -- VASurfaceID va_surface_id = VA_INVALID_SURFACE; -- -- if (!pic->nonexisting) { -- scoped_refptr dec_surface = -- H264PictureToVaapiDecodeSurface(pic); -- va_surface_id = dec_surface->va_surface()->id(); -- } -- -- va_pic->picture_id = va_surface_id; -- va_pic->frame_idx = pic->frame_num; -- va_pic->flags = 0; -- -- switch (pic->field) { -- case H264Picture::FIELD_NONE: -- break; -- case H264Picture::FIELD_TOP: -- va_pic->flags |= VA_PICTURE_H264_TOP_FIELD; -- break; -- case H264Picture::FIELD_BOTTOM: -- va_pic->flags |= VA_PICTURE_H264_BOTTOM_FIELD; -- break; -- } -- -- if (pic->ref) { -- va_pic->flags |= pic->long_term ? VA_PICTURE_H264_LONG_TERM_REFERENCE -- : VA_PICTURE_H264_SHORT_TERM_REFERENCE; -- } -- -- va_pic->TopFieldOrderCnt = pic->top_field_order_cnt; -- va_pic->BottomFieldOrderCnt = pic->bottom_field_order_cnt; --} -- --int VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVARefFramesFromDPB( -- const H264DPB& dpb, -- VAPictureH264* va_pics, -- int num_pics) { -- H264Picture::Vector::const_reverse_iterator rit; -- int i; -- -- // Return reference frames in reverse order of insertion. -- // Libva does not document this, but other implementations (e.g. mplayer) -- // do it this way as well. -- for (rit = dpb.rbegin(), i = 0; rit != dpb.rend() && i < num_pics; ++rit) { -- if ((*rit)->ref) -- FillVAPicture(&va_pics[i++], *rit); -- } -- -- return i; --} -- --VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::VaapiVP8Accelerator( -- VaapiVideoDecodeAccelerator* vaapi_dec, -- VaapiWrapper* vaapi_wrapper) -- : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { -- DCHECK(vaapi_wrapper_); -- DCHECK(vaapi_dec_); --} -- --VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::~VaapiVP8Accelerator() {} -- --scoped_refptr --VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::CreateVP8Picture() { -- scoped_refptr va_surface = vaapi_dec_->CreateSurface(); -- if (!va_surface) -- return nullptr; -- -- return new VaapiVP8Picture(std::move(va_surface)); --} -- --#define ARRAY_MEMCPY_CHECKED(to, from) \ -- do { \ -- static_assert(sizeof(to) == sizeof(from), \ -- #from " and " #to " arrays must be of same size"); \ -- memcpy(to, from, sizeof(to)); \ -- } while (0) -- --bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::SubmitDecode( -- const scoped_refptr& pic, -- const Vp8FrameHeader* frame_hdr, -- const scoped_refptr& last_frame, -- const scoped_refptr& golden_frame, -- const scoped_refptr& alt_frame) { -- VAIQMatrixBufferVP8 iq_matrix_buf; -- memset(&iq_matrix_buf, 0, sizeof(VAIQMatrixBufferVP8)); -- -- const Vp8SegmentationHeader& sgmnt_hdr = frame_hdr->segmentation_hdr; -- const Vp8QuantizationHeader& quant_hdr = frame_hdr->quantization_hdr; -- static_assert(arraysize(iq_matrix_buf.quantization_index) == kMaxMBSegments, -- "incorrect quantization matrix size"); -- for (size_t i = 0; i < kMaxMBSegments; ++i) { -- int q = quant_hdr.y_ac_qi; -- -- if (sgmnt_hdr.segmentation_enabled) { -- if (sgmnt_hdr.segment_feature_mode == -- Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) -- q = sgmnt_hdr.quantizer_update_value[i]; -- else -- q += sgmnt_hdr.quantizer_update_value[i]; -- } -- --#define CLAMP_Q(q) std::min(std::max(q, 0), 127) -- static_assert(arraysize(iq_matrix_buf.quantization_index[i]) == 6, -- "incorrect quantization matrix size"); -- iq_matrix_buf.quantization_index[i][0] = CLAMP_Q(q); -- iq_matrix_buf.quantization_index[i][1] = CLAMP_Q(q + quant_hdr.y_dc_delta); -- iq_matrix_buf.quantization_index[i][2] = CLAMP_Q(q + quant_hdr.y2_dc_delta); -- iq_matrix_buf.quantization_index[i][3] = CLAMP_Q(q + quant_hdr.y2_ac_delta); -- iq_matrix_buf.quantization_index[i][4] = CLAMP_Q(q + quant_hdr.uv_dc_delta); -- iq_matrix_buf.quantization_index[i][5] = CLAMP_Q(q + quant_hdr.uv_ac_delta); --#undef CLAMP_Q -- } -- -- if (!vaapi_wrapper_->SubmitBuffer( -- VAIQMatrixBufferType, sizeof(VAIQMatrixBufferVP8), &iq_matrix_buf)) -- return false; -- -- VAProbabilityDataBufferVP8 prob_buf; -- memset(&prob_buf, 0, sizeof(VAProbabilityDataBufferVP8)); -- -- const Vp8EntropyHeader& entr_hdr = frame_hdr->entropy_hdr; -- ARRAY_MEMCPY_CHECKED(prob_buf.dct_coeff_probs, entr_hdr.coeff_probs); -- -- if (!vaapi_wrapper_->SubmitBuffer(VAProbabilityBufferType, -- sizeof(VAProbabilityDataBufferVP8), -- &prob_buf)) -- return false; -- -- VAPictureParameterBufferVP8 pic_param; -- memset(&pic_param, 0, sizeof(VAPictureParameterBufferVP8)); -- pic_param.frame_width = frame_hdr->width; -- pic_param.frame_height = frame_hdr->height; -- -- if (last_frame) { -- scoped_refptr last_frame_surface = -- VP8PictureToVaapiDecodeSurface(last_frame); -- pic_param.last_ref_frame = last_frame_surface->va_surface()->id(); -- } else { -- pic_param.last_ref_frame = VA_INVALID_SURFACE; -- } -- -- if (golden_frame) { -- scoped_refptr golden_frame_surface = -- VP8PictureToVaapiDecodeSurface(golden_frame); -- pic_param.golden_ref_frame = golden_frame_surface->va_surface()->id(); -- } else { -- pic_param.golden_ref_frame = VA_INVALID_SURFACE; -- } -- -- if (alt_frame) { -- scoped_refptr alt_frame_surface = -- VP8PictureToVaapiDecodeSurface(alt_frame); -- pic_param.alt_ref_frame = alt_frame_surface->va_surface()->id(); -- } else { -- pic_param.alt_ref_frame = VA_INVALID_SURFACE; -- } -- -- pic_param.out_of_loop_frame = VA_INVALID_SURFACE; -- -- const Vp8LoopFilterHeader& lf_hdr = frame_hdr->loopfilter_hdr; -- --#define FHDR_TO_PP_PF(a, b) pic_param.pic_fields.bits.a = (b) -- FHDR_TO_PP_PF(key_frame, frame_hdr->IsKeyframe() ? 0 : 1); -- FHDR_TO_PP_PF(version, frame_hdr->version); -- FHDR_TO_PP_PF(segmentation_enabled, sgmnt_hdr.segmentation_enabled); -- FHDR_TO_PP_PF(update_mb_segmentation_map, -- sgmnt_hdr.update_mb_segmentation_map); -- FHDR_TO_PP_PF(update_segment_feature_data, -- sgmnt_hdr.update_segment_feature_data); -- FHDR_TO_PP_PF(filter_type, lf_hdr.type); -- FHDR_TO_PP_PF(sharpness_level, lf_hdr.sharpness_level); -- FHDR_TO_PP_PF(loop_filter_adj_enable, lf_hdr.loop_filter_adj_enable); -- FHDR_TO_PP_PF(mode_ref_lf_delta_update, lf_hdr.mode_ref_lf_delta_update); -- FHDR_TO_PP_PF(sign_bias_golden, frame_hdr->sign_bias_golden); -- FHDR_TO_PP_PF(sign_bias_alternate, frame_hdr->sign_bias_alternate); -- FHDR_TO_PP_PF(mb_no_coeff_skip, frame_hdr->mb_no_skip_coeff); -- FHDR_TO_PP_PF(loop_filter_disable, lf_hdr.level == 0); --#undef FHDR_TO_PP_PF -- -- ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, sgmnt_hdr.segment_prob); -- -- static_assert(arraysize(sgmnt_hdr.lf_update_value) == -- arraysize(pic_param.loop_filter_level), -- "loop filter level arrays mismatch"); -- for (size_t i = 0; i < arraysize(sgmnt_hdr.lf_update_value); ++i) { -- int lf_level = lf_hdr.level; -- if (sgmnt_hdr.segmentation_enabled) { -- if (sgmnt_hdr.segment_feature_mode == -- Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE) -- lf_level = sgmnt_hdr.lf_update_value[i]; -- else -- lf_level += sgmnt_hdr.lf_update_value[i]; -- } -- -- // Clamp to [0..63] range. -- lf_level = std::min(std::max(lf_level, 0), 63); -- pic_param.loop_filter_level[i] = lf_level; -- } -- -- static_assert( -- arraysize(lf_hdr.ref_frame_delta) == -- arraysize(pic_param.loop_filter_deltas_ref_frame) && -- arraysize(lf_hdr.mb_mode_delta) == -- arraysize(pic_param.loop_filter_deltas_mode) && -- arraysize(lf_hdr.ref_frame_delta) == arraysize(lf_hdr.mb_mode_delta), -- "loop filter deltas arrays size mismatch"); -- for (size_t i = 0; i < arraysize(lf_hdr.ref_frame_delta); ++i) { -- pic_param.loop_filter_deltas_ref_frame[i] = lf_hdr.ref_frame_delta[i]; -- pic_param.loop_filter_deltas_mode[i] = lf_hdr.mb_mode_delta[i]; -- } -- --#define FHDR_TO_PP(a) pic_param.a = frame_hdr->a -- FHDR_TO_PP(prob_skip_false); -- FHDR_TO_PP(prob_intra); -- FHDR_TO_PP(prob_last); -- FHDR_TO_PP(prob_gf); --#undef FHDR_TO_PP -- -- ARRAY_MEMCPY_CHECKED(pic_param.y_mode_probs, entr_hdr.y_mode_probs); -- ARRAY_MEMCPY_CHECKED(pic_param.uv_mode_probs, entr_hdr.uv_mode_probs); -- ARRAY_MEMCPY_CHECKED(pic_param.mv_probs, entr_hdr.mv_probs); -- -- pic_param.bool_coder_ctx.range = frame_hdr->bool_dec_range; -- pic_param.bool_coder_ctx.value = frame_hdr->bool_dec_value; -- pic_param.bool_coder_ctx.count = frame_hdr->bool_dec_count; -- -- if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, -- sizeof(pic_param), &pic_param)) -- return false; -- -- VASliceParameterBufferVP8 slice_param; -- memset(&slice_param, 0, sizeof(slice_param)); -- slice_param.slice_data_size = frame_hdr->frame_size; -- slice_param.slice_data_offset = frame_hdr->first_part_offset; -- slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -- slice_param.macroblock_offset = frame_hdr->macroblock_bit_offset; -- // Number of DCT partitions plus control partition. -- slice_param.num_of_partitions = frame_hdr->num_of_dct_partitions + 1; -- -- // Per VAAPI, this size only includes the size of the macroblock data in -- // the first partition (in bytes), so we have to subtract the header size. -- slice_param.partition_size[0] = -- frame_hdr->first_part_size - ((frame_hdr->macroblock_bit_offset + 7) / 8); -- -- for (size_t i = 0; i < frame_hdr->num_of_dct_partitions; ++i) -- slice_param.partition_size[i + 1] = frame_hdr->dct_partition_sizes[i]; -- -- if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, -- sizeof(VASliceParameterBufferVP8), -- &slice_param)) -- return false; -- -- void* non_const_ptr = const_cast(frame_hdr->data); -- if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, -- frame_hdr->frame_size, non_const_ptr)) -- return false; -- -- scoped_refptr dec_surface = -- VP8PictureToVaapiDecodeSurface(pic); -- -- return vaapi_dec_->DecodeSurface(dec_surface); --} -- --bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::OutputPicture( -- const scoped_refptr& pic) { -- scoped_refptr dec_surface = -- VP8PictureToVaapiDecodeSurface(pic); -- dec_surface->set_visible_rect(pic->visible_rect); -- vaapi_dec_->SurfaceReady(dec_surface); -- return true; --} -- --scoped_refptr --VaapiVideoDecodeAccelerator::VaapiVP8Accelerator:: -- VP8PictureToVaapiDecodeSurface(const scoped_refptr& pic) { -- VaapiVP8Picture* vaapi_pic = pic->AsVaapiVP8Picture(); -- CHECK(vaapi_pic); -- return vaapi_pic->dec_surface(); --} -- --VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::VaapiVP9Accelerator( -- VaapiVideoDecodeAccelerator* vaapi_dec, -- VaapiWrapper* vaapi_wrapper) -- : vaapi_wrapper_(vaapi_wrapper), vaapi_dec_(vaapi_dec) { -- DCHECK(vaapi_wrapper_); -- DCHECK(vaapi_dec_); --} -- --VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::~VaapiVP9Accelerator() {} -- --scoped_refptr --VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::CreateVP9Picture() { -- scoped_refptr va_surface = vaapi_dec_->CreateSurface(); -- if (!va_surface) -- return nullptr; -- -- return new VaapiVP9Picture(std::move(va_surface)); --} -- --bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::SubmitDecode( -- const scoped_refptr& pic, -- const Vp9SegmentationParams& seg, -- const Vp9LoopFilterParams& lf, -- const std::vector>& ref_pictures, -- const base::Closure& done_cb) { -- // |done_cb| should be null as we return false from IsFrameContextRequired(). -- DCHECK(done_cb.is_null()); -- -- VADecPictureParameterBufferVP9 pic_param; -- memset(&pic_param, 0, sizeof(pic_param)); -- -- const Vp9FrameHeader* frame_hdr = pic->frame_hdr.get(); -- DCHECK(frame_hdr); -- -- pic_param.frame_width = base::checked_cast(frame_hdr->frame_width); -- pic_param.frame_height = -- base::checked_cast(frame_hdr->frame_height); -- -- CHECK_EQ(ref_pictures.size(), arraysize(pic_param.reference_frames)); -- for (size_t i = 0; i < arraysize(pic_param.reference_frames); ++i) { -- VASurfaceID va_surface_id; -- if (ref_pictures[i]) { -- scoped_refptr surface = -- VP9PictureToVaapiDecodeSurface(ref_pictures[i]); -- va_surface_id = surface->va_surface()->id(); -- } else { -- va_surface_id = VA_INVALID_SURFACE; -- } -- -- pic_param.reference_frames[i] = va_surface_id; -- } -- --#define FHDR_TO_PP_PF1(a) pic_param.pic_fields.bits.a = frame_hdr->a --#define FHDR_TO_PP_PF2(a, b) pic_param.pic_fields.bits.a = b -- FHDR_TO_PP_PF2(subsampling_x, frame_hdr->subsampling_x == 1); -- FHDR_TO_PP_PF2(subsampling_y, frame_hdr->subsampling_y == 1); -- FHDR_TO_PP_PF2(frame_type, frame_hdr->IsKeyframe() ? 0 : 1); -- FHDR_TO_PP_PF1(show_frame); -- FHDR_TO_PP_PF1(error_resilient_mode); -- FHDR_TO_PP_PF1(intra_only); -- FHDR_TO_PP_PF1(allow_high_precision_mv); -- FHDR_TO_PP_PF2(mcomp_filter_type, frame_hdr->interpolation_filter); -- FHDR_TO_PP_PF1(frame_parallel_decoding_mode); -- FHDR_TO_PP_PF1(reset_frame_context); -- FHDR_TO_PP_PF1(refresh_frame_context); -- FHDR_TO_PP_PF2(frame_context_idx, frame_hdr->frame_context_idx_to_save_probs); -- FHDR_TO_PP_PF2(segmentation_enabled, seg.enabled); -- FHDR_TO_PP_PF2(segmentation_temporal_update, seg.temporal_update); -- FHDR_TO_PP_PF2(segmentation_update_map, seg.update_map); -- FHDR_TO_PP_PF2(last_ref_frame, frame_hdr->ref_frame_idx[0]); -- FHDR_TO_PP_PF2(last_ref_frame_sign_bias, -- frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_LAST]); -- FHDR_TO_PP_PF2(golden_ref_frame, frame_hdr->ref_frame_idx[1]); -- FHDR_TO_PP_PF2(golden_ref_frame_sign_bias, -- frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_GOLDEN]); -- FHDR_TO_PP_PF2(alt_ref_frame, frame_hdr->ref_frame_idx[2]); -- FHDR_TO_PP_PF2(alt_ref_frame_sign_bias, -- frame_hdr->ref_frame_sign_bias[Vp9RefType::VP9_FRAME_ALTREF]); -- FHDR_TO_PP_PF2(lossless_flag, frame_hdr->quant_params.IsLossless()); --#undef FHDR_TO_PP_PF2 --#undef FHDR_TO_PP_PF1 -- -- pic_param.filter_level = lf.level; -- pic_param.sharpness_level = lf.sharpness; -- pic_param.log2_tile_rows = frame_hdr->tile_rows_log2; -- pic_param.log2_tile_columns = frame_hdr->tile_cols_log2; -- pic_param.frame_header_length_in_bytes = frame_hdr->uncompressed_header_size; -- pic_param.first_partition_size = frame_hdr->header_size_in_bytes; -- -- ARRAY_MEMCPY_CHECKED(pic_param.mb_segment_tree_probs, seg.tree_probs); -- ARRAY_MEMCPY_CHECKED(pic_param.segment_pred_probs, seg.pred_probs); -- -- pic_param.profile = frame_hdr->profile; -- pic_param.bit_depth = frame_hdr->bit_depth; -- DCHECK((pic_param.profile == 0 && pic_param.bit_depth == 8) || -- (pic_param.profile == 2 && pic_param.bit_depth == 10)); -- -- if (!vaapi_wrapper_->SubmitBuffer(VAPictureParameterBufferType, -- sizeof(pic_param), &pic_param)) -- return false; -- -- VASliceParameterBufferVP9 slice_param; -- memset(&slice_param, 0, sizeof(slice_param)); -- slice_param.slice_data_size = frame_hdr->frame_size; -- slice_param.slice_data_offset = 0; -- slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; -- -- static_assert(arraysize(Vp9SegmentationParams::feature_enabled) == -- arraysize(slice_param.seg_param), -- "seg_param array of incorrect size"); -- for (size_t i = 0; i < arraysize(slice_param.seg_param); ++i) { -- VASegmentParameterVP9& seg_param = slice_param.seg_param[i]; --#define SEG_TO_SP_SF(a, b) seg_param.segment_flags.fields.a = b -- SEG_TO_SP_SF( -- segment_reference_enabled, -- seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); -- SEG_TO_SP_SF(segment_reference, -- seg.FeatureData(i, Vp9SegmentationParams::SEG_LVL_REF_FRAME)); -- SEG_TO_SP_SF(segment_reference_skipped, -- seg.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_SKIP)); --#undef SEG_TO_SP_SF -- -- ARRAY_MEMCPY_CHECKED(seg_param.filter_level, lf.lvl[i]); -- -- seg_param.luma_dc_quant_scale = seg.y_dequant[i][0]; -- seg_param.luma_ac_quant_scale = seg.y_dequant[i][1]; -- seg_param.chroma_dc_quant_scale = seg.uv_dequant[i][0]; -- seg_param.chroma_ac_quant_scale = seg.uv_dequant[i][1]; -- } -- -- if (!vaapi_wrapper_->SubmitBuffer(VASliceParameterBufferType, -- sizeof(slice_param), &slice_param)) -- return false; -- -- void* non_const_ptr = const_cast(frame_hdr->data); -- if (!vaapi_wrapper_->SubmitBuffer(VASliceDataBufferType, -- frame_hdr->frame_size, non_const_ptr)) -- return false; -- -- scoped_refptr dec_surface = -- VP9PictureToVaapiDecodeSurface(pic); -- -- return vaapi_dec_->DecodeSurface(dec_surface); --} -- --bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::OutputPicture( -- const scoped_refptr& pic) { -- scoped_refptr dec_surface = -- VP9PictureToVaapiDecodeSurface(pic); -- dec_surface->set_visible_rect(pic->visible_rect); -- vaapi_dec_->SurfaceReady(dec_surface); -- return true; --} -- --bool VaapiVideoDecodeAccelerator::VaapiVP9Accelerator::GetFrameContext( -- const scoped_refptr& pic, -- Vp9FrameContext* frame_ctx) { -- NOTIMPLEMENTED() << "Frame context update not supported"; -- return false; --} -- --scoped_refptr --VaapiVideoDecodeAccelerator::VaapiVP9Accelerator:: -- VP9PictureToVaapiDecodeSurface(const scoped_refptr& pic) { -- VaapiVP9Picture* vaapi_pic = pic->AsVaapiVP9Picture(); -- CHECK(vaapi_pic); -- return vaapi_pic->dec_surface(); --} -- --// static --VideoDecodeAccelerator::SupportedProfiles --VaapiVideoDecodeAccelerator::GetSupportedProfiles() { -- return VaapiWrapper::GetSupportedDecodeProfiles(); --} -- --} // namespace media ---- a/media/gpu/vaapi_video_decode_accelerator.h -+++ /dev/null -@@ -1,325 +0,0 @@ --// Copyright (c) 2012 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. --// --// This file contains an implementation of VideoDecoderAccelerator --// that utilizes hardware video decoder present on Intel CPUs. -- --#ifndef MEDIA_GPU_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ --#define MEDIA_GPU_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ -- --#include --#include -- --#include --#include --#include --#include --#include -- --#include "base/containers/queue.h" --#include "base/logging.h" --#include "base/macros.h" --#include "base/memory/linked_ptr.h" --#include "base/memory/ref_counted.h" --#include "base/memory/weak_ptr.h" --#include "base/single_thread_task_runner.h" --#include "base/synchronization/condition_variable.h" --#include "base/synchronization/lock.h" --#include "base/threading/thread.h" --#include "media/base/bitstream_buffer.h" --#include "media/gpu/gpu_video_decode_accelerator_helpers.h" --#include "media/gpu/media_gpu_export.h" --#include "media/gpu/shared_memory_region.h" --#include "media/gpu/vaapi/vaapi_picture_factory.h" --#include "media/gpu/vaapi_wrapper.h" --#include "media/video/picture.h" --#include "media/video/video_decode_accelerator.h" -- --namespace gl { --class GLImage; --} -- --namespace media { -- --class AcceleratedVideoDecoder; --class VaapiPicture; -- --// Class to provide video decode acceleration for Intel systems with hardware --// support for it, and on which libva is available. --// Decoding tasks are performed in a separate decoding thread. --// --// Threading/life-cycle: this object is created & destroyed on the GPU --// ChildThread. A few methods on it are called on the decoder thread which is --// stopped during |this->Destroy()|, so any tasks posted to the decoder thread --// can assume |*this| is still alive. See |weak_this_| below for more details. --class MEDIA_GPU_EXPORT VaapiVideoDecodeAccelerator -- : public VideoDecodeAccelerator { -- public: -- // Wrapper of a VASurface with id and visible area. -- class VaapiDecodeSurface; -- -- VaapiVideoDecodeAccelerator( -- const MakeGLContextCurrentCallback& make_context_current_cb, -- const BindGLImageCallback& bind_image_cb); -- -- ~VaapiVideoDecodeAccelerator() override; -- -- // VideoDecodeAccelerator implementation. -- bool Initialize(const Config& config, Client* client) override; -- void Decode(const BitstreamBuffer& bitstream_buffer) override; -- void AssignPictureBuffers(const std::vector& buffers) override; --#if defined(USE_OZONE) -- void ImportBufferForPicture( -- int32_t picture_buffer_id, -- const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override; --#endif -- void ReusePictureBuffer(int32_t picture_buffer_id) override; -- void Flush() override; -- void Reset() override; -- void Destroy() override; -- bool TryToSetupDecodeOnSeparateThread( -- const base::WeakPtr& decode_client, -- const scoped_refptr& decode_task_runner) -- override; -- -- static VideoDecodeAccelerator::SupportedProfiles GetSupportedProfiles(); -- -- private: -- friend class VaapiVideoDecodeAcceleratorTest; -- class VaapiH264Accelerator; -- class VaapiVP8Accelerator; -- class VaapiVP9Accelerator; -- -- // An input buffer with id provided by the client and awaiting consumption. -- class InputBuffer; -- -- // Notify the client that an error has occurred and decoding cannot continue. -- void NotifyError(Error error); -- -- // Queue a input buffer for decode. -- void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer); -- -- // Get a new input buffer from the queue and set it up in decoder. This will -- // sleep if no input buffers are available. Return true if a new buffer has -- // been set up, false if an early exit has been requested (due to initiated -- // reset/flush/destroy). -- bool GetInputBuffer_Locked(); -- -- // Signal the client that the current buffer has been read and can be -- // returned. Will also release the mapping. -- void ReturnCurrInputBuffer_Locked(); -- -- // Wait for more surfaces to become available. Return true once they do or -- // false if an early exit has been requested (due to an initiated -- // reset/flush/destroy). -- bool WaitForSurfaces_Locked(); -- -- // Continue decoding given input buffers and sleep waiting for input/output -- // as needed. Will exit if a new set of surfaces or reset/flush/destroy -- // is requested. -- void DecodeTask(); -- -- // Scheduled after receiving a flush request and executed after the current -- // decoding task finishes decoding pending inputs. Makes the decoder return -- // all remaining output pictures and puts it in an idle state, ready -- // to resume if needed and schedules a FinishFlush. -- void FlushTask(); -- -- // Scheduled by the FlushTask after decoder is flushed to put VAVDA into idle -- // state and notify the client that flushing has been finished. -- void FinishFlush(); -- -- // Scheduled after receiving a reset request and executed after the current -- // decoding task finishes decoding the current frame. Puts the decoder into -- // an idle state, ready to resume if needed, discarding decoded but not yet -- // outputted pictures (decoder keeps ownership of their associated picture -- // buffers). Schedules a FinishReset afterwards. -- void ResetTask(); -- -- // Scheduled by ResetTask after it's done putting VAVDA into an idle state. -- // Drops remaining input buffers and notifies the client that reset has been -- // finished. -- void FinishReset(); -- -- // Helper for Destroy(), doing all the actual work except for deleting self. -- void Cleanup(); -- -- // Get a usable framebuffer configuration for use in binding textures -- // or return false on failure. -- bool InitializeFBConfig(); -- -- // Callback to be executed once we have a |va_surface| to be output and -- // an available |picture| to use for output. -- // Puts contents of |va_surface| into given |picture|, releases the surface -- // and passes the resulting picture to client to output the given -- // |visible_rect| part of it. -- void OutputPicture(const scoped_refptr& va_surface, -- int32_t input_id, -- gfx::Rect visible_rect, -- VaapiPicture* picture); -- -- // Try to OutputPicture() if we have both a ready surface and picture. -- void TryOutputSurface(); -- -- // Called when a VASurface is no longer in use by the decoder or is not being -- // synced/waiting to be synced to a picture. Returns it to available surfaces -- // pool. -- void RecycleVASurfaceID(VASurfaceID va_surface_id); -- -- // Initiate wait cycle for surfaces to be released before we release them -- // and allocate new ones, as requested by the decoder. -- void InitiateSurfaceSetChange(size_t num_pics, gfx::Size size); -- -- // Check if the surfaces have been released or post ourselves for later. -- void TryFinishSurfaceSetChange(); -- -- // -- // Below methods are used by accelerator implementations. -- // -- // Decode of |dec_surface| is ready to be submitted and all codec-specific -- // settings are set in hardware. -- bool DecodeSurface(const scoped_refptr& dec_surface); -- -- // |dec_surface| is ready to be outputted once decode is finished. -- // This can be called before decode is actually done in hardware, and this -- // method is responsible for maintaining the ordering, i.e. the surfaces have -- // to be outputted in the same order as SurfaceReady is called. -- // On Intel, we don't have to explicitly maintain the ordering however, as the -- // driver will maintain ordering, as well as dependencies, and will process -- // each submitted command in order, and run each command only if its -- // dependencies are ready. -- void SurfaceReady(const scoped_refptr& dec_surface); -- -- // Return a new VaapiDecodeSurface for decoding into, or nullptr if not -- // available. -- scoped_refptr CreateSurface(); -- -- // VAVDA state. -- enum State { -- // Initialize() not called yet or failed. -- kUninitialized, -- // DecodeTask running. -- kDecoding, -- // Resetting, waiting for decoder to finish current task and cleanup. -- kResetting, -- // Idle, decoder in state ready to start/resume decoding. -- kIdle, -- // Destroying, waiting for the decoder to finish current task. -- kDestroying, -- }; -- -- // Protects input buffer and surface queues and state_. -- base::Lock lock_; -- State state_; -- Config::OutputMode output_mode_; -- -- // Queue of available InputBuffers (picture_buffer_ids). -- base::queue> input_buffers_; -- // Signalled when input buffers are queued onto |input_buffers_| queue. -- base::ConditionVariable input_ready_; -- -- // Current input buffer at decoder. -- std::unique_ptr curr_input_buffer_; -- -- // Queue for incoming output buffers (texture ids). -- using OutputBuffers = base::queue; -- OutputBuffers output_buffers_; -- -- std::unique_ptr vaapi_picture_factory_; -- -- scoped_refptr vaapi_wrapper_; -- -- // All allocated Pictures, regardless of their current state. Pictures are -- // allocated once using |create_vaapi_picture_callback_| and destroyed at the -- // end of decode. Comes after |vaapi_wrapper_| to ensure all pictures are -- // destroyed before said |vaapi_wrapper_| is destroyed. -- using Pictures = std::map>; -- Pictures pictures_; -- -- // Return a VaapiPicture associated with given client-provided id. -- VaapiPicture* PictureById(int32_t picture_buffer_id); -- -- // VA Surfaces no longer in use that can be passed back to the decoder for -- // reuse, once it requests them. -- std::list available_va_surfaces_; -- // Signalled when output surfaces are queued onto the available_va_surfaces_ -- // queue. -- base::ConditionVariable surfaces_available_; -- -- // Pending output requests from the decoder. When it indicates that we should -- // output a surface and we have an available Picture (i.e. texture) ready -- // to use, we'll execute the callback passing the Picture. The callback -- // will put the contents of the surface into the picture and return it to -- // the client, releasing the surface as well. -- // If we don't have any available Pictures at the time when the decoder -- // requests output, we'll store the request on pending_output_cbs_ queue for -- // later and run it once the client gives us more textures -- // via ReusePictureBuffer(). -- using OutputCB = base::Callback; -- base::queue pending_output_cbs_; -- -- // ChildThread's task runner. -- scoped_refptr task_runner_; -- -- // WeakPtr<> pointing to |this| for use in posting tasks from the decoder -- // thread back to the ChildThread. Because the decoder thread is a member of -- // this class, any task running on the decoder thread is guaranteed that this -- // object is still alive. As a result, tasks posted from ChildThread to -- // decoder thread should use base::Unretained(this), and tasks posted from the -- // decoder thread to the ChildThread should use |weak_this_|. -- base::WeakPtr weak_this_; -- -- // Callback used when creating VASurface objects. -- VASurface::ReleaseCB va_surface_release_cb_; -- -- // To expose client callbacks from VideoDecodeAccelerator. -- // NOTE: all calls to these objects *MUST* be executed on task_runner_. -- std::unique_ptr> client_ptr_factory_; -- base::WeakPtr client_; -- -- // Accelerators come after vaapi_wrapper_ to ensure they are destroyed first. -- std::unique_ptr h264_accelerator_; -- std::unique_ptr vp8_accelerator_; -- std::unique_ptr vp9_accelerator_; -- // After *_accelerator_ to ensure correct destruction order. -- std::unique_ptr decoder_; -- -- base::Thread decoder_thread_; -- // Use this to post tasks to |decoder_thread_| instead of -- // |decoder_thread_.message_loop()| because the latter will be NULL once -- // |decoder_thread_.Stop()| returns. -- scoped_refptr decoder_thread_task_runner_; -- -- int num_frames_at_client_; -- -- // Whether we are waiting for any pending_output_cbs_ to be run before -- // NotifyingFlushDone. -- bool finish_flush_pending_; -- -- // Decoder requested a new surface set and we are waiting for all the surfaces -- // to be returned before we can free them. -- bool awaiting_va_surfaces_recycle_; -- -- // Last requested number/resolution of output picture buffers and their -- // format. -- size_t requested_num_pics_; -- gfx::Size requested_pic_size_; -- gfx::BufferFormat output_format_; -- VideoCodecProfile profile_; -- -- // Callback to make GL context current. -- MakeGLContextCurrentCallback make_context_current_cb_; -- -- // Callback to bind a GLImage to a given texture. -- BindGLImageCallback bind_image_cb_; -- -- // The WeakPtrFactory for |weak_this_|. -- base::WeakPtrFactory weak_this_factory_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAccelerator); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VAAPI_VIDEO_DECODE_ACCELERATOR_H_ ---- a/media/gpu/vaapi_video_decode_accelerator_unittest.cc -+++ /dev/null -@@ -1,367 +0,0 @@ --// Copyright 2017 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_video_decode_accelerator.h" -- --#include "base/bind.h" --#include "base/memory/ptr_util.h" --#include "base/run_loop.h" --#include "base/test/scoped_task_environment.h" --#include "media/gpu/accelerated_video_decoder.h" --#include "media/gpu/format_utils.h" --#include "media/gpu/vaapi/vaapi_picture.h" --#include "media/gpu/vaapi/vaapi_picture_factory.h" --#include "media/gpu/vaapi_wrapper.h" --#include "testing/gmock/include/gmock/gmock.h" --#include "testing/gtest/include/gtest/gtest.h" -- --using ::testing::_; --using ::testing::DoAll; --using ::testing::Invoke; --using ::testing::Return; --using ::testing::TestWithParam; --using ::testing::ValuesIn; --using ::testing::WithArgs; -- --namespace media { -- --namespace { -- --ACTION_P(RunClosure, closure) { -- closure.Run(); --} -- --constexpr VideoCodecProfile kCodecProfiles[] = {H264PROFILE_MIN, VP8PROFILE_MIN, -- VP9PROFILE_MIN}; --constexpr int kBitstreamId = 123; --constexpr size_t kInputSize = 256; -- --} // namespace -- --class MockAcceleratedVideoDecoder : public AcceleratedVideoDecoder { -- public: -- MockAcceleratedVideoDecoder() = default; -- ~MockAcceleratedVideoDecoder() override = default; -- -- MOCK_METHOD2(SetStream, void(const uint8_t* ptr, size_t size)); -- MOCK_METHOD0(Flush, bool()); -- MOCK_METHOD0(Reset, void()); -- MOCK_METHOD0(Decode, DecodeResult()); -- MOCK_CONST_METHOD0(GetPicSize, gfx::Size()); -- MOCK_CONST_METHOD0(GetRequiredNumOfPictures, size_t()); --}; -- --class MockVaapiWrapper : public VaapiWrapper { -- public: -- MockVaapiWrapper() = default; -- MOCK_METHOD4( -- CreateSurfaces, -- bool(unsigned int, const gfx::Size&, size_t, std::vector*)); -- MOCK_METHOD0(DestroySurfaces, void()); -- -- private: -- ~MockVaapiWrapper() override = default; --}; -- --class MockVaapiPicture : public VaapiPicture { -- public: -- MockVaapiPicture(const scoped_refptr& vaapi_wrapper, -- const MakeGLContextCurrentCallback& make_context_current_cb, -- const BindGLImageCallback& bind_image_cb, -- int32_t picture_buffer_id, -- const gfx::Size& size, -- uint32_t texture_id, -- uint32_t client_texture_id) -- : VaapiPicture(vaapi_wrapper, -- make_context_current_cb, -- bind_image_cb, -- picture_buffer_id, -- size, -- texture_id, -- client_texture_id) {} -- ~MockVaapiPicture() override = default; -- -- // VaapiPicture implementation. -- bool Allocate(gfx::BufferFormat format) override { return true; } -- bool ImportGpuMemoryBufferHandle( -- gfx::BufferFormat format, -- const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override { -- return true; -- } -- bool DownloadFromSurface( -- const scoped_refptr& va_surface) override { -- return true; -- } -- bool AllowOverlay() const override { return false; } --}; -- --class MockVaapiPictureFactory : public VaapiPictureFactory { -- public: -- MockVaapiPictureFactory() = default; -- ~MockVaapiPictureFactory() override = default; -- -- MOCK_METHOD2(MockCreateVaapiPicture, void(VaapiWrapper*, const gfx::Size&)); -- std::unique_ptr Create( -- const scoped_refptr& vaapi_wrapper, -- const MakeGLContextCurrentCallback& make_context_current_cb, -- const BindGLImageCallback& bind_image_cb, -- int32_t picture_buffer_id, -- const gfx::Size& size, -- uint32_t texture_id, -- uint32_t client_texture_id) override { -- MockCreateVaapiPicture(vaapi_wrapper.get(), size); -- return std::make_unique( -- vaapi_wrapper, make_context_current_cb, bind_image_cb, -- picture_buffer_id, size, texture_id, client_texture_id); -- } --}; -- --class VaapiVideoDecodeAcceleratorTest : public TestWithParam, -- public VideoDecodeAccelerator::Client { -- public: -- VaapiVideoDecodeAcceleratorTest() -- : vda_(base::Bind([] { return true; }), -- base::Bind([](uint32_t client_texture_id, -- uint32_t texture_target, -- const scoped_refptr& image, -- bool can_bind_to_sampler) { return true; })), -- decoder_thread_("VaapiVideoDecodeAcceleratorTestThread"), -- mock_decoder_(new MockAcceleratedVideoDecoder), -- mock_vaapi_picture_factory_(new MockVaapiPictureFactory()), -- mock_vaapi_wrapper_(new MockVaapiWrapper()), -- weak_ptr_factory_(this) { -- decoder_thread_.Start(); -- -- // Don't want to go through a vda_->Initialize() because it binds too many -- // items of the environment. Instead, just start the decoder thread. -- vda_.decoder_thread_task_runner_ = decoder_thread_.task_runner(); -- -- // Plug in all the mocks and ourselves as the |client_|. -- vda_.decoder_.reset(mock_decoder_); -- vda_.client_ = weak_ptr_factory_.GetWeakPtr(); -- vda_.vaapi_wrapper_ = mock_vaapi_wrapper_; -- vda_.vaapi_picture_factory_.reset(mock_vaapi_picture_factory_); -- -- vda_.state_ = VaapiVideoDecodeAccelerator::kIdle; -- } -- ~VaapiVideoDecodeAcceleratorTest() {} -- -- void SetUp() override { -- in_shm_.reset(new base::SharedMemory); -- ASSERT_TRUE(in_shm_->CreateAndMapAnonymous(kInputSize)); -- } -- -- void SetVdaStateToUnitialized() { -- vda_.state_ = VaapiVideoDecodeAccelerator::kUninitialized; -- } -- -- void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer) { -- vda_.QueueInputBuffer(bitstream_buffer); -- } -- -- void AssignPictureBuffers(const std::vector& picture_buffers) { -- vda_.AssignPictureBuffers(picture_buffers); -- } -- -- // Reset epilogue, needed to get |vda_| worker thread out of its Wait(). -- void ResetSequence() { -- base::RunLoop run_loop; -- base::Closure quit_closure = run_loop.QuitClosure(); -- EXPECT_CALL(*mock_decoder_, Reset()); -- EXPECT_CALL(*this, NotifyResetDone()).WillOnce(RunClosure(quit_closure)); -- vda_.Reset(); -- run_loop.Run(); -- } -- -- // VideoDecodeAccelerator::Client methods. -- MOCK_METHOD1(NotifyInitializationComplete, void(bool)); -- MOCK_METHOD5( -- ProvidePictureBuffers, -- void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t)); -- MOCK_METHOD1(DismissPictureBuffer, void(int32_t)); -- MOCK_METHOD1(PictureReady, void(const Picture&)); -- MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t)); -- MOCK_METHOD0(NotifyFlushDone, void()); -- MOCK_METHOD0(NotifyResetDone, void()); -- MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error)); -- -- base::test::ScopedTaskEnvironment scoped_task_environment_; -- -- // The class under test and a worker thread for it. -- VaapiVideoDecodeAccelerator vda_; -- base::Thread decoder_thread_; -- -- // Ownership passed to |vda_|, but we retain a pointer to it for MOCK checks. -- MockAcceleratedVideoDecoder* mock_decoder_; -- MockVaapiPictureFactory* mock_vaapi_picture_factory_; -- -- scoped_refptr mock_vaapi_wrapper_; -- -- std::unique_ptr in_shm_; -- -- private: -- base::WeakPtrFactory weak_ptr_factory_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAcceleratorTest); --}; -- --// This test checks that QueueInputBuffer() fails when state is kUnitialized. --TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndError) { -- SetVdaStateToUnitialized(); -- -- base::SharedMemoryHandle handle; -- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -- -- EXPECT_CALL(*this, -- NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)); -- QueueInputBuffer(bitstream_buffer); --} -- --// Verifies that Decode() returning kDecodeError ends up pinging NotifyError(). --TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeError) { -- base::SharedMemoryHandle handle; -- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -- -- base::RunLoop run_loop; -- base::Closure quit_closure = run_loop.QuitClosure(); -- EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); -- EXPECT_CALL(*mock_decoder_, Decode()) -- .WillOnce(Return(AcceleratedVideoDecoder::kDecodeError)); -- EXPECT_CALL(*this, NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE)) -- .WillOnce(RunClosure(quit_closure)); -- -- QueueInputBuffer(bitstream_buffer); -- run_loop.Run(); --} -- --// Tests usual startup sequence: a BitstreamBuffer is enqueued for decode, --// |vda_| asks for PictureBuffers, that we provide, and then the same Decode() --// is tried again. --TEST_P(VaapiVideoDecodeAcceleratorTest, -- QueueInputBufferAndAssignPictureBuffersAndDecode) { -- // Try and QueueInputBuffer(), |vda_| will ping us to ProvidePictureBuffers(). -- const uint32_t kNumPictures = 2; -- const gfx::Size kPictureSize(64, 48); -- { -- base::SharedMemoryHandle handle; -- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -- -- base::RunLoop run_loop; -- base::Closure quit_closure = run_loop.QuitClosure(); -- EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); -- EXPECT_CALL(*mock_decoder_, Decode()) -- .WillOnce(Return(AcceleratedVideoDecoder::kAllocateNewSurfaces)); -- -- EXPECT_CALL(*mock_decoder_, GetRequiredNumOfPictures()) -- .WillOnce(Return(kNumPictures)); -- EXPECT_CALL(*mock_decoder_, GetPicSize()).WillOnce(Return(kPictureSize)); -- EXPECT_CALL(*mock_vaapi_wrapper_, DestroySurfaces()); -- -- EXPECT_CALL(*this, -- ProvidePictureBuffers(kNumPictures, _, 1, kPictureSize, _)) -- .WillOnce(RunClosure(quit_closure)); -- -- QueueInputBuffer(bitstream_buffer); -- run_loop.Run(); -- } -- // AssignPictureBuffers() accordingly and expect another go at Decode(). -- { -- base::RunLoop run_loop; -- base::Closure quit_closure = run_loop.QuitClosure(); -- -- const std::vector kPictureBuffers( -- {{2, kPictureSize}, {3, kPictureSize}}); -- EXPECT_EQ(kPictureBuffers.size(), kNumPictures); -- -- EXPECT_CALL(*mock_vaapi_wrapper_, -- CreateSurfaces(_, kPictureSize, kNumPictures, _)) -- .WillOnce(DoAll( -- WithArgs<3>(Invoke([](std::vector* va_surface_ids) { -- va_surface_ids->resize(kNumPictures); -- })), -- Return(true))); -- EXPECT_CALL(*mock_vaapi_picture_factory_, -- MockCreateVaapiPicture(mock_vaapi_wrapper_.get(), kPictureSize)) -- .Times(2); -- -- EXPECT_CALL(*mock_decoder_, Decode()) -- .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); -- EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) -- .WillOnce(RunClosure(quit_closure)); -- -- AssignPictureBuffers(kPictureBuffers); -- run_loop.Run(); -- } -- -- ResetSequence(); --} -- --// Verifies that Decode() replying kRanOutOfStreamData (to signal it's finished) --// rolls to a NotifyEndOfBitstreamBuffer(). --TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeFinished) { -- base::SharedMemoryHandle handle; -- handle = base::SharedMemory::DuplicateHandle(in_shm_->handle()); -- BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize); -- -- { -- base::RunLoop run_loop; -- base::Closure quit_closure = run_loop.QuitClosure(); -- EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize)); -- EXPECT_CALL(*mock_decoder_, Decode()) -- .WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData)); -- EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId)) -- .WillOnce(RunClosure(quit_closure)); -- -- QueueInputBuffer(bitstream_buffer); -- run_loop.Run(); -- } -- -- ResetSequence(); --} -- --// Verify that it is possible to select DRM(egl) and TFP(glx) at runtime. --TEST_P(VaapiVideoDecodeAcceleratorTest, SupportedPlatforms) { -- EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationNone, -- mock_vaapi_picture_factory_->GetVaapiImplementation( -- gl::kGLImplementationNone)); -- EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationDrm, -- mock_vaapi_picture_factory_->GetVaapiImplementation( -- gl::kGLImplementationEGLGLES2)); -- --#if defined(USE_X11) -- EXPECT_EQ(VaapiPictureFactory::kVaapiImplementationX11, -- mock_vaapi_picture_factory_->GetVaapiImplementation( -- gl::kGLImplementationDesktopGL)); --#endif --} -- --// Verifies the expected buffer format for each output mode. --TEST_P(VaapiVideoDecodeAcceleratorTest, PictureBufferFormat) { -- gfx::BufferFormat allocate_format = -- mock_vaapi_picture_factory_->GetBufferFormatForAllocateMode(); -- gfx::BufferFormat import_format = -- mock_vaapi_picture_factory_->GetBufferFormatForImportMode(); -- --#if defined(USE_OZONE) -- EXPECT_EQ(gfx::BufferFormat::BGRX_8888, allocate_format); --#else -- EXPECT_EQ(gfx::BufferFormat::RGBX_8888, allocate_format); --#endif // USE_OZONE -- -- EXPECT_EQ(gfx::BufferFormat::YVU_420, import_format); -- -- EXPECT_EQ(PIXEL_FORMAT_XRGB, -- GfxBufferFormatToVideoPixelFormat(allocate_format)); -- EXPECT_EQ(PIXEL_FORMAT_YV12, -- GfxBufferFormatToVideoPixelFormat(import_format)); --} -- --INSTANTIATE_TEST_CASE_P(/* No prefix. */, -- VaapiVideoDecodeAcceleratorTest, -- ValuesIn(kCodecProfiles)); -- --} // namespace media ---- a/media/gpu/vaapi_video_encode_accelerator.cc -+++ /dev/null -@@ -1,1102 +0,0 @@ --// Copyright 2014 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_video_encode_accelerator.h" -- --#include -- --#include --#include -- --#include -- --#include "base/bind.h" --#include "base/callback.h" --#include "base/macros.h" --#include "base/metrics/histogram_macros.h" --#include "base/numerics/safe_conversions.h" --#include "base/single_thread_task_runner.h" --#include "base/threading/thread_task_runner_handle.h" --#include "media/base/bind_to_current_loop.h" --#include "media/gpu/h264_dpb.h" --#include "media/gpu/shared_memory_region.h" -- --#define VLOGF(level) VLOG(level) << __func__ << "(): " --#define DVLOGF(level) DVLOG(level) << __func__ << "(): " -- --#define NOTIFY_ERROR(error, msg) \ -- do { \ -- SetState(kError); \ -- VLOGF(1) << msg; \ -- VLOGF(1) << "Calling NotifyError(" << error << ")"; \ -- NotifyError(error); \ -- } while (0) -- --namespace media { -- --namespace { --// Need 2 surfaces for each frame: one for input data and one for --// reconstructed picture, which is later used for reference. --const size_t kMinSurfacesToEncode = 2; -- --// Subjectively chosen. --const size_t kNumInputBuffers = 4; --const size_t kMaxNumReferenceFrames = 4; -- --// TODO(owenlin): Adjust the value after b/71367113 is fixed. --const size_t kExtraOutputBufferSize = 32768; // bytes -- --// We need up to kMaxNumReferenceFrames surfaces for reference, plus one --// for input and one for encode (which will be added to the set of reference --// frames for subsequent frames). Actual execution of HW encode is done --// in parallel, and we want to process more frames in the meantime. --// To have kNumInputBuffers in flight, we need a full set of reference + --// encode surfaces (i.e. kMaxNumReferenceFrames + kMinSurfacesToEncode), and --// (kNumInputBuffers - 1) of kMinSurfacesToEncode for the remaining frames --// in flight. --const size_t kNumSurfaces = kMaxNumReferenceFrames + kMinSurfacesToEncode + -- kMinSurfacesToEncode * (kNumInputBuffers - 1); -- --// An IDR every 2048 frames, an I frame every 256 and no B frames. --// We choose IDR period to equal MaxFrameNum so it must be a power of 2. --const int kIDRPeriod = 2048; --const int kIPeriod = 256; --const int kIPPeriod = 1; -- --const int kDefaultFramerate = 30; -- --// HRD parameters (ch. E.2.2 in spec). --const int kBitRateScale = 0; // bit_rate_scale for SPS HRD parameters. --const int kCPBSizeScale = 0; // cpb_size_scale for SPS HRD parameters. -- --const int kDefaultQP = 26; --// All Intel codecs can do at least 4.1. --const int kDefaultLevelIDC = 41; --const int kChromaFormatIDC = 1; // 4:2:0 -- --// Arbitrarily chosen bitrate window size for rate control, in ms. --const int kCPBWindowSizeMs = 1500; -- --// UMA errors that the VaapiVideoEncodeAccelerator class reports. --enum VAVEAEncoderFailure { -- VAAPI_ERROR = 0, -- VAVEA_ENCODER_FAILURES_MAX, --}; --} -- --// Round |value| up to |alignment|, which must be a power of 2. --static inline size_t RoundUpToPowerOf2(size_t value, size_t alignment) { -- // Check that |alignment| is a power of 2. -- DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); -- return ((value + (alignment - 1)) & ~(alignment - 1)); --} -- --static void ReportToUMA(VAVEAEncoderFailure failure) { -- UMA_HISTOGRAM_ENUMERATION("Media.VAVEA.EncoderFailure", failure, -- VAVEA_ENCODER_FAILURES_MAX + 1); --} -- --struct VaapiVideoEncodeAccelerator::InputFrameRef { -- InputFrameRef(const scoped_refptr& frame, bool force_keyframe) -- : frame(frame), force_keyframe(force_keyframe) {} -- const scoped_refptr frame; -- const bool force_keyframe; --}; -- --struct VaapiVideoEncodeAccelerator::BitstreamBufferRef { -- BitstreamBufferRef(int32_t id, std::unique_ptr shm) -- : id(id), shm(std::move(shm)) {} -- const int32_t id; -- const std::unique_ptr shm; --}; -- --VideoEncodeAccelerator::SupportedProfiles --VaapiVideoEncodeAccelerator::GetSupportedProfiles() { -- return VaapiWrapper::GetSupportedEncodeProfiles(); --} -- --static unsigned int Log2OfPowerOf2(unsigned int x) { -- CHECK_GT(x, 0u); -- DCHECK_EQ(x & (x - 1), 0u); -- -- int log = 0; -- while (x > 1) { -- x >>= 1; -- ++log; -- } -- return log; --} -- --VaapiVideoEncodeAccelerator::VaapiVideoEncodeAccelerator() -- : profile_(VIDEO_CODEC_PROFILE_UNKNOWN), -- mb_width_(0), -- mb_height_(0), -- output_buffer_byte_size_(0), -- state_(kUninitialized), -- frame_num_(0), -- idr_pic_id_(0), -- bitrate_(0), -- framerate_(0), -- cpb_size_(0), -- encoding_parameters_changed_(false), -- encoder_thread_("VAVEAEncoderThread"), -- child_task_runner_(base::ThreadTaskRunnerHandle::Get()), -- weak_this_ptr_factory_(this) { -- VLOGF(2); -- weak_this_ = weak_this_ptr_factory_.GetWeakPtr(); -- max_ref_idx_l0_size_ = kMaxNumReferenceFrames; -- qp_ = kDefaultQP; -- idr_period_ = kIDRPeriod; -- i_period_ = kIPeriod; -- ip_period_ = kIPPeriod; --} -- --VaapiVideoEncodeAccelerator::~VaapiVideoEncodeAccelerator() { -- VLOGF(2); -- DCHECK(child_task_runner_->BelongsToCurrentThread()); -- DCHECK(!encoder_thread_.IsRunning()); --} -- --bool VaapiVideoEncodeAccelerator::Initialize( -- VideoPixelFormat format, -- const gfx::Size& input_visible_size, -- VideoCodecProfile output_profile, -- uint32_t initial_bitrate, -- Client* client) { -- DCHECK(child_task_runner_->BelongsToCurrentThread()); -- DCHECK(!encoder_thread_.IsRunning()); -- DCHECK_EQ(state_, kUninitialized); -- -- VLOGF(2) << "Initializing VAVEA, input_format: " -- << VideoPixelFormatToString(format) -- << ", input_visible_size: " << input_visible_size.ToString() -- << ", output_profile: " << GetProfileName(output_profile) -- << ", initial_bitrate: " << initial_bitrate; -- -- client_ptr_factory_.reset(new base::WeakPtrFactory(client)); -- client_ = client_ptr_factory_->GetWeakPtr(); -- -- const SupportedProfiles& profiles = GetSupportedProfiles(); -- auto profile = find_if(profiles.begin(), profiles.end(), -- [output_profile](const SupportedProfile& profile) { -- return profile.profile == output_profile; -- }); -- if (profile == profiles.end()) { -- VLOGF(1) << "Unsupported output profile " << GetProfileName(output_profile); -- return false; -- } -- if (input_visible_size.width() > profile->max_resolution.width() || -- input_visible_size.height() > profile->max_resolution.height()) { -- VLOGF(1) << "Input size too big: " << input_visible_size.ToString() -- << ", max supported size: " << profile->max_resolution.ToString(); -- return false; -- } -- -- if (format != PIXEL_FORMAT_I420) { -- VLOGF(1) << "Unsupported input format: " -- << VideoPixelFormatToString(format); -- return false; -- } -- -- profile_ = output_profile; -- visible_size_ = input_visible_size; -- // 4:2:0 format has to be 2-aligned. -- DCHECK_EQ(visible_size_.width() % 2, 0); -- DCHECK_EQ(visible_size_.height() % 2, 0); -- coded_size_ = gfx::Size(RoundUpToPowerOf2(visible_size_.width(), 16), -- RoundUpToPowerOf2(visible_size_.height(), 16)); -- mb_width_ = coded_size_.width() / 16; -- mb_height_ = coded_size_.height() / 16; -- output_buffer_byte_size_ = coded_size_.GetArea() + kExtraOutputBufferSize; -- -- UpdateRates(initial_bitrate, kDefaultFramerate); -- -- vaapi_wrapper_ = -- VaapiWrapper::CreateForVideoCodec(VaapiWrapper::kEncode, output_profile, -- base::Bind(&ReportToUMA, VAAPI_ERROR)); -- if (!vaapi_wrapper_.get()) { -- VLOGF(1) << "Failed initializing VAAPI for profile " -- << GetProfileName(output_profile); -- return false; -- } -- -- if (!encoder_thread_.Start()) { -- VLOGF(1) << "Failed to start encoder thread"; -- return false; -- } -- encoder_thread_task_runner_ = encoder_thread_.task_runner(); -- -- // Finish the remaining initialization on the encoder thread. -- encoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::InitializeTask, -- base::Unretained(this))); -- -- return true; --} -- --void VaapiVideoEncodeAccelerator::InitializeTask() { -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- DCHECK_EQ(state_, kUninitialized); -- VLOGF(2); -- -- va_surface_release_cb_ = BindToCurrentLoop( -- base::Bind(&VaapiVideoEncodeAccelerator::RecycleVASurfaceID, -- base::Unretained(this))); -- -- if (!vaapi_wrapper_->CreateSurfaces(VA_RT_FORMAT_YUV420, coded_size_, -- kNumSurfaces, -- &available_va_surface_ids_)) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed creating VASurfaces"); -- return; -- } -- -- UpdateSPS(); -- GeneratePackedSPS(); -- -- UpdatePPS(); -- GeneratePackedPPS(); -- -- child_task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&Client::RequireBitstreamBuffers, client_, kNumInputBuffers, -- coded_size_, output_buffer_byte_size_)); -- -- SetState(kEncoding); --} -- --void VaapiVideoEncodeAccelerator::RecycleVASurfaceID( -- VASurfaceID va_surface_id) { -- DVLOGF(4) << "va_surface_id: " << va_surface_id; -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- -- available_va_surface_ids_.push_back(va_surface_id); -- EncodeFrameTask(); --} -- --void VaapiVideoEncodeAccelerator::BeginFrame(bool force_keyframe) { -- current_pic_ = new H264Picture(); -- -- // If the current picture is an IDR picture, frame_num shall be equal to 0. -- if (force_keyframe) -- frame_num_ = 0; -- -- current_pic_->frame_num = frame_num_++; -- frame_num_ %= idr_period_; -- -- if (current_pic_->frame_num == 0) { -- current_pic_->idr = true; -- // H264 spec mandates idr_pic_id to differ between two consecutive IDRs. -- idr_pic_id_ ^= 1; -- ref_pic_list0_.clear(); -- } -- -- if (current_pic_->frame_num % i_period_ == 0) -- current_pic_->type = H264SliceHeader::kISlice; -- else -- current_pic_->type = H264SliceHeader::kPSlice; -- -- if (current_pic_->type != H264SliceHeader::kBSlice) -- current_pic_->ref = true; -- -- current_pic_->pic_order_cnt = current_pic_->frame_num * 2; -- current_pic_->top_field_order_cnt = current_pic_->pic_order_cnt; -- current_pic_->pic_order_cnt_lsb = current_pic_->pic_order_cnt; -- -- current_encode_job_->keyframe = current_pic_->idr; -- -- DVLOGF(4) << "Starting a new frame, type: " << current_pic_->type -- << (force_keyframe ? " (forced keyframe)" : "") -- << " frame_num: " << current_pic_->frame_num -- << " POC: " << current_pic_->pic_order_cnt; --} -- --void VaapiVideoEncodeAccelerator::EndFrame() { -- DCHECK(current_pic_); -- // Store the picture on the list of reference pictures and keep the list -- // below maximum size, dropping oldest references. -- if (current_pic_->ref) -- ref_pic_list0_.push_front(current_encode_job_->recon_surface); -- size_t max_num_ref_frames = -- base::checked_cast(current_sps_.max_num_ref_frames); -- while (ref_pic_list0_.size() > max_num_ref_frames) -- ref_pic_list0_.pop_back(); -- -- submitted_encode_jobs_.push(make_linked_ptr(current_encode_job_.release())); --} -- --static void InitVAPicture(VAPictureH264* va_pic) { -- memset(va_pic, 0, sizeof(*va_pic)); -- va_pic->picture_id = VA_INVALID_ID; -- va_pic->flags = VA_PICTURE_H264_INVALID; --} -- --bool VaapiVideoEncodeAccelerator::SubmitFrameParameters() { -- DCHECK(current_pic_); -- VAEncSequenceParameterBufferH264 seq_param; -- memset(&seq_param, 0, sizeof(seq_param)); -- --#define SPS_TO_SP(a) seq_param.a = current_sps_.a; -- SPS_TO_SP(seq_parameter_set_id); -- SPS_TO_SP(level_idc); -- -- seq_param.intra_period = i_period_; -- seq_param.intra_idr_period = idr_period_; -- seq_param.ip_period = ip_period_; -- seq_param.bits_per_second = bitrate_; -- -- SPS_TO_SP(max_num_ref_frames); -- seq_param.picture_width_in_mbs = mb_width_; -- seq_param.picture_height_in_mbs = mb_height_; -- --#define SPS_TO_SP_FS(a) seq_param.seq_fields.bits.a = current_sps_.a; -- SPS_TO_SP_FS(chroma_format_idc); -- SPS_TO_SP_FS(frame_mbs_only_flag); -- SPS_TO_SP_FS(log2_max_frame_num_minus4); -- SPS_TO_SP_FS(pic_order_cnt_type); -- SPS_TO_SP_FS(log2_max_pic_order_cnt_lsb_minus4); --#undef SPS_TO_SP_FS -- -- SPS_TO_SP(bit_depth_luma_minus8); -- SPS_TO_SP(bit_depth_chroma_minus8); -- -- SPS_TO_SP(frame_cropping_flag); -- if (current_sps_.frame_cropping_flag) { -- SPS_TO_SP(frame_crop_left_offset); -- SPS_TO_SP(frame_crop_right_offset); -- SPS_TO_SP(frame_crop_top_offset); -- SPS_TO_SP(frame_crop_bottom_offset); -- } -- -- SPS_TO_SP(vui_parameters_present_flag); --#define SPS_TO_SP_VF(a) seq_param.vui_fields.bits.a = current_sps_.a; -- SPS_TO_SP_VF(timing_info_present_flag); --#undef SPS_TO_SP_VF -- SPS_TO_SP(num_units_in_tick); -- SPS_TO_SP(time_scale); --#undef SPS_TO_SP -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncSequenceParameterBufferType, -- sizeof(seq_param), &seq_param)) -- return false; -- -- VAEncPictureParameterBufferH264 pic_param; -- memset(&pic_param, 0, sizeof(pic_param)); -- -- pic_param.CurrPic.picture_id = current_encode_job_->recon_surface->id(); -- pic_param.CurrPic.TopFieldOrderCnt = current_pic_->top_field_order_cnt; -- pic_param.CurrPic.BottomFieldOrderCnt = current_pic_->bottom_field_order_cnt; -- pic_param.CurrPic.flags = 0; -- -- for (size_t i = 0; i < arraysize(pic_param.ReferenceFrames); ++i) -- InitVAPicture(&pic_param.ReferenceFrames[i]); -- -- DCHECK_LE(ref_pic_list0_.size(), arraysize(pic_param.ReferenceFrames)); -- RefPicList::const_iterator iter = ref_pic_list0_.begin(); -- for (size_t i = 0; -- i < arraysize(pic_param.ReferenceFrames) && iter != ref_pic_list0_.end(); -- ++iter, ++i) { -- pic_param.ReferenceFrames[i].picture_id = (*iter)->id(); -- pic_param.ReferenceFrames[i].flags = 0; -- } -- -- pic_param.coded_buf = current_encode_job_->coded_buffer; -- pic_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; -- pic_param.seq_parameter_set_id = current_pps_.seq_parameter_set_id; -- pic_param.frame_num = current_pic_->frame_num; -- pic_param.pic_init_qp = qp_; -- pic_param.num_ref_idx_l0_active_minus1 = max_ref_idx_l0_size_ - 1; -- pic_param.pic_fields.bits.idr_pic_flag = current_pic_->idr; -- pic_param.pic_fields.bits.reference_pic_flag = current_pic_->ref; --#define PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = current_pps_.a; -- PPS_TO_PP_PF(entropy_coding_mode_flag); -- PPS_TO_PP_PF(transform_8x8_mode_flag); -- PPS_TO_PP_PF(deblocking_filter_control_present_flag); --#undef PPS_TO_PP_PF -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPictureParameterBufferType, -- sizeof(pic_param), &pic_param)) -- return false; -- -- VAEncSliceParameterBufferH264 slice_param; -- memset(&slice_param, 0, sizeof(slice_param)); -- -- slice_param.num_macroblocks = mb_width_ * mb_height_; -- slice_param.macroblock_info = VA_INVALID_ID; -- slice_param.slice_type = current_pic_->type; -- slice_param.pic_parameter_set_id = current_pps_.pic_parameter_set_id; -- slice_param.idr_pic_id = idr_pic_id_; -- slice_param.pic_order_cnt_lsb = current_pic_->pic_order_cnt_lsb; -- slice_param.num_ref_idx_active_override_flag = true; -- -- for (size_t i = 0; i < arraysize(slice_param.RefPicList0); ++i) -- InitVAPicture(&slice_param.RefPicList0[i]); -- -- for (size_t i = 0; i < arraysize(slice_param.RefPicList1); ++i) -- InitVAPicture(&slice_param.RefPicList1[i]); -- -- DCHECK_LE(ref_pic_list0_.size(), arraysize(slice_param.RefPicList0)); -- iter = ref_pic_list0_.begin(); -- for (size_t i = 0; -- i < arraysize(slice_param.RefPicList0) && iter != ref_pic_list0_.end(); -- ++iter, ++i) { -- InitVAPicture(&slice_param.RefPicList0[i]); -- slice_param.RefPicList0[i].picture_id = (*iter)->id(); -- slice_param.RefPicList0[i].flags = 0; -- } -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncSliceParameterBufferType, -- sizeof(slice_param), &slice_param)) -- return false; -- -- VAEncMiscParameterRateControl rate_control_param; -- memset(&rate_control_param, 0, sizeof(rate_control_param)); -- rate_control_param.bits_per_second = bitrate_; -- rate_control_param.target_percentage = 90; -- rate_control_param.window_size = kCPBWindowSizeMs; -- rate_control_param.initial_qp = qp_; -- rate_control_param.rc_flags.bits.disable_frame_skip = true; -- -- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( -- VAEncMiscParameterTypeRateControl, sizeof(rate_control_param), -- &rate_control_param)) -- return false; -- -- VAEncMiscParameterFrameRate framerate_param; -- memset(&framerate_param, 0, sizeof(framerate_param)); -- framerate_param.framerate = framerate_; -- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( -- VAEncMiscParameterTypeFrameRate, sizeof(framerate_param), -- &framerate_param)) -- return false; -- -- VAEncMiscParameterHRD hrd_param; -- memset(&hrd_param, 0, sizeof(hrd_param)); -- hrd_param.buffer_size = cpb_size_; -- hrd_param.initial_buffer_fullness = cpb_size_ / 2; -- if (!vaapi_wrapper_->SubmitVAEncMiscParamBuffer( -- VAEncMiscParameterTypeHRD, sizeof(hrd_param), &hrd_param)) -- return false; -- -- return true; --} -- --bool VaapiVideoEncodeAccelerator::SubmitHeadersIfNeeded() { -- DCHECK(current_pic_); -- if (current_pic_->type != H264SliceHeader::kISlice) -- return true; -- -- // Submit SPS. -- VAEncPackedHeaderParameterBuffer par_buffer; -- memset(&par_buffer, 0, sizeof(par_buffer)); -- par_buffer.type = VAEncPackedHeaderSequence; -- par_buffer.bit_length = packed_sps_.BytesInBuffer() * 8; -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, -- sizeof(par_buffer), &par_buffer)) -- return false; -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, -- packed_sps_.BytesInBuffer(), -- packed_sps_.data())) -- return false; -- -- // Submit PPS. -- memset(&par_buffer, 0, sizeof(par_buffer)); -- par_buffer.type = VAEncPackedHeaderPicture; -- par_buffer.bit_length = packed_pps_.BytesInBuffer() * 8; -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderParameterBufferType, -- sizeof(par_buffer), &par_buffer)) -- return false; -- -- if (!vaapi_wrapper_->SubmitBuffer(VAEncPackedHeaderDataBufferType, -- packed_pps_.BytesInBuffer(), -- packed_pps_.data())) -- return false; -- -- return true; --} -- --bool VaapiVideoEncodeAccelerator::ExecuteEncode() { -- DCHECK(current_pic_); -- DVLOGF(4) << "Encoding frame_num: " << current_pic_->frame_num; -- return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( -- current_encode_job_->input_surface->id()); --} -- --bool VaapiVideoEncodeAccelerator::UploadFrame( -- const scoped_refptr& frame) { -- return vaapi_wrapper_->UploadVideoFrameToSurface( -- frame, current_encode_job_->input_surface->id()); --} -- --void VaapiVideoEncodeAccelerator::TryToReturnBitstreamBuffer() { -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- -- if (state_ != kEncoding) -- return; -- -- while (!submitted_encode_jobs_.empty()) { -- linked_ptr encode_job = submitted_encode_jobs_.front(); -- // An null job indicates a flush command. -- if (encode_job == nullptr) { -- submitted_encode_jobs_.pop(); -- DVLOGF(2) << "FlushDone"; -- DCHECK(flush_callback_); -- child_task_runner_->PostTask( -- FROM_HERE, base::BindOnce(std::move(flush_callback_), true)); -- continue; -- } -- -- if (available_bitstream_buffers_.empty()) -- break; -- auto buffer = available_bitstream_buffers_.front(); -- -- available_bitstream_buffers_.pop(); -- submitted_encode_jobs_.pop(); -- -- uint8_t* target_data = reinterpret_cast(buffer->shm->memory()); -- -- size_t data_size = 0; -- if (!vaapi_wrapper_->DownloadAndDestroyCodedBuffer( -- encode_job->coded_buffer, encode_job->input_surface->id(), -- target_data, buffer->shm->size(), &data_size)) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed downloading coded buffer"); -- return; -- } -- -- DVLOGF(4) << "Returning bitstream buffer " -- << (encode_job->keyframe ? "(keyframe)" : "") -- << " id: " << buffer->id << " size: " << data_size; -- -- child_task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&Client::BitstreamBufferReady, client_, buffer->id, -- data_size, encode_job->keyframe, encode_job->timestamp)); -- break; -- } --} -- --void VaapiVideoEncodeAccelerator::Encode(const scoped_refptr& frame, -- bool force_keyframe) { -- DVLOGF(4) << "Frame timestamp: " << frame->timestamp().InMilliseconds() -- << " force_keyframe: " << force_keyframe; -- DCHECK(child_task_runner_->BelongsToCurrentThread()); -- -- encoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::EncodeTask, -- base::Unretained(this), frame, force_keyframe)); --} -- --bool VaapiVideoEncodeAccelerator::PrepareNextJob(base::TimeDelta timestamp) { -- if (available_va_surface_ids_.size() < kMinSurfacesToEncode) -- return false; -- -- DCHECK(!current_encode_job_); -- current_encode_job_.reset(new EncodeJob()); -- -- if (!vaapi_wrapper_->CreateCodedBuffer(output_buffer_byte_size_, -- ¤t_encode_job_->coded_buffer)) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed creating coded buffer"); -- return false; -- } -- -- current_encode_job_->timestamp = timestamp; -- -- current_encode_job_->input_surface = new VASurface( -- available_va_surface_ids_.back(), coded_size_, -- vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); -- available_va_surface_ids_.pop_back(); -- -- current_encode_job_->recon_surface = new VASurface( -- available_va_surface_ids_.back(), coded_size_, -- vaapi_wrapper_->va_surface_format(), va_surface_release_cb_); -- available_va_surface_ids_.pop_back(); -- -- // Reference surfaces are needed until the job is done, but they get -- // removed from ref_pic_list0_ when it's full at the end of job submission. -- // Keep refs to them along with the job and only release after sync. -- current_encode_job_->reference_surfaces = ref_pic_list0_; -- -- return true; --} -- --void VaapiVideoEncodeAccelerator::EncodeTask( -- const scoped_refptr& frame, -- bool force_keyframe) { -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- DCHECK_NE(state_, kUninitialized); -- -- encoder_input_queue_.push( -- make_linked_ptr(new InputFrameRef(frame, force_keyframe))); -- EncodeFrameTask(); --} -- --void VaapiVideoEncodeAccelerator::EncodeFrameTask() { -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- -- if (state_ != kEncoding || encoder_input_queue_.empty()) -- return; -- -- if (!PrepareNextJob(encoder_input_queue_.front()->frame->timestamp())) { -- DVLOGF(4) << "Not ready for next frame yet"; -- return; -- } -- -- linked_ptr frame_ref = encoder_input_queue_.front(); -- encoder_input_queue_.pop(); -- -- if (!UploadFrame(frame_ref->frame)) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed uploading source frame to HW."); -- return; -- } -- -- BeginFrame(frame_ref->force_keyframe || encoding_parameters_changed_); -- encoding_parameters_changed_ = false; -- -- if (!SubmitFrameParameters()) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame parameters."); -- return; -- } -- -- if (!SubmitHeadersIfNeeded()) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting frame headers."); -- return; -- } -- -- if (!ExecuteEncode()) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed submitting encode job to HW."); -- return; -- } -- -- EndFrame(); -- TryToReturnBitstreamBuffer(); --} -- --void VaapiVideoEncodeAccelerator::UseOutputBitstreamBuffer( -- const BitstreamBuffer& buffer) { -- DVLOGF(4) << "id: " << buffer.id(); -- DCHECK(child_task_runner_->BelongsToCurrentThread()); -- -- if (buffer.size() < output_buffer_byte_size_) { -- NOTIFY_ERROR(kInvalidArgumentError, "Provided bitstream buffer too small"); -- return; -- } -- -- std::unique_ptr shm( -- new SharedMemoryRegion(buffer, false)); -- if (!shm->Map()) { -- NOTIFY_ERROR(kPlatformFailureError, "Failed mapping shared memory."); -- return; -- } -- -- std::unique_ptr buffer_ref( -- new BitstreamBufferRef(buffer.id(), std::move(shm))); -- -- encoder_thread_task_runner_->PostTask( -- FROM_HERE, -- base::Bind(&VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask, -- base::Unretained(this), base::Passed(&buffer_ref))); --} -- --void VaapiVideoEncodeAccelerator::UseOutputBitstreamBufferTask( -- std::unique_ptr buffer_ref) { -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- DCHECK_NE(state_, kUninitialized); -- -- available_bitstream_buffers_.push(make_linked_ptr(buffer_ref.release())); -- TryToReturnBitstreamBuffer(); --} -- --void VaapiVideoEncodeAccelerator::RequestEncodingParametersChange( -- uint32_t bitrate, -- uint32_t framerate) { -- VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; -- DCHECK(child_task_runner_->BelongsToCurrentThread()); -- -- encoder_thread_task_runner_->PostTask( -- FROM_HERE, -- base::Bind( -- &VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask, -- base::Unretained(this), bitrate, framerate)); --} -- --void VaapiVideoEncodeAccelerator::UpdateRates(uint32_t bitrate, -- uint32_t framerate) { -- if (encoder_thread_.IsRunning()) -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- DCHECK_NE(bitrate, 0u); -- DCHECK_NE(framerate, 0u); -- bitrate_ = bitrate; -- framerate_ = framerate; -- cpb_size_ = bitrate_ * kCPBWindowSizeMs / 1000; --} -- --void VaapiVideoEncodeAccelerator::RequestEncodingParametersChangeTask( -- uint32_t bitrate, -- uint32_t framerate) { -- VLOGF(2) << "bitrate: " << bitrate << " framerate: " << framerate; -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- DCHECK_NE(state_, kUninitialized); -- -- // This is a workaround to zero being temporarily, as part of the initial -- // setup, provided by the webrtc video encode and a zero bitrate and -- // framerate not being accepted by VAAPI -- // TODO: This code is common with v4l2_video_encode_accelerator.cc, perhaps -- // it could be pulled up to RTCVideoEncoder -- if (bitrate < 1) -- bitrate = 1; -- if (framerate < 1) -- framerate = 1; -- -- if (bitrate_ == bitrate && framerate_ == framerate) -- return; -- -- UpdateRates(bitrate, framerate); -- -- UpdateSPS(); -- GeneratePackedSPS(); -- -- // Submit new parameters along with next frame that will be processed. -- encoding_parameters_changed_ = true; --} -- --void VaapiVideoEncodeAccelerator::Flush(FlushCallback flush_callback) { -- DVLOGF(2); -- DCHECK(child_task_runner_->BelongsToCurrentThread()); -- if (flush_callback_) { -- NOTIFY_ERROR(kIllegalStateError, "There is a pending flush"); -- std::move(flush_callback).Run(false); -- return; -- } -- flush_callback_ = std::move(flush_callback); -- encoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::FlushTask, -- base::Unretained(this))); --} -- --void VaapiVideoEncodeAccelerator::FlushTask() { -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- -- // Insert an null job to indicate a flush command. -- submitted_encode_jobs_.push(linked_ptr(nullptr)); -- TryToReturnBitstreamBuffer(); --} -- --void VaapiVideoEncodeAccelerator::Destroy() { -- DCHECK(child_task_runner_->BelongsToCurrentThread()); -- -- // Can't call client anymore after Destroy() returns. -- client_ptr_factory_.reset(); -- weak_this_ptr_factory_.InvalidateWeakPtrs(); -- -- // Early-exit encoder tasks if they are running and join the thread. -- if (encoder_thread_.IsRunning()) { -- encoder_thread_.task_runner()->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::DestroyTask, -- base::Unretained(this))); -- encoder_thread_.Stop(); -- } -- -- if (flush_callback_) -- std::move(flush_callback_).Run(false); -- -- delete this; --} -- --void VaapiVideoEncodeAccelerator::DestroyTask() { -- VLOGF(2); -- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread()); -- SetState(kError); --} -- --void VaapiVideoEncodeAccelerator::UpdateSPS() { -- memset(¤t_sps_, 0, sizeof(H264SPS)); -- -- // Spec A.2 and A.3. -- switch (profile_) { -- case H264PROFILE_BASELINE: -- // Due to crbug.com/345569, we don't distinguish between constrained -- // and non-constrained baseline profiles. Since many codecs can't do -- // non-constrained, and constrained is usually what we mean (and it's a -- // subset of non-constrained), default to it. -- current_sps_.profile_idc = H264SPS::kProfileIDCBaseline; -- current_sps_.constraint_set0_flag = true; -- break; -- case H264PROFILE_MAIN: -- current_sps_.profile_idc = H264SPS::kProfileIDCMain; -- current_sps_.constraint_set1_flag = true; -- break; -- case H264PROFILE_HIGH: -- current_sps_.profile_idc = H264SPS::kProfileIDCHigh; -- break; -- default: -- NOTIMPLEMENTED(); -- return; -- } -- -- current_sps_.level_idc = kDefaultLevelIDC; -- current_sps_.seq_parameter_set_id = 0; -- current_sps_.chroma_format_idc = kChromaFormatIDC; -- -- DCHECK_GE(idr_period_, 1u << 4); -- current_sps_.log2_max_frame_num_minus4 = Log2OfPowerOf2(idr_period_) - 4; -- current_sps_.pic_order_cnt_type = 0; -- current_sps_.log2_max_pic_order_cnt_lsb_minus4 = -- Log2OfPowerOf2(idr_period_ * 2) - 4; -- current_sps_.max_num_ref_frames = max_ref_idx_l0_size_; -- -- current_sps_.frame_mbs_only_flag = true; -- -- DCHECK_GT(mb_width_, 0u); -- DCHECK_GT(mb_height_, 0u); -- current_sps_.pic_width_in_mbs_minus1 = mb_width_ - 1; -- DCHECK(current_sps_.frame_mbs_only_flag); -- current_sps_.pic_height_in_map_units_minus1 = mb_height_ - 1; -- -- if (visible_size_ != coded_size_) { -- // Visible size differs from coded size, fill crop information. -- current_sps_.frame_cropping_flag = true; -- DCHECK(!current_sps_.separate_colour_plane_flag); -- // Spec table 6-1. Only 4:2:0 for now. -- DCHECK_EQ(current_sps_.chroma_format_idc, 1); -- // Spec 7.4.2.1.1. Crop is in crop units, which is 2 pixels for 4:2:0. -- const unsigned int crop_unit_x = 2; -- const unsigned int crop_unit_y = 2 * (2 - current_sps_.frame_mbs_only_flag); -- current_sps_.frame_crop_left_offset = 0; -- current_sps_.frame_crop_right_offset = -- (coded_size_.width() - visible_size_.width()) / crop_unit_x; -- current_sps_.frame_crop_top_offset = 0; -- current_sps_.frame_crop_bottom_offset = -- (coded_size_.height() - visible_size_.height()) / crop_unit_y; -- } -- -- current_sps_.vui_parameters_present_flag = true; -- current_sps_.timing_info_present_flag = true; -- current_sps_.num_units_in_tick = 1; -- current_sps_.time_scale = framerate_ * 2; // See equation D-2 in spec. -- current_sps_.fixed_frame_rate_flag = true; -- -- current_sps_.nal_hrd_parameters_present_flag = true; -- // H.264 spec ch. E.2.2. -- current_sps_.cpb_cnt_minus1 = 0; -- current_sps_.bit_rate_scale = kBitRateScale; -- current_sps_.cpb_size_scale = kCPBSizeScale; -- current_sps_.bit_rate_value_minus1[0] = -- (bitrate_ >> (kBitRateScale + H264SPS::kBitRateScaleConstantTerm)) - 1; -- current_sps_.cpb_size_value_minus1[0] = -- (cpb_size_ >> (kCPBSizeScale + H264SPS::kCPBSizeScaleConstantTerm)) - 1; -- current_sps_.cbr_flag[0] = true; -- current_sps_.initial_cpb_removal_delay_length_minus_1 = -- H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; -- current_sps_.cpb_removal_delay_length_minus1 = -- H264SPS::kDefaultInitialCPBRemovalDelayLength - 1; -- current_sps_.dpb_output_delay_length_minus1 = -- H264SPS::kDefaultDPBOutputDelayLength - 1; -- current_sps_.time_offset_length = H264SPS::kDefaultTimeOffsetLength; -- current_sps_.low_delay_hrd_flag = false; --} -- --void VaapiVideoEncodeAccelerator::GeneratePackedSPS() { -- packed_sps_.Reset(); -- -- packed_sps_.BeginNALU(H264NALU::kSPS, 3); -- -- packed_sps_.AppendBits(8, current_sps_.profile_idc); -- packed_sps_.AppendBool(current_sps_.constraint_set0_flag); -- packed_sps_.AppendBool(current_sps_.constraint_set1_flag); -- packed_sps_.AppendBool(current_sps_.constraint_set2_flag); -- packed_sps_.AppendBool(current_sps_.constraint_set3_flag); -- packed_sps_.AppendBool(current_sps_.constraint_set4_flag); -- packed_sps_.AppendBool(current_sps_.constraint_set5_flag); -- packed_sps_.AppendBits(2, 0); // reserved_zero_2bits -- packed_sps_.AppendBits(8, current_sps_.level_idc); -- packed_sps_.AppendUE(current_sps_.seq_parameter_set_id); -- -- if (current_sps_.profile_idc == H264SPS::kProfileIDCHigh) { -- packed_sps_.AppendUE(current_sps_.chroma_format_idc); -- if (current_sps_.chroma_format_idc == 3) -- packed_sps_.AppendBool(current_sps_.separate_colour_plane_flag); -- packed_sps_.AppendUE(current_sps_.bit_depth_luma_minus8); -- packed_sps_.AppendUE(current_sps_.bit_depth_chroma_minus8); -- packed_sps_.AppendBool(current_sps_.qpprime_y_zero_transform_bypass_flag); -- packed_sps_.AppendBool(current_sps_.seq_scaling_matrix_present_flag); -- CHECK(!current_sps_.seq_scaling_matrix_present_flag); -- } -- -- packed_sps_.AppendUE(current_sps_.log2_max_frame_num_minus4); -- packed_sps_.AppendUE(current_sps_.pic_order_cnt_type); -- if (current_sps_.pic_order_cnt_type == 0) -- packed_sps_.AppendUE(current_sps_.log2_max_pic_order_cnt_lsb_minus4); -- else if (current_sps_.pic_order_cnt_type == 1) { -- CHECK(1); -- } -- -- packed_sps_.AppendUE(current_sps_.max_num_ref_frames); -- packed_sps_.AppendBool(current_sps_.gaps_in_frame_num_value_allowed_flag); -- packed_sps_.AppendUE(current_sps_.pic_width_in_mbs_minus1); -- packed_sps_.AppendUE(current_sps_.pic_height_in_map_units_minus1); -- -- packed_sps_.AppendBool(current_sps_.frame_mbs_only_flag); -- if (!current_sps_.frame_mbs_only_flag) -- packed_sps_.AppendBool(current_sps_.mb_adaptive_frame_field_flag); -- -- packed_sps_.AppendBool(current_sps_.direct_8x8_inference_flag); -- -- packed_sps_.AppendBool(current_sps_.frame_cropping_flag); -- if (current_sps_.frame_cropping_flag) { -- packed_sps_.AppendUE(current_sps_.frame_crop_left_offset); -- packed_sps_.AppendUE(current_sps_.frame_crop_right_offset); -- packed_sps_.AppendUE(current_sps_.frame_crop_top_offset); -- packed_sps_.AppendUE(current_sps_.frame_crop_bottom_offset); -- } -- -- packed_sps_.AppendBool(current_sps_.vui_parameters_present_flag); -- if (current_sps_.vui_parameters_present_flag) { -- packed_sps_.AppendBool(false); // aspect_ratio_info_present_flag -- packed_sps_.AppendBool(false); // overscan_info_present_flag -- packed_sps_.AppendBool(false); // video_signal_type_present_flag -- packed_sps_.AppendBool(false); // chroma_loc_info_present_flag -- -- packed_sps_.AppendBool(current_sps_.timing_info_present_flag); -- if (current_sps_.timing_info_present_flag) { -- packed_sps_.AppendBits(32, current_sps_.num_units_in_tick); -- packed_sps_.AppendBits(32, current_sps_.time_scale); -- packed_sps_.AppendBool(current_sps_.fixed_frame_rate_flag); -- } -- -- packed_sps_.AppendBool(current_sps_.nal_hrd_parameters_present_flag); -- if (current_sps_.nal_hrd_parameters_present_flag) { -- packed_sps_.AppendUE(current_sps_.cpb_cnt_minus1); -- packed_sps_.AppendBits(4, current_sps_.bit_rate_scale); -- packed_sps_.AppendBits(4, current_sps_.cpb_size_scale); -- CHECK_LT(base::checked_cast(current_sps_.cpb_cnt_minus1), -- arraysize(current_sps_.bit_rate_value_minus1)); -- for (int i = 0; i <= current_sps_.cpb_cnt_minus1; ++i) { -- packed_sps_.AppendUE(current_sps_.bit_rate_value_minus1[i]); -- packed_sps_.AppendUE(current_sps_.cpb_size_value_minus1[i]); -- packed_sps_.AppendBool(current_sps_.cbr_flag[i]); -- } -- packed_sps_.AppendBits( -- 5, current_sps_.initial_cpb_removal_delay_length_minus_1); -- packed_sps_.AppendBits(5, current_sps_.cpb_removal_delay_length_minus1); -- packed_sps_.AppendBits(5, current_sps_.dpb_output_delay_length_minus1); -- packed_sps_.AppendBits(5, current_sps_.time_offset_length); -- } -- -- packed_sps_.AppendBool(false); // vcl_hrd_parameters_flag -- if (current_sps_.nal_hrd_parameters_present_flag) -- packed_sps_.AppendBool(current_sps_.low_delay_hrd_flag); -- -- packed_sps_.AppendBool(false); // pic_struct_present_flag -- packed_sps_.AppendBool(true); // bitstream_restriction_flag -- -- packed_sps_.AppendBool(false); // motion_vectors_over_pic_boundaries_flag -- packed_sps_.AppendUE(2); // max_bytes_per_pic_denom -- packed_sps_.AppendUE(1); // max_bits_per_mb_denom -- packed_sps_.AppendUE(16); // log2_max_mv_length_horizontal -- packed_sps_.AppendUE(16); // log2_max_mv_length_vertical -- -- // Explicitly set max_num_reorder_frames to 0 to allow the decoder to -- // output pictures early. -- packed_sps_.AppendUE(0); // max_num_reorder_frames -- -- // The value of max_dec_frame_buffering shall be greater than or equal to -- // max_num_ref_frames. -- const unsigned int max_dec_frame_buffering = -- current_sps_.max_num_ref_frames; -- packed_sps_.AppendUE(max_dec_frame_buffering); -- } -- -- packed_sps_.FinishNALU(); --} -- --void VaapiVideoEncodeAccelerator::UpdatePPS() { -- memset(¤t_pps_, 0, sizeof(H264PPS)); -- -- current_pps_.seq_parameter_set_id = current_sps_.seq_parameter_set_id; -- current_pps_.pic_parameter_set_id = 0; -- -- current_pps_.entropy_coding_mode_flag = -- current_sps_.profile_idc >= H264SPS::kProfileIDCMain; -- -- CHECK_GT(max_ref_idx_l0_size_, 0u); -- current_pps_.num_ref_idx_l0_default_active_minus1 = max_ref_idx_l0_size_ - 1; -- current_pps_.num_ref_idx_l1_default_active_minus1 = 0; -- DCHECK_LE(qp_, 51u); -- current_pps_.pic_init_qp_minus26 = qp_ - 26; -- current_pps_.deblocking_filter_control_present_flag = true; -- current_pps_.transform_8x8_mode_flag = -- (current_sps_.profile_idc == H264SPS::kProfileIDCHigh); --} -- --void VaapiVideoEncodeAccelerator::GeneratePackedPPS() { -- packed_pps_.Reset(); -- -- packed_pps_.BeginNALU(H264NALU::kPPS, 3); -- -- packed_pps_.AppendUE(current_pps_.pic_parameter_set_id); -- packed_pps_.AppendUE(current_pps_.seq_parameter_set_id); -- packed_pps_.AppendBool(current_pps_.entropy_coding_mode_flag); -- packed_pps_.AppendBool( -- current_pps_.bottom_field_pic_order_in_frame_present_flag); -- CHECK_EQ(current_pps_.num_slice_groups_minus1, 0); -- packed_pps_.AppendUE(current_pps_.num_slice_groups_minus1); -- -- packed_pps_.AppendUE(current_pps_.num_ref_idx_l0_default_active_minus1); -- packed_pps_.AppendUE(current_pps_.num_ref_idx_l1_default_active_minus1); -- -- packed_pps_.AppendBool(current_pps_.weighted_pred_flag); -- packed_pps_.AppendBits(2, current_pps_.weighted_bipred_idc); -- -- packed_pps_.AppendSE(current_pps_.pic_init_qp_minus26); -- packed_pps_.AppendSE(current_pps_.pic_init_qs_minus26); -- packed_pps_.AppendSE(current_pps_.chroma_qp_index_offset); -- -- packed_pps_.AppendBool(current_pps_.deblocking_filter_control_present_flag); -- packed_pps_.AppendBool(current_pps_.constrained_intra_pred_flag); -- packed_pps_.AppendBool(current_pps_.redundant_pic_cnt_present_flag); -- -- packed_pps_.AppendBool(current_pps_.transform_8x8_mode_flag); -- packed_pps_.AppendBool(current_pps_.pic_scaling_matrix_present_flag); -- DCHECK(!current_pps_.pic_scaling_matrix_present_flag); -- packed_pps_.AppendSE(current_pps_.second_chroma_qp_index_offset); -- -- packed_pps_.FinishNALU(); --} -- --void VaapiVideoEncodeAccelerator::SetState(State state) { -- // Only touch state on encoder thread, unless it's not running. -- if (encoder_thread_.IsRunning() && -- !encoder_thread_task_runner_->BelongsToCurrentThread()) { -- encoder_thread_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::SetState, -- base::Unretained(this), state)); -- return; -- } -- -- VLOGF(2) << "setting state to: " << state; -- state_ = state; --} -- --void VaapiVideoEncodeAccelerator::NotifyError(Error error) { -- if (!child_task_runner_->BelongsToCurrentThread()) { -- child_task_runner_->PostTask( -- FROM_HERE, base::Bind(&VaapiVideoEncodeAccelerator::NotifyError, -- weak_this_, error)); -- return; -- } -- -- if (client_) { -- client_->NotifyError(error); -- client_ptr_factory_.reset(); -- } --} -- --VaapiVideoEncodeAccelerator::EncodeJob::EncodeJob() -- : coded_buffer(VA_INVALID_ID), keyframe(false) {} -- --VaapiVideoEncodeAccelerator::EncodeJob::~EncodeJob() {} -- --} // namespace media ---- a/media/gpu/vaapi_video_encode_accelerator.h -+++ /dev/null -@@ -1,275 +0,0 @@ --// Copyright 2014 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#ifndef MEDIA_GPU_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ --#define MEDIA_GPU_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ -- --#include --#include -- --#include --#include -- --#include "base/containers/queue.h" --#include "base/macros.h" --#include "base/memory/linked_ptr.h" --#include "base/threading/thread.h" --#include "media/filters/h264_bitstream_buffer.h" --#include "media/gpu/h264_dpb.h" --#include "media/gpu/media_gpu_export.h" --#include "media/gpu/va_surface.h" --#include "media/gpu/vaapi_wrapper.h" --#include "media/video/video_encode_accelerator.h" -- --namespace media { -- --// A VideoEncodeAccelerator implementation that uses VA-API --// (http://www.freedesktop.org/wiki/Software/vaapi) for HW-accelerated --// video encode. --class MEDIA_GPU_EXPORT VaapiVideoEncodeAccelerator -- : public VideoEncodeAccelerator { -- public: -- VaapiVideoEncodeAccelerator(); -- ~VaapiVideoEncodeAccelerator() override; -- -- // VideoEncodeAccelerator implementation. -- VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles() override; -- bool Initialize(VideoPixelFormat format, -- const gfx::Size& input_visible_size, -- VideoCodecProfile output_profile, -- uint32_t initial_bitrate, -- Client* client) override; -- void Encode(const scoped_refptr& frame, -- bool force_keyframe) override; -- void UseOutputBitstreamBuffer(const BitstreamBuffer& buffer) override; -- void RequestEncodingParametersChange(uint32_t bitrate, -- uint32_t framerate) override; -- void Destroy() override; -- void Flush(FlushCallback flush_callback) override; -- -- private: -- // Reference picture list. -- typedef std::list> RefPicList; -- -- // Encode job for one frame. Created when an input frame is awaiting and -- // enough resources are available to proceed. Once the job is prepared and -- // submitted to the hardware, it awaits on the submitted_encode_jobs_ queue -- // for an output bitstream buffer to become available. Once one is ready, -- // the encoded bytes are downloaded to it and job resources are released -- // and become available for reuse. -- struct EncodeJob { -- // Input surface for video frame data. -- scoped_refptr input_surface; -- // Surface for a reconstructed picture, which is used for reference -- // for subsequent frames. -- scoped_refptr recon_surface; -- // Buffer that will contain output bitstream for this frame. -- VABufferID coded_buffer; -- // Reference surfaces required to encode this picture. We keep references -- // to them here, because we may discard some of them from ref_pic_list* -- // before the HW job is done. -- RefPicList reference_surfaces; -- // True if this job will produce a keyframe. Used to report -- // to BitstreamBufferReady(). -- bool keyframe; -- // Source timestamp. -- base::TimeDelta timestamp; -- -- EncodeJob(); -- ~EncodeJob(); -- }; -- -- // Encoder state. -- enum State { -- kUninitialized, -- kEncoding, -- kError, -- }; -- -- // Holds input frames coming from the client ready to be encoded. -- struct InputFrameRef; -- // Holds output buffers coming from the client ready to be filled. -- struct BitstreamBufferRef; -- -- // Tasks for each of the VEA interface calls to be executed on the -- // encoder thread. -- void InitializeTask(); -- void EncodeTask(const scoped_refptr& frame, bool force_keyframe); -- void UseOutputBitstreamBufferTask( -- std::unique_ptr buffer_ref); -- void RequestEncodingParametersChangeTask(uint32_t bitrate, -- uint32_t framerate); -- void DestroyTask(); -- void FlushTask(); -- -- // Prepare and schedule an encode job if we have an input to encode -- // and enough resources to proceed. -- void EncodeFrameTask(); -- -- // Fill current_sps_/current_pps_ with current values. -- void UpdateSPS(); -- void UpdatePPS(); -- void UpdateRates(uint32_t bitrate, uint32_t framerate); -- -- // Generate packed SPS and PPS in packed_sps_/packed_pps_, using -- // values in current_sps_/current_pps_. -- void GeneratePackedSPS(); -- void GeneratePackedPPS(); -- -- // Check if we have sufficient resources for a new encode job, claim them and -- // fill current_encode_job_ with them. -- // Return false if we cannot start a new job yet, true otherwise. -- bool PrepareNextJob(base::TimeDelta timestamp); -- -- // Begin a new frame, making it a keyframe if |force_keyframe| is true, -- // updating current_pic_. -- void BeginFrame(bool force_keyframe); -- -- // End current frame, updating reference picture lists and storing current -- // job in the jobs awaiting completion on submitted_encode_jobs_. -- void EndFrame(); -- -- // Submit parameters for the current frame to the hardware. -- bool SubmitFrameParameters(); -- // Submit keyframe headers to the hardware if the current frame is a keyframe. -- bool SubmitHeadersIfNeeded(); -- -- // Upload image data from |frame| to the input surface for current job. -- bool UploadFrame(const scoped_refptr& frame); -- -- // Execute encode in hardware. This does not block and will return before -- // the job is finished. -- bool ExecuteEncode(); -- -- // Callback that returns a no longer used VASurfaceID to -- // available_va_surface_ids_ for reuse. -- void RecycleVASurfaceID(VASurfaceID va_surface_id); -- -- // Tries to return a bitstream buffer if both a submitted job awaits to -- // be completed and we have bitstream buffers from the client available -- // to download the encoded data to. -- void TryToReturnBitstreamBuffer(); -- -- // Puts the encoder into en error state and notifies client about the error. -- void NotifyError(Error error); -- -- // Sets the encoder state on the correct thread. -- void SetState(State state); -- -- // VaapiWrapper is the owner of all HW resources (surfaces and buffers) -- // and will free them on destruction. -- scoped_refptr vaapi_wrapper_; -- -- // Input profile and sizes. -- VideoCodecProfile profile_; -- gfx::Size visible_size_; -- gfx::Size coded_size_; // Macroblock-aligned. -- // Width/height in macroblocks. -- unsigned int mb_width_; -- unsigned int mb_height_; -- -- // Maximum size of the reference list 0. -- unsigned int max_ref_idx_l0_size_; -- -- // Initial QP. -- unsigned int qp_; -- -- // IDR frame period. -- unsigned int idr_period_; -- // I frame period. -- unsigned int i_period_; -- // IP period, i.e. how often do we need to have either an I or a P frame in -- // the stream. Period of 1 means we can have no B frames. -- unsigned int ip_period_; -- -- // Size in bytes required for input bitstream buffers. -- size_t output_buffer_byte_size_; -- -- // All of the members below must be accessed on the encoder_thread_, -- // while it is running. -- -- // Encoder state. Encode tasks will only run in kEncoding state. -- State state_; -- -- // frame_num to be used for the next frame. -- unsigned int frame_num_; -- // idr_pic_id to be used for the next frame. -- unsigned int idr_pic_id_; -- -- // Current bitrate in bps. -- unsigned int bitrate_; -- // Current fps. -- unsigned int framerate_; -- // CPB size in bits, i.e. bitrate in kbps * window size in ms/1000. -- unsigned int cpb_size_; -- // True if the parameters have changed and we need to submit a keyframe -- // with updated parameters. -- bool encoding_parameters_changed_; -- -- // Job currently being prepared for encode. -- std::unique_ptr current_encode_job_; -- -- // Current SPS, PPS and their packed versions. Packed versions are their NALUs -- // in AnnexB format *without* emulation prevention three-byte sequences -- // (those will be added by the driver). -- H264SPS current_sps_; -- H264BitstreamBuffer packed_sps_; -- H264PPS current_pps_; -- H264BitstreamBuffer packed_pps_; -- -- // Picture currently being prepared for encode. -- scoped_refptr current_pic_; -- -- // VA surfaces available for reuse. -- std::vector available_va_surface_ids_; -- -- // VA buffers for coded frames. -- std::vector available_va_buffer_ids_; -- -- // Currently active reference surfaces. -- RefPicList ref_pic_list0_; -- -- // Callback via which finished VA surfaces are returned to us. -- VASurface::ReleaseCB va_surface_release_cb_; -- -- // VideoFrames passed from the client, waiting to be encoded. -- base::queue> encoder_input_queue_; -- -- // BitstreamBuffers mapped, ready to be filled. -- base::queue> available_bitstream_buffers_; -- -- // Jobs submitted for encode, awaiting bitstream buffers to become available. -- // A pending flush command, indicated by a null job, will be also put in the -- // queue. -- base::queue> submitted_encode_jobs_; -- -- // Encoder thread. All tasks are executed on it. -- base::Thread encoder_thread_; -- scoped_refptr encoder_thread_task_runner_; -- -- const scoped_refptr child_task_runner_; -- -- // To expose client callbacks from VideoEncodeAccelerator. -- // NOTE: all calls to these objects *MUST* be executed on -- // child_task_runner_. -- std::unique_ptr> client_ptr_factory_; -- base::WeakPtr client_; -- -- // WeakPtr to post from the encoder thread back to the ChildThread, as it may -- // outlive this. Posting from the ChildThread using base::Unretained(this) -- // to the encoder thread is safe, because |this| always outlives the encoder -- // thread (it's a member of this class). -- base::WeakPtr weak_this_; -- -- // The completion callback of the Flush() function. -- FlushCallback flush_callback_; -- -- base::WeakPtrFactory weak_this_ptr_factory_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiVideoEncodeAccelerator); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VAAPI_VIDEO_ENCODE_ACCELERATOR_H_ ---- a/media/gpu/vaapi_wrapper.cc -+++ /dev/null -@@ -1,1372 +0,0 @@ --// Copyright 2013 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. -- --#include "media/gpu/vaapi_wrapper.h" -- --#include --#include -- --#include --#include --#include --#include -- --#include "base/bind.h" --#include "base/callback_helpers.h" --#include "base/environment.h" --#include "base/logging.h" --#include "base/macros.h" --#include "base/numerics/safe_conversions.h" --#include "base/stl_util.h" --#include "base/sys_info.h" --#include "build/build_config.h" -- --// Auto-generated for dlopen libva libraries --#include "media/gpu/vaapi/va_stubs.h" -- --#include "media/gpu/vaapi/vaapi_picture.h" --#include "third_party/libyuv/include/libyuv.h" --#include "ui/gfx/buffer_format_util.h" --#include "ui/gfx/native_pixmap.h" --#include "ui/gl/gl_bindings.h" --#include "ui/gl/gl_implementation.h" -- --#if defined(USE_X11) --#include --#include "ui/gfx/x/x11_types.h" // nogncheck --#endif -- --#if defined(USE_OZONE) --#include "ui/ozone/public/ozone_platform.h" --#include "ui/ozone/public/surface_factory_ozone.h" --#endif -- --using media_gpu_vaapi::kModuleVa; --using media_gpu_vaapi::kModuleVa_drm; --#if defined(USE_X11) --using media_gpu_vaapi::kModuleVa_x11; --#endif --using media_gpu_vaapi::InitializeStubs; --using media_gpu_vaapi::StubPathMap; -- --#define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ -- do { \ -- LOG(ERROR) << err_msg << " VA error: " << vaErrorStr(va_error); \ -- report_error_to_uma_cb_.Run(); \ -- } while (0) -- --#define VA_LOG_ON_ERROR(va_error, err_msg) \ -- do { \ -- if ((va_error) != VA_STATUS_SUCCESS) \ -- LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ -- } while (0) -- --#define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ -- do { \ -- if ((va_error) != VA_STATUS_SUCCESS) { \ -- LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ -- return (ret); \ -- } \ -- } while (0) -- --namespace { -- --uint32_t BufferFormatToVAFourCC(gfx::BufferFormat fmt) { -- switch (fmt) { -- case gfx::BufferFormat::BGRX_8888: -- return VA_FOURCC_BGRX; -- case gfx::BufferFormat::BGRA_8888: -- return VA_FOURCC_BGRA; -- case gfx::BufferFormat::RGBX_8888: -- return VA_FOURCC_RGBX; -- case gfx::BufferFormat::UYVY_422: -- return VA_FOURCC_UYVY; -- case gfx::BufferFormat::YVU_420: -- return VA_FOURCC_YV12; -- default: -- NOTREACHED(); -- return 0; -- } --} -- --uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt) { -- switch (fmt) { -- case gfx::BufferFormat::UYVY_422: -- return VA_RT_FORMAT_YUV422; -- case gfx::BufferFormat::BGRX_8888: -- case gfx::BufferFormat::BGRA_8888: -- case gfx::BufferFormat::RGBX_8888: -- return VA_RT_FORMAT_RGB32; -- case gfx::BufferFormat::YVU_420: -- return VA_RT_FORMAT_YUV420; -- default: -- NOTREACHED(); -- return 0; -- } --} -- --} // namespace -- --namespace media { -- --namespace { -- --// Maximum framerate of encoded profile. This value is an arbitary limit --// and not taken from HW documentation. --const int kMaxEncoderFramerate = 30; -- --// Attributes required for encode. This only applies to video encode, not JPEG --// encode. --static const VAConfigAttrib kVideoEncodeVAConfigAttribs[] = { -- {VAConfigAttribRateControl, VA_RC_CBR}, -- {VAConfigAttribEncPackedHeaders, -- VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE}, --}; -- --// A map between VideoCodecProfile and VAProfile. --static const struct { -- VideoCodecProfile profile; -- VAProfile va_profile; --} kProfileMap[] = { -- {H264PROFILE_BASELINE, VAProfileH264Baseline}, -- {H264PROFILE_MAIN, VAProfileH264Main}, -- // TODO(posciak): See if we can/want to support other variants of -- // H264PROFILE_HIGH*. -- {H264PROFILE_HIGH, VAProfileH264High}, -- {VP8PROFILE_ANY, VAProfileVP8Version0_3}, -- {VP9PROFILE_PROFILE0, VAProfileVP9Profile0}, -- {VP9PROFILE_PROFILE1, VAProfileVP9Profile1}, -- {VP9PROFILE_PROFILE2, VAProfileVP9Profile2}, -- {VP9PROFILE_PROFILE3, VAProfileVP9Profile3}, --}; -- --// This class is a wrapper around its |va_display_| (and its associated --// |va_lock_|) to guarantee mutual exclusion and singleton behaviour. --class VADisplayState { -- public: -- static VADisplayState* Get(); -- -- // Initialize static data before sandbox is enabled. -- static void PreSandboxInitialization(); -- -- VADisplayState(); -- ~VADisplayState() = delete; -- -- // |va_lock_| must be held on entry. -- bool Initialize(); -- void Deinitialize(VAStatus* status); -- -- base::Lock* va_lock() { return &va_lock_; } -- VADisplay va_display() const { return va_display_; } -- -- void SetDrmFd(base::PlatformFile fd) { drm_fd_.reset(HANDLE_EINTR(dup(fd))); } -- -- private: -- // Returns false on init failure. -- static bool PostSandboxInitialization(); -- -- // Protected by |va_lock_|. -- int refcount_; -- -- // Libva is not thread safe, so we have to do locking for it ourselves. -- // This lock is to be taken for the duration of all VA-API calls and for -- // the entire job submission sequence in ExecuteAndDestroyPendingBuffers(). -- base::Lock va_lock_; -- -- // Drm fd used to obtain access to the driver interface by VA. -- base::ScopedFD drm_fd_; -- -- // The VADisplay handle. -- VADisplay va_display_; -- -- // True if vaInitialize() has been called successfully. -- bool va_initialized_; --}; -- --// static --VADisplayState* VADisplayState::Get() { -- static VADisplayState* display_state = new VADisplayState(); -- return display_state; --} -- --// static --void VADisplayState::PreSandboxInitialization() { -- const char kDriRenderNode0Path[] = "/dev/dri/renderD128"; -- base::File drm_file = base::File( -- base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path), -- base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); -- if (drm_file.IsValid()) -- VADisplayState::Get()->SetDrmFd(drm_file.GetPlatformFile()); --} -- --// static --bool VADisplayState::PostSandboxInitialization() { -- const std::string va_suffix(std::to_string(VA_MAJOR_VERSION + 1)); -- StubPathMap paths; -- -- paths[kModuleVa].push_back(std::string("libva.so.") + va_suffix); -- paths[kModuleVa_drm].push_back(std::string("libva-drm.so.") + va_suffix); --#if defined(USE_X11) -- // libva-x11 does not exist on libva >= 2 -- if (VA_MAJOR_VERSION == 0) -- paths[kModuleVa_x11].push_back("libva-x11.so.1"); --#endif -- -- const bool success = InitializeStubs(paths); -- if (!success) { -- static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; --#if defined(OS_CHROMEOS) -- // When Chrome runs on Linux with target_os="chromeos", do not log error -- // message without VAAPI libraries. -- LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS()) << kErrorMsg; --#else -- DVLOG(1) << kErrorMsg; --#endif -- } -- return success; --} -- --VADisplayState::VADisplayState() -- : refcount_(0), va_display_(nullptr), va_initialized_(false) {} -- --bool VADisplayState::Initialize() { -- va_lock_.AssertAcquired(); -- -- static bool result = PostSandboxInitialization(); -- if (!result) -- return false; -- -- if (refcount_++ > 0) -- return true; -- -- switch (gl::GetGLImplementation()) { -- case gl::kGLImplementationEGLGLES2: -- va_display_ = vaGetDisplayDRM(drm_fd_.get()); -- break; -- case gl::kGLImplementationDesktopGL: --#if defined(USE_X11) -- va_display_ = vaGetDisplay(gfx::GetXDisplay()); --#else -- LOG(WARNING) << "HW video decode acceleration not available without " -- "DesktopGL (GLX)."; --#endif // USE_X11 -- break; -- // Cannot infer platform from GL, try all available displays -- case gl::kGLImplementationNone: --#if defined(USE_X11) -- va_display_ = vaGetDisplay(gfx::GetXDisplay()); -- if (vaDisplayIsValid(va_display_)) -- break; --#endif // USE_X11 -- va_display_ = vaGetDisplayDRM(drm_fd_.get()); -- break; -- -- default: -- LOG(WARNING) << "HW video decode acceleration not available for " -- << gl::GetGLImplementationName(gl::GetGLImplementation()); -- return false; -- } -- -- if (!vaDisplayIsValid(va_display_)) { -- LOG(ERROR) << "Could not get a valid VA display"; -- return false; -- } -- -- // Set VA logging level to enable error messages, unless already set -- constexpr char libva_log_level_env[] = "LIBVA_MESSAGING_LEVEL"; -- std::unique_ptr env(base::Environment::Create()); -- if (!env->HasVar(libva_log_level_env)) -- env->SetVar(libva_log_level_env, "1"); -- -- // The VAAPI version. -- int major_version, minor_version; -- VAStatus va_res = vaInitialize(va_display_, &major_version, &minor_version); -- if (va_res != VA_STATUS_SUCCESS) { -- LOG(ERROR) << "vaInitialize failed: " << vaErrorStr(va_res); -- return false; -- } -- -- va_initialized_ = true; -- DVLOG(1) << "VAAPI version: " << major_version << "." << minor_version; -- -- if (major_version != VA_MAJOR_VERSION || minor_version != VA_MINOR_VERSION) { -- LOG(ERROR) << "This build of Chromium requires VA-API version " -- << VA_MAJOR_VERSION << "." << VA_MINOR_VERSION -- << ", system version: " << major_version << "." << minor_version; -- return false; -- } -- return true; --} -- --void VADisplayState::Deinitialize(VAStatus* status) { -- va_lock_.AssertAcquired(); -- if (--refcount_ > 0) -- return; -- -- // Must check if vaInitialize completed successfully, to work around a bug in -- // libva. The bug was fixed upstream: -- // http://lists.freedesktop.org/archives/libva/2013-July/001807.html -- // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once -- // the fix has rolled out sufficiently. -- if (va_initialized_ && va_display_) -- *status = vaTerminate(va_display_); -- va_initialized_ = false; -- va_display_ = nullptr; --} -- --static std::vector GetRequiredAttribs( -- VaapiWrapper::CodecMode mode, -- VAProfile profile) { -- std::vector required_attribs; -- // VAConfigAttribRTFormat is common to both encode and decode |mode|s. -- if (profile == VAProfileVP9Profile2 || profile == VAProfileVP9Profile3) { -- required_attribs.push_back( -- {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420_10BPP}); -- } else { -- required_attribs.push_back({VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}); -- } -- if (mode == VaapiWrapper::kEncode && profile != VAProfileJPEGBaseline) { -- required_attribs.insert( -- required_attribs.end(), kVideoEncodeVAConfigAttribs, -- kVideoEncodeVAConfigAttribs + arraysize(kVideoEncodeVAConfigAttribs)); -- } -- return required_attribs; --} -- --static VAEntrypoint GetVaEntryPoint(VaapiWrapper::CodecMode mode, -- VAProfile profile) { -- switch (mode) { -- case VaapiWrapper::kDecode: -- return VAEntrypointVLD; -- case VaapiWrapper::kEncode: -- if (profile == VAProfileJPEGBaseline) -- return VAEntrypointEncPicture; -- else -- return VAEntrypointEncSlice; -- case VaapiWrapper::kCodecModeMax: -- NOTREACHED(); -- return VAEntrypointVLD; -- } --} -- --// This class encapsulates reading and giving access to the list of supported --// ProfileInfo entries, in a singleton way. --class VASupportedProfiles { -- public: -- struct ProfileInfo { -- VAProfile va_profile; -- gfx::Size max_resolution; -- }; -- static VASupportedProfiles* Get(); -- -- std::vector GetSupportedProfileInfosForCodecMode( -- VaapiWrapper::CodecMode mode); -- -- bool IsProfileSupported(VaapiWrapper::CodecMode mode, VAProfile va_profile); -- -- private: -- VASupportedProfiles(); -- ~VASupportedProfiles() = default; -- -- bool GetSupportedVAProfiles(std::vector* profiles); -- -- // Gets supported profile infos for |mode|. -- std::vector GetSupportedProfileInfosForCodecModeInternal( -- VaapiWrapper::CodecMode mode); -- -- // |va_lock_| must be held on entry in the following _Locked methods. -- -- // Checks if |va_profile| supports |entrypoint| or not. -- bool IsEntrypointSupported_Locked(VAProfile va_profile, -- VAEntrypoint entrypoint); -- // Returns true if |va_profile| for |entrypoint| with |required_attribs| is -- // supported. -- bool AreAttribsSupported_Locked( -- VAProfile va_profile, -- VAEntrypoint entrypoint, -- const std::vector& required_attribs); -- // Gets maximum resolution for |va_profile| and |entrypoint| with -- // |required_attribs|. If return value is true, |resolution| is the maximum -- // resolution. -- bool GetMaxResolution_Locked(VAProfile va_profile, -- VAEntrypoint entrypoint, -- std::vector& required_attribs, -- gfx::Size* resolution); -- -- std::vector supported_profiles_[VaapiWrapper::kCodecModeMax]; -- -- // Pointer to VADisplayState's members |va_lock_| and its |va_display_|. -- base::Lock* va_lock_; -- VADisplay va_display_; -- -- const base::Closure report_error_to_uma_cb_; --}; -- --// static --VASupportedProfiles* VASupportedProfiles::Get() { -- static VASupportedProfiles* profile_infos = new VASupportedProfiles(); -- return profile_infos; --} -- --std::vector --VASupportedProfiles::GetSupportedProfileInfosForCodecMode( -- VaapiWrapper::CodecMode mode) { -- return supported_profiles_[mode]; --} -- --bool VASupportedProfiles::IsProfileSupported(VaapiWrapper::CodecMode mode, -- VAProfile va_profile) { -- for (const auto& profile : supported_profiles_[mode]) { -- if (profile.va_profile == va_profile) -- return true; -- } -- return false; --} -- --VASupportedProfiles::VASupportedProfiles() -- : va_lock_(VADisplayState::Get()->va_lock()), -- va_display_(nullptr), -- report_error_to_uma_cb_(base::Bind(&base::DoNothing)) { -- static_assert(arraysize(supported_profiles_) == VaapiWrapper::kCodecModeMax, -- "The array size of supported profile is incorrect."); -- { -- base::AutoLock auto_lock(*va_lock_); -- if (!VADisplayState::Get()->Initialize()) -- return; -- } -- -- va_display_ = VADisplayState::Get()->va_display(); -- DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; -- -- for (size_t i = 0; i < VaapiWrapper::kCodecModeMax; ++i) { -- supported_profiles_[i] = GetSupportedProfileInfosForCodecModeInternal( -- static_cast(i)); -- } -- -- { -- base::AutoLock auto_lock(*va_lock_); -- VAStatus va_res = VA_STATUS_SUCCESS; -- VADisplayState::Get()->Deinitialize(&va_res); -- VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); -- va_display_ = nullptr; -- } --} -- --std::vector --VASupportedProfiles::GetSupportedProfileInfosForCodecModeInternal( -- VaapiWrapper::CodecMode mode) { -- std::vector supported_profile_infos; -- std::vector va_profiles; -- if (!GetSupportedVAProfiles(&va_profiles)) -- return supported_profile_infos; -- -- base::AutoLock auto_lock(*va_lock_); -- for (const auto& va_profile : va_profiles) { -- VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); -- std::vector required_attribs = -- GetRequiredAttribs(mode, va_profile); -- if (!IsEntrypointSupported_Locked(va_profile, entrypoint)) -- continue; -- if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs)) -- continue; -- ProfileInfo profile_info; -- if (!GetMaxResolution_Locked(va_profile, entrypoint, required_attribs, -- &profile_info.max_resolution)) { -- LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile -- << " and entrypoint " << entrypoint; -- continue; -- } -- profile_info.va_profile = va_profile; -- supported_profile_infos.push_back(profile_info); -- } -- return supported_profile_infos; --} -- --bool VASupportedProfiles::GetSupportedVAProfiles( -- std::vector* profiles) { -- base::AutoLock auto_lock(*va_lock_); -- // Query the driver for supported profiles. -- const int max_profiles = vaMaxNumProfiles(va_display_); -- std::vector supported_profiles( -- base::checked_cast(max_profiles)); -- -- int num_supported_profiles; -- VAStatus va_res = vaQueryConfigProfiles(va_display_, &supported_profiles[0], -- &num_supported_profiles); -- VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false); -- if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) { -- LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_supported_profiles; -- return false; -- } -- -- supported_profiles.resize(base::checked_cast(num_supported_profiles)); -- *profiles = supported_profiles; -- return true; --} -- --bool VASupportedProfiles::IsEntrypointSupported_Locked( -- VAProfile va_profile, -- VAEntrypoint entrypoint) { -- va_lock_->AssertAcquired(); -- // Query the driver for supported entrypoints. -- int max_entrypoints = vaMaxNumEntrypoints(va_display_); -- std::vector supported_entrypoints( -- base::checked_cast(max_entrypoints)); -- -- int num_supported_entrypoints; -- VAStatus va_res = vaQueryConfigEntrypoints(va_display_, va_profile, -- &supported_entrypoints[0], -- &num_supported_entrypoints); -- VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false); -- if (num_supported_entrypoints < 0 || -- num_supported_entrypoints > max_entrypoints) { -- LOG(ERROR) << "vaQueryConfigEntrypoints returned: " -- << num_supported_entrypoints; -- return false; -- } -- -- return base::ContainsValue(supported_entrypoints, entrypoint); --} -- --bool VASupportedProfiles::AreAttribsSupported_Locked( -- VAProfile va_profile, -- VAEntrypoint entrypoint, -- const std::vector& required_attribs) { -- va_lock_->AssertAcquired(); -- // Query the driver for required attributes. -- std::vector attribs = required_attribs; -- for (size_t i = 0; i < required_attribs.size(); ++i) -- attribs[i].value = 0; -- -- VAStatus va_res = vaGetConfigAttributes(va_display_, va_profile, entrypoint, -- &attribs[0], attribs.size()); -- VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); -- -- for (size_t i = 0; i < required_attribs.size(); ++i) { -- if (attribs[i].type != required_attribs[i].type || -- (attribs[i].value & required_attribs[i].value) != -- required_attribs[i].value) { -- DVLOG(1) << "Unsupported value " << required_attribs[i].value -- << " for attribute type " << required_attribs[i].type; -- return false; -- } -- } -- return true; --} -- --bool VASupportedProfiles::GetMaxResolution_Locked( -- VAProfile va_profile, -- VAEntrypoint entrypoint, -- std::vector& required_attribs, -- gfx::Size* resolution) { -- va_lock_->AssertAcquired(); -- VAConfigID va_config_id; -- VAStatus va_res = -- vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], -- required_attribs.size(), &va_config_id); -- VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); -- -- // Calls vaQuerySurfaceAttributes twice. The first time is to get the number -- // of attributes to prepare the space and the second time is to get all -- // attributes. -- unsigned int num_attribs; -- va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, nullptr, -- &num_attribs); -- VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); -- if (!num_attribs) -- return false; -- -- std::vector attrib_list( -- base::checked_cast(num_attribs)); -- -- va_res = vaQuerySurfaceAttributes(va_display_, va_config_id, &attrib_list[0], -- &num_attribs); -- VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); -- -- resolution->SetSize(0, 0); -- for (const auto& attrib : attrib_list) { -- if (attrib.type == VASurfaceAttribMaxWidth) -- resolution->set_width(attrib.value.value.i); -- else if (attrib.type == VASurfaceAttribMaxHeight) -- resolution->set_height(attrib.value.value.i); -- } -- if (resolution->IsEmpty()) { -- LOG(ERROR) << "Wrong codec resolution: " << resolution->ToString(); -- return false; -- } -- return true; --} -- --// Maps VideoCodecProfile enum values to VaProfile values. This function --// includes a workaround for https://crbug.com/345569: if va_profile is h264 --// baseline and it is not supported, we try constrained baseline. --VAProfile ProfileToVAProfile(VideoCodecProfile profile, -- VaapiWrapper::CodecMode mode) { -- VAProfile va_profile = VAProfileNone; -- for (size_t i = 0; i < arraysize(kProfileMap); ++i) { -- if (kProfileMap[i].profile == profile) { -- va_profile = kProfileMap[i].va_profile; -- break; -- } -- } -- if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile) && -- va_profile == VAProfileH264Baseline) { -- // https://crbug.com/345569: ProfileIDToVideoCodecProfile() currently strips -- // the information whether the profile is constrained or not, so we have no -- // way to know here. Try for baseline first, but if it is not supported, -- // try constrained baseline and hope this is what it actually is -- // (which in practice is true for a great majority of cases). -- if (VASupportedProfiles::Get()->IsProfileSupported( -- mode, VAProfileH264ConstrainedBaseline)) { -- va_profile = VAProfileH264ConstrainedBaseline; -- DVLOG(1) << "Fall back to constrained baseline profile."; -- } -- } -- return va_profile; --} -- --void DestroyVAImage(VADisplay va_display, VAImage image) { -- if (image.image_id != VA_INVALID_ID) -- vaDestroyImage(va_display, image.image_id); --} -- --} // namespace -- --VaapiWrapper::VaapiWrapper() -- : va_surface_format_(0), -- va_display_(NULL), -- va_config_id_(VA_INVALID_ID), -- va_context_id_(VA_INVALID_ID), -- va_vpp_config_id_(VA_INVALID_ID), -- va_vpp_context_id_(VA_INVALID_ID), -- va_vpp_buffer_id_(VA_INVALID_ID) { -- va_lock_ = VADisplayState::Get()->va_lock(); --} -- --VaapiWrapper::~VaapiWrapper() { -- DestroyPendingBuffers(); -- DestroyCodedBuffers(); -- DestroySurfaces(); -- DeinitializeVpp(); -- Deinitialize(); --} -- --// static --scoped_refptr VaapiWrapper::Create( -- CodecMode mode, -- VAProfile va_profile, -- const base::Closure& report_error_to_uma_cb) { -- if (!VASupportedProfiles::Get()->IsProfileSupported(mode, va_profile)) { -- DVLOG(1) << "Unsupported va_profile: " << va_profile; -- return nullptr; -- } -- -- scoped_refptr vaapi_wrapper(new VaapiWrapper()); -- if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) { -- if (vaapi_wrapper->Initialize(mode, va_profile)) -- return vaapi_wrapper; -- } -- LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " << va_profile; -- return nullptr; --} -- --// static --scoped_refptr VaapiWrapper::CreateForVideoCodec( -- CodecMode mode, -- VideoCodecProfile profile, -- const base::Closure& report_error_to_uma_cb) { -- VAProfile va_profile = ProfileToVAProfile(profile, mode); -- return Create(mode, va_profile, report_error_to_uma_cb); --} -- --// static --VideoEncodeAccelerator::SupportedProfiles --VaapiWrapper::GetSupportedEncodeProfiles() { -- VideoEncodeAccelerator::SupportedProfiles profiles; -- std::vector encode_profile_infos = -- VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kEncode); -- -- for (size_t i = 0; i < arraysize(kProfileMap); ++i) { -- VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode); -- if (va_profile == VAProfileNone) -- continue; -- for (const auto& profile_info : encode_profile_infos) { -- if (profile_info.va_profile == va_profile) { -- VideoEncodeAccelerator::SupportedProfile profile; -- profile.profile = kProfileMap[i].profile; -- profile.max_resolution = profile_info.max_resolution; -- profile.max_framerate_numerator = kMaxEncoderFramerate; -- profile.max_framerate_denominator = 1; -- profiles.push_back(profile); -- break; -- } -- } -- } -- return profiles; --} -- --// static --VideoDecodeAccelerator::SupportedProfiles --VaapiWrapper::GetSupportedDecodeProfiles() { -- VideoDecodeAccelerator::SupportedProfiles profiles; -- std::vector decode_profile_infos = -- VASupportedProfiles::Get()->GetSupportedProfileInfosForCodecMode(kDecode); -- -- for (size_t i = 0; i < arraysize(kProfileMap); ++i) { -- VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kDecode); -- if (va_profile == VAProfileNone) -- continue; -- for (const auto& profile_info : decode_profile_infos) { -- if (profile_info.va_profile == va_profile) { -- VideoDecodeAccelerator::SupportedProfile profile; -- profile.profile = kProfileMap[i].profile; -- profile.max_resolution = profile_info.max_resolution; -- profile.min_resolution.SetSize(16, 16); -- profiles.push_back(profile); -- break; -- } -- } -- } -- return profiles; --} -- --// static --bool VaapiWrapper::IsJpegDecodeSupported() { -- return VASupportedProfiles::Get()->IsProfileSupported(kDecode, -- VAProfileJPEGBaseline); --} -- --// static --bool VaapiWrapper::IsJpegEncodeSupported() { -- return VASupportedProfiles::Get()->IsProfileSupported(kEncode, -- VAProfileJPEGBaseline); --} -- --void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { -- base::AutoLock auto_lock(*va_lock_); -- VADisplayAttribute item = {VADisplayAttribRenderMode, -- 1, // At least support '_LOCAL_OVERLAY'. -- -1, // The maximum possible support 'ALL'. -- VA_RENDER_MODE_LOCAL_GPU, -- VA_DISPLAY_ATTRIB_SETTABLE}; -- -- VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1); -- if (va_res != VA_STATUS_SUCCESS) -- DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; --} -- --bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) { -- report_error_to_uma_cb_ = report_error_to_uma_cb; -- { -- base::AutoLock auto_lock(*va_lock_); -- if (!VADisplayState::Get()->Initialize()) -- return false; -- } -- -- va_display_ = VADisplayState::Get()->va_display(); -- DCHECK(va_display_) << "VADisplayState hasn't been properly Initialize()d"; -- return true; --} -- --bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) { -- TryToSetVADisplayAttributeToLocalGPU(); -- -- VAEntrypoint entrypoint = GetVaEntryPoint(mode, va_profile); -- std::vector required_attribs = -- GetRequiredAttribs(mode, va_profile); -- base::AutoLock auto_lock(*va_lock_); -- VAStatus va_res = -- vaCreateConfig(va_display_, va_profile, entrypoint, &required_attribs[0], -- required_attribs.size(), &va_config_id_); -- VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); -- -- return true; --} -- --void VaapiWrapper::Deinitialize() { -- base::AutoLock auto_lock(*va_lock_); -- -- if (va_config_id_ != VA_INVALID_ID) { -- VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); -- VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); -- } -- -- VAStatus va_res = VA_STATUS_SUCCESS; -- VADisplayState::Get()->Deinitialize(&va_res); -- VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); -- -- va_config_id_ = VA_INVALID_ID; -- va_display_ = NULL; --} -- --bool VaapiWrapper::CreateSurfaces(unsigned int va_format, -- const gfx::Size& size, -- size_t num_surfaces, -- std::vector* va_surfaces) { -- base::AutoLock auto_lock(*va_lock_); -- DVLOG(2) << "Creating " << num_surfaces << " surfaces"; -- -- DCHECK(va_surfaces->empty()); -- DCHECK(va_surface_ids_.empty()); -- DCHECK_EQ(va_surface_format_, 0u); -- va_surface_ids_.resize(num_surfaces); -- -- // Allocate surfaces in driver. -- VAStatus va_res = -- vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), -- &va_surface_ids_[0], va_surface_ids_.size(), NULL, 0); -- -- VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); -- if (va_res != VA_STATUS_SUCCESS) { -- va_surface_ids_.clear(); -- return false; -- } -- -- // And create a context associated with them. -- va_res = vaCreateContext(va_display_, va_config_id_, size.width(), -- size.height(), VA_PROGRESSIVE, &va_surface_ids_[0], -- va_surface_ids_.size(), &va_context_id_); -- -- VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); -- if (va_res != VA_STATUS_SUCCESS) { -- DestroySurfaces_Locked(); -- return false; -- } -- -- *va_surfaces = va_surface_ids_; -- va_surface_format_ = va_format; -- return true; --} -- --void VaapiWrapper::DestroySurfaces() { -- base::AutoLock auto_lock(*va_lock_); -- DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; -- -- DestroySurfaces_Locked(); --} -- --void VaapiWrapper::DestroySurfaces_Locked() { -- va_lock_->AssertAcquired(); -- -- if (va_context_id_ != VA_INVALID_ID) { -- VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); -- VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); -- } -- -- if (!va_surface_ids_.empty()) { -- VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0], -- va_surface_ids_.size()); -- VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); -- } -- -- va_surface_ids_.clear(); -- va_context_id_ = VA_INVALID_ID; -- va_surface_format_ = 0; --} -- --scoped_refptr VaapiWrapper::CreateUnownedSurface( -- unsigned int va_format, -- const gfx::Size& size, -- const std::vector& va_attribs) { -- base::AutoLock auto_lock(*va_lock_); -- -- std::vector attribs(va_attribs); -- VASurfaceID va_surface_id; -- VAStatus va_res = -- vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), -- &va_surface_id, 1, &attribs[0], attribs.size()); -- -- scoped_refptr va_surface; -- VA_SUCCESS_OR_RETURN(va_res, "Failed to create unowned VASurface", -- va_surface); -- -- // This is safe to use Unretained() here, because the VDA takes care -- // of the destruction order. All the surfaces will be destroyed -- // before VaapiWrapper. -- va_surface = new VASurface( -- va_surface_id, size, va_format, -- base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this))); -- -- return va_surface; --} -- --scoped_refptr VaapiWrapper::CreateVASurfaceForPixmap( -- const scoped_refptr& pixmap) { -- // Create a VASurface for a NativePixmap by importing the underlying dmabufs. -- VASurfaceAttribExternalBuffers va_attrib_extbuf; -- memset(&va_attrib_extbuf, 0, sizeof(va_attrib_extbuf)); -- -- va_attrib_extbuf.pixel_format = -- BufferFormatToVAFourCC(pixmap->GetBufferFormat()); -- gfx::Size size = pixmap->GetBufferSize(); -- va_attrib_extbuf.width = size.width(); -- va_attrib_extbuf.height = size.height(); -- -- size_t num_fds = pixmap->GetDmaBufFdCount(); -- size_t num_planes = -- gfx::NumberOfPlanesForBufferFormat(pixmap->GetBufferFormat()); -- if (num_fds == 0 || num_fds > num_planes) { -- LOG(ERROR) << "Invalid number of dmabuf fds: " << num_fds -- << " , planes: " << num_planes; -- return nullptr; -- } -- -- for (size_t i = 0; i < num_planes; ++i) { -- va_attrib_extbuf.pitches[i] = pixmap->GetDmaBufPitch(i); -- va_attrib_extbuf.offsets[i] = pixmap->GetDmaBufOffset(i); -- DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i] -- << " offset: " << va_attrib_extbuf.offsets[i]; -- } -- va_attrib_extbuf.num_planes = num_planes; -- -- std::vector fds(num_fds); -- for (size_t i = 0; i < num_fds; ++i) { -- int dmabuf_fd = pixmap->GetDmaBufFd(i); -- if (dmabuf_fd < 0) { -- LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap"; -- return nullptr; -- } -- fds[i] = dmabuf_fd; -- } -- va_attrib_extbuf.buffers = fds.data(); -- va_attrib_extbuf.num_buffers = fds.size(); -- -- va_attrib_extbuf.flags = 0; -- va_attrib_extbuf.private_data = NULL; -- -- std::vector va_attribs(2); -- -- va_attribs[0].type = VASurfaceAttribMemoryType; -- va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; -- va_attribs[0].value.type = VAGenericValueTypeInteger; -- va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; -- -- va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; -- va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; -- va_attribs[1].value.type = VAGenericValueTypePointer; -- va_attribs[1].value.value.p = &va_attrib_extbuf; -- -- scoped_refptr va_surface = CreateUnownedSurface( -- BufferFormatToVARTFormat(pixmap->GetBufferFormat()), size, va_attribs); -- if (!va_surface) { -- LOG(ERROR) << "Failed to create VASurface for an Ozone NativePixmap"; -- return nullptr; -- } -- -- return va_surface; --} -- --void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id) { -- base::AutoLock auto_lock(*va_lock_); -- -- VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1); -- VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces on surface failed"); --} -- --bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, -- size_t size, -- void* buffer) { -- base::AutoLock auto_lock(*va_lock_); -- -- VABufferID buffer_id; -- VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, va_buffer_type, -- size, 1, buffer, &buffer_id); -- VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); -- -- switch (va_buffer_type) { -- case VASliceParameterBufferType: -- case VASliceDataBufferType: -- case VAEncSliceParameterBufferType: -- pending_slice_bufs_.push_back(buffer_id); -- break; -- -- default: -- pending_va_bufs_.push_back(buffer_id); -- break; -- } -- -- return true; --} -- --bool VaapiWrapper::SubmitVAEncMiscParamBuffer( -- VAEncMiscParameterType misc_param_type, -- size_t size, -- void* buffer) { -- base::AutoLock auto_lock(*va_lock_); -- -- VABufferID buffer_id; -- VAStatus va_res = vaCreateBuffer( -- va_display_, va_context_id_, VAEncMiscParameterBufferType, -- sizeof(VAEncMiscParameterBuffer) + size, 1, NULL, &buffer_id); -- VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); -- -- void* data_ptr = NULL; -- va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr); -- VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); -- if (va_res != VA_STATUS_SUCCESS) { -- vaDestroyBuffer(va_display_, buffer_id); -- return false; -- } -- -- DCHECK(data_ptr); -- -- VAEncMiscParameterBuffer* misc_param = -- reinterpret_cast(data_ptr); -- misc_param->type = misc_param_type; -- memcpy(misc_param->data, buffer, size); -- va_res = vaUnmapBuffer(va_display_, buffer_id); -- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -- -- pending_va_bufs_.push_back(buffer_id); -- return true; --} -- --void VaapiWrapper::DestroyPendingBuffers() { -- base::AutoLock auto_lock(*va_lock_); -- -- for (const auto& pending_va_buf : pending_va_bufs_) { -- VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf); -- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -- } -- -- for (const auto& pending_slice_buf : pending_slice_bufs_) { -- VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf); -- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -- } -- -- pending_va_bufs_.clear(); -- pending_slice_bufs_.clear(); --} -- --bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) { -- base::AutoLock auto_lock(*va_lock_); -- VAStatus va_res = -- vaCreateBuffer(va_display_, va_context_id_, VAEncCodedBufferType, size, 1, -- NULL, buffer_id); -- VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false); -- -- const auto is_new_entry = coded_buffers_.insert(*buffer_id).second; -- DCHECK(is_new_entry); -- return true; --} -- --void VaapiWrapper::DestroyCodedBuffers() { -- base::AutoLock auto_lock(*va_lock_); -- -- for (std::set::const_iterator iter = coded_buffers_.begin(); -- iter != coded_buffers_.end(); ++iter) { -- VAStatus va_res = vaDestroyBuffer(va_display_, *iter); -- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -- } -- -- coded_buffers_.clear(); --} -- --bool VaapiWrapper::Execute(VASurfaceID va_surface_id) { -- base::AutoLock auto_lock(*va_lock_); -- -- DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); -- DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); -- DVLOG(4) << "Target VA surface " << va_surface_id; -- -- // Get ready to execute for given surface. -- VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, va_surface_id); -- VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); -- -- if (pending_va_bufs_.size() > 0) { -- // Commit parameter and slice buffers. -- va_res = vaRenderPicture(va_display_, va_context_id_, &pending_va_bufs_[0], -- pending_va_bufs_.size()); -- VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); -- } -- -- if (pending_slice_bufs_.size() > 0) { -- va_res = -- vaRenderPicture(va_display_, va_context_id_, &pending_slice_bufs_[0], -- pending_slice_bufs_.size()); -- VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); -- } -- -- // Instruct HW codec to start processing committed buffers. -- // Does not block and the job is not finished after this returns. -- va_res = vaEndPicture(va_display_, va_context_id_); -- VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); -- -- return true; --} -- --bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { -- bool result = Execute(va_surface_id); -- DestroyPendingBuffers(); -- return result; --} -- --#if defined(USE_X11) --bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, -- Pixmap x_pixmap, -- gfx::Size dest_size) { -- base::AutoLock auto_lock(*va_lock_); -- -- VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); -- VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); -- -- // Put the data into an X Pixmap. -- va_res = vaPutSurface(va_display_, -- va_surface_id, -- x_pixmap, -- 0, 0, dest_size.width(), dest_size.height(), -- 0, 0, dest_size.width(), dest_size.height(), -- NULL, 0, 0); -- VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false); -- return true; --} --#endif // USE_X11 -- --bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id, -- VAImageFormat* format, -- const gfx::Size& size, -- VAImage* image, -- void** mem) { -- base::AutoLock auto_lock(*va_lock_); -- -- VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); -- VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); -- -- va_res = -- vaCreateImage(va_display_, format, size.width(), size.height(), image); -- VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false); -- -- va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(), -- size.height(), image->image_id); -- VA_LOG_ON_ERROR(va_res, "vaGetImage failed"); -- -- if (va_res == VA_STATUS_SUCCESS) { -- // Map the VAImage into memory -- va_res = vaMapBuffer(va_display_, image->buf, mem); -- VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); -- } -- -- if (va_res != VA_STATUS_SUCCESS) { -- va_res = vaDestroyImage(va_display_, image->image_id); -- VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); -- return false; -- } -- -- return true; --} -- --void VaapiWrapper::ReturnVaImage(VAImage* image) { -- base::AutoLock auto_lock(*va_lock_); -- -- VAStatus va_res = vaUnmapBuffer(va_display_, image->buf); -- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -- -- va_res = vaDestroyImage(va_display_, image->image_id); -- VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); --} -- --bool VaapiWrapper::UploadVideoFrameToSurface( -- const scoped_refptr& frame, -- VASurfaceID va_surface_id) { -- base::AutoLock auto_lock(*va_lock_); -- -- VAImage image; -- VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image); -- VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false); -- base::ScopedClosureRunner vaimage_deleter( -- base::Bind(&DestroyVAImage, va_display_, image)); -- -- if (image.format.fourcc != VA_FOURCC_NV12) { -- LOG(ERROR) << "Unsupported image format: " << image.format.fourcc; -- return false; -- } -- -- if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) { -- LOG(ERROR) << "Buffer too small to fit the frame."; -- return false; -- } -- -- void* image_ptr = NULL; -- va_res = vaMapBuffer(va_display_, image.buf, &image_ptr); -- VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); -- DCHECK(image_ptr); -- -- int ret = 0; -- { -- base::AutoUnlock auto_unlock(*va_lock_); -- ret = libyuv::I420ToNV12( -- frame->data(VideoFrame::kYPlane), frame->stride(VideoFrame::kYPlane), -- frame->data(VideoFrame::kUPlane), frame->stride(VideoFrame::kUPlane), -- frame->data(VideoFrame::kVPlane), frame->stride(VideoFrame::kVPlane), -- static_cast(image_ptr) + image.offsets[0], image.pitches[0], -- static_cast(image_ptr) + image.offsets[1], image.pitches[1], -- image.width, image.height); -- } -- -- va_res = vaUnmapBuffer(va_display_, image.buf); -- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -- -- return ret == 0; --} -- --bool VaapiWrapper::DownloadFromCodedBuffer(VABufferID buffer_id, -- VASurfaceID sync_surface_id, -- uint8_t* target_ptr, -- size_t target_size, -- size_t* coded_data_size) { -- base::AutoLock auto_lock(*va_lock_); -- -- VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id); -- VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); -- -- VACodedBufferSegment* buffer_segment = NULL; -- va_res = vaMapBuffer(va_display_, buffer_id, -- reinterpret_cast(&buffer_segment)); -- VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); -- DCHECK(target_ptr); -- -- { -- base::AutoUnlock auto_unlock(*va_lock_); -- *coded_data_size = 0; -- -- while (buffer_segment) { -- DCHECK(buffer_segment->buf); -- -- if (buffer_segment->size > target_size) { -- LOG(ERROR) << "Insufficient output buffer size"; -- break; -- } -- -- memcpy(target_ptr, buffer_segment->buf, buffer_segment->size); -- -- target_ptr += buffer_segment->size; -- *coded_data_size += buffer_segment->size; -- target_size -= buffer_segment->size; -- -- buffer_segment = -- reinterpret_cast(buffer_segment->next); -- } -- } -- -- va_res = vaUnmapBuffer(va_display_, buffer_id); -- VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); -- return buffer_segment == NULL; --} -- --bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id, -- VASurfaceID sync_surface_id, -- uint8_t* target_ptr, -- size_t target_size, -- size_t* coded_data_size) { -- bool result = DownloadFromCodedBuffer(buffer_id, sync_surface_id, target_ptr, -- target_size, coded_data_size); -- -- VAStatus va_res = vaDestroyBuffer(va_display_, buffer_id); -- VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); -- const auto was_found = coded_buffers_.erase(buffer_id); -- DCHECK(was_found); -- -- return result; --} -- --bool VaapiWrapper::BlitSurface( -- const scoped_refptr& va_surface_src, -- const scoped_refptr& va_surface_dest) { -- base::AutoLock auto_lock(*va_lock_); -- -- // Initialize the post processing engine if not already done. -- if (va_vpp_buffer_id_ == VA_INVALID_ID) { -- if (!InitializeVpp_Locked()) -- return false; -- } -- -- VAProcPipelineParameterBuffer* pipeline_param; -- VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_, va_vpp_buffer_id_, -- reinterpret_cast(&pipeline_param)), -- "Couldn't map vpp buffer", false); -- -- memset(pipeline_param, 0, sizeof *pipeline_param); -- const gfx::Size src_size = va_surface_src->size(); -- const gfx::Size dest_size = va_surface_dest->size(); -- -- VARectangle input_region; -- input_region.x = input_region.y = 0; -- input_region.width = src_size.width(); -- input_region.height = src_size.height(); -- pipeline_param->surface_region = &input_region; -- pipeline_param->surface = va_surface_src->id(); -- pipeline_param->surface_color_standard = VAProcColorStandardNone; -- -- VARectangle output_region; -- output_region.x = output_region.y = 0; -- output_region.width = dest_size.width(); -- output_region.height = dest_size.height(); -- pipeline_param->output_region = &output_region; -- pipeline_param->output_background_color = 0xff000000; -- pipeline_param->output_color_standard = VAProcColorStandardNone; -- pipeline_param->filter_flags = VA_FILTER_SCALING_DEFAULT; -- -- VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_, va_vpp_buffer_id_), -- "Couldn't unmap vpp buffer", false); -- -- VA_SUCCESS_OR_RETURN( -- vaBeginPicture(va_display_, va_vpp_context_id_, va_surface_dest->id()), -- "Couldn't begin picture", false); -- -- VA_SUCCESS_OR_RETURN( -- vaRenderPicture(va_display_, va_vpp_context_id_, &va_vpp_buffer_id_, 1), -- "Couldn't render picture", false); -- -- VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_, va_vpp_context_id_), -- "Couldn't end picture", false); -- -- return true; --} -- --bool VaapiWrapper::InitializeVpp_Locked() { -- va_lock_->AssertAcquired(); -- -- VA_SUCCESS_OR_RETURN( -- vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0, -- &va_vpp_config_id_), -- "Couldn't create config", false); -- -- // The size of the picture for the context is irrelevant in the case -- // of the VPP, just passing 1x1. -- VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_, va_vpp_config_id_, 1, 1, 0, -- NULL, 0, &va_vpp_context_id_), -- "Couldn't create context", false); -- -- VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_, va_vpp_context_id_, -- VAProcPipelineParameterBufferType, -- sizeof(VAProcPipelineParameterBuffer), 1, -- NULL, &va_vpp_buffer_id_), -- "Couldn't create buffer", false); -- -- return true; --} -- --void VaapiWrapper::DeinitializeVpp() { -- base::AutoLock auto_lock(*va_lock_); -- -- if (va_vpp_buffer_id_ != VA_INVALID_ID) { -- vaDestroyBuffer(va_display_, va_vpp_buffer_id_); -- va_vpp_buffer_id_ = VA_INVALID_ID; -- } -- if (va_vpp_context_id_ != VA_INVALID_ID) { -- vaDestroyContext(va_display_, va_vpp_context_id_); -- va_vpp_context_id_ = VA_INVALID_ID; -- } -- if (va_vpp_config_id_ != VA_INVALID_ID) { -- vaDestroyConfig(va_display_, va_vpp_config_id_); -- va_vpp_config_id_ = VA_INVALID_ID; -- } --} -- --// static --void VaapiWrapper::PreSandboxInitialization() { -- VADisplayState::PreSandboxInitialization(); --} -- --} // namespace media ---- a/media/gpu/vaapi_wrapper.h -+++ /dev/null -@@ -1,288 +0,0 @@ --// Copyright 2013 The Chromium Authors. All rights reserved. --// Use of this source code is governed by a BSD-style license that can be --// found in the LICENSE file. --// --// This file contains an implementation of VaapiWrapper, used by --// VaapiVideoDecodeAccelerator and VaapiH264Decoder for decode, --// and VaapiVideoEncodeAccelerator for encode, to interface --// with libva (VA-API library for hardware video codec). -- --#ifndef MEDIA_GPU_VAAPI_WRAPPER_H_ --#define MEDIA_GPU_VAAPI_WRAPPER_H_ -- --#include --#include -- --#include --#include -- --#include -- --#include "base/files/file.h" --#include "base/macros.h" --#include "base/memory/ref_counted.h" --#include "base/synchronization/lock.h" --#include "media/base/video_decoder_config.h" --#include "media/base/video_frame.h" --#include "media/gpu/media_gpu_export.h" --#include "media/gpu/va_surface.h" --#include "media/video/jpeg_decode_accelerator.h" --#include "media/video/video_decode_accelerator.h" --#include "media/video/video_encode_accelerator.h" --#include "ui/gfx/geometry/size.h" -- --#if defined(USE_X11) --#include "ui/gfx/x/x11.h" --#endif // USE_X11 -- --namespace gfx { --class NativePixmap; --} -- --namespace media { -- --// This class handles VA-API calls and ensures proper locking of VA-API calls --// to libva, the userspace shim to the HW codec driver. libva is not --// thread-safe, so we have to perform locking ourselves. This class is fully --// synchronous and its methods can be called from any thread and may wait on --// the va_lock_ while other, concurrent calls run. --// --// This class is responsible for managing VAAPI connection, contexts and state. --// It is also responsible for managing and freeing VABuffers (not VASurfaces), --// which are used to queue parameters and slice data to the HW codec, --// as well as underlying memory for VASurfaces themselves. --class MEDIA_GPU_EXPORT VaapiWrapper -- : public base::RefCountedThreadSafe { -- public: -- enum CodecMode { -- kDecode, -- kEncode, -- kCodecModeMax, -- }; -- -- // Return an instance of VaapiWrapper initialized for |va_profile| and -- // |mode|. |report_error_to_uma_cb| will be called independently from -- // reporting errors to clients via method return values. -- static scoped_refptr Create( -- CodecMode mode, -- VAProfile va_profile, -- const base::Closure& report_error_to_uma_cb); -- -- // Create VaapiWrapper for VideoCodecProfile. It maps VideoCodecProfile -- // |profile| to VAProfile. -- // |report_error_to_uma_cb| will be called independently from reporting -- // errors to clients via method return values. -- static scoped_refptr CreateForVideoCodec( -- CodecMode mode, -- VideoCodecProfile profile, -- const base::Closure& report_error_to_uma_cb); -- -- // Return the supported video encode profiles. -- static VideoEncodeAccelerator::SupportedProfiles GetSupportedEncodeProfiles(); -- -- // Return the supported video decode profiles. -- static VideoDecodeAccelerator::SupportedProfiles GetSupportedDecodeProfiles(); -- -- // Return true when JPEG decode is supported. -- static bool IsJpegDecodeSupported(); -- -- // Return true when JPEG encode is supported. -- static bool IsJpegEncodeSupported(); -- -- // Create |num_surfaces| backing surfaces in driver for VASurfaces of -- // |va_format|, each of size |size|. Returns true when successful, with the -- // created IDs in |va_surfaces| to be managed and later wrapped in -- // VASurfaces. -- // The client must DestroySurfaces() each time before calling this method -- // again to free the allocated surfaces first, but is not required to do so -- // at destruction time, as this will be done automatically from -- // the destructor. -- virtual bool CreateSurfaces(unsigned int va_format, -- const gfx::Size& size, -- size_t num_surfaces, -- std::vector* va_surfaces); -- -- // Free all memory allocated in CreateSurfaces. -- virtual void DestroySurfaces(); -- -- // Create a VASurface of |va_format|, |size| and using |va_attribs| -- // attributes. The ownership of the surface is transferred to the -- // caller. It differs from surfaces created using CreateSurfaces(), -- // where VaapiWrapper is the owner of the surfaces. -- scoped_refptr CreateUnownedSurface( -- unsigned int va_format, -- const gfx::Size& size, -- const std::vector& va_attribs); -- -- // Create a VASurface for |pixmap|. The ownership of the surface is -- // transferred to the caller. It differs from surfaces created using -- // CreateSurfaces(), where VaapiWrapper is the owner of the surfaces. -- scoped_refptr CreateVASurfaceForPixmap( -- const scoped_refptr& pixmap); -- -- // Submit parameters or slice data of |va_buffer_type|, copying them from -- // |buffer| of size |size|, into HW codec. The data in |buffer| is no -- // longer needed and can be freed after this method returns. -- // Data submitted via this method awaits in the HW codec until -- // ExecuteAndDestroyPendingBuffers() is called to execute or -- // DestroyPendingBuffers() is used to cancel a pending job. -- bool SubmitBuffer(VABufferType va_buffer_type, size_t size, void* buffer); -- -- // Submit a VAEncMiscParameterBuffer of given |misc_param_type|, copying its -- // data from |buffer| of size |size|, into HW codec. The data in |buffer| is -- // no longer needed and can be freed after this method returns. -- // Data submitted via this method awaits in the HW codec until -- // ExecuteAndDestroyPendingBuffers() is called to execute or -- // DestroyPendingBuffers() is used to cancel a pending job. -- bool SubmitVAEncMiscParamBuffer(VAEncMiscParameterType misc_param_type, -- size_t size, -- void* buffer); -- -- // Cancel and destroy all buffers queued to the HW codec via SubmitBuffer(). -- // Useful when a pending job is to be cancelled (on reset or error). -- void DestroyPendingBuffers(); -- -- // Execute job in hardware on target |va_surface_id| and destroy pending -- // buffers. Return false if Execute() fails. -- bool ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id); -- --#if defined(USE_X11) -- // Put data from |va_surface_id| into |x_pixmap| of size -- // |dest_size|, converting/scaling to it. -- bool PutSurfaceIntoPixmap(VASurfaceID va_surface_id, -- Pixmap x_pixmap, -- gfx::Size dest_size); --#endif // USE_X11 -- -- // Get a VAImage from a VASurface |va_surface_id| and map it into memory with -- // given |format| and |size|. The output is |image| and the mapped memory is -- // |mem|. If |format| doesn't equal to the internal format, the underlying -- // implementation will do format conversion if supported. |size| should be -- // smaller than or equal to the surface. If |size| is smaller, the image will -- // be cropped. The VAImage should be released using the ReturnVaImage -- // function. Returns true when successful. -- bool GetVaImage(VASurfaceID va_surface_id, -- VAImageFormat* format, -- const gfx::Size& size, -- VAImage* image, -- void** mem); -- -- // Release the VAImage (and the associated memory mapping) obtained from -- // GetVaImage(). -- void ReturnVaImage(VAImage* image); -- -- // Upload contents of |frame| into |va_surface_id| for encode. -- bool UploadVideoFrameToSurface(const scoped_refptr& frame, -- VASurfaceID va_surface_id); -- -- // Create a buffer of |size| bytes to be used as encode output. -- bool CreateCodedBuffer(size_t size, VABufferID* buffer_id); -- -- // Download the contents of the buffer with given |buffer_id| into a buffer of -- // size |target_size|, pointed to by |target_ptr|. The number of bytes -- // downloaded will be returned in |coded_data_size|. |sync_surface_id| will -- // be used as a sync point, i.e. it will have to become idle before starting -- // the download. |sync_surface_id| should be the source surface passed -- // to the encode job. -- bool DownloadFromCodedBuffer(VABufferID buffer_id, -- VASurfaceID sync_surface_id, -- uint8_t* target_ptr, -- size_t target_size, -- size_t* coded_data_size); -- -- // See DownloadFromCodedBuffer() for details. After downloading, it deletes -- // the VA buffer with |buffer_id|. -- bool DownloadAndDestroyCodedBuffer(VABufferID buffer_id, -- VASurfaceID sync_surface_id, -- uint8_t* target_ptr, -- size_t target_size, -- size_t* coded_data_size); -- -- // Destroy all previously-allocated (and not yet destroyed) coded buffers. -- void DestroyCodedBuffers(); -- -- // Blits a VASurface |va_surface_src| into another VASurface -- // |va_surface_dest| applying pixel format conversion and scaling -- // if needed. -- bool BlitSurface(const scoped_refptr& va_surface_src, -- const scoped_refptr& va_surface_dest); -- -- // Initialize static data before sandbox is enabled. -- static void PreSandboxInitialization(); -- -- // Get the created surfaces format. -- unsigned int va_surface_format() const { return va_surface_format_; } -- -- protected: -- VaapiWrapper(); -- virtual ~VaapiWrapper(); -- -- private: -- friend class base::RefCountedThreadSafe; -- -- bool Initialize(CodecMode mode, VAProfile va_profile); -- void Deinitialize(); -- bool VaInitialize(const base::Closure& report_error_to_uma_cb); -- -- // Free all memory allocated in CreateSurfaces. -- void DestroySurfaces_Locked(); -- // Destroys a |va_surface| created using CreateUnownedSurface. -- void DestroyUnownedSurface(VASurfaceID va_surface_id); -- -- // Initialize the video post processing context with the |size| of -- // the input pictures to be processed. -- bool InitializeVpp_Locked(); -- -- // Deinitialize the video post processing context. -- void DeinitializeVpp(); -- -- // Execute pending job in hardware and destroy pending buffers. Return false -- // if vaapi driver refuses to accept parameter or slice buffers submitted -- // by client, or if execution fails in hardware. -- bool Execute(VASurfaceID va_surface_id); -- -- // Attempt to set render mode to "render to texture.". Failure is non-fatal. -- void TryToSetVADisplayAttributeToLocalGPU(); -- -- // Pointer to VADisplayState's member |va_lock_|. Guaranteed to be valid for -- // the lifetime of VaapiWrapper. -- base::Lock* va_lock_; -- -- // Allocated ids for VASurfaces. -- std::vector va_surface_ids_; -- -- // VA format of surfaces with va_surface_ids_. -- unsigned int va_surface_format_; -- -- // VA handles. -- // All valid after successful Initialize() and until Deinitialize(). -- VADisplay va_display_; -- VAConfigID va_config_id_; -- // Created for the current set of va_surface_ids_ in CreateSurfaces() and -- // valid until DestroySurfaces(). -- VAContextID va_context_id_; -- -- // Data queued up for HW codec, to be committed on next execution. -- std::vector pending_slice_bufs_; -- std::vector pending_va_bufs_; -- -- // Bitstream buffers for encode. -- std::set coded_buffers_; -- -- // Called to report codec errors to UMA. Errors to clients are reported via -- // return values from public methods. -- base::Closure report_error_to_uma_cb_; -- -- // VPP (Video Post Processing) context, this is used to convert -- // pictures used by the decoder to RGBA pictures usable by GL or the -- // display hardware. -- VAConfigID va_vpp_config_id_; -- VAContextID va_vpp_context_id_; -- VABufferID va_vpp_buffer_id_; -- -- DISALLOW_COPY_AND_ASSIGN(VaapiWrapper); --}; -- --} // namespace media -- --#endif // MEDIA_GPU_VAAPI_WRAPPER_H_ ---- a/media/gpu/video_decode_accelerator_unittest.cc -+++ b/media/gpu/video_decode_accelerator_unittest.cc -@@ -75,7 +75,7 @@ - #include "media/gpu/dxva_video_decode_accelerator_win.h" - #endif // defined(OS_WIN) - #if BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #endif // BUILDFLAG(USE_VAAPI) - - #if defined(OS_CHROMEOS) ---- a/media/gpu/video_encode_accelerator_unittest.cc -+++ b/media/gpu/video_encode_accelerator_unittest.cc -@@ -56,7 +56,7 @@ - #include "testing/gtest/include/gtest/gtest.h" - - #if BUILDFLAG(USE_VAAPI) --#include "media/gpu/vaapi_wrapper.h" -+#include "media/gpu/vaapi/vaapi_wrapper.h" - #elif defined(OS_WIN) - #include "media/gpu/media_foundation_video_encode_accelerator_win.h" - #endif diff --git a/chromium-vaapi-r16.patch b/chromium-vaapi-r16.patch index 0020ea7..283582d 100644 --- a/chromium-vaapi-r16.patch +++ b/chromium-vaapi-r16.patch @@ -1,6 +1,6 @@ -From 7f29b44224439377592be2b75da0b86d82d8ad6d Mon Sep 17 00:00:00 2001 +From 4bf343ab8c4a538205f9c1e96a661e40620c716b Mon Sep 17 00:00:00 2001 From: Daniel Charles -Date: Wed, 18 Oct 2017 17:27:42 -0700 +Date: Fri, 09 Feb 2018 14:39:27 -0800 Subject: [PATCH] Enable VAVDA, VAVEA and VAJDA on linux with VAAPI only This patch contains all the changes necessary to use VA-API along with @@ -24,12 +24,10 @@ video are used. The other flags are kept for ChromeOS and other OSes. Developer testing was made on skylake hardware, ChromeOS and Ubuntu. BUG=NONE -TEST="subjective testing with VAVDA,VAVEA and VAJDA, autotest for encoder" -TEST="and decoder hardware accelerated" -TEST="have libva/intel-vaapi-driver installed and not installed in the system" -TEST="repeat on different hardware families" -R=posciak@chromium.org -R=kcwu@chromium.org +TEST=subjective testing with VAVDA,VAVEA and VAJDA, autotest for encoder + and decoder hardware accelerated + have libva/intel-vaapi-driver installed and not installed in the system + repeat on different hardware families Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel Change-Id: Ifbbf5c9e5221a8b5733fc6d4d0cf984a1f103171 @@ -39,10 +37,10 @@ Signed-off-by: Daniel Charles --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1249,12 +1249,14 @@ const FeatureEntry kFeatureEntries[] = { - flag_descriptions::kEnablePreventLayerSquashingDescription, kOsAll, - FEATURE_VALUE_TYPE(features::kEnablePreventLayerSquashing)}, + flag_descriptions::kUiPartialSwapDescription, kOsAll, + SINGLE_DISABLE_VALUE_TYPE(switches::kUIDisablePartialSwap)}, #if BUILDFLAG(ENABLE_WEBRTC) -+#if !defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) ++#if defined(OS_CHROMEOS) || defined(OS_ANDROID) {"disable-webrtc-hw-decoding", flag_descriptions::kWebrtcHwDecodingName, flag_descriptions::kWebrtcHwDecodingDescription, kOsAndroid | kOsCrOS, SINGLE_DISABLE_VALUE_TYPE(switches::kDisableWebRtcHWDecoding)}, @@ -53,8 +51,8 @@ Signed-off-by: Daniel Charles {"enable-webrtc-hw-h264-encoding", flag_descriptions::kWebrtcHwH264EncodingName, flag_descriptions::kWebrtcHwH264EncodingDescription, kOsAndroid | kOsCrOS, -@@ -1535,6 +1537,13 @@ const FeatureEntry kFeatureEntries[] = { - flag_descriptions::kShowTouchHudDescription, kOsAll, +@@ -1550,6 +1552,13 @@ const FeatureEntry kFeatureEntries[] = { + flag_descriptions::kShowTouchHudDescription, kOsCrOS, SINGLE_VALUE_TYPE(ash::switches::kAshTouchHud)}, #endif // OS_CHROMEOS +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) @@ -67,15 +65,15 @@ Signed-off-by: Daniel Charles { "disable-accelerated-video-decode", flag_descriptions::kAcceleratedVideoDecodeName, -@@ -1542,6 +1551,7 @@ const FeatureEntry kFeatureEntries[] = { +@@ -1557,6 +1566,7 @@ const FeatureEntry kFeatureEntries[] = { kOsMac | kOsWin | kOsCrOS | kOsAndroid, SINGLE_DISABLE_VALUE_TYPE(switches::kDisableAcceleratedVideoDecode), }, +#endif - {"mojo-video-encode-accelerator", - flag_descriptions::kMojoVideoEncodeAcceleratorName, - flag_descriptions::kMojoVideoEncodeAcceleratorDescription, -@@ -2270,12 +2280,17 @@ const FeatureEntry kFeatureEntries[] = { + #if defined(OS_WIN) + {"enable-hdr", flag_descriptions::kEnableHDRName, + flag_descriptions::kEnableHDRDescription, kOsWin, +@@ -2268,12 +2278,17 @@ const FeatureEntry kFeatureEntries[] = { FEATURE_VALUE_TYPE(features::kOpenVR)}, #endif // ENABLE_OPENVR #endif // ENABLE_VR @@ -105,7 +103,7 @@ Signed-off-by: Daniel Charles #include "cc/base/switches.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/boot_times_recorder.h" -@@ -84,8 +85,13 @@ void DeriveCommandLine(const GURL& start +@@ -84,9 +85,14 @@ void DeriveCommandLine(const GURL& start ::switches::kDisable2dCanvasImageChromium, ::switches::kDisableAccelerated2dCanvas, ::switches::kDisableAcceleratedJpegDecoding, @@ -115,11 +113,12 @@ Signed-off-by: Daniel Charles +#else ::switches::kDisableAcceleratedMjpegDecode, ::switches::kDisableAcceleratedVideoDecode, + ::switches::kDisableAcceleratedVideoEncode, +#endif ::switches::kDisableBlinkFeatures, ::switches::kDisableCastStreamingHWEncoding, ::switches::kDisableDistanceFieldText, -@@ -166,7 +172,7 @@ void DeriveCommandLine(const GURL& start +@@ -164,7 +170,7 @@ void DeriveCommandLine(const GURL& start ::switches::kDisableWebGLImageChromium, ::switches::kEnableWebGLImageChromium, ::switches::kEnableWebVR, @@ -130,11 +129,11 @@ Signed-off-by: Daniel Charles #endif --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc -@@ -19,6 +19,13 @@ const char kAccelerated2dCanvasDescripti +@@ -14,6 +14,13 @@ const char kAccelerated2dCanvasDescripti "Enables the use of the GPU to perform 2d canvas rendering instead of " "using software rendering."; -+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) ++#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) +const char kAcceleratedVideoName[] = "Hardware-accelerated video"; +const char kAcceleratedVideoDescription[] = + "Hardware-accelerated video where VA-API driver is installed on the" @@ -144,15 +143,15 @@ Signed-off-by: Daniel Charles const char kAcceleratedVideoDecodeName[] = "Hardware-accelerated video decode"; const char kAcceleratedVideoDecodeDescription[] = "Hardware-accelerated video decode where available."; -@@ -1572,6 +1579,7 @@ const char kWebrtcEchoCanceller3Name[] = +@@ -1597,6 +1604,7 @@ const char kWebrtcEchoCanceller3Name[] = const char kWebrtcEchoCanceller3Description[] = "Experimental WebRTC echo canceller (AEC3)."; -+#if !defined(OS_LINUX) || !defined(OS_CHROMEOS) ++#if defined(OS_CHROMEOS) || defined(OS_ANDROID) const char kWebrtcHwDecodingName[] = "WebRTC hardware video decoding"; const char kWebrtcHwDecodingDescription[] = "Support in WebRTC for decoding video streams using platform hardware."; -@@ -1579,6 +1587,7 @@ const char kWebrtcHwDecodingDescription[ +@@ -1604,6 +1612,7 @@ const char kWebrtcHwDecodingDescription[ const char kWebrtcHwEncodingName[] = "WebRTC hardware video encoding"; const char kWebrtcHwEncodingDescription[] = "Support in WebRTC for encoding video streams using platform hardware."; @@ -160,7 +159,7 @@ Signed-off-by: Daniel Charles const char kWebrtcHwH264EncodingName[] = "WebRTC hardware h264 video encoding"; const char kWebrtcHwH264EncodingDescription[] = -@@ -2390,14 +2399,16 @@ const char kTranslateNewUxDescription[] +@@ -2434,14 +2443,16 @@ const char kTranslateNewUxDescription[] // Chrome OS ------------------------------------------------------------------- @@ -191,7 +190,7 @@ Signed-off-by: Daniel Charles extern const char kAcceleratedVideoDecodeName[]; extern const char kAcceleratedVideoDecodeDescription[]; -@@ -1480,13 +1484,17 @@ extern const char kPermissionPromptPersi +@@ -1498,13 +1502,17 @@ extern const char kPermissionPromptPersi #endif // defined(OS_MACOSX) @@ -214,75 +213,21 @@ Signed-off-by: Daniel Charles --- a/content/browser/gpu/compositor_util.cc +++ b/content/browser/gpu/compositor_util.cc -@@ -103,7 +103,11 @@ const GpuFeatureInfo GetGpuFeatureInfo(s - {"video_decode", - manager->IsFeatureBlacklisted( - gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE), -+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -+ !command_line.HasSwitch(switches::kEnableAcceleratedVideo), -+#else - command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode), -+#endif - "Accelerated video decode has been disabled, either via blacklist," - " about:flags or the command line.", - true}, ---- a/content/browser/gpu/gpu_data_manager_impl_private.cc -+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc -@@ -709,7 +709,11 @@ void GpuDataManagerImplPrivate::AppendRe - DCHECK(command_line); - - if (ShouldDisableAcceleratedVideoDecode(command_line)) -+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -+ command_line->AppendSwitch(switches::kEnableAcceleratedVideo); -+#else - command_line->AppendSwitch(switches::kDisableAcceleratedVideoDecode); -+#endif - } - - void GpuDataManagerImplPrivate::AppendGpuCommandLine( -@@ -810,7 +814,12 @@ void GpuDataManagerImplPrivate::UpdateRe - prefs->accelerated_2d_canvas_enabled = false; - } - if (!ShouldDisableAcceleratedVideoDecode(command_line) && -- !command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) { -+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -+ command_line->HasSwitch(switches::kEnableAcceleratedVideo) -+#else -+ !command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode) -+#endif -+ ) { - prefs->pepper_accelerated_video_decode_enabled = true; - } - } -@@ -959,7 +968,13 @@ bool GpuDataManagerImplPrivate::UpdateAc - - bool GpuDataManagerImplPrivate::ShouldDisableAcceleratedVideoDecode( - const base::CommandLine* command_line) const { -- if (command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) { -+ if ( +@@ -98,7 +98,11 @@ const GpuFeatureData GetGpuFeatureData(s + {"video_decode", + manager->GetFeatureStatus( + gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE), +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -+ !command_line->HasSwitch(switches::kEnableAcceleratedVideo) ++ !command_line.HasSwitch(switches::kEnableAcceleratedVideo), +#else -+ command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode) + command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode), +#endif -+ ) { - // It was already disabled on the command line. - return false; - } + "Accelerated video decode has been disabled, either via blacklist," + " about:flags or the command line.", + true}, --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc -@@ -114,13 +114,18 @@ static const char* const kSwitchNames[] - service_manager::switches::kDisableSeccompFilterSandbox, - service_manager::switches::kGpuSandboxAllowSysVShm, - service_manager::switches::kGpuSandboxFailuresFatal, -+#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(OS_ANDROID) -+ switches::kEnableAcceleratedVideo, -+#else -+ switches::kDisableAcceleratedVideoDecode, -+#endif - switches::kDisableBreakpad, - switches::kDisableGpuRasterization, - switches::kDisableGpuSandbox, +@@ -120,7 +120,7 @@ static const char* const kSwitchNames[] switches::kDisableGLExtensions, switches::kDisableLogging, switches::kDisableShaderNameHashing, @@ -309,7 +254,7 @@ Signed-off-by: Daniel Charles --- a/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc +++ b/content/browser/renderer_host/media/video_capture_gpu_jpeg_decoder.cc -@@ -64,15 +64,21 @@ void VideoCaptureGpuJpegDecoder::Initial +@@ -65,15 +65,21 @@ void VideoCaptureGpuJpegDecoder::Initial bool is_platform_supported = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseFakeJpegDecodeAccelerator); @@ -337,7 +282,7 @@ Signed-off-by: Daniel Charles return; --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc -@@ -2526,7 +2526,11 @@ void RenderProcessHostImpl::PropagateBro +@@ -2530,7 +2530,11 @@ void RenderProcessHostImpl::PropagateBro switches::kDefaultTileHeight, switches::kDisable2dCanvasImageChromium, switches::kDisableAcceleratedJpegDecoding, @@ -348,8 +293,8 @@ Signed-off-by: Daniel Charles +#endif switches::kDisableBackgroundTimerThrottling, switches::kDisableBreakpad, - switches::kDisableBrowserSideNavigation, -@@ -2674,8 +2678,10 @@ void RenderProcessHostImpl::PropagateBro + switches::kDisableCompositorUkmForTests, +@@ -2663,8 +2667,10 @@ void RenderProcessHostImpl::PropagateBro switches::kDisableMojoRenderer, #endif #if BUILDFLAG(ENABLE_WEBRTC) @@ -378,15 +323,7 @@ Signed-off-by: Daniel Charles private: --- a/content/gpu/BUILD.gn +++ b/content/gpu/BUILD.gn -@@ -48,7 +48,6 @@ target(link_target_type, "gpu_sources") - ] - - configs += [ "//content:content_implementation" ] -- - deps = [ - "//base", - "//base/third_party/dynamic_annotations", -@@ -124,4 +123,8 @@ target(link_target_type, "gpu_sources") +@@ -125,4 +125,8 @@ target(link_target_type, "gpu_sources") if (is_desktop_linux && (!is_chromecast || is_cast_desktop_build)) { configs += [ "//build/config/linux/dri" ] } @@ -397,16 +334,16 @@ Signed-off-by: Daniel Charles } --- a/content/gpu/gpu_main.cc +++ b/content/gpu/gpu_main.cc -@@ -271,7 +271,7 @@ int GpuMain(const MainFunctionParams& pa - // Initializes StatisticsRecorder which tracks UMA histograms. - base::StatisticsRecorder::Initialize(); +@@ -273,7 +273,7 @@ int GpuMain(const MainFunctionParams& pa + + base::PlatformThread::SetName("CrGpuMain"); -#if defined(OS_ANDROID) || defined(OS_CHROMEOS) +#if defined(OS_LINUX) // Set thread priority before sandbox initialization. base::PlatformThread::SetCurrentThreadPriority(base::ThreadPriority::DISPLAY); #endif -@@ -300,7 +300,7 @@ int GpuMain(const MainFunctionParams& pa +@@ -302,7 +302,7 @@ int GpuMain(const MainFunctionParams& pa GetContentClient()->SetGpuInfo(gpu_init->gpu_info()); base::ThreadPriority io_thread_priority = base::ThreadPriority::NORMAL; @@ -425,7 +362,7 @@ Signed-off-by: Daniel Charles #include "content/browser/gpu/gpu_process_host.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" -@@ -55,12 +56,19 @@ const gpu::GpuPreferences GetGpuPreferen +@@ -55,10 +56,18 @@ const gpu::GpuPreferences GetGpuPreferen gpu_preferences.in_process_gpu = command_line->HasSwitch(switches::kInProcessGPU); gpu_preferences.disable_accelerated_video_decode = @@ -433,17 +370,15 @@ Signed-off-by: Daniel Charles + !command_line->HasSwitch(switches::kEnableAcceleratedVideo); +#else command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode); --#if defined(OS_CHROMEOS) +#endif + gpu_preferences.disable_accelerated_video_encode = +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -+ gpu_preferences.disable_vaapi_accelerated_video_encode = + !command_line->HasSwitch(switches::kEnableAcceleratedVideo); -+#elif defined(OS_CHROMEOS) - gpu_preferences.disable_vaapi_accelerated_video_encode = - command_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode); - #endif ++#else + command_line->HasSwitch(switches::kDisableAcceleratedVideoEncode); -#if BUILDFLAG(ENABLE_WEBRTC) -+#if BUILDFLAG(ENABLE_WEBRTC) && (!defined(OS_LINUX) || defined(OS_CHROMEOS)) ++#endif ++#if BUILDFLAG(ENABLE_WEBRTC) && (defined(OS_CHROMEOS) || defined(OS_ANDROID)) gpu_preferences.disable_web_rtc_hw_encoding = command_line->HasSwitch(switches::kDisableWebRtcHWEncoding); #endif @@ -472,7 +407,7 @@ Signed-off-by: Daniel Charles // Disables hardware acceleration of video decode, where available. const char kDisableAcceleratedVideoDecode[] = "disable-accelerated-video-decode"; -@@ -903,11 +912,13 @@ const char kZygoteProcess[] +@@ -888,11 +897,13 @@ const char kZygoteProcess[] // ignores this switch on its stable and beta channels. const char kDisableWebRtcEncryption[] = "disable-webrtc-encryption"; @@ -498,8 +433,8 @@ Signed-off-by: Daniel Charles CONTENT_EXPORT extern const char kDisableAcceleratedMjpegDecode[]; +#endif CONTENT_EXPORT extern const char kDisableAcceleratedVideoDecode[]; + CONTENT_EXPORT extern const char kDisableAcceleratedVideoEncode[]; CONTENT_EXPORT extern const char kDisableAudioSupportForDesktopShare[]; - extern const char kDisableBackingStoreLimit[]; @@ -107,6 +111,9 @@ CONTENT_EXPORT extern const char kDisabl CONTENT_EXPORT extern const char kDomAutomationController[]; extern const char kDisable2dCanvasClipAntialiasing[]; @@ -510,7 +445,7 @@ Signed-off-by: Daniel Charles CONTENT_EXPORT extern const char kEnableAggressiveDOMStorageFlushing[]; CONTENT_EXPORT extern const char kEnableAutomation[]; CONTENT_EXPORT extern const char kEnablePreferCompositingToLCDText[]; -@@ -248,8 +255,10 @@ CONTENT_EXPORT extern const char kZygote +@@ -244,8 +251,10 @@ CONTENT_EXPORT extern const char kZygote #if BUILDFLAG(ENABLE_WEBRTC) CONTENT_EXPORT extern const char kDisableWebRtcEncryption[]; @@ -523,7 +458,7 @@ Signed-off-by: Daniel Charles CONTENT_EXPORT extern const char kEnableWebRtcStunOrigin[]; --- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc +++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc -@@ -243,10 +243,19 @@ void PeerConnectionDependencyFactory::In +@@ -245,12 +245,19 @@ void PeerConnectionDependencyFactory::In const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (gpu_factories && gpu_factories->IsGpuVideoAcceleratorEnabled()) { @@ -540,24 +475,25 @@ Signed-off-by: Daniel Charles +#else + if (!cmd_line->HasSwitch(switches::kDisableWebRtcHWEncoding)) +#endif -+ { encoder_factory.reset(new RTCVideoEncoderFactory(gpu_factories)); - } +- } } + + #if defined(OS_ANDROID) --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc -@@ -1495,7 +1495,11 @@ media::GpuVideoAcceleratorFactories* Ren +@@ -1485,7 +1485,11 @@ media::GpuVideoAcceleratorFactories* Ren scoped_refptr media_task_runner = GetMediaThreadTaskRunner(); const bool enable_video_accelerator = +#if defined(OS_LINUX) && !defined(OS_CHROMEOS) -+ cmd_line->HasSwitch(switches::kEnableAcceleratedVideo); ++ cmd_line->HasSwitch(switches::kEnableAcceleratedVideo) && +#else - !cmd_line->HasSwitch(switches::kDisableAcceleratedVideoDecode); + !cmd_line->HasSwitch(switches::kDisableAcceleratedVideoDecode) && +#endif - const bool enable_gpu_memory_buffer_video_frames = - !is_gpu_compositing_disabled_ && - #if defined(OS_MACOSX) || defined(OS_LINUX) + (gpu_channel_host->gpu_feature_info() + .status_values[gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE] == + gpu::kGpuFeatureStatusEnabled); --- a/gpu/config/software_rendering_list.json +++ b/gpu/config/software_rendering_list.json @@ -373,17 +373,6 @@ @@ -580,7 +516,7 @@ Signed-off-by: Daniel Charles "cr_bugs": [145531, 332596, 571899, 629434], --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn -@@ -25,6 +25,14 @@ if (is_mac) { +@@ -24,6 +24,14 @@ if (is_mac) { import("//build/config/mac/mac_sdk.gni") } @@ -595,7 +531,7 @@ Signed-off-by: Daniel Charles if (use_vaapi) { action("libva_generate_stubs") { extra_header = "vaapi/va_stub_header.fragment" -@@ -356,6 +364,10 @@ component("gpu") { +@@ -353,6 +361,10 @@ component("gpu") { if (use_ozone) { deps += [ "//ui/ozone" ] } @@ -606,13 +542,3 @@ Signed-off-by: Daniel Charles } if (is_win) { ---- a/media/gpu/gpu_video_decode_accelerator_factory.cc -+++ b/media/gpu/gpu_video_decode_accelerator_factory.cc -@@ -87,6 +87,7 @@ GpuVideoDecodeAcceleratorFactory::GetDec - // profile (instead of calculating a superset). - // TODO(posciak,henryhsu): improve this so that we choose a superset of - // resolutions and other supported profile parameters. -+ DVLOG(1) << "Get Supported profiles"; - #if defined(OS_WIN) - capabilities.supported_profiles = - DXVAVideoDecodeAccelerator::GetSupportedProfiles(gpu_preferences, diff --git a/chromium-vaapi-rgbx.patch b/chromium-vaapi-rgbx.patch deleted file mode 100644 index f6e1deb..0000000 --- a/chromium-vaapi-rgbx.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 51357dc19efbf30328ca05655fbf69886f6e9113 Mon Sep 17 00:00:00 2001 -From: Julien Isorce -Date: Tue, 05 Dec 2017 23:39:45 +0000 -Subject: [PATCH] Allow RGBX for VaapiTFPPicture - -Fixes regression on non-ozone platforms and introduced by - 73d609f366ba6a86324048bbad81527f76d237b5 - https://chromium-review.googlesource.com/787290 - -Note that the format is only used for sanity check. All the logic is -done automatically in the vaapi driver's implementation of vaPutSurface. - -Bug: 785201 -Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel -Change-Id: Idc0bdf184874bf2c238e28da6f42f4e3572e9743 -Reviewed-on: https://chromium-review.googlesource.com/807928 -Reviewed-by: Dan Sanders -Commit-Queue: Julien Isorce -Cr-Commit-Position: refs/heads/master@{#521897} ---- - -diff --git a/media/gpu/vaapi/vaapi_tfp_picture.cc b/media/gpu/vaapi/vaapi_tfp_picture.cc -index e9eecce..bd7823c 100644 ---- a/media/gpu/vaapi/vaapi_tfp_picture.cc -+++ b/media/gpu/vaapi/vaapi_tfp_picture.cc -@@ -72,7 +72,8 @@ - bool VaapiTFPPicture::Allocate(gfx::BufferFormat format) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (format != gfx::BufferFormat::BGRX_8888 && -- format != gfx::BufferFormat::BGRA_8888) { -+ format != gfx::BufferFormat::BGRA_8888 && -+ format != gfx::BufferFormat::RGBX_8888) { - DLOG(ERROR) << "Unsupported format"; - return false; - } From d61fb35ac8aac8af51d4f367494b8653ee82d602 Mon Sep 17 00:00:00 2001 From: gcarq Date: Thu, 15 Mar 2018 17:20:43 +0100 Subject: [PATCH 9/9] apply upstream changes --- PKGBUILD | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 9874129..045fb13 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -5,7 +5,7 @@ # Contributor: Daniel J Griffiths pkgname=inox -pkgver=65.0.3325.146 +pkgver=65.0.3325.162 pkgrel=1 _launcher_ver=5 pkgdesc="Chromium Spin-off to enhance privacy by disabling data transmission to Google" @@ -62,9 +62,9 @@ source=(https://commondatastorage.googleapis.com/chromium-browser-official/chrom https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0020-launcher-branding.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/0021-disable-rlz.patch https://raw.githubusercontent.com/gcarq/inox-patchset/$pkgver/9000-disable-metrics.patch) -sha256sums=('cb4f2f3f5a3344f7c452b61f8086d4b4e56af6f5bc34309c3ede8be6b4ab81a3' +sha256sums=('627e7bfd84795de1553fac305239130d25186acf2d3c77d39d824327cd116cce' '4dc3428f2c927955d9ae117f2fb24d098cc6dd67adb760ac9c82b522ec8b0587' - 'adfeb830af4c9b55c4a6481ef245e82ad2b9fc3cfe0fe339b30baa8573f701e7' + 'bed2a7ef4b1ebd53b28e2f38963a2dd761267ccc8818693c34ce8596db53dd4c' '71471fa4690894420f9e04a2e9a622af620d92ac2714a35f9a4c4e90fa3968dd' '4a533acefbbc1567b0d74a1c0903e9179b8c59c1beabe748850795815366e509' '7b88830c5e0e9819f514ad68aae885d427541a907e25607e47dee1b0f38975fd' @@ -105,7 +105,7 @@ sha256sums=('cb4f2f3f5a3344f7c452b61f8086d4b4e56af6f5bc34309c3ede8be6b4ab81a3' # Possible replacements are listed in build/linux/unbundle/replace_gn_files.py # Keys are the names in the above script; values are the dependencies in Arch -readonly -A _system_libs=( +declare -gA _system_libs=( #[ffmpeg]=ffmpeg # https://crbug.com/731766 [flac]=flac [fontconfig]=fontconfig @@ -125,7 +125,7 @@ readonly -A _system_libs=( [yasm]= [zlib]=minizip ) -readonly _unwanted_bundled_libs=( +_unwanted_bundled_libs=( ${!_system_libs[@]} ${_system_libs[libjpeg]+libjpeg_turbo} )