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 #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 pending_bitstream_ids_.push(bitstream.id()); |
| 187 // of order. | |
| 188 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | 197 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
| 189 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), | 198 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
| 190 bitstream)); | 199 bitstream)); |
| 191 } | 200 } |
| 192 | 201 |
| 193 // TODO(sandersd): Proper error reporting instead of CHECKs. | 202 // TODO(sandersd): Proper error reporting instead of CHECKs. |
| 194 void VTVideoDecodeAccelerator::DecodeTask( | 203 void VTVideoDecodeAccelerator::DecodeTask( |
| 195 const media::BitstreamBuffer bitstream) { | 204 const media::BitstreamBuffer bitstream) { |
| 196 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 205 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 197 | 206 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 229 nalus.push_back(nalu); | 238 nalus.push_back(nalu); |
| 230 data_size += kNALUHeaderLength + nalu.size; | 239 data_size += kNALUHeaderLength + nalu.size; |
| 231 } | 240 } |
| 232 } | 241 } |
| 233 | 242 |
| 234 // 2. Initialize VideoToolbox. | 243 // 2. Initialize VideoToolbox. |
| 235 // TODO(sandersd): Reinitialize when there are new parameter sets. | 244 // TODO(sandersd): Reinitialize when there are new parameter sets. |
| 236 if (!session_) | 245 if (!session_) |
| 237 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); | 246 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); |
| 238 | 247 |
| 248 if (!data_size) { | |
|
Pawel Osciak
2014/09/25 01:27:29
Could you explain? If there is no data we want to
sandersd (OOO until July 31)
2014/09/25 19:18:52
Done. (And yes.)
| |
| 249 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 250 &VTVideoDecodeAccelerator::OutputTask, | |
| 251 weak_this_factory_.GetWeakPtr(), | |
| 252 DecodedFrame(bitstream.id(), NULL))); | |
| 253 return; | |
| 254 } | |
| 255 | |
| 239 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. | 256 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. |
| 240 base::ScopedCFTypeRef<CMBlockBufferRef> data; | 257 base::ScopedCFTypeRef<CMBlockBufferRef> data; |
| 241 CHECK(!CMBlockBufferCreateWithMemoryBlock( | 258 CHECK(!CMBlockBufferCreateWithMemoryBlock( |
| 242 kCFAllocatorDefault, | 259 kCFAllocatorDefault, |
| 243 NULL, // &memory_block | 260 NULL, // &memory_block |
| 244 data_size, // block_length | 261 data_size, // block_length |
| 245 kCFAllocatorDefault, // block_allocator | 262 kCFAllocatorDefault, // block_allocator |
| 246 NULL, // &custom_block_source | 263 NULL, // &custom_block_source |
| 247 0, // offset_to_data | 264 0, // offset_to_data |
| 248 data_size, // data_length | 265 data_size, // data_length |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 304 CFRetain(image_buffer); | 321 CFRetain(image_buffer); |
| 305 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 322 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 306 &VTVideoDecodeAccelerator::OutputTask, | 323 &VTVideoDecodeAccelerator::OutputTask, |
| 307 weak_this_factory_.GetWeakPtr(), | 324 weak_this_factory_.GetWeakPtr(), |
| 308 DecodedFrame(bitstream_id, image_buffer))); | 325 DecodedFrame(bitstream_id, image_buffer))); |
| 309 } | 326 } |
| 310 | 327 |
| 311 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { | 328 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { |
| 312 DCHECK(CalledOnValidThread()); | 329 DCHECK(CalledOnValidThread()); |
| 313 decoded_frames_.push(frame); | 330 decoded_frames_.push(frame); |
| 314 SendPictures(); | 331 ProcessDecodedFrames(); |
| 315 } | 332 } |
| 316 | 333 |
| 317 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { | 334 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { |
| 318 DCHECK(CalledOnValidThread()); | 335 DCHECK(CalledOnValidThread()); |
| 319 texture_size_ = coded_size; | 336 texture_size_ = coded_size; |
| 320 // TODO(sandersd): Dismiss existing picture buffers. | 337 // TODO(sandersd): Dismiss existing picture buffers. |
| 321 client_->ProvidePictureBuffers( | 338 client_->ProvidePictureBuffers( |
| 322 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); | 339 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); |
| 323 } | 340 } |
| 324 | 341 |
| 325 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 342 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
| 326 const std::vector<media::PictureBuffer>& pictures) { | 343 const std::vector<media::PictureBuffer>& pictures) { |
| 327 DCHECK(CalledOnValidThread()); | 344 DCHECK(CalledOnValidThread()); |
| 328 | 345 |
| 329 for (size_t i = 0; i < pictures.size(); i++) { | 346 for (size_t i = 0; i < pictures.size(); i++) { |
| 330 CHECK(!texture_ids_.count(pictures[i].id())); | 347 CHECK(!texture_ids_.count(pictures[i].id())); |
| 331 available_picture_ids_.push(pictures[i].id()); | 348 available_picture_ids_.push(pictures[i].id()); |
| 332 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | 349 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); |
| 333 } | 350 } |
| 334 | 351 |
| 335 // Pictures are not marked as uncleared until this method returns. They will | 352 // Pictures are not marked as uncleared until after this method returns, and |
| 336 // become broken if they are used before that happens. | 353 // they will be broken if they are used before that happens. So, schedule |
| 354 // future work after that happens. | |
| 337 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 355 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 338 &VTVideoDecodeAccelerator::SendPictures, | 356 &VTVideoDecodeAccelerator::ProcessDecodedFrames, |
| 339 weak_this_factory_.GetWeakPtr())); | 357 weak_this_factory_.GetWeakPtr())); |
| 340 } | 358 } |
| 341 | 359 |
| 342 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 360 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
| 343 DCHECK(CalledOnValidThread()); | 361 DCHECK(CalledOnValidThread()); |
| 344 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); | 362 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); |
| 345 picture_bindings_.erase(picture_id); | 363 picture_bindings_.erase(picture_id); |
| 346 available_picture_ids_.push(picture_id); | 364 available_picture_ids_.push(picture_id); |
| 347 SendPictures(); | 365 ProcessDecodedFrames(); |
| 348 } | 366 } |
| 349 | 367 |
| 350 // TODO(sandersd): Proper error reporting instead of CHECKs. | 368 void VTVideoDecodeAccelerator::CompleteAction(Action action) { |
| 351 void VTVideoDecodeAccelerator::SendPictures() { | |
| 352 DCHECK(CalledOnValidThread()); | 369 DCHECK(CalledOnValidThread()); |
| 353 if (available_picture_ids_.empty() || decoded_frames_.empty()) | 370 switch (action) { |
| 354 return; | 371 case ACTION_FLUSH: |
| 372 client_->NotifyFlushDone(); | |
| 373 break; | |
| 374 case ACTION_RESET: | |
| 375 client_->NotifyResetDone(); | |
| 376 break; | |
| 377 case ACTION_DESTROY: | |
| 378 delete this; | |
| 379 break; | |
| 380 } | |
| 381 } | |
| 355 | 382 |
| 383 void VTVideoDecodeAccelerator::CompleteActions(int32_t bitstream_id) { | |
| 384 DCHECK(CalledOnValidThread()); | |
| 385 while (!pending_actions_.empty() && | |
| 386 pending_actions_.front().bitstream_id == bitstream_id) { | |
|
Pawel Osciak
2014/09/25 01:27:29
Wouldn't you want to do <= instead of == here?
sandersd (OOO until July 31)
2014/09/25 19:18:53
No, CompleteActions() is called every time a pendi
Pawel Osciak
2014/09/26 04:25:07
Ah, lack of doc for the method confused me.
And I
sandersd (OOO until July 31)
2014/09/26 04:56:41
Acknowledged.
| |
| 387 CompleteAction(pending_actions_.front().action); | |
| 388 pending_actions_.pop(); | |
| 389 } | |
| 390 } | |
| 391 | |
| 392 void VTVideoDecodeAccelerator::ProcessDecodedFrames() { | |
| 393 DCHECK(CalledOnValidThread()); | |
| 394 | |
| 395 while (true) { | |
|
Pawel Osciak
2014/09/25 01:27:29
Maybe just while (!decoded_frames_.empty()) instea
sandersd (OOO until July 31)
2014/09/25 19:18:52
Done.
| |
| 396 if (decoded_frames_.empty()) | |
| 397 return; | |
| 398 | |
| 399 if (pending_actions_.empty()) { | |
| 400 // No pending actions; send frames normally. | |
| 401 if (!available_picture_ids_.empty()) | |
| 402 SendPictures(pending_bitstream_ids_.back()); | |
| 403 return; | |
| 404 } else if (pending_actions_.front().action == ACTION_FLUSH) { | |
|
Pawel Osciak
2014/09/25 01:27:28
I think it could be more readable if you:
if (pen
sandersd (OOO until July 31)
2014/09/25 19:18:52
I like this, thanks!
| |
| 405 // Flushing; send frames normally, then complete any actions. | |
| 406 if (!available_picture_ids_.empty()) { | |
|
Pawel Osciak
2014/09/25 01:27:28
You make this check in SendPictures anyway. I'd re
sandersd (OOO until July 31)
2014/09/25 19:18:52
This check served a few purposes:
- It avoids ne
| |
| 407 int32_t last_sent_bitstream_id = | |
| 408 SendPictures(pending_actions_.front().bitstream_id); | |
| 409 CompleteActions(last_sent_bitstream_id); | |
| 410 // Loop again, as there may be work to do for a new pending action. | |
| 411 continue; | |
| 412 } | |
| 413 return; | |
|
Pawel Osciak
2014/09/25 01:27:29
I'd just remove continue and return and let the ex
sandersd (OOO until July 31)
2014/09/25 19:18:53
Acknowledged.
Pawel Osciak
2014/09/26 04:25:07
But not implemented?
sandersd (OOO until July 31)
2014/09/26 04:56:41
I thought the switch version made this clear enoug
Pawel Osciak
2014/09/26 05:20:30
My point is, continue is the default action. So yo
sandersd (OOO until July 31)
2014/09/26 06:56:02
Done.
| |
| 414 } else if (pending_actions_.front().action == ACTION_RESET) { | |
| 415 // Reseting; drop a decoded frame and then complete any actions. | |
|
Pawel Osciak
2014/09/25 01:27:28
One frame? Not drop frames until the id for the ac
sandersd (OOO until July 31)
2014/09/25 19:18:53
It's the same, but I've switched it around.
| |
| 416 int32_t bitstream_id = decoded_frames_.front().bitstream_id; | |
|
Pawel Osciak
2014/09/25 01:27:28
Why not do things until action's bitstream id? Thi
sandersd (OOO until July 31)
2014/09/25 19:18:53
Done.
| |
| 417 decoded_frames_.pop(); | |
| 418 DCHECK_EQ(pending_bitstream_ids_.front(), bitstream_id); | |
| 419 pending_bitstream_ids_.pop(); | |
| 420 client_->NotifyEndOfBitstreamBuffer(bitstream_id); | |
| 421 CompleteActions(bitstream_id); | |
| 422 // Loop again, as there may be more frames to drop or work to do for a new | |
| 423 // pending action. | |
| 424 continue; | |
|
Pawel Osciak
2014/09/25 01:27:29
Not needed.
sandersd (OOO until July 31)
2014/09/25 19:18:52
Acknowledged.
Pawel Osciak
2014/09/26 04:25:07
But not removed?
sandersd (OOO until July 31)
2014/09/26 04:56:41
See above.
| |
| 425 } else { | |
| 426 // Destroying; drop a decoded frame, then destroy if ready. | |
|
Pawel Osciak
2014/09/25 01:27:28
Why only one not up to action id? That would be si
sandersd (OOO until July 31)
2014/09/25 19:18:52
Done.
| |
| 427 DCHECK_EQ(pending_actions_.front().action, ACTION_DESTROY); | |
| 428 int32_t bitstream_id = decoded_frames_.front().bitstream_id; | |
| 429 decoded_frames_.pop(); | |
| 430 if (pending_actions_.front().bitstream_id == bitstream_id) { | |
| 431 CompleteAction(ACTION_DESTROY); | |
| 432 return; | |
| 433 } | |
| 434 // Loop again, as there may be more frames to drop. | |
| 435 continue; | |
| 436 } | |
| 437 | |
| 438 NOTREACHED(); | |
|
Pawel Osciak
2014/09/25 01:27:29
This would go to default: clause.
sandersd (OOO until July 31)
2014/09/25 19:18:52
Not quite, I want to be sure that every path at th
Pawel Osciak
2014/09/26 04:25:07
You can do that by removing continues and putting
sandersd (OOO until July 31)
2014/09/26 04:56:41
I don't believe that is the same. The compiler can
Pawel Osciak
2014/09/26 05:20:30
That's exactly the point I'm trying to make. Nobod
sandersd (OOO until July 31)
2014/09/26 06:56:02
Done.
| |
| 439 } | |
| 440 } | |
| 441 | |
| 442 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { | |
| 443 DCHECK(CalledOnValidThread()); | |
| 444 DCHECK(!decoded_frames_.empty()); | |
| 445 DCHECK(!available_picture_ids_.empty()); | |
| 446 | |
| 447 int32_t last_sent_bitstream_id = -1; | |
| 356 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); | 448 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); |
| 357 glEnable(GL_TEXTURE_RECTANGLE_ARB); | 449 glEnable(GL_TEXTURE_RECTANGLE_ARB); |
| 358 | 450 |
| 359 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) { | 451 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) { |
| 452 DecodedFrame frame = decoded_frames_.front(); | |
| 453 decoded_frames_.pop(); | |
| 454 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); | |
| 455 pending_bitstream_ids_.pop(); | |
| 360 int32_t picture_id = available_picture_ids_.front(); | 456 int32_t picture_id = available_picture_ids_.front(); |
| 361 available_picture_ids_.pop(); | 457 available_picture_ids_.pop(); |
| 362 DecodedFrame frame = decoded_frames_.front(); | |
| 363 decoded_frames_.pop(); | |
| 364 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); | |
| 365 | 458 |
| 366 gfx::ScopedTextureBinder | 459 CVImageBufferRef image_buffer = frame.image_buffer.get(); |
| 367 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); | 460 if (image_buffer) { |
|
Pawel Osciak
2014/09/25 01:27:28
What happens if there is no image_buffer? Don't we
sandersd (OOO until July 31)
2014/09/25 19:18:52
This happens in if !data_size, or if the decoder d
| |
| 368 CHECK(!CGLTexImageIOSurface2D( | 461 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 | 462 |
| 379 picture_bindings_[picture_id] = frame.image_buffer; | 463 // TODO(sandersd): Find out why this somtimes fails due to no GL context. |
|
Pawel Osciak
2014/09/25 01:27:28
s/somtimes/sometimes/
How do you know it failed?
sandersd (OOO until July 31)
2014/09/25 19:18:53
It fails when the CGLTexImageIOSurface2D() check f
Pawel Osciak
2014/09/26 04:25:07
Well, I'd really like to express my strong prefere
sandersd (OOO until July 31)
2014/09/26 04:56:41
Understood, replacing all the CHECKs is the next i
| |
| 380 client_->PictureReady(media::Picture( | 464 gfx::ScopedTextureBinder |
| 381 picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); | 465 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); |
| 466 CHECK(!CGLTexImageIOSurface2D( | |
| 467 cgl_context_, // ctx | |
| 468 GL_TEXTURE_RECTANGLE_ARB, // target | |
| 469 GL_RGB, // internal_format | |
| 470 texture_size_.width(), // width | |
| 471 texture_size_.height(), // height | |
| 472 GL_YCBCR_422_APPLE, // format | |
| 473 GL_UNSIGNED_SHORT_8_8_APPLE, // type | |
| 474 surface, // io_surface | |
| 475 0)); // plane | |
| 476 | |
| 477 picture_bindings_[picture_id] = frame.image_buffer; | |
| 478 client_->PictureReady(media::Picture( | |
| 479 picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); | |
| 480 } | |
| 481 | |
| 382 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | 482 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
| 483 last_sent_bitstream_id = frame.bitstream_id; | |
| 484 if (frame.bitstream_id == up_to_bitstream_id) | |
|
Pawel Osciak
2014/09/25 01:27:29
Make this a part of while() condition?
sandersd (OOO until July 31)
2014/09/25 19:18:53
Done.
| |
| 485 break; | |
| 383 } | 486 } |
| 384 | 487 |
| 385 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 488 glDisable(GL_TEXTURE_RECTANGLE_ARB); |
| 489 return last_sent_bitstream_id; | |
| 490 } | |
| 491 | |
| 492 void VTVideoDecodeAccelerator::FlushTask() { | |
| 493 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
| 494 CHECK(!VTDecompressionSessionFinishDelayedFrames(session_)); | |
| 495 } | |
| 496 | |
| 497 void VTVideoDecodeAccelerator::QueueAction(Action action) { | |
| 498 DCHECK(CalledOnValidThread()); | |
| 499 if (pending_bitstream_ids_.empty()) { | |
| 500 CompleteAction(action); | |
|
Pawel Osciak
2014/09/25 01:27:29
Could you just let ProcessDecodedFrames() to this
sandersd (OOO until July 31)
2014/09/25 19:18:52
ProcessDecodedFrames() doesn't currently handle an
| |
| 501 } else { | |
| 502 pending_actions_.push(PendingAction(action, pending_bitstream_ids_.back())); | |
| 503 ProcessDecodedFrames(); | |
| 504 if (!pending_actions_.empty() && !pending_bitstream_ids_.empty()) { | |
| 505 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | |
| 506 &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this))); | |
|
Pawel Osciak
2014/09/25 01:27:28
This requires an explanation...
sandersd (OOO until July 31)
2014/09/25 19:18:53
I think the condition was just wrong, as there wil
| |
| 507 } | |
| 508 } | |
| 386 } | 509 } |
| 387 | 510 |
| 388 void VTVideoDecodeAccelerator::Flush() { | 511 void VTVideoDecodeAccelerator::Flush() { |
| 389 DCHECK(CalledOnValidThread()); | 512 DCHECK(CalledOnValidThread()); |
| 390 // TODO(sandersd): Trigger flush, sending frames. | 513 QueueAction(ACTION_FLUSH); |
| 391 } | 514 } |
| 392 | 515 |
| 393 void VTVideoDecodeAccelerator::Reset() { | 516 void VTVideoDecodeAccelerator::Reset() { |
| 394 DCHECK(CalledOnValidThread()); | 517 DCHECK(CalledOnValidThread()); |
| 395 // TODO(sandersd): Trigger flush, discarding frames. | 518 QueueAction(ACTION_RESET); |
| 396 } | 519 } |
| 397 | 520 |
| 398 void VTVideoDecodeAccelerator::Destroy() { | 521 void VTVideoDecodeAccelerator::Destroy() { |
| 399 DCHECK(CalledOnValidThread()); | 522 DCHECK(CalledOnValidThread()); |
|
Pawel Osciak
2014/09/25 01:27:29
If you stopped the decoder thread here, you wouldn
sandersd (OOO until July 31)
2014/09/25 19:18:52
This is indeed possible, as long as the FlushTask
Pawel Osciak
2014/09/26 04:25:07
I don't understand the part about FlushTask... Que
sandersd (OOO until July 31)
2014/09/26 04:56:41
Queued to the decoder thread message loop, before
Pawel Osciak
2014/09/26 05:20:30
If you decoder_thread_.Stop() from Destroy(), it w
sandersd (OOO until July 31)
2014/09/26 06:56:02
This makes sense, but I'd still rather hold off. I
Pawel Osciak
2014/09/26 15:30:49
The idea was to do away with ACTION_DESTROY. It wo
sandersd (OOO until July 31)
2014/09/26 17:28:51
Acknowledged.
| |
| 400 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. | 523 // Drop any other pending actions. |
| 401 delete this; | 524 while (!pending_actions_.empty()) |
| 525 pending_actions_.pop(); | |
| 526 // Return all bitstream buffers. | |
| 527 while (!pending_bitstream_ids_.empty()) { | |
| 528 client_->NotifyEndOfBitstreamBuffer(pending_bitstream_ids_.front()); | |
|
Pawel Osciak
2014/09/25 01:27:29
You shouldn't have to do this on Destroy().
sandersd (OOO until July 31)
2014/09/25 19:18:53
It seems I do, GpuVideoDecoder DCHECKS that there
Pawel Osciak
2014/09/26 04:25:07
Acknowledged.
| |
| 529 pending_bitstream_ids_.pop(); | |
| 530 } | |
| 531 QueueAction(ACTION_DESTROY); | |
|
Pawel Osciak
2014/09/25 01:27:29
Do we actually need this? You dropped everything a
sandersd (OOO until July 31)
2014/09/25 19:18:52
We tell the client we are done with the buffers, b
Pawel Osciak
2014/09/26 04:25:07
How do you guarantee that during that client_ will
sandersd (OOO until July 31)
2014/09/26 04:56:41
During destroy, ProcessDecodedFrames() specificall
Pawel Osciak
2014/09/26 05:20:30
Could you tell me how this is achieved please?
sandersd (OOO until July 31)
2014/09/26 06:56:02
Because there is a queued ACTION_DESTROY, it alway
| |
| 402 } | 532 } |
| 403 | 533 |
| 404 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 534 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
| 405 return false; | 535 return false; |
| 406 } | 536 } |
| 407 | 537 |
| 408 } // namespace content | 538 } // namespace content |
| OLD | NEW |