diff options
author | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-20 23:42:21 +0000 |
---|---|---|
committer | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-20 23:42:21 +0000 |
commit | 929ee6479d6016026bc35bc3ecee26d08bbba19b (patch) | |
tree | 2eb78d955de7280799343419548805419f87a112 /media | |
parent | 0cfea58dc67d34b7c7781613e13ddbea413c23a1 (diff) | |
download | chromium_src-929ee6479d6016026bc35bc3ecee26d08bbba19b.zip chromium_src-929ee6479d6016026bc35bc3ecee26d08bbba19b.tar.gz chromium_src-929ee6479d6016026bc35bc3ecee26d08bbba19b.tar.bz2 |
Fire canplaythrough as soon as download defers to fix autoplay
Reenables delayed firing of canplaythrough for media elements, and fixes
the bug that had been introduced where a video with autoplay=true sometimes
never starts. With this change, a video with autoplay=true should always
(though not necessarily immediately) start playback on its own.
BUG=106480,73609
TEST=media_unitests, manually checking video files in various conditions
Review URL: https://chromiumcodereview.appspot.com/9113023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@118546 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/base/download_rate_monitor.cc | 31 | ||||
-rw-r--r-- | media/base/download_rate_monitor.h | 8 | ||||
-rw-r--r-- | media/base/download_rate_monitor_unittest.cc | 231 | ||||
-rw-r--r-- | media/base/pipeline.cc | 8 |
4 files changed, 169 insertions, 109 deletions
diff --git a/media/base/download_rate_monitor.cc b/media/base/download_rate_monitor.cc index f27b2e8..dd3c7ef 100644 --- a/media/base/download_rate_monitor.cc +++ b/media/base/download_rate_monitor.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -91,8 +91,6 @@ void DownloadRateMonitor::SetBufferedBytes( if (stopped_) return; - is_downloading_data_ = true; - // Check monotonically nondecreasing constraint. base::Time previous_time; if (!current_sample_.is_null()) @@ -120,12 +118,11 @@ void DownloadRateMonitor::SetBufferedBytes( } void DownloadRateMonitor::SetNetworkActivity(bool is_downloading_data) { - if (is_downloading_data == is_downloading_data_) - return; - // Invalidate the current sample if downloading is going from start to stopped - // or vice versa. - current_sample_.Reset(); - is_downloading_data_ = is_downloading_data; + // Record when download defers for the first time. + if (!is_downloading_data && !has_deferred_) { + has_deferred_ = true; + NotifyCanPlayThroughIfNeeded(); + } } void DownloadRateMonitor::Stop() { @@ -139,13 +136,13 @@ void DownloadRateMonitor::Reset() { has_notified_can_play_through_ = false; current_sample_.Reset(); sample_window_.clear(); - is_downloading_data_ = false; total_bytes_ = -1; buffered_bytes_ = 0; local_source_ = false; bitrate_ = 0; stopped_ = true; streaming_ = false; + has_deferred_ = false; } DownloadRateMonitor::~DownloadRateMonitor() { } @@ -210,8 +207,9 @@ bool DownloadRateMonitor::ShouldNotifyCanPlayThrough() { if (local_source_ || streaming_) return true; - // If all bytes are buffered, fire CanPlayThrough. - if (buffered_bytes_ == total_bytes_) + // If all bytes are buffered or if enough bytes were buffered such that + // downloading has deferred, fire CanPlayThrough. + if (buffered_bytes_ == total_bytes_ || has_deferred_) return true; // If bitrate is unknown, optimistically fire CanPlayThrough immediately. @@ -228,12 +226,9 @@ bool DownloadRateMonitor::ShouldNotifyCanPlayThrough() { if (download_rate > 0) return download_rate >= bytes_needed_per_second; - // If download rate is unknown, it may be because the media is being - // downloaded so fast that it cannot collect an adequate number of samples - // before the download gets deferred. - // - // To catch this case, we also look at how much data is being downloaded - // immediately after the download begins. + // With very fast connections, we may want to fire CanPlayThrough before + // waiting for the sample window size to reach |kNumberOfSamples|. Check for + // this scenario. if (sample_window_.size() < kNumberOfSamples) { int64 bytes_downloaded_since_start = bytes_downloaded_in_window() + current_sample_.bytes_downloaded(); diff --git a/media/base/download_rate_monitor.h b/media/base/download_rate_monitor.h index 4fca328..b26c61b 100644 --- a/media/base/download_rate_monitor.h +++ b/media/base/download_rate_monitor.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -126,9 +126,6 @@ class MEDIA_EXPORT DownloadRateMonitor { Sample current_sample_; std::deque<Sample> sample_window_; - // True if actively downloading bytes, false otherwise. - bool is_downloading_data_; - // Total number of bytes in the media file, 0 if unknown or undefined. int64 total_bytes_; @@ -149,6 +146,9 @@ class MEDIA_EXPORT DownloadRateMonitor { // True if the data source is a streaming source, false otherwise. bool streaming_; + // True if downloading has been deferred at least once, false otherwise. + bool has_deferred_; + DISALLOW_COPY_AND_ASSIGN(DownloadRateMonitor); }; diff --git a/media/base/download_rate_monitor_unittest.cc b/media/base/download_rate_monitor_unittest.cc index 321ad63..9715526 100644 --- a/media/base/download_rate_monitor_unittest.cc +++ b/media/base/download_rate_monitor_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -15,151 +15,216 @@ namespace media { class DownloadRateMonitorTest : public ::testing::Test { public: - DownloadRateMonitorTest() { - monitor_.set_total_bytes(kMediaSizeInBytes); - } + DownloadRateMonitorTest() + : canplaythrough_cb_(base::Bind(&DownloadRateMonitorTest::CanPlayThrough, + base::Unretained(this))) { } virtual ~DownloadRateMonitorTest() { } MOCK_METHOD0(CanPlayThrough, void()); protected: - static const int kMediaSizeInBytes = 20 * 1024 * 1024; - - // Simulates downloading of the media file. Packets are timed evenly in - // |ms_between_packets| intervals, starting at |starting_time|, which is - // number of seconds since unix epoch (Jan 1, 1970). - // Returns the number of bytes buffered in the media file after the - // network activity. - int SimulateNetwork(double starting_time, - int starting_bytes, - int bytes_per_packet, - int ms_between_packets, - int number_of_packets) { - int bytes_buffered = starting_bytes; - base::Time packet_time = base::Time::FromDoubleT(starting_time); - - monitor_.SetNetworkActivity(true); - // Loop executes (number_of_packets + 1) times because a packet needs a - // starting and end point. - for (int i = 0; i < number_of_packets + 1; ++i) { - monitor_.SetBufferedBytes(bytes_buffered, packet_time); - packet_time += base::TimeDelta::FromMilliseconds(ms_between_packets); - bytes_buffered += bytes_per_packet; + static const int kMediaDuration = 120; + static const int kMediaBitrate = 1024 * 1024 * 8; + static const int kMediaByterate = kMediaBitrate / 8; + static const int kMediaSizeInBytes = kMediaDuration * kMediaByterate; + static const int kDeferThreshold = 30 * kMediaByterate; + + // Simulates downloading |bytes_to_download| bytes of the media file, at + // |download_speed_in_bps| bytes per second. + void SimulateNetwork(int bytes_to_download, int download_speed_in_bps) { + int bytes_downloaded = 0; + while (bytes_downloaded < bytes_to_download && + !data_source_.is_deferred()) { + int bytes_left_to_download = bytes_to_download - bytes_downloaded; + int packet_size = std::min(download_speed_in_bps, bytes_left_to_download); + time_elapsed_ += base::TimeDelta::FromMilliseconds( + 1000 * packet_size / download_speed_in_bps); + data_source_.ReceiveData( + packet_size, time_elapsed_ + base::Time::UnixEpoch()); + bytes_downloaded += packet_size; } - monitor_.SetNetworkActivity(false); - return bytes_buffered; } - void StartMonitor(int bitrate) { - StartMonitor(bitrate, false, false); + void Initialize() { + Initialize(false, false); + } + + void Initialize(bool streaming, bool local_source) { + data_source_.Initialize( + kMediaBitrate, streaming, local_source, canplaythrough_cb_); + } + + bool DownloadIsDeferred() { + return data_source_.is_deferred(); } - void StartMonitor(int bitrate, bool streaming, bool local_source) { - monitor_.Start(base::Bind(&DownloadRateMonitorTest::CanPlayThrough, - base::Unretained(this)), bitrate, streaming, local_source); + void SeekTo(int offset) { + data_source_.SeekTo(offset); } - DownloadRateMonitor monitor_; + // Returns the size of |seconds| seconds of media data in bytes. + int SecondsToBytes(int seconds) { + return seconds * kMediaByterate; + } private: + // Helper class to simulate a buffered data source. + class FakeDataSource { + public: + FakeDataSource() + : total_bytes_(kMediaSizeInBytes), + buffered_bytes_(0), + offset_(0), + defer_threshold_(kDeferThreshold), + is_downloading_data_(true) { } + + bool is_deferred() { return !is_downloading_data_; } + + void ReceiveData(int bytes_received, base::Time packet_time) { + CHECK(is_downloading_data_); + buffered_bytes_ += bytes_received; + // This simulates that the download is being deferred. + if (buffered_bytes_ >= defer_threshold_) + is_downloading_data_ = false; + + // Update monitor's state. + monitor_.SetNetworkActivity(is_downloading_data_); + monitor_.set_total_bytes(total_bytes_); + monitor_.SetBufferedBytes(buffered_bytes_ + offset_, packet_time); + } + + void Initialize(int bitrate, bool streaming, bool local_source, + const base::Closure& canplaythrough_cb) { + monitor_.Start(canplaythrough_cb, bitrate, streaming, local_source); + } + + void SeekTo(int byte_position) { + offset_ = byte_position; + // Simulate recreating the buffer after a seek. + buffered_bytes_ = 0; + is_downloading_data_ = true; + } + + private: + DownloadRateMonitor monitor_; + + // Size of the media file being downloaded, in bytes. + int total_bytes_; + + // Number of bytes currently in buffer. + int buffered_bytes_; + + // The byte offset into the file from which the download began. + int offset_; + + // The size of |buffered_bytes_| at which downloading should defer. + int defer_threshold_; + + // True if download is active (not deferred_, false otherwise. + bool is_downloading_data_; + }; + + FakeDataSource data_source_; + base::Closure canplaythrough_cb_; + + // Amount of time elapsed while downloading data. + base::TimeDelta time_elapsed_; + DISALLOW_COPY_AND_ASSIGN(DownloadRateMonitorTest); }; TEST_F(DownloadRateMonitorTest, DownloadRateGreaterThanBitrate) { - static const int media_bitrate = 1024 * 1024 * 8; - // Simulate downloading at double the media's bitrate. - StartMonitor(media_bitrate); + Initialize(); EXPECT_CALL(*this, CanPlayThrough()); - SimulateNetwork(1, 0, 2 * media_bitrate / 8, 1000, 10); + SimulateNetwork(SecondsToBytes(20), 2 * kMediaByterate); } -// If the user pauses and the pipeline stops downloading data, make sure the -// DownloadRateMonitor understands that the download is not stalling. -TEST_F(DownloadRateMonitorTest, DownloadRateGreaterThanBitrate_Pause) { - static const int media_bitrate = 1024 * 1024 * 8; - static const int download_byte_rate = 1.1 * media_bitrate / 8; +TEST_F(DownloadRateMonitorTest, DownloadRateLessThanBitrate_Defer) { + Initialize(); - // Start downloading faster than the media's bitrate. - StartMonitor(media_bitrate); + // Download slower than the media's bitrate, but buffer enough data such that + // the data source defers. EXPECT_CALL(*this, CanPlayThrough()); - int buffered = SimulateNetwork(1, 0, download_byte_rate, 1000, 2); - - // Then "pause" for 3 minutes and continue downloading at same rate. - SimulateNetwork(60 * 3, buffered, download_byte_rate, 1000, 4); + SimulateNetwork(kDeferThreshold, 0.3 * kMediaByterate); + CHECK(DownloadIsDeferred()); } TEST_F(DownloadRateMonitorTest, DownloadRateGreaterThanBitrate_SeekForward) { - static const int media_bitrate = 1024 * 1024 * 8; - static const int download_byte_rate = 1.1 * media_bitrate / 8; - // Start downloading faster than the media's bitrate. - EXPECT_CALL(*this, CanPlayThrough()); - StartMonitor(media_bitrate); - SimulateNetwork(1, 0, download_byte_rate, 1000, 2); + Initialize(); + SimulateNetwork(SecondsToBytes(3), 1.1 * kMediaByterate); // Then seek forward mid-file and continue downloading at same rate. - SimulateNetwork(4, kMediaSizeInBytes / 2, download_byte_rate, 1000, 4); + SeekTo(kMediaSizeInBytes / 2); + EXPECT_CALL(*this, CanPlayThrough()); + SimulateNetwork(7 * kMediaByterate, 1.1 * kMediaByterate); + + // Verify deferring is not what caused CanPlayThrough to fire. + CHECK(!DownloadIsDeferred()); } TEST_F(DownloadRateMonitorTest, DownloadRateGreaterThanBitrate_SeekBackward) { - static const int media_bitrate = 1024 * 1024 * 8; - static const int download_byte_rate = 1.1 * media_bitrate / 8; - // Start downloading faster than the media's bitrate, in middle of file. - StartMonitor(media_bitrate); - SimulateNetwork(1, kMediaSizeInBytes / 2, download_byte_rate, 1000, 2); + Initialize(); + SeekTo(kMediaSizeInBytes / 2); + SimulateNetwork(SecondsToBytes(3), 1.1 * kMediaByterate); // Then seek back to beginning and continue downloading at same rate. + SeekTo(0); EXPECT_CALL(*this, CanPlayThrough()); - SimulateNetwork(4, 0, download_byte_rate, 1000, 4); -} + SimulateNetwork(SecondsToBytes(7), 1.1 * kMediaByterate); -TEST_F(DownloadRateMonitorTest, DownloadRateLessThanBitrate) { - static const int media_bitrate = 1024 * 1024 * 8; + // Verify deferring is not what caused CanPlayThrough to fire. + CHECK(!DownloadIsDeferred()); +} +TEST_F(DownloadRateMonitorTest, DownloadRateLessThanByterate) { // Simulate downloading at half the media's bitrate. EXPECT_CALL(*this, CanPlayThrough()) .Times(0); - StartMonitor(media_bitrate); - SimulateNetwork(1, 0, media_bitrate / 8 / 2, 1000, 10); + Initialize(); + SimulateNetwork(SecondsToBytes(10), kMediaByterate / 2); } TEST_F(DownloadRateMonitorTest, MediaSourceIsLocal) { - static const int media_bitrate = 1024 * 1024 * 8; - // Simulate no data downloaded. EXPECT_CALL(*this, CanPlayThrough()); - StartMonitor(media_bitrate, false, true); + Initialize(false, true); } TEST_F(DownloadRateMonitorTest, MediaSourceIsStreaming) { - static const int media_bitrate = 1024 * 1024 * 8; - // Simulate downloading at the media's bitrate while streaming. EXPECT_CALL(*this, CanPlayThrough()); - StartMonitor(media_bitrate, true, false); - SimulateNetwork(1, 0, media_bitrate / 8, 1000, 10); + Initialize(true, false); + SimulateNetwork(SecondsToBytes(10), kMediaByterate); } TEST_F(DownloadRateMonitorTest, VeryFastDownloadRate) { - static const int media_bitrate = 1024 * 1024 * 8; - // Simulate downloading half the video very quickly in one chunk. - StartMonitor(media_bitrate); + Initialize(); EXPECT_CALL(*this, CanPlayThrough()); - SimulateNetwork(1, 0, kMediaSizeInBytes / 2, 10, 1); + SimulateNetwork(kMediaSizeInBytes / 2, kMediaSizeInBytes * 10); } TEST_F(DownloadRateMonitorTest, DownloadEntireVideo) { - static const int seconds_of_data = 20; - static const int media_bitrate = kMediaSizeInBytes * 8 / seconds_of_data; - // Simulate downloading entire video at half the bitrate of the video. - StartMonitor(media_bitrate); + Initialize(); EXPECT_CALL(*this, CanPlayThrough()); - SimulateNetwork(1, 0, media_bitrate / 8 / 2, 1000, seconds_of_data * 2); + SimulateNetwork(kMediaSizeInBytes, kMediaByterate / 2); +} + +TEST_F(DownloadRateMonitorTest, DownloadEndOfVideo) { + Initialize(); + // Seek to 10s before the end of the file, then download the remainder of the + // file at half the bitrate. + SeekTo((kMediaDuration - 10) * kMediaByterate); + EXPECT_CALL(*this, CanPlayThrough()); + SimulateNetwork(SecondsToBytes(10), kMediaByterate/ 2); + + // Verify deferring is not what caused CanPlayThrough to fire. + CHECK(!DownloadIsDeferred()); } } // namespace media diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc index 7995796..d1b68fb 100644 --- a/media/base/pipeline.cc +++ b/media/base/pipeline.cc @@ -1020,11 +1020,8 @@ void Pipeline::FilterStateTransitionTask() { // Start monitoring rate of downloading. int bitrate = 0; - if (demuxer_.get()) { + if (demuxer_.get()) bitrate = demuxer_->GetBitrate(); - local_source_ = demuxer_->IsLocalSource(); - streaming_ = !demuxer_->IsSeekable(); - } // Needs to be locked because most other calls to |download_rate_monitor_| // occur on the renderer thread. download_rate_monitor_.Start( @@ -1139,6 +1136,9 @@ void Pipeline::OnDemuxerBuilt(PipelineStatus status, Demuxer* demuxer) { } demuxer_ = demuxer; + // Set fields obtained from demuxer. + local_source_ = demuxer_->IsLocalSource(); + streaming_ = !demuxer_->IsSeekable(); demuxer_->set_host(this); { |