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 31 static WebDataSource* NewBufferedDataSource(MessageLoop* render_loop, 32 WebKit::WebFrame* frame) { 33 return new BufferedDataSource(render_loop, frame); 34 } 35 36 // static 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 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 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. 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. 90 base::TimeDelta BufferedDataSource::GetTimeoutMilliseconds() { 91 return base::TimeDelta::FromMilliseconds(kTimeoutMilliseconds); 92 } 93 94 void BufferedDataSource::set_host(media::FilterHost* host) { 95 DataSource::set_host(host); 96 97 if (loader_.get()) 98 UpdateHostState(); 99 } 100 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 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. 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 153 void BufferedDataSource::SetPlaybackRate(float playback_rate) { 154 render_loop_->PostTask(FROM_HERE, 155 NewRunnableMethod(this, &BufferedDataSource::SetPlaybackRateTask, 156 playback_rate)); 157 } 158 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. 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 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 199 bool BufferedDataSource::IsStreaming() { 200 return streaming_; 201 } 202 203 bool BufferedDataSource::HasSingleOrigin() { 204 DCHECK(MessageLoop::current() == render_loop_); 205 return loader_.get() ? loader_->HasSingleOrigin() : true; 206 } 207 208 void BufferedDataSource::Abort() { 209 DCHECK(MessageLoop::current() == render_loop_); 210 211 CleanupTask(); 212 frame_ = NULL; 213 } 214 215 ///////////////////////////////////////////////////////////////////////////// 216 // Render thread tasks. 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 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 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 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 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 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 380 void BufferedDataSource::SetPreloadTask(media::Preload preload) { 381 DCHECK(MessageLoop::current() == render_loop_); 382 preload_ = preload; 383 } 384 385 BufferedResourceLoader::DeferStrategy 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. 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. 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 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. 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 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 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 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 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 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