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

Side by Side Diff: content/renderer/media/rtc_video_decoder.cc

Issue 1236663003: RTCVideoDecoder: Fixes for high resolutions and high pressure cases. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 5 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
« no previous file with comments | « content/renderer/media/rtc_video_decoder.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "content/renderer/media/rtc_video_decoder.h" 5 #include "content/renderer/media/rtc_video_decoder.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/memory/ref_counted.h" 9 #include "base/memory/ref_counted.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
(...skipping 14 matching lines...) Expand all
25 25
26 const int32 RTCVideoDecoder::ID_LAST = 0x3FFFFFFF; 26 const int32 RTCVideoDecoder::ID_LAST = 0x3FFFFFFF;
27 const int32 RTCVideoDecoder::ID_HALF = 0x20000000; 27 const int32 RTCVideoDecoder::ID_HALF = 0x20000000;
28 const int32 RTCVideoDecoder::ID_INVALID = -1; 28 const int32 RTCVideoDecoder::ID_INVALID = -1;
29 29
30 // Maximum number of concurrent VDA::Decode() operations RVD will maintain. 30 // Maximum number of concurrent VDA::Decode() operations RVD will maintain.
31 // Higher values allow better pipelining in the GPU, but also require more 31 // Higher values allow better pipelining in the GPU, but also require more
32 // resources. 32 // resources.
33 static const size_t kMaxInFlightDecodes = 8; 33 static const size_t kMaxInFlightDecodes = 8;
34 34
35 // Size of shared-memory segments we allocate. Since we reuse them we let them 35 // Number of allocated shared memory segments.
36 // be on the beefy side. 36 static const size_t kNumSharedMemorySegments = 16;
37 static const size_t kSharedMemorySegmentBytes = 100 << 10;
38 37
39 // Maximum number of allocated shared-memory segments. 38 // Maximum number of pending WebRTC buffers that are waiting for shared memory.
40 static const int kMaxNumSharedMemorySegments = 16; 39 static const size_t kMaxNumOfPendingBuffers = 8;
41
42 // Maximum number of pending WebRTC buffers that are waiting for the shared
43 // memory. 10 seconds for 30 fps.
44 static const size_t kMaxNumOfPendingBuffers = 300;
45 40
46 // A shared memory segment and its allocated size. This class has the ownership 41 // A shared memory segment and its allocated size. This class has the ownership
47 // of |shm|. 42 // of |shm|.
48 class RTCVideoDecoder::SHMBuffer { 43 class RTCVideoDecoder::SHMBuffer {
49 public: 44 public:
50 SHMBuffer(scoped_ptr<base::SharedMemory> shm, size_t size); 45 SHMBuffer(scoped_ptr<base::SharedMemory> shm, size_t size);
51 ~SHMBuffer(); 46 ~SHMBuffer();
52 scoped_ptr<base::SharedMemory> const shm; 47 scoped_ptr<base::SharedMemory> const shm;
53 const size_t size; 48 const size_t size;
54 }; 49 };
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 DVLOG(2) << "~RTCVideoDecoder"; 87 DVLOG(2) << "~RTCVideoDecoder";
93 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); 88 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
94 DestroyVDA(); 89 DestroyVDA();
95 90
96 // Delete all shared memories. 91 // Delete all shared memories.
97 STLDeleteElements(&available_shm_segments_); 92 STLDeleteElements(&available_shm_segments_);
98 STLDeleteValues(&bitstream_buffers_in_decoder_); 93 STLDeleteValues(&bitstream_buffers_in_decoder_);
99 STLDeleteContainerPairFirstPointers(decode_buffers_.begin(), 94 STLDeleteContainerPairFirstPointers(decode_buffers_.begin(),
100 decode_buffers_.end()); 95 decode_buffers_.end());
101 decode_buffers_.clear(); 96 decode_buffers_.clear();
102 97 ClearPendingBuffers();
103 // Delete WebRTC input buffers.
104 for (std::deque<std::pair<webrtc::EncodedImage, BufferData> >::iterator it =
105 pending_buffers_.begin();
106 it != pending_buffers_.end();
107 ++it) {
108 delete[] it->first._buffer;
109 }
110 } 98 }
111 99
112 // static 100 // static
113 scoped_ptr<RTCVideoDecoder> RTCVideoDecoder::Create( 101 scoped_ptr<RTCVideoDecoder> RTCVideoDecoder::Create(
114 webrtc::VideoCodecType type, 102 webrtc::VideoCodecType type,
115 const scoped_refptr<media::GpuVideoAcceleratorFactories>& factories) { 103 const scoped_refptr<media::GpuVideoAcceleratorFactories>& factories) {
116 scoped_ptr<RTCVideoDecoder> decoder; 104 scoped_ptr<RTCVideoDecoder> decoder;
117 // Convert WebRTC codec type to media codec profile. 105 // Convert WebRTC codec type to media codec profile.
118 media::VideoCodecProfile profile; 106 media::VideoCodecProfile profile;
119 switch (type) { 107 switch (type) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 codecSettings->codecSpecific.VP8.feedbackModeOn) { 142 codecSettings->codecSpecific.VP8.feedbackModeOn) {
155 LOG(ERROR) << "Feedback mode not supported"; 143 LOG(ERROR) << "Feedback mode not supported";
156 return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_ERROR); 144 return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_ERROR);
157 } 145 }
158 146
159 base::AutoLock auto_lock(lock_); 147 base::AutoLock auto_lock(lock_);
160 if (state_ == UNINITIALIZED || state_ == DECODE_ERROR) { 148 if (state_ == UNINITIALIZED || state_ == DECODE_ERROR) {
161 LOG(ERROR) << "VDA is not initialized. state=" << state_; 149 LOG(ERROR) << "VDA is not initialized. state=" << state_;
162 return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_UNINITIALIZED); 150 return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_UNINITIALIZED);
163 } 151 }
164 // Create some shared memory if the queue is empty. 152
165 if (available_shm_segments_.size() == 0) {
166 factories_->GetTaskRunner()->PostTask(
167 FROM_HERE,
168 base::Bind(&RTCVideoDecoder::CreateSHM,
169 weak_factory_.GetWeakPtr(),
170 kMaxInFlightDecodes,
171 kSharedMemorySegmentBytes));
172 }
173 return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_OK); 153 return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_OK);
174 } 154 }
175 155
176 int32_t RTCVideoDecoder::Decode( 156 int32_t RTCVideoDecoder::Decode(
177 const webrtc::EncodedImage& inputImage, 157 const webrtc::EncodedImage& inputImage,
178 bool missingFrames, 158 bool missingFrames,
179 const webrtc::RTPFragmentationHeader* /*fragmentation*/, 159 const webrtc::RTPFragmentationHeader* /*fragmentation*/,
180 const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/, 160 const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/,
181 int64_t /*renderTimeMs*/) { 161 int64_t /*renderTimeMs*/) {
182 DVLOG(3) << "Decode"; 162 DVLOG(3) << "Decode";
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 BufferData buffer_data(next_bitstream_buffer_id_, 222 BufferData buffer_data(next_bitstream_buffer_id_,
243 inputImage._timeStamp, 223 inputImage._timeStamp,
244 inputImage._length); 224 inputImage._length);
245 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. 225 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
246 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & ID_LAST; 226 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & ID_LAST;
247 227
248 // If a shared memory segment is available, there are no pending buffers, and 228 // If a shared memory segment is available, there are no pending buffers, and
249 // this isn't a mid-stream resolution change, then send the buffer for decode 229 // this isn't a mid-stream resolution change, then send the buffer for decode
250 // immediately. Otherwise, save the buffer in the queue for later decode. 230 // immediately. Otherwise, save the buffer in the queue for later decode.
251 scoped_ptr<SHMBuffer> shm_buffer; 231 scoped_ptr<SHMBuffer> shm_buffer;
252 if (!need_to_reset_for_midstream_resize && pending_buffers_.size() == 0) 232 if (!need_to_reset_for_midstream_resize && pending_buffers_.empty())
253 shm_buffer = GetSHM_Locked(inputImage._length); 233 shm_buffer = GetSHM_Locked(inputImage._length);
254 if (!shm_buffer) { 234 if (!shm_buffer) {
255 if (!SaveToPendingBuffers_Locked(inputImage, buffer_data)) 235 if (!SaveToPendingBuffers_Locked(inputImage, buffer_data)) {
236 // We have exceeded the pending buffers count, we are severely behind.
237 // Since we are returning ERROR, WebRTC will not be interested in the
238 // remaining buffers, and will provide us with a new keyframe instead.
239 // Better to drop any pending buffers and start afresh to catch up faster.
240 DVLOG(1) << "Exceeded maximum pending buffer count, dropping";
241 ClearPendingBuffers();
256 return WEBRTC_VIDEO_CODEC_ERROR; 242 return WEBRTC_VIDEO_CODEC_ERROR;
243 }
244
257 if (need_to_reset_for_midstream_resize) { 245 if (need_to_reset_for_midstream_resize) {
258 base::AutoUnlock auto_unlock(lock_); 246 base::AutoUnlock auto_unlock(lock_);
259 Reset(); 247 Reset();
260 } 248 }
249
261 return WEBRTC_VIDEO_CODEC_OK; 250 return WEBRTC_VIDEO_CODEC_OK;
262 } 251 }
263 252
264 SaveToDecodeBuffers_Locked(inputImage, shm_buffer.Pass(), buffer_data); 253 SaveToDecodeBuffers_Locked(inputImage, shm_buffer.Pass(), buffer_data);
265 factories_->GetTaskRunner()->PostTask( 254 factories_->GetTaskRunner()->PostTask(
266 FROM_HERE, 255 FROM_HERE,
267 base::Bind(&RTCVideoDecoder::RequestBufferDecode, 256 base::Bind(&RTCVideoDecoder::RequestBufferDecode,
268 weak_factory_.GetWeakPtr())); 257 weak_factory_.GetWeakPtr()));
269 return WEBRTC_VIDEO_CODEC_OK; 258 return WEBRTC_VIDEO_CODEC_OK;
270 } 259 }
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after
504 493
505 MovePendingBuffersToDecodeBuffers(); 494 MovePendingBuffersToDecodeBuffers();
506 495
507 while (CanMoreDecodeWorkBeDone()) { 496 while (CanMoreDecodeWorkBeDone()) {
508 // Get a buffer and data from the queue. 497 // Get a buffer and data from the queue.
509 SHMBuffer* shm_buffer = NULL; 498 SHMBuffer* shm_buffer = NULL;
510 BufferData buffer_data; 499 BufferData buffer_data;
511 { 500 {
512 base::AutoLock auto_lock(lock_); 501 base::AutoLock auto_lock(lock_);
513 // Do not request decode if VDA is resetting. 502 // Do not request decode if VDA is resetting.
514 if (decode_buffers_.size() == 0 || state_ == RESETTING) 503 if (decode_buffers_.empty() || state_ == RESETTING)
515 return; 504 return;
516 shm_buffer = decode_buffers_.front().first; 505 shm_buffer = decode_buffers_.front().first;
517 buffer_data = decode_buffers_.front().second; 506 buffer_data = decode_buffers_.front().second;
518 decode_buffers_.pop_front(); 507 decode_buffers_.pop_front();
519 // Drop the buffers before Reset or Release is called. 508 // Drop the buffers before Reset or Release is called.
520 if (!IsBufferAfterReset(buffer_data.bitstream_buffer_id, 509 if (!IsBufferAfterReset(buffer_data.bitstream_buffer_id,
521 reset_bitstream_buffer_id_)) { 510 reset_bitstream_buffer_id_)) {
522 PutSHM_Locked(scoped_ptr<SHMBuffer>(shm_buffer)); 511 PutSHM_Locked(scoped_ptr<SHMBuffer>(shm_buffer));
523 continue; 512 continue;
524 } 513 }
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
725 if (vda_) 714 if (vda_)
726 vda_.release()->Destroy(); 715 vda_.release()->Destroy();
727 DestroyTextures(); 716 DestroyTextures();
728 base::AutoLock auto_lock(lock_); 717 base::AutoLock auto_lock(lock_);
729 state_ = UNINITIALIZED; 718 state_ = UNINITIALIZED;
730 } 719 }
731 720
732 scoped_ptr<RTCVideoDecoder::SHMBuffer> RTCVideoDecoder::GetSHM_Locked( 721 scoped_ptr<RTCVideoDecoder::SHMBuffer> RTCVideoDecoder::GetSHM_Locked(
733 size_t min_size) { 722 size_t min_size) {
734 // Reuse a SHM if possible. 723 // Reuse a SHM if possible.
735 SHMBuffer* ret = NULL;
736 if (!available_shm_segments_.empty() && 724 if (!available_shm_segments_.empty() &&
737 available_shm_segments_.back()->size >= min_size) { 725 available_shm_segments_.back()->size >= min_size) {
738 ret = available_shm_segments_.back(); 726 scoped_ptr<SHMBuffer> buffer(available_shm_segments_.back());
739 available_shm_segments_.pop_back(); 727 available_shm_segments_.pop_back();
728 return buffer;
740 } 729 }
741 // Post to vda thread to create shared memory if SHM cannot be reused or the 730
742 // queue is almost empty. 731 if (available_shm_segments_.size() != num_shm_buffers_) {
743 if (num_shm_buffers_ < kMaxNumSharedMemorySegments && 732 // Either available_shm_segments_ is empty (and we already have some SHM
744 (ret == NULL || available_shm_segments_.size() <= 1)) { 733 // buffers allocated), or the size of available segments is not large
745 factories_->GetTaskRunner()->PostTask( 734 // enough. In the former case we need to wait for buffers to be returned,
746 FROM_HERE, 735 // in the latter we need to wait for all buffers to be returned to drop
747 base::Bind(&RTCVideoDecoder::CreateSHM, 736 // them and reallocate with a new size.
748 weak_factory_.GetWeakPtr(), 737 return NULL;
749 1,
750 min_size));
751 } 738 }
752 return scoped_ptr<SHMBuffer>(ret); 739
740 if (num_shm_buffers_ != 0) {
741 STLDeleteElements(&available_shm_segments_);
742 num_shm_buffers_ = 0;
743 }
744
745 // Create twice as large buffers as required, to avoid frequent reallocation.
746 factories_->GetTaskRunner()->PostTask(
747 FROM_HERE,
748 base::Bind(&RTCVideoDecoder::CreateSHM, weak_factory_.GetWeakPtr(),
749 kNumSharedMemorySegments, min_size * 2));
750
751 // We'll be called again after the shared memory is created.
752 return NULL;
753 } 753 }
754 754
755 void RTCVideoDecoder::PutSHM_Locked(scoped_ptr<SHMBuffer> shm_buffer) { 755 void RTCVideoDecoder::PutSHM_Locked(scoped_ptr<SHMBuffer> shm_buffer) {
756 available_shm_segments_.push_back(shm_buffer.release()); 756 available_shm_segments_.push_back(shm_buffer.release());
757 } 757 }
758 758
759 void RTCVideoDecoder::CreateSHM(int number, size_t min_size) { 759 void RTCVideoDecoder::CreateSHM(size_t count, size_t size) {
760 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); 760 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent();
761 DVLOG(2) << "CreateSHM. size=" << min_size; 761 DVLOG(2) << "CreateSHM. count=" << count << ", size=" << size;
762 int number_to_allocate; 762
763 { 763 for (size_t i = 0; i < count; i++) {
764 scoped_ptr<base::SharedMemory> shm = factories_->CreateSharedMemory(size);
Ville-Mikko Rautio 2015/07/17 11:26:15 Earlier it was quaranteed that each buffer would b
Pawel Osciak 2015/07/21 07:10:07 Yes, that was intentional. We may have to realloca
765 if (!shm) {
766 LOG(ERROR) << "Failed allocating shared memory of size=" << size;
767 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
768 return;
769 }
770
764 base::AutoLock auto_lock(lock_); 771 base::AutoLock auto_lock(lock_);
765 number_to_allocate = 772 PutSHM_Locked(scoped_ptr<SHMBuffer>(new SHMBuffer(shm.Pass(), size)));
766 std::min(kMaxNumSharedMemorySegments - num_shm_buffers_, number); 773 ++num_shm_buffers_;
767 } 774 }
768 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); 775
769 for (int i = 0; i < number_to_allocate; i++) {
770 scoped_ptr<base::SharedMemory> shm =
771 factories_->CreateSharedMemory(size_to_allocate);
772 if (shm) {
773 base::AutoLock auto_lock(lock_);
774 num_shm_buffers_++;
775 PutSHM_Locked(
776 scoped_ptr<SHMBuffer>(new SHMBuffer(shm.Pass(), size_to_allocate)));
777 }
778 }
779 // Kick off the decoding. 776 // Kick off the decoding.
780 RequestBufferDecode(); 777 RequestBufferDecode();
781 } 778 }
782 779
783 void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) { 780 void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) {
784 input_buffer_data_.push_front(buffer_data); 781 input_buffer_data_.push_front(buffer_data);
785 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but 782 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but
786 // that's too small for some pathological B-frame test videos. The cost of 783 // that's too small for some pathological B-frame test videos. The cost of
787 // using too-high a value is low (192 bits per extra slot). 784 // using too-high a value is low (192 bits per extra slot).
788 static const size_t kMaxInputBufferDataSize = 128; 785 static const size_t kMaxInputBufferDataSize = 128;
(...skipping 22 matching lines...) Expand all
811 bool sample = (status == WEBRTC_VIDEO_CODEC_OK) ? true : false; 808 bool sample = (status == WEBRTC_VIDEO_CODEC_OK) ? true : false;
812 UMA_HISTOGRAM_BOOLEAN("Media.RTCVideoDecoderInitDecodeSuccess", sample); 809 UMA_HISTOGRAM_BOOLEAN("Media.RTCVideoDecoderInitDecodeSuccess", sample);
813 return status; 810 return status;
814 } 811 }
815 812
816 void RTCVideoDecoder::DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent() 813 void RTCVideoDecoder::DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent()
817 const { 814 const {
818 DCHECK(factories_->GetTaskRunner()->BelongsToCurrentThread()); 815 DCHECK(factories_->GetTaskRunner()->BelongsToCurrentThread());
819 } 816 }
820 817
818 void RTCVideoDecoder::ClearPendingBuffers() {
819 // Delete WebRTC input buffers.
820 for (std::deque<std::pair<webrtc::EncodedImage, BufferData>>::iterator it =
821 pending_buffers_.begin();
822 it != pending_buffers_.end(); ++it) {
823 delete[] it->first._buffer;
824 }
825
826 pending_buffers_.clear();
827 }
828
821 } // namespace content 829 } // namespace content
OLDNEW
« no previous file with comments | « content/renderer/media/rtc_video_decoder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698