OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/filters/ffmpeg_demuxer.h" | 5 #include "media/filters/ffmpeg_demuxer.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
52 default: | 52 default: |
53 NOTREACHED(); | 53 NOTREACHED(); |
54 break; | 54 break; |
55 } | 55 } |
56 | 56 |
57 // Calculate the duration. | 57 // Calculate the duration. |
58 duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration); | 58 duration_ = ConvertStreamTimestamp(stream->time_base, stream->duration); |
59 } | 59 } |
60 | 60 |
61 bool FFmpegDemuxerStream::HasPendingReads() { | 61 bool FFmpegDemuxerStream::HasPendingReads() { |
62 DCHECK_EQ(MessageLoop::current(), demuxer_->message_loop()); | 62 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); |
63 base::AutoLock auto_lock(lock_); | 63 base::AutoLock auto_lock(lock_); |
64 DCHECK(!stopped_ || read_queue_.empty()) | 64 DCHECK(!stopped_ || read_queue_.empty()) |
65 << "Read queue should have been emptied if demuxing stream is stopped"; | 65 << "Read queue should have been emptied if demuxing stream is stopped"; |
66 return !read_queue_.empty(); | 66 return !read_queue_.empty(); |
67 } | 67 } |
68 | 68 |
69 void FFmpegDemuxerStream::EnqueuePacket( | 69 void FFmpegDemuxerStream::EnqueuePacket( |
70 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket> packet) { | 70 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket> packet) { |
71 DCHECK_EQ(MessageLoop::current(), demuxer_->message_loop()); | 71 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); |
72 | 72 |
73 base::AutoLock auto_lock(lock_); | 73 base::AutoLock auto_lock(lock_); |
74 if (stopped_) { | 74 if (stopped_) { |
75 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; | 75 NOTREACHED() << "Attempted to enqueue packet on a stopped stream"; |
76 return; | 76 return; |
77 } | 77 } |
78 | 78 |
79 scoped_refptr<DecoderBuffer> buffer; | 79 scoped_refptr<DecoderBuffer> buffer; |
80 if (!packet.get()) { | 80 if (!packet.get()) { |
81 buffer = DecoderBuffer::CreateEOSBuffer(); | 81 buffer = DecoderBuffer::CreateEOSBuffer(); |
(...skipping 21 matching lines...) Expand all Loading... |
103 } | 103 } |
104 last_packet_timestamp_ = buffer->GetTimestamp(); | 104 last_packet_timestamp_ = buffer->GetTimestamp(); |
105 } | 105 } |
106 | 106 |
107 buffer_queue_.push_back(buffer); | 107 buffer_queue_.push_back(buffer); |
108 FulfillPendingRead(); | 108 FulfillPendingRead(); |
109 return; | 109 return; |
110 } | 110 } |
111 | 111 |
112 void FFmpegDemuxerStream::FlushBuffers() { | 112 void FFmpegDemuxerStream::FlushBuffers() { |
113 DCHECK_EQ(MessageLoop::current(), demuxer_->message_loop()); | 113 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); |
114 base::AutoLock auto_lock(lock_); | 114 base::AutoLock auto_lock(lock_); |
115 DCHECK(read_queue_.empty()) << "Read requests should be empty"; | 115 DCHECK(read_queue_.empty()) << "Read requests should be empty"; |
116 buffer_queue_.clear(); | 116 buffer_queue_.clear(); |
117 last_packet_timestamp_ = kNoTimestamp(); | 117 last_packet_timestamp_ = kNoTimestamp(); |
118 } | 118 } |
119 | 119 |
120 void FFmpegDemuxerStream::Stop() { | 120 void FFmpegDemuxerStream::Stop() { |
121 DCHECK_EQ(MessageLoop::current(), demuxer_->message_loop()); | 121 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); |
122 base::AutoLock auto_lock(lock_); | 122 base::AutoLock auto_lock(lock_); |
123 buffer_queue_.clear(); | 123 buffer_queue_.clear(); |
124 for (ReadQueue::iterator it = read_queue_.begin(); | 124 for (ReadQueue::iterator it = read_queue_.begin(); |
125 it != read_queue_.end(); ++it) { | 125 it != read_queue_.end(); ++it) { |
126 it->Run(DemuxerStream::kOk, | 126 it->Run(DemuxerStream::kOk, |
127 scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer())); | 127 scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer())); |
128 } | 128 } |
129 read_queue_.clear(); | 129 read_queue_.clear(); |
130 stopped_ = true; | 130 stopped_ = true; |
131 } | 131 } |
(...skipping 29 matching lines...) Expand all Loading... |
161 return; | 161 return; |
162 } | 162 } |
163 | 163 |
164 // Send the oldest buffer back. | 164 // Send the oldest buffer back. |
165 scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front(); | 165 scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front(); |
166 buffer_queue_.pop_front(); | 166 buffer_queue_.pop_front(); |
167 read_cb.Run(DemuxerStream::kOk, buffer); | 167 read_cb.Run(DemuxerStream::kOk, buffer); |
168 } | 168 } |
169 | 169 |
170 void FFmpegDemuxerStream::ReadTask(const ReadCB& read_cb) { | 170 void FFmpegDemuxerStream::ReadTask(const ReadCB& read_cb) { |
171 DCHECK_EQ(MessageLoop::current(), demuxer_->message_loop()); | 171 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); |
172 | 172 |
173 base::AutoLock auto_lock(lock_); | 173 base::AutoLock auto_lock(lock_); |
174 // Don't accept any additional reads if we've been told to stop. | 174 // Don't accept any additional reads if we've been told to stop. |
175 // | 175 // |
176 // TODO(scherkus): it would be cleaner if we replied with an error message. | 176 // TODO(scherkus): it would be cleaner if we replied with an error message. |
177 if (stopped_) { | 177 if (stopped_) { |
178 read_cb.Run(DemuxerStream::kOk, | 178 read_cb.Run(DemuxerStream::kOk, |
179 scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer())); | 179 scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer())); |
180 return; | 180 return; |
181 } | 181 } |
182 | 182 |
183 // Enqueue the callback and attempt to satisfy it immediately. | 183 // Enqueue the callback and attempt to satisfy it immediately. |
184 read_queue_.push_back(read_cb); | 184 read_queue_.push_back(read_cb); |
185 FulfillPendingRead(); | 185 FulfillPendingRead(); |
186 | 186 |
187 // Check if there are still pending reads, demux some more. | 187 // Check if there are still pending reads, demux some more. |
188 if (!read_queue_.empty()) { | 188 if (!read_queue_.empty()) { |
189 demuxer_->PostDemuxTask(); | 189 demuxer_->PostDemuxTask(); |
190 } | 190 } |
191 } | 191 } |
192 | 192 |
193 void FFmpegDemuxerStream::FulfillPendingRead() { | 193 void FFmpegDemuxerStream::FulfillPendingRead() { |
194 DCHECK_EQ(MessageLoop::current(), demuxer_->message_loop()); | 194 DCHECK(demuxer_->message_loop()->BelongsToCurrentThread()); |
195 lock_.AssertAcquired(); | 195 lock_.AssertAcquired(); |
196 if (buffer_queue_.empty() || read_queue_.empty()) { | 196 if (buffer_queue_.empty() || read_queue_.empty()) { |
197 return; | 197 return; |
198 } | 198 } |
199 | 199 |
200 // Dequeue a buffer and pending read pair. | 200 // Dequeue a buffer and pending read pair. |
201 scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front(); | 201 scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front(); |
202 ReadCB read_cb(read_queue_.front()); | 202 ReadCB read_cb(read_queue_.front()); |
203 buffer_queue_.pop_front(); | 203 buffer_queue_.pop_front(); |
204 read_queue_.pop_front(); | 204 read_queue_.pop_front(); |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) | 248 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) |
249 return kNoTimestamp(); | 249 return kNoTimestamp(); |
250 | 250 |
251 return ConvertFromTimeBase(time_base, timestamp); | 251 return ConvertFromTimeBase(time_base, timestamp); |
252 } | 252 } |
253 | 253 |
254 // | 254 // |
255 // FFmpegDemuxer | 255 // FFmpegDemuxer |
256 // | 256 // |
257 FFmpegDemuxer::FFmpegDemuxer( | 257 FFmpegDemuxer::FFmpegDemuxer( |
258 MessageLoop* message_loop, | 258 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
259 const scoped_refptr<DataSource>& data_source) | 259 const scoped_refptr<DataSource>& data_source) |
260 : host_(NULL), | 260 : host_(NULL), |
261 message_loop_(message_loop), | 261 message_loop_(message_loop), |
262 format_context_(NULL), | 262 format_context_(NULL), |
263 data_source_(data_source), | 263 data_source_(data_source), |
264 read_event_(false, false), | 264 read_event_(false, false), |
265 read_has_failed_(false), | 265 read_has_failed_(false), |
266 last_read_bytes_(0), | 266 last_read_bytes_(0), |
267 read_position_(0), | 267 read_position_(0), |
268 bitrate_(0), | 268 bitrate_(0), |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
401 DCHECK(data_source_); | 401 DCHECK(data_source_); |
402 return data_source_->GetSize(size_out); | 402 return data_source_->GetSize(size_out); |
403 } | 403 } |
404 | 404 |
405 bool FFmpegDemuxer::IsStreaming() { | 405 bool FFmpegDemuxer::IsStreaming() { |
406 DCHECK(host_); | 406 DCHECK(host_); |
407 DCHECK(data_source_); | 407 DCHECK(data_source_); |
408 return data_source_->IsStreaming(); | 408 return data_source_->IsStreaming(); |
409 } | 409 } |
410 | 410 |
411 MessageLoop* FFmpegDemuxer::message_loop() { | 411 scoped_refptr<base::MessageLoopProxy> FFmpegDemuxer::message_loop() { |
412 return message_loop_; | 412 return message_loop_; |
413 } | 413 } |
414 | 414 |
415 // Helper for calculating the bitrate of the media based on information stored | 415 // Helper for calculating the bitrate of the media based on information stored |
416 // in |format_context| or failing that the size and duration of the media. | 416 // in |format_context| or failing that the size and duration of the media. |
417 // | 417 // |
418 // Returns 0 if a bitrate could not be determined. | 418 // Returns 0 if a bitrate could not be determined. |
419 static int CalculateBitrate( | 419 static int CalculateBitrate( |
420 AVFormatContext* format_context, | 420 AVFormatContext* format_context, |
421 const base::TimeDelta& duration, | 421 const base::TimeDelta& duration, |
(...skipping 21 matching lines...) Expand all Loading... |
443 | 443 |
444 // Do math in floating point as we'd overflow an int64 if the filesize was | 444 // Do math in floating point as we'd overflow an int64 if the filesize was |
445 // larger than ~1073GB. | 445 // larger than ~1073GB. |
446 double bytes = filesize_in_bytes; | 446 double bytes = filesize_in_bytes; |
447 double duration_us = duration.InMicroseconds(); | 447 double duration_us = duration.InMicroseconds(); |
448 return bytes * 8000000.0 / duration_us; | 448 return bytes * 8000000.0 / duration_us; |
449 } | 449 } |
450 | 450 |
451 void FFmpegDemuxer::InitializeTask(DemuxerHost* host, | 451 void FFmpegDemuxer::InitializeTask(DemuxerHost* host, |
452 const PipelineStatusCB& status_cb) { | 452 const PipelineStatusCB& status_cb) { |
453 DCHECK_EQ(MessageLoop::current(), message_loop_); | 453 DCHECK(message_loop_->BelongsToCurrentThread()); |
454 host_ = host; | 454 host_ = host; |
455 | 455 |
456 // TODO(scherkus): DataSource should have a host by this point, | 456 // TODO(scherkus): DataSource should have a host by this point, |
457 // see http://crbug.com/122071 | 457 // see http://crbug.com/122071 |
458 data_source_->set_host(host); | 458 data_source_->set_host(host); |
459 | 459 |
460 // Add ourself to Protocol list and get our unique key. | 460 // Add ourself to Protocol list and get our unique key. |
461 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); | 461 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); |
462 | 462 |
463 // Open FFmpeg AVFormatContext. | 463 // Open FFmpeg AVFormatContext. |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
562 int64 filesize_in_bytes = 0; | 562 int64 filesize_in_bytes = 0; |
563 GetSize(&filesize_in_bytes); | 563 GetSize(&filesize_in_bytes); |
564 bitrate_ = CalculateBitrate(format_context_, max_duration, filesize_in_bytes); | 564 bitrate_ = CalculateBitrate(format_context_, max_duration, filesize_in_bytes); |
565 if (bitrate_ > 0) | 565 if (bitrate_ > 0) |
566 data_source_->SetBitrate(bitrate_); | 566 data_source_->SetBitrate(bitrate_); |
567 | 567 |
568 status_cb.Run(PIPELINE_OK); | 568 status_cb.Run(PIPELINE_OK); |
569 } | 569 } |
570 | 570 |
571 void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) { | 571 void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) { |
572 DCHECK_EQ(MessageLoop::current(), message_loop_); | 572 DCHECK(message_loop_->BelongsToCurrentThread()); |
573 | 573 |
574 // Tell streams to flush buffers due to seeking. | 574 // Tell streams to flush buffers due to seeking. |
575 StreamVector::iterator iter; | 575 StreamVector::iterator iter; |
576 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 576 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
577 if (*iter) | 577 if (*iter) |
578 (*iter)->FlushBuffers(); | 578 (*iter)->FlushBuffers(); |
579 } | 579 } |
580 | 580 |
581 // Always seek to a timestamp less than or equal to the desired timestamp. | 581 // Always seek to a timestamp less than or equal to the desired timestamp. |
582 int flags = AVSEEK_FLAG_BACKWARD; | 582 int flags = AVSEEK_FLAG_BACKWARD; |
583 | 583 |
584 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 584 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
585 // will attempt to use the lowest-index video stream, if present, followed by | 585 // will attempt to use the lowest-index video stream, if present, followed by |
586 // the lowest-index audio stream. | 586 // the lowest-index audio stream. |
587 if (av_seek_frame(format_context_, -1, time.InMicroseconds(), flags) < 0) { | 587 if (av_seek_frame(format_context_, -1, time.InMicroseconds(), flags) < 0) { |
588 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being | 588 // Use VLOG(1) instead of NOTIMPLEMENTED() to prevent the message being |
589 // captured from stdout and contaminates testing. | 589 // captured from stdout and contaminates testing. |
590 // TODO(scherkus): Implement this properly and signal error (BUG=23447). | 590 // TODO(scherkus): Implement this properly and signal error (BUG=23447). |
591 VLOG(1) << "Not implemented"; | 591 VLOG(1) << "Not implemented"; |
592 } | 592 } |
593 | 593 |
594 // Notify we're finished seeking. | 594 // Notify we're finished seeking. |
595 cb.Run(PIPELINE_OK); | 595 cb.Run(PIPELINE_OK); |
596 } | 596 } |
597 | 597 |
598 void FFmpegDemuxer::DemuxTask() { | 598 void FFmpegDemuxer::DemuxTask() { |
599 DCHECK_EQ(MessageLoop::current(), message_loop_); | 599 DCHECK(message_loop_->BelongsToCurrentThread()); |
600 | 600 |
601 // Make sure we have work to do before demuxing. | 601 // Make sure we have work to do before demuxing. |
602 if (!StreamsHavePendingReads()) { | 602 if (!StreamsHavePendingReads()) { |
603 return; | 603 return; |
604 } | 604 } |
605 | 605 |
606 // Allocate and read an AVPacket from the media. | 606 // Allocate and read an AVPacket from the media. |
607 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket> packet(new AVPacket()); | 607 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket> packet(new AVPacket()); |
608 int result = av_read_frame(format_context_, packet.get()); | 608 int result = av_read_frame(format_context_, packet.get()); |
609 if (result < 0) { | 609 if (result < 0) { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
648 } | 648 } |
649 | 649 |
650 // Create a loop by posting another task. This allows seek and message loop | 650 // Create a loop by posting another task. This allows seek and message loop |
651 // quit tasks to get processed. | 651 // quit tasks to get processed. |
652 if (StreamsHavePendingReads()) { | 652 if (StreamsHavePendingReads()) { |
653 PostDemuxTask(); | 653 PostDemuxTask(); |
654 } | 654 } |
655 } | 655 } |
656 | 656 |
657 void FFmpegDemuxer::StopTask(const base::Closure& callback) { | 657 void FFmpegDemuxer::StopTask(const base::Closure& callback) { |
658 DCHECK_EQ(MessageLoop::current(), message_loop_); | 658 DCHECK(message_loop_->BelongsToCurrentThread()); |
659 StreamVector::iterator iter; | 659 StreamVector::iterator iter; |
660 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 660 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
661 if (*iter) | 661 if (*iter) |
662 (*iter)->Stop(); | 662 (*iter)->Stop(); |
663 } | 663 } |
664 if (data_source_) { | 664 if (data_source_) { |
665 data_source_->Stop(callback); | 665 data_source_->Stop(callback); |
666 } else { | 666 } else { |
667 callback.Run(); | 667 callback.Run(); |
668 } | 668 } |
669 } | 669 } |
670 | 670 |
671 void FFmpegDemuxer::DisableAudioStreamTask() { | 671 void FFmpegDemuxer::DisableAudioStreamTask() { |
672 DCHECK_EQ(MessageLoop::current(), message_loop_); | 672 DCHECK(message_loop_->BelongsToCurrentThread()); |
673 audio_disabled_ = true; | 673 audio_disabled_ = true; |
674 StreamVector::iterator iter; | 674 StreamVector::iterator iter; |
675 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 675 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
676 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | 676 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
677 (*iter)->Stop(); | 677 (*iter)->Stop(); |
678 } | 678 } |
679 } | 679 } |
680 } | 680 } |
681 | 681 |
682 bool FFmpegDemuxer::StreamsHavePendingReads() { | 682 bool FFmpegDemuxer::StreamsHavePendingReads() { |
683 DCHECK_EQ(MessageLoop::current(), message_loop_); | 683 DCHECK(message_loop_->BelongsToCurrentThread()); |
684 StreamVector::iterator iter; | 684 StreamVector::iterator iter; |
685 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 685 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
686 if (*iter && (*iter)->HasPendingReads()) { | 686 if (*iter && (*iter)->HasPendingReads()) { |
687 return true; | 687 return true; |
688 } | 688 } |
689 } | 689 } |
690 return false; | 690 return false; |
691 } | 691 } |
692 | 692 |
693 void FFmpegDemuxer::StreamHasEnded() { | 693 void FFmpegDemuxer::StreamHasEnded() { |
694 DCHECK_EQ(MessageLoop::current(), message_loop_); | 694 DCHECK(message_loop_->BelongsToCurrentThread()); |
695 StreamVector::iterator iter; | 695 StreamVector::iterator iter; |
696 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 696 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
697 if (!*iter || | 697 if (!*iter || |
698 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { | 698 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { |
699 continue; | 699 continue; |
700 } | 700 } |
701 (*iter)->EnqueuePacket( | 701 (*iter)->EnqueuePacket( |
702 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket>()); | 702 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket>()); |
703 } | 703 } |
704 } | 704 } |
705 | 705 |
706 int FFmpegDemuxer::WaitForRead() { | 706 int FFmpegDemuxer::WaitForRead() { |
707 read_event_.Wait(); | 707 read_event_.Wait(); |
708 return last_read_bytes_; | 708 return last_read_bytes_; |
709 } | 709 } |
710 | 710 |
711 void FFmpegDemuxer::SignalReadCompleted(int size) { | 711 void FFmpegDemuxer::SignalReadCompleted(int size) { |
712 last_read_bytes_ = size; | 712 last_read_bytes_ = size; |
713 read_event_.Signal(); | 713 read_event_.Signal(); |
714 } | 714 } |
715 | 715 |
716 void FFmpegDemuxer::NotifyBufferingChanged() { | 716 void FFmpegDemuxer::NotifyBufferingChanged() { |
717 DCHECK_EQ(MessageLoop::current(), message_loop_); | 717 DCHECK(message_loop_->BelongsToCurrentThread()); |
718 Ranges<base::TimeDelta> buffered; | 718 Ranges<base::TimeDelta> buffered; |
719 scoped_refptr<FFmpegDemuxerStream> audio = | 719 scoped_refptr<FFmpegDemuxerStream> audio = |
720 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); | 720 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); |
721 scoped_refptr<FFmpegDemuxerStream> video = | 721 scoped_refptr<FFmpegDemuxerStream> video = |
722 GetFFmpegStream(DemuxerStream::VIDEO); | 722 GetFFmpegStream(DemuxerStream::VIDEO); |
723 if (audio && video) { | 723 if (audio && video) { |
724 buffered = audio->GetBufferedRanges().IntersectionWith( | 724 buffered = audio->GetBufferedRanges().IntersectionWith( |
725 video->GetBufferedRanges()); | 725 video->GetBufferedRanges()); |
726 } else if (audio) { | 726 } else if (audio) { |
727 buffered = audio->GetBufferedRanges(); | 727 buffered = audio->GetBufferedRanges(); |
728 } else if (video) { | 728 } else if (video) { |
729 buffered = video->GetBufferedRanges(); | 729 buffered = video->GetBufferedRanges(); |
730 } | 730 } |
731 for (size_t i = 0; i < buffered.size(); ++i) | 731 for (size_t i = 0; i < buffered.size(); ++i) |
732 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 732 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
733 } | 733 } |
734 | 734 |
735 } // namespace media | 735 } // namespace media |
OLD | NEW |