Commit 77d5626f authored by Eugene Zemtsov's avatar Eugene Zemtsov Committed by Commit Bot

Report WebMediaPlayer related memory via MemoryDumpProvider

* MediaMemoryDumper - simple wrapper over MemoryDumpProvider
that takes care of registering/unregestering and calling a external
callback.

* Two instances of MediaMemoryDumper inside of WebMediaPlayerImpl.
One runs on the media thread and counts Demuxer's memory,
the other runs on the main thread and handles everything else
(video, audio, data source).

* The fact that demuxers need to be createad and destroyed on the
main thread, but used only on the media thread makes things interesting.

Bug: 1056839
Change-Id: I9a3386626789ddd1079d4d6759243d7622dea39c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2084083
Commit-Queue: Eugene Zemtsov <eugene@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#747585}
parent 25b44e5d
......@@ -212,6 +212,8 @@ jumbo_source_set("base") {
"media_url_params.h",
"media_util.cc",
"media_util.h",
"memory_dump_provider_proxy.cc",
"memory_dump_provider_proxy.h",
"mime_util.cc",
"mime_util.h",
"mime_util_internal.cc",
......
// Copyright 2020 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/base/memory_dump_provider_proxy.h"
#include <utility>
#include "base/trace_event/memory_dump_manager.h"
namespace media {
MemoryDumpProviderProxy::MemoryDumpProviderProxy(
const char* name,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MemoryDumpCB dump_cb)
: dump_cb_(std::move(dump_cb)) {
base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
this, name, std::move(task_runner));
}
MemoryDumpProviderProxy::~MemoryDumpProviderProxy() {
base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
this);
}
bool MemoryDumpProviderProxy::OnMemoryDump(
const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) {
dump_cb_.Run(pmd);
return true;
}
} // namespace media
// Copyright 2020 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_BASE_MEMORY_DUMP_PROVIDER_PROXY_H_
#define MEDIA_BASE_MEMORY_DUMP_PROVIDER_PROXY_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h"
#include "base/trace_event/memory_dump_provider.h"
#include "media/base/media_export.h"
namespace media {
using MemoryDumpCB =
base::RepeatingCallback<void(base::trace_event::ProcessMemoryDump* pmd)>;
class MEDIA_EXPORT MemoryDumpProviderProxy final
: public base::trace_event::MemoryDumpProvider {
public:
MemoryDumpProviderProxy(
const char* name,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MemoryDumpCB dump_cb);
~MemoryDumpProviderProxy() override;
bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
base::trace_event::ProcessMemoryDump* pmd) override;
private:
MemoryDumpCB dump_cb_;
DISALLOW_COPY_AND_ASSIGN(MemoryDumpProviderProxy);
};
} // namespace media
#endif // MEDIA_BASE_MEMORY_DUMP_PROVIDER_PROXY_H_
......@@ -27,6 +27,7 @@
#include "base/task/thread_pool.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "cc/layers/video_layer.h"
......@@ -40,6 +41,7 @@
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/base/media_url_demuxer.h"
#include "media/base/memory_dump_provider_proxy.h"
#include "media/base/text_renderer.h"
#include "media/base/timestamp_constants.h"
#include "media/base/video_frame.h"
......@@ -264,6 +266,24 @@ std::string SanitizeUserStringProperty(blink::WebString value) {
return base::IsStringUTF8(converted) ? converted : "[invalid property]";
}
void CreateAllocation(base::trace_event::ProcessMemoryDump* pmd,
int32_t id,
const char* name,
int64_t bytes) {
if (bytes <= 0)
return;
auto full_name =
base::StringPrintf("media/webmediaplayer/player_%d/%s", id, name);
auto* dump = pmd->CreateAllocatorDump(full_name);
dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
base::trace_event::MemoryAllocatorDump::kUnitsBytes, bytes);
auto* std_allocator = base::trace_event::MemoryDumpManager::GetInstance()
->system_allocator_pool_name();
pmd->AddSuballocation(dump->guid(), std_allocator);
}
} // namespace
class BufferedDataSourceHostImpl;
......@@ -392,6 +412,11 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
memory_usage_reporting_timer_.SetTaskRunner(
frame_->GetTaskRunner(blink::TaskType::kInternalMedia));
main_thread_mem_dumper_ = std::make_unique<MemoryDumpProviderProxy>(
"WebMediaPlayer_MainThread", main_task_runner_,
base::BindRepeating(&WebMediaPlayerImpl::OnMainThreadMemoryDump,
weak_this_, media_log_->id()));
#if defined(OS_ANDROID)
renderer_factory_selector_->SetRemotePlayStateChangeCB(
BindToCurrentLoop(base::BindRepeating(
......@@ -418,6 +443,11 @@ WebMediaPlayerImpl::~WebMediaPlayerImpl() {
// Finalize any watch time metrics before destroying the pipeline.
watch_time_reporter_.reset();
// Unregister dump providers on their corresponding threads.
media_task_runner_->DeleteSoon(FROM_HERE,
std::move(media_thread_mem_dumper_));
main_thread_mem_dumper_.reset();
// The underlying Pipeline must be stopped before it is destroyed.
//
// Note: This destruction happens synchronously on the media thread and
......@@ -1777,6 +1807,8 @@ void WebMediaPlayerImpl::OnError(PipelineStatus status) {
pipeline_controller_->Stop();
SetMemoryReportingState(false);
media_task_runner_->DeleteSoon(FROM_HERE,
std::move(media_thread_mem_dumper_));
// Trampoline through the media task runner to destruct the demuxer and
// data source now that we're switching to HLS playback.
......@@ -2714,11 +2746,11 @@ void WebMediaPlayerImpl::StartPipeline() {
// reporter.
video_decode_stats_reporter_.reset();
demuxer_ = std::make_unique<MediaUrlDemuxer>(
SetDemuxer(std::make_unique<MediaUrlDemuxer>(
media_task_runner_, loaded_url_,
frame_->GetDocument().SiteForCookies().RepresentativeUrl(),
frame_->GetDocument().TopFrameOrigin(),
allow_media_player_renderer_credentials_, demuxer_found_hls_);
allow_media_player_renderer_credentials_, demuxer_found_hls_));
pipeline_controller_->Start(Pipeline::StartType::kNormal, demuxer_.get(),
this, false, false);
return;
......@@ -2735,9 +2767,9 @@ void WebMediaPlayerImpl::StartPipeline() {
BindToCurrentLoop(base::BindRepeating(
&WebMediaPlayerImpl::OnFFmpegMediaTracksUpdated, weak_this_));
demuxer_ = std::make_unique<FFmpegDemuxer>(
SetDemuxer(std::make_unique<FFmpegDemuxer>(
media_task_runner_, data_source_.get(), encrypted_media_init_data_cb,
media_tracks_updated_cb, media_log_.get(), IsLocalFile(loaded_url_));
media_tracks_updated_cb, media_log_.get(), IsLocalFile(loaded_url_)));
#else
OnError(PipelineStatus::DEMUXER_ERROR_COULD_NOT_OPEN);
return;
......@@ -2752,7 +2784,7 @@ void WebMediaPlayerImpl::StartPipeline() {
BindToCurrentLoop(base::Bind(
&WebMediaPlayerImpl::OnProgress, weak_this_)),
encrypted_media_init_data_cb, media_log_.get());
demuxer_.reset(chunk_demuxer_);
SetDemuxer(std::unique_ptr<Demuxer>(chunk_demuxer_));
if (base::FeatureList::IsEnabled(kMemoryPressureBasedSourceBufferGC)) {
// base::Unretained is safe because |this| owns memory_pressure_listener_.
......@@ -3104,6 +3136,23 @@ WebMediaPlayerImpl::UpdatePlayState_ComputePlayState(bool is_flinging,
return result;
}
void WebMediaPlayerImpl::SetDemuxer(std::unique_ptr<Demuxer> demuxer) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
DCHECK(!demuxer_);
DCHECK(!media_thread_mem_dumper_);
DCHECK(demuxer);
demuxer_ = std::move(demuxer);
// base::Unretained() is safe here. |demuxer_| is destroyed on the main
// thread, but before doing it ~WebMediaPlayerImpl() posts a media thread task
// that deletes media_thread_mem_dumper_ and waits for it to finish.
media_thread_mem_dumper_ = std::make_unique<MemoryDumpProviderProxy>(
"WebMediaPlayer_MediaThread", media_task_runner_,
base::BindRepeating(&WebMediaPlayerImpl::OnMediaThreadMemoryDump,
media_log_->id(), base::Unretained(demuxer_.get())));
}
void WebMediaPlayerImpl::ReportMemoryUsage() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
......@@ -3182,6 +3231,38 @@ void WebMediaPlayerImpl::FinishMemoryUsageReport(int64_t demuxer_memory_usage) {
}
}
void WebMediaPlayerImpl::OnMainThreadMemoryDump(
int32_t id,
base::trace_event::ProcessMemoryDump* pmd) {
const PipelineStatistics stats = GetPipelineStatistics();
bool suspended = pipeline_controller_->IsPipelineSuspended();
auto parent_dump_name =
base::StringPrintf("media/webmediaplayer/player_%d", id);
auto player_state =
base::StringPrintf("Paused: %d Ended: %d ReadyState: %d Suspended: %d",
paused_, ended_, GetReadyState(), suspended);
auto* parent_dump = pmd->CreateAllocatorDump(parent_dump_name);
parent_dump->AddString("player_state", "", player_state);
CreateAllocation(pmd, id, "audio", stats.audio_memory_usage);
CreateAllocation(pmd, id, "video", stats.video_memory_usage);
if (data_source_)
CreateAllocation(pmd, id, "data_source", data_source_->GetMemoryUsage());
}
// static
void WebMediaPlayerImpl::OnMediaThreadMemoryDump(
int32_t id,
Demuxer* demuxer,
base::trace_event::ProcessMemoryDump* pmd) {
if (!demuxer)
return;
CreateAllocation(pmd, id, "demuxer", demuxer->GetMemoryUsage());
}
void WebMediaPlayerImpl::ScheduleIdlePauseTimer() {
// Only schedule the pause timer if we're not paused or paused but going to
// resume when foregrounded, and are suspended and have audio.
......
......@@ -83,6 +83,7 @@ class CdmContextRef;
class ChunkDemuxer;
class VideoDecodeStatsReporter;
class MediaLog;
class MemoryDumpProviderProxy;
class UrlIndex;
class VideoFrameCompositor;
class WatchTimeReporter;
......@@ -470,6 +471,8 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
void SetMemoryReportingState(bool is_memory_reporting_enabled);
void SetSuspendState(bool is_suspended);
void SetDemuxer(std::unique_ptr<Demuxer> demuxer);
// Called at low frequency to tell external observers how much memory we're
// using for video playback. Called by |memory_usage_reporting_timer_|.
// Memory usage reporting is done in two steps, because |demuxer_| must be
......@@ -477,6 +480,13 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
void ReportMemoryUsage();
void FinishMemoryUsageReport(int64_t demuxer_memory_usage);
void OnMainThreadMemoryDump(int32_t id,
base::trace_event::ProcessMemoryDump* pmd);
static void OnMediaThreadMemoryDump(
int32_t id,
Demuxer* demuxer,
base::trace_event::ProcessMemoryDump* pmd);
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
......@@ -755,6 +765,8 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
base::RepeatingTimer memory_usage_reporting_timer_;
WebMediaPlayerParams::AdjustAllocatedMemoryCB adjust_allocated_memory_cb_;
int64_t last_reported_memory_usage_ = 0;
std::unique_ptr<MemoryDumpProviderProxy> main_thread_mem_dumper_;
std::unique_ptr<MemoryDumpProviderProxy> media_thread_mem_dumper_;
// Routes audio playback to either AudioRendererSink or WebAudio.
scoped_refptr<blink::WebAudioSourceProviderImpl> audio_source_provider_;
......
......@@ -24,12 +24,14 @@
#include "base/test/simple_test_tick_clock.h"
#include "base/threading/thread.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/memory_dump_manager.h"
#include "build/build_config.h"
#include "cc/layers/layer.h"
#include "components/viz/test/test_context_provider.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
#include "media/base/memory_dump_provider_proxy.h"
#include "media/base/mock_audio_renderer_sink.h"
#include "media/base/mock_filters.h"
#include "media/base/mock_media_log.h"
......@@ -88,6 +90,7 @@ namespace media {
constexpr char kAudioOnlyTestFile[] = "sfx-opus-441.webm";
constexpr char kVideoOnlyTestFile[] = "bear-320x240-video-only.webm";
constexpr char kVideoAudioTestFile[] = "bear-320x240-16x9-aspect.webm";
constexpr char kEncryptedVideoOnlyTestFile[] = "bear-320x240-av_enc-v.webm";
constexpr base::TimeDelta kAudioOnlyTestFileDuration =
......@@ -323,7 +326,9 @@ class WebMediaPlayerImplTest : public testing::Test {
nullptr,
nullptr)),
context_provider_(viz::TestContextProvider::Create()),
audio_parameters_(TestAudioParameters::Normal()) {
audio_parameters_(TestAudioParameters::Normal()),
memory_dump_manager_(
base::trace_event::MemoryDumpManager::CreateInstanceForTesting()) {
media_thread_.StartAndWaitForTesting();
}
......@@ -409,6 +414,8 @@ class WebMediaPlayerImplTest : public testing::Test {
}
~WebMediaPlayerImplTest() override {
if (!wmpi_)
return;
EXPECT_CALL(client_, SetCcLayer(nullptr));
EXPECT_CALL(client_, MediaRemotingStopped(_));
......@@ -793,6 +800,15 @@ class WebMediaPlayerImplTest : public testing::Test {
wmpi_->SetCdmInternal(web_cdm_.get());
}
MemoryDumpProviderProxy* GetMainThreadMemDumper() {
return wmpi_->main_thread_mem_dumper_.get();
}
MemoryDumpProviderProxy* GetMediaThreadMemDumper() {
return wmpi_->media_thread_mem_dumper_.get();
}
int32_t GetMediaLogId() { return media_log_->id(); }
// "Media" thread. This is necessary because WMPI destruction waits on a
// WaitableEvent.
base::Thread media_thread_;
......@@ -852,6 +868,8 @@ class WebMediaPlayerImplTest : public testing::Test {
// The WebMediaPlayerImpl instance under test.
std::unique_ptr<WebMediaPlayerImpl> wmpi_;
std::unique_ptr<base::trace_event::MemoryDumpManager> memory_dump_manager_;
private:
DISALLOW_COPY_AND_ASSIGN(WebMediaPlayerImplTest);
};
......@@ -2025,6 +2043,79 @@ TEST_F(WebMediaPlayerImplTest, OnProgressClearsStale) {
}
}
TEST_F(WebMediaPlayerImplTest, MemDumpProvidersRegistration) {
auto* dump_manager = base::trace_event::MemoryDumpManager::GetInstance();
InitializeWebMediaPlayerImpl();
wmpi_->SetPreload(blink::WebMediaPlayer::kPreloadAuto);
auto* main_dumper = GetMainThreadMemDumper();
EXPECT_TRUE(dump_manager->IsDumpProviderRegisteredForTesting(main_dumper));
LoadAndWaitForCurrentData(kVideoAudioTestFile);
auto* media_dumper = GetMediaThreadMemDumper();
EXPECT_TRUE(dump_manager->IsDumpProviderRegisteredForTesting(media_dumper));
CycleThreads();
wmpi_.reset();
CycleThreads();
EXPECT_FALSE(dump_manager->IsDumpProviderRegisteredForTesting(main_dumper));
EXPECT_FALSE(dump_manager->IsDumpProviderRegisteredForTesting(media_dumper));
}
TEST_F(WebMediaPlayerImplTest, MemDumpReporting) {
InitializeWebMediaPlayerImpl();
wmpi_->SetPreload(blink::WebMediaPlayer::kPreloadAuto);
LoadAndWaitForCurrentData(kVideoAudioTestFile);
CycleThreads();
base::trace_event::MemoryDumpRequestArgs args = {
1 /* dump_guid*/, base::trace_event::MemoryDumpType::EXPLICITLY_TRIGGERED,
base::trace_event::MemoryDumpLevelOfDetail::LIGHT};
struct TestContext {
int32_t id = 0;
bool dump_was_created = false;
} ctx;
ctx.id = GetMediaLogId();
auto on_memory_dump_done =
[](struct TestContext* ctx, bool success, uint64_t dump_guid,
std::unique_ptr<base::trace_event::ProcessMemoryDump> pmd) {
ASSERT_TRUE(success);
const auto& dumps = pmd->allocator_dumps();
std::vector<const char*> allocations = {"audio", "video", "data_source",
"demuxer"};
for (const char* name : allocations) {
auto it = dumps.find(base::StringPrintf(
"media/webmediaplayer/player_%d/%s", ctx->id, name));
ASSERT_NE(dumps.end(), it) << name;
ASSERT_GT(it->second->GetSizeInternal(), 0u) << name;
}
auto it = dumps.find(
base::StringPrintf("media/webmediaplayer/player_%d", ctx->id));
ASSERT_NE(dumps.end(), it);
const auto& entries = it->second->entries();
auto player_state_id =
find_if(entries.begin(), entries.end(), [](const auto& e) {
return e.name == "player_state" && !e.value_string.empty();
});
ASSERT_NE(entries.end(), player_state_id);
ctx->dump_was_created = true;
};
base::trace_event::MemoryDumpManager::GetInstance()->CreateProcessDump(
args, base::BindOnce(on_memory_dump_done, base::Unretained(&ctx)));
CycleThreads();
EXPECT_TRUE(ctx.dump_was_created);
}
class WebMediaPlayerImplBackgroundBehaviorTest
: public WebMediaPlayerImplTest,
public ::testing::WithParamInterface<
......
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