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/message_loop/message_loop_proxy.h" | 10 #include "base/message_loop/message_loop_proxy.h" |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 61 timestamp(timestamp), | 61 timestamp(timestamp), |
| 62 width(width), | 62 width(width), |
| 63 height(height), | 63 height(height), |
| 64 size(size) {} | 64 size(size) {} |
| 65 | 65 |
| 66 RTCVideoDecoder::BufferData::BufferData() {} | 66 RTCVideoDecoder::BufferData::BufferData() {} |
| 67 | 67 |
| 68 RTCVideoDecoder::BufferData::~BufferData() {} | 68 RTCVideoDecoder::BufferData::~BufferData() {} |
| 69 | 69 |
| 70 RTCVideoDecoder::RTCVideoDecoder( | 70 RTCVideoDecoder::RTCVideoDecoder( |
| 71 const scoped_refptr<media::GpuVideoDecoder::Factories>& factories) | 71 const scoped_refptr<media::GpuVideoDecoder::Factories>& factories) |
|
scherkus (not reviewing)
2013/07/18 00:54:43
if GVD::Factories is being used outside of GVD, it
wuchengli
2013/07/18 09:42:00
Done.
wuchengli
2013/07/18 15:40:05
I'll upload a CL tomorrow to refactor GpuVideoDeco
wuchengli
2013/07/19 07:47:13
The CL to move GpuVideoDecoder::Factories to a spp
| |
| 72 : weak_factory_(this), | 72 : weak_factory_(this), |
| 73 weak_this_(weak_factory_.GetWeakPtr()), | 73 weak_this_(weak_factory_.GetWeakPtr()), |
| 74 factories_(factories), | 74 factories_(factories), |
| 75 vda_loop_proxy_(factories_->GetMessageLoop()), | 75 vda_loop_proxy_(factories_->GetMessageLoop()), |
| 76 create_shm_thread_("CreateSHMThread"), | |
| 77 decoder_texture_target_(0), | 76 decoder_texture_target_(0), |
| 78 next_picture_buffer_id_(0), | 77 next_picture_buffer_id_(0), |
| 79 state_(UNINITIALIZED), | 78 state_(UNINITIALIZED), |
| 80 decode_complete_callback_(NULL), | 79 decode_complete_callback_(NULL), |
| 81 num_shm_buffers_(0), | 80 num_shm_buffers_(0), |
| 82 next_bitstream_buffer_id_(0), | 81 next_bitstream_buffer_id_(0), |
| 83 reset_bitstream_buffer_id_(ID_INVALID) { | 82 reset_bitstream_buffer_id_(ID_INVALID) { |
| 84 create_shm_thread_.Start(); | 83 DCHECK(!vda_loop_proxy_->BelongsToCurrentThread()); |
| 85 // Initialize directly if |vda_loop_proxy_| is the renderer thread. | 84 base::WaitableEvent message_loop_async_waiter(false, false); |
| 86 base::WaitableEvent compositor_loop_async_waiter(false, false); | 85 // Waiting here is safe because RTCVideoDecoderFactory owns the thread. |
| 87 if (vda_loop_proxy_->BelongsToCurrentThread()) { | |
| 88 Initialize(&compositor_loop_async_waiter); | |
| 89 return; | |
| 90 } | |
| 91 // Post the task if |vda_loop_proxy_| is the compositor thread. Waiting here | |
| 92 // is safe because the compositor thread will not be stopped until the | |
| 93 // renderer thread shuts down. | |
| 94 vda_loop_proxy_->PostTask(FROM_HERE, | 86 vda_loop_proxy_->PostTask(FROM_HERE, |
| 95 base::Bind(&RTCVideoDecoder::Initialize, | 87 base::Bind(&RTCVideoDecoder::Initialize, |
| 96 base::Unretained(this), | 88 base::Unretained(this), |
| 97 &compositor_loop_async_waiter)); | 89 &message_loop_async_waiter)); |
| 98 compositor_loop_async_waiter.Wait(); | 90 message_loop_async_waiter.Wait(); |
| 99 } | 91 } |
| 100 | 92 |
| 101 RTCVideoDecoder::~RTCVideoDecoder() { | 93 RTCVideoDecoder::~RTCVideoDecoder() { |
| 102 DVLOG(2) << "~RTCVideoDecoder"; | 94 DVLOG(2) << "~RTCVideoDecoder"; |
| 103 factories_->Abort(); | 95 // Remove |this| from the observer if vda thread is alive. |
| 104 create_shm_thread_.Stop(); | 96 if (vda_loop_proxy_->BelongsToCurrentThread()) |
| 105 // Delete vda and remove |this| from the observer if vda thread is alive. | |
| 106 if (vda_loop_proxy_->BelongsToCurrentThread()) { | |
| 107 base::MessageLoop::current()->RemoveDestructionObserver(this); | 97 base::MessageLoop::current()->RemoveDestructionObserver(this); |
| 108 DestroyVDA(); | 98 // VDA should have been destroyed. |
| 109 } else { | 99 DCHECK(!vda_); |
| 110 // VDA should have been destroyed in WillDestroyCurrentMessageLoop. | |
| 111 DCHECK(!vda_); | |
| 112 } | |
| 113 | 100 |
| 114 // Delete all shared memories. | 101 // Delete all shared memories. |
| 115 STLDeleteElements(&available_shm_segments_); | 102 STLDeleteElements(&available_shm_segments_); |
| 116 STLDeleteValues(&bitstream_buffers_in_decoder_); | 103 STLDeleteValues(&bitstream_buffers_in_decoder_); |
| 117 STLDeleteContainerPairFirstPointers(decode_buffers_.begin(), | 104 STLDeleteContainerPairFirstPointers(decode_buffers_.begin(), |
| 118 decode_buffers_.end()); | 105 decode_buffers_.end()); |
| 119 decode_buffers_.clear(); | 106 decode_buffers_.clear(); |
| 120 | 107 |
| 121 // Delete WebRTC input buffers. | 108 // Delete WebRTC input buffers. |
| 122 for (std::deque<std::pair<webrtc::EncodedImage, BufferData> >::iterator it = | 109 for (std::deque<std::pair<webrtc::EncodedImage, BufferData> >::iterator it = |
| 123 pending_buffers_.begin(); | 110 pending_buffers_.begin(); |
| 124 it != pending_buffers_.end(); | 111 it != pending_buffers_.end(); |
| 125 ++it) { | 112 ++it) { |
| 126 delete[] it->first._buffer; | 113 delete[] it->first._buffer; |
| 127 } | 114 } |
| 128 } | 115 } |
| 129 | 116 |
| 130 scoped_ptr<RTCVideoDecoder> RTCVideoDecoder::Create( | 117 scoped_ptr<RTCVideoDecoder> RTCVideoDecoder::Create( |
| 118 webrtc::VideoCodecType type, | |
| 131 const scoped_refptr<media::GpuVideoDecoder::Factories>& factories) { | 119 const scoped_refptr<media::GpuVideoDecoder::Factories>& factories) { |
| 132 scoped_ptr<RTCVideoDecoder> decoder(new RTCVideoDecoder(factories)); | 120 scoped_ptr<RTCVideoDecoder> decoder; |
| 133 decoder->vda_.reset(factories->CreateVideoDecodeAccelerator( | 121 // Convert WebRTC codec type to media codec profile. |
| 134 media::VP8PROFILE_MAIN, decoder.get())); | 122 media::VideoCodecProfile profile; |
| 123 switch (type) { | |
| 124 case webrtc::kVideoCodecVP8: | |
| 125 profile = media::VP8PROFILE_MAIN; | |
| 126 break; | |
| 127 default: | |
| 128 DVLOG(2) << "Video codec not supported:" << type; | |
| 129 return decoder.Pass(); | |
| 130 } | |
| 131 | |
| 132 decoder.reset(new RTCVideoDecoder(factories)); | |
| 133 decoder->vda_ | |
| 134 .reset(factories->CreateVideoDecodeAccelerator(profile, decoder.get())); | |
| 135 // vda can be NULL if VP8 is not supported. | 135 // vda can be NULL if VP8 is not supported. |
| 136 if (decoder->vda_ != NULL) { | 136 if (decoder->vda_ != NULL) { |
| 137 decoder->state_ = INITIALIZED; | 137 decoder->state_ = INITIALIZED; |
| 138 } else { | 138 } else { |
| 139 factories->GetMessageLoop()->DeleteSoon(FROM_HERE, decoder.release()); | 139 factories->GetMessageLoop()->DeleteSoon(FROM_HERE, decoder.release()); |
| 140 } | 140 } |
| 141 return decoder.Pass(); | 141 return decoder.Pass(); |
| 142 } | 142 } |
| 143 | 143 |
| 144 int32_t RTCVideoDecoder::InitDecode(const webrtc::VideoCodec* codecSettings, | 144 int32_t RTCVideoDecoder::InitDecode(const webrtc::VideoCodec* codecSettings, |
| 145 int32_t /*numberOfCores*/) { | 145 int32_t /*numberOfCores*/) { |
| 146 DVLOG(2) << "InitDecode"; | 146 DVLOG(2) << "InitDecode"; |
| 147 DCHECK_EQ(codecSettings->codecType, webrtc::kVideoCodecVP8); | 147 DCHECK_EQ(codecSettings->codecType, webrtc::kVideoCodecVP8); |
| 148 if (codecSettings->codecSpecific.VP8.feedbackModeOn) { | 148 if (codecSettings->codecSpecific.VP8.feedbackModeOn) { |
| 149 LOG(ERROR) << "Feedback mode not supported"; | 149 LOG(ERROR) << "Feedback mode not supported"; |
| 150 return WEBRTC_VIDEO_CODEC_ERROR; | 150 return WEBRTC_VIDEO_CODEC_ERROR; |
| 151 } | 151 } |
| 152 | 152 |
| 153 base::AutoLock auto_lock(lock_); | 153 base::AutoLock auto_lock(lock_); |
| 154 if (state_ == UNINITIALIZED || state_ == DECODE_ERROR) { | 154 if (state_ == UNINITIALIZED || state_ == DECODE_ERROR) { |
| 155 LOG(ERROR) << "VDA is not initialized. state=" << state_; | 155 LOG(ERROR) << "VDA is not initialized. state=" << state_; |
| 156 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 156 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| 157 } | 157 } |
| 158 // Create some shared memory if the queue is empty. | 158 // Create some shared memory if the queue is empty. |
| 159 if (available_shm_segments_.size() == 0) { | 159 if (available_shm_segments_.size() == 0) { |
| 160 // Unretained is safe because the destructor will wait until | 160 vda_loop_proxy_->PostTask(FROM_HERE, |
| 161 // |create_shm_thread_| stops. | 161 base::Bind(&RTCVideoDecoder::CreateSHM, |
| 162 create_shm_thread_.message_loop_proxy() | 162 weak_this_, |
| 163 ->PostTask(FROM_HERE, | 163 kMaxInFlightDecodes, |
| 164 base::Bind(&RTCVideoDecoder::CreateSHM, | 164 kSharedMemorySegmentBytes)); |
| 165 base::Unretained(this), | |
| 166 kMaxInFlightDecodes, | |
| 167 kSharedMemorySegmentBytes)); | |
| 168 } | 165 } |
| 169 return WEBRTC_VIDEO_CODEC_OK; | 166 return WEBRTC_VIDEO_CODEC_OK; |
| 170 } | 167 } |
| 171 | 168 |
| 172 int32_t RTCVideoDecoder::Decode( | 169 int32_t RTCVideoDecoder::Decode( |
| 173 const webrtc::EncodedImage& inputImage, | 170 const webrtc::EncodedImage& inputImage, |
| 174 bool missingFrames, | 171 bool missingFrames, |
| 175 const webrtc::RTPFragmentationHeader* /*fragmentation*/, | 172 const webrtc::RTPFragmentationHeader* /*fragmentation*/, |
| 176 const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/, | 173 const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/, |
| 177 int64_t /*renderTimeMs*/) { | 174 int64_t /*renderTimeMs*/) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 224 int32_t RTCVideoDecoder::RegisterDecodeCompleteCallback( | 221 int32_t RTCVideoDecoder::RegisterDecodeCompleteCallback( |
| 225 webrtc::DecodedImageCallback* callback) { | 222 webrtc::DecodedImageCallback* callback) { |
| 226 DVLOG(2) << "RegisterDecodeCompleteCallback"; | 223 DVLOG(2) << "RegisterDecodeCompleteCallback"; |
| 227 base::AutoLock auto_lock(lock_); | 224 base::AutoLock auto_lock(lock_); |
| 228 decode_complete_callback_ = callback; | 225 decode_complete_callback_ = callback; |
| 229 return WEBRTC_VIDEO_CODEC_OK; | 226 return WEBRTC_VIDEO_CODEC_OK; |
| 230 } | 227 } |
| 231 | 228 |
| 232 int32_t RTCVideoDecoder::Release() { | 229 int32_t RTCVideoDecoder::Release() { |
| 233 DVLOG(2) << "Release"; | 230 DVLOG(2) << "Release"; |
| 234 // Do not destroy VDA because the decoder will be recycled by | 231 base::AutoLock auto_lock(lock_); |
| 235 // RTCVideoDecoderFactory. Just reset VDA. | 232 if (state_ == UNINITIALIZED) { |
| 236 return Reset(); | 233 LOG(ERROR) << "Decoder not initialized."; |
| 234 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
| 235 } | |
| 236 if (next_bitstream_buffer_id_ != 0) | |
| 237 reset_bitstream_buffer_id_ = next_bitstream_buffer_id_ - 1; | |
| 238 else | |
| 239 reset_bitstream_buffer_id_ = ID_LAST; | |
| 240 factories_->Abort(); | |
| 241 vda_loop_proxy_->PostTask( | |
| 242 FROM_HERE, base::Bind(&RTCVideoDecoder::ReleaseInternal, weak_this_)); | |
| 243 return WEBRTC_VIDEO_CODEC_OK; | |
| 237 } | 244 } |
| 238 | 245 |
| 239 int32_t RTCVideoDecoder::Reset() { | 246 int32_t RTCVideoDecoder::Reset() { |
| 240 DVLOG(2) << "Reset"; | 247 DVLOG(2) << "Reset"; |
| 241 base::AutoLock auto_lock(lock_); | 248 base::AutoLock auto_lock(lock_); |
| 242 if (state_ == UNINITIALIZED) { | 249 if (state_ == UNINITIALIZED) { |
| 243 LOG(ERROR) << "Decoder not initialized."; | 250 LOG(ERROR) << "Decoder not initialized."; |
| 244 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 251 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| 245 } | 252 } |
| 246 if (next_bitstream_buffer_id_ != 0) | 253 if (next_bitstream_buffer_id_ != 0) |
| (...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 587 } | 594 } |
| 588 } | 595 } |
| 589 | 596 |
| 590 void RTCVideoDecoder::ResetInternal() { | 597 void RTCVideoDecoder::ResetInternal() { |
| 591 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | 598 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); |
| 592 DVLOG(2) << "ResetInternal"; | 599 DVLOG(2) << "ResetInternal"; |
| 593 if (vda_) | 600 if (vda_) |
| 594 vda_->Reset(); | 601 vda_->Reset(); |
| 595 } | 602 } |
| 596 | 603 |
| 604 void RTCVideoDecoder::ReleaseInternal() { | |
| 605 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 606 DVLOG(2) << "ReleaseInternal"; | |
| 607 DestroyVDA(); | |
| 608 } | |
| 609 | |
| 597 void RTCVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id, | 610 void RTCVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id, |
| 598 uint32 sync_point) { | 611 uint32 sync_point) { |
| 599 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | 612 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); |
| 600 DVLOG(3) << "ReusePictureBuffer. id=" << picture_buffer_id; | 613 DVLOG(3) << "ReusePictureBuffer. id=" << picture_buffer_id; |
| 601 | 614 |
| 602 if (!vda_) | 615 if (!vda_) |
| 603 return; | 616 return; |
| 604 | 617 |
| 605 CHECK(!picture_buffers_at_display_.empty()); | 618 CHECK(!picture_buffers_at_display_.empty()); |
| 606 | 619 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 653 | 666 |
| 654 scoped_ptr<RTCVideoDecoder::SHMBuffer> RTCVideoDecoder::GetSHM_Locked( | 667 scoped_ptr<RTCVideoDecoder::SHMBuffer> RTCVideoDecoder::GetSHM_Locked( |
| 655 size_t min_size) { | 668 size_t min_size) { |
| 656 // Reuse a SHM if possible. | 669 // Reuse a SHM if possible. |
| 657 SHMBuffer* ret = NULL; | 670 SHMBuffer* ret = NULL; |
| 658 if (!available_shm_segments_.empty() && | 671 if (!available_shm_segments_.empty() && |
| 659 available_shm_segments_.back()->size >= min_size) { | 672 available_shm_segments_.back()->size >= min_size) { |
| 660 ret = available_shm_segments_.back(); | 673 ret = available_shm_segments_.back(); |
| 661 available_shm_segments_.pop_back(); | 674 available_shm_segments_.pop_back(); |
| 662 } | 675 } |
| 663 // Post to the child thread to create shared memory if SHM cannot be reused | 676 // Post to vda thread to create shared memory if SHM cannot be reused or the |
| 664 // or the queue is almost empty. | 677 // queue is almost empty. |
| 665 if (num_shm_buffers_ < kMaxNumSharedMemorySegments && | 678 if (num_shm_buffers_ < kMaxNumSharedMemorySegments && |
| 666 (ret == NULL || available_shm_segments_.size() <= 1)) { | 679 (ret == NULL || available_shm_segments_.size() <= 1)) { |
| 667 create_shm_thread_.message_loop_proxy()->PostTask( | 680 vda_loop_proxy_->PostTask( |
| 668 FROM_HERE, | 681 FROM_HERE, |
| 669 // Unretained is safe because the destructor will wait until | 682 base::Bind(&RTCVideoDecoder::CreateSHM, weak_this_, 1, min_size)); |
| 670 // |create_shm_thread_| stops. | |
| 671 base::Bind( | |
| 672 &RTCVideoDecoder::CreateSHM, base::Unretained(this), 1, min_size)); | |
| 673 } | 683 } |
| 674 return scoped_ptr<SHMBuffer>(ret); | 684 return scoped_ptr<SHMBuffer>(ret); |
| 675 } | 685 } |
| 676 | 686 |
| 677 void RTCVideoDecoder::PutSHM_Locked(scoped_ptr<SHMBuffer> shm_buffer) { | 687 void RTCVideoDecoder::PutSHM_Locked(scoped_ptr<SHMBuffer> shm_buffer) { |
| 678 available_shm_segments_.push_back(shm_buffer.release()); | 688 available_shm_segments_.push_back(shm_buffer.release()); |
| 679 } | 689 } |
| 680 | 690 |
| 681 void RTCVideoDecoder::CreateSHM(int number, size_t min_size) { | 691 void RTCVideoDecoder::CreateSHM(int number, size_t min_size) { |
| 682 DCHECK(create_shm_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 692 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); |
| 683 DVLOG(2) << "CreateSHM. size=" << min_size; | 693 DVLOG(2) << "CreateSHM. size=" << min_size; |
| 684 int number_to_allocate; | 694 int number_to_allocate; |
| 685 { | 695 { |
| 686 base::AutoLock auto_lock(lock_); | 696 base::AutoLock auto_lock(lock_); |
| 687 number_to_allocate = | 697 number_to_allocate = |
| 688 std::min(kMaxNumSharedMemorySegments - num_shm_buffers_, number); | 698 std::min(kMaxNumSharedMemorySegments - num_shm_buffers_, number); |
| 689 } | 699 } |
| 690 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); | 700 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); |
| 691 for (int i = 0; i < number_to_allocate; i++) { | 701 for (int i = 0; i < number_to_allocate; i++) { |
| 692 base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate); | 702 base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate); |
| 693 if (shm != NULL) { | 703 if (shm != NULL) { |
| 694 base::AutoLock auto_lock(lock_); | 704 base::AutoLock auto_lock(lock_); |
| 695 num_shm_buffers_++; | 705 num_shm_buffers_++; |
| 696 PutSHM_Locked( | 706 PutSHM_Locked( |
| 697 scoped_ptr<SHMBuffer>(new SHMBuffer(shm, size_to_allocate))); | 707 scoped_ptr<SHMBuffer>(new SHMBuffer(shm, size_to_allocate))); |
| 698 // Kick off the decoding. | |
| 699 vda_loop_proxy_->PostTask( | |
| 700 FROM_HERE, | |
| 701 base::Bind(&RTCVideoDecoder::RequestBufferDecode, weak_this_)); | |
| 702 } | 708 } |
| 703 } | 709 } |
| 710 // Kick off the decoding. | |
| 711 RequestBufferDecode(); | |
| 704 } | 712 } |
| 705 | 713 |
| 706 void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) { | 714 void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) { |
| 707 input_buffer_data_.push_front(buffer_data); | 715 input_buffer_data_.push_front(buffer_data); |
| 708 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but | 716 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but |
| 709 // that's too small for some pathological B-frame test videos. The cost of | 717 // that's too small for some pathological B-frame test videos. The cost of |
| 710 // using too-high a value is low (192 bits per extra slot). | 718 // using too-high a value is low (192 bits per extra slot). |
| 711 static const size_t kMaxInputBufferDataSize = 128; | 719 static const size_t kMaxInputBufferDataSize = 128; |
| 712 // Pop from the back of the list, because that's the oldest and least likely | 720 // Pop from the back of the list, because that's the oldest and least likely |
| 713 // to be useful in the future data. | 721 // to be useful in the future data. |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 727 continue; | 735 continue; |
| 728 *timestamp = it->timestamp; | 736 *timestamp = it->timestamp; |
| 729 *width = it->width; | 737 *width = it->width; |
| 730 *height = it->height; | 738 *height = it->height; |
| 731 return; | 739 return; |
| 732 } | 740 } |
| 733 NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; | 741 NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; |
| 734 } | 742 } |
| 735 | 743 |
| 736 } // namespace content | 744 } // namespace content |
| OLD | NEW |