• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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