Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/renderer/media/rtc_video_decoder.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/memory/ref_counted.h" | |
| 10 #include "base/message_loop/message_loop_proxy.h" | |
| 11 #include "base/safe_numerics.h" | |
| 12 #include "base/stl_util.h" | |
| 13 #include "base/task_runner_util.h" | |
| 14 #include "content/child/child_thread.h" | |
| 15 #include "content/renderer/media/native_handle_impl.h" | |
| 16 #include "media/base/bind_to_loop.h" | |
| 17 #include "third_party/webrtc/common_video/interface/texture_video_frame.h" | |
| 18 #include "third_party/webrtc/system_wrappers/interface/ref_count.h" | |
| 19 | |
| 20 namespace content { | |
| 21 | |
| 22 const int32 RTCVideoDecoder::ID_LAST = 0x3FFFFFFF; | |
|
Ami GONE FROM CHROMIUM
2013/07/02 23:36:57
FWIW these could as well be enums declared here.
I
| |
| 23 const int32 RTCVideoDecoder::ID_HALF = 0x20000000; | |
| 24 const int32 RTCVideoDecoder::ID_INVALID = -1; | |
| 25 | |
| 26 // Maximum number of concurrent VDA::Decode() operations RVD will maintain. | |
| 27 // Higher values allow better pipelining in the GPU, but also require more | |
| 28 // resources. | |
| 29 static const size_t kMaxInFlightDecodes = 8; | |
| 30 | |
| 31 // Size of shared-memory segments we allocate. Since we reuse them we let them | |
| 32 // be on the beefy side. | |
| 33 static const size_t kSharedMemorySegmentBytes = 100 << 10; | |
| 34 | |
| 35 // Maximum number of allocated shared-memory segments. | |
| 36 static const int kMaxNumSharedMemorySegments = 16; | |
| 37 | |
| 38 // Maximum number of pending WebRTC buffers that are waiting for the shared | |
| 39 // memory. 10 seconds for 30 fps. | |
| 40 static const size_t kMaxNumOfPendingBuffers = 300; | |
| 41 | |
| 42 // A shared memory segment and its allocated size. This class has the ownership | |
| 43 // of |shm|. | |
| 44 class RTCVideoDecoder::SHMBuffer { | |
| 45 public: | |
| 46 SHMBuffer(base::SharedMemory* shm, size_t size); | |
| 47 ~SHMBuffer(); | |
| 48 base::SharedMemory* const shm; | |
| 49 const size_t size; | |
| 50 }; | |
| 51 | |
| 52 RTCVideoDecoder::SHMBuffer::SHMBuffer(base::SharedMemory* shm, size_t size) | |
| 53 : shm(shm), size(size) {} | |
| 54 | |
| 55 RTCVideoDecoder::SHMBuffer::~SHMBuffer() { shm->Close(); } | |
| 56 | |
| 57 // Metadata of a bitstream buffer. | |
| 58 struct RTCVideoDecoder::BufferData { | |
| 59 BufferData(int32 bitstream_buffer_id, | |
| 60 uint32_t timestamp, | |
| 61 int width, | |
| 62 int height, | |
| 63 size_t size); | |
| 64 ~BufferData(); | |
| 65 int32 bitstream_buffer_id; | |
| 66 uint32_t timestamp; // in 90KHz | |
| 67 uint32_t width; | |
| 68 uint32_t height; | |
| 69 size_t size; // buffer size | |
| 70 }; | |
| 71 | |
| 72 RTCVideoDecoder::BufferData::BufferData(int32 bitstream_buffer_id, | |
| 73 uint32_t timestamp, | |
| 74 int width, | |
| 75 int height, | |
| 76 size_t size) | |
| 77 : bitstream_buffer_id(bitstream_buffer_id), | |
| 78 timestamp(timestamp), | |
| 79 width(width), | |
| 80 height(height), | |
| 81 size(size) {} | |
| 82 | |
| 83 RTCVideoDecoder::BufferData::~BufferData() {} | |
| 84 | |
| 85 RTCVideoDecoder::RTCVideoDecoder( | |
| 86 const scoped_refptr<media::GpuVideoDecoder::Factories>& factories) | |
| 87 : weak_factory_(this), | |
| 88 factories_(factories), | |
| 89 vda_loop_proxy_(factories_->GetMessageLoop()), | |
| 90 create_shm_thread_("CreateSHMThread"), | |
| 91 decoder_texture_target_(0), | |
| 92 next_picture_buffer_id_(0), | |
| 93 state_(UNINITIALIZED), | |
| 94 decode_complete_callback_(NULL), | |
| 95 num_shm_buffers_(0), | |
| 96 next_bitstream_buffer_id_(0), | |
| 97 reset_bitstream_buffer_id_(0) { | |
| 98 // Initialize directly if |vda_loop_proxy_| is the renderer thread. | |
| 99 base::WaitableEvent compositor_loop_async_waiter(false, false); | |
| 100 if (vda_loop_proxy_->BelongsToCurrentThread()) { | |
| 101 Initialize(&compositor_loop_async_waiter); | |
| 102 return; | |
| 103 } | |
| 104 // Post the task if |vda_loop_proxy_| is the compositor thread. Waiting here | |
| 105 // is safe because the compositor thread will not be stopped until the | |
| 106 // renderer thread shuts down. | |
| 107 vda_loop_proxy_->PostTask(FROM_HERE, | |
| 108 base::Bind(&RTCVideoDecoder::Initialize, | |
| 109 base::Unretained(this), | |
| 110 &compositor_loop_async_waiter)); | |
| 111 compositor_loop_async_waiter.Wait(); | |
| 112 } | |
| 113 | |
| 114 RTCVideoDecoder::~RTCVideoDecoder() { | |
| 115 DVLOG(2) << "~RTCVideoDecoder"; | |
| 116 factories_->Abort(); | |
| 117 create_shm_thread_.Stop(); | |
| 118 // Delete vda and remove |this| from the observer if vda thread is alive. | |
| 119 if (vda_loop_proxy_->BelongsToCurrentThread()) { | |
| 120 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
| 121 DestroyVDA(); | |
| 122 } else { | |
| 123 // VDA should have been destroyed in WillDestroyCurrentMessageLoop. | |
| 124 DCHECK(!vda_); | |
| 125 } | |
| 126 | |
| 127 // Delete all shared memories. | |
| 128 STLDeleteElements(&available_shm_segments_); | |
| 129 STLDeleteValues(&bitstream_buffers_in_decoder_); | |
| 130 STLDeleteContainerPairFirstPointers(decode_buffers_.begin(), | |
| 131 decode_buffers_.end()); | |
| 132 decode_buffers_.clear(); | |
| 133 | |
| 134 // Delete WebRTC input buffers. | |
| 135 for (std::deque<std::pair<webrtc::EncodedImage, BufferData> >::iterator it = | |
| 136 pending_buffers_.begin(); | |
| 137 it != pending_buffers_.end(); | |
| 138 ++it) { | |
| 139 delete it->first._buffer; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 scoped_ptr<RTCVideoDecoder> RTCVideoDecoder::Create( | |
| 144 const scoped_refptr<media::GpuVideoDecoder::Factories>& factories) { | |
| 145 scoped_ptr<RTCVideoDecoder> decoder(new RTCVideoDecoder(factories)); | |
| 146 decoder->vda_.reset(factories->CreateVideoDecodeAccelerator( | |
| 147 media::VP8PROFILE_MAIN, decoder.get())); | |
| 148 // vda can be NULL if VP8 is not supported. | |
| 149 if (decoder->vda_ != NULL) { | |
| 150 decoder->state_ = INITIALIZED; | |
| 151 } else { | |
| 152 factories->GetMessageLoop()->DeleteSoon(FROM_HERE, decoder.release()); | |
| 153 } | |
| 154 return decoder.Pass(); | |
| 155 } | |
| 156 | |
| 157 int32_t RTCVideoDecoder::InitDecode(const webrtc::VideoCodec* codecSettings, | |
| 158 int32_t /*numberOfCores*/) { | |
| 159 DVLOG(2) << "InitDecode"; | |
| 160 DCHECK_EQ(codecSettings->codecType, webrtc::kVideoCodecVP8); | |
| 161 if (codecSettings->codecSpecific.VP8.feedbackModeOn) { | |
| 162 LOG(ERROR) << "Feedback mode not supported"; | |
| 163 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 164 } | |
| 165 | |
| 166 base::AutoLock auto_lock(lock_); | |
| 167 if (state_ == UNINITIALIZED || state_ == DECODE_ERROR) { | |
| 168 LOG(ERROR) << "VDA is not initialized. state=" << state_; | |
| 169 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
| 170 } | |
| 171 // Create some shared memory if the queue is empty. | |
| 172 if (available_shm_segments_.size() == 0) { | |
| 173 create_shm_thread_.message_loop_proxy() | |
| 174 ->PostTask(FROM_HERE, | |
| 175 base::Bind(&RTCVideoDecoder::CreateSHM, | |
| 176 weak_this_, | |
| 177 kMaxInFlightDecodes, | |
| 178 kSharedMemorySegmentBytes)); | |
| 179 } | |
| 180 return WEBRTC_VIDEO_CODEC_OK; | |
| 181 } | |
| 182 | |
| 183 int32_t RTCVideoDecoder::Decode( | |
| 184 const webrtc::EncodedImage& inputImage, | |
| 185 bool missingFrames, | |
| 186 const webrtc::RTPFragmentationHeader* /*fragmentation*/, | |
| 187 const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/, | |
| 188 int64_t /*renderTimeMs*/) { | |
| 189 DVLOG(3) << "Decode"; | |
| 190 | |
| 191 int bitstream_buffer_id; | |
| 192 bool no_pending_buffers; | |
| 193 { | |
| 194 base::AutoLock auto_lock(lock_); | |
| 195 if (state_ == UNINITIALIZED || decode_complete_callback_ == NULL) { | |
| 196 LOG(ERROR) << "The decoder has not initialized."; | |
| 197 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
| 198 } | |
| 199 if (state_ == DECODE_ERROR) { | |
| 200 LOG(ERROR) << "Decoding error occurred."; | |
| 201 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 202 } | |
| 203 bitstream_buffer_id = next_bitstream_buffer_id_; | |
| 204 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. | |
| 205 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & ID_LAST; | |
| 206 no_pending_buffers = (pending_buffers_.size() == 0); | |
| 207 } | |
| 208 if (missingFrames || !inputImage._completeFrame) { | |
| 209 DLOG(ERROR) << "Missing or incomplete frames."; | |
| 210 // Unlike the SW decoder in libvpx, hw decoder cannot handle broken frames. | |
| 211 // Return an error to request a key frame. | |
| 212 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 213 } | |
| 214 if (inputImage._frameType == webrtc::kKeyFrame) | |
| 215 frame_size_.SetSize(inputImage._encodedWidth, inputImage._encodedHeight); | |
| 216 | |
| 217 // Create buffer metadata. | |
| 218 BufferData buffer_data(bitstream_buffer_id, | |
| 219 inputImage._timeStamp, | |
| 220 frame_size_.width(), | |
| 221 frame_size_.height(), | |
| 222 inputImage._length); | |
| 223 | |
| 224 // If the shared memory is available and there are no pending buffers, send | |
| 225 // the buffer for decode. If not, save the buffer in the queue for decode | |
| 226 // later. | |
| 227 scoped_ptr<SHMBuffer> shm_buffer; | |
| 228 if (no_pending_buffers) | |
|
Ami GONE FROM CHROMIUM
2013/07/02 23:36:57
setting this under lock but then inspecting it wit
wuchengli
2013/07/03 09:09:38
It should be OK because the only place that increa
| |
| 229 shm_buffer = GetSHM(inputImage._length); | |
| 230 | |
| 231 if (!shm_buffer) { | |
| 232 if (SaveToPendingBuffers(inputImage, buffer_data)) | |
| 233 return WEBRTC_VIDEO_CODEC_OK; | |
| 234 else | |
| 235 return WEBRTC_VIDEO_CODEC_ERROR; | |
| 236 } | |
| 237 | |
| 238 SaveToDecodeBuffers(inputImage, shm_buffer.Pass(), buffer_data); | |
| 239 vda_loop_proxy_->PostTask( | |
| 240 FROM_HERE, base::Bind(&RTCVideoDecoder::RequestBufferDecode, weak_this_)); | |
| 241 return WEBRTC_VIDEO_CODEC_OK; | |
| 242 } | |
| 243 | |
| 244 int32_t RTCVideoDecoder::RegisterDecodeCompleteCallback( | |
| 245 webrtc::DecodedImageCallback* callback) { | |
| 246 DVLOG(2) << "RegisterDecodeCompleteCallback"; | |
| 247 base::AutoLock auto_lock(lock_); | |
| 248 decode_complete_callback_ = callback; | |
| 249 return WEBRTC_VIDEO_CODEC_OK; | |
| 250 } | |
| 251 | |
| 252 int32_t RTCVideoDecoder::Release() { | |
| 253 DVLOG(2) << "Release"; | |
| 254 // Do not destroy VDA because the decoder will be recycled by | |
| 255 // RTCVideoDecoderFactory. Just reset VDA. | |
| 256 return Reset(); | |
| 257 } | |
| 258 | |
| 259 int32_t RTCVideoDecoder::Reset() { | |
| 260 DVLOG(2) << "Reset"; | |
| 261 base::AutoLock auto_lock(lock_); | |
| 262 if (state_ == UNINITIALIZED) { | |
| 263 LOG(ERROR) << "Decoder not initialized."; | |
| 264 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
| 265 } | |
| 266 reset_bitstream_buffer_id_ = next_bitstream_buffer_id_; | |
| 267 // If VDA is already resetting, no need to request the reset again. | |
| 268 if (state_ != RESETTING) { | |
| 269 state_ = RESETTING; | |
| 270 vda_loop_proxy_->PostTask( | |
| 271 FROM_HERE, base::Bind(&RTCVideoDecoder::ResetInternal, weak_this_)); | |
| 272 } | |
| 273 return WEBRTC_VIDEO_CODEC_OK; | |
| 274 } | |
| 275 | |
| 276 void RTCVideoDecoder::NotifyInitializeDone() { | |
| 277 DVLOG(2) << "NotifyInitializeDone"; | |
| 278 NOTREACHED(); | |
| 279 } | |
| 280 | |
| 281 void RTCVideoDecoder::ProvidePictureBuffers(uint32 count, | |
| 282 const gfx::Size& size, | |
| 283 uint32 texture_target) { | |
| 284 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 285 DVLOG(3) << "ProvidePictureBuffers. texture_target=" << texture_target; | |
| 286 | |
| 287 if (!vda_) | |
| 288 return; | |
| 289 | |
| 290 std::vector<uint32> texture_ids; | |
| 291 std::vector<gpu::Mailbox> texture_mailboxes; | |
| 292 decoder_texture_target_ = texture_target; | |
| 293 // Discards the sync point returned here since PictureReady will imply that | |
| 294 // the produce has already happened, and the texture is ready for use. | |
| 295 if (!factories_->CreateTextures(count, | |
| 296 size, | |
| 297 &texture_ids, | |
| 298 &texture_mailboxes, | |
| 299 decoder_texture_target_)) { | |
| 300 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 301 return; | |
| 302 } | |
| 303 DCHECK_EQ(count, texture_ids.size()); | |
| 304 DCHECK_EQ(count, texture_mailboxes.size()); | |
| 305 | |
| 306 std::vector<media::PictureBuffer> picture_buffers; | |
| 307 for (size_t i = 0; i < texture_ids.size(); ++i) { | |
| 308 picture_buffers.push_back(media::PictureBuffer( | |
| 309 next_picture_buffer_id_++, size, texture_ids[i], texture_mailboxes[i])); | |
| 310 bool inserted = assigned_picture_buffers_.insert(std::make_pair( | |
| 311 picture_buffers.back().id(), picture_buffers.back())).second; | |
| 312 DCHECK(inserted); | |
| 313 } | |
| 314 vda_->AssignPictureBuffers(picture_buffers); | |
| 315 } | |
| 316 | |
| 317 void RTCVideoDecoder::DismissPictureBuffer(int32 id) { | |
| 318 DVLOG(3) << "DismissPictureBuffer. id=" << id; | |
| 319 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 320 | |
| 321 std::map<int32, media::PictureBuffer>::iterator it = | |
| 322 assigned_picture_buffers_.find(id); | |
| 323 if (it == assigned_picture_buffers_.end()) { | |
| 324 NOTREACHED() << "Missing picture buffer: " << id; | |
| 325 return; | |
| 326 } | |
| 327 | |
| 328 media::PictureBuffer buffer_to_dismiss = it->second; | |
| 329 assigned_picture_buffers_.erase(it); | |
| 330 | |
| 331 std::set<int32>::iterator at_display_it = | |
| 332 picture_buffers_at_display_.find(id); | |
| 333 | |
| 334 if (at_display_it == picture_buffers_at_display_.end()) { | |
| 335 // We can delete the texture immediately as it's not being displayed. | |
| 336 factories_->DeleteTexture(buffer_to_dismiss.texture_id()); | |
| 337 } else { | |
| 338 // Texture in display. Postpone deletion until after it's returned to us. | |
| 339 bool inserted = dismissed_picture_buffers_ | |
| 340 .insert(std::make_pair(id, buffer_to_dismiss)).second; | |
| 341 DCHECK(inserted); | |
| 342 } | |
| 343 } | |
| 344 | |
| 345 void RTCVideoDecoder::PictureReady(const media::Picture& picture) { | |
| 346 DVLOG(3) << "PictureReady"; | |
| 347 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 348 | |
| 349 std::map<int32, media::PictureBuffer>::iterator it = | |
| 350 assigned_picture_buffers_.find(picture.picture_buffer_id()); | |
| 351 if (it == assigned_picture_buffers_.end()) { | |
| 352 NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id(); | |
| 353 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 354 return; | |
| 355 } | |
| 356 const media::PictureBuffer& pb = it->second; | |
| 357 | |
| 358 // Create a media::VideoFrame. | |
| 359 uint32_t timestamp = 0, width = 0, height = 0; | |
| 360 size_t size = 0; | |
| 361 GetBufferData( | |
| 362 picture.bitstream_buffer_id(), ×tamp, &width, &height, &size); | |
| 363 scoped_refptr<media::VideoFrame> frame = | |
| 364 CreateVideoFrame(picture, pb, timestamp, width, height, size); | |
| 365 bool inserted = | |
| 366 picture_buffers_at_display_.insert(picture.picture_buffer_id()).second; | |
| 367 DCHECK(inserted); | |
| 368 | |
| 369 // Create a WebRTC video frame. | |
| 370 webrtc::RefCountImpl<NativeHandleImpl>* handle = | |
| 371 new webrtc::RefCountImpl<NativeHandleImpl>(); | |
| 372 handle->SetHandle(frame.get()); | |
| 373 webrtc::TextureVideoFrame decoded_image(width, height, timestamp, 0, handle); | |
| 374 | |
| 375 // Invoke decode callback. WebRTC expects no frame callback after Release. | |
| 376 { | |
| 377 base::AutoLock auto_lock(lock_); | |
| 378 DCHECK(decode_complete_callback_ != NULL); | |
| 379 if (IsBufferAfterReset(picture.bitstream_buffer_id(), | |
| 380 reset_bitstream_buffer_id_)) { | |
| 381 decode_complete_callback_->Decoded(decoded_image); | |
| 382 } | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 scoped_refptr<media::VideoFrame> RTCVideoDecoder::CreateVideoFrame( | |
| 387 const media::Picture& picture, | |
| 388 const media::PictureBuffer& pb, | |
| 389 uint32_t timestamp, | |
| 390 uint32_t width, | |
| 391 uint32_t height, | |
| 392 size_t size) { | |
| 393 gfx::Rect visible_rect(width, height); | |
| 394 gfx::Size natural_size(width, height); | |
| 395 DCHECK(decoder_texture_target_); | |
| 396 // Convert timestamp from 90KHz to ms. | |
| 397 base::TimeDelta timestamp_ms = base::TimeDelta::FromInternalValue( | |
| 398 base::checked_numeric_cast<uint64_t>(timestamp) * 1000 / 90); | |
| 399 return media::VideoFrame::WrapNativeTexture( | |
| 400 new media::VideoFrame::MailboxHolder( | |
| 401 pb.texture_mailbox(), | |
| 402 0, // sync_point | |
| 403 media::BindToCurrentLoop( | |
| 404 base::Bind(&RTCVideoDecoder::ReusePictureBuffer, | |
| 405 weak_this_, | |
| 406 picture.picture_buffer_id()))), | |
| 407 decoder_texture_target_, | |
| 408 pb.size(), | |
| 409 visible_rect, | |
| 410 natural_size, | |
| 411 timestamp_ms, | |
| 412 base::Bind(&media::GpuVideoDecoder::Factories::ReadPixels, | |
| 413 factories_, | |
| 414 pb.texture_id(), | |
| 415 decoder_texture_target_, | |
| 416 natural_size), | |
| 417 base::Closure()); | |
| 418 } | |
| 419 | |
| 420 void RTCVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { | |
| 421 DVLOG(3) << "NotifyEndOfBitstreamBuffer. id=" << id; | |
| 422 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 423 | |
| 424 std::map<int32, SHMBuffer*>::iterator it = | |
| 425 bitstream_buffers_in_decoder_.find(id); | |
| 426 if (it == bitstream_buffers_in_decoder_.end()) { | |
| 427 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
| 428 NOTREACHED() << "Missing bitstream buffer: " << id; | |
| 429 return; | |
| 430 } | |
| 431 | |
| 432 PutSHM(scoped_ptr<SHMBuffer>(it->second)); | |
| 433 bitstream_buffers_in_decoder_.erase(it); | |
| 434 | |
| 435 RequestBufferDecode(); | |
| 436 } | |
| 437 | |
| 438 void RTCVideoDecoder::NotifyFlushDone() { | |
| 439 DVLOG(3) << "NotifyFlushDone"; | |
| 440 NOTREACHED() << "Unexpected flush done notification."; | |
| 441 } | |
| 442 | |
| 443 void RTCVideoDecoder::NotifyResetDone() { | |
| 444 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 445 DVLOG(3) << "NotifyResetDone"; | |
| 446 | |
| 447 if (!vda_) | |
| 448 return; | |
| 449 | |
| 450 input_buffer_data_.clear(); | |
| 451 { | |
| 452 base::AutoLock auto_lock(lock_); | |
| 453 state_ = INITIALIZED; | |
| 454 } | |
| 455 // Send the pending buffers for decoding. | |
| 456 RequestBufferDecode(); | |
| 457 } | |
| 458 | |
| 459 void RTCVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { | |
| 460 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 461 if (!vda_) | |
| 462 return; | |
| 463 | |
| 464 DLOG(ERROR) << "VDA Error:" << error; | |
| 465 DestroyVDA(); | |
| 466 | |
| 467 base::AutoLock auto_lock(lock_); | |
| 468 state_ = DECODE_ERROR; | |
| 469 } | |
| 470 | |
| 471 void RTCVideoDecoder::WillDestroyCurrentMessageLoop() { | |
| 472 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 473 factories_->Abort(); | |
| 474 weak_factory_.InvalidateWeakPtrs(); | |
| 475 DestroyVDA(); | |
| 476 } | |
| 477 | |
| 478 void RTCVideoDecoder::Initialize(base::WaitableEvent* waiter) { | |
| 479 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 480 base::MessageLoop::current()->AddDestructionObserver(this); | |
| 481 weak_this_ = weak_factory_.GetWeakPtr(); | |
| 482 create_shm_thread_.Start(); | |
| 483 waiter->Signal(); | |
| 484 } | |
| 485 | |
| 486 void RTCVideoDecoder::RequestBufferDecode() { | |
| 487 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 488 if (!vda_) | |
| 489 return; | |
| 490 | |
| 491 MovePendingBuffersToDecodeBuffers(); | |
| 492 | |
| 493 while (CanMoreDecodeWorkBeDone()) { | |
| 494 // Get a buffer and data from the queue. | |
| 495 std::pair<SHMBuffer*, BufferData>* buffer_pair; | |
| 496 SHMBuffer* shm_buffer = NULL; | |
| 497 BufferData* buffer_data = NULL; | |
| 498 { | |
| 499 base::AutoLock auto_lock(lock_); | |
| 500 // Do not request decode if VDA is resetting. | |
| 501 if (decode_buffers_.size() == 0 || state_ == RESETTING) | |
| 502 return; | |
| 503 buffer_pair = &decode_buffers_.front(); | |
| 504 decode_buffers_.pop_front(); | |
| 505 shm_buffer = buffer_pair->first; | |
| 506 buffer_data = &buffer_pair->second; | |
| 507 // Drop the buffers before Reset or Release is called. | |
| 508 if (!IsBufferAfterReset(buffer_data->bitstream_buffer_id, | |
| 509 reset_bitstream_buffer_id_)) { | |
| 510 available_shm_segments_.push_back(shm_buffer); | |
| 511 continue; | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 // Create a BitstreamBuffer and send to VDA to decode. | |
| 516 media::BitstreamBuffer bitstream_buffer(buffer_data->bitstream_buffer_id, | |
| 517 shm_buffer->shm->handle(), | |
| 518 buffer_data->size); | |
| 519 bool inserted = bitstream_buffers_in_decoder_ | |
| 520 .insert(std::make_pair(bitstream_buffer.id(), shm_buffer)).second; | |
| 521 DCHECK(inserted); | |
| 522 RecordBufferData(*buffer_data); | |
| 523 vda_->Decode(bitstream_buffer); | |
| 524 } | |
| 525 } | |
| 526 | |
| 527 bool RTCVideoDecoder::CanMoreDecodeWorkBeDone() { | |
| 528 return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes; | |
| 529 } | |
| 530 | |
| 531 bool RTCVideoDecoder::IsBufferAfterReset(int32 id_buffer, int32 id_reset) { | |
| 532 if (id_reset == ID_INVALID) | |
| 533 return true; | |
| 534 int32 diff = id_buffer - id_reset; | |
| 535 if (diff < 0) | |
| 536 diff += ID_LAST + 1; | |
| 537 return diff < ID_HALF; | |
| 538 } | |
| 539 | |
| 540 void RTCVideoDecoder::SaveToDecodeBuffers( | |
| 541 const webrtc::EncodedImage& input_image, | |
| 542 scoped_ptr<SHMBuffer> shm_buffer, | |
| 543 const BufferData& buffer_data) { | |
| 544 memcpy(shm_buffer->shm->memory(), input_image._buffer, input_image._length); | |
| 545 std::pair<SHMBuffer*, BufferData> buffer_pair = | |
| 546 std::make_pair(shm_buffer.release(), buffer_data); | |
| 547 | |
| 548 // Store the buffer and the metadata to the queue. | |
| 549 base::AutoLock auto_lock(lock_); | |
| 550 decode_buffers_.push_back(buffer_pair); | |
| 551 } | |
| 552 | |
| 553 bool RTCVideoDecoder::SaveToPendingBuffers( | |
| 554 const webrtc::EncodedImage& input_image, | |
| 555 const BufferData& buffer_data) { | |
| 556 DVLOG(2) << "SaveToPendingBuffers"; | |
| 557 // Queued too many buffers. Something goes wrong. | |
| 558 if (pending_buffers_.size() >= kMaxNumOfPendingBuffers) { | |
| 559 LOG(WARNING) << "Too many pending buffers!"; | |
| 560 return false; | |
| 561 } | |
| 562 | |
| 563 // Clone the input image and save it to the queue. | |
| 564 uint8_t* buffer = static_cast<uint8_t*>(malloc(input_image._length)); | |
| 565 // TODO(wuchengli): avoid memcpy. Extend webrtc::VideoDecoder::Decode() | |
| 566 // interface to take a non-const ptr to the frame and add a method to the | |
| 567 // frame that will swap buffers with another. | |
| 568 memcpy(buffer, input_image._buffer, input_image._length); | |
| 569 webrtc::EncodedImage encoded_image( | |
| 570 buffer, input_image._length, input_image._length); | |
| 571 std::pair<webrtc::EncodedImage, BufferData> buffer_pair = | |
| 572 std::make_pair(encoded_image, buffer_data); | |
| 573 | |
| 574 base::AutoLock auto_lock(lock_); | |
| 575 pending_buffers_.push_back(buffer_pair); | |
| 576 return true; | |
| 577 } | |
| 578 | |
| 579 void RTCVideoDecoder::MovePendingBuffersToDecodeBuffers() { | |
| 580 while (true) { | |
| 581 webrtc::EncodedImage* input_image; | |
| 582 BufferData* buffer_data; | |
| 583 { | |
| 584 base::AutoLock auto_lock(lock_); | |
| 585 if (pending_buffers_.size() == 0) | |
| 586 return; | |
| 587 DVLOG(2) << "MovePendingBuffersToDecodeBuffers" | |
| 588 << ". pending_buffers size=" << pending_buffers_.size() | |
| 589 << ". decode_buffers_ size=" << decode_buffers_.size() | |
| 590 << ". available_shm size=" << available_shm_segments_.size(); | |
| 591 // Get a pending buffer from the queue. | |
| 592 input_image = &pending_buffers_.front().first; | |
| 593 buffer_data = &pending_buffers_.front().second; | |
| 594 | |
| 595 // Drop the frame if it comes before Reset or Release. | |
| 596 if (!IsBufferAfterReset(buffer_data->bitstream_buffer_id, | |
| 597 reset_bitstream_buffer_id_)) { | |
| 598 delete input_image->_buffer; | |
| 599 pending_buffers_.pop_front(); | |
| 600 continue; | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 // Get shared memory and save it to decode buffers. | |
| 605 scoped_ptr<SHMBuffer> shm_buffer = GetSHM(input_image->_length); | |
| 606 if (!shm_buffer) | |
| 607 return; | |
| 608 SaveToDecodeBuffers(*input_image, shm_buffer.Pass(), *buffer_data); | |
|
Ami GONE FROM CHROMIUM
2013/07/02 23:36:57
The locking in this function is wonky. Would thin
wuchengli
2013/07/03 09:09:38
Done. My concern was SaveToPendingBuffers and Save
| |
| 609 delete input_image->_buffer; | |
| 610 base::AutoLock auto_lock(lock_); | |
| 611 pending_buffers_.pop_front(); | |
| 612 } | |
| 613 } | |
| 614 | |
| 615 void RTCVideoDecoder::ResetInternal() { | |
| 616 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 617 DVLOG(2) << "ResetInternal"; | |
| 618 if (vda_) | |
| 619 vda_->Reset(); | |
| 620 } | |
| 621 | |
| 622 void RTCVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id, | |
| 623 uint32 sync_point) { | |
| 624 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 625 DVLOG(3) << "ReusePictureBuffer. id=" << picture_buffer_id; | |
| 626 | |
| 627 if (!vda_) | |
| 628 return; | |
| 629 | |
| 630 CHECK(!picture_buffers_at_display_.empty()); | |
| 631 | |
| 632 size_t num_erased = picture_buffers_at_display_.erase(picture_buffer_id); | |
| 633 DCHECK(num_erased); | |
| 634 | |
| 635 std::map<int32, media::PictureBuffer>::iterator it = | |
| 636 assigned_picture_buffers_.find(picture_buffer_id); | |
| 637 | |
| 638 if (it == assigned_picture_buffers_.end()) { | |
| 639 // This picture was dismissed while in display, so we postponed deletion. | |
| 640 it = dismissed_picture_buffers_.find(picture_buffer_id); | |
| 641 DCHECK(it != dismissed_picture_buffers_.end()); | |
| 642 factories_->DeleteTexture(it->second.texture_id()); | |
| 643 dismissed_picture_buffers_.erase(it); | |
| 644 return; | |
| 645 } | |
| 646 | |
| 647 factories_->WaitSyncPoint(sync_point); | |
| 648 | |
| 649 vda_->ReusePictureBuffer(picture_buffer_id); | |
| 650 } | |
| 651 | |
| 652 void RTCVideoDecoder::DestroyTextures() { | |
| 653 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 654 std::map<int32, media::PictureBuffer>::iterator it; | |
| 655 | |
| 656 for (it = assigned_picture_buffers_.begin(); | |
| 657 it != assigned_picture_buffers_.end(); | |
| 658 ++it) { | |
| 659 factories_->DeleteTexture(it->second.texture_id()); | |
| 660 } | |
| 661 assigned_picture_buffers_.clear(); | |
| 662 | |
| 663 for (it = dismissed_picture_buffers_.begin(); | |
| 664 it != dismissed_picture_buffers_.end(); | |
| 665 ++it) { | |
| 666 factories_->DeleteTexture(it->second.texture_id()); | |
| 667 } | |
| 668 dismissed_picture_buffers_.clear(); | |
| 669 } | |
| 670 | |
| 671 void RTCVideoDecoder::DestroyVDA() { | |
| 672 DVLOG(2) << "DestroyVDA"; | |
| 673 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
| 674 if (vda_) | |
| 675 vda_.release()->Destroy(); | |
| 676 DestroyTextures(); | |
| 677 } | |
| 678 | |
| 679 scoped_ptr<RTCVideoDecoder::SHMBuffer> RTCVideoDecoder::GetSHM( | |
| 680 size_t min_size) { | |
| 681 // Reuse a SHM if possible. | |
| 682 SHMBuffer* ret = NULL; | |
| 683 base::AutoLock auto_lock(lock_); | |
| 684 if (!available_shm_segments_.empty() && | |
| 685 available_shm_segments_.back()->size >= min_size) { | |
| 686 ret = available_shm_segments_.back(); | |
| 687 available_shm_segments_.pop_back(); | |
| 688 } | |
| 689 // Post to the child thread to create shared memory if SHM cannot be reused | |
| 690 // or the queue is almost empty. | |
| 691 if (num_shm_buffers_ < kMaxNumSharedMemorySegments && | |
| 692 (ret == NULL || available_shm_segments_.size() <= 1)) { | |
| 693 create_shm_thread_.message_loop_proxy()->PostTask( | |
| 694 FROM_HERE, | |
| 695 base::Bind(&RTCVideoDecoder::CreateSHM, weak_this_, 1, min_size)); | |
| 696 } | |
| 697 return scoped_ptr<SHMBuffer>(ret); | |
| 698 } | |
| 699 | |
| 700 void RTCVideoDecoder::CreateSHM(int number, size_t min_size) { | |
| 701 DCHECK(create_shm_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 702 DVLOG(2) << "CreateSharedMemory. size=" << min_size; | |
| 703 int number_to_allocate; | |
| 704 { | |
| 705 base::AutoLock auto_lock(lock_); | |
| 706 number_to_allocate = | |
| 707 std::min(kMaxNumSharedMemorySegments - num_shm_buffers_, number); | |
| 708 } | |
| 709 for (int i = 0; i < number_to_allocate; i++) { | |
| 710 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); | |
|
Ami GONE FROM CHROMIUM
2013/07/02 23:36:57
can go outside loop
wuchengli
2013/07/03 09:09:38
Done.
| |
| 711 base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate); | |
| 712 if (shm != NULL) { | |
| 713 { | |
| 714 base::AutoLock auto_lock(lock_); | |
| 715 num_shm_buffers_++; | |
| 716 } | |
| 717 PutSHM(scoped_ptr<SHMBuffer>(new SHMBuffer(shm, size_to_allocate))); | |
|
Ami GONE FROM CHROMIUM
2013/07/02 23:36:57
Ditto would PutSHM_Locked() make more sense?
wuchengli
2013/07/03 09:09:38
Done.
| |
| 718 // Kick off the decoding. | |
| 719 vda_loop_proxy_->PostTask( | |
| 720 FROM_HERE, | |
| 721 base::Bind(&RTCVideoDecoder::RequestBufferDecode, weak_this_)); | |
| 722 } | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 void RTCVideoDecoder::PutSHM(scoped_ptr<SHMBuffer> shm_buffer) { | |
| 727 base::AutoLock auto_lock(lock_); | |
| 728 available_shm_segments_.push_back(shm_buffer.release()); | |
| 729 } | |
| 730 | |
| 731 void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) { | |
| 732 input_buffer_data_.push_front(buffer_data); | |
| 733 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but | |
| 734 // that's too small for some pathological B-frame test videos. The cost of | |
| 735 // using too-high a value is low (192 bits per extra slot). | |
| 736 static const size_t kMaxInputBufferDataSize = 128; | |
| 737 // Pop from the back of the list, because that's the oldest and least likely | |
| 738 // to be useful in the future data. | |
| 739 if (input_buffer_data_.size() > kMaxInputBufferDataSize) | |
| 740 input_buffer_data_.pop_back(); | |
| 741 } | |
| 742 | |
| 743 void RTCVideoDecoder::GetBufferData(int32 bitstream_buffer_id, | |
| 744 uint32_t* timestamp, | |
| 745 uint32_t* width, | |
| 746 uint32_t* height, | |
| 747 size_t* size) { | |
| 748 for (std::list<BufferData>::iterator it = input_buffer_data_.begin(); | |
| 749 it != input_buffer_data_.end(); | |
| 750 ++it) { | |
| 751 if (it->bitstream_buffer_id != bitstream_buffer_id) | |
| 752 continue; | |
| 753 *timestamp = it->timestamp; | |
| 754 *width = it->width; | |
| 755 *height = it->height; | |
| 756 return; | |
| 757 } | |
| 758 NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; | |
| 759 } | |
| 760 | |
| 761 } // namespace content | |
| OLD | NEW |