Commit 57fd8da7 authored by Michael Crouse's avatar Michael Crouse Committed by Commit Bot

[LiteVideo] Observe MediaPlayer rebuffer events to stop throttling.

This change adds plumbing to allow the LiteVideo optimization to
observe when rebuffer events occur so that all throttling can be
stopped.

A future change will record the even in the user blocklist and
record the event in UKM.

Bug: 1082554
Change-Id: I9109fe221800b355e46c78e80254f75f433f9448
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2316849Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarrajendrant <rajendrant@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Commit-Queue: Michael Crouse <mcrouse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#793232}
parent 1ac641df
......@@ -95,7 +95,8 @@ class LiteVideoBrowserTest : public InProcessBrowserTest {
void TestMSEPlayback(const std::string& media_file,
const std::string& segment_duration,
const std::string& segment_fetch_delay_before_end) {
const std::string& segment_fetch_delay_before_end,
bool has_subframe_video) {
base::StringPairs query_params;
std::string media_files = media_file;
// Add few media segments, separated by ';'
......@@ -109,8 +110,9 @@ class LiteVideoBrowserTest : public InProcessBrowserTest {
query_params.emplace_back("MSESegmentDurationMS", segment_duration);
query_params.emplace_back("MSESegmentFetchDelayBeforeEndMS",
segment_fetch_delay_before_end);
RunMediaTestPage("mse_player.html", query_params,
base::ASCIIToUTF16(media::kEnded));
RunMediaTestPage(
has_subframe_video ? "multi_frame_mse_player.html" : "mse_player.html",
query_params, base::ASCIIToUTF16(media::kEnded));
}
// Runs a html page with a list of URL query parameters.
......@@ -121,7 +123,6 @@ class LiteVideoBrowserTest : public InProcessBrowserTest {
std::string query = media::GetURLQueryString(query_params);
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
EXPECT_TRUE(ui_test_utils::NavigateToURL(
browser(), http_server_.GetURL("/" + html_page + "?" + query)));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
......@@ -139,15 +140,26 @@ class LiteVideoBrowserTest : public InProcessBrowserTest {
};
IN_PROC_BROWSER_TEST_F(LiteVideoBrowserTest, SimplePlayback) {
TestMSEPlayback("bear-vp9.webm", "2000", "2000");
TestMSEPlayback("bear-vp9.webm", "2000", "2000", false);
RetryForHistogramUntilCountReached(histogram_tester(),
"Media.VideoHeight.Initial.MSE", 1);
histogram_tester().ExpectUniqueSample("LiteVideo.HintAgent.HasHint", true, 1);
histogram_tester().ExpectTotalCount("LiteVideo.URLLoader.ThrottleLatency", 4);
histogram_tester().ExpectTotalCount(
"LiteVideo.HintAgent.StopThrottleDueToBufferUnderflow", 0);
}
IN_PROC_BROWSER_TEST_F(LiteVideoBrowserTest, SimplePlaybackWithSubframe) {
TestMSEPlayback("bear-vp9.webm", "2000", "2000", true);
RetryForHistogramUntilCountReached(histogram_tester(),
"Media.VideoHeight.Initial.MSE", 1);
RetryForHistogramUntilCountReached(histogram_tester(),
"LiteVideo.HintAgent.HasHint", 2);
histogram_tester().ExpectUniqueSample("LiteVideo.HintAgent.HasHint", true, 2);
histogram_tester().ExpectTotalCount("LiteVideo.URLLoader.ThrottleLatency", 4);
}
class LiteVideoWithLiteModeDisabledBrowserTest : public LiteVideoBrowserTest {
......@@ -159,7 +171,7 @@ class LiteVideoWithLiteModeDisabledBrowserTest : public LiteVideoBrowserTest {
IN_PROC_BROWSER_TEST_F(LiteVideoWithLiteModeDisabledBrowserTest,
VideoThrottleDisabled) {
TestMSEPlayback("bear-vp9.webm", "2000", "2000");
TestMSEPlayback("bear-vp9.webm", "2000", "2000", false);
RetryForHistogramUntilCountReached(histogram_tester(),
"Media.VideoHeight.Initial.MSE", 1);
......@@ -168,6 +180,44 @@ IN_PROC_BROWSER_TEST_F(LiteVideoWithLiteModeDisabledBrowserTest,
histogram_tester().ExpectTotalCount("LiteVideo.URLLoader.ThrottleLatency", 0);
}
IN_PROC_BROWSER_TEST_F(LiteVideoBrowserTest,
MSEPlaybackStalledDueToBufferUnderflow) {
TestMSEPlayback("bear-vp9.webm", "2700", "500", false);
RetryForHistogramUntilCountReached(histogram_tester(),
"Media.VideoHeight.Initial.MSE", 1);
histogram_tester().ExpectUniqueSample("LiteVideo.HintAgent.HasHint", true, 1);
// Verify some responses were throttled and some video stalls were
// encountered.
EXPECT_GE(1U, histogram_tester()
.GetAllSamples("LiteVideo.URLLoader.ThrottleLatency")
.size());
EXPECT_GE(1U, histogram_tester()
.GetAllSamples("LiteVideo.HintsAgent.StopThrottling")
.size());
}
IN_PROC_BROWSER_TEST_F(LiteVideoBrowserTest,
MSEPlaybackStalledDueToBufferUnderflow_WithSubframe) {
TestMSEPlayback("bear-vp9.webm", "2700", "500", true);
RetryForHistogramUntilCountReached(histogram_tester(),
"Media.VideoHeight.Initial.MSE", 1);
RetryForHistogramUntilCountReached(histogram_tester(),
"LiteVideo.HintAgent.HasHint", 2);
histogram_tester().ExpectUniqueSample("LiteVideo.HintAgent.HasHint", true, 2);
// Verify some responses were throttled and some video stalls were
// encountered.
EXPECT_GE(2U, histogram_tester()
.GetAllSamples("LiteVideo.URLLoader.ThrottleLatency")
.size());
EXPECT_GE(2U, histogram_tester()
.GetAllSamples("LiteVideo.HintsAgent.StopThrottling")
.size());
}
class LiteVideoAndLiteModeDisabledBrowserTest : public LiteVideoBrowserTest {
public:
LiteVideoAndLiteModeDisabledBrowserTest()
......@@ -177,7 +227,7 @@ class LiteVideoAndLiteModeDisabledBrowserTest : public LiteVideoBrowserTest {
IN_PROC_BROWSER_TEST_F(LiteVideoAndLiteModeDisabledBrowserTest,
VideoThrottleDisabled) {
TestMSEPlayback("bear-vp9.webm", "2000", "2000");
TestMSEPlayback("bear-vp9.webm", "2000", "2000", false);
RetryForHistogramUntilCountReached(histogram_tester(),
"Media.VideoHeight.Initial.MSE", 1);
......
......@@ -157,4 +157,26 @@ void LiteVideoObserver::MaybeUpdateCoinflipExperimentState(
: base::RandInt(0, 1);
}
void LiteVideoObserver::MediaBufferUnderflow(const content::MediaPlayerId& id) {
content::RenderFrameHost* render_frame_host = id.render_frame_host;
if (!render_frame_host || !render_frame_host->GetProcess())
return;
mojo::AssociatedRemote<blink::mojom::PreviewsResourceLoadingHintsReceiver>
loading_hints_agent;
if (render_frame_host->GetRemoteAssociatedInterfaces()) {
render_frame_host->GetRemoteAssociatedInterfaces()->GetInterface(
&loading_hints_agent);
loading_hints_agent->StopThrottlingMediaRequests();
}
// TODO(crbug/1101563 Update the user blocklist. This needs additional
// work to operate on local state mapping the current render frame id
// to the navigation's origin.
// TODO(crbug/1097792): Flush a UKM event for this render frame host.
}
WEB_CONTENTS_USER_DATA_KEY_IMPL(LiteVideoObserver)
......@@ -10,6 +10,7 @@
#include "base/timer/timer.h"
#include "chrome/browser/lite_video/lite_video_navigation_metrics.h"
#include "chrome/browser/lite_video/lite_video_user_blocklist.h"
#include "content/public/browser/media_player_id.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
......@@ -38,6 +39,7 @@ class LiteVideoObserver
// content::WebContentsObserver.
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
void MediaBufferUnderflow(const content::MediaPlayerId& id) override;
// Determines the LiteVideoDecision based on |hint| and the coinflip
// holdback state.
......
......@@ -54,16 +54,16 @@ class LiteVideoHintAgent
return active_throttles_;
}
// Stop throttling and resume the current throttled media requests
// immediately. Throttling could start again for new requests
void StopThrottling();
private:
friend class LiteVideoHintAgentTest;
// content::RenderFrameObserver overrides
void OnDestruct() override;
// Stop throttling and resume the current throttled media requests
// immediately. Throttling could start again for new requests
void StopThrottling();
// The network downlink bandwidth target in kilobytes per second used to
// calculate the throttling delay on media requests
base::Optional<int> target_downlink_bandwidth_kbps_;
......
......@@ -7,6 +7,7 @@
#include <vector>
#include "base/metrics/histogram_macros.h"
#include "base/metrics/histogram_macros_local.h"
#include "content/public/renderer/render_frame.h"
#include "third_party/blink/public/platform/web_loading_hints_provider.h"
#include "third_party/blink/public/platform/web_string.h"
......@@ -132,4 +133,13 @@ void ResourceLoadingHintsAgent::SetLiteVideoHint(
lite_video_hint_agent->SetLiteVideoHint(std::move(lite_video_hint));
}
void ResourceLoadingHintsAgent::StopThrottlingMediaRequests() {
auto* lite_video_hint_agent =
lite_video::LiteVideoHintAgent::Get(render_frame());
if (lite_video_hint_agent) {
LOCAL_HISTOGRAM_BOOLEAN("LiteVideo.HintsAgent.StopThrottling", true);
lite_video_hint_agent->StopThrottling();
}
}
} // namespace previews
......@@ -63,6 +63,7 @@ class ResourceLoadingHintsAgent
blink::mojom::CompressPublicImagesHintsPtr images_hints) override;
void SetLiteVideoHint(
blink::mojom::LiteVideoHintPtr lite_video_hint) override;
void StopThrottlingMediaRequests() override;
void SetReceiver(
mojo::PendingAssociatedReceiver<
......
......@@ -220,6 +220,8 @@ bool MediaWebContentsObserver::OnMessageReceived(
IPC_MESSAGE_HANDLER(
MediaPlayerDelegateHostMsg_OnPictureInPictureAvailabilityChanged,
OnPictureInPictureAvailabilityChanged)
IPC_MESSAGE_HANDLER(MediaPlayerDelegateHostMsg_OnBufferUnderflow,
OnBufferUnderflow)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
......@@ -373,6 +375,13 @@ void MediaWebContentsObserver::OnPictureInPictureAvailabilityChanged(
MediaPlayerId(render_frame_host, delegate_id), available);
}
void MediaWebContentsObserver::OnBufferUnderflow(
RenderFrameHost* render_frame_host,
int delegate_id) {
const MediaPlayerId id(render_frame_host, delegate_id);
web_contents_impl()->MediaBufferUnderflow(id);
}
device::mojom::WakeLock* MediaWebContentsObserver::GetAudioWakeLock() {
// Here is a lazy binding, and will not reconnect after connection error.
if (!audio_wake_lock_) {
......
......@@ -148,6 +148,7 @@ class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver {
void OnPictureInPictureAvailabilityChanged(RenderFrameHost* render_frame_host,
int delegate_id,
bool available);
void OnBufferUnderflow(RenderFrameHost* render_frame_host, int delegate_id);
device::mojom::WakeLock* GetAudioWakeLock();
......
......@@ -7610,6 +7610,11 @@ void WebContentsImpl::MediaResized(const gfx::Size& size,
observer.MediaResized(size, id);
}
void WebContentsImpl::MediaBufferUnderflow(const MediaPlayerId& id) {
for (auto& observer : observers_)
observer.MediaBufferUnderflow(id);
}
void WebContentsImpl::MediaEffectivelyFullscreenChanged(bool is_fullscreen) {
for (auto& observer : observers_)
observer.MediaEffectivelyFullscreenChanged(is_fullscreen);
......
......@@ -1052,6 +1052,10 @@ class CONTENT_EXPORT WebContentsImpl : public WebContents,
void MediaResized(const gfx::Size& size, const MediaPlayerId& id);
void MediaEffectivelyFullscreenChanged(bool is_fullscreen);
// Called by MediaWebContentsObserver when a buffer underflow occurs. See the
// WebContentsObserver function stubs for more details.
void MediaBufferUnderflow(const MediaPlayerId& id);
int GetCurrentlyPlayingVideoCount() override;
base::Optional<gfx::Size> GetFullscreenVideoSize() override;
......
......@@ -120,4 +120,7 @@ IPC_MESSAGE_ROUTED2(
int /* delegate_id, distinguishes instances */,
bool /* picture-in-picture availability */)
IPC_MESSAGE_ROUTED1(MediaPlayerDelegateHostMsg_OnBufferUnderflow,
int /* delegate_id, distinguishes instances */)
#endif // CONTENT_COMMON_MEDIA_MEDIA_PLAYER_DELEGATE_MESSAGES_H_
......@@ -582,6 +582,7 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener {
virtual void MediaEffectivelyFullscreenChanged(bool is_fullscreen) {}
virtual void MediaPictureInPictureChanged(bool is_picture_in_picture) {}
virtual void MediaMutedStatusChanged(const MediaPlayerId& id, bool muted) {}
virtual void MediaBufferUnderflow(const MediaPlayerId& id) {}
// Invoked when the renderer process changes the page scale factor.
virtual void OnPageScaleFactorChanged(float page_scale_factor) {}
......
......@@ -222,6 +222,11 @@ void RendererWebMediaPlayerDelegate::DidPictureInPictureAvailabilityChange(
routing_id(), delegate_id, available));
}
void RendererWebMediaPlayerDelegate::DidBufferUnderflow(int player_id) {
Send(new MediaPlayerDelegateHostMsg_OnBufferUnderflow(routing_id(),
player_id));
}
void RendererWebMediaPlayerDelegate::WasHidden() {
RecordAction(base::UserMetricsAction("Media.Hidden"));
......
......@@ -72,6 +72,7 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate
const media_session::MediaPosition& position) override;
void DidPictureInPictureAvailabilityChange(int delegate_id,
bool available) override;
void DidBufferUnderflow(int player_id) override;
// content::RenderFrameObserver overrides.
void WasHidden() override;
......
......@@ -2226,6 +2226,7 @@ void WebMediaPlayerImpl::OnBufferingStateChangeInternal(
!seeking_) {
underflow_timer_ = std::make_unique<base::ElapsedTimer>();
watch_time_reporter_->OnUnderflow();
delegate_->DidBufferUnderflow(delegate_id_);
if (playback_events_recorder_)
playback_events_recorder_->OnBuffering();
......
......@@ -244,6 +244,10 @@ class MockWebMediaPlayerDelegate : public blink::WebMediaPlayerDelegate {
DCHECK_EQ(player_id_, player_id);
}
void DidBufferUnderflow(int player_id) override {
DCHECK_EQ(player_id_, player_id);
}
bool IsFrameHidden() override { return is_hidden_; }
bool IsFrameClosed() override { return is_closed_; }
......
<!DOCTYPE html>
<html>
<head>
<title>MSE Player</title>
</head>
<body onload="runTest();">
<video controls></video>
<script src='eme_player_js/app_loader.js' type='text/javascript'></script>
<script type="text/javascript">
var video = document.querySelector('video');
// The test completes after media finishes playing all the segments.
// The test stops when an error occurs unexpectedly.
function runTest() {
var testConfig = new TestConfig();
testConfig.loadQueryParams();
if (typeof(testConfig.mediaFile) == "string") {
// Multiple media segments can be passed down separated by ';'
testConfig.mediaFile = testConfig.mediaFile.split(';')
}
Utils.installTitleEventHandler(video, 'error');
Utils.installTitleEventHandler(video, 'ended');
MSEPlayerUtils.loadMediaSegmentsFromTestConfig(testConfig);
video.play();
// Configure the iframe to be an MSE video that plays the same file and params.
var loc = window.location.toString();
var params = loc.split('?')[1];
document.getElementById("subframe-mse").src = "mse_player.html?" + params;
}
</script>
<iframe id="subframe-mse" title="Subframe with MSE Player">
</iframe>
</body>
</html>
......@@ -57,4 +57,8 @@ interface PreviewsResourceLoadingHintsReceiver {
// Sends the hints for reducing the data-costs of streaming
// media/videos from the browser to renderers.
SetLiteVideoHint(LiteVideoHint lite_video_hint);
// Notifies the renderers that active media request throttles should be stopped
// and not allow any new throttles.
StopThrottlingMediaRequests();
};
......@@ -130,6 +130,9 @@ class BLINK_PLATFORM_EXPORT WebMediaPlayerDelegate {
virtual void DidPictureInPictureAvailabilityChange(int delegate_id,
bool available) = 0;
// Notify that a buffer underflow event happened for the media player.
virtual void DidBufferUnderflow(int player_id) = 0;
// Notify that playback is stopped. This will drop wake locks and remove any
// external controls.
//
......
......@@ -131,6 +131,10 @@ class FakeWebMediaPlayerDelegate
EXPECT_EQ(delegate_id_, delegate_id);
}
void DidBufferUnderflow(int delegate_id) override {
EXPECT_EQ(delegate_id_, delegate_id);
}
void PlayerGone(int delegate_id) override {
EXPECT_EQ(delegate_id_, delegate_id);
is_gone_ = true;
......
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