Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |