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/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 139 nalu_data_ptrs.size(), // parameter_set_count | 139 nalu_data_ptrs.size(), // parameter_set_count |
| 140 &nalu_data_ptrs.front(), // ¶meter_set_pointers | 140 &nalu_data_ptrs.front(), // ¶meter_set_pointers |
| 141 &nalu_data_sizes.front(), // ¶meter_set_sizes | 141 &nalu_data_sizes.front(), // ¶meter_set_sizes |
| 142 kNALUHeaderLength, // nal_unit_header_length | 142 kNALUHeaderLength, // nal_unit_header_length |
| 143 format_.InitializeInto()); | 143 format_.InitializeInto()); |
| 144 if (status) { | 144 if (status) { |
| 145 NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()", | 145 NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()", |
| 146 status); | 146 status); |
| 147 return false; | 147 return false; |
| 148 } | 148 } |
| 149 CMVideoDimensions coded_dimensions = | 149 |
| 150 CMVideoFormatDescriptionGetDimensions(format_); | 150 // If the session is compatible, there's nothing to do. |
| 151 if (session_ && | |
| 152 VTDecompressionSessionCanAcceptFormatDescription(session_, format_)) { | |
| 153 return true; | |
| 154 } | |
| 151 | 155 |
| 152 // Prepare VideoToolbox configuration dictionaries. | 156 // Prepare VideoToolbox configuration dictionaries. |
| 153 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( | 157 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
| 154 CFDictionaryCreateMutable( | 158 CFDictionaryCreateMutable( |
| 155 kCFAllocatorDefault, | 159 kCFAllocatorDefault, |
| 156 1, // capacity | 160 1, // capacity |
| 157 &kCFTypeDictionaryKeyCallBacks, | 161 &kCFTypeDictionaryKeyCallBacks, |
| 158 &kCFTypeDictionaryValueCallBacks)); | 162 &kCFTypeDictionaryValueCallBacks)); |
| 159 | 163 |
| 160 CFDictionarySetValue( | 164 CFDictionarySetValue( |
| 161 decoder_config, | 165 decoder_config, |
| 162 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | 166 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
| 163 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | 167 CFSTR("EnableHardwareAcceleratedVideoDecoder"), |
| 164 kCFBooleanTrue); | 168 kCFBooleanTrue); |
| 165 | 169 |
| 166 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | 170 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
| 167 CFDictionaryCreateMutable( | 171 CFDictionaryCreateMutable( |
| 168 kCFAllocatorDefault, | 172 kCFAllocatorDefault, |
| 169 4, // capacity | 173 4, // capacity |
| 170 &kCFTypeDictionaryKeyCallBacks, | 174 &kCFTypeDictionaryKeyCallBacks, |
| 171 &kCFTypeDictionaryValueCallBacks)); | 175 &kCFTypeDictionaryValueCallBacks)); |
| 172 | 176 |
| 177 CMVideoDimensions coded_dimensions = | |
| 178 CMVideoFormatDescriptionGetDimensions(format_); | |
| 173 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | 179 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
| 174 // TODO(sandersd): RGBA option for 4:4:4 video. | 180 // TODO(sandersd): RGBA option for 4:4:4 video. |
| 175 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; | 181 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; |
| 176 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); | 182 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); |
| 177 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); | 183 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); |
| 178 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); | 184 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
| 179 #undef CFINT | 185 #undef CFINT |
| 180 CFDictionarySetValue( | 186 CFDictionarySetValue( |
| 181 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | 187 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
| 182 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | 188 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
| 183 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | 189 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
| 184 CFDictionarySetValue( | 190 CFDictionarySetValue( |
| 185 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | 191 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
| 186 | 192 |
| 187 // TODO(sandersd): Check if the session is already compatible. | 193 // TODO(sandersd): Does the old session need to be flushed first? |
| 188 session_.reset(); | 194 session_.reset(); |
| 189 status = VTDecompressionSessionCreate( | 195 status = VTDecompressionSessionCreate( |
| 190 kCFAllocatorDefault, | 196 kCFAllocatorDefault, |
| 191 format_, // video_format_description | 197 format_, // video_format_description |
| 192 decoder_config, // video_decoder_specification | 198 decoder_config, // video_decoder_specification |
| 193 image_config, // destination_image_buffer_attributes | 199 image_config, // destination_image_buffer_attributes |
| 194 &callback_, // output_callback | 200 &callback_, // output_callback |
| 195 session_.InitializeInto()); | 201 session_.InitializeInto()); |
| 196 if (status) { | 202 if (status) { |
| 197 NOTIFY_STATUS("VTDecompressionSessionCreate()", status); | 203 NOTIFY_STATUS("VTDecompressionSessionCreate()", status); |
| 198 return false; | 204 return false; |
| 199 } | 205 } |
| 200 | 206 |
| 201 // If the size has changed, trigger a request for new picture buffers. | |
| 202 // TODO(sandersd): Move to SendPictures(), and use this just as a hint for an | |
| 203 // upcoming size change. | |
| 204 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); | |
| 205 if (coded_size_ != new_coded_size) { | |
| 206 coded_size_ = new_coded_size; | |
| 207 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 208 &VTVideoDecodeAccelerator::SizeChangedTask, | |
| 209 weak_this_factory_.GetWeakPtr(), | |
| 210 coded_size_));; | |
| 211 } | |
| 212 | |
| 213 return true; | 207 return true; |
| 214 } | 208 } |
| 215 | 209 |
| 216 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 210 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
| 217 DCHECK(CalledOnValidThread()); | 211 DCHECK(CalledOnValidThread()); |
| 218 // Not actually a requirement of the VDA API, but we're lazy and use negative | 212 // Not actually a requirement of the VDA API, but we're lazy and use negative |
| 219 // values as flags internally. Revisit that if this actually happens. | 213 // values as flags internally. Revisit that if this actually happens. |
| 220 if (bitstream.id() < 0) { | 214 if (bitstream.id() < 0) { |
| 221 LOG(ERROR) << "Negative bitstream ID"; | 215 LOG(ERROR) << "Negative bitstream ID"; |
| 222 NotifyError(INVALID_ARGUMENT); | 216 NotifyError(INVALID_ARGUMENT); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 249 return; | 243 return; |
| 250 } | 244 } |
| 251 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); | 245 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); |
| 252 | 246 |
| 253 // NALUs are stored with Annex B format in the bitstream buffer (start codes), | 247 // NALUs are stored with Annex B format in the bitstream buffer (start codes), |
| 254 // but VideoToolbox expects AVCC/MPEG-4 format (length headers), so we must | 248 // but VideoToolbox expects AVCC/MPEG-4 format (length headers), so we must |
| 255 // rewrite the data. | 249 // rewrite the data. |
| 256 // | 250 // |
| 257 // 1. Locate relevant NALUs and compute the size of the translated data. | 251 // 1. Locate relevant NALUs and compute the size of the translated data. |
| 258 // Also record any parameter sets for VideoToolbox initialization. | 252 // Also record any parameter sets for VideoToolbox initialization. |
| 253 bool config_changed = false; | |
| 259 size_t data_size = 0; | 254 size_t data_size = 0; |
| 260 std::vector<media::H264NALU> nalus; | 255 std::vector<media::H264NALU> nalus; |
| 261 std::vector<const uint8_t*> config_nalu_data_ptrs; | |
| 262 std::vector<size_t> config_nalu_data_sizes; | |
| 263 parser_.SetStream(buf, size); | 256 parser_.SetStream(buf, size); |
| 264 media::H264NALU nalu; | 257 media::H264NALU nalu; |
| 265 while (true) { | 258 while (true) { |
| 266 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); | 259 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); |
| 267 if (result == media::H264Parser::kEOStream) | 260 if (result == media::H264Parser::kEOStream) |
| 268 break; | 261 break; |
| 269 if (result != media::H264Parser::kOk) { | 262 if (result != media::H264Parser::kOk) { |
| 270 LOG(ERROR) << "Failed to find H.264 NALU"; | 263 LOG(ERROR) << "Failed to find H.264 NALU"; |
| 271 NotifyError(PLATFORM_FAILURE); | 264 NotifyError(PLATFORM_FAILURE); |
| 272 return; | 265 return; |
| 273 } | 266 } |
| 274 // TODO(sandersd): Check that these are only at the start. | 267 // TODO(sandersd): Strict ordering rules. |
| 275 if (nalu.nal_unit_type == media::H264NALU::kSPS || | 268 switch (nalu.nal_unit_type) { |
| 276 nalu.nal_unit_type == media::H264NALU::kPPS || | 269 case media::H264NALU::kSPS: |
| 277 nalu.nal_unit_type == media::H264NALU::kSPSExt) { | 270 last_sps_.assign(nalu.data, nalu.data + nalu.size); |
| 278 DVLOG(2) << "Parameter set " << nalu.nal_unit_type; | 271 last_spsext_.clear(); |
| 279 config_nalu_data_ptrs.push_back(nalu.data); | 272 config_changed = true; |
| 280 config_nalu_data_sizes.push_back(nalu.size); | 273 break; |
| 281 } else { | 274 case media::H264NALU::kSPSExt: |
| 282 nalus.push_back(nalu); | 275 // TODO(sandersd): Check that the previous NALU was an SPS. |
| 283 data_size += kNALUHeaderLength + nalu.size; | 276 last_spsext_.assign(nalu.data, nalu.data + nalu.size); |
| 277 config_changed = true; | |
| 278 break; | |
| 279 case media::H264NALU::kPPS: | |
|
Pawel Osciak
2014/10/30 11:02:52
Does PPS actually change config? PPSes don't have
sandersd (OOO until July 31)
2014/10/30 22:54:40
If the PPS changes, then VT needs to be reconfigur
| |
| 280 last_pps_.assign(nalu.data, nalu.data + nalu.size); | |
| 281 config_changed = true; | |
| 282 break; | |
| 283 default: | |
| 284 nalus.push_back(nalu); | |
| 285 data_size += kNALUHeaderLength + nalu.size; | |
| 286 break; | |
| 284 } | 287 } |
| 285 } | 288 } |
| 286 | 289 |
| 287 // 2. Initialize VideoToolbox. | 290 // 2. Initialize VideoToolbox. |
| 288 // TODO(sandersd): Reinitialize when there are new parameter sets. | 291 // TODO(sandersd): Check if the new configuration is identical before |
| 289 if (!session_) { | 292 // reconfiguring. |
| 290 // If configuring fails, ConfigureDecoder() already called NotifyError(). | 293 if (config_changed && last_sps_.size() != 0 && last_pps_.size() != 0) { |
|
Pawel Osciak
2014/10/30 11:02:52
Shouldn't we return an error if config_changed, bu
sandersd (OOO until July 31)
2014/10/30 22:54:40
Seems reasonable. Done.
| |
| 291 if (!ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes)) | 294 // TODO(sandersd): Check that the SPS and PPS IDs match. |
| 295 std::vector<const uint8_t*> nalu_data_ptrs; | |
| 296 std::vector<size_t> nalu_data_sizes; | |
| 297 nalu_data_ptrs.push_back(&last_sps_.front()); | |
| 298 nalu_data_sizes.push_back(last_sps_.size()); | |
| 299 if (last_spsext_.size() != 0) { | |
| 300 nalu_data_ptrs.push_back(&last_spsext_.front()); | |
| 301 nalu_data_sizes.push_back(last_spsext_.size()); | |
| 302 } | |
| 303 nalu_data_ptrs.push_back(&last_pps_.front()); | |
| 304 nalu_data_sizes.push_back(last_pps_.size()); | |
| 305 | |
| 306 // If ConfigureDecoder() fails, it already called NotifyError(). | |
| 307 if (!ConfigureDecoder(nalu_data_ptrs, nalu_data_sizes)) | |
| 292 return; | 308 return; |
| 293 } | 309 } |
| 294 | 310 |
| 295 // If there are no non-configuration units, immediately return an empty | 311 // If there are no non-configuration units, immediately return an empty |
| 296 // (ie. dropped) frame. It is an error to create a MemoryBlock with zero | 312 // (ie. dropped) frame. It is an error to create a MemoryBlock with zero |
| 297 // size. | 313 // size. |
| 298 if (!data_size) | 314 if (!data_size) |
| 299 return; | 315 return; |
| 300 | 316 |
| 317 // If the session is not configured, fail. | |
| 318 if (!session_) { | |
| 319 LOG(ERROR) << "Image slice without configuration data"; | |
| 320 NotifyError(INVALID_ARGUMENT); | |
| 321 return; | |
| 322 } | |
| 323 | |
| 301 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. | 324 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. |
| 325 // TODO(sandersd): Check that the slice's PPS matches the current PPS. | |
|
Pawel Osciak
2014/10/30 11:02:52
That doesn't have to be the case... You could refe
sandersd (OOO until July 31)
2014/10/30 22:54:40
VT requires that we pack both the SPS and PPS into
| |
| 302 base::ScopedCFTypeRef<CMBlockBufferRef> data; | 326 base::ScopedCFTypeRef<CMBlockBufferRef> data; |
| 303 OSStatus status = CMBlockBufferCreateWithMemoryBlock( | 327 OSStatus status = CMBlockBufferCreateWithMemoryBlock( |
| 304 kCFAllocatorDefault, | 328 kCFAllocatorDefault, |
| 305 NULL, // &memory_block | 329 NULL, // &memory_block |
| 306 data_size, // block_length | 330 data_size, // block_length |
| 307 kCFAllocatorDefault, // block_allocator | 331 kCFAllocatorDefault, // block_allocator |
| 308 NULL, // &custom_block_source | 332 NULL, // &custom_block_source |
| 309 0, // offset_to_data | 333 0, // offset_to_data |
| 310 data_size, // data_length | 334 data_size, // data_length |
| 311 0, // flags | 335 0, // flags |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 399 weak_this_factory_.GetWeakPtr(), | 423 weak_this_factory_.GetWeakPtr(), |
| 400 DecodedFrame(bitstream_id, image_buffer))); | 424 DecodedFrame(bitstream_id, image_buffer))); |
| 401 } | 425 } |
| 402 | 426 |
| 403 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { | 427 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { |
| 404 DCHECK(CalledOnValidThread()); | 428 DCHECK(CalledOnValidThread()); |
| 405 decoded_frames_.push(frame); | 429 decoded_frames_.push(frame); |
| 406 ProcessDecodedFrames(); | 430 ProcessDecodedFrames(); |
| 407 } | 431 } |
| 408 | 432 |
| 409 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { | |
| 410 DCHECK(CalledOnValidThread()); | |
| 411 texture_size_ = coded_size; | |
| 412 // TODO(sandersd): Dismiss existing picture buffers. | |
| 413 client_->ProvidePictureBuffers( | |
| 414 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); | |
| 415 } | |
| 416 | |
| 417 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 433 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
| 418 const std::vector<media::PictureBuffer>& pictures) { | 434 const std::vector<media::PictureBuffer>& pictures) { |
| 419 DCHECK(CalledOnValidThread()); | 435 DCHECK(CalledOnValidThread()); |
| 420 | 436 |
| 421 for (size_t i = 0; i < pictures.size(); i++) { | 437 for (size_t i = 0; i < pictures.size(); i++) { |
| 422 DCHECK(!texture_ids_.count(pictures[i].id())); | 438 DCHECK(!texture_ids_.count(pictures[i].id())); |
| 423 available_picture_ids_.push(pictures[i].id()); | 439 assigned_picture_ids_.insert(pictures[i].id()); |
| 440 available_picture_ids_.push_back(pictures[i].id()); | |
| 424 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | 441 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); |
| 425 } | 442 } |
| 426 | 443 |
| 427 // Pictures are not marked as uncleared until after this method returns, and | 444 // Pictures are not marked as uncleared until after this method returns, and |
| 428 // they will be broken if they are used before that happens. So, schedule | 445 // they will be broken if they are used before that happens. So, schedule |
| 429 // future work after that happens. | 446 // future work after that happens. |
| 430 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 447 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 431 &VTVideoDecodeAccelerator::ProcessDecodedFrames, | 448 &VTVideoDecodeAccelerator::ProcessDecodedFrames, |
| 432 weak_this_factory_.GetWeakPtr())); | 449 weak_this_factory_.GetWeakPtr())); |
| 433 } | 450 } |
| 434 | 451 |
| 435 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 452 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
| 436 DCHECK(CalledOnValidThread()); | 453 DCHECK(CalledOnValidThread()); |
| 437 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); | 454 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); |
| 438 picture_bindings_.erase(picture_id); | 455 picture_bindings_.erase(picture_id); |
| 439 available_picture_ids_.push(picture_id); | 456 // Don't put the picture back in the available list if has been dismissed. |
| 440 ProcessDecodedFrames(); | 457 if (assigned_picture_ids_.count(picture_id) != 0) { |
| 458 available_picture_ids_.push_back(picture_id); | |
| 459 ProcessDecodedFrames(); | |
| 460 } | |
| 441 } | 461 } |
| 442 | 462 |
| 443 void VTVideoDecodeAccelerator::CompleteAction(Action action) { | 463 void VTVideoDecodeAccelerator::CompleteAction(Action action) { |
| 444 DCHECK(CalledOnValidThread()); | 464 DCHECK(CalledOnValidThread()); |
| 465 | |
| 445 switch (action) { | 466 switch (action) { |
| 446 case ACTION_FLUSH: | 467 case ACTION_FLUSH: |
| 447 client_->NotifyFlushDone(); | 468 client_->NotifyFlushDone(); |
| 448 break; | 469 break; |
| 449 case ACTION_RESET: | 470 case ACTION_RESET: |
| 450 client_->NotifyResetDone(); | 471 client_->NotifyResetDone(); |
| 451 break; | 472 break; |
| 452 case ACTION_DESTROY: | 473 case ACTION_DESTROY: |
| 453 delete this; | 474 delete this; |
| 454 break; | 475 break; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 519 // until more frames are decoded. | 540 // until more frames are decoded. |
| 520 if (last_sent_bitstream_id != next_action_bitstream_id) | 541 if (last_sent_bitstream_id != next_action_bitstream_id) |
| 521 return; | 542 return; |
| 522 | 543 |
| 523 // Complete all actions pending for this |bitstream_id|, then loop to see | 544 // Complete all actions pending for this |bitstream_id|, then loop to see |
| 524 // if progress can be made on the next action. | 545 // if progress can be made on the next action. |
| 525 CompleteActions(next_action_bitstream_id); | 546 CompleteActions(next_action_bitstream_id); |
| 526 } | 547 } |
| 527 } | 548 } |
| 528 | 549 |
| 550 int32_t VTVideoDecodeAccelerator::ProcessDroppedFrames( | |
| 551 int32_t last_sent_bitstream_id, | |
| 552 int32_t up_to_bitstream_id) { | |
| 553 DCHECK(CalledOnValidThread()); | |
| 554 while (!decoded_frames_.empty() && | |
|
Pawel Osciak
2014/10/30 11:02:52
Please document, this is getting pretty convoluted
sandersd (OOO until July 31)
2014/10/30 22:54:40
Done.
| |
| 555 last_sent_bitstream_id != up_to_bitstream_id && | |
| 556 decoded_frames_.front().image_buffer.get() == NULL) { | |
| 557 const DecodedFrame& frame = decoded_frames_.front(); | |
| 558 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); | |
| 559 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | |
| 560 last_sent_bitstream_id = frame.bitstream_id; | |
| 561 decoded_frames_.pop(); | |
| 562 pending_bitstream_ids_.pop(); | |
| 563 } | |
| 564 return last_sent_bitstream_id; | |
| 565 } | |
| 566 | |
| 567 // TODO(sandersd): If GpuVideoDecoder didn't specifically check the size, this | |
|
Pawel Osciak
2014/10/30 11:02:52
Could you explain this comment please?
sandersd (OOO until July 31)
2014/10/30 22:54:40
Done.
| |
| 568 // would be unnecessary, as the size is not a property of the texture. | |
| 569 void VTVideoDecodeAccelerator::ProcessSizeChange() { | |
| 570 DCHECK(CalledOnValidThread()); | |
| 571 DCHECK(!decoded_frames_.empty()); | |
| 572 | |
| 573 // Find the size of the next image. | |
| 574 const DecodedFrame& frame = decoded_frames_.front(); | |
| 575 CVImageBufferRef image_buffer = frame.image_buffer.get(); | |
| 576 size_t width = CVPixelBufferGetWidth(image_buffer); | |
| 577 size_t height = CVPixelBufferGetHeight(image_buffer); | |
| 578 gfx::Size image_size(width, height); | |
| 579 | |
| 580 if (picture_size_ != image_size) { | |
| 581 // Dismiss all assigned picture buffers. | |
| 582 for (int32_t picture_id : assigned_picture_ids_) | |
| 583 client_->DismissPictureBuffer(picture_id); | |
| 584 assigned_picture_ids_.clear(); | |
| 585 available_picture_ids_.clear(); | |
| 586 | |
| 587 // Request new pictures. | |
| 588 client_->ProvidePictureBuffers( | |
| 589 kNumPictureBuffers, image_size, GL_TEXTURE_RECTANGLE_ARB); | |
| 590 picture_size_ = image_size; | |
| 591 } | |
| 592 } | |
| 593 | |
| 529 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { | 594 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { |
| 530 DCHECK(CalledOnValidThread()); | 595 DCHECK(CalledOnValidThread()); |
| 531 DCHECK(!decoded_frames_.empty()); | 596 DCHECK(!decoded_frames_.empty()); |
| 532 | 597 |
| 533 // TODO(sandersd): Store the actual last sent bitstream ID? | 598 // TODO(sandersd): Store the actual last sent bitstream ID? |
| 534 int32_t last_sent_bitstream_id = -1; | 599 int32_t last_sent_bitstream_id = -1; |
| 535 | 600 |
| 601 last_sent_bitstream_id = | |
| 602 ProcessDroppedFrames(last_sent_bitstream_id, up_to_bitstream_id); | |
| 603 if (last_sent_bitstream_id == up_to_bitstream_id || decoded_frames_.empty()) | |
| 604 return last_sent_bitstream_id; | |
| 605 | |
| 606 ProcessSizeChange(); | |
| 536 if (available_picture_ids_.empty()) | 607 if (available_picture_ids_.empty()) |
| 537 return last_sent_bitstream_id; | 608 return last_sent_bitstream_id; |
| 538 | 609 |
| 539 if (!make_context_current_.Run()) { | 610 if (!make_context_current_.Run()) { |
| 540 LOG(ERROR) << "Failed to make GL context current"; | 611 LOG(ERROR) << "Failed to make GL context current"; |
| 541 NotifyError(PLATFORM_FAILURE); | 612 NotifyError(PLATFORM_FAILURE); |
| 542 return last_sent_bitstream_id; | 613 return last_sent_bitstream_id; |
| 543 } | 614 } |
| 544 | 615 |
| 545 glEnable(GL_TEXTURE_RECTANGLE_ARB); | 616 glEnable(GL_TEXTURE_RECTANGLE_ARB); |
| 546 while (!available_picture_ids_.empty() && | 617 while (!available_picture_ids_.empty() && !has_error_) { |
| 547 !decoded_frames_.empty() && | 618 DCHECK_NE(last_sent_bitstream_id, up_to_bitstream_id); |
| 548 last_sent_bitstream_id != up_to_bitstream_id && | 619 DCHECK(!decoded_frames_.empty()); |
| 549 !has_error_) { | 620 |
| 550 // We don't pop |frame| until it is consumed, which won't happen if an | 621 // We don't pop |frame| or |picture_id| until they are consumed, which may |
| 551 // error occurs. Conveniently, this also removes some refcounting. | 622 // not happen if an error occurs. Conveniently, this also removes some |
| 623 // refcounting. | |
| 552 const DecodedFrame& frame = decoded_frames_.front(); | 624 const DecodedFrame& frame = decoded_frames_.front(); |
| 553 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); | 625 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); |
| 554 | 626 int32_t picture_id = available_picture_ids_.back(); |
| 555 // Likewise, |picture_id| won't be popped if |image_buffer| is NULL or an | |
| 556 // error occurs. | |
| 557 // TODO(sandersd): Don't block waiting for a |picture_id| when | |
| 558 // |image_buffer| is NULL. | |
| 559 int32_t picture_id = available_picture_ids_.front(); | |
| 560 | 627 |
| 561 CVImageBufferRef image_buffer = frame.image_buffer.get(); | 628 CVImageBufferRef image_buffer = frame.image_buffer.get(); |
| 562 if (image_buffer) { | 629 IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer); |
| 563 IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer); | |
| 564 | 630 |
| 565 gfx::ScopedTextureBinder | 631 gfx::ScopedTextureBinder |
| 566 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); | 632 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); |
| 567 CGLError status = CGLTexImageIOSurface2D( | 633 CGLError status = CGLTexImageIOSurface2D( |
| 568 cgl_context_, // ctx | 634 cgl_context_, // ctx |
| 569 GL_TEXTURE_RECTANGLE_ARB, // target | 635 GL_TEXTURE_RECTANGLE_ARB, // target |
| 570 GL_RGB, // internal_format | 636 GL_RGB, // internal_format |
| 571 texture_size_.width(), // width | 637 picture_size_.width(), // width |
| 572 texture_size_.height(), // height | 638 picture_size_.height(), // height |
| 573 GL_YCBCR_422_APPLE, // format | 639 GL_YCBCR_422_APPLE, // format |
| 574 GL_UNSIGNED_SHORT_8_8_APPLE, // type | 640 GL_UNSIGNED_SHORT_8_8_APPLE, // type |
| 575 surface, // io_surface | 641 surface, // io_surface |
| 576 0); // plane | 642 0); // plane |
| 577 if (status != kCGLNoError) { | 643 if (status != kCGLNoError) { |
| 578 NOTIFY_STATUS("CGLTexImageIOSurface2D()", status); | 644 NOTIFY_STATUS("CGLTexImageIOSurface2D()", status); |
| 579 break; | 645 break; |
| 580 } | |
| 581 | |
| 582 picture_bindings_[picture_id] = frame.image_buffer; | |
| 583 client_->PictureReady(media::Picture( | |
| 584 picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); | |
| 585 available_picture_ids_.pop(); | |
| 586 } | 646 } |
| 587 | 647 |
| 648 picture_bindings_[picture_id] = frame.image_buffer; | |
| 649 client_->PictureReady(media::Picture( | |
| 650 picture_id, frame.bitstream_id, gfx::Rect(picture_size_))); | |
| 651 available_picture_ids_.pop_back(); | |
| 588 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | 652 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
| 589 last_sent_bitstream_id = frame.bitstream_id; | 653 last_sent_bitstream_id = frame.bitstream_id; |
| 590 decoded_frames_.pop(); | 654 decoded_frames_.pop(); |
| 591 pending_bitstream_ids_.pop(); | 655 pending_bitstream_ids_.pop(); |
| 656 | |
| 657 last_sent_bitstream_id = | |
| 658 ProcessDroppedFrames(last_sent_bitstream_id, up_to_bitstream_id); | |
| 659 if (last_sent_bitstream_id == up_to_bitstream_id || decoded_frames_.empty()) | |
| 660 break; | |
| 661 | |
| 662 ProcessSizeChange(); | |
| 592 } | 663 } |
| 593 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 664 glDisable(GL_TEXTURE_RECTANGLE_ARB); |
| 594 | 665 |
| 595 return last_sent_bitstream_id; | 666 return last_sent_bitstream_id; |
| 596 } | 667 } |
| 597 | 668 |
| 598 void VTVideoDecodeAccelerator::FlushTask() { | 669 void VTVideoDecodeAccelerator::FlushTask() { |
| 599 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 670 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 600 OSStatus status = VTDecompressionSessionFinishDelayedFrames(session_); | 671 OSStatus status = VTDecompressionSessionFinishDelayedFrames(session_); |
| 601 if (status) | 672 if (status) |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 661 pending_bitstream_ids_.pop(); | 732 pending_bitstream_ids_.pop(); |
| 662 } | 733 } |
| 663 QueueAction(ACTION_DESTROY); | 734 QueueAction(ACTION_DESTROY); |
| 664 } | 735 } |
| 665 | 736 |
| 666 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 737 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
| 667 return false; | 738 return false; |
| 668 } | 739 } |
| 669 | 740 |
| 670 } // namespace content | 741 } // namespace content |
| OLD | NEW |