Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(369)

Side by Side Diff: chrome/renderer/media/buffered_data_source.cc

Issue 88047: Buffered data source that does range request to provide data to media pipelin... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/renderer/media/buffered_data_source.h ('k') | chrome/renderer/renderer.vcproj » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
2 // source code is governed by a BSD-style license that can be found in the
3 // LICENSE file.
4
5 #include "base/compiler_specific.h"
6 #include "base/message_loop.h"
7 #include "base/process_util.h"
8 #include "base/string_util.h"
9 #include "chrome/common/extensions/url_pattern.h"
10 #include "chrome/renderer/media/buffered_data_source.h"
11 #include "chrome/renderer/render_view.h"
12 #include "chrome/renderer/webmediaplayer_delegate_impl.h"
13 #include "chrome/renderer/render_thread.h"
14 #include "media/base/filter_host.h"
15 #include "net/base/load_flags.h"
16 #include "net/base/net_errors.h"
17 #include "net/http/http_response_headers.h"
18 #include "webkit/glue/webappcachecontext.h"
19
20 namespace {
21
22 const char kHttpScheme[] = "http";
23 const char kHttpsScheme[] = "https";
24 const int64 kPositionNotSpecified = -1;
25 const int kHttpOK = 200;
26 const int kHttpPartialContent = 206;
27
28 // A helper method that accepts only HTTP, HTTPS and FILE protocol.
29 bool IsSchemeSupported(const GURL& url) {
30 return url.SchemeIs(kHttpScheme) ||
31 url.SchemeIs(kHttpsScheme) ||
32 url.SchemeIsFile();
33 }
34
35 } // namespace
36
37 /////////////////////////////////////////////////////////////////////////////
38 // BufferedResourceLoader
39 BufferedResourceLoader::BufferedResourceLoader(int route_id,
40 const GURL& url,
41 int64 first_byte_position,
42 int64 last_byte_position)
43 : start_callback_(NULL),
44 bridge_(NULL),
45 offset_(0),
46 content_length_(kPositionNotSpecified),
47 buffered_bytes_(0),
48 buffer_limit_(10240000), // By default 10MB.
49 buffer_event_(false, false),
50 deferred_(false),
51 stopped_(false),
52 completed_(false),
53 range_requested_(false),
54 async_start_(false),
55 route_id_(route_id),
56 url_(url),
57 first_byte_position_(first_byte_position),
58 last_byte_position_(last_byte_position),
59 render_loop_(RenderThread::current()->message_loop()) {
60 }
61
62 BufferedResourceLoader::~BufferedResourceLoader() {
63 for (size_t i = 0; i < buffers_.size(); ++i)
64 delete buffers_[i];
65 buffers_.clear();
66 }
67
68 int BufferedResourceLoader::Start(net::CompletionCallback* start_callback) {
69 // Make sure we only start no more than once.
70 DCHECK(!bridge_.get());
71 DCHECK(!start_callback_.get());
72 start_callback_.reset(start_callback);
73
74 // Save the information that we are doing an asynchronous start since
75 // start_callback_ may get reset, we can't rely on it.
76 if (start_callback_.get())
77 async_start_ = true;
78
79 std::string header;
80 if (first_byte_position_ != kPositionNotSpecified &&
81 last_byte_position_ != kPositionNotSpecified) {
82 header = StringPrintf("Range: bytes=%lld-%lld",
83 first_byte_position_,
84 last_byte_position_);
85 range_requested_ = true;
86 offset_ = first_byte_position_;
87 } else if (first_byte_position_ != kPositionNotSpecified) {
88 header = StringPrintf("Range: bytes=%lld-", first_byte_position_);
89 range_requested_ = true;
90 offset_ = first_byte_position_;
91 } else if (last_byte_position_ != kPositionNotSpecified) {
92 NOTIMPLEMENTED() << "Suffix length range request not implemented.";
93 }
94
95 bridge_.reset(RenderThread::current()->resource_dispatcher()->CreateBridge(
96 "GET",
97 GURL(url_),
98 GURL(url_),
99 GURL(), // TODO(hclam): provide referer here.
100 "null", // TODO(abarth): provide frame_origin
101 "null", // TODO(abarth): provide main_frame_origin
102 header,
103 net::LOAD_BYPASS_CACHE,
104 base::GetCurrentProcId(),
105 ResourceType::MEDIA,
106 0,
107 // TODO(michaeln): delegate->mediaplayer->frame->
108 // app_cache_context()->context_id()
109 // For now don't service media resource requests from the appcache.
110 WebAppCacheContext::kNoAppCacheContextId,
111 route_id_));
112
113 // We may receive stop signal while we are inside this method, it's because
114 // Start() may get called on demuxer thread while Stop() is called on
115 // pipeline thread, so we want to protect the posting of OnStart() task
116 // with a lock.
117 bool task_posted = false;
118 {
119 AutoLock auto_lock(lock_);
120 if (!stopped_) {
121 task_posted = true;
122 render_loop_->PostTask(FROM_HERE,
123 NewRunnableMethod(this, &BufferedResourceLoader::OnStart));
124 }
125 }
126
127 // Wait for response to arrive we don't have an async start.
128 if (task_posted && !async_start_)
129 buffer_event_.Wait();
130
131 {
132 AutoLock auto_lock(lock_);
133 // We may have stopped because of a bad response from the server.
134 if (stopped_)
135 return net::ERR_ABORTED;
136 else if (completed_)
137 return net::ERR_FAILED;
138 else if (async_start_)
139 return net::ERR_IO_PENDING;
140 return net::OK;
141 }
142 }
143
144 void BufferedResourceLoader::Stop() {
145 {
146 AutoLock auto_lock(lock_);
147 stopped_ = true;
148 }
149
150 // Wakes up the waiting thread so they can catch the stop signal.
151 render_loop_->PostTask(FROM_HERE,
152 NewRunnableMethod(this, &BufferedResourceLoader::OnDestroy));
153 buffer_event_.Signal();
154 }
155
156 size_t BufferedResourceLoader::Read(uint8* data, size_t size) {
157 size_t taken = 0;
158 while (taken < size) {
159 Buffer* buffer = NULL;
160 {
161 AutoLock auto_lock(lock_);
162 if (stopped_)
163 break;
164 if (!buffers_.empty())
165 buffer = buffers_.front();
166 else if (completed_)
167 break;
168 }
169 if (buffer) {
170 size_t copy = std::min(size - taken, buffer->size - buffer->taken);
171 memcpy(data + taken, buffer->data.get() + buffer->taken, copy);
172 taken += copy;
173 buffer->taken += copy;
174 if (buffer->taken == buffer->size) {
175 // The buffer has been consumed, remove it.
176 {
177 AutoLock auto_lock(lock_);
178 buffers_.pop_front();
179 }
180 delete buffer;
181 }
182 } else {
183 buffer_event_.Wait();
184 }
185 }
186 if (taken > 0) {
187 offset_ += taken;
188 {
189 AutoLock auto_lock(lock_);
190 buffered_bytes_ -= taken;
191 DCHECK(buffered_bytes_ >= 0);
192 }
193 if (ShouldDisableDefer()) {
194 AutoLock auto_lock(lock_);
195 if (!stopped_) {
196 render_loop_->PostTask(FROM_HERE,
197 NewRunnableMethod(this,
198 &BufferedResourceLoader::OnDisableDeferLoading));
199 }
200 }
201 }
202 return taken;
203 }
204
205 bool BufferedResourceLoader::SeekForward(int64 position) {
206 // Use of |offset_| is safe without a lock, because it's modified only in
207 // Read() and this method after Start(). Read() and SeekForward() happens
208 // on the same thread.
209 // Seeking backward.
210 if (position < offset_)
211 return false;
212 // Done seeking forward.
213 else if (position == offset_)
214 return true;
215
216 while(true) {
217 {
218 AutoLock auto_lock(lock_);
219 // Loader has stopped.
220 if (stopped_)
221 return false;
222 // Seek position exceeds bufferable range, buffer_limit_ can be changed.
223 if (position >= offset_ + buffer_limit_)
224 return false;
225 // Response completed and seek position exceeds buffered range.
226 if (completed_ && position >= offset_ + buffered_bytes_)
227 return false;
228
229 if (!buffers_.empty()) {
230 Buffer* buffer = buffers_.front();
231 int64 bytes_to_take = position - offset_;
232 if (!buffers_.empty()) {
233 size_t available_bytes_in_buffer = buffer->size - buffer->taken;
234 size_t taken = 0;
235 if (available_bytes_in_buffer <= bytes_to_take) {
236 taken = available_bytes_in_buffer;
237 buffers_.pop_front();
238 delete buffer;
239 } else {
240 taken = static_cast<size_t>(bytes_to_take);
241 buffer->taken += taken;
242 }
243 offset_ += taken;
244 if (bytes_to_take == taken)
245 return true;
246 }
247 continue;
248 }
249 }
250 buffer_event_.Wait();
251 }
252 }
253
254 int64 BufferedResourceLoader::GetOffset() {
255 AutoLock auto_lock(lock_);
256 return offset_;
257 }
258
259 int64 BufferedResourceLoader::GetBufferLimit() {
260 AutoLock auto_lock(lock_);
261 return buffer_limit_;
262 }
263
264 void BufferedResourceLoader::SetBufferLimit(size_t buffer_limit) {
265 {
266 AutoLock auto_lock(lock_);
267 buffer_limit_ = buffer_limit;
268 }
269
270 if (ShouldDisableDefer()) {
271 AutoLock auto_lock(lock_);
272 if (!stopped_) {
273 render_loop_->PostTask(FROM_HERE,
274 NewRunnableMethod(this,
275 &BufferedResourceLoader::OnDisableDeferLoading));
276 }
277 }
278 if (ShouldEnableDefer()) {
279 AutoLock auto_lock(lock_);
280 if (!stopped_) {
281 render_loop_->PostTask(FROM_HERE,
282 NewRunnableMethod(this,
283 &BufferedResourceLoader::OnEnableDeferLoading));
284 }
285 }
286 }
287
288 size_t BufferedResourceLoader::GetTimeout() {
289 // TODO(hclam): implement.
290 return 0;
291 }
292
293 void BufferedResourceLoader::SetTimeout(size_t milliseconds) {
294 // TODO(hclam): implement.
295 }
296
297 /////////////////////////////////////////////////////////////////////////////
298 // BufferedResourceLoader,
299 // webkit_glue::ResourceLoaderBridge::Peer implementations
300 void BufferedResourceLoader::OnReceivedRedirect(const GURL& new_url) {
301 url_ = new_url;
302
303 // If we got redirected to an unsupported protocol then stop.
304 if (!IsSchemeSupported(new_url))
305 Stop();
306 }
307
308 void BufferedResourceLoader::OnReceivedResponse(
309 const webkit_glue::ResourceLoaderBridge::ResponseInfo& info,
310 bool content_filtered) {
311 int64 first_byte_position = -1;
312 int64 last_byte_position = -1;
313 int64 instance_size = -1;
314
315 // The file:// protocol should be able to serve any request we want, so we
316 // take an exception for file protocol.
317 if (!url_.SchemeIsFile()) {
318 if (!info.headers) {
319 // We expect to receive headers because this is a HTTP or HTTPS protocol,
320 // if not report failure.
321 InvokeAndResetStartCallback(net::ERR_INVALID_RESPONSE);
322 Stop();
323 return;
324 } else if (range_requested_) {
325 if (info.headers->response_code() != kHttpPartialContent ||
326 !info.headers->GetContentRange(&first_byte_position,
327 &last_byte_position,
328 &instance_size)) {
329 // We requested a range, but server didn't reply with partial content or
330 // the "Content-Range" header is corrupted.
331 InvokeAndResetStartCallback(net::ERR_INVALID_RESPONSE);
332 Stop();
333 return;
334 }
335 } else if (info.headers->response_code() != kHttpOK) {
336 // We didn't request a range but server didn't reply with "200 OK".
337 InvokeAndResetStartCallback(net::ERR_FAILED);
338 Stop();
339 return;
340 }
341 }
342
343 {
344 AutoLock auto_lock(lock_);
345 // |info.content_length| can be -1, in that case |content_length_| is
346 // not specified and this is a streaming response.
347 content_length_ = info.content_length;
348 // We only care about the first byte position if it's given by the server.
349 if (first_byte_position != kPositionNotSpecified)
350 offset_ = first_byte_position;
351 }
352
353 // If we have started asynchronously we just need to invoke the callback or
354 // we need to signal the Start() method to wake up.
355 if (async_start_)
356 InvokeAndResetStartCallback(net::OK);
357 else
358 buffer_event_.Signal();
359 }
360
361 void BufferedResourceLoader::OnReceivedData(const char* data, int len) {
362 DCHECK(bridge_.get());
363
364 AppendToBuffer(reinterpret_cast<const uint8*>(data), len);
365 if (ShouldEnableDefer())
366 bridge_->SetDefersLoading(true);
367 }
368
369 void BufferedResourceLoader::OnCompletedRequest(
370 const URLRequestStatus& status, const std::string& security_info) {
371 SignalComplete();
372 // After the response has completed, we don't need the bridge any more.
373 bridge_.reset();
374
375 if (async_start_)
376 InvokeAndResetStartCallback(status.os_error());
377 }
378
379 /////////////////////////////////////////////////////////////////////////////
380 // BufferedResourceLoader, private
381 void BufferedResourceLoader::AppendToBuffer(const uint8* data, size_t size) {
382 Buffer* buffer = new Buffer(size);
383 memcpy(buffer->data.get(), data, size);
384 {
385 AutoLock auto_lock(lock_);
386 buffers_.push_back(buffer);
387 buffered_bytes_ += size;
388 }
389 buffer_event_.Signal();
390 }
391
392 void BufferedResourceLoader::SignalComplete() {
393 {
394 AutoLock auto_lock(lock_);
395 completed_ = true;
396 }
397 buffer_event_.Signal();
398 }
399
400 bool BufferedResourceLoader::ShouldEnableDefer() {
401 AutoLock auto_lock(lock_);
402 if (deferred_) {
403 return false;
404 } else if (buffered_bytes_ >= buffer_limit_) {
405 deferred_ = true;
406 return true;
407 }
408 return false;
409 }
410
411 bool BufferedResourceLoader::ShouldDisableDefer() {
412 AutoLock auto_lock(lock_);
413 if (deferred_ && buffered_bytes_ < buffer_limit_ / 2)
414 return true;
415 else
416 return false;
417 }
418
419 void BufferedResourceLoader::OnStart() {
420 DCHECK(MessageLoop::current() == render_loop_);
421 DCHECK(bridge_.get());
422 bridge_->Start(this);
423 }
424
425 void BufferedResourceLoader::OnDestroy() {
426 DCHECK(MessageLoop::current() == render_loop_);
427 bridge_.reset();
428 }
429
430 void BufferedResourceLoader::OnEnableDeferLoading() {
431 DCHECK(MessageLoop::current() == render_loop_);
432 // This message may arrive after the bridge is destroyed.
433 if (bridge_.get())
434 bridge_->SetDefersLoading(true);
435 }
436
437 void BufferedResourceLoader::OnDisableDeferLoading() {
438 DCHECK(MessageLoop::current() == render_loop_);
439 // This message may arrive after the bridge is destroyed.
440 if (bridge_.get())
441 bridge_->SetDefersLoading(false);
442 }
443
444 void BufferedResourceLoader::InvokeAndResetStartCallback(int error) {
445 AutoLock auto_lock(lock_);
446 if (start_callback_.get()) {
447 start_callback_->Run(error);
448 start_callback_.reset();
449 }
450 }
451
452 //////////////////////////////////////////////////////////////////////////////
453 // BufferedDataSource
454 BufferedDataSource::BufferedDataSource(WebMediaPlayerDelegateImpl* delegate)
455 : delegate_(delegate),
456 stopped_(false),
457 position_(0),
458 total_bytes_(kPositionNotSpecified),
459 buffered_resource_loader_(NULL),
460 pipeline_loop_(MessageLoop::current()) {
461 }
462
463 BufferedDataSource::~BufferedDataSource() {
464 }
465
466 void BufferedDataSource::Stop() {
467 scoped_refptr<BufferedResourceLoader> resource_loader = NULL;
468 // Set the stop signal first.
469 {
470 AutoLock auto_lock(lock_);
471 stopped_ = true;
472 resource_loader = buffered_resource_loader_;
473 // Release the reference to the resource loader.
474 buffered_resource_loader_ = NULL;
475 }
476 // Tell the loader to stop.
477 if (resource_loader)
478 resource_loader->Stop();
479 }
480
481 bool BufferedDataSource::Initialize(const std::string& url) {
482 // Save the url.
483 url_ = GURL(url);
484
485 // Make sure we support the scheme of the URL.
486 if (!IsSchemeSupported(url_)) {
487 host_->Error(media::PIPELINE_ERROR_NETWORK);
488 return false;
489 }
490
491 media_format_.SetAsString(media::MediaFormat::kMimeType,
492 media::mime_type::kApplicationOctetStream);
493 media_format_.SetAsString(media::MediaFormat::kURL, url);
494
495 // Setup the BufferedResourceLoader here.
496 scoped_refptr<BufferedResourceLoader> resource_loader = NULL;
497 {
498 AutoLock auto_lock(lock_);
499 if (!stopped_) {
500 buffered_resource_loader_ = new BufferedResourceLoader(
501 delegate_->view()->routing_id(),
502 url_, kPositionNotSpecified, kPositionNotSpecified);
503 resource_loader = buffered_resource_loader_;
504 }
505 }
506
507 // Use the local reference to start the request.
508 if (resource_loader) {
509 if (net::ERR_IO_PENDING != resource_loader->Start(
510 NewCallback(this, &BufferedDataSource::InitialRequestStarted))) {
511 host_->Error(media::PIPELINE_ERROR_NETWORK);
512 return false;
513 }
514 return true;
515 }
516 host_->Error(media::PIPELINE_ERROR_NETWORK);
517 return false;
518 }
519
520 size_t BufferedDataSource::Read(uint8* data, size_t size) {
521 // We try two times here:
522 // 1. Use the existing resource loader to seek forward and read from it.
523 // 2. If any of the above operations failed, we create a new resource loader
524 // starting with a new range. Goto 1.
525 // TODO(hclam): change the logic here to do connection recovery and allow a
526 // maximum of trials.
527 for (int trials = 2; trials > 0; --trials) {
528 scoped_refptr<BufferedResourceLoader> resource_loader = NULL;
529 {
530 AutoLock auto_lock(lock_);
531 resource_loader = buffered_resource_loader_;
532 }
533
534 if (resource_loader && resource_loader->SeekForward(position_)) {
535 size_t read = resource_loader->Read(data, size);
536 if (read >= 0) {
537 position_ += read;
538 return read;
539 } else {
540 return DataSource::kReadError;
541 }
542 } else {
543 // We enter here because the current resource loader cannot serve the
544 // range requested, we will need to create a new request for doing it.
545 scoped_refptr<BufferedResourceLoader> old_resource_loader = NULL;
546 {
547 AutoLock auto_lock(lock_);
548 if (stopped_)
549 return DataSource::kReadError;
550 old_resource_loader = buffered_resource_loader_;
551 buffered_resource_loader_ =
552 new BufferedResourceLoader(delegate_->view()->routing_id(),
553 url_, position_,
554 kPositionNotSpecified);
555 resource_loader = buffered_resource_loader_;
556 }
557 if (old_resource_loader)
558 old_resource_loader->Stop();
559 if (resource_loader) {
560 if (net::OK != resource_loader->Start(NULL)) {
561 // We have started a new request but failed, report that.
562 // TODO(hclam): should allow some retry mechanism here.
563 HandleError(media::PIPELINE_ERROR_NETWORK);
564 return DataSource::kReadError;
565 }
566 }
567 }
568 }
569 return DataSource::kReadError;
570 }
571
572 bool BufferedDataSource::GetPosition(int64* position_out) {
573 *position_out = position_;
574 return true;
575 }
576
577 bool BufferedDataSource::SetPosition(int64 position) {
578 position_ = position;
579 return true;
580 }
581
582 bool BufferedDataSource::GetSize(int64* size_out) {
583 if (total_bytes_ != kPositionNotSpecified) {
584 *size_out = total_bytes_;
585 return true;
586 }
587 *size_out = 0;
588 return false;
589 }
590
591 bool BufferedDataSource::IsSeekable() {
592 return total_bytes_ != kPositionNotSpecified;
593 }
594
595 void BufferedDataSource::HandleError(media::PipelineError error) {
596 AutoLock auto_lock(lock_);
597 if (!stopped_) {
598 host_->Error(error);
599 }
600 }
601
602 void BufferedDataSource::InitialRequestStarted(int error) {
603 // Don't take any lock and call to |host_| here, this method is called from
604 // BufferedResourceLoader after the response has started or failed, it is
605 // very likely we are called within a lock in BufferedResourceLoader.
606 // Acquiring an additional lock here we might have a deadlock situation,
607 // but one thing very sure is that pipeline thread is still alive, so we
608 // just need to post a task on that thread.
609 total_bytes_ = buffered_resource_loader_->content_length();
610 pipeline_loop_->PostTask(FROM_HERE,
611 NewRunnableMethod(this,
612 &BufferedDataSource::OnInitialRequestStarted, error));
613 }
614
615 void BufferedDataSource::OnInitialRequestStarted(int error) {
616 // Acquiring a lock should not be needed because stopped_ is only written
617 // on pipeline thread and we are on pipeline thread but just to be safe.
618 AutoLock auto_lock(lock_);
619 if (!stopped_) {
620 if (error == net::OK) {
621 if (IsSeekable()) {
622 host_->SetTotalBytes(total_bytes_);
623 // TODO(hclam): report the amount of bytes buffered accurately.
624 host_->SetBufferedBytes(total_bytes_);
625 }
626 host_->InitializationComplete();
627 } else {
628 host_->Error(media::PIPELINE_ERROR_NETWORK);
629 }
630 }
631 }
632
633 const media::MediaFormat& BufferedDataSource::media_format() {
634 return media_format_;
635 }
OLDNEW
« no previous file with comments | « chrome/renderer/media/buffered_data_source.h ('k') | chrome/renderer/renderer.vcproj » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698