Chromium Code Reviews| 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/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 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 385 // are appended to the range covered by |track_buffer_|. | 385 // are appended to the range covered by |track_buffer_|. |
| 386 if (!track_buffer_.empty()) { | 386 if (!track_buffer_.empty()) { |
| 387 DecodeTimestamp keyframe_timestamp = | 387 DecodeTimestamp keyframe_timestamp = |
| 388 FindKeyframeAfterTimestamp(track_buffer_.front()->GetDecodeTimestamp()); | 388 FindKeyframeAfterTimestamp(track_buffer_.front()->GetDecodeTimestamp()); |
| 389 if (keyframe_timestamp != kNoDecodeTimestamp()) | 389 if (keyframe_timestamp != kNoDecodeTimestamp()) |
| 390 PruneTrackBuffer(keyframe_timestamp); | 390 PruneTrackBuffer(keyframe_timestamp); |
| 391 } | 391 } |
| 392 | 392 |
| 393 SetSelectedRangeIfNeeded(next_buffer_timestamp); | 393 SetSelectedRangeIfNeeded(next_buffer_timestamp); |
| 394 | 394 |
| 395 GarbageCollectIfNeeded(); | |
| 396 | |
| 397 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() | 395 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() |
| 398 << ": done. ranges_=" << RangesToString(ranges_); | 396 << ": done. ranges_=" << RangesToString(ranges_); |
| 399 DCHECK(IsRangeListSorted(ranges_)); | 397 DCHECK(IsRangeListSorted(ranges_)); |
| 400 DCHECK(OnlySelectedRangeIsSeeked()); | 398 DCHECK(OnlySelectedRangeIsSeeked()); |
| 401 return true; | 399 return true; |
| 402 } | 400 } |
| 403 | 401 |
| 404 void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end, | 402 void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end, |
| 405 base::TimeDelta duration) { | 403 base::TimeDelta duration) { |
| 406 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() | 404 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 634 } | 632 } |
| 635 } | 633 } |
| 636 | 634 |
| 637 void SourceBufferStream::SetConfigIds(const BufferQueue& buffers) { | 635 void SourceBufferStream::SetConfigIds(const BufferQueue& buffers) { |
| 638 for (BufferQueue::const_iterator itr = buffers.begin(); | 636 for (BufferQueue::const_iterator itr = buffers.begin(); |
| 639 itr != buffers.end(); ++itr) { | 637 itr != buffers.end(); ++itr) { |
| 640 (*itr)->SetConfigId(append_config_index_); | 638 (*itr)->SetConfigId(append_config_index_); |
| 641 } | 639 } |
| 642 } | 640 } |
| 643 | 641 |
| 644 void SourceBufferStream::GarbageCollectIfNeeded() { | 642 bool SourceBufferStream::GarbageCollectIfNeeded(DecodeTimestamp media_time) { |
| 643 DCHECK(media_time != kNoDecodeTimestamp()); | |
| 645 // Compute size of |ranges_|. | 644 // Compute size of |ranges_|. |
| 646 size_t ranges_size = 0; | 645 size_t ranges_size = 0; |
| 647 for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) | 646 for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) |
| 648 ranges_size += (*itr)->size_in_bytes(); | 647 ranges_size += (*itr)->size_in_bytes(); |
| 649 | 648 |
| 650 // Return if we're under or at the memory limit. | 649 // Return if we're under or at the memory limit. |
| 651 if (ranges_size <= memory_limit_) | 650 if (ranges_size <= memory_limit_) |
| 652 return; | 651 return true; |
| 653 | 652 |
| 654 size_t bytes_to_free = ranges_size - memory_limit_; | 653 size_t bytes_to_free = ranges_size - memory_limit_; |
| 655 | 654 |
| 656 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": Before GC" | 655 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": Before GC" |
| 656 << " media_time=" << media_time.InSecondsF() | |
| 657 << " ranges_=" << RangesToString(ranges_) | |
| 657 << " ranges_size=" << ranges_size | 658 << " ranges_size=" << ranges_size |
| 658 << " ranges_=" << RangesToString(ranges_) | 659 << " memory_limit_=" << memory_limit_ |
| 659 << " memory_limit_=" << memory_limit_; | 660 << " last_appended_buffer_timestamp_=" |
| 661 << last_appended_buffer_timestamp_.InSecondsF(); | |
| 660 | 662 |
| 661 // Begin deleting after the last appended buffer. | 663 size_t bytes_freed = 0; |
| 662 size_t bytes_freed = FreeBuffersAfterLastAppended(bytes_to_free); | |
| 663 | 664 |
| 664 // Begin deleting from the front. | 665 // If last appended buffer position was earlier than the current playback time |
| 665 if (bytes_freed < bytes_to_free) | 666 // then try deleting data between last append and current media_time. |
| 666 bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, false); | 667 if (last_appended_buffer_timestamp_ != kNoDecodeTimestamp() && |
| 668 last_appended_buffer_timestamp_ < media_time) { | |
| 669 size_t between = FreeBuffersAfterLastAppended(bytes_to_free, media_time); | |
| 670 DVLOG(3) << __FUNCTION__ << " FreeBuffersAfterLastAppended " | |
| 671 << " released " << between << " bytes" | |
| 672 << " ranges_=" << RangesToString(ranges_); | |
| 673 bytes_freed += between; | |
| 667 | 674 |
| 668 // Begin deleting from the back. | 675 // If the last append happened before the current playback position |
| 669 if (bytes_freed < bytes_to_free) | 676 // |media_time|, then JS player is probably preparing to seek back and we |
| 670 bytes_freed += FreeBuffers(bytes_to_free - bytes_freed, true); | 677 // should try to preserve all most recently appended data (which is in |
| 678 // range_for_next_append_) from being removed by GC (see crbug.com/440173) | |
| 679 if (range_for_next_append_ != ranges_.end()) { | |
| 680 DCHECK((*range_for_next_append_)->GetStartTimestamp() <= media_time); | |
| 681 media_time = (*range_for_next_append_)->GetStartTimestamp(); | |
|
wolenetz
2015/08/12 21:09:57
This may be too lax, though web apps can follow-up
servolk
2015/08/13 00:31:42
Wait, we will only adjust media time here if range
| |
| 682 } | |
| 683 } | |
| 684 | |
| 685 // Try removing data from the front of the SourceBuffer up to |media_time| | |
| 686 // position. | |
| 687 if (bytes_freed < bytes_to_free) { | |
| 688 size_t front = FreeBuffers(bytes_to_free - bytes_freed, media_time, false); | |
| 689 DVLOG(3) << __FUNCTION__ << " Removed " << front << " bytes from the front" | |
| 690 << " ranges_=" << RangesToString(ranges_); | |
| 691 bytes_freed += front; | |
| 692 } | |
| 693 | |
| 694 // Try removing data from the back of the SourceBuffer, until we reach the | |
| 695 // most recent append position. | |
| 696 if (bytes_freed < bytes_to_free) { | |
| 697 size_t back = FreeBuffers(bytes_to_free - bytes_freed, media_time, true); | |
| 698 DVLOG(3) << __FUNCTION__ << " Removed " << back << " bytes from the back" | |
| 699 << " ranges_=" << RangesToString(ranges_); | |
| 700 bytes_freed += back; | |
| 701 } | |
| 671 | 702 |
| 672 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": After GC" | 703 DVLOG(2) << __FUNCTION__ << " " << GetStreamTypeName() << ": After GC" |
| 704 << " bytes_to_free=" << bytes_to_free | |
| 673 << " bytes_freed=" << bytes_freed | 705 << " bytes_freed=" << bytes_freed |
| 674 << " ranges_=" << RangesToString(ranges_); | 706 << " ranges_=" << RangesToString(ranges_); |
| 707 | |
| 708 return bytes_freed >= bytes_to_free; | |
| 675 } | 709 } |
| 676 | 710 |
| 677 size_t SourceBufferStream::FreeBuffersAfterLastAppended( | 711 size_t SourceBufferStream::FreeBuffersAfterLastAppended( |
| 678 size_t total_bytes_to_free) { | 712 size_t total_bytes_to_free, DecodeTimestamp media_time) { |
| 679 DecodeTimestamp next_buffer_timestamp = GetNextBufferTimestamp(); | 713 DVLOG(4) << __FUNCTION__ << " last_appended_buffer_timestamp_=" |
| 680 if (last_appended_buffer_timestamp_ == kNoDecodeTimestamp() || | 714 << last_appended_buffer_timestamp_.InSecondsF() |
| 681 next_buffer_timestamp == kNoDecodeTimestamp() || | 715 << " media_time=" << media_time.InSecondsF(); |
| 682 last_appended_buffer_timestamp_ >= next_buffer_timestamp) { | |
| 683 return 0; | |
| 684 } | |
| 685 | 716 |
| 686 DecodeTimestamp remove_range_start = last_appended_buffer_timestamp_; | 717 DecodeTimestamp remove_range_start = last_appended_buffer_timestamp_; |
| 687 if (last_appended_buffer_is_keyframe_) | 718 if (last_appended_buffer_is_keyframe_) |
| 688 remove_range_start += GetMaxInterbufferDistance(); | 719 remove_range_start += GetMaxInterbufferDistance(); |
| 689 | 720 |
| 690 DecodeTimestamp remove_range_start_keyframe = FindKeyframeAfterTimestamp( | 721 DecodeTimestamp remove_range_start_keyframe = FindKeyframeAfterTimestamp( |
| 691 remove_range_start); | 722 remove_range_start); |
| 692 if (remove_range_start_keyframe != kNoDecodeTimestamp()) | 723 if (remove_range_start_keyframe != kNoDecodeTimestamp()) |
| 693 remove_range_start = remove_range_start_keyframe; | 724 remove_range_start = remove_range_start_keyframe; |
| 694 if (remove_range_start >= next_buffer_timestamp) | 725 if (remove_range_start >= media_time) |
| 695 return 0; | 726 return 0; |
| 696 | 727 |
| 697 DecodeTimestamp remove_range_end; | 728 DecodeTimestamp remove_range_end; |
| 698 size_t bytes_freed = GetRemovalRange(remove_range_start, | 729 size_t bytes_freed = GetRemovalRange(remove_range_start, |
| 699 next_buffer_timestamp, | 730 media_time, |
| 700 total_bytes_to_free, | 731 total_bytes_to_free, |
| 701 &remove_range_end); | 732 &remove_range_end); |
| 702 if (bytes_freed > 0) { | 733 if (bytes_freed > 0) { |
| 734 DVLOG(4) << __FUNCTION__ << " removing [" | |
| 735 << remove_range_start.ToPresentationTime().InSecondsF() << ";" | |
| 736 << remove_range_end.ToPresentationTime().InSecondsF() << "]"; | |
| 703 Remove(remove_range_start.ToPresentationTime(), | 737 Remove(remove_range_start.ToPresentationTime(), |
| 704 remove_range_end.ToPresentationTime(), | 738 remove_range_end.ToPresentationTime(), |
| 705 next_buffer_timestamp.ToPresentationTime()); | 739 media_time.ToPresentationTime()); |
| 706 } | 740 } |
| 707 | 741 |
| 708 return bytes_freed; | 742 return bytes_freed; |
| 709 } | 743 } |
| 710 | 744 |
| 711 size_t SourceBufferStream::GetRemovalRange( | 745 size_t SourceBufferStream::GetRemovalRange( |
| 712 DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp, | 746 DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp, |
| 713 size_t total_bytes_to_free, DecodeTimestamp* removal_end_timestamp) { | 747 size_t total_bytes_to_free, DecodeTimestamp* removal_end_timestamp) { |
| 714 DCHECK(start_timestamp >= DecodeTimestamp()) << start_timestamp.InSecondsF(); | 748 DCHECK(start_timestamp >= DecodeTimestamp()) << start_timestamp.InSecondsF(); |
| 715 DCHECK(start_timestamp < end_timestamp) | 749 DCHECK(start_timestamp < end_timestamp) |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 728 | 762 |
| 729 size_t bytes_to_free = total_bytes_to_free - bytes_freed; | 763 size_t bytes_to_free = total_bytes_to_free - bytes_freed; |
| 730 size_t bytes_removed = range->GetRemovalGOP( | 764 size_t bytes_removed = range->GetRemovalGOP( |
| 731 start_timestamp, end_timestamp, bytes_to_free, removal_end_timestamp); | 765 start_timestamp, end_timestamp, bytes_to_free, removal_end_timestamp); |
| 732 bytes_freed += bytes_removed; | 766 bytes_freed += bytes_removed; |
| 733 } | 767 } |
| 734 return bytes_freed; | 768 return bytes_freed; |
| 735 } | 769 } |
| 736 | 770 |
| 737 size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free, | 771 size_t SourceBufferStream::FreeBuffers(size_t total_bytes_to_free, |
| 772 DecodeTimestamp media_time, | |
| 738 bool reverse_direction) { | 773 bool reverse_direction) { |
| 739 TRACE_EVENT2("media", "SourceBufferStream::FreeBuffers", | 774 TRACE_EVENT2("media", "SourceBufferStream::FreeBuffers", |
| 740 "total bytes to free", total_bytes_to_free, | 775 "total bytes to free", total_bytes_to_free, |
| 741 "reverse direction", reverse_direction); | 776 "reverse direction", reverse_direction); |
| 742 | 777 |
| 743 DCHECK_GT(total_bytes_to_free, 0u); | 778 DCHECK_GT(total_bytes_to_free, 0u); |
| 744 size_t bytes_freed = 0; | 779 size_t bytes_freed = 0; |
| 745 | 780 |
| 746 // This range will save the last GOP appended to |range_for_next_append_| | 781 // This range will save the last GOP appended to |range_for_next_append_| |
| 747 // if the buffers surrounding it get deleted during garbage collection. | 782 // if the buffers surrounding it get deleted during garbage collection. |
| 748 SourceBufferRange* new_range_for_append = NULL; | 783 SourceBufferRange* new_range_for_append = NULL; |
| 749 | 784 |
| 750 while (!ranges_.empty() && bytes_freed < total_bytes_to_free) { | 785 while (!ranges_.empty() && bytes_freed < total_bytes_to_free) { |
| 751 SourceBufferRange* current_range = NULL; | 786 SourceBufferRange* current_range = NULL; |
| 752 BufferQueue buffers; | 787 BufferQueue buffers; |
| 753 size_t bytes_deleted = 0; | 788 size_t bytes_deleted = 0; |
| 754 | 789 |
| 755 if (reverse_direction) { | 790 if (reverse_direction) { |
| 756 current_range = ranges_.back(); | 791 current_range = ranges_.back(); |
| 792 DVLOG(5) << "current_range=" << RangeToString(*current_range); | |
| 757 if (current_range->LastGOPContainsNextBufferPosition()) { | 793 if (current_range->LastGOPContainsNextBufferPosition()) { |
| 758 DCHECK_EQ(current_range, selected_range_); | 794 DCHECK_EQ(current_range, selected_range_); |
| 795 DVLOG(5) << "current_range contains next read position, stopping GC"; | |
| 759 break; | 796 break; |
| 760 } | 797 } |
| 798 DVLOG(5) << "Deleting GOP from back: " << RangeToString(*current_range); | |
| 761 bytes_deleted = current_range->DeleteGOPFromBack(&buffers); | 799 bytes_deleted = current_range->DeleteGOPFromBack(&buffers); |
| 762 } else { | 800 } else { |
| 763 current_range = ranges_.front(); | 801 current_range = ranges_.front(); |
| 764 if (current_range->FirstGOPContainsNextBufferPosition()) { | 802 DVLOG(5) << "current_range=" << RangeToString(*current_range); |
| 765 DCHECK_EQ(current_range, selected_range_); | 803 if (!current_range->FirstGOPEarlierThanMediaTime(media_time)) { |
| 804 // We have removed all data up to the GOP that contains current playback | |
| 805 // position, we can't delete any further. | |
| 806 DVLOG(5) << "current_range contains playback position, stopping GC"; | |
| 766 break; | 807 break; |
| 767 } | 808 } |
| 809 DVLOG(4) << "Deleting GOP from front: " << RangeToString(*current_range); | |
| 768 bytes_deleted = current_range->DeleteGOPFromFront(&buffers); | 810 bytes_deleted = current_range->DeleteGOPFromFront(&buffers); |
| 769 } | 811 } |
| 770 | 812 |
| 771 // Check to see if we've just deleted the GOP that was last appended. | 813 // Check to see if we've just deleted the GOP that was last appended. |
| 772 DecodeTimestamp end_timestamp = buffers.back()->GetDecodeTimestamp(); | 814 DecodeTimestamp end_timestamp = buffers.back()->GetDecodeTimestamp(); |
| 773 if (end_timestamp == last_appended_buffer_timestamp_) { | 815 if (end_timestamp == last_appended_buffer_timestamp_) { |
| 774 DCHECK(last_appended_buffer_timestamp_ != kNoDecodeTimestamp()); | 816 DCHECK(last_appended_buffer_timestamp_ != kNoDecodeTimestamp()); |
| 775 DCHECK(!new_range_for_append); | 817 DCHECK(!new_range_for_append); |
| 818 | |
| 776 // Create a new range containing these buffers. | 819 // Create a new range containing these buffers. |
| 777 new_range_for_append = new SourceBufferRange( | 820 new_range_for_append = new SourceBufferRange( |
| 778 TypeToGapPolicy(GetType()), | 821 TypeToGapPolicy(GetType()), |
| 779 buffers, kNoDecodeTimestamp(), | 822 buffers, kNoDecodeTimestamp(), |
| 780 base::Bind(&SourceBufferStream::GetMaxInterbufferDistance, | 823 base::Bind(&SourceBufferStream::GetMaxInterbufferDistance, |
| 781 base::Unretained(this))); | 824 base::Unretained(this))); |
| 782 range_for_next_append_ = ranges_.end(); | 825 range_for_next_append_ = ranges_.end(); |
| 783 } else { | 826 } else { |
| 784 bytes_freed += bytes_deleted; | 827 bytes_freed += bytes_deleted; |
| 785 } | 828 } |
| 786 | 829 |
| 787 if (current_range->size_in_bytes() == 0) { | 830 if (current_range->size_in_bytes() == 0) { |
| 788 DCHECK_NE(current_range, selected_range_); | 831 DCHECK_NE(current_range, selected_range_); |
| 789 DCHECK(range_for_next_append_ == ranges_.end() || | 832 DCHECK(range_for_next_append_ == ranges_.end() || |
| 790 *range_for_next_append_ != current_range); | 833 *range_for_next_append_ != current_range); |
| 791 delete current_range; | 834 delete current_range; |
| 792 reverse_direction ? ranges_.pop_back() : ranges_.pop_front(); | 835 reverse_direction ? ranges_.pop_back() : ranges_.pop_front(); |
| 793 } | 836 } |
| 837 | |
| 838 if (reverse_direction && new_range_for_append) { | |
| 839 // We don't want to delete any further, or we'll be creating gaps | |
| 840 break; | |
| 841 } | |
| 794 } | 842 } |
| 795 | 843 |
| 796 // Insert |new_range_for_append| into |ranges_|, if applicable. | 844 // Insert |new_range_for_append| into |ranges_|, if applicable. |
| 797 if (new_range_for_append) { | 845 if (new_range_for_append) { |
| 798 range_for_next_append_ = AddToRanges(new_range_for_append); | 846 range_for_next_append_ = AddToRanges(new_range_for_append); |
| 799 DCHECK(range_for_next_append_ != ranges_.end()); | 847 DCHECK(range_for_next_append_ != ranges_.end()); |
| 800 | 848 |
| 801 // Check to see if we need to merge |new_range_for_append| with the range | 849 // Check to see if we need to merge |new_range_for_append| with the range |
| 802 // before or after it. |new_range_for_append| is created whenever the last | 850 // before or after it. |new_range_for_append| is created whenever the last |
| 803 // GOP appended is encountered, regardless of whether any buffers after it | 851 // GOP appended is encountered, regardless of whether any buffers after it |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1213 } | 1261 } |
| 1214 | 1262 |
| 1215 void SourceBufferStream::SeekAndSetSelectedRange( | 1263 void SourceBufferStream::SeekAndSetSelectedRange( |
| 1216 SourceBufferRange* range, DecodeTimestamp seek_timestamp) { | 1264 SourceBufferRange* range, DecodeTimestamp seek_timestamp) { |
| 1217 if (range) | 1265 if (range) |
| 1218 range->Seek(seek_timestamp); | 1266 range->Seek(seek_timestamp); |
| 1219 SetSelectedRange(range); | 1267 SetSelectedRange(range); |
| 1220 } | 1268 } |
| 1221 | 1269 |
| 1222 void SourceBufferStream::SetSelectedRange(SourceBufferRange* range) { | 1270 void SourceBufferStream::SetSelectedRange(SourceBufferRange* range) { |
| 1223 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() | 1271 DVLOG(1) << __FUNCTION__ << " " << GetStreamTypeName() << ": " |
| 1224 << ": " << selected_range_ << " -> " << range; | 1272 << selected_range_ << " " |
| 1273 << (selected_range_ ? RangeToString(*selected_range_) : "") | |
| 1274 << " -> " << range << " " << (range ? RangeToString(*range) : ""); | |
| 1225 if (selected_range_) | 1275 if (selected_range_) |
| 1226 selected_range_->ResetNextBufferPosition(); | 1276 selected_range_->ResetNextBufferPosition(); |
| 1227 DCHECK(!range || range->HasNextBufferPosition()); | 1277 DCHECK(!range || range->HasNextBufferPosition()); |
| 1228 selected_range_ = range; | 1278 selected_range_ = range; |
| 1229 } | 1279 } |
| 1230 | 1280 |
| 1231 Ranges<base::TimeDelta> SourceBufferStream::GetBufferedTime() const { | 1281 Ranges<base::TimeDelta> SourceBufferStream::GetBufferedTime() const { |
| 1232 Ranges<base::TimeDelta> ranges; | 1282 Ranges<base::TimeDelta> ranges; |
| 1233 for (RangeList::const_iterator itr = ranges_.begin(); | 1283 for (RangeList::const_iterator itr = ranges_.begin(); |
| 1234 itr != ranges_.end(); ++itr) { | 1284 itr != ranges_.end(); ++itr) { |
| (...skipping 432 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1667 return false; | 1717 return false; |
| 1668 | 1718 |
| 1669 DCHECK_NE(have_splice_buffers, have_preroll_buffer); | 1719 DCHECK_NE(have_splice_buffers, have_preroll_buffer); |
| 1670 splice_buffers_index_ = 0; | 1720 splice_buffers_index_ = 0; |
| 1671 pending_buffer_.swap(*out_buffer); | 1721 pending_buffer_.swap(*out_buffer); |
| 1672 pending_buffers_complete_ = false; | 1722 pending_buffers_complete_ = false; |
| 1673 return true; | 1723 return true; |
| 1674 } | 1724 } |
| 1675 | 1725 |
| 1676 } // namespace media | 1726 } // namespace media |
| OLD | NEW |