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; | |
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_(ID_INVALID) { | |
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 base::AutoLock auto_lock(lock_); | |
192 if (state_ == UNINITIALIZED || decode_complete_callback_ == NULL) { | |
193 LOG(ERROR) << "The decoder has not initialized."; | |
194 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
195 } | |
196 if (state_ == DECODE_ERROR) { | |
197 LOG(ERROR) << "Decoding error occurred."; | |
198 return WEBRTC_VIDEO_CODEC_ERROR; | |
199 } | |
200 if (missingFrames || !inputImage._completeFrame) { | |
201 DLOG(ERROR) << "Missing or incomplete frames."; | |
202 // Unlike the SW decoder in libvpx, hw decoder cannot handle broken frames. | |
203 // Return an error to request a key frame. | |
204 return WEBRTC_VIDEO_CODEC_ERROR; | |
205 } | |
206 if (inputImage._frameType == webrtc::kKeyFrame) | |
207 frame_size_.SetSize(inputImage._encodedWidth, inputImage._encodedHeight); | |
208 | |
209 // Create buffer metadata. | |
210 BufferData buffer_data(next_bitstream_buffer_id_, | |
211 inputImage._timeStamp, | |
212 frame_size_.width(), | |
213 frame_size_.height(), | |
214 inputImage._length); | |
215 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. | |
216 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & ID_LAST; | |
217 | |
218 // If the shared memory is available and there are no pending buffers, send | |
219 // the buffer for decode. If not, save the buffer in the queue for decode | |
220 // later. | |
221 scoped_ptr<SHMBuffer> shm_buffer; | |
222 if (pending_buffers_.size() == 0) | |
223 shm_buffer = GetSHM_Locked(inputImage._length); | |
224 if (!shm_buffer) { | |
225 int32_t result = SaveToPendingBuffers_Locked(inputImage, buffer_data); | |
226 return result ? WEBRTC_VIDEO_CODEC_OK : WEBRTC_VIDEO_CODEC_ERROR; | |
227 } | |
228 | |
229 SaveToDecodeBuffers_Locked(inputImage, shm_buffer.Pass(), buffer_data); | |
230 vda_loop_proxy_->PostTask( | |
231 FROM_HERE, base::Bind(&RTCVideoDecoder::RequestBufferDecode, weak_this_)); | |
232 return WEBRTC_VIDEO_CODEC_OK; | |
233 } | |
234 | |
235 int32_t RTCVideoDecoder::RegisterDecodeCompleteCallback( | |
236 webrtc::DecodedImageCallback* callback) { | |
237 DVLOG(2) << "RegisterDecodeCompleteCallback"; | |
238 base::AutoLock auto_lock(lock_); | |
239 decode_complete_callback_ = callback; | |
240 return WEBRTC_VIDEO_CODEC_OK; | |
241 } | |
242 | |
243 int32_t RTCVideoDecoder::Release() { | |
244 DVLOG(2) << "Release"; | |
245 // Do not destroy VDA because the decoder will be recycled by | |
246 // RTCVideoDecoderFactory. Just reset VDA. | |
247 return Reset(); | |
248 } | |
249 | |
250 int32_t RTCVideoDecoder::Reset() { | |
251 DVLOG(2) << "Reset"; | |
252 base::AutoLock auto_lock(lock_); | |
253 if (state_ == UNINITIALIZED) { | |
254 LOG(ERROR) << "Decoder not initialized."; | |
255 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
256 } | |
257 if (next_bitstream_buffer_id_ != 0) | |
258 reset_bitstream_buffer_id_ = next_bitstream_buffer_id_ - 1; | |
259 else | |
260 reset_bitstream_buffer_id_ = ID_LAST; | |
261 // If VDA is already resetting, no need to request the reset again. | |
262 if (state_ != RESETTING) { | |
263 state_ = RESETTING; | |
264 vda_loop_proxy_->PostTask( | |
265 FROM_HERE, base::Bind(&RTCVideoDecoder::ResetInternal, weak_this_)); | |
266 } | |
267 return WEBRTC_VIDEO_CODEC_OK; | |
268 } | |
269 | |
270 void RTCVideoDecoder::NotifyInitializeDone() { | |
271 DVLOG(2) << "NotifyInitializeDone"; | |
272 NOTREACHED(); | |
273 } | |
274 | |
275 void RTCVideoDecoder::ProvidePictureBuffers(uint32 count, | |
276 const gfx::Size& size, | |
277 uint32 texture_target) { | |
278 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
279 DVLOG(3) << "ProvidePictureBuffers. texture_target=" << texture_target; | |
280 | |
281 if (!vda_) | |
282 return; | |
283 | |
284 std::vector<uint32> texture_ids; | |
285 std::vector<gpu::Mailbox> texture_mailboxes; | |
286 decoder_texture_target_ = texture_target; | |
287 // Discards the sync point returned here since PictureReady will imply that | |
288 // the produce has already happened, and the texture is ready for use. | |
289 if (!factories_->CreateTextures(count, | |
290 size, | |
291 &texture_ids, | |
292 &texture_mailboxes, | |
293 decoder_texture_target_)) { | |
294 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
295 return; | |
296 } | |
297 DCHECK_EQ(count, texture_ids.size()); | |
298 DCHECK_EQ(count, texture_mailboxes.size()); | |
299 | |
300 std::vector<media::PictureBuffer> picture_buffers; | |
301 for (size_t i = 0; i < texture_ids.size(); ++i) { | |
302 picture_buffers.push_back(media::PictureBuffer( | |
303 next_picture_buffer_id_++, size, texture_ids[i], texture_mailboxes[i])); | |
304 bool inserted = assigned_picture_buffers_.insert(std::make_pair( | |
305 picture_buffers.back().id(), picture_buffers.back())).second; | |
306 DCHECK(inserted); | |
307 } | |
308 vda_->AssignPictureBuffers(picture_buffers); | |
309 } | |
310 | |
311 void RTCVideoDecoder::DismissPictureBuffer(int32 id) { | |
312 DVLOG(3) << "DismissPictureBuffer. id=" << id; | |
313 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
314 | |
315 std::map<int32, media::PictureBuffer>::iterator it = | |
316 assigned_picture_buffers_.find(id); | |
317 if (it == assigned_picture_buffers_.end()) { | |
318 NOTREACHED() << "Missing picture buffer: " << id; | |
319 return; | |
320 } | |
321 | |
322 media::PictureBuffer buffer_to_dismiss = it->second; | |
323 assigned_picture_buffers_.erase(it); | |
324 | |
325 std::set<int32>::iterator at_display_it = | |
326 picture_buffers_at_display_.find(id); | |
327 | |
328 if (at_display_it == picture_buffers_at_display_.end()) { | |
329 // We can delete the texture immediately as it's not being displayed. | |
330 factories_->DeleteTexture(buffer_to_dismiss.texture_id()); | |
331 } else { | |
332 // Texture in display. Postpone deletion until after it's returned to us. | |
333 bool inserted = dismissed_picture_buffers_ | |
334 .insert(std::make_pair(id, buffer_to_dismiss)).second; | |
335 DCHECK(inserted); | |
336 } | |
337 } | |
338 | |
339 void RTCVideoDecoder::PictureReady(const media::Picture& picture) { | |
340 DVLOG(3) << "PictureReady"; | |
341 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
342 | |
343 std::map<int32, media::PictureBuffer>::iterator it = | |
344 assigned_picture_buffers_.find(picture.picture_buffer_id()); | |
345 if (it == assigned_picture_buffers_.end()) { | |
346 NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id(); | |
347 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
348 return; | |
349 } | |
350 const media::PictureBuffer& pb = it->second; | |
351 | |
352 // Create a media::VideoFrame. | |
353 uint32_t timestamp = 0, width = 0, height = 0; | |
354 size_t size = 0; | |
355 GetBufferData( | |
356 picture.bitstream_buffer_id(), ×tamp, &width, &height, &size); | |
357 scoped_refptr<media::VideoFrame> frame = | |
358 CreateVideoFrame(picture, pb, timestamp, width, height, size); | |
359 bool inserted = | |
360 picture_buffers_at_display_.insert(picture.picture_buffer_id()).second; | |
361 DCHECK(inserted); | |
362 | |
363 // Create a WebRTC video frame. | |
364 webrtc::RefCountImpl<NativeHandleImpl>* handle = | |
365 new webrtc::RefCountImpl<NativeHandleImpl>(); | |
366 handle->SetHandle(frame.get()); | |
367 webrtc::TextureVideoFrame decoded_image(width, height, timestamp, 0, handle); | |
368 | |
369 // Invoke decode callback. WebRTC expects no frame callback after Release. | |
370 { | |
371 base::AutoLock auto_lock(lock_); | |
372 DCHECK(decode_complete_callback_ != NULL); | |
373 if (IsBufferAfterReset(picture.bitstream_buffer_id(), | |
374 reset_bitstream_buffer_id_)) { | |
375 decode_complete_callback_->Decoded(decoded_image); | |
376 } | |
377 } | |
378 } | |
379 | |
380 scoped_refptr<media::VideoFrame> RTCVideoDecoder::CreateVideoFrame( | |
381 const media::Picture& picture, | |
382 const media::PictureBuffer& pb, | |
383 uint32_t timestamp, | |
384 uint32_t width, | |
385 uint32_t height, | |
386 size_t size) { | |
387 gfx::Rect visible_rect(width, height); | |
388 gfx::Size natural_size(width, height); | |
389 DCHECK(decoder_texture_target_); | |
390 // Convert timestamp from 90KHz to ms. | |
391 base::TimeDelta timestamp_ms = base::TimeDelta::FromInternalValue( | |
392 base::checked_numeric_cast<uint64_t>(timestamp) * 1000 / 90); | |
393 return media::VideoFrame::WrapNativeTexture( | |
394 new media::VideoFrame::MailboxHolder( | |
395 pb.texture_mailbox(), | |
396 0, // sync_point | |
397 media::BindToCurrentLoop( | |
398 base::Bind(&RTCVideoDecoder::ReusePictureBuffer, | |
399 weak_this_, | |
400 picture.picture_buffer_id()))), | |
401 decoder_texture_target_, | |
402 pb.size(), | |
403 visible_rect, | |
404 natural_size, | |
405 timestamp_ms, | |
406 base::Bind(&media::GpuVideoDecoder::Factories::ReadPixels, | |
407 factories_, | |
408 pb.texture_id(), | |
409 decoder_texture_target_, | |
410 natural_size), | |
411 base::Closure()); | |
412 } | |
413 | |
414 void RTCVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { | |
415 DVLOG(3) << "NotifyEndOfBitstreamBuffer. id=" << id; | |
416 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
417 | |
418 std::map<int32, SHMBuffer*>::iterator it = | |
419 bitstream_buffers_in_decoder_.find(id); | |
420 if (it == bitstream_buffers_in_decoder_.end()) { | |
421 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
422 NOTREACHED() << "Missing bitstream buffer: " << id; | |
423 return; | |
424 } | |
425 | |
426 { | |
427 base::AutoLock auto_lock(lock_); | |
428 PutSHM_Locked(scoped_ptr<SHMBuffer>(it->second)); | |
429 } | |
430 bitstream_buffers_in_decoder_.erase(it); | |
431 | |
432 RequestBufferDecode(); | |
433 } | |
434 | |
435 void RTCVideoDecoder::NotifyFlushDone() { | |
436 DVLOG(3) << "NotifyFlushDone"; | |
437 NOTREACHED() << "Unexpected flush done notification."; | |
438 } | |
439 | |
440 void RTCVideoDecoder::NotifyResetDone() { | |
441 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
442 DVLOG(3) << "NotifyResetDone"; | |
443 | |
444 if (!vda_) | |
445 return; | |
446 | |
447 input_buffer_data_.clear(); | |
448 { | |
449 base::AutoLock auto_lock(lock_); | |
450 state_ = INITIALIZED; | |
451 } | |
452 // Send the pending buffers for decoding. | |
453 RequestBufferDecode(); | |
454 } | |
455 | |
456 void RTCVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { | |
457 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
458 if (!vda_) | |
459 return; | |
460 | |
461 LOG(ERROR) << "VDA Error:" << error; | |
462 DestroyVDA(); | |
463 | |
464 base::AutoLock auto_lock(lock_); | |
465 state_ = DECODE_ERROR; | |
466 } | |
467 | |
468 void RTCVideoDecoder::WillDestroyCurrentMessageLoop() { | |
469 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
470 factories_->Abort(); | |
471 weak_factory_.InvalidateWeakPtrs(); | |
472 DestroyVDA(); | |
473 } | |
474 | |
475 void RTCVideoDecoder::Initialize(base::WaitableEvent* waiter) { | |
476 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
477 base::MessageLoop::current()->AddDestructionObserver(this); | |
478 weak_this_ = weak_factory_.GetWeakPtr(); | |
479 create_shm_thread_.Start(); | |
480 waiter->Signal(); | |
481 } | |
482 | |
483 void RTCVideoDecoder::RequestBufferDecode() { | |
484 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
485 if (!vda_) | |
486 return; | |
487 | |
488 MovePendingBuffersToDecodeBuffers(); | |
489 | |
490 while (CanMoreDecodeWorkBeDone()) { | |
491 // Get a buffer and data from the queue. | |
492 std::pair<SHMBuffer*, BufferData>* buffer_pair; | |
493 SHMBuffer* shm_buffer = NULL; | |
494 BufferData* buffer_data = NULL; | |
495 { | |
496 base::AutoLock auto_lock(lock_); | |
497 // Do not request decode if VDA is resetting. | |
498 if (decode_buffers_.size() == 0 || state_ == RESETTING) | |
499 return; | |
500 buffer_pair = &decode_buffers_.front(); | |
501 decode_buffers_.pop_front(); | |
502 shm_buffer = buffer_pair->first; | |
503 buffer_data = &buffer_pair->second; | |
504 // Drop the buffers before Reset or Release is called. | |
505 if (!IsBufferAfterReset(buffer_data->bitstream_buffer_id, | |
506 reset_bitstream_buffer_id_)) { | |
507 PutSHM_Locked(scoped_ptr<SHMBuffer>(shm_buffer)); | |
508 continue; | |
509 } | |
510 } | |
511 | |
512 // Create a BitstreamBuffer and send to VDA to decode. | |
513 media::BitstreamBuffer bitstream_buffer(buffer_data->bitstream_buffer_id, | |
514 shm_buffer->shm->handle(), | |
515 buffer_data->size); | |
516 bool inserted = bitstream_buffers_in_decoder_ | |
517 .insert(std::make_pair(bitstream_buffer.id(), shm_buffer)).second; | |
518 DCHECK(inserted); | |
519 RecordBufferData(*buffer_data); | |
520 vda_->Decode(bitstream_buffer); | |
521 } | |
522 } | |
523 | |
524 bool RTCVideoDecoder::CanMoreDecodeWorkBeDone() { | |
525 return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes; | |
526 } | |
527 | |
528 bool RTCVideoDecoder::IsBufferAfterReset(int32 id_buffer, int32 id_reset) { | |
529 if (id_reset == ID_INVALID) | |
530 return true; | |
531 int32 diff = id_buffer - id_reset; | |
532 if (diff <= 0) | |
533 diff += ID_LAST + 1; | |
534 return diff < ID_HALF; | |
Ami GONE FROM CHROMIUM
2013/07/03 16:45:57
Do you think this would be more easily understanda
wuchengli
2013/07/04 15:46:34
This needs a little modification because diff==0 s
| |
535 } | |
536 | |
537 void RTCVideoDecoder::SaveToDecodeBuffers_Locked( | |
538 const webrtc::EncodedImage& input_image, | |
539 scoped_ptr<SHMBuffer> shm_buffer, | |
540 const BufferData& buffer_data) { | |
541 memcpy(shm_buffer->shm->memory(), input_image._buffer, input_image._length); | |
542 std::pair<SHMBuffer*, BufferData> buffer_pair = | |
543 std::make_pair(shm_buffer.release(), buffer_data); | |
544 | |
545 // Store the buffer and the metadata to the queue. | |
546 decode_buffers_.push_back(buffer_pair); | |
547 } | |
548 | |
549 bool RTCVideoDecoder::SaveToPendingBuffers_Locked( | |
550 const webrtc::EncodedImage& input_image, | |
551 const BufferData& buffer_data) { | |
552 DVLOG(2) << "SaveToPendingBuffers_Locked" | |
553 << ". pending_buffers size=" << pending_buffers_.size() | |
554 << ". decode_buffers_ size=" << decode_buffers_.size() | |
555 << ". available_shm size=" << available_shm_segments_.size(); | |
556 // Queued too many buffers. Something goes wrong. | |
557 if (pending_buffers_.size() >= kMaxNumOfPendingBuffers) { | |
558 LOG(WARNING) << "Too many pending buffers!"; | |
559 return false; | |
560 } | |
561 | |
562 // Clone the input image and save it to the queue. | |
563 uint8_t* buffer = static_cast<uint8_t*>(malloc(input_image._length)); | |
564 // TODO(wuchengli): avoid memcpy. Extend webrtc::VideoDecoder::Decode() | |
565 // interface to take a non-const ptr to the frame and add a method to the | |
566 // frame that will swap buffers with another. | |
567 memcpy(buffer, input_image._buffer, input_image._length); | |
568 webrtc::EncodedImage encoded_image( | |
569 buffer, input_image._length, input_image._length); | |
570 std::pair<webrtc::EncodedImage, BufferData> buffer_pair = | |
571 std::make_pair(encoded_image, buffer_data); | |
572 | |
573 pending_buffers_.push_back(buffer_pair); | |
574 return true; | |
575 } | |
576 | |
577 void RTCVideoDecoder::MovePendingBuffersToDecodeBuffers() { | |
578 base::AutoLock auto_lock(lock_); | |
579 while (pending_buffers_.size() > 0) { | |
580 // Get a pending buffer from the queue. | |
581 const webrtc::EncodedImage& input_image = pending_buffers_.front().first; | |
582 const BufferData& buffer_data = pending_buffers_.front().second; | |
583 | |
584 // Drop the frame if it comes before Reset or Release. | |
585 if (!IsBufferAfterReset(buffer_data.bitstream_buffer_id, | |
586 reset_bitstream_buffer_id_)) { | |
587 delete input_image._buffer; | |
588 pending_buffers_.pop_front(); | |
589 continue; | |
590 } | |
591 // Get shared memory and save it to decode buffers. | |
592 scoped_ptr<SHMBuffer> shm_buffer = GetSHM_Locked(input_image._length); | |
593 if (!shm_buffer) | |
594 return; | |
595 SaveToDecodeBuffers_Locked(input_image, shm_buffer.Pass(), buffer_data); | |
596 delete input_image._buffer; | |
597 pending_buffers_.pop_front(); | |
598 } | |
599 } | |
600 | |
601 void RTCVideoDecoder::ResetInternal() { | |
602 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
603 DVLOG(2) << "ResetInternal"; | |
604 if (vda_) | |
605 vda_->Reset(); | |
606 } | |
607 | |
608 void RTCVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id, | |
609 uint32 sync_point) { | |
610 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
611 DVLOG(3) << "ReusePictureBuffer. id=" << picture_buffer_id; | |
612 | |
613 if (!vda_) | |
614 return; | |
615 | |
616 CHECK(!picture_buffers_at_display_.empty()); | |
617 | |
618 size_t num_erased = picture_buffers_at_display_.erase(picture_buffer_id); | |
619 DCHECK(num_erased); | |
620 | |
621 std::map<int32, media::PictureBuffer>::iterator it = | |
622 assigned_picture_buffers_.find(picture_buffer_id); | |
623 | |
624 if (it == assigned_picture_buffers_.end()) { | |
625 // This picture was dismissed while in display, so we postponed deletion. | |
626 it = dismissed_picture_buffers_.find(picture_buffer_id); | |
627 DCHECK(it != dismissed_picture_buffers_.end()); | |
628 factories_->DeleteTexture(it->second.texture_id()); | |
629 dismissed_picture_buffers_.erase(it); | |
630 return; | |
631 } | |
632 | |
633 factories_->WaitSyncPoint(sync_point); | |
634 | |
635 vda_->ReusePictureBuffer(picture_buffer_id); | |
636 } | |
637 | |
638 void RTCVideoDecoder::DestroyTextures() { | |
639 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
640 std::map<int32, media::PictureBuffer>::iterator it; | |
641 | |
642 for (it = assigned_picture_buffers_.begin(); | |
643 it != assigned_picture_buffers_.end(); | |
644 ++it) { | |
645 factories_->DeleteTexture(it->second.texture_id()); | |
646 } | |
647 assigned_picture_buffers_.clear(); | |
648 | |
649 for (it = dismissed_picture_buffers_.begin(); | |
650 it != dismissed_picture_buffers_.end(); | |
651 ++it) { | |
652 factories_->DeleteTexture(it->second.texture_id()); | |
653 } | |
654 dismissed_picture_buffers_.clear(); | |
655 } | |
656 | |
657 void RTCVideoDecoder::DestroyVDA() { | |
658 DVLOG(2) << "DestroyVDA"; | |
659 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
660 if (vda_) | |
661 vda_.release()->Destroy(); | |
662 DestroyTextures(); | |
663 } | |
664 | |
665 scoped_ptr<RTCVideoDecoder::SHMBuffer> RTCVideoDecoder::GetSHM_Locked( | |
666 size_t min_size) { | |
667 // Reuse a SHM if possible. | |
668 SHMBuffer* ret = NULL; | |
669 if (!available_shm_segments_.empty() && | |
670 available_shm_segments_.back()->size >= min_size) { | |
671 ret = available_shm_segments_.back(); | |
672 available_shm_segments_.pop_back(); | |
673 } | |
674 // Post to the child thread to create shared memory if SHM cannot be reused | |
675 // or the queue is almost empty. | |
676 if (num_shm_buffers_ < kMaxNumSharedMemorySegments && | |
677 (ret == NULL || available_shm_segments_.size() <= 1)) { | |
678 create_shm_thread_.message_loop_proxy()->PostTask( | |
679 FROM_HERE, | |
680 base::Bind(&RTCVideoDecoder::CreateSHM, weak_this_, 1, min_size)); | |
681 } | |
682 return scoped_ptr<SHMBuffer>(ret); | |
683 } | |
684 | |
685 void RTCVideoDecoder::PutSHM_Locked(scoped_ptr<SHMBuffer> shm_buffer) { | |
686 available_shm_segments_.push_back(shm_buffer.release()); | |
687 } | |
688 | |
689 void RTCVideoDecoder::CreateSHM(int number, size_t min_size) { | |
690 DCHECK(create_shm_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
691 DVLOG(2) << "CreateSharedMemory. size=" << min_size; | |
692 int number_to_allocate; | |
693 { | |
694 base::AutoLock auto_lock(lock_); | |
695 number_to_allocate = | |
696 std::min(kMaxNumSharedMemorySegments - num_shm_buffers_, number); | |
697 } | |
698 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); | |
699 for (int i = 0; i < number_to_allocate; i++) { | |
700 base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate); | |
701 if (shm != NULL) { | |
702 base::AutoLock auto_lock(lock_); | |
703 num_shm_buffers_++; | |
704 PutSHM_Locked( | |
705 scoped_ptr<SHMBuffer>(new SHMBuffer(shm, size_to_allocate))); | |
706 // Kick off the decoding. | |
707 vda_loop_proxy_->PostTask( | |
708 FROM_HERE, | |
709 base::Bind(&RTCVideoDecoder::RequestBufferDecode, weak_this_)); | |
710 } | |
711 } | |
712 } | |
713 | |
714 void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) { | |
715 input_buffer_data_.push_front(buffer_data); | |
716 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but | |
717 // that's too small for some pathological B-frame test videos. The cost of | |
718 // using too-high a value is low (192 bits per extra slot). | |
719 static const size_t kMaxInputBufferDataSize = 128; | |
720 // Pop from the back of the list, because that's the oldest and least likely | |
721 // to be useful in the future data. | |
722 if (input_buffer_data_.size() > kMaxInputBufferDataSize) | |
723 input_buffer_data_.pop_back(); | |
724 } | |
725 | |
726 void RTCVideoDecoder::GetBufferData(int32 bitstream_buffer_id, | |
727 uint32_t* timestamp, | |
728 uint32_t* width, | |
729 uint32_t* height, | |
730 size_t* size) { | |
731 for (std::list<BufferData>::iterator it = input_buffer_data_.begin(); | |
732 it != input_buffer_data_.end(); | |
733 ++it) { | |
734 if (it->bitstream_buffer_id != bitstream_buffer_id) | |
735 continue; | |
736 *timestamp = it->timestamp; | |
737 *width = it->width; | |
738 *height = it->height; | |
739 return; | |
740 } | |
741 NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; | |
742 } | |
743 | |
744 } // namespace content | |
OLD | NEW |