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

Side by Side Diff: media/filters/source_buffer_stream.cc

Issue 1008463002: Fix MSE GC, make it less aggressive, more spec-compliant (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Use newDataSize in SourceBufferStream::GarbageCollectIfNeeded Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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/source_buffer_stream.h" 5 #include "media/filters/source_buffer_stream.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <map> 8 #include <map>
9 #include <sstream> 9 #include <sstream>
10 10
(...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after
394 // are appended to the range covered by |track_buffer_|. 394 // are appended to the range covered by |track_buffer_|.
395 if (!track_buffer_.empty()) { 395 if (!track_buffer_.empty()) {
396 DecodeTimestamp keyframe_timestamp = 396 DecodeTimestamp keyframe_timestamp =
397 FindKeyframeAfterTimestamp(track_buffer_.front()->GetDecodeTimestamp()); 397 FindKeyframeAfterTimestamp(track_buffer_.front()->GetDecodeTimestamp());
398 if (keyframe_timestamp != kNoDecodeTimestamp()) 398 if (keyframe_timestamp != kNoDecodeTimestamp())
399 PruneTrackBuffer(keyframe_timestamp); 399 PruneTrackBuffer(keyframe_timestamp);
400 } 400 }
401 401
402 SetSelectedRangeIfNeeded(next_buffer_timestamp); 402 SetSelectedRangeIfNeeded(next_buffer_timestamp);
403 403
404 GarbageCollectIfNeeded();
405
406 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() 404 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName()
407 << ": done. ranges_=" << RangesToString(ranges_); 405 << ": done. ranges_=" << RangesToString(ranges_);
408 DCHECK(IsRangeListSorted(ranges_)); 406 DCHECK(IsRangeListSorted(ranges_));
409 DCHECK(OnlySelectedRangeIsSeeked()); 407 DCHECK(OnlySelectedRangeIsSeeked());
410 return true; 408 return true;
411 } 409 }
412 410
413 void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end, 411 void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end,
414 base::TimeDelta duration) { 412 base::TimeDelta duration) {
415 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() 413 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName()
(...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after
644 } 642 }
645 } 643 }
646 644
647 void SourceBufferStream::SetConfigIds(const BufferQueue& buffers) { 645 void SourceBufferStream::SetConfigIds(const BufferQueue& buffers) {
648 for (BufferQueue::const_iterator itr = buffers.begin(); 646 for (BufferQueue::const_iterator itr = buffers.begin();
649 itr != buffers.end(); ++itr) { 647 itr != buffers.end(); ++itr) {
650 (*itr)->SetConfigId(append_config_index_); 648 (*itr)->SetConfigId(append_config_index_);
651 } 649 }
652 } 650 }
653 651
654 void SourceBufferStream::GarbageCollectIfNeeded() { 652 bool SourceBufferStream::GarbageCollectIfNeeded(DecodeTimestamp media_time,
653 size_t newDataSize) {
654 DCHECK(media_time != kNoDecodeTimestamp());
655 // Compute size of |ranges_|. 655 // Compute size of |ranges_|.
656 size_t ranges_size = 0; 656 size_t ranges_size = 0;
657 for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) 657 for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr)
wolenetz 2015/08/20 23:23:37 instead, use GetBufferedSize() ?
servolk 2015/08/21 01:32:08 Done.
658 ranges_size += (*itr)->size_in_bytes(); 658 ranges_size += (*itr)->size_in_bytes();
659 659
660 // Return if we're under or at the memory limit. 660 // Return if we're under or at the memory limit.
661 if (ranges_size <= memory_limit_) 661 if (ranges_size + newDataSize <= memory_limit_)
wolenetz 2015/08/20 23:23:37 this can overflow
servolk 2015/08/21 01:32:08 Done, added a sanity and overflow check
662 return; 662 return true;
663 663
664 size_t bytes_to_free = ranges_size - memory_limit_; 664 size_t bytes_to_free = ranges_size + newDataSize - memory_limit_;
wolenetz 2015/08/20 23:23:37 If newDataSize > memory_limit, trivially return fa
servolk 2015/08/21 01:32:08 Done.
665 665
666 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": Before GC" 666 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": Before GC"
667 << " media_time=" << media_time.InSecondsF()
668 << " ranges_=" << RangesToString(ranges_)
667 << " ranges_size=" << ranges_size 669 << " ranges_size=" << ranges_size
668 << " ranges_=" << RangesToString(ranges_) 670 << " newDataSize=" << newDataSize
669 << " memory_limit_=" << memory_limit_; 671 << " memory_limit_=" << memory_limit_
672 << " last_appended_buffer_timestamp_="
673 << last_appended_buffer_timestamp_.InSecondsF();
670 674
671 // Begin deleting after the last appended buffer. 675 size_t bytes_freed = 0;
672 size_t bytes_freed = FreeBuffersAfterLastAppended(bytes_to_free);
673 676
674 // Begin deleting from the front. 677 // If last appended buffer position was earlier than the current playback time
675 if (bytes_freed < bytes_to_free) 678 // then try deleting data between last append and current media_time.
676 bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, false); 679 if (last_appended_buffer_timestamp_ != kNoDecodeTimestamp() &&
680 last_appended_buffer_timestamp_ < media_time) {
681 size_t between = FreeBuffersAfterLastAppended(bytes_to_free, media_time);
682 DVLOG(3) << __FUNCTION__ << " FreeBuffersAfterLastAppended "
683 << " released " << between << " bytes"
684 << " ranges_=" << RangesToString(ranges_);
685 bytes_freed += between;
677 686
678 // Begin deleting from the back. 687 // If the last append happened before the current playback position
679 if (bytes_freed < bytes_to_free) 688 // |media_time|, then JS player is probably preparing to seek back and we
680 bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, true); 689 // should try to preserve all most recently appended data (which is in
690 // range_for_next_append_) from being removed by GC (see crbug.com/440173)
691 if (range_for_next_append_ != ranges_.end()) {
692 DCHECK((*range_for_next_append_)->GetStartTimestamp() <= media_time);
693 media_time = (*range_for_next_append_)->GetStartTimestamp();
694 }
695 }
696
697 // Try removing data from the front of the SourceBuffer up to |media_time|
698 // position.
699 if (bytes_freed < bytes_to_free) {
700 size_t front = FreeBuffers(bytes_to_free - bytes_freed, media_time, false);
701 DVLOG(3) << __FUNCTION__ << " Removed " << front << " bytes from the front"
702 << " ranges_=" << RangesToString(ranges_);
703 bytes_freed += front;
704 }
705
706 // Try removing data from the back of the SourceBuffer, until we reach the
707 // most recent append position.
708 if (bytes_freed < bytes_to_free) {
709 size_t back = FreeBuffers(bytes_to_free - bytes_freed, media_time, true);
710 DVLOG(3) << __FUNCTION__ << " Removed " << back << " bytes from the back"
711 << " ranges_=" << RangesToString(ranges_);
712 bytes_freed += back;
713 }
681 714
682 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": After GC" 715 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": After GC"
716 << " bytes_to_free=" << bytes_to_free
683 << " bytes_freed=" << bytes_freed 717 << " bytes_freed=" << bytes_freed
684 << " ranges_=" << RangesToString(ranges_); 718 << " ranges_=" << RangesToString(ranges_);
719
720 return bytes_freed >= bytes_to_free;
685 } 721 }
686 722
687 size_t SourceBufferStream::FreeBuffersAfterLastAppended( 723 size_t SourceBufferStream::FreeBuffersAfterLastAppended(
688 size_t total_bytes_to_free) { 724 size_t total_bytes_to_free, DecodeTimestamp media_time) {
689 DecodeTimestamp next_buffer_timestamp = GetNextBufferTimestamp(); 725 DVLOG(4) << __FUNCTION__ << " last_appended_buffer_timestamp_="
690 if (last_appended_buffer_timestamp_ == kNoDecodeTimestamp() || 726 << last_appended_buffer_timestamp_.InSecondsF()
691 next_buffer_timestamp == kNoDecodeTimestamp() || 727 << " media_time=" << media_time.InSecondsF();
692 last_appended_buffer_timestamp_ >= next_buffer_timestamp) {
693 return 0;
694 }
695 728
696 DecodeTimestamp remove_range_start = last_appended_buffer_timestamp_; 729 DecodeTimestamp remove_range_start = last_appended_buffer_timestamp_;
697 if (last_appended_buffer_is_keyframe_) 730 if (last_appended_buffer_is_keyframe_)
698 remove_range_start += GetMaxInterbufferDistance(); 731 remove_range_start += GetMaxInterbufferDistance();
699 732
700 DecodeTimestamp remove_range_start_keyframe = FindKeyframeAfterTimestamp( 733 DecodeTimestamp remove_range_start_keyframe = FindKeyframeAfterTimestamp(
701 remove_range_start); 734 remove_range_start);
702 if (remove_range_start_keyframe != kNoDecodeTimestamp()) 735 if (remove_range_start_keyframe != kNoDecodeTimestamp())
703 remove_range_start = remove_range_start_keyframe; 736 remove_range_start = remove_range_start_keyframe;
704 if (remove_range_start >= next_buffer_timestamp) 737 if (remove_range_start >= media_time)
705 return 0; 738 return 0;
706 739
707 DecodeTimestamp remove_range_end; 740 DecodeTimestamp remove_range_end;
708 size_t bytes_freed = GetRemovalRange(remove_range_start, 741 size_t bytes_freed = GetRemovalRange(remove_range_start,
709 next_buffer_timestamp, 742 media_time,
710 total_bytes_to_free, 743 total_bytes_to_free,
711 &remove_range_end); 744 &remove_range_end);
712 if (bytes_freed > 0) { 745 if (bytes_freed > 0) {
746 DVLOG(4) << __FUNCTION__ << " removing ["
747 << remove_range_start.ToPresentationTime().InSecondsF() << ";"
748 << remove_range_end.ToPresentationTime().InSecondsF() << "]";
713 Remove(remove_range_start.ToPresentationTime(), 749 Remove(remove_range_start.ToPresentationTime(),
714 remove_range_end.ToPresentationTime(), 750 remove_range_end.ToPresentationTime(),
715 next_buffer_timestamp.ToPresentationTime()); 751 media_time.ToPresentationTime());
716 } 752 }
717 753
718 return bytes_freed; 754 return bytes_freed;
719 } 755 }
720 756
721 size_t SourceBufferStream::GetRemovalRange( 757 size_t SourceBufferStream::GetRemovalRange(
722 DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp, 758 DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp,
723 size_t total_bytes_to_free, DecodeTimestamp* removal_end_timestamp) { 759 size_t total_bytes_to_free, DecodeTimestamp* removal_end_timestamp) {
724 DCHECK(start_timestamp >= DecodeTimestamp()) << start_timestamp.InSecondsF(); 760 DCHECK(start_timestamp >= DecodeTimestamp()) << start_timestamp.InSecondsF();
725 DCHECK(start_timestamp < end_timestamp) 761 DCHECK(start_timestamp < end_timestamp)
(...skipping 12 matching lines...) Expand all
738 774
739 size_t bytes_to_free = total_bytes_to_free - bytes_freed; 775 size_t bytes_to_free = total_bytes_to_free - bytes_freed;
740 size_t bytes_removed = range->GetRemovalGOP( 776 size_t bytes_removed = range->GetRemovalGOP(
741 start_timestamp, end_timestamp, bytes_to_free, removal_end_timestamp); 777 start_timestamp, end_timestamp, bytes_to_free, removal_end_timestamp);
742 bytes_freed += bytes_removed; 778 bytes_freed += bytes_removed;
743 } 779 }
744 return bytes_freed; 780 return bytes_freed;
745 } 781 }
746 782
747 size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free, 783 size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free,
784 DecodeTimestamp media_time,
748 bool reverse_direction) { 785 bool reverse_direction) {
749 TRACE_EVENT2("media", "SourceBufferStream::FreeBuffers", 786 TRACE_EVENT2("media", "SourceBufferStream::FreeBuffers",
750 "total bytes to free", total_bytes_to_free, 787 "total bytes to free", total_bytes_to_free,
751 "reverse direction", reverse_direction); 788 "reverse direction", reverse_direction);
752 789
753 DCHECK_GT(total_bytes_to_free, 0u); 790 DCHECK_GT(total_bytes_to_free, 0u);
754 size_t bytes_freed = 0; 791 size_t bytes_freed = 0;
755 792
756 // This range will save the last GOP appended to |range_for_next_append_| 793 // This range will save the last GOP appended to |range_for_next_append_|
757 // if the buffers surrounding it get deleted during garbage collection. 794 // if the buffers surrounding it get deleted during garbage collection.
758 SourceBufferRange* new_range_for_append = NULL; 795 SourceBufferRange* new_range_for_append = NULL;
759 796
760 while (!ranges_.empty() && bytes_freed < total_bytes_to_free) { 797 while (!ranges_.empty() && bytes_freed < total_bytes_to_free) {
761 SourceBufferRange* current_range = NULL; 798 SourceBufferRange* current_range = NULL;
762 BufferQueue buffers; 799 BufferQueue buffers;
763 size_t bytes_deleted = 0; 800 size_t bytes_deleted = 0;
764 801
765 if (reverse_direction) { 802 if (reverse_direction) {
766 current_range = ranges_.back(); 803 current_range = ranges_.back();
804 DVLOG(5) << "current_range=" << RangeToString(*current_range);
767 if (current_range->LastGOPContainsNextBufferPosition()) { 805 if (current_range->LastGOPContainsNextBufferPosition()) {
768 DCHECK_EQ(current_range, selected_range_); 806 DCHECK_EQ(current_range, selected_range_);
807 DVLOG(5) << "current_range contains next read position, stopping GC";
769 break; 808 break;
770 } 809 }
810 DVLOG(5) << "Deleting GOP from back: " << RangeToString(*current_range);
771 bytes_deleted = current_range->DeleteGOPFromBack(&buffers); 811 bytes_deleted = current_range->DeleteGOPFromBack(&buffers);
772 } else { 812 } else {
773 current_range = ranges_.front(); 813 current_range = ranges_.front();
774 if (current_range->FirstGOPContainsNextBufferPosition()) { 814 DVLOG(5) << "current_range=" << RangeToString(*current_range);
775 DCHECK_EQ(current_range, selected_range_); 815 if (!current_range->FirstGOPEarlierThanMediaTime(media_time)) {
816 // We have removed all data up to the GOP that contains current playback
817 // position, we can't delete any further.
818 DVLOG(5) << "current_range contains playback position, stopping GC";
776 break; 819 break;
777 } 820 }
821 DVLOG(4) << "Deleting GOP from front: " << RangeToString(*current_range);
778 bytes_deleted = current_range->DeleteGOPFromFront(&buffers); 822 bytes_deleted = current_range->DeleteGOPFromFront(&buffers);
779 } 823 }
780 824
781 // Check to see if we've just deleted the GOP that was last appended. 825 // Check to see if we've just deleted the GOP that was last appended.
782 DecodeTimestamp end_timestamp = buffers.back()->GetDecodeTimestamp(); 826 DecodeTimestamp end_timestamp = buffers.back()->GetDecodeTimestamp();
783 if (end_timestamp == last_appended_buffer_timestamp_) { 827 if (end_timestamp == last_appended_buffer_timestamp_) {
784 DCHECK(last_appended_buffer_timestamp_ != kNoDecodeTimestamp()); 828 DCHECK(last_appended_buffer_timestamp_ != kNoDecodeTimestamp());
785 DCHECK(!new_range_for_append); 829 DCHECK(!new_range_for_append);
830
786 // Create a new range containing these buffers. 831 // Create a new range containing these buffers.
787 new_range_for_append = new SourceBufferRange( 832 new_range_for_append = new SourceBufferRange(
788 TypeToGapPolicy(GetType()), 833 TypeToGapPolicy(GetType()),
789 buffers, kNoDecodeTimestamp(), 834 buffers, kNoDecodeTimestamp(),
790 base::Bind(&SourceBufferStream::GetMaxInterbufferDistance, 835 base::Bind(&SourceBufferStream::GetMaxInterbufferDistance,
791 base::Unretained(this))); 836 base::Unretained(this)));
792 range_for_next_append_ = ranges_.end(); 837 range_for_next_append_ = ranges_.end();
793 } else { 838 } else {
794 bytes_freed += bytes_deleted; 839 bytes_freed += bytes_deleted;
795 } 840 }
796 841
797 if (current_range->size_in_bytes() == 0) { 842 if (current_range->size_in_bytes() == 0) {
798 DCHECK_NE(current_range, selected_range_); 843 DCHECK_NE(current_range, selected_range_);
799 DCHECK(range_for_next_append_ == ranges_.end() || 844 DCHECK(range_for_next_append_ == ranges_.end() ||
800 *range_for_next_append_ != current_range); 845 *range_for_next_append_ != current_range);
801 delete current_range; 846 delete current_range;
802 reverse_direction ? ranges_.pop_back() : ranges_.pop_front(); 847 reverse_direction ? ranges_.pop_back() : ranges_.pop_front();
803 } 848 }
849
850 if (reverse_direction && new_range_for_append) {
851 // We don't want to delete any further, or we'll be creating gaps
852 break;
853 }
804 } 854 }
805 855
806 // Insert |new_range_for_append| into |ranges_|, if applicable. 856 // Insert |new_range_for_append| into |ranges_|, if applicable.
807 if (new_range_for_append) { 857 if (new_range_for_append) {
808 range_for_next_append_ = AddToRanges(new_range_for_append); 858 range_for_next_append_ = AddToRanges(new_range_for_append);
809 DCHECK(range_for_next_append_ != ranges_.end()); 859 DCHECK(range_for_next_append_ != ranges_.end());
810 860
811 // Check to see if we need to merge |new_range_for_append| with the range 861 // Check to see if we need to merge |new_range_for_append| with the range
812 // before or after it. |new_range_for_append| is created whenever the last 862 // before or after it. |new_range_for_append| is created whenever the last
813 // GOP appended is encountered, regardless of whether any buffers after it 863 // GOP appended is encountered, regardless of whether any buffers after it
(...skipping 439 matching lines...) Expand 10 before | Expand all | Expand 10 after
1253 } 1303 }
1254 1304
1255 void SourceBufferStream::SeekAndSetSelectedRange( 1305 void SourceBufferStream::SeekAndSetSelectedRange(
1256 SourceBufferRange* range, DecodeTimestamp seek_timestamp) { 1306 SourceBufferRange* range, DecodeTimestamp seek_timestamp) {
1257 if (range) 1307 if (range)
1258 range->Seek(seek_timestamp); 1308 range->Seek(seek_timestamp);
1259 SetSelectedRange(range); 1309 SetSelectedRange(range);
1260 } 1310 }
1261 1311
1262 void SourceBufferStream::SetSelectedRange(SourceBufferRange* range) { 1312 void SourceBufferStream::SetSelectedRange(SourceBufferRange* range) {
1263 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() 1313 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() << ": "
1264 << ": " << selected_range_ << " -> " << range; 1314 << selected_range_ << " "
1315 << (selected_range_ ? RangeToString(*selected_range_) : "")
1316 << " -> " << range << " " << (range ? RangeToString(*range) : "");
1265 if (selected_range_) 1317 if (selected_range_)
1266 selected_range_->ResetNextBufferPosition(); 1318 selected_range_->ResetNextBufferPosition();
1267 DCHECK(!range || range->HasNextBufferPosition()); 1319 DCHECK(!range || range->HasNextBufferPosition());
1268 selected_range_ = range; 1320 selected_range_ = range;
1269 } 1321 }
1270 1322
1271 Ranges<base::TimeDelta> SourceBufferStream::GetBufferedTime() const { 1323 Ranges<base::TimeDelta> SourceBufferStream::GetBufferedTime() const {
1272 Ranges<base::TimeDelta> ranges; 1324 Ranges<base::TimeDelta> ranges;
1273 for (RangeList::const_iterator itr = ranges_.begin(); 1325 for (RangeList::const_iterator itr = ranges_.begin();
1274 itr != ranges_.end(); ++itr) { 1326 itr != ranges_.end(); ++itr) {
1275 ranges.Add((*itr)->GetStartTimestamp().ToPresentationTime(), 1327 ranges.Add((*itr)->GetStartTimestamp().ToPresentationTime(),
1276 (*itr)->GetBufferedEndTimestamp().ToPresentationTime()); 1328 (*itr)->GetBufferedEndTimestamp().ToPresentationTime());
1277 } 1329 }
1278 return ranges; 1330 return ranges;
1279 } 1331 }
1280 1332
1281 base::TimeDelta SourceBufferStream::GetBufferedDuration() const { 1333 base::TimeDelta SourceBufferStream::GetBufferedDuration() const {
1282 if (ranges_.empty()) 1334 if (ranges_.empty())
1283 return base::TimeDelta(); 1335 return base::TimeDelta();
1284 1336
1285 return ranges_.back()->GetBufferedEndTimestamp().ToPresentationTime(); 1337 return ranges_.back()->GetBufferedEndTimestamp().ToPresentationTime();
1286 } 1338 }
1287 1339
1340 size_t SourceBufferStream::GetBufferedSize() const {
1341 size_t ranges_size = 0;
1342 for (const auto& range : ranges_)
1343 ranges_size += range->size_in_bytes();
1344 return ranges_size;
1345 }
1346
1288 void SourceBufferStream::MarkEndOfStream() { 1347 void SourceBufferStream::MarkEndOfStream() {
1289 DCHECK(!end_of_stream_); 1348 DCHECK(!end_of_stream_);
1290 end_of_stream_ = true; 1349 end_of_stream_ = true;
1291 } 1350 }
1292 1351
1293 void SourceBufferStream::UnmarkEndOfStream() { 1352 void SourceBufferStream::UnmarkEndOfStream() {
1294 DCHECK(end_of_stream_); 1353 DCHECK(end_of_stream_);
1295 end_of_stream_ = false; 1354 end_of_stream_ = false;
1296 } 1355 }
1297 1356
(...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after
1707 return false; 1766 return false;
1708 1767
1709 DCHECK_NE(have_splice_buffers, have_preroll_buffer); 1768 DCHECK_NE(have_splice_buffers, have_preroll_buffer);
1710 splice_buffers_index_ = 0; 1769 splice_buffers_index_ = 0;
1711 pending_buffer_.swap(*out_buffer); 1770 pending_buffer_.swap(*out_buffer);
1712 pending_buffers_complete_ = false; 1771 pending_buffers_complete_ = false;
1713 return true; 1772 return true;
1714 } 1773 }
1715 1774
1716 } // namespace media 1775 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698