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 |