1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "webkit/glue/media/buffered_data_source.h"
6
7 #include "media/base/filter_host.h"
8 #include "net/base/net_errors.h"
9 #include "webkit/glue/media/web_data_source_factory.h"
10 #include "webkit/glue/webkit_glue.h"
11
12 using WebKit::WebFrame;
13
14 namespace webkit_glue {
15
16 // Defines how long we should wait for more data before we declare a connection
17 // timeout and start a new request.
18 // TODO(hclam): Set it to 5s, calibrate this value later.
19 static const int kTimeoutMilliseconds = 5000;
20
21 // Defines how many times we should try to read from a buffered resource loader
22 // before we declare a read error. After each failure of read from a buffered
23 // resource loader, a new one is created to be read.
24 static const int kReadTrials = 3;
25
26 // BufferedDataSource has an intermediate buffer, this value governs the initial
27 // size of that buffer. It is set to 32KB because this is a typical read size
28 // of FFmpeg.
29 static const int kInitialReadBufferSize = 32768;
30
NewBufferedDataSource(MessageLoop * render_loop,WebKit::WebFrame * frame)31 static WebDataSource* NewBufferedDataSource(MessageLoop* render_loop,
32 WebKit::WebFrame* frame) {
33 return new BufferedDataSource(render_loop, frame);
34 }
35
36 // static
CreateFactory(MessageLoop * render_loop,WebKit::WebFrame * frame,WebDataSourceBuildObserverHack * build_observer)37 media::DataSourceFactory* BufferedDataSource::CreateFactory(
38 MessageLoop* render_loop,
39 WebKit::WebFrame* frame,
40 WebDataSourceBuildObserverHack* build_observer) {
41 return new WebDataSourceFactory(render_loop, frame, &NewBufferedDataSource,
42 build_observer);
43 }
44
BufferedDataSource(MessageLoop * render_loop,WebFrame * frame)45 BufferedDataSource::BufferedDataSource(
46 MessageLoop* render_loop,
47 WebFrame* frame)
48 : total_bytes_(kPositionNotSpecified),
49 buffered_bytes_(0),
50 loaded_(false),
51 streaming_(false),
52 frame_(frame),
53 loader_(NULL),
54 network_activity_(false),
55 initialize_callback_(NULL),
56 read_callback_(NULL),
57 read_position_(0),
58 read_size_(0),
59 read_buffer_(NULL),
60 read_attempts_(0),
61 intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
62 intermediate_read_buffer_size_(kInitialReadBufferSize),
63 render_loop_(render_loop),
64 stop_signal_received_(false),
65 stopped_on_render_loop_(false),
66 media_is_paused_(true),
67 media_has_played_(false),
68 preload_(media::METADATA),
69 using_range_request_(true) {
70 }
71
~BufferedDataSource()72 BufferedDataSource::~BufferedDataSource() {
73 }
74
75 // A factory method to create BufferedResourceLoader using the read parameters.
76 // This method can be overrided to inject mock BufferedResourceLoader object
77 // for testing purpose.
CreateResourceLoader(int64 first_byte_position,int64 last_byte_position)78 BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
79 int64 first_byte_position, int64 last_byte_position) {
80 DCHECK(MessageLoop::current() == render_loop_);
81
82 return new BufferedResourceLoader(url_,
83 first_byte_position,
84 last_byte_position);
85 }
86
87 // This method simply returns kTimeoutMilliseconds. The purpose of this
88 // method is to be overidded so as to provide a different timeout value
89 // for testing purpose.
GetTimeoutMilliseconds()90 base::TimeDelta BufferedDataSource::GetTimeoutMilliseconds() {
91 return base::TimeDelta::FromMilliseconds(kTimeoutMilliseconds);
92 }
93
set_host(media::FilterHost * host)94 void BufferedDataSource::set_host(media::FilterHost* host) {
95 DataSource::set_host(host);
96
97 if (loader_.get())
98 UpdateHostState();
99 }
100
Initialize(const std::string & url,media::PipelineStatusCallback * callback)101 void BufferedDataSource::Initialize(const std::string& url,
102 media::PipelineStatusCallback* callback) {
103 // Saves the url.
104 url_ = GURL(url);
105
106 // This data source doesn't support data:// protocol so reject it.
107 if (url_.SchemeIs(kDataScheme)) {
108 callback->Run(media::DATASOURCE_ERROR_URL_NOT_SUPPORTED);
109 delete callback;
110 return;
111 } else if (!IsProtocolSupportedForMedia(url_)) {
112 callback->Run(media::PIPELINE_ERROR_NETWORK);
113 delete callback;
114 return;
115 }
116
117 DCHECK(callback);
118 initialize_callback_.reset(callback);
119
120 media_format_.SetAsString(media::MediaFormat::kURL, url);
121
122 // Post a task to complete the initialization task.
123 render_loop_->PostTask(FROM_HERE,
124 NewRunnableMethod(this, &BufferedDataSource::InitializeTask));
125 }
126
CancelInitialize()127 void BufferedDataSource::CancelInitialize() {
128 base::AutoLock auto_lock(lock_);
129 DCHECK(initialize_callback_.get());
130
131 initialize_callback_.reset();
132
133 render_loop_->PostTask(
134 FROM_HERE, NewRunnableMethod(this, &BufferedDataSource::CleanupTask));
135 }
136
137 /////////////////////////////////////////////////////////////////////////////
138 // media::Filter implementation.
Stop(media::FilterCallback * callback)139 void BufferedDataSource::Stop(media::FilterCallback* callback) {
140 {
141 base::AutoLock auto_lock(lock_);
142 stop_signal_received_ = true;
143 }
144 if (callback) {
145 callback->Run();
146 delete callback;
147 }
148
149 render_loop_->PostTask(FROM_HERE,
150 NewRunnableMethod(this, &BufferedDataSource::CleanupTask));
151 }
152
SetPlaybackRate(float playback_rate)153 void BufferedDataSource::SetPlaybackRate(float playback_rate) {
154 render_loop_->PostTask(FROM_HERE,
155 NewRunnableMethod(this, &BufferedDataSource::SetPlaybackRateTask,
156 playback_rate));
157 }
158
SetPreload(media::Preload preload)159 void BufferedDataSource::SetPreload(media::Preload preload) {
160 render_loop_->PostTask(FROM_HERE,
161 NewRunnableMethod(this, &BufferedDataSource::SetPreloadTask,
162 preload));
163 }
164
165 /////////////////////////////////////////////////////////////////////////////
166 // media::DataSource implementation.
Read(int64 position,size_t size,uint8 * data,media::DataSource::ReadCallback * read_callback)167 void BufferedDataSource::Read(int64 position, size_t size, uint8* data,
168 media::DataSource::ReadCallback* read_callback) {
169 DCHECK(read_callback);
170
171 {
172 base::AutoLock auto_lock(lock_);
173 DCHECK(!read_callback_.get());
174
175 if (stop_signal_received_ || stopped_on_render_loop_) {
176 read_callback->RunWithParams(
177 Tuple1<size_t>(static_cast<size_t>(media::DataSource::kReadError)));
178 delete read_callback;
179 return;
180 }
181
182 read_callback_.reset(read_callback);
183 }
184
185 render_loop_->PostTask(FROM_HERE,
186 NewRunnableMethod(this, &BufferedDataSource::ReadTask,
187 position, static_cast<int>(size), data));
188 }
189
GetSize(int64 * size_out)190 bool BufferedDataSource::GetSize(int64* size_out) {
191 if (total_bytes_ != kPositionNotSpecified) {
192 *size_out = total_bytes_;
193 return true;
194 }
195 *size_out = 0;
196 return false;
197 }
198
IsStreaming()199 bool BufferedDataSource::IsStreaming() {
200 return streaming_;
201 }
202
HasSingleOrigin()203 bool BufferedDataSource::HasSingleOrigin() {
204 DCHECK(MessageLoop::current() == render_loop_);
205 return loader_.get() ? loader_->HasSingleOrigin() : true;
206 }
207
Abort()208 void BufferedDataSource::Abort() {
209 DCHECK(MessageLoop::current() == render_loop_);
210
211 CleanupTask();
212 frame_ = NULL;
213 }
214
215 /////////////////////////////////////////////////////////////////////////////
216 // Render thread tasks.
InitializeTask()217 void BufferedDataSource::InitializeTask() {
218 DCHECK(MessageLoop::current() == render_loop_);
219 DCHECK(!loader_.get());
220 if (stopped_on_render_loop_ || !initialize_callback_.get())
221 return;
222
223 // Kick starts the watch dog task that will handle connection timeout.
224 // We run the watch dog 2 times faster the actual timeout so as to catch
225 // the timeout more accurately.
226 watch_dog_timer_.Start(
227 GetTimeoutMilliseconds() / 2,
228 this,
229 &BufferedDataSource::WatchDogTask);
230
231 if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
232 // Do an unbounded range request starting at the beginning. If the server
233 // responds with 200 instead of 206 we'll fall back into a streaming mode.
234 loader_ = CreateResourceLoader(0, kPositionNotSpecified);
235 loader_->Start(
236 NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
237 NewCallback(this, &BufferedDataSource::NetworkEventCallback),
238 frame_);
239 } else {
240 // For all other protocols, assume they support range request. We fetch
241 // the full range of the resource to obtain the instance size because
242 // we won't be served HTTP headers.
243 loader_ = CreateResourceLoader(kPositionNotSpecified,
244 kPositionNotSpecified);
245 loader_->Start(
246 NewCallback(this, &BufferedDataSource::NonHttpInitialStartCallback),
247 NewCallback(this, &BufferedDataSource::NetworkEventCallback),
248 frame_);
249 }
250 }
251
ReadTask(int64 position,int read_size,uint8 * buffer)252 void BufferedDataSource::ReadTask(
253 int64 position,
254 int read_size,
255 uint8* buffer) {
256 DCHECK(MessageLoop::current() == render_loop_);
257 {
258 base::AutoLock auto_lock(lock_);
259 if (stopped_on_render_loop_)
260 return;
261
262 DCHECK(read_callback_.get());
263 }
264
265 // Saves the read parameters.
266 read_position_ = position;
267 read_size_ = read_size;
268 read_buffer_ = buffer;
269 read_submitted_time_ = base::Time::Now();
270 read_attempts_ = 0;
271
272 // Call to read internal to perform the actual read.
273 ReadInternal();
274 }
275
CleanupTask()276 void BufferedDataSource::CleanupTask() {
277 DCHECK(MessageLoop::current() == render_loop_);
278
279 {
280 base::AutoLock auto_lock(lock_);
281 if (stopped_on_render_loop_)
282 return;
283
284 // Signal that stop task has finished execution.
285 // NOTE: it's vital that this be set under lock, as that's how Read() tests
286 // before registering a new |read_callback_| (which is cleared below).
287 stopped_on_render_loop_ = true;
288
289 if (read_callback_.get())
290 DoneRead_Locked(net::ERR_FAILED);
291 }
292
293 // Stop the watch dog.
294 watch_dog_timer_.Stop();
295
296 // We just need to stop the loader, so it stops activity.
297 if (loader_.get())
298 loader_->Stop();
299
300 // Reset the parameters of the current read request.
301 read_position_ = 0;
302 read_size_ = 0;
303 read_buffer_ = 0;
304 read_submitted_time_ = base::Time();
305 read_attempts_ = 0;
306 }
307
RestartLoadingTask()308 void BufferedDataSource::RestartLoadingTask() {
309 DCHECK(MessageLoop::current() == render_loop_);
310 if (stopped_on_render_loop_)
311 return;
312
313 {
314 // If there's no outstanding read then return early.
315 base::AutoLock auto_lock(lock_);
316 if (!read_callback_.get())
317 return;
318 }
319
320 loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
321 BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
322 loader_->UpdateDeferStrategy(strategy);
323 loader_->Start(
324 NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
325 NewCallback(this, &BufferedDataSource::NetworkEventCallback),
326 frame_);
327 }
328
WatchDogTask()329 void BufferedDataSource::WatchDogTask() {
330 DCHECK(MessageLoop::current() == render_loop_);
331 if (stopped_on_render_loop_)
332 return;
333
334 // We only care if there is an active read request.
335 {
336 base::AutoLock auto_lock(lock_);
337 if (!read_callback_.get())
338 return;
339 }
340
341 DCHECK(loader_.get());
342 base::TimeDelta delta = base::Time::Now() - read_submitted_time_;
343 if (delta < GetTimeoutMilliseconds())
344 return;
345
346 // TODO(hclam): Maybe raise an error here. But if an error is reported
347 // the whole pipeline may get destroyed...
348 if (read_attempts_ >= kReadTrials)
349 return;
350
351 ++read_attempts_;
352 read_submitted_time_ = base::Time::Now();
353
354 // Stops the current loader and creates a new resource loader and
355 // retry the request.
356 loader_->Stop();
357 loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
358 BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
359 loader_->UpdateDeferStrategy(strategy);
360 loader_->Start(
361 NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
362 NewCallback(this, &BufferedDataSource::NetworkEventCallback),
363 frame_);
364 }
365
SetPlaybackRateTask(float playback_rate)366 void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
367 DCHECK(MessageLoop::current() == render_loop_);
368 DCHECK(loader_.get());
369
370 bool previously_paused = media_is_paused_;
371 media_is_paused_ = (playback_rate == 0.0);
372
373 if (!media_has_played_ && previously_paused && !media_is_paused_)
374 media_has_played_ = true;
375
376 BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
377 loader_->UpdateDeferStrategy(strategy);
378 }
379
SetPreloadTask(media::Preload preload)380 void BufferedDataSource::SetPreloadTask(media::Preload preload) {
381 DCHECK(MessageLoop::current() == render_loop_);
382 preload_ = preload;
383 }
384
385 BufferedResourceLoader::DeferStrategy
ChooseDeferStrategy()386 BufferedDataSource::ChooseDeferStrategy() {
387 // If the user indicates preload=metadata, then just load exactly
388 // what is needed for starting the pipeline and prerolling frames.
389 if (preload_ == media::METADATA && !media_has_played_)
390 return BufferedResourceLoader::kReadThenDefer;
391
392 // In general, we want to try to buffer the entire video when the video
393 // is paused. But we don't want to do this if the video hasn't played yet
394 // and preload!=auto.
395 if (media_is_paused_ &&
396 (preload_ == media::AUTO || media_has_played_)) {
397 return BufferedResourceLoader::kNeverDefer;
398 }
399
400 // When the video is playing, regardless of preload state, we buffer up
401 // to a hard limit and enable/disable deferring when the buffer is
402 // depleted/full.
403 return BufferedResourceLoader::kThresholdDefer;
404 }
405
406 // This method is the place where actual read happens, |loader_| must be valid
407 // prior to make this method call.
ReadInternal()408 void BufferedDataSource::ReadInternal() {
409 DCHECK(MessageLoop::current() == render_loop_);
410 DCHECK(loader_);
411
412 // First we prepare the intermediate read buffer for BufferedResourceLoader
413 // to write to.
414 if (read_size_ > intermediate_read_buffer_size_) {
415 intermediate_read_buffer_.reset(new uint8[read_size_]);
416 }
417
418 // Perform the actual read with BufferedResourceLoader.
419 loader_->Read(read_position_, read_size_, intermediate_read_buffer_.get(),
420 NewCallback(this, &BufferedDataSource::ReadCallback));
421 }
422
423 // Method to report the results of the current read request. Also reset all
424 // the read parameters.
DoneRead_Locked(int error)425 void BufferedDataSource::DoneRead_Locked(int error) {
426 DCHECK(MessageLoop::current() == render_loop_);
427 DCHECK(read_callback_.get());
428 lock_.AssertAcquired();
429
430 if (error >= 0) {
431 read_callback_->RunWithParams(Tuple1<size_t>(error));
432 } else {
433 read_callback_->RunWithParams(
434 Tuple1<size_t>(static_cast<size_t>(media::DataSource::kReadError)));
435 }
436
437 read_callback_.reset();
438 read_position_ = 0;
439 read_size_ = 0;
440 read_buffer_ = 0;
441 }
442
DoneInitialization_Locked(media::PipelineStatus status)443 void BufferedDataSource::DoneInitialization_Locked(
444 media::PipelineStatus status) {
445 DCHECK(MessageLoop::current() == render_loop_);
446 DCHECK(initialize_callback_.get());
447 lock_.AssertAcquired();
448
449 scoped_ptr<media::PipelineStatusCallback> initialize_callback(
450 initialize_callback_.release());
451 initialize_callback->Run(status);
452 }
453
454 /////////////////////////////////////////////////////////////////////////////
455 // BufferedResourceLoader callback methods.
HttpInitialStartCallback(int error)456 void BufferedDataSource::HttpInitialStartCallback(int error) {
457 DCHECK(MessageLoop::current() == render_loop_);
458 DCHECK(loader_.get());
459
460 int64 instance_size = loader_->instance_size();
461 bool success = error == net::OK;
462
463 if (!initialize_callback_.get()) {
464 loader_->Stop();
465 return;
466 }
467
468 if (success) {
469 // TODO(hclam): Needs more thinking about supporting servers without range
470 // request or their partial response is not complete.
471 total_bytes_ = instance_size;
472 loaded_ = false;
473 streaming_ = (instance_size == kPositionNotSpecified) ||
474 !loader_->range_supported();
475 } else {
476 // TODO(hclam): In case of failure, we can retry several times.
477 loader_->Stop();
478 }
479
480 if (error == net::ERR_INVALID_RESPONSE && using_range_request_) {
481 // Assuming that the Range header was causing the problem. Retry without
482 // the Range header.
483 using_range_request_ = false;
484 loader_ = CreateResourceLoader(kPositionNotSpecified,
485 kPositionNotSpecified);
486 loader_->Start(
487 NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
488 NewCallback(this, &BufferedDataSource::NetworkEventCallback),
489 frame_);
490 return;
491 }
492
493 // Reference to prevent destruction while inside the |initialize_callback_|
494 // call. This is a temporary fix to prevent crashes caused by holding the
495 // lock and running the destructor.
496 // TODO: Review locking in this class and figure out a way to run the callback
497 // w/o the lock.
498 scoped_refptr<BufferedDataSource> destruction_guard(this);
499 {
500 // We need to prevent calling to filter host and running the callback if
501 // we have received the stop signal. We need to lock down the whole callback
502 // method to prevent bad things from happening. The reason behind this is
503 // that we cannot guarantee tasks on render thread have completely stopped
504 // when we receive the Stop() method call. The only way to solve this is to
505 // let tasks on render thread to run but make sure they don't call outside
506 // this object when Stop() method is ever called. Locking this method is
507 // safe because |lock_| is only acquired in tasks on render thread.
508 base::AutoLock auto_lock(lock_);
509 if (stop_signal_received_)
510 return;
511
512 if (!success) {
513 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
514 return;
515 }
516
517 UpdateHostState();
518 DoneInitialization_Locked(media::PIPELINE_OK);
519 }
520 }
521
NonHttpInitialStartCallback(int error)522 void BufferedDataSource::NonHttpInitialStartCallback(int error) {
523 DCHECK(MessageLoop::current() == render_loop_);
524 DCHECK(loader_.get());
525
526 if (!initialize_callback_.get()) {
527 loader_->Stop();
528 return;
529 }
530
531 int64 instance_size = loader_->instance_size();
532 bool success = error == net::OK && instance_size != kPositionNotSpecified;
533
534 if (success) {
535 total_bytes_ = instance_size;
536 buffered_bytes_ = total_bytes_;
537 loaded_ = true;
538 } else {
539 loader_->Stop();
540 }
541
542 // Reference to prevent destruction while inside the |initialize_callback_|
543 // call. This is a temporary fix to prevent crashes caused by holding the
544 // lock and running the destructor.
545 // TODO: Review locking in this class and figure out a way to run the callback
546 // w/o the lock.
547 scoped_refptr<BufferedDataSource> destruction_guard(this);
548 {
549 // We need to prevent calling to filter host and running the callback if
550 // we have received the stop signal. We need to lock down the whole callback
551 // method to prevent bad things from happening. The reason behind this is
552 // that we cannot guarantee tasks on render thread have completely stopped
553 // when we receive the Stop() method call. The only way to solve this is to
554 // let tasks on render thread to run but make sure they don't call outside
555 // this object when Stop() method is ever called. Locking this method is
556 // safe because |lock_| is only acquired in tasks on render thread.
557 base::AutoLock auto_lock(lock_);
558 if (stop_signal_received_ || !initialize_callback_.get())
559 return;
560
561 if (!success) {
562 DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
563 return;
564 }
565
566 UpdateHostState();
567 DoneInitialization_Locked(media::PIPELINE_OK);
568 }
569 }
570
PartialReadStartCallback(int error)571 void BufferedDataSource::PartialReadStartCallback(int error) {
572 DCHECK(MessageLoop::current() == render_loop_);
573 DCHECK(loader_.get());
574
575 if (error == net::OK) {
576 // Once the request has started successfully, we can proceed with
577 // reading from it.
578 ReadInternal();
579 return;
580 }
581
582 // Stop the resource loader since we have received an error.
583 loader_->Stop();
584
585 // We need to prevent calling to filter host and running the callback if
586 // we have received the stop signal. We need to lock down the whole callback
587 // method to prevent bad things from happening. The reason behind this is
588 // that we cannot guarantee tasks on render thread have completely stopped
589 // when we receive the Stop() method call. So only way to solve this is to
590 // let tasks on render thread to run but make sure they don't call outside
591 // this object when Stop() method is ever called. Locking this method is
592 // safe because |lock_| is only acquired in tasks on render thread.
593 base::AutoLock auto_lock(lock_);
594 if (stop_signal_received_)
595 return;
596 DoneRead_Locked(net::ERR_INVALID_RESPONSE);
597 }
598
ReadCallback(int error)599 void BufferedDataSource::ReadCallback(int error) {
600 DCHECK(MessageLoop::current() == render_loop_);
601
602 if (error < 0) {
603 DCHECK(loader_.get());
604
605 // Stop the resource load if it failed.
606 loader_->Stop();
607
608 if (error == net::ERR_CACHE_MISS) {
609 render_loop_->PostTask(FROM_HERE,
610 NewRunnableMethod(this, &BufferedDataSource::RestartLoadingTask));
611 return;
612 }
613 }
614
615 // We need to prevent calling to filter host and running the callback if
616 // we have received the stop signal. We need to lock down the whole callback
617 // method to prevent bad things from happening. The reason behind this is
618 // that we cannot guarantee tasks on render thread have completely stopped
619 // when we receive the Stop() method call. So only way to solve this is to
620 // let tasks on render thread to run but make sure they don't call outside
621 // this object when Stop() method is ever called. Locking this method is safe
622 // because |lock_| is only acquired in tasks on render thread.
623 base::AutoLock auto_lock(lock_);
624 if (stop_signal_received_)
625 return;
626
627 if (error > 0) {
628 // If a position error code is received, read was successful. So copy
629 // from intermediate read buffer to the target read buffer.
630 memcpy(read_buffer_, intermediate_read_buffer_.get(), error);
631 } else if (error == 0 && total_bytes_ == kPositionNotSpecified) {
632 // We've reached the end of the file and we didn't know the total size
633 // before. Update the total size so Read()s past the end of the file will
634 // fail like they would if we had known the file size at the beginning.
635 total_bytes_ = loader_->instance_size();
636
637 if (host() && total_bytes_ != kPositionNotSpecified)
638 host()->SetTotalBytes(total_bytes_);
639 }
640 DoneRead_Locked(error);
641 }
642
NetworkEventCallback()643 void BufferedDataSource::NetworkEventCallback() {
644 DCHECK(MessageLoop::current() == render_loop_);
645 DCHECK(loader_.get());
646
647 // In case of non-HTTP request we don't need to report network events,
648 // so return immediately.
649 if (loaded_)
650 return;
651
652 bool network_activity = loader_->network_activity();
653 int64 buffered_position = loader_->GetBufferedPosition();
654
655 // If we get an unspecified value, return immediately.
656 if (buffered_position == kPositionNotSpecified)
657 return;
658
659 // We need to prevent calling to filter host and running the callback if
660 // we have received the stop signal. We need to lock down the whole callback
661 // method to prevent bad things from happening. The reason behind this is
662 // that we cannot guarantee tasks on render thread have completely stopped
663 // when we receive the Stop() method call. So only way to solve this is to
664 // let tasks on render thread to run but make sure they don't call outside
665 // this object when Stop() method is ever called. Locking this method is safe
666 // because |lock_| is only acquired in tasks on render thread.
667 base::AutoLock auto_lock(lock_);
668 if (stop_signal_received_)
669 return;
670
671 if (network_activity != network_activity_) {
672 network_activity_ = network_activity;
673 if (host())
674 host()->SetNetworkActivity(network_activity);
675 }
676
677 buffered_bytes_ = buffered_position + 1;
678 if (host())
679 host()->SetBufferedBytes(buffered_bytes_);
680 }
681
UpdateHostState()682 void BufferedDataSource::UpdateHostState() {
683 media::FilterHost* filter_host = host();
684 if (!filter_host)
685 return;
686
687 filter_host->SetLoaded(loaded_);
688
689 if (streaming_) {
690 filter_host->SetStreaming(true);
691 } else {
692 filter_host->SetTotalBytes(total_bytes_);
693 filter_host->SetBufferedBytes(buffered_bytes_);
694 }
695 }
696
697 } // namespace webkit_glue
698