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/callback_helpers.h" | |
9 #include "base/logging.h" | |
10 #include "base/memory/ref_counted.h" | |
11 #include "base/message_loop_proxy.h" | |
12 #include "base/safe_numerics.h" | |
13 #include "base/task_runner_util.h" | |
14 #include "content/renderer/media/native_handle_impl.h" | |
15 #include "media/base/bind_to_loop.h" | |
16 #include "third_party/webrtc/system_wrappers/interface/ref_count.h" | |
17 | |
18 namespace content { | |
19 | |
20 // A shared memory segment and its allocated size. |shm| is unowned and the | |
21 // users should delete it. | |
22 struct RTCVideoDecoder::SHMBuffer { | |
23 SHMBuffer(base::SharedMemory* shm, size_t size); | |
24 ~SHMBuffer(); | |
25 base::SharedMemory* const shm; | |
26 const size_t size; | |
27 }; | |
28 | |
29 RTCVideoDecoder::SHMBuffer::SHMBuffer(base::SharedMemory* shm, size_t size) | |
30 : shm(shm), size(size) {} | |
31 | |
32 RTCVideoDecoder::SHMBuffer::~SHMBuffer() {} | |
33 | |
34 // Metadata of a bitstream buffer. | |
35 struct RTCVideoDecoder::BufferData { | |
36 BufferData(int32 bitstream_buffer_id, | |
37 uint32_t timestamp, | |
38 int width, | |
39 int height, | |
40 size_t sisze); | |
41 ~BufferData(); | |
42 const int32 bitstream_buffer_id; | |
43 const uint32_t timestamp; // in 90KHz | |
44 const uint32_t width; | |
45 const uint32_t height; | |
46 const size_t size; // buffer size | |
47 }; | |
48 | |
49 RTCVideoDecoder::BufferData::BufferData(int32 bitstream_buffer_id, | |
50 uint32_t timestamp, | |
51 int width, | |
52 int height, | |
53 size_t size) | |
54 : bitstream_buffer_id(bitstream_buffer_id), | |
55 timestamp(timestamp), | |
56 width(width), | |
57 height(height), | |
58 size(size) {} | |
59 | |
60 RTCVideoDecoder::BufferData::~BufferData() {} | |
61 | |
62 RTCVideoDecoder::RTCVideoDecoder( | |
63 const scoped_refptr<media::GpuVideoDecoder::Factories>& factories) | |
64 : compositor_loop_async_waiter_(false, false), | |
65 state_(UNINITIALIZED), | |
66 decode_complete_callback_(NULL), | |
67 weak_factory_(this), | |
68 factories_(factories), | |
69 vda_loop_proxy_(factories_->GetMessageLoop()), | |
70 decoder_texture_target_(0), | |
71 next_picture_buffer_id_(0), | |
72 next_bitstream_buffer_id_(0) { | |
73 // Initialize directly if |vda_loop_proxy_| is the renderer thread. | |
74 if (vda_loop_proxy_->BelongsToCurrentThread()) { | |
75 Initialize(); | |
76 compositor_loop_async_waiter_.Reset(); | |
77 return; | |
78 } | |
79 // Post the task if |vda_loop_proxy_| is the compositor thread. Waiting here | |
80 // is safe because the compositor thread will not be stopped until the | |
81 // renderer thread shuts down. | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
This means that you don't actually need the waiter
wuchengli
2013/06/19 13:01:13
Done.
| |
82 vda_loop_proxy_->PostTask( | |
83 FROM_HERE, | |
84 base::Bind(&RTCVideoDecoder::Initialize, base::Unretained(this))); | |
85 compositor_loop_async_waiter_.Wait(); | |
86 } | |
87 | |
88 RTCVideoDecoder::~RTCVideoDecoder() { | |
89 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
90 DCHECK(!vda_); // Release should have been already called. | |
91 DVLOG(2) << "~RTCVideoDecoder"; | |
92 // Delete all shared memories. | |
93 for (size_t i = 0; i < available_shm_segments_.size(); ++i) { | |
94 available_shm_segments_[i]->shm->Close(); | |
95 delete available_shm_segments_[i]; | |
96 } | |
97 available_shm_segments_.clear(); | |
98 for (std::map<int32, SHMBuffer*>::iterator it = | |
99 bitstream_buffers_in_decoder_.begin(); | |
100 it != bitstream_buffers_in_decoder_.end(); | |
101 ++it) { | |
102 it->second->shm->Close(); | |
103 delete it->second; | |
104 } | |
105 bitstream_buffers_in_decoder_.clear(); | |
106 ClearBufferQueue(buffers_to_be_decoded_); | |
107 ClearBufferQueue(buffers_delayed_); | |
108 | |
109 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
110 } | |
111 | |
112 bool RTCVideoDecoder::InitVideoDecodeAccelerator() { | |
113 vda_.reset( | |
114 factories_->CreateVideoDecodeAccelerator(media::VP8PROFILE_MAIN, this)); | |
115 | |
116 // vda can be NULL if the codec type is not supported. | |
117 if (vda_ != NULL) { | |
118 base::AutoLock auto_lock(lock_); | |
119 state_ = INITIALIZED; | |
120 return true; | |
121 } | |
122 return false; | |
123 } | |
124 | |
125 int32_t RTCVideoDecoder::InitDecode(const webrtc::VideoCodec* codecSettings, | |
126 int32_t /*numberOfCores*/) { | |
127 DVLOG(2) << "InitDecode"; | |
128 DCHECK_EQ(codecSettings->codecType, webrtc::kVideoCodecVP8); | |
129 if (codecSettings->codecSpecific.VP8.feedbackModeOn) { | |
130 LOG(ERROR) << "Feedback mode not supported"; | |
131 return WEBRTC_VIDEO_CODEC_ERROR; | |
132 } | |
133 | |
134 base::AutoLock auto_lock(lock_); | |
135 if (state_ == UNINITIALIZED || state_ == DECODE_ERROR) { | |
136 LOG(ERROR) << "VDA is not initialized. state=" << state_; | |
137 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
138 } | |
139 return WEBRTC_VIDEO_CODEC_OK; | |
140 } | |
141 | |
142 int32_t RTCVideoDecoder::Decode( | |
143 const webrtc::EncodedImage& inputImage, | |
144 bool missingFrames, | |
145 const webrtc::RTPFragmentationHeader* /*fragmentation*/, | |
146 const webrtc::CodecSpecificInfo* /*codecSpecificInfo*/, | |
147 int64_t /*renderTimeMs*/) { | |
148 DVLOG(3) << "Decode"; | |
149 | |
150 { | |
151 base::AutoLock auto_lock(lock_); | |
152 if (state_ == UNINITIALIZED || decode_complete_callback_ == NULL) { | |
153 LOG(ERROR) << "The decoder has not initialized."; | |
154 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
155 } | |
156 if (state_ == DECODE_ERROR) { | |
157 LOG(ERROR) << "Decoding error occurred."; | |
158 return WEBRTC_VIDEO_CODEC_ERROR; | |
159 } | |
160 } | |
161 if (missingFrames || !inputImage._completeFrame) { | |
162 DLOG(ERROR) << "Missing or incomplete frames."; | |
163 // Unlike the SW decoder in libvpx, hw decoder cannot handle broken frames. | |
164 // Return an error to request a key frame. | |
165 return WEBRTC_VIDEO_CODEC_ERROR; | |
166 } | |
167 | |
168 if (inputImage._frameType == webrtc::kKeyFrame) | |
169 frame_size_.SetSize(inputImage._encodedWidth, inputImage._encodedHeight); | |
170 | |
171 // Copy WebRTC buffer to SHM buffer and create buffer metadata. | |
172 SHMBuffer* shm_buffer = GetSHM(inputImage._length); | |
173 if (!shm_buffer) | |
174 return WEBRTC_VIDEO_CODEC_ERROR; | |
175 memcpy(shm_buffer->shm->memory(), inputImage._buffer, inputImage._length); | |
176 BufferData buffer_data(next_bitstream_buffer_id_, | |
177 inputImage._timeStamp, | |
178 frame_size_.width(), | |
179 frame_size_.height(), | |
180 inputImage._length); | |
181 // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. | |
182 next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF; | |
183 std::pair<SHMBuffer*, BufferData> buffer_pair = | |
184 std::make_pair(shm_buffer, buffer_data); | |
185 | |
186 // Store the buffer and the metadata to the queue. | |
187 base::AutoLock auto_lock(lock_); | |
188 if (state_ == RESETTING) { | |
189 // If VDA is resetting, save the buffer but do not request decode. | |
190 buffers_delayed_.push_back(buffer_pair); | |
191 } else { | |
192 buffers_to_be_decoded_.push_back(buffer_pair); | |
193 vda_loop_proxy_->PostTask( | |
194 FROM_HERE, | |
195 base::Bind(&RTCVideoDecoder::RequestBufferDecode, weak_this_)); | |
196 } | |
197 return WEBRTC_VIDEO_CODEC_OK; | |
198 } | |
199 | |
200 int32_t RTCVideoDecoder::RegisterDecodeCompleteCallback( | |
201 webrtc::DecodedImageCallback* callback) { | |
202 base::AutoLock auto_lock(lock_); | |
203 decode_complete_callback_ = callback; | |
204 return WEBRTC_VIDEO_CODEC_OK; | |
205 } | |
206 | |
207 int32_t RTCVideoDecoder::Release() { | |
208 DVLOG(2) << "Release"; | |
209 // Do not destroy VDA because the decoder will be recycled by | |
210 // RTCVideoDecoderFactory. Just reset VDA. | |
211 return Reset(); | |
212 } | |
213 | |
214 int32_t RTCVideoDecoder::Reset() { | |
215 DVLOG(2) << "Reset"; | |
216 base::AutoLock auto_lock(lock_); | |
217 if (state_ == UNINITIALIZED) { | |
218 LOG(ERROR) << "Decoder not initialized."; | |
219 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | |
220 } else if (state_ == RESETTING) { | |
221 // If VDA is already resetting, empty the pending buffer queue. No need to | |
222 // request the reset again. | |
223 while (buffers_delayed_.size() > 0) { | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
l.223-226 is:
buffers_to_be_decoded_.insert(buffer
wuchengli
2013/06/19 13:01:13
Done. I had to remove const in BufferData because
| |
224 buffers_to_be_decoded_.push_back(buffers_delayed_.front()); | |
225 buffers_delayed_.pop_front(); | |
226 } | |
227 } else { | |
228 state_ = RESETTING; | |
229 vda_loop_proxy_->PostTask( | |
230 FROM_HERE, base::Bind(&RTCVideoDecoder::ResetInternal, weak_this_)); | |
231 } | |
232 return WEBRTC_VIDEO_CODEC_OK; | |
233 } | |
234 | |
235 void RTCVideoDecoder::NotifyInitializeDone() { | |
236 DVLOG(2) << "NotifyInitializeDone"; | |
237 NOTREACHED(); | |
238 } | |
239 | |
240 void RTCVideoDecoder::ProvidePictureBuffers(uint32 count, | |
241 const gfx::Size& size, | |
242 uint32 texture_target) { | |
243 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
244 DVLOG(3) << "ProvidePictureBuffers. texture_target=" << texture_target; | |
245 std::vector<uint32> texture_ids; | |
246 decoder_texture_target_ = texture_target; | |
247 if (!factories_->CreateTextures( | |
248 count, size, &texture_ids, decoder_texture_target_)) { | |
249 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
250 return; | |
251 } | |
252 DCHECK_EQ(count, texture_ids.size()); | |
253 | |
254 if (!vda_) | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
why not do this at the top of the method?
wuchengli
2013/06/19 13:01:13
Done.
GpuVideoDecoder puts it in the same place.
| |
255 return; | |
256 | |
257 std::vector<media::PictureBuffer> picture_buffers; | |
258 for (size_t i = 0; i < texture_ids.size(); ++i) { | |
259 picture_buffers.push_back( | |
260 media::PictureBuffer(next_picture_buffer_id_++, size, texture_ids[i])); | |
261 bool inserted = assigned_picture_buffers_.insert(std::make_pair( | |
262 picture_buffers.back().id(), picture_buffers.back())).second; | |
263 DCHECK(inserted); | |
264 } | |
265 vda_->AssignPictureBuffers(picture_buffers); | |
266 } | |
267 | |
268 void RTCVideoDecoder::DismissPictureBuffer(int32 id) { | |
269 DVLOG(3) << "DismissPictureBuffer. id=" << id; | |
270 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
271 | |
272 std::map<int32, media::PictureBuffer>::iterator it = | |
273 assigned_picture_buffers_.find(id); | |
274 if (it == assigned_picture_buffers_.end()) { | |
275 NOTREACHED() << "Missing picture buffer: " << id; | |
276 return; | |
277 } | |
278 | |
279 media::PictureBuffer buffer_to_dismiss = it->second; | |
280 assigned_picture_buffers_.erase(it); | |
281 | |
282 std::set<int32>::iterator at_display_it = | |
283 picture_buffers_at_display_.find(id); | |
284 | |
285 if (at_display_it == picture_buffers_at_display_.end()) { | |
286 // We can delete the texture immediately as it's not being displayed. | |
287 factories_->DeleteTexture(buffer_to_dismiss.texture_id()); | |
288 } else { | |
289 // Texture in display. Postpone deletion until after it's returned to us. | |
290 bool inserted = dismissed_picture_buffers_ | |
291 .insert(std::make_pair(id, buffer_to_dismiss)).second; | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
period goes on previous line
(git cl format is yo
wuchengli
2013/06/19 13:01:13
I did run git cl format. I completely follow its r
| |
292 DCHECK(inserted); | |
293 } | |
294 } | |
295 | |
296 void RTCVideoDecoder::PictureReady(const media::Picture& picture) { | |
297 DVLOG(3) << "PictureReady"; | |
298 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
299 | |
300 std::map<int32, media::PictureBuffer>::iterator it = | |
301 assigned_picture_buffers_.find(picture.picture_buffer_id()); | |
302 if (it == assigned_picture_buffers_.end()) { | |
303 NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id(); | |
304 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
305 return; | |
306 } | |
307 const media::PictureBuffer& pb = it->second; | |
308 | |
309 // Create a media::VideoFrame. | |
310 uint32_t timestamp = 0; | |
311 uint32_t width = 0, height = 0; | |
312 size_t size = 0; | |
313 GetBufferData( | |
314 picture.bitstream_buffer_id(), ×tamp, &width, &height, &size); | |
315 gfx::Rect visible_rect(width, height); | |
316 gfx::Size natural_size(width, height); | |
317 DCHECK(decoder_texture_target_); | |
318 // Convert timestamp from 90KHz to ms. | |
319 base::TimeDelta timestamp_ms = base::TimeDelta::FromInternalValue( | |
320 base::checked_numeric_cast<uint64_t>(timestamp) * 1000 / 90); | |
321 scoped_refptr<media::VideoFrame> frame(media::VideoFrame::WrapNativeTexture( | |
322 pb.texture_id(), | |
323 decoder_texture_target_, | |
324 pb.size(), | |
325 visible_rect, | |
326 natural_size, | |
327 timestamp_ms, | |
328 base::Bind(&media::GpuVideoDecoder::Factories::ReadPixels, | |
329 factories_, | |
330 pb.texture_id(), | |
331 decoder_texture_target_, | |
332 natural_size), | |
333 media::BindToCurrentLoop(base::Bind(&RTCVideoDecoder::ReusePictureBuffer, | |
334 weak_this_, | |
335 picture.picture_buffer_id())))); | |
336 bool inserted = | |
337 picture_buffers_at_display_.insert(picture.picture_buffer_id()).second; | |
338 DCHECK(inserted); | |
339 | |
340 // Create a webrtc::I420VideoFrame. | |
341 webrtc::I420VideoFrame decoded_image; | |
342 // TODO(wuchengli): remove the malloc. | |
343 decoded_image.CreateEmptyFrame(width, height, width, height / 2, width / 2); | |
344 webrtc::RefCountImpl<NativeHandleImpl>* handle = | |
345 new webrtc::RefCountImpl<NativeHandleImpl>(); | |
346 handle->SetHandle(frame.get()); | |
347 decoded_image.set_native_handle(handle); | |
348 decoded_image.set_timestamp(timestamp); | |
349 | |
350 // Send to decode callback. | |
351 webrtc::DecodedImageCallback* callback; | |
352 { | |
353 base::AutoLock auto_lock(lock_); | |
354 callback = decode_complete_callback_; | |
355 } | |
356 DCHECK(callback != NULL); | |
357 callback->Decoded(decoded_image); | |
358 } | |
359 | |
360 void RTCVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { | |
361 DVLOG(3) << "NotifyEndOfBitstreamBuffer. id=" << id; | |
362 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
363 | |
364 std::map<int32, SHMBuffer*>::iterator it = | |
365 bitstream_buffers_in_decoder_.find(id); | |
366 if (it == bitstream_buffers_in_decoder_.end()) { | |
367 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | |
368 NOTREACHED() << "Missing bitstream buffer: " << id; | |
369 return; | |
370 } | |
371 | |
372 PutSHM(it->second); | |
373 bitstream_buffers_in_decoder_.erase(it); | |
374 | |
375 RequestBufferDecode(); | |
376 } | |
377 | |
378 void RTCVideoDecoder::NotifyFlushDone() { | |
379 DVLOG(3) << "NotifyFlushDone"; | |
380 NOTREACHED() << "Unexpected flush done notification."; | |
381 } | |
382 | |
383 void RTCVideoDecoder::NotifyResetDone() { | |
384 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
385 DVLOG(3) << "NotifyResetDone"; | |
386 | |
387 if (!vda_) | |
388 return; | |
389 | |
390 input_buffer_data_.clear(); | |
391 int num_buffers = 0; | |
392 { | |
393 base::AutoLock auto_lock(lock_); | |
394 // Clear and recycle the old buffers. | |
395 for (std::deque<std::pair<SHMBuffer*, BufferData> >::const_iterator it = | |
396 buffers_to_be_decoded_.begin(); | |
397 it != buffers_to_be_decoded_.end(); | |
398 it++) { | |
399 PutSHM(it->first); | |
400 } | |
401 buffers_to_be_decoded_.clear(); | |
402 | |
403 swap(buffers_to_be_decoded_, buffers_delayed_); | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
std::swap() to avoid debugging pain down the line.
wuchengli
2013/06/19 13:01:13
Done.
| |
404 num_buffers = buffers_to_be_decoded_.size(); | |
405 state_ = INITIALIZED; | |
406 } | |
407 // Send the pending buffers for decoding. | |
408 for (int i = 0; i < num_buffers; i++) | |
409 RequestBufferDecode(); | |
410 } | |
411 | |
412 void RTCVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) { | |
413 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
414 if (!vda_) | |
415 return; | |
416 | |
417 DLOG(ERROR) << "VDA Error:" << error; | |
418 DestroyVDA(); | |
419 | |
420 base::AutoLock auto_lock(lock_); | |
421 state_ = DECODE_ERROR; | |
422 } | |
423 | |
424 void RTCVideoDecoder::WillDestroyCurrentMessageLoop() { | |
425 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
426 factories_->Abort(); | |
427 } | |
428 | |
429 void RTCVideoDecoder::Initialize() { | |
430 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
431 base::MessageLoop::current()->AddDestructionObserver(this); | |
432 weak_this_ = weak_factory_.GetWeakPtr(); | |
433 compositor_loop_async_waiter_.Signal(); | |
434 } | |
435 | |
436 void RTCVideoDecoder::RequestBufferDecode() { | |
437 if (!CanMoreDecodeWorkBeDone()) | |
438 return; | |
439 | |
440 // Get a buffer and data from the queue. | |
441 std::pair<SHMBuffer*, BufferData>* buffer_pair; | |
442 { | |
443 base::AutoLock auto_lock(lock_); | |
444 if (buffers_to_be_decoded_.size() == 0 || state_ == RESETTING) | |
445 return; | |
446 buffer_pair = &buffers_to_be_decoded_.front(); | |
447 buffers_to_be_decoded_.pop_front(); | |
448 } | |
449 SHMBuffer* shm_buffer = buffer_pair->first; | |
450 BufferData buffer_data = buffer_pair->second; | |
451 | |
452 // Create a BitstreamBuffer and send to VDA to decode. | |
453 media::BitstreamBuffer bitstream_buffer(buffer_data.bitstream_buffer_id, | |
454 shm_buffer->shm->handle(), | |
455 buffer_data.size); | |
456 bool inserted = bitstream_buffers_in_decoder_ | |
457 .insert(std::make_pair(bitstream_buffer.id(), shm_buffer)).second; | |
458 DCHECK(inserted); | |
459 RecordBufferData(buffer_data); | |
460 vda_->Decode(bitstream_buffer); | |
461 } | |
462 | |
463 // Maximum number of concurrent VDA::Decode() operations RVD will maintain. | |
464 // Higher values allow better pipelining in the GPU, but also require more | |
465 // resources. | |
466 enum { | |
467 kMaxInFlightDecodes = 8 | |
468 }; | |
469 | |
470 bool RTCVideoDecoder::CanMoreDecodeWorkBeDone() { | |
471 return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes; | |
472 } | |
473 | |
474 void RTCVideoDecoder::ResetInternal() { | |
475 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
476 DVLOG(2) << "ResetInternal"; | |
477 vda_->Reset(); | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
if (!vda_)
return;
first
wuchengli
2013/06/19 13:01:13
Done. I also added the check in RequestBufferDecod
| |
478 } | |
479 | |
480 void RTCVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) { | |
481 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
482 DVLOG(3) << "ReusePictureBuffer. id=" << picture_buffer_id; | |
483 | |
484 if (!vda_) | |
485 return; | |
486 | |
487 CHECK(!picture_buffers_at_display_.empty()); | |
488 | |
489 size_t num_erased = picture_buffers_at_display_.erase(picture_buffer_id); | |
490 DCHECK(num_erased); | |
491 | |
492 std::map<int32, media::PictureBuffer>::iterator it = | |
493 assigned_picture_buffers_.find(picture_buffer_id); | |
494 | |
495 if (it == assigned_picture_buffers_.end()) { | |
496 // This picture was dismissed while in display, so we postponed deletion. | |
497 it = dismissed_picture_buffers_.find(picture_buffer_id); | |
498 DCHECK(it != dismissed_picture_buffers_.end()); | |
499 factories_->DeleteTexture(it->second.texture_id()); | |
500 dismissed_picture_buffers_.erase(it); | |
501 return; | |
502 } | |
503 | |
504 vda_->ReusePictureBuffer(picture_buffer_id); | |
505 } | |
506 | |
507 void RTCVideoDecoder::DestroyTextures() { | |
508 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
509 std::map<int32, media::PictureBuffer>::iterator it; | |
510 | |
511 for (it = assigned_picture_buffers_.begin(); | |
512 it != assigned_picture_buffers_.end(); | |
513 ++it) { | |
514 factories_->DeleteTexture(it->second.texture_id()); | |
515 } | |
516 assigned_picture_buffers_.clear(); | |
517 | |
518 for (it = dismissed_picture_buffers_.begin(); | |
519 it != dismissed_picture_buffers_.end(); | |
520 ++it) { | |
521 factories_->DeleteTexture(it->second.texture_id()); | |
522 } | |
523 dismissed_picture_buffers_.clear(); | |
524 } | |
525 | |
526 void RTCVideoDecoder::DestroyVDA() { | |
527 DVLOG(2) << "DestroyVDA"; | |
528 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
529 if (vda_) | |
530 vda_.release()->Destroy(); | |
531 DestroyTextures(); | |
532 } | |
533 | |
534 void RTCVideoDecoder::ClearBufferQueue( | |
535 std::deque<std::pair<SHMBuffer*, BufferData> >& queue) { | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
pass by pointer
(never non-const ref in chromium s
wuchengli
2013/06/19 13:01:13
Done.
| |
536 base::AutoLock auto_lock(lock_); | |
Ami GONE FROM CHROMIUM
2013/06/18 21:54:29
It is a code-smell to take a member lock in a meth
wuchengli
2013/06/19 13:01:13
You are right. Done.
| |
537 for (std::deque<std::pair<SHMBuffer*, BufferData> >::iterator it = | |
538 queue.begin(); | |
539 it != queue.end(); | |
540 ++it) { | |
541 it->first->shm->Close(); | |
542 delete it->first; | |
543 } | |
544 queue.clear(); | |
545 } | |
546 | |
547 // Size of shared-memory segments we allocate. Since we reuse them we let them | |
548 // be on the beefy side. | |
549 static const size_t kSharedMemorySegmentBytes = 100 << 10; | |
550 | |
551 RTCVideoDecoder::SHMBuffer* RTCVideoDecoder::GetSHM(size_t min_size) { | |
552 { | |
553 // Reuse a SHM if possible. | |
554 base::AutoLock auto_lock(lock_); | |
555 if (!available_shm_segments_.empty() && | |
556 available_shm_segments_.back()->size >= min_size) { | |
557 SHMBuffer* ret = available_shm_segments_.back(); | |
558 available_shm_segments_.pop_back(); | |
559 return ret; | |
560 } | |
561 } | |
562 // Create a new shared memory. This is done in main thread. | |
563 size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes); | |
564 base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate); | |
565 if (!shm) | |
566 return NULL; | |
567 return new SHMBuffer(shm, size_to_allocate); | |
568 } | |
569 | |
570 void RTCVideoDecoder::PutSHM(SHMBuffer* shm_buffer) { | |
571 DCHECK(vda_loop_proxy_->BelongsToCurrentThread()); | |
572 base::AutoLock auto_lock(lock_); | |
573 available_shm_segments_.push_back(shm_buffer); | |
574 } | |
575 | |
576 void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) { | |
577 input_buffer_data_.push_front(buffer_data); | |
578 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but | |
579 // that's too small for some pathological B-frame test videos. The cost of | |
580 // using too-high a value is low (192 bits per extra slot). | |
581 static const size_t kMaxInputBufferDataSize = 128; | |
582 // Pop from the back of the list, because that's the oldest and least likely | |
583 // to be useful in the future data. | |
584 if (input_buffer_data_.size() > kMaxInputBufferDataSize) | |
585 input_buffer_data_.pop_back(); | |
586 } | |
587 | |
588 void RTCVideoDecoder::GetBufferData(int32 bitstream_buffer_id, | |
589 uint32_t* timestamp, | |
590 uint32_t* width, | |
591 uint32_t* height, | |
592 size_t* size) { | |
593 for (std::list<BufferData>::iterator it = input_buffer_data_.begin(); | |
594 it != input_buffer_data_.end(); | |
595 ++it) { | |
596 if (it->bitstream_buffer_id != bitstream_buffer_id) | |
597 continue; | |
598 *timestamp = it->timestamp; | |
599 *width = it->width; | |
600 *height = it->height; | |
601 return; | |
602 } | |
603 NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; | |
604 } | |
605 | |
606 } // namespace content | |
OLD | NEW |