diff options
-rw-r--r-- | webkit/media/buffered_data_source.cc | 18 | ||||
-rw-r--r-- | webkit/media/buffered_data_source.h | 12 | ||||
-rw-r--r-- | webkit/media/buffered_data_source_unittest.cc | 216 | ||||
-rw-r--r-- | webkit/media/test_response_generator.cc | 13 | ||||
-rw-r--r-- | webkit/media/test_response_generator.h | 7 |
5 files changed, 212 insertions, 54 deletions
diff --git a/webkit/media/buffered_data_source.cc b/webkit/media/buffered_data_source.cc index 7375350..24d9a57 100644 --- a/webkit/media/buffered_data_source.cc +++ b/webkit/media/buffered_data_source.cc @@ -40,7 +40,7 @@ BufferedDataSource::BufferedDataSource( const DownloadingCB& downloading_cb) : cors_mode_(BufferedResourceLoader::kUnspecified), total_bytes_(kPositionNotSpecified), - buffered_bytes_(0), + assume_fully_buffered_(false), streaming_(false), frame_(frame), read_size_(0), @@ -288,7 +288,7 @@ void BufferedDataSource::RestartLoadingTask() { frame_); } else { loader_->Start( - base::Bind(&BufferedDataSource::NonHttpInitialStartCallback, this), + base::Bind(&BufferedDataSource::PartialReadStartCallback, this), base::Bind(&NonHttpLoadingStateChangedCallback), base::Bind(&NonHttpProgressCallback), frame_); @@ -452,7 +452,7 @@ void BufferedDataSource::NonHttpInitialStartCallback( if (success) { total_bytes_ = instance_size; - buffered_bytes_ = total_bytes_; + assume_fully_buffered_ = true; } else { loader_->Stop(); } @@ -607,11 +607,13 @@ void BufferedDataSource::UpdateHostState_Locked() { if (!host()) return; - if (total_bytes_ != kPositionNotSpecified) - host()->SetTotalBytes(total_bytes_); - int64 start = loader_->first_byte_position(); - if (buffered_bytes_ > start) - host()->AddBufferedByteRange(start, buffered_bytes_); + if (total_bytes_ == kPositionNotSpecified) + return; + + host()->SetTotalBytes(total_bytes_); + + if (assume_fully_buffered_) + host()->AddBufferedByteRange(0, total_bytes_); } } // namespace webkit_media diff --git a/webkit/media/buffered_data_source.h b/webkit/media/buffered_data_source.h index 61f5e3f..b4526cb 100644 --- a/webkit/media/buffered_data_source.h +++ b/webkit/media/buffered_data_source.h @@ -154,12 +154,14 @@ class BufferedDataSource : public media::DataSource { // crossorigin attribute on the corresponding HTML media element, if any. BufferedResourceLoader::CORSMode cors_mode_; - // Members for total bytes of the requested object. It is written once on - // render thread but may be read from any thread. However reading of this - // member is guaranteed to happen after it is first written, so we don't - // need to protect it. + // The total size of the resource. Set during StartCallback() if the size is + // known, otherwise it will remain kPositionNotSpecified until the size is + // determined by reaching EOF. int64 total_bytes_; - int64 buffered_bytes_; + + // Some resources are assumed to be fully buffered (i.e., file://) so we don't + // need to report what |loader_| has buffered. + bool assume_fully_buffered_; // This value will be true if this data source can only support streaming. // i.e. range request is not supported. diff --git a/webkit/media/buffered_data_source_unittest.cc b/webkit/media/buffered_data_source_unittest.cc index af2a6f7..66059be 100644 --- a/webkit/media/buffered_data_source_unittest.cc +++ b/webkit/media/buffered_data_source_unittest.cc @@ -86,11 +86,13 @@ static const int64 kFileSize = 5000000; static const int64 kFarReadPosition = 4000000; static const int kDataSize = 1024; +static const char kHttpUrl[] = "http://localhost/foo.webm"; +static const char kFileUrl[] = "file:///tmp/bar.webm"; + class BufferedDataSourceTest : public testing::Test { public: BufferedDataSourceTest() - : response_generator_(GURL("http://localhost/foo.webm"), kFileSize), - view_(WebView::create(NULL)) { + : view_(WebView::create(NULL)) { view_->initializeMainFrame(&client_); data_source_ = new MockBufferedDataSource(&message_loop_, @@ -102,23 +104,35 @@ class BufferedDataSourceTest : public testing::Test { view_->close(); } - void Initialize(media::PipelineStatus expected) { - ExpectCreateResourceLoader(); + void Initialize(const char* url, media::PipelineStatus expected) { + GURL gurl(url); + response_generator_.reset(new TestResponseGenerator(gurl, kFileSize)); - EXPECT_FALSE(data_source_->downloading()); - data_source_->Initialize(response_generator_.gurl(), + ExpectCreateResourceLoader(); + data_source_->Initialize(gurl, BufferedResourceLoader::kUnspecified, media::NewExpectedStatusCB(expected)); message_loop_.RunAllPending(); - EXPECT_TRUE(data_source_->downloading()); + + bool is_http = gurl.SchemeIs(kHttpScheme) || gurl.SchemeIs(kHttpsScheme); + EXPECT_EQ(data_source_->downloading(), is_http); } // Helper to initialize tests with a valid 206 response. void InitializeWith206Response() { - Initialize(media::PIPELINE_OK); + Initialize(kHttpUrl, media::PIPELINE_OK); + + EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length())); + Respond(response_generator_->Generate206(0)); + } - EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); - Respond(response_generator_.Generate206(0)); + // Helper to initialize tests with a valid file:// response. + void InitializeWithFileResponse() { + Initialize(kFileUrl, media::PIPELINE_OK); + + EXPECT_CALL(host_, SetTotalBytes(kFileSize)); + EXPECT_CALL(host_, AddBufferedByteRange(0, kFileSize)); + Respond(response_generator_->GenerateFileResponse(0)); } // Stops any active loaders and shuts down the data source. @@ -127,7 +141,7 @@ class BufferedDataSourceTest : public testing::Test { // appropriate to do when tearing down a test. void Stop() { if (data_source_->loading()) { - loader()->didFail(url_loader(), response_generator_.GenerateError()); + loader()->didFail(url_loader(), response_generator_->GenerateError()); message_loop_.RunAllPending(); } @@ -189,7 +203,7 @@ class BufferedDataSourceTest : public testing::Test { scoped_refptr<MockBufferedDataSource> data_source_; - TestResponseGenerator response_generator_; + scoped_ptr<TestResponseGenerator> response_generator_; MockWebFrameClient client_; WebView* view_; @@ -204,10 +218,10 @@ class BufferedDataSourceTest : public testing::Test { }; TEST_F(BufferedDataSourceTest, Range_Supported) { - Initialize(media::PIPELINE_OK); + Initialize(kHttpUrl, media::PIPELINE_OK); - EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); - Respond(response_generator_.Generate206(0)); + EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length())); + Respond(response_generator_->Generate206(0)); EXPECT_TRUE(data_source_->loading()); EXPECT_FALSE(data_source_->IsStreaming()); @@ -215,9 +229,9 @@ TEST_F(BufferedDataSourceTest, Range_Supported) { } TEST_F(BufferedDataSourceTest, Range_InstanceSizeUnknown) { - Initialize(media::PIPELINE_OK); + Initialize(kHttpUrl, media::PIPELINE_OK); - Respond(response_generator_.Generate206( + Respond(response_generator_->Generate206( 0, TestResponseGenerator::kNoContentRangeInstanceSize)); EXPECT_TRUE(data_source_->loading()); @@ -226,17 +240,17 @@ TEST_F(BufferedDataSourceTest, Range_InstanceSizeUnknown) { } TEST_F(BufferedDataSourceTest, Range_NotFound) { - Initialize(media::PIPELINE_ERROR_NETWORK); - Respond(response_generator_.Generate404()); + Initialize(kHttpUrl, media::PIPELINE_ERROR_NETWORK); + Respond(response_generator_->Generate404()); EXPECT_FALSE(data_source_->loading()); Stop(); } TEST_F(BufferedDataSourceTest, Range_NotSupported) { - Initialize(media::PIPELINE_OK); - EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); - Respond(response_generator_.Generate200()); + Initialize(kHttpUrl, media::PIPELINE_OK); + EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length())); + Respond(response_generator_->Generate200()); EXPECT_TRUE(data_source_->loading()); EXPECT_TRUE(data_source_->IsStreaming()); @@ -246,9 +260,9 @@ TEST_F(BufferedDataSourceTest, Range_NotSupported) { // Special carve-out for Apache versions that choose to return a 200 for // Range:0- ("because it's more efficient" than a 206) TEST_F(BufferedDataSourceTest, Range_SupportedButReturned200) { - Initialize(media::PIPELINE_OK); - EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); - WebURLResponse response = response_generator_.Generate200(); + Initialize(kHttpUrl, media::PIPELINE_OK); + EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length())); + WebURLResponse response = response_generator_->Generate200(); response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"), WebString::fromUTF8("bytes")); Respond(response); @@ -259,8 +273,8 @@ TEST_F(BufferedDataSourceTest, Range_SupportedButReturned200) { } TEST_F(BufferedDataSourceTest, Range_MissingContentRange) { - Initialize(media::PIPELINE_ERROR_NETWORK); - Respond(response_generator_.Generate206( + Initialize(kHttpUrl, media::PIPELINE_ERROR_NETWORK); + Respond(response_generator_->Generate206( 0, TestResponseGenerator::kNoContentRange)); EXPECT_FALSE(data_source_->loading()); @@ -268,11 +282,11 @@ TEST_F(BufferedDataSourceTest, Range_MissingContentRange) { } TEST_F(BufferedDataSourceTest, Range_MissingContentLength) { - Initialize(media::PIPELINE_OK); + Initialize(kHttpUrl, media::PIPELINE_OK); // It'll manage without a Content-Length response. - EXPECT_CALL(host_, SetTotalBytes(response_generator_.content_length())); - Respond(response_generator_.Generate206( + EXPECT_CALL(host_, SetTotalBytes(response_generator_->content_length())); + Respond(response_generator_->Generate206( 0, TestResponseGenerator::kNoContentLength)); EXPECT_TRUE(data_source_->loading()); @@ -281,10 +295,10 @@ TEST_F(BufferedDataSourceTest, Range_MissingContentLength) { } TEST_F(BufferedDataSourceTest, Range_WrongContentRange) { - Initialize(media::PIPELINE_ERROR_NETWORK); + Initialize(kHttpUrl, media::PIPELINE_ERROR_NETWORK); // Now it's done and will fail. - Respond(response_generator_.Generate206(1337)); + Respond(response_generator_->Generate206(1337)); EXPECT_FALSE(data_source_->loading()); Stop(); @@ -301,13 +315,13 @@ TEST_F(BufferedDataSourceTest, Range_ServerLied) { // Return a 200 in response to a range request. EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); - Respond(response_generator_.Generate200()); + Respond(response_generator_->Generate200()); EXPECT_FALSE(data_source_->loading()); Stop(); } -TEST_F(BufferedDataSourceTest, Range_AbortWhileReading) { +TEST_F(BufferedDataSourceTest, Http_AbortWhileReading) { InitializeWith206Response(); // Make sure there's a pending read -- we'll expect it to error. @@ -322,7 +336,68 @@ TEST_F(BufferedDataSourceTest, Range_AbortWhileReading) { Stop(); } -TEST_F(BufferedDataSourceTest, Range_TooManyRetries) { +TEST_F(BufferedDataSourceTest, File_AbortWhileReading) { + InitializeWithFileResponse(); + + // Make sure there's a pending read -- we'll expect it to error. + ReadAt(0); + + // Abort!!! + EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + data_source_->Abort(); + message_loop_.RunAllPending(); + + EXPECT_FALSE(data_source_->loading()); + Stop(); +} + +TEST_F(BufferedDataSourceTest, Http_Retry) { + InitializeWith206Response(); + + // Read to advance our position. + EXPECT_CALL(*this, ReadCallback(kDataSize)); + EXPECT_CALL(host_, AddBufferedByteRange(0, kDataSize - 1)); + ReadAt(0); + ReceiveData(kDataSize); + + // Issue a pending read but terminate the connection to force a retry. + ReadAt(kDataSize); + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_->Generate206(kDataSize)); + + // Complete the read. + EXPECT_CALL(*this, ReadCallback(kDataSize)); + EXPECT_CALL(host_, AddBufferedByteRange(kDataSize, (kDataSize * 2) - 1)); + ReceiveData(kDataSize); + + EXPECT_TRUE(data_source_->loading()); + Stop(); +} + +TEST_F(BufferedDataSourceTest, File_Retry) { + InitializeWithFileResponse(); + + // Read to advance our position. + EXPECT_CALL(*this, ReadCallback(kDataSize)); + ReadAt(0); + ReceiveData(kDataSize); + + // Issue a pending read but terminate the connection to force a retry. + ReadAt(kDataSize); + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_->GenerateFileResponse(kDataSize)); + + // Complete the read. + EXPECT_CALL(*this, ReadCallback(kDataSize)); + ReceiveData(kDataSize); + + EXPECT_TRUE(data_source_->loading()); + Stop(); +} + +TEST_F(BufferedDataSourceTest, Http_TooManyRetries) { InitializeWith206Response(); // Make sure there's a pending read -- we'll expect it to error. @@ -331,15 +406,42 @@ TEST_F(BufferedDataSourceTest, Range_TooManyRetries) { // It'll try three times. ExpectCreateResourceLoader(); FinishLoading(); - Respond(response_generator_.Generate206(0)); + Respond(response_generator_->Generate206(0)); + + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_->Generate206(0)); + + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_->Generate206(0)); + + // It'll error after this. + EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); + FinishLoading(); + + EXPECT_FALSE(data_source_->loading()); + Stop(); +} + +TEST_F(BufferedDataSourceTest, File_TooManyRetries) { + InitializeWithFileResponse(); + + // Make sure there's a pending read -- we'll expect it to error. + ReadAt(0); + + // It'll try three times. + ExpectCreateResourceLoader(); + FinishLoading(); + Respond(response_generator_->GenerateFileResponse(0)); ExpectCreateResourceLoader(); FinishLoading(); - Respond(response_generator_.Generate206(0)); + Respond(response_generator_->GenerateFileResponse(0)); ExpectCreateResourceLoader(); FinishLoading(); - Respond(response_generator_.Generate206(0)); + Respond(response_generator_->GenerateFileResponse(0)); // It'll error after this. EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError)); @@ -349,6 +451,23 @@ TEST_F(BufferedDataSourceTest, Range_TooManyRetries) { Stop(); } +TEST_F(BufferedDataSourceTest, File_InstanceSizeUnknown) { + Initialize(kFileUrl, media::PIPELINE_ERROR_NETWORK); + EXPECT_FALSE(data_source_->downloading()); + + Respond(response_generator_->GenerateFileResponse(-1)); + + EXPECT_FALSE(data_source_->loading()); + Stop(); +} + +TEST_F(BufferedDataSourceTest, File_Successful) { + InitializeWithFileResponse(); + + EXPECT_TRUE(data_source_->loading()); + Stop(); +} + static void SetTrue(bool* value) { *value = true; } @@ -402,7 +521,7 @@ TEST_F(BufferedDataSourceTest, SetBitrate) { BufferedResourceLoader* old_loader = loader(); ExpectCreateResourceLoader(); ReadAt(kFarReadPosition); - Respond(response_generator_.Generate206(kFarReadPosition)); + Respond(response_generator_->Generate206(kFarReadPosition)); // Verify loader changed but still has same bitrate. EXPECT_NE(old_loader, loader()); @@ -425,7 +544,7 @@ TEST_F(BufferedDataSourceTest, SetPlaybackRate) { BufferedResourceLoader* old_loader = loader(); ExpectCreateResourceLoader(); ReadAt(kFarReadPosition); - Respond(response_generator_.Generate206(kFarReadPosition)); + Respond(response_generator_->Generate206(kFarReadPosition)); // Verify loader changed but still has same playback rate. EXPECT_NE(old_loader, loader()); @@ -435,7 +554,7 @@ TEST_F(BufferedDataSourceTest, SetPlaybackRate) { Stop(); } -TEST_F(BufferedDataSourceTest, Read) { +TEST_F(BufferedDataSourceTest, Http_Read) { InitializeWith206Response(); ReadAt(0); @@ -453,4 +572,19 @@ TEST_F(BufferedDataSourceTest, Read) { Stop(); } +TEST_F(BufferedDataSourceTest, File_Read) { + InitializeWithFileResponse(); + + ReadAt(0); + + // Receive first half of the read but no buffering update. + ReceiveData(kDataSize / 2); + + // Receive last half of the read but no buffering update. + EXPECT_CALL(*this, ReadCallback(kDataSize)); + ReceiveData(kDataSize / 2); + + Stop(); +} + } // namespace webkit_media diff --git a/webkit/media/test_response_generator.cc b/webkit/media/test_response_generator.cc index d5485b3..b2d605c 100644 --- a/webkit/media/test_response_generator.cc +++ b/webkit/media/test_response_generator.cc @@ -85,4 +85,17 @@ WebURLResponse TestResponseGenerator::Generate404() { return response; } +WebURLResponse TestResponseGenerator::GenerateFileResponse( + int64 first_byte_offset) { + WebURLResponse response(gurl_); + response.setHTTPStatusCode(0); + + if (first_byte_offset >= 0) { + response.setExpectedContentLength(content_length_ - first_byte_offset); + } else { + response.setExpectedContentLength(-1); + } + return response; +} + } // namespace webkit_media diff --git a/webkit/media/test_response_generator.h b/webkit/media/test_response_generator.h index d912d88..f73e94b 100644 --- a/webkit/media/test_response_generator.h +++ b/webkit/media/test_response_generator.h @@ -45,6 +45,13 @@ class TestResponseGenerator { // Generates a regular HTTP 404 response. WebKit::WebURLResponse Generate404(); + // Generates a file:// response starting from |first_byte_offset| until the + // end of the resource. + // + // If |first_byte_offset| is negative a response containing no content length + // will be returned. + WebKit::WebURLResponse GenerateFileResponse(int64 first_byte_offset); + const GURL& gurl() { return gurl_; } int64 content_length() { return content_length_; } |