Commit 5fd9b902 authored by Chris Cunningham's avatar Chris Cunningham Committed by Commit Bot

[media] Optionally use LibVpx for opaque Vp8

This CL adds a switch to use libvpx for opaque (no alpha transparency)
VP8. There is no change to the default behavior (using ffmpeg). The
switch will be toggled via finch to compare the performance libvpx and
ffvp8.

Switching to libvpx would workaround a bug we're hitting decoding
vp8 streams from intel hw encoders (bug believed to be in ffmpeg, filed
https://trac.ffmpeg.org/ticket/8069). FFvp8 has better performance
than libvpx, but these gains may not matter on modern systems. Vp8 is
also very uncommon and generally low-resolution on today's web.

FFmpeg and libVPX have different behaviors around mid-stream frame-size
changes. LibVPX simply trusts the container metadata and we scale any
deviation from that is simply scaled. This is also true for hardware
decoders (for other codecs) and VP9 (which already uses libvpx). In
contrast, FFmpeg trusts the frames themselves and changes the size of
the video dynamically. This motivated a re-write of the
video-frame-size-change.html, which is focused on rendered size
changes rather than nuance of a specific codec. Folks desiring a size
change should use MSE to append a new init segment to explicitly signal
the new size. Given very limited use of VP8 today, we expect this
does not break folks (and hope the finch experiment will confirm that).

(https://simpl.info/videoalpha/)

Bug: 992235, 963740, b:138840822
Test: media_unittests and manually verified alpha still works with vpx
Change-Id: I48cec468946eb5a769fb2966da6b92ba28b43d8b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1745711
Commit-Queue: Chrome Cunningham <chcunningham@chromium.org>
Reviewed-by: default avatarDan Sanders <sandersd@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686684}
parent 603e7389
......@@ -185,6 +185,10 @@ const char kUserGestureRequiredPolicy[] = "user-gesture-required";
namespace media {
// Prefer FFmpeg to LibVPX for Vp8 decoding with opaque alpha mode.
const base::Feature kFFmpegDecodeOpaqueVP8{"FFmpegDecodeOpaqueVP8",
base::FEATURE_ENABLED_BY_DEFAULT};
// Only used for disabling overlay fullscreen (aka SurfaceView) in Clank.
const base::Feature kOverlayFullscreenVideo{"overlay-fullscreen-video",
base::FEATURE_ENABLED_BY_DEFAULT};
......
......@@ -102,6 +102,7 @@ MEDIA_EXPORT extern const base::Feature kBackgroundVideoPauseOptimization;
MEDIA_EXPORT extern const base::Feature kD3D11VideoDecoder;
MEDIA_EXPORT extern const base::Feature kD3D11VideoDecoderIgnoreWorkarounds;
MEDIA_EXPORT extern const base::Feature kExternalClearKeyForTesting;
MEDIA_EXPORT extern const base::Feature kFFmpegDecodeOpaqueVP8;
MEDIA_EXPORT extern const base::Feature kFailUrlProvisionFetcherForTesting;
MEDIA_EXPORT extern const base::Feature kFallbackAfterDecodeError;
MEDIA_EXPORT extern const base::Feature kGlobalMediaControls;
......
......@@ -13,6 +13,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/feature_list.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
......@@ -203,9 +204,11 @@ bool VpxVideoDecoder::ConfigureDecoder(const VideoDecoderConfig& config) {
return false;
#if BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
// When FFmpegVideoDecoder is available it handles VP8 that doesn't have
// alpha, and VpxVideoDecoder will handle VP8 with alpha.
if (config.codec() == kCodecVP8 &&
// When enabled, ffmpeg handles VP8 that doesn't have alpha, and
// VpxVideoDecoder will handle VP8 with alpha. FFvp8 is being deprecated.
// See http://crbug.com/992235.
if (base::FeatureList::IsEnabled(kFFmpegDecodeOpaqueVP8) &&
config.codec() == kCodecVP8 &&
config.alpha_mode() == VideoDecoderConfig::AlphaMode::kIsOpaque) {
return false;
}
......
......@@ -12,7 +12,6 @@ crbug.com/24182 storage/indexeddb/objectstore-cursor.html [ Slow ]
crbug.com/24182 storage/indexeddb/mozilla/test_objectStore_openKeyCursor.html [ Slow ]
crbug.com/24182 editing/selection/modify_move/move-by-word-visually-mac.html [ Slow ]
crbug.com/24182 compositing/culling/filter-occlusion-blur-large.html [ Slow ]
crbug.com/24182 compositing/video-frame-size-change.html [ Slow ]
crbug.com/24182 editing/selection/caret-at-bidi-boundary.html [ Slow ]
crbug.com/24182 editing/selection/modify_move/move-by-word-visually-crash-test-5.html [ Slow ]
crbug.com/24182 fast/canvas/canvas-toBlob-toDataURL-race-imageEncoder-jpeg.html [ Slow ]
......
......@@ -6216,7 +6216,6 @@ crbug.com/963141 [ Mac ] virtual/audio-service/media/video-aspect-ratio.html [ P
# Sheriff 2019-05-16
crbug.com/963141 [ Linux ] media/video-object-fit.html [ Pass Failure ]
crbug.com/963141 [ Linux ] virtual/audio-service/media/video-object-fit.html [ Pass Failure ]
crbug.com/963740 compositing/video-frame-size-change.html [ Pass Failure ]
# Sheriff 2019-05-17
crbug.com/964239 virtual/threaded/external/wpt/css/css-scroll-snap/scroll-margin.html [ Pass Failure ]
......
<!DOCTYPE html>
<html>
<body onload="load()">
<p>Tests decoding and rendering a video element that has a changing resolution.</p>
<video width=320></video>
<video width=320></video>
<script>
if (window.testRunner) {
testRunner.waitUntilDone();
}
function load() {
function oncanplay() {
// Make sure we render the first frame.
requestAnimationFrame(function() {
video.play();
});
};
// Get the first video to stay on frame zero for comparison purposes.
var video = document.getElementsByTagName("video")[0];
video.src = "../media/resources/frame_size_change.webm";
video.addEventListener('canplay', oncanplay, {once: true});
// Get the second video to play through the clip with changing dimensions.
video = document.getElementsByTagName("video")[1];
video.src = "../media/resources/frame_size_change.webm";
video.addEventListener('canplay', oncanplay, {once: true});
video.addEventListener('playing', function() {
// Make sure the video plays for a bit.
video.addEventListener('timeupdate', function() {
if (video.currentTime > 1.0) {
video.pause();
}
});
});
video.addEventListener('pause', function() {
// Now seek back to a point where resolution should be different.
video.addEventListener('seeked', function() {
if (window.testRunner) {
testRunner.notifyDone();
}
});
video.currentTime = 0.5;
});
}
</script>
</body>
</html>
<!DOCTYPE html>
<style>
/* Keep videos side-by-side and top-aligned for a nice pixel diff */
#wrapper {
white-space: nowrap;
}
#wrapper > video {
vertical-align: top;
}
</style>
<div id='wrapper'>
<video id='single_size'></video><video id='mixed_size' ></video></div>
<script>
if (window.testRunner) {
testRunner.waitUntilDone();
}
async function setupMse(video, contentType) {
let promise = new Promise((resolve, reject) => {
let mediaSource = new MediaSource();
video.src = window.URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', function(e) {
let sourceBuffer = mediaSource.addSourceBuffer(contentType);
resolve(sourceBuffer);
});
});
return promise;
}
async function appendBuffer(sourceBuffer, data) {
let promise = new Promise((resolve, reject) => {
sourceBuffer.addEventListener('updateend', resolve);
sourceBuffer.appendBuffer(data);
});
return promise;
}
(async _ => {
let response = await fetch('/media/resources/media-source/webm/test-v-256k-320x240-30fps-10kfr.webm');
let videoData240p = await response.arrayBuffer();
response = await fetch('/media/resources/media-source/webm/test-v-128k-640x480-30fps-10kfr.webm');
let videoData480p = await response.arrayBuffer();
// Set up the first video with content of a single size.
let singleSizeVideo = document.getElementById('single_size');
let ss_sourceBuffer = await setupMse(singleSizeVideo, 'video/webm; codecs="vp8"');
await appendBuffer(ss_sourceBuffer, videoData240p);
// Setup the second video with a single size for 1 second, followed by a larger size for 2 additional seconds.
let mixedSizeVideo = document.getElementById('mixed_size');
let ms_sourceBuffer = await setupMse(mixedSizeVideo, 'video/webm; codecs="vp8"');
await appendBuffer(ms_sourceBuffer, videoData240p);
ms_sourceBuffer.timestampOffset = 1;
await appendBuffer(ms_sourceBuffer, videoData480p);
// Seek the second video beyond the first size.
mixedSizeVideo.addEventListener('seeked', () => {
if (window.testRunner) {
// Make sure we render the seeked frame.
requestAnimationFrame(() => {
testRunner.notifyDone();
});
}
});
mixedSizeVideo.currentTime = 2;
})();
</script>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment