Commit d0ab2edb authored by dalecurtis's avatar dalecurtis Committed by Commit bot

Reduce number of active codecs on low end devices.

This fixes OOM crashes in the mediaserver process on older devices
by suspending players more frequently and preventing unused players
and SurfaceTextures from ever being created. Both fixes are required
to enable playback on JellyBean devices; independently neither is
sufficient for playback to succeed.

- On low end devices, suspends all idle players immediately upon
  playback of another player.
- On low end devices, if a codec already exists, codec creation is
  deferred until the first Decode() call. Resolves issues with sites
  like vimeo.com that are appending initialization segments to MSE
  but never any further data.
- Once more than 2 players exist on a low end device, every extra
  player will cause immediate idle player collection. Normal devices
  will now do this after 8 players.

A followup CL will set preload=none for all JellyBean devices.

BUG=612909
TEST=manual, new tests.

Review-Url: https://codereview.chromium.org/2333983002
Cr-Commit-Position: refs/heads/master@{#419375}
parent 69142169
...@@ -9,11 +9,16 @@ ...@@ -9,11 +9,16 @@
#include "base/auto_reset.h" #include "base/auto_reset.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics_action.h" #include "base/metrics/user_metrics_action.h"
#include "base/sys_info.h"
#include "content/common/media/media_player_delegate_messages.h" #include "content/common/media/media_player_delegate_messages.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_thread.h"
#include "third_party/WebKit/public/platform/WebMediaPlayer.h" #include "third_party/WebKit/public/platform/WebMediaPlayer.h"
#if defined(OS_ANDROID)
#include "base/android/build_info.h"
#endif
namespace { namespace {
void RecordAction(const base::UserMetricsAction& action) { void RecordAction(const base::UserMetricsAction& action) {
...@@ -27,10 +32,21 @@ namespace media { ...@@ -27,10 +32,21 @@ namespace media {
RendererWebMediaPlayerDelegate::RendererWebMediaPlayerDelegate( RendererWebMediaPlayerDelegate::RendererWebMediaPlayerDelegate(
content::RenderFrame* render_frame) content::RenderFrame* render_frame)
: RenderFrameObserver(render_frame), : RenderFrameObserver(render_frame),
idle_cleanup_timer_(true, true),
default_tick_clock_(new base::DefaultTickClock()), default_tick_clock_(new base::DefaultTickClock()),
tick_clock_(default_tick_clock_.get()) { tick_clock_(default_tick_clock_.get()) {
idle_cleanup_interval_ = base::TimeDelta::FromSeconds(5); idle_cleanup_interval_ = base::TimeDelta::FromSeconds(5);
idle_timeout_ = base::TimeDelta::FromSeconds(15); idle_timeout_ = base::TimeDelta::FromSeconds(15);
// To conserve resources, cleanup idle players more often on low end devices.
is_low_end_device_ = base::SysInfo::IsLowEndDevice();
#if defined(OS_ANDROID)
// On Android, due to the instability of the OS level media components, we
// consider all pre-KitKat devices to be low end.
is_low_end_device_ |=
base::android::BuildInfo::GetInstance()->sdk_int() <= 18;
#endif
} }
RendererWebMediaPlayerDelegate::~RendererWebMediaPlayerDelegate() {} RendererWebMediaPlayerDelegate::~RendererWebMediaPlayerDelegate() {}
...@@ -63,6 +79,11 @@ void RendererWebMediaPlayerDelegate::DidPlay( ...@@ -63,6 +79,11 @@ void RendererWebMediaPlayerDelegate::DidPlay(
else else
playing_videos_.erase(delegate_id); playing_videos_.erase(delegate_id);
RemoveIdleDelegate(delegate_id); RemoveIdleDelegate(delegate_id);
// Upon receipt of a playback request, suspend everything that's not used.
if (is_low_end_device_)
CleanupIdleDelegates(base::TimeDelta());
Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying( Send(new MediaPlayerDelegateHostMsg_OnMediaPlaying(
routing_id(), delegate_id, has_video, has_audio, is_remote, routing_id(), delegate_id, has_video, has_audio, is_remote,
media_content_type)); media_content_type));
...@@ -80,7 +101,6 @@ void RendererWebMediaPlayerDelegate::DidPause(int delegate_id, ...@@ -80,7 +101,6 @@ void RendererWebMediaPlayerDelegate::DidPause(int delegate_id,
void RendererWebMediaPlayerDelegate::PlayerGone(int delegate_id) { void RendererWebMediaPlayerDelegate::PlayerGone(int delegate_id) {
DCHECK(id_map_.Lookup(delegate_id)); DCHECK(id_map_.Lookup(delegate_id));
RemoveIdleDelegate(delegate_id);
playing_videos_.erase(delegate_id); playing_videos_.erase(delegate_id);
Send(new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(), Send(new MediaPlayerDelegateHostMsg_OnMediaDestroyed(routing_id(),
delegate_id)); delegate_id));
...@@ -126,10 +146,12 @@ bool RendererWebMediaPlayerDelegate::OnMessageReceived( ...@@ -126,10 +146,12 @@ bool RendererWebMediaPlayerDelegate::OnMessageReceived(
void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting( void RendererWebMediaPlayerDelegate::SetIdleCleanupParamsForTesting(
base::TimeDelta idle_timeout, base::TimeDelta idle_timeout,
base::TickClock* tick_clock) { base::TickClock* tick_clock,
bool is_low_end_device) {
idle_cleanup_interval_ = base::TimeDelta(); idle_cleanup_interval_ = base::TimeDelta();
idle_timeout_ = idle_timeout; idle_timeout_ = idle_timeout;
tick_clock_ = tick_clock; tick_clock_ = tick_clock;
is_low_end_device_ = is_low_end_device;
} }
void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int delegate_id) { void RendererWebMediaPlayerDelegate::OnMediaDelegatePause(int delegate_id) {
...@@ -171,9 +193,16 @@ void RendererWebMediaPlayerDelegate::AddIdleDelegate(int delegate_id) { ...@@ -171,9 +193,16 @@ void RendererWebMediaPlayerDelegate::AddIdleDelegate(int delegate_id) {
idle_delegate_map_[delegate_id] = tick_clock_->NowTicks(); idle_delegate_map_[delegate_id] = tick_clock_->NowTicks();
if (!idle_cleanup_timer_.IsRunning()) { if (!idle_cleanup_timer_.IsRunning()) {
idle_cleanup_timer_.Start( idle_cleanup_timer_.Start(
FROM_HERE, idle_cleanup_interval_, this, FROM_HERE, idle_cleanup_interval_,
&RendererWebMediaPlayerDelegate::CleanupIdleDelegates); base::Bind(&RendererWebMediaPlayerDelegate::CleanupIdleDelegates,
base::Unretained(this), idle_timeout_));
} }
// When we reach the maximum number of idle players, aggressively suspend idle
// delegates to try and remain under the limit. Values chosen after testing on
// a Galaxy Nexus device for http://crbug.com/612909.
if (idle_delegate_map_.size() > (is_low_end_device_ ? 2u : 8u))
CleanupIdleDelegates(base::TimeDelta());
} }
void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) { void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) {
...@@ -189,14 +218,15 @@ void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) { ...@@ -189,14 +218,15 @@ void RendererWebMediaPlayerDelegate::RemoveIdleDelegate(int delegate_id) {
idle_cleanup_timer_.Stop(); idle_cleanup_timer_.Stop();
} }
void RendererWebMediaPlayerDelegate::CleanupIdleDelegates() { void RendererWebMediaPlayerDelegate::CleanupIdleDelegates(
base::TimeDelta timeout) {
// Iterate over the delegates and suspend the idle ones. Note: The call to // Iterate over the delegates and suspend the idle ones. Note: The call to
// OnHidden() can trigger calls into RemoveIdleDelegate(), so for iterator // OnHidden() can trigger calls into RemoveIdleDelegate(), so for iterator
// validity we set |idle_cleanup_running_| to true and defer deletions. // validity we set |idle_cleanup_running_| to true and defer deletions.
base::AutoReset<bool> scoper(&idle_cleanup_running_, true); base::AutoReset<bool> scoper(&idle_cleanup_running_, true);
const base::TimeTicks now = tick_clock_->NowTicks(); const base::TimeTicks now = tick_clock_->NowTicks();
for (auto& idle_delegate_entry : idle_delegate_map_) { for (auto& idle_delegate_entry : idle_delegate_map_) {
if (now - idle_delegate_entry.second > idle_timeout_) { if (now - idle_delegate_entry.second > timeout) {
id_map_.Lookup(idle_delegate_entry.first)->OnSuspendRequested(false); id_map_.Lookup(idle_delegate_entry.first)->OnSuspendRequested(false);
// Whether or not the player accepted the suspension, mark it for removal // Whether or not the player accepted the suspension, mark it for removal
......
...@@ -63,11 +63,12 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate ...@@ -63,11 +63,12 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate
bool OnMessageReceived(const IPC::Message& msg) override; bool OnMessageReceived(const IPC::Message& msg) override;
void OnDestruct() override; void OnDestruct() override;
// Zeros out |idle_cleanup_interval_|, and sets |idle_timeout_| to // Zeros out |idle_cleanup_interval_|, sets |idle_timeout_| to |idle_timeout|,
// |idle_timeout|. A zero cleanup interval will cause the idle timer to run // and |is_low_end_device_| to |is_low_end_device|. A zero cleanup interval
// with each run of the message loop. // will cause the idle timer to run with each run of the message loop.
void SetIdleCleanupParamsForTesting(base::TimeDelta idle_timeout, void SetIdleCleanupParamsForTesting(base::TimeDelta idle_timeout,
base::TickClock* tick_clock); base::TickClock* tick_clock,
bool is_low_end_device);
bool IsIdleCleanupTimerRunningForTesting() const { bool IsIdleCleanupTimerRunningForTesting() const {
return idle_cleanup_timer_.IsRunning(); return idle_cleanup_timer_.IsRunning();
} }
...@@ -86,8 +87,9 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate ...@@ -86,8 +87,9 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate
void AddIdleDelegate(int delegate_id); void AddIdleDelegate(int delegate_id);
void RemoveIdleDelegate(int delegate_id); void RemoveIdleDelegate(int delegate_id);
// Runs periodically to suspend idle delegates in |idle_delegate_map_|. // Runs periodically to suspend idle delegates in |idle_delegate_map_| which
void CleanupIdleDelegates(); // have been idle for longer than |timeout|.
void CleanupIdleDelegates(base::TimeDelta timeout);
// Setter for |is_playing_background_video_| that updates the metrics. // Setter for |is_playing_background_video_| that updates the metrics.
void SetIsPlayingBackgroundVideo(bool is_playing); void SetIsPlayingBackgroundVideo(bool is_playing);
...@@ -99,7 +101,7 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate ...@@ -99,7 +101,7 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate
// inactivity these players will be suspended to release unused resources. // inactivity these players will be suspended to release unused resources.
bool idle_cleanup_running_ = false; bool idle_cleanup_running_ = false;
std::map<int, base::TimeTicks> idle_delegate_map_; std::map<int, base::TimeTicks> idle_delegate_map_;
base::RepeatingTimer idle_cleanup_timer_; base::Timer idle_cleanup_timer_;
// Amount of time allowed to elapse after a delegate enters the paused before // Amount of time allowed to elapse after a delegate enters the paused before
// the delegate is suspended. // the delegate is suspended.
...@@ -129,6 +131,10 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate ...@@ -129,6 +131,10 @@ class CONTENT_EXPORT RendererWebMediaPlayerDelegate
// not. // not.
std::set<int> playing_videos_; std::set<int> playing_videos_;
// Determined at construction time based on system information; determines
// when the idle cleanup timer should be fired more aggressively.
bool is_low_end_device_;
DISALLOW_COPY_AND_ASSIGN(RendererWebMediaPlayerDelegate); DISALLOW_COPY_AND_ASSIGN(RendererWebMediaPlayerDelegate);
}; };
......
...@@ -173,13 +173,89 @@ TEST_F(RendererWebMediaPlayerDelegateTest, DeliversObserverNotifications) { ...@@ -173,13 +173,89 @@ TEST_F(RendererWebMediaPlayerDelegateTest, DeliversObserverNotifications) {
delegate_manager_->RemoveObserver(delegate_id); delegate_manager_->RemoveObserver(delegate_id);
} }
TEST_F(RendererWebMediaPlayerDelegateTest, PlaySuspendsLowEndIdleDelegates) {
// Start the tick clock off at a non-null value.
base::SimpleTestTickClock tick_clock;
tick_clock.Advance(base::TimeDelta::FromSeconds(1234));
const base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(10);
delegate_manager_->SetIdleCleanupParamsForTesting(kIdleTimeout, &tick_clock,
true);
EXPECT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
// Add two observers, both of which should keep the idle timer running.
testing::StrictMock<MockWebMediaPlayerDelegateObserver> observer_1;
const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1);
EXPECT_TRUE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
testing::StrictMock<MockWebMediaPlayerDelegateObserver> observer_2;
const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2);
EXPECT_TRUE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
// Calling play on the first player should suspend the other idle player.
EXPECT_CALL(observer_2, OnSuspendRequested(false))
.WillOnce(RunClosure(base::Bind(
&RendererWebMediaPlayerDelegate::PlayerGone,
base::Unretained(delegate_manager_.get()), delegate_id_2)));
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
run_loop.QuitClosure());
tick_clock.Advance(base::TimeDelta::FromMicroseconds(1));
delegate_manager_->DidPlay(delegate_id_1, true, true, false,
media::MediaContentType::Persistent);
run_loop.Run();
}
TEST_F(RendererWebMediaPlayerDelegateTest, MaxLowEndIdleDelegates) {
// Start the tick clock off at a non-null value.
base::SimpleTestTickClock tick_clock;
tick_clock.Advance(base::TimeDelta::FromSeconds(1234));
const base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(10);
delegate_manager_->SetIdleCleanupParamsForTesting(kIdleTimeout, &tick_clock,
true);
EXPECT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
// Add two observers, both of which should keep the idle timer running.
testing::StrictMock<MockWebMediaPlayerDelegateObserver> observer_1;
const int delegate_id_1 = delegate_manager_->AddObserver(&observer_1);
EXPECT_TRUE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
testing::StrictMock<MockWebMediaPlayerDelegateObserver> observer_2;
const int delegate_id_2 = delegate_manager_->AddObserver(&observer_2);
EXPECT_TRUE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
tick_clock.Advance(base::TimeDelta::FromMicroseconds(1));
// Just adding a third idle observer should suspend the others.
EXPECT_CALL(observer_1, OnSuspendRequested(false))
.WillOnce(RunClosure(base::Bind(
&RendererWebMediaPlayerDelegate::PlayerGone,
base::Unretained(delegate_manager_.get()), delegate_id_1)));
EXPECT_CALL(observer_2, OnSuspendRequested(false))
.WillOnce(RunClosure(base::Bind(
&RendererWebMediaPlayerDelegate::PlayerGone,
base::Unretained(delegate_manager_.get()), delegate_id_2)));
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
run_loop.QuitClosure());
testing::StrictMock<MockWebMediaPlayerDelegateObserver> observer_3;
delegate_manager_->AddObserver(&observer_3);
EXPECT_TRUE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
run_loop.Run();
}
TEST_F(RendererWebMediaPlayerDelegateTest, IdleDelegatesAreSuspended) { TEST_F(RendererWebMediaPlayerDelegateTest, IdleDelegatesAreSuspended) {
// Start the tick clock off at a non-null value. // Start the tick clock off at a non-null value.
base::SimpleTestTickClock tick_clock; base::SimpleTestTickClock tick_clock;
tick_clock.Advance(base::TimeDelta::FromSeconds(1234)); tick_clock.Advance(base::TimeDelta::FromSeconds(1234));
const base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(2); const base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(2);
delegate_manager_->SetIdleCleanupParamsForTesting(kIdleTimeout, &tick_clock); delegate_manager_->SetIdleCleanupParamsForTesting(kIdleTimeout, &tick_clock,
false);
EXPECT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting()); EXPECT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
// Just adding an observer should start the idle timer. // Just adding an observer should start the idle timer.
...@@ -264,7 +340,8 @@ TEST_F(RendererWebMediaPlayerDelegateTest, IdleDelegatesIgnoresSuspendRequest) { ...@@ -264,7 +340,8 @@ TEST_F(RendererWebMediaPlayerDelegateTest, IdleDelegatesIgnoresSuspendRequest) {
tick_clock.Advance(base::TimeDelta::FromSeconds(1234)); tick_clock.Advance(base::TimeDelta::FromSeconds(1234));
const base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(2); const base::TimeDelta kIdleTimeout = base::TimeDelta::FromSeconds(2);
delegate_manager_->SetIdleCleanupParamsForTesting(kIdleTimeout, &tick_clock); delegate_manager_->SetIdleCleanupParamsForTesting(kIdleTimeout, &tick_clock,
false);
EXPECT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting()); EXPECT_FALSE(delegate_manager_->IsIdleCleanupTimerRunningForTesting());
testing::StrictMock<MockWebMediaPlayerDelegateObserver> observer_1; testing::StrictMock<MockWebMediaPlayerDelegateObserver> observer_1;
......
...@@ -1579,13 +1579,7 @@ void WebMediaPlayerImpl::UpdatePlayState() { ...@@ -1579,13 +1579,7 @@ void WebMediaPlayerImpl::UpdatePlayState() {
} }
void WebMediaPlayerImpl::SetDelegateState(DelegateState new_state) { void WebMediaPlayerImpl::SetDelegateState(DelegateState new_state) {
if (!delegate_) if (!delegate_ || delegate_state_ == new_state)
return;
// Dedupe state changes in the general case, but make an exception for gone
// since the delegate will use that information to decide when the idle timer
// should be fired.
if (delegate_state_ == new_state && new_state != DelegateState::GONE)
return; return;
delegate_state_ = new_state; delegate_state_ = new_state;
......
...@@ -276,12 +276,10 @@ TEST_F(WebMediaPlayerImplTest, DidLoadingProgressClearsIdle) { ...@@ -276,12 +276,10 @@ TEST_F(WebMediaPlayerImplTest, DidLoadingProgressClearsIdle) {
EXPECT_CALL(strict_delegate, IsPlayingBackgroundVideo()).Times(AnyNumber()); EXPECT_CALL(strict_delegate, IsPlayingBackgroundVideo()).Times(AnyNumber());
InitializeWebMediaPlayerImpl(strict_delegate.AsWeakPtr()); InitializeWebMediaPlayerImpl(strict_delegate.AsWeakPtr());
EXPECT_FALSE(IsSuspended()); EXPECT_FALSE(IsSuspended());
EXPECT_CALL(strict_delegate, PlayerGone(0));
wmpi_->OnSuspendRequested(false); wmpi_->OnSuspendRequested(false);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_TRUE(IsSuspended()); EXPECT_TRUE(IsSuspended());
AddBufferedRanges(); AddBufferedRanges();
EXPECT_CALL(strict_delegate, PlayerGone(0));
wmpi_->didLoadingProgress(); wmpi_->didLoadingProgress();
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
EXPECT_FALSE(IsSuspended()); EXPECT_FALSE(IsSuspended());
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h" #include "base/task_runner_util.h"
#include "base/threading/thread_checker.h" #include "base/threading/thread_checker.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
...@@ -324,6 +325,16 @@ class AVDAManager { ...@@ -324,6 +325,16 @@ class AVDAManager {
waiter->OnSurfaceAvailable(true); waiter->OnSurfaceAvailable(true);
} }
// On low end devices (< KitKat is always low-end due to buggy MediaCodec),
// defer the surface creation until the codec is actually used if we know no
// software fallback exists.
bool ShouldDeferSurfaceCreation(int surface_id, VideoCodec codec) {
return surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID &&
codec == kCodecH264 && !thread_avda_instances_.empty() &&
(base::android::BuildInfo::GetInstance()->sdk_int() <= 18 ||
base::SysInfo::IsLowEndDevice());
}
private: private:
friend struct base::DefaultLazyInstanceTraits<AVDAManager>; friend struct base::DefaultLazyInstanceTraits<AVDAManager>;
...@@ -425,6 +436,7 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( ...@@ -425,6 +436,7 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator(
defer_errors_(false), defer_errors_(false),
deferred_initialization_pending_(false), deferred_initialization_pending_(false),
codec_needs_reset_(false), codec_needs_reset_(false),
defer_surface_creation_(false),
weak_this_factory_(this) {} weak_this_factory_(this) {}
AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
...@@ -471,15 +483,6 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, ...@@ -471,15 +483,6 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config,
codec_config_->initial_expected_coded_size_ = codec_config_->initial_expected_coded_size_ =
config.initial_expected_coded_size; config.initial_expected_coded_size;
// We signalled that we support deferred initialization, so see if the client
// does also.
deferred_initialization_pending_ = config.is_deferred_initialization_allowed;
if (config_.is_encrypted && !deferred_initialization_pending_) {
DLOG(ERROR) << "Deferred initialization must be used for encrypted streams";
return false;
}
if (codec_config_->codec_ != kCodecVP8 && if (codec_config_->codec_ != kCodecVP8 &&
codec_config_->codec_ != kCodecVP9 && codec_config_->codec_ != kCodecVP9 &&
#if BUILDFLAG(ENABLE_HEVC_DEMUXING) #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
...@@ -507,13 +510,31 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, ...@@ -507,13 +510,31 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config,
return false; return false;
} }
if (!make_context_current_cb_.Run()) { // If we're low on resources, we may decide to defer creation of the surface
LOG(ERROR) << "Failed to make this decoder's GL context current."; // until the codec is actually used.
if (g_avda_manager.Get().ShouldDeferSurfaceCreation(config_.surface_id,
codec_config_->codec_)) {
DCHECK(!deferred_initialization_pending_);
// We should never be here if a SurfaceView is required.
DCHECK_EQ(config_.surface_id, Config::kNoSurfaceID);
DCHECK(g_avda_manager.Get().AllocateSurface(config_.surface_id, this));
defer_surface_creation_ = true;
NotifyInitializationComplete(true);
return true;
}
// We signaled that we support deferred initialization, so see if the client
// does also.
deferred_initialization_pending_ = config.is_deferred_initialization_allowed;
if (config_.is_encrypted && !deferred_initialization_pending_) {
DLOG(ERROR) << "Deferred initialization must be used for encrypted streams";
return false; return false;
} }
if (g_avda_manager.Get().AllocateSurface(config_.surface_id, this)) { if (g_avda_manager.Get().AllocateSurface(config_.surface_id, this)) {
// We have succesfully owned the surface, so finish initialization now. // We have successfully owned the surface, so finish initialization now.
return InitializePictureBufferManager(); return InitializePictureBufferManager();
} }
...@@ -524,6 +545,7 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, ...@@ -524,6 +545,7 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config,
void AndroidVideoDecodeAccelerator::OnSurfaceAvailable(bool success) { void AndroidVideoDecodeAccelerator::OnSurfaceAvailable(bool success) {
DCHECK(deferred_initialization_pending_); DCHECK(deferred_initialization_pending_);
DCHECK(!defer_surface_creation_);
if (!success || !InitializePictureBufferManager()) { if (!success || !InitializePictureBufferManager()) {
NotifyInitializationComplete(false); NotifyInitializationComplete(false);
...@@ -532,6 +554,11 @@ void AndroidVideoDecodeAccelerator::OnSurfaceAvailable(bool success) { ...@@ -532,6 +554,11 @@ void AndroidVideoDecodeAccelerator::OnSurfaceAvailable(bool success) {
} }
bool AndroidVideoDecodeAccelerator::InitializePictureBufferManager() { bool AndroidVideoDecodeAccelerator::InitializePictureBufferManager() {
if (!make_context_current_cb_.Run()) {
LOG(ERROR) << "Failed to make this decoder's GL context current.";
return false;
}
codec_config_->surface_ = codec_config_->surface_ =
picture_buffer_manager_.Initialize(this, config_.surface_id); picture_buffer_manager_.Initialize(this, config_.surface_id);
if (codec_config_->surface_.IsEmpty()) if (codec_config_->surface_.IsEmpty())
...@@ -552,7 +579,8 @@ bool AndroidVideoDecodeAccelerator::InitializePictureBufferManager() { ...@@ -552,7 +579,8 @@ bool AndroidVideoDecodeAccelerator::InitializePictureBufferManager() {
return true; return true;
} }
if (deferred_initialization_pending_) { if (deferred_initialization_pending_ || defer_surface_creation_) {
defer_surface_creation_ = false;
ConfigureMediaCodecAsynchronously(); ConfigureMediaCodecAsynchronously();
return true; return true;
} }
...@@ -790,7 +818,7 @@ bool AndroidVideoDecodeAccelerator::DequeueOutput() { ...@@ -790,7 +818,7 @@ bool AndroidVideoDecodeAccelerator::DequeueOutput() {
// decoded images. Breaking their connection to the decoded image will // decoded images. Breaking their connection to the decoded image will
// cause rendering of black frames. Instead, we let the existing // cause rendering of black frames. Instead, we let the existing
// PictureBuffers live on and we simply update their size the next time // PictureBuffers live on and we simply update their size the next time
// they're attachted to an image of the new resolution. See the // they're attached to an image of the new resolution. See the
// size update in |SendDecodedFrameToClient| and https://crbug/587994. // size update in |SendDecodedFrameToClient| and https://crbug/587994.
if (output_picture_buffers_.empty() && !picturebuffers_requested_) { if (output_picture_buffers_.empty() && !picturebuffers_requested_) {
picturebuffers_requested_ = true; picturebuffers_requested_ = true;
...@@ -928,6 +956,12 @@ void AndroidVideoDecodeAccelerator::Decode( ...@@ -928,6 +956,12 @@ void AndroidVideoDecodeAccelerator::Decode(
const BitstreamBuffer& bitstream_buffer) { const BitstreamBuffer& bitstream_buffer) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (defer_surface_creation_ && !InitializePictureBufferManager()) {
POST_ERROR(PLATFORM_FAILURE,
"Failed deferred surface and MediaCodec initialization.");
return;
}
// If we previously deferred a codec restart, take care of it now. This can // If we previously deferred a codec restart, take care of it now. This can
// happen on older devices where configuration changes require a codec reset. // happen on older devices where configuration changes require a codec reset.
if (codec_needs_reset_) { if (codec_needs_reset_) {
...@@ -1027,7 +1061,7 @@ void AndroidVideoDecodeAccelerator::Flush() { ...@@ -1027,7 +1061,7 @@ void AndroidVideoDecodeAccelerator::Flush() {
DVLOG(1) << __FUNCTION__; DVLOG(1) << __FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == SURFACE_DESTROYED) if (state_ == SURFACE_DESTROYED || defer_surface_creation_)
NotifyFlushDone(); NotifyFlushDone();
else else
StartCodecDrain(DRAIN_FOR_FLUSH); StartCodecDrain(DRAIN_FOR_FLUSH);
...@@ -1258,7 +1292,7 @@ void AndroidVideoDecodeAccelerator::ResetCodecState() { ...@@ -1258,7 +1292,7 @@ void AndroidVideoDecodeAccelerator::ResetCodecState() {
// Flush the codec if possible, or create a new one if not. // Flush the codec if possible, or create a new one if not.
if (!did_codec_error_happen && if (!did_codec_error_happen &&
!media::MediaCodecUtil::CodecNeedsFlushWorkaround(media_codec_.get())) { !MediaCodecUtil::CodecNeedsFlushWorkaround(media_codec_.get())) {
DVLOG(3) << __FUNCTION__ << " Flushing MediaCodec."; DVLOG(3) << __FUNCTION__ << " Flushing MediaCodec.";
media_codec_->Flush(); media_codec_->Flush();
// Since we just flushed all the output buffers, make sure that nothing is // Since we just flushed all the output buffers, make sure that nothing is
...@@ -1277,6 +1311,16 @@ void AndroidVideoDecodeAccelerator::Reset() { ...@@ -1277,6 +1311,16 @@ void AndroidVideoDecodeAccelerator::Reset() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
TRACE_EVENT0("media", "AVDA::Reset"); TRACE_EVENT0("media", "AVDA::Reset");
if (defer_surface_creation_) {
DCHECK(!media_codec_);
DCHECK(pending_bitstream_records_.empty());
DCHECK_EQ(state_, NO_ERROR);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&AndroidVideoDecodeAccelerator::NotifyResetDone,
weak_this_factory_.GetWeakPtr()));
return;
}
while (!pending_bitstream_records_.empty()) { while (!pending_bitstream_records_.empty()) {
int32_t bitstream_buffer_id = int32_t bitstream_buffer_id =
pending_bitstream_records_.front().buffer.id(); pending_bitstream_records_.front().buffer.id();
...@@ -1635,10 +1679,10 @@ AndroidVideoDecodeAccelerator::GetCapabilities( ...@@ -1635,10 +1679,10 @@ AndroidVideoDecodeAccelerator::GetCapabilities(
// is disabled (http://crbug.com/582170). // is disabled (http://crbug.com/582170).
if (gpu_preferences.enable_threaded_texture_mailboxes) { if (gpu_preferences.enable_threaded_texture_mailboxes) {
capabilities.flags |= capabilities.flags |=
media::VideoDecodeAccelerator::Capabilities::REQUIRES_TEXTURE_COPY; VideoDecodeAccelerator::Capabilities::REQUIRES_TEXTURE_COPY;
} else if (media::MediaCodecUtil::IsSurfaceViewOutputSupported()) { } else if (MediaCodecUtil::IsSurfaceViewOutputSupported()) {
capabilities.flags |= media::VideoDecodeAccelerator::Capabilities:: capabilities.flags |=
SUPPORTS_EXTERNAL_OUTPUT_SURFACE; VideoDecodeAccelerator::Capabilities::SUPPORTS_EXTERNAL_OUTPUT_SURFACE;
} }
#if BUILDFLAG(ENABLE_HEVC_DEMUXING) #if BUILDFLAG(ENABLE_HEVC_DEMUXING)
......
...@@ -374,6 +374,10 @@ class MEDIA_GPU_EXPORT AndroidVideoDecodeAccelerator ...@@ -374,6 +374,10 @@ class MEDIA_GPU_EXPORT AndroidVideoDecodeAccelerator
// when the EOS buffer is received. // when the EOS buffer is received.
bool codec_needs_reset_; bool codec_needs_reset_;
// True if surface creation and |picture_buffer_manager_| initialization has
// been defered until the first Decode() call.
bool defer_surface_creation_;
// Copy of the VDA::Config we were given. // Copy of the VDA::Config we were given.
Config config_; Config config_;
......
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