Chromium Code Reviews| 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 | 7 |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/sys_byteorder.h" | 10 #include "base/sys_byteorder.h" |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 32 | 32 |
| 33 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | 33 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
| 34 static void OutputThunk( | 34 static void OutputThunk( |
| 35 void* decompression_output_refcon, | 35 void* decompression_output_refcon, |
| 36 void* source_frame_refcon, | 36 void* source_frame_refcon, |
| 37 OSStatus status, | 37 OSStatus status, |
| 38 VTDecodeInfoFlags info_flags, | 38 VTDecodeInfoFlags info_flags, |
| 39 CVImageBufferRef image_buffer, | 39 CVImageBufferRef image_buffer, |
| 40 CMTime presentation_time_stamp, | 40 CMTime presentation_time_stamp, |
| 41 CMTime presentation_duration) { | 41 CMTime presentation_duration) { |
| 42 // TODO(sandersd): Implement flush-before-delete to guarantee validity. | |
| 43 VTVideoDecodeAccelerator* vda = | 42 VTVideoDecodeAccelerator* vda = |
| 44 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); | 43 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
| 45 int32_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); | 44 int32_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); |
| 46 vda->Output(bitstream_id, status, image_buffer); | 45 vda->Output(bitstream_id, status, image_buffer); |
| 47 } | 46 } |
| 48 | 47 |
| 49 VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame( | 48 VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame( |
| 50 int32_t bitstream_id, | 49 int32_t bitstream_id, |
| 51 CVImageBufferRef image_buffer) | 50 CVImageBufferRef image_buffer) |
| 52 : bitstream_id(bitstream_id), | 51 : bitstream_id(bitstream_id), |
| 53 image_buffer(image_buffer) { | 52 image_buffer(image_buffer) { |
| 54 } | 53 } |
| 55 | 54 |
| 56 VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() { | 55 VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() { |
| 57 } | 56 } |
| 58 | 57 |
| 59 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 58 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
| 60 : cgl_context_(cgl_context), | 59 : cgl_context_(cgl_context), |
| 61 client_(NULL), | 60 client_(NULL), |
| 61 state_(NORMAL), | |
| 62 frames_pending_decode_(0), | |
| 62 format_(NULL), | 63 format_(NULL), |
| 63 session_(NULL), | 64 session_(NULL), |
| 64 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 65 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| 65 weak_this_factory_(this), | 66 weak_this_factory_(this), |
| 66 decoder_thread_("VTDecoderThread") { | 67 decoder_thread_("VTDecoderThread") { |
| 67 callback_.decompressionOutputCallback = OutputThunk; | 68 callback_.decompressionOutputCallback = OutputThunk; |
| 68 callback_.decompressionOutputRefCon = this; | 69 callback_.decompressionOutputRefCon = this; |
| 69 } | 70 } |
| 70 | 71 |
| 71 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 72 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 152 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); | 153 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
| 153 #undef CFINT | 154 #undef CFINT |
| 154 CFDictionarySetValue( | 155 CFDictionarySetValue( |
| 155 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | 156 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
| 156 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | 157 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
| 157 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | 158 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
| 158 CFDictionarySetValue( | 159 CFDictionarySetValue( |
| 159 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | 160 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
| 160 | 161 |
| 161 // TODO(sandersd): Check if the session is already compatible. | 162 // TODO(sandersd): Check if the session is already compatible. |
| 162 // TODO(sandersd): Flush. | |
| 163 session_.reset(); | 163 session_.reset(); |
| 164 CHECK(!VTDecompressionSessionCreate( | 164 CHECK(!VTDecompressionSessionCreate( |
| 165 kCFAllocatorDefault, | 165 kCFAllocatorDefault, |
| 166 format_, // video_format_description | 166 format_, // video_format_description |
| 167 decoder_config, // video_decoder_specification | 167 decoder_config, // video_decoder_specification |
| 168 image_config, // destination_image_buffer_attributes | 168 image_config, // destination_image_buffer_attributes |
| 169 &callback_, // output_callback | 169 &callback_, // output_callback |
| 170 session_.InitializeInto())); | 170 session_.InitializeInto())); |
| 171 | 171 |
| 172 // If the size has changed, trigger a request for new picture buffers. | 172 // If the size has changed, trigger a request for new picture buffers. |
| 173 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); | 173 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); |
| 174 if (coded_size_ != new_coded_size) { | 174 if (coded_size_ != new_coded_size) { |
| 175 coded_size_ = new_coded_size; | 175 coded_size_ = new_coded_size; |
| 176 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 176 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 177 &VTVideoDecodeAccelerator::SizeChangedTask, | 177 &VTVideoDecodeAccelerator::SizeChangedTask, |
| 178 weak_this_factory_.GetWeakPtr(), | 178 weak_this_factory_.GetWeakPtr(), |
| 179 coded_size_));; | 179 coded_size_));; |
| 180 } | 180 } |
| 181 } | 181 } |
| 182 | 182 |
| 183 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 183 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
| 184 DCHECK(CalledOnValidThread()); | 184 DCHECK(CalledOnValidThread()); |
| 185 // TODO(sandersd): Test what happens if bitstream buffers are passed to VT out | 185 frames_pending_decode_++; |
|
Pawel Osciak
2014/08/27 08:25:20
Client will keep calling Decode()s after it calls
sandersd (OOO until July 31)
2014/08/27 21:40:43
video_decoder.h specifies that no decode calls are
Pawel Osciak
2014/08/28 12:09:12
This class implements VideoDecodeAccelerator, not
| |
| 186 // of order. | |
| 187 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | 186 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
| 188 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), | 187 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
| 189 bitstream)); | 188 bitstream)); |
| 190 } | 189 } |
| 191 | 190 |
| 192 // TODO(sandersd): Proper error reporting instead of CHECKs. | 191 // TODO(sandersd): Proper error reporting instead of CHECKs. |
| 193 void VTVideoDecodeAccelerator::DecodeTask( | 192 void VTVideoDecodeAccelerator::DecodeTask( |
| 194 const media::BitstreamBuffer bitstream) { | 193 const media::BitstreamBuffer bitstream) { |
| 195 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 194 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 196 | 195 |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 302 CHECK_EQ(CFGetTypeID(image_buffer), CVPixelBufferGetTypeID()); | 301 CHECK_EQ(CFGetTypeID(image_buffer), CVPixelBufferGetTypeID()); |
| 303 CFRetain(image_buffer); | 302 CFRetain(image_buffer); |
| 304 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 303 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 305 &VTVideoDecodeAccelerator::OutputTask, | 304 &VTVideoDecodeAccelerator::OutputTask, |
| 306 weak_this_factory_.GetWeakPtr(), | 305 weak_this_factory_.GetWeakPtr(), |
| 307 DecodedFrame(bitstream_id, image_buffer))); | 306 DecodedFrame(bitstream_id, image_buffer))); |
| 308 } | 307 } |
| 309 | 308 |
| 310 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { | 309 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { |
| 311 DCHECK(CalledOnValidThread()); | 310 DCHECK(CalledOnValidThread()); |
| 312 decoded_frames_.push(frame); | 311 if (state_ == RESETTING || state_ == DESTROYING) { |
| 313 SendPictures(); | 312 // Drop all decoded frames when resetting or destroying. |
| 313 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | |
| 314 frames_pending_decode_--; | |
| 315 if (!frames_pending_decode_) | |
| 316 FlushDone(); | |
| 317 } else { | |
| 318 decoded_frames_.push(frame); | |
| 319 SendPictures(); | |
| 320 } | |
| 314 } | 321 } |
| 315 | 322 |
| 316 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { | 323 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { |
| 317 DCHECK(CalledOnValidThread()); | 324 DCHECK(CalledOnValidThread()); |
| 318 texture_size_ = coded_size; | 325 texture_size_ = coded_size; |
| 319 // TODO(sandersd): Dismiss existing picture buffers. | 326 // TODO(sandersd): Dismiss existing picture buffers. |
| 320 client_->ProvidePictureBuffers( | 327 client_->ProvidePictureBuffers( |
| 321 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); | 328 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); |
| 322 } | 329 } |
| 323 | 330 |
| 324 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 331 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
| 325 const std::vector<media::PictureBuffer>& pictures) { | 332 const std::vector<media::PictureBuffer>& pictures) { |
| 326 DCHECK(CalledOnValidThread()); | 333 DCHECK(CalledOnValidThread()); |
| 327 | 334 |
| 328 for (size_t i = 0; i < pictures.size(); i++) { | 335 for (size_t i = 0; i < pictures.size(); i++) { |
| 329 CHECK(!texture_ids_.count(pictures[i].id())); | 336 CHECK(!texture_ids_.count(pictures[i].id())); |
| 330 available_picture_ids_.push(pictures[i].id()); | 337 available_picture_ids_.push(pictures[i].id()); |
| 331 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | 338 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); |
| 332 } | 339 } |
| 333 | 340 |
| 334 // Pictures are not marked as uncleared until this method returns. They will | 341 // Pictures are not marked as uncleared until this method returns. They will |
| 335 // become broken if they are used before that happens. | 342 // become broken if they are used before that happens. So, instead of calling |
| 343 // SendPictures(), arrange for it to be called later. | |
| 336 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 344 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 337 &VTVideoDecodeAccelerator::SendPictures, | 345 &VTVideoDecodeAccelerator::SendPictures, |
| 338 weak_this_factory_.GetWeakPtr())); | 346 weak_this_factory_.GetWeakPtr())); |
| 339 } | 347 } |
| 340 | 348 |
| 341 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 349 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
| 342 DCHECK(CalledOnValidThread()); | 350 DCHECK(CalledOnValidThread()); |
| 343 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); | 351 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); |
| 344 picture_bindings_.erase(picture_id); | 352 picture_bindings_.erase(picture_id); |
| 345 available_picture_ids_.push(picture_id); | 353 available_picture_ids_.push(picture_id); |
| 346 SendPictures(); | 354 SendPictures(); |
| 347 } | 355 } |
| 348 | 356 |
| 349 // TODO(sandersd): Proper error reporting instead of CHECKs. | 357 // TODO(sandersd): Proper error reporting instead of CHECKs. |
| 350 void VTVideoDecodeAccelerator::SendPictures() { | 358 void VTVideoDecodeAccelerator::SendPictures() { |
| 351 DCHECK(CalledOnValidThread()); | 359 DCHECK(CalledOnValidThread()); |
| 352 if (available_picture_ids_.empty() || decoded_frames_.empty()) | 360 if (available_picture_ids_.empty() || decoded_frames_.empty()) |
| 353 return; | 361 return; |
| 354 | 362 |
| 355 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); | 363 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); |
| 356 glEnable(GL_TEXTURE_RECTANGLE_ARB); | 364 glEnable(GL_TEXTURE_RECTANGLE_ARB); |
| 357 | 365 |
| 358 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) { | 366 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) { |
| 359 int32_t picture_id = available_picture_ids_.front(); | 367 int32_t picture_id = available_picture_ids_.front(); |
| 360 available_picture_ids_.pop(); | 368 available_picture_ids_.pop(); |
| 361 DecodedFrame frame = decoded_frames_.front(); | 369 DecodedFrame frame = decoded_frames_.front(); |
| 362 decoded_frames_.pop(); | 370 decoded_frames_.pop(); |
| 363 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); | 371 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); |
| 364 | 372 |
| 373 // TODO(sandersd): Find out why this fails due to no GL context sometimes. | |
| 365 gfx::ScopedTextureBinder | 374 gfx::ScopedTextureBinder |
| 366 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); | 375 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); |
| 367 CHECK(!CGLTexImageIOSurface2D( | 376 CHECK(!CGLTexImageIOSurface2D( |
| 368 cgl_context_, // ctx | 377 cgl_context_, // ctx |
| 369 GL_TEXTURE_RECTANGLE_ARB, // target | 378 GL_TEXTURE_RECTANGLE_ARB, // target |
| 370 GL_RGB, // internal_format | 379 GL_RGB, // internal_format |
| 371 texture_size_.width(), // width | 380 texture_size_.width(), // width |
| 372 texture_size_.height(), // height | 381 texture_size_.height(), // height |
| 373 GL_YCBCR_422_APPLE, // format | 382 GL_YCBCR_422_APPLE, // format |
| 374 GL_UNSIGNED_SHORT_8_8_APPLE, // type | 383 GL_UNSIGNED_SHORT_8_8_APPLE, // type |
| 375 surface, // io_surface | 384 surface, // io_surface |
| 376 0)); // plane | 385 0)); // plane |
| 377 | 386 |
| 378 picture_bindings_[picture_id] = frame.image_buffer; | 387 picture_bindings_[picture_id] = frame.image_buffer; |
| 379 client_->PictureReady(media::Picture(picture_id, frame.bitstream_id)); | 388 client_->PictureReady(media::Picture(picture_id, frame.bitstream_id)); |
| 380 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | 389 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
| 390 frames_pending_decode_--; | |
| 381 } | 391 } |
| 382 | 392 |
| 383 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 393 glDisable(GL_TEXTURE_RECTANGLE_ARB); |
| 394 | |
| 395 if (state_ != NORMAL && !frames_pending_decode_) | |
| 396 FlushDone(); | |
| 397 } | |
| 398 | |
| 399 void VTVideoDecodeAccelerator::FlushStart(State state) { | |
| 400 DCHECK(CalledOnValidThread()); | |
| 401 state_ = state; | |
| 402 | |
| 403 // If resetting or destroying, drop all pending frames. | |
| 404 if (state_ == RESETTING || state_ == DESTROYING) { | |
| 405 while (!decoded_frames_.empty()) { | |
| 406 DecodedFrame frame = decoded_frames_.front(); | |
| 407 decoded_frames_.pop(); | |
| 408 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | |
| 409 frames_pending_decode_--; | |
| 410 } | |
| 411 } | |
| 412 | |
| 413 if (frames_pending_decode_) { | |
| 414 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | |
| 415 &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this))); | |
| 416 } else { | |
| 417 FlushDone(); | |
| 418 } | |
| 419 } | |
| 420 | |
| 421 void VTVideoDecodeAccelerator::FlushTask() { | |
| 422 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 423 if (session_) | |
| 424 CHECK(!VTDecompressionSessionFinishDelayedFrames(session_)); | |
| 425 } | |
| 426 | |
| 427 void VTVideoDecodeAccelerator::FlushDone() { | |
| 428 DCHECK(CalledOnValidThread()); | |
| 429 switch (state_) { | |
| 430 case NORMAL: | |
| 431 NOTREACHED(); | |
| 432 break; | |
| 433 case FLUSHING: | |
| 434 client_->NotifyFlushDone(); | |
| 435 state_ = NORMAL; | |
| 436 break; | |
| 437 case RESETTING: | |
| 438 client_->NotifyResetDone(); | |
| 439 state_ = NORMAL; | |
| 440 break; | |
| 441 case DESTROYING: | |
| 442 delete this; | |
| 443 break; | |
| 444 } | |
| 384 } | 445 } |
| 385 | 446 |
| 386 void VTVideoDecodeAccelerator::Flush() { | 447 void VTVideoDecodeAccelerator::Flush() { |
| 387 DCHECK(CalledOnValidThread()); | 448 DCHECK(CalledOnValidThread()); |
| 388 // TODO(sandersd): Trigger flush, sending frames. | 449 FlushStart(FLUSHING); |
| 389 } | 450 } |
| 390 | 451 |
| 391 void VTVideoDecodeAccelerator::Reset() { | 452 void VTVideoDecodeAccelerator::Reset() { |
| 392 DCHECK(CalledOnValidThread()); | 453 DCHECK(CalledOnValidThread()); |
| 393 // TODO(sandersd): Trigger flush, discarding frames. | 454 FlushStart(RESETTING); |
| 394 } | 455 } |
| 395 | 456 |
| 396 void VTVideoDecodeAccelerator::Destroy() { | 457 void VTVideoDecodeAccelerator::Destroy() { |
| 397 DCHECK(CalledOnValidThread()); | 458 DCHECK(CalledOnValidThread()); |
| 398 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. | 459 FlushStart(DESTROYING); |
| 399 delete this; | |
| 400 } | 460 } |
| 401 | 461 |
| 402 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 462 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
| 403 return false; | 463 return false; |
| 404 } | 464 } |
| 405 | 465 |
| 406 } // namespace content | 466 } // namespace content |
| OLD | NEW |