OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 <CoreVideo/CoreVideo.h> | 5 #include <CoreVideo/CoreVideo.h> |
6 #include <OpenGL/CGLIOSurface.h> | 6 #include <OpenGL/CGLIOSurface.h> |
7 #include <OpenGL/gl.h> | 7 #include <OpenGL/gl.h> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 22 matching lines...) Expand all Loading... |
33 | 33 |
34 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | 34 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
35 static void OutputThunk( | 35 static void OutputThunk( |
36 void* decompression_output_refcon, | 36 void* decompression_output_refcon, |
37 void* source_frame_refcon, | 37 void* source_frame_refcon, |
38 OSStatus status, | 38 OSStatus status, |
39 VTDecodeInfoFlags info_flags, | 39 VTDecodeInfoFlags info_flags, |
40 CVImageBufferRef image_buffer, | 40 CVImageBufferRef image_buffer, |
41 CMTime presentation_time_stamp, | 41 CMTime presentation_time_stamp, |
42 CMTime presentation_duration) { | 42 CMTime presentation_duration) { |
43 // TODO(sandersd): Implement flush-before-delete to guarantee validity. | |
44 VTVideoDecodeAccelerator* vda = | 43 VTVideoDecodeAccelerator* vda = |
45 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); | 44 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
46 int32_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); | 45 int32_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); |
47 vda->Output(bitstream_id, status, image_buffer); | 46 vda->Output(bitstream_id, status, image_buffer); |
48 } | 47 } |
49 | 48 |
50 VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame( | 49 VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame( |
51 int32_t bitstream_id, | 50 int32_t bitstream_id, |
52 CVImageBufferRef image_buffer) | 51 CVImageBufferRef image_buffer) |
53 : bitstream_id(bitstream_id), | 52 : bitstream_id(bitstream_id), |
54 image_buffer(image_buffer) { | 53 image_buffer(image_buffer) { |
55 } | 54 } |
56 | 55 |
57 VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() { | 56 VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() { |
58 } | 57 } |
59 | 58 |
| 59 VTVideoDecodeAccelerator::PendingAction::PendingAction( |
| 60 Action action, |
| 61 int32_t bitstream_id) |
| 62 : action(action), |
| 63 bitstream_id(bitstream_id) { |
| 64 } |
| 65 |
| 66 VTVideoDecodeAccelerator::PendingAction::~PendingAction() { |
| 67 } |
| 68 |
60 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 69 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
61 : cgl_context_(cgl_context), | 70 : cgl_context_(cgl_context), |
62 client_(NULL), | 71 client_(NULL), |
63 format_(NULL), | 72 format_(NULL), |
64 session_(NULL), | 73 session_(NULL), |
65 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 74 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
66 weak_this_factory_(this), | 75 weak_this_factory_(this), |
67 decoder_thread_("VTDecoderThread") { | 76 decoder_thread_("VTDecoderThread") { |
68 callback_.decompressionOutputCallback = OutputThunk; | 77 callback_.decompressionOutputCallback = OutputThunk; |
69 callback_.decompressionOutputRefCon = this; | 78 callback_.decompressionOutputRefCon = this; |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); | 162 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
154 #undef CFINT | 163 #undef CFINT |
155 CFDictionarySetValue( | 164 CFDictionarySetValue( |
156 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | 165 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
157 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | 166 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
158 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | 167 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
159 CFDictionarySetValue( | 168 CFDictionarySetValue( |
160 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | 169 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
161 | 170 |
162 // TODO(sandersd): Check if the session is already compatible. | 171 // TODO(sandersd): Check if the session is already compatible. |
163 // TODO(sandersd): Flush. | |
164 session_.reset(); | 172 session_.reset(); |
165 CHECK(!VTDecompressionSessionCreate( | 173 CHECK(!VTDecompressionSessionCreate( |
166 kCFAllocatorDefault, | 174 kCFAllocatorDefault, |
167 format_, // video_format_description | 175 format_, // video_format_description |
168 decoder_config, // video_decoder_specification | 176 decoder_config, // video_decoder_specification |
169 image_config, // destination_image_buffer_attributes | 177 image_config, // destination_image_buffer_attributes |
170 &callback_, // output_callback | 178 &callback_, // output_callback |
171 session_.InitializeInto())); | 179 session_.InitializeInto())); |
172 | 180 |
173 // If the size has changed, trigger a request for new picture buffers. | 181 // If the size has changed, trigger a request for new picture buffers. |
| 182 // TODO(sandersd): Move to SendPictures(), and use this just as a hint for an |
| 183 // upcoming size change. |
174 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); | 184 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); |
175 if (coded_size_ != new_coded_size) { | 185 if (coded_size_ != new_coded_size) { |
176 coded_size_ = new_coded_size; | 186 coded_size_ = new_coded_size; |
177 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 187 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
178 &VTVideoDecodeAccelerator::SizeChangedTask, | 188 &VTVideoDecodeAccelerator::SizeChangedTask, |
179 weak_this_factory_.GetWeakPtr(), | 189 weak_this_factory_.GetWeakPtr(), |
180 coded_size_));; | 190 coded_size_));; |
181 } | 191 } |
182 } | 192 } |
183 | 193 |
184 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 194 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
185 DCHECK(CalledOnValidThread()); | 195 DCHECK(CalledOnValidThread()); |
186 // TODO(sandersd): Test what happens if bitstream buffers are passed to VT out | 196 CHECK_GE(bitstream.id(), 0) << "Negative bitstream_id"; |
187 // of order. | 197 pending_bitstream_ids_.push(bitstream.id()); |
188 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | 198 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
189 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), | 199 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
190 bitstream)); | 200 bitstream)); |
191 } | 201 } |
192 | 202 |
193 // TODO(sandersd): Proper error reporting instead of CHECKs. | 203 // TODO(sandersd): Proper error reporting instead of CHECKs. |
194 void VTVideoDecodeAccelerator::DecodeTask( | 204 void VTVideoDecodeAccelerator::DecodeTask( |
195 const media::BitstreamBuffer bitstream) { | 205 const media::BitstreamBuffer bitstream) { |
196 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 206 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
197 | 207 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 nalus.push_back(nalu); | 239 nalus.push_back(nalu); |
230 data_size += kNALUHeaderLength + nalu.size; | 240 data_size += kNALUHeaderLength + nalu.size; |
231 } | 241 } |
232 } | 242 } |
233 | 243 |
234 // 2. Initialize VideoToolbox. | 244 // 2. Initialize VideoToolbox. |
235 // TODO(sandersd): Reinitialize when there are new parameter sets. | 245 // TODO(sandersd): Reinitialize when there are new parameter sets. |
236 if (!session_) | 246 if (!session_) |
237 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); | 247 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); |
238 | 248 |
| 249 // If there are no non-configuration units, immediately return an empty |
| 250 // (ie. dropped) frame. It is an error to create a MemoryBlock with zero |
| 251 // size. |
| 252 if (!data_size) { |
| 253 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 254 &VTVideoDecodeAccelerator::OutputTask, |
| 255 weak_this_factory_.GetWeakPtr(), |
| 256 DecodedFrame(bitstream.id(), NULL))); |
| 257 return; |
| 258 } |
| 259 |
239 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. | 260 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. |
240 base::ScopedCFTypeRef<CMBlockBufferRef> data; | 261 base::ScopedCFTypeRef<CMBlockBufferRef> data; |
241 CHECK(!CMBlockBufferCreateWithMemoryBlock( | 262 CHECK(!CMBlockBufferCreateWithMemoryBlock( |
242 kCFAllocatorDefault, | 263 kCFAllocatorDefault, |
243 NULL, // &memory_block | 264 NULL, // &memory_block |
244 data_size, // block_length | 265 data_size, // block_length |
245 kCFAllocatorDefault, // block_allocator | 266 kCFAllocatorDefault, // block_allocator |
246 NULL, // &custom_block_source | 267 NULL, // &custom_block_source |
247 0, // offset_to_data | 268 0, // offset_to_data |
248 data_size, // data_length | 269 data_size, // data_length |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 CFRetain(image_buffer); | 325 CFRetain(image_buffer); |
305 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 326 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
306 &VTVideoDecodeAccelerator::OutputTask, | 327 &VTVideoDecodeAccelerator::OutputTask, |
307 weak_this_factory_.GetWeakPtr(), | 328 weak_this_factory_.GetWeakPtr(), |
308 DecodedFrame(bitstream_id, image_buffer))); | 329 DecodedFrame(bitstream_id, image_buffer))); |
309 } | 330 } |
310 | 331 |
311 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { | 332 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { |
312 DCHECK(CalledOnValidThread()); | 333 DCHECK(CalledOnValidThread()); |
313 decoded_frames_.push(frame); | 334 decoded_frames_.push(frame); |
314 SendPictures(); | 335 ProcessDecodedFrames(); |
315 } | 336 } |
316 | 337 |
317 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { | 338 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { |
318 DCHECK(CalledOnValidThread()); | 339 DCHECK(CalledOnValidThread()); |
319 texture_size_ = coded_size; | 340 texture_size_ = coded_size; |
320 // TODO(sandersd): Dismiss existing picture buffers. | 341 // TODO(sandersd): Dismiss existing picture buffers. |
321 client_->ProvidePictureBuffers( | 342 client_->ProvidePictureBuffers( |
322 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); | 343 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); |
323 } | 344 } |
324 | 345 |
325 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 346 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
326 const std::vector<media::PictureBuffer>& pictures) { | 347 const std::vector<media::PictureBuffer>& pictures) { |
327 DCHECK(CalledOnValidThread()); | 348 DCHECK(CalledOnValidThread()); |
328 | 349 |
329 for (size_t i = 0; i < pictures.size(); i++) { | 350 for (size_t i = 0; i < pictures.size(); i++) { |
330 CHECK(!texture_ids_.count(pictures[i].id())); | 351 CHECK(!texture_ids_.count(pictures[i].id())); |
331 available_picture_ids_.push(pictures[i].id()); | 352 available_picture_ids_.push(pictures[i].id()); |
332 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | 353 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); |
333 } | 354 } |
334 | 355 |
335 // Pictures are not marked as uncleared until this method returns. They will | 356 // Pictures are not marked as uncleared until after this method returns, and |
336 // become broken if they are used before that happens. | 357 // they will be broken if they are used before that happens. So, schedule |
| 358 // future work after that happens. |
337 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 359 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
338 &VTVideoDecodeAccelerator::SendPictures, | 360 &VTVideoDecodeAccelerator::ProcessDecodedFrames, |
339 weak_this_factory_.GetWeakPtr())); | 361 weak_this_factory_.GetWeakPtr())); |
340 } | 362 } |
341 | 363 |
342 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 364 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
343 DCHECK(CalledOnValidThread()); | 365 DCHECK(CalledOnValidThread()); |
344 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); | 366 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); |
345 picture_bindings_.erase(picture_id); | 367 picture_bindings_.erase(picture_id); |
346 available_picture_ids_.push(picture_id); | 368 available_picture_ids_.push(picture_id); |
347 SendPictures(); | 369 ProcessDecodedFrames(); |
348 } | 370 } |
349 | 371 |
350 // TODO(sandersd): Proper error reporting instead of CHECKs. | 372 void VTVideoDecodeAccelerator::CompleteAction(Action action) { |
351 void VTVideoDecodeAccelerator::SendPictures() { | |
352 DCHECK(CalledOnValidThread()); | 373 DCHECK(CalledOnValidThread()); |
353 if (available_picture_ids_.empty() || decoded_frames_.empty()) | 374 switch (action) { |
354 return; | 375 case ACTION_FLUSH: |
| 376 client_->NotifyFlushDone(); |
| 377 break; |
| 378 case ACTION_RESET: |
| 379 client_->NotifyResetDone(); |
| 380 break; |
| 381 case ACTION_DESTROY: |
| 382 delete this; |
| 383 break; |
| 384 } |
| 385 } |
| 386 |
| 387 void VTVideoDecodeAccelerator::CompleteActions(int32_t bitstream_id) { |
| 388 DCHECK(CalledOnValidThread()); |
| 389 while (!pending_actions_.empty() && |
| 390 pending_actions_.front().bitstream_id == bitstream_id) { |
| 391 CompleteAction(pending_actions_.front().action); |
| 392 pending_actions_.pop(); |
| 393 } |
| 394 } |
| 395 |
| 396 void VTVideoDecodeAccelerator::ProcessDecodedFrames() { |
| 397 DCHECK(CalledOnValidThread()); |
| 398 |
| 399 while (!decoded_frames_.empty()) { |
| 400 if (pending_actions_.empty()) { |
| 401 // No pending actions; send frames normally. |
| 402 SendPictures(pending_bitstream_ids_.back()); |
| 403 return; |
| 404 } |
| 405 |
| 406 int32_t next_action_bitstream_id = pending_actions_.front().bitstream_id; |
| 407 int32_t last_sent_bitstream_id = -1; |
| 408 switch (pending_actions_.front().action) { |
| 409 case ACTION_FLUSH: |
| 410 // Send frames normally. |
| 411 last_sent_bitstream_id = SendPictures(next_action_bitstream_id); |
| 412 break; |
| 413 |
| 414 case ACTION_RESET: |
| 415 // Drop decoded frames. |
| 416 while (!decoded_frames_.empty() && |
| 417 last_sent_bitstream_id != next_action_bitstream_id) { |
| 418 last_sent_bitstream_id = decoded_frames_.front().bitstream_id; |
| 419 decoded_frames_.pop(); |
| 420 DCHECK_EQ(pending_bitstream_ids_.front(), last_sent_bitstream_id); |
| 421 pending_bitstream_ids_.pop(); |
| 422 client_->NotifyEndOfBitstreamBuffer(last_sent_bitstream_id); |
| 423 } |
| 424 break; |
| 425 |
| 426 case ACTION_DESTROY: |
| 427 // Drop decoded frames, without bookkeeping. |
| 428 while (!decoded_frames_.empty()) { |
| 429 last_sent_bitstream_id = decoded_frames_.front().bitstream_id; |
| 430 decoded_frames_.pop(); |
| 431 } |
| 432 |
| 433 // Handle completing the action specially, as it is important not to |
| 434 // access |this| after calling CompleteAction(). |
| 435 if (last_sent_bitstream_id == next_action_bitstream_id) |
| 436 CompleteAction(ACTION_DESTROY); |
| 437 |
| 438 // Either |this| was deleted or no more progress can be made. |
| 439 return; |
| 440 } |
| 441 |
| 442 // If we ran out of buffers (or pictures), no more progress can be made |
| 443 // until more frames are decoded. |
| 444 if (last_sent_bitstream_id != next_action_bitstream_id) |
| 445 return; |
| 446 |
| 447 // Complete all actions pending for this |bitstream_id|, then loop to see |
| 448 // if progress can be made on the next action. |
| 449 CompleteActions(next_action_bitstream_id); |
| 450 } |
| 451 } |
| 452 |
| 453 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { |
| 454 DCHECK(CalledOnValidThread()); |
| 455 DCHECK(!decoded_frames_.empty()); |
| 456 |
| 457 if (available_picture_ids_.empty()) |
| 458 return -1; |
355 | 459 |
356 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); | 460 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); |
357 glEnable(GL_TEXTURE_RECTANGLE_ARB); | 461 glEnable(GL_TEXTURE_RECTANGLE_ARB); |
358 | 462 |
359 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) { | 463 int32_t last_sent_bitstream_id = -1; |
| 464 while (!available_picture_ids_.empty() && |
| 465 !decoded_frames_.empty() && |
| 466 last_sent_bitstream_id != up_to_bitstream_id) { |
| 467 DecodedFrame frame = decoded_frames_.front(); |
| 468 decoded_frames_.pop(); |
| 469 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); |
| 470 pending_bitstream_ids_.pop(); |
360 int32_t picture_id = available_picture_ids_.front(); | 471 int32_t picture_id = available_picture_ids_.front(); |
361 available_picture_ids_.pop(); | 472 available_picture_ids_.pop(); |
362 DecodedFrame frame = decoded_frames_.front(); | |
363 decoded_frames_.pop(); | |
364 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); | |
365 | 473 |
366 gfx::ScopedTextureBinder | 474 CVImageBufferRef image_buffer = frame.image_buffer.get(); |
367 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); | 475 if (image_buffer) { |
368 CHECK(!CGLTexImageIOSurface2D( | 476 IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer); |
369 cgl_context_, // ctx | |
370 GL_TEXTURE_RECTANGLE_ARB, // target | |
371 GL_RGB, // internal_format | |
372 texture_size_.width(), // width | |
373 texture_size_.height(), // height | |
374 GL_YCBCR_422_APPLE, // format | |
375 GL_UNSIGNED_SHORT_8_8_APPLE, // type | |
376 surface, // io_surface | |
377 0)); // plane | |
378 | 477 |
379 picture_bindings_[picture_id] = frame.image_buffer; | 478 // TODO(sandersd): Find out why this sometimes fails due to no GL context. |
380 client_->PictureReady(media::Picture( | 479 gfx::ScopedTextureBinder |
381 picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); | 480 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); |
| 481 CHECK(!CGLTexImageIOSurface2D( |
| 482 cgl_context_, // ctx |
| 483 GL_TEXTURE_RECTANGLE_ARB, // target |
| 484 GL_RGB, // internal_format |
| 485 texture_size_.width(), // width |
| 486 texture_size_.height(), // height |
| 487 GL_YCBCR_422_APPLE, // format |
| 488 GL_UNSIGNED_SHORT_8_8_APPLE, // type |
| 489 surface, // io_surface |
| 490 0)); // plane |
| 491 |
| 492 picture_bindings_[picture_id] = frame.image_buffer; |
| 493 client_->PictureReady(media::Picture( |
| 494 picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); |
| 495 } |
| 496 |
382 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | 497 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
| 498 last_sent_bitstream_id = frame.bitstream_id; |
383 } | 499 } |
384 | 500 |
385 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 501 glDisable(GL_TEXTURE_RECTANGLE_ARB); |
| 502 return last_sent_bitstream_id; |
| 503 } |
| 504 |
| 505 void VTVideoDecodeAccelerator::FlushTask() { |
| 506 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 507 CHECK(!VTDecompressionSessionFinishDelayedFrames(session_)); |
| 508 } |
| 509 |
| 510 void VTVideoDecodeAccelerator::QueueAction(Action action) { |
| 511 DCHECK(CalledOnValidThread()); |
| 512 if (pending_bitstream_ids_.empty()) { |
| 513 // If there are no pending frames, all actions complete immediately. |
| 514 CompleteAction(action); |
| 515 } else { |
| 516 // Otherwise, queue the action. |
| 517 pending_actions_.push(PendingAction(action, pending_bitstream_ids_.back())); |
| 518 |
| 519 // Request a flush to make sure the action will eventually complete. |
| 520 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
| 521 &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this))); |
| 522 |
| 523 // See if we can make progress now that there is a new pending action. |
| 524 ProcessDecodedFrames(); |
| 525 } |
386 } | 526 } |
387 | 527 |
388 void VTVideoDecodeAccelerator::Flush() { | 528 void VTVideoDecodeAccelerator::Flush() { |
389 DCHECK(CalledOnValidThread()); | 529 DCHECK(CalledOnValidThread()); |
390 // TODO(sandersd): Trigger flush, sending frames. | 530 QueueAction(ACTION_FLUSH); |
391 } | 531 } |
392 | 532 |
393 void VTVideoDecodeAccelerator::Reset() { | 533 void VTVideoDecodeAccelerator::Reset() { |
394 DCHECK(CalledOnValidThread()); | 534 DCHECK(CalledOnValidThread()); |
395 // TODO(sandersd): Trigger flush, discarding frames. | 535 QueueAction(ACTION_RESET); |
396 } | 536 } |
397 | 537 |
398 void VTVideoDecodeAccelerator::Destroy() { | 538 void VTVideoDecodeAccelerator::Destroy() { |
399 DCHECK(CalledOnValidThread()); | 539 DCHECK(CalledOnValidThread()); |
400 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. | 540 // Drop any other pending actions. |
401 delete this; | 541 while (!pending_actions_.empty()) |
| 542 pending_actions_.pop(); |
| 543 // Return all bitstream buffers. |
| 544 while (!pending_bitstream_ids_.empty()) { |
| 545 client_->NotifyEndOfBitstreamBuffer(pending_bitstream_ids_.front()); |
| 546 pending_bitstream_ids_.pop(); |
| 547 } |
| 548 QueueAction(ACTION_DESTROY); |
402 } | 549 } |
403 | 550 |
404 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 551 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
405 return false; | 552 return false; |
406 } | 553 } |
407 | 554 |
408 } // namespace content | 555 } // namespace content |
OLD | NEW |