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: | |
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) { |
291 if (!ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes)) | 294 if (last_sps_.size() == 0 || last_pps_.size() == 0) { |
295 LOG(ERROR) << "Invalid configuration data"; | |
296 NotifyError(INVALID_ARGUMENT); | |
297 return; | |
298 } | |
299 // TODO(sandersd): Check that the SPS and PPS IDs match. | |
300 std::vector<const uint8_t*> nalu_data_ptrs; | |
301 std::vector<size_t> nalu_data_sizes; | |
302 nalu_data_ptrs.push_back(&last_sps_.front()); | |
303 nalu_data_sizes.push_back(last_sps_.size()); | |
304 if (last_spsext_.size() != 0) { | |
305 nalu_data_ptrs.push_back(&last_spsext_.front()); | |
306 nalu_data_sizes.push_back(last_spsext_.size()); | |
307 } | |
308 nalu_data_ptrs.push_back(&last_pps_.front()); | |
309 nalu_data_sizes.push_back(last_pps_.size()); | |
310 | |
311 // If ConfigureDecoder() fails, it already called NotifyError(). | |
312 if (!ConfigureDecoder(nalu_data_ptrs, nalu_data_sizes)) | |
292 return; | 313 return; |
293 } | 314 } |
294 | 315 |
295 // If there are no non-configuration units, immediately return an empty | 316 // 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 | 317 // (ie. dropped) frame. It is an error to create a MemoryBlock with zero |
297 // size. | 318 // size. |
298 if (!data_size) | 319 if (!data_size) |
299 return; | 320 return; |
300 | 321 |
322 // If the session is not configured, fail. | |
323 if (!session_) { | |
324 LOG(ERROR) << "Image slice without configuration data"; | |
325 NotifyError(INVALID_ARGUMENT); | |
326 return; | |
327 } | |
328 | |
301 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. | 329 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. |
330 // TODO(sandersd): Check that the slice's PPS matches the current PPS. | |
302 base::ScopedCFTypeRef<CMBlockBufferRef> data; | 331 base::ScopedCFTypeRef<CMBlockBufferRef> data; |
303 OSStatus status = CMBlockBufferCreateWithMemoryBlock( | 332 OSStatus status = CMBlockBufferCreateWithMemoryBlock( |
304 kCFAllocatorDefault, | 333 kCFAllocatorDefault, |
305 NULL, // &memory_block | 334 NULL, // &memory_block |
306 data_size, // block_length | 335 data_size, // block_length |
307 kCFAllocatorDefault, // block_allocator | 336 kCFAllocatorDefault, // block_allocator |
308 NULL, // &custom_block_source | 337 NULL, // &custom_block_source |
309 0, // offset_to_data | 338 0, // offset_to_data |
310 data_size, // data_length | 339 data_size, // data_length |
311 0, // flags | 340 0, // flags |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
380 | 409 |
381 // This method may be called on any VideoToolbox thread. | 410 // This method may be called on any VideoToolbox thread. |
382 void VTVideoDecodeAccelerator::Output( | 411 void VTVideoDecodeAccelerator::Output( |
383 int32_t bitstream_id, | 412 int32_t bitstream_id, |
384 OSStatus status, | 413 OSStatus status, |
385 CVImageBufferRef image_buffer) { | 414 CVImageBufferRef image_buffer) { |
386 if (status) { | 415 if (status) { |
387 // TODO(sandersd): Handle dropped frames. | 416 // TODO(sandersd): Handle dropped frames. |
388 NOTIFY_STATUS("Decoding", status); | 417 NOTIFY_STATUS("Decoding", status); |
389 image_buffer = NULL; | 418 image_buffer = NULL; |
390 } else if (CFGetTypeID(image_buffer) != CVPixelBufferGetTypeID()) { | 419 } else if (CFGetTypeID(image_buffer) != CVPixelBufferGetTypeID()) { |
391 LOG(ERROR) << "Decoded frame is not a CVPixelBuffer"; | 420 LOG(ERROR) << "Decoded frame is not a CVPixelBuffer"; |
392 NotifyError(PLATFORM_FAILURE); | 421 NotifyError(PLATFORM_FAILURE); |
393 image_buffer = NULL; | 422 image_buffer = NULL; |
394 } else { | 423 } else { |
395 CFRetain(image_buffer); | 424 CFRetain(image_buffer); |
396 } | 425 } |
397 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 426 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
398 &VTVideoDecodeAccelerator::OutputTask, | 427 &VTVideoDecodeAccelerator::OutputTask, |
399 weak_this_factory_.GetWeakPtr(), | 428 weak_this_factory_.GetWeakPtr(), |
400 DecodedFrame(bitstream_id, image_buffer))); | 429 DecodedFrame(bitstream_id, image_buffer))); |
401 } | 430 } |
402 | 431 |
403 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { | 432 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { |
404 DCHECK(CalledOnValidThread()); | 433 DCHECK(CalledOnValidThread()); |
405 decoded_frames_.push(frame); | 434 decoded_frames_.push(frame); |
406 ProcessDecodedFrames(); | 435 ProcessDecodedFrames(); |
407 } | 436 } |
408 | 437 |
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( | 438 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
418 const std::vector<media::PictureBuffer>& pictures) { | 439 const std::vector<media::PictureBuffer>& pictures) { |
419 DCHECK(CalledOnValidThread()); | 440 DCHECK(CalledOnValidThread()); |
420 | 441 |
421 for (size_t i = 0; i < pictures.size(); i++) { | 442 for (size_t i = 0; i < pictures.size(); i++) { |
422 DCHECK(!texture_ids_.count(pictures[i].id())); | 443 DCHECK(!texture_ids_.count(pictures[i].id())); |
423 available_picture_ids_.push(pictures[i].id()); | 444 assigned_picture_ids_.insert(pictures[i].id()); |
445 available_picture_ids_.push_back(pictures[i].id()); | |
424 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | 446 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); |
425 } | 447 } |
426 | 448 |
427 // Pictures are not marked as uncleared until after this method returns, and | 449 // 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 | 450 // they will be broken if they are used before that happens. So, schedule |
429 // future work after that happens. | 451 // future work after that happens. |
430 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 452 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
431 &VTVideoDecodeAccelerator::ProcessDecodedFrames, | 453 &VTVideoDecodeAccelerator::ProcessDecodedFrames, |
432 weak_this_factory_.GetWeakPtr())); | 454 weak_this_factory_.GetWeakPtr())); |
433 } | 455 } |
434 | 456 |
435 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 457 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
436 DCHECK(CalledOnValidThread()); | 458 DCHECK(CalledOnValidThread()); |
437 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); | 459 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); |
438 picture_bindings_.erase(picture_id); | 460 picture_bindings_.erase(picture_id); |
439 available_picture_ids_.push(picture_id); | 461 // Don't put the picture back in the available list if has been dismissed. |
440 ProcessDecodedFrames(); | 462 if (assigned_picture_ids_.count(picture_id) != 0) { |
463 available_picture_ids_.push_back(picture_id); | |
464 ProcessDecodedFrames(); | |
465 } | |
441 } | 466 } |
442 | 467 |
443 void VTVideoDecodeAccelerator::CompleteAction(Action action) { | 468 void VTVideoDecodeAccelerator::CompleteAction(Action action) { |
444 DCHECK(CalledOnValidThread()); | 469 DCHECK(CalledOnValidThread()); |
470 | |
445 switch (action) { | 471 switch (action) { |
446 case ACTION_FLUSH: | 472 case ACTION_FLUSH: |
447 client_->NotifyFlushDone(); | 473 client_->NotifyFlushDone(); |
448 break; | 474 break; |
449 case ACTION_RESET: | 475 case ACTION_RESET: |
450 client_->NotifyResetDone(); | 476 client_->NotifyResetDone(); |
451 break; | 477 break; |
452 case ACTION_DESTROY: | 478 case ACTION_DESTROY: |
453 delete this; | 479 delete this; |
454 break; | 480 break; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
519 // until more frames are decoded. | 545 // until more frames are decoded. |
520 if (last_sent_bitstream_id != next_action_bitstream_id) | 546 if (last_sent_bitstream_id != next_action_bitstream_id) |
521 return; | 547 return; |
522 | 548 |
523 // Complete all actions pending for this |bitstream_id|, then loop to see | 549 // Complete all actions pending for this |bitstream_id|, then loop to see |
524 // if progress can be made on the next action. | 550 // if progress can be made on the next action. |
525 CompleteActions(next_action_bitstream_id); | 551 CompleteActions(next_action_bitstream_id); |
526 } | 552 } |
527 } | 553 } |
528 | 554 |
555 int32_t VTVideoDecodeAccelerator::ProcessDroppedFrames( | |
556 int32_t last_sent_bitstream_id, | |
557 int32_t up_to_bitstream_id) { | |
558 DCHECK(CalledOnValidThread()); | |
559 // Drop frames as long as there is a frame, we have not reached the next | |
560 // action, and the next frame has no image. | |
561 while (!decoded_frames_.empty() && | |
562 last_sent_bitstream_id != up_to_bitstream_id && | |
563 decoded_frames_.front().image_buffer.get() == NULL) { | |
564 const DecodedFrame& frame = decoded_frames_.front(); | |
565 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); | |
566 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | |
567 last_sent_bitstream_id = frame.bitstream_id; | |
568 decoded_frames_.pop(); | |
569 pending_bitstream_ids_.pop(); | |
570 } | |
571 return last_sent_bitstream_id; | |
572 } | |
573 | |
574 // TODO(sandersd): If GpuVideoDecoder didn't specifically check the size of | |
575 // textures, this would be unnecessary, as the size is actually a property of | |
576 // the texture binding, not the texture. We rebind every frame, so the size | |
577 // passed to ProvidePictureBuffers() is meaningless. | |
578 void VTVideoDecodeAccelerator::ProcessSizeChange() { | |
579 DCHECK(CalledOnValidThread()); | |
580 DCHECK(!decoded_frames_.empty()); | |
581 | |
582 // Find the size of the next image. | |
583 const DecodedFrame& frame = decoded_frames_.front(); | |
584 CVImageBufferRef image_buffer = frame.image_buffer.get(); | |
585 size_t width = CVPixelBufferGetWidth(image_buffer); | |
586 size_t height = CVPixelBufferGetHeight(image_buffer); | |
587 gfx::Size image_size(width, height); | |
588 | |
589 if (picture_size_ != image_size) { | |
590 // Dismiss all assigned picture buffers. | |
591 for (int32_t picture_id : assigned_picture_ids_) | |
592 client_->DismissPictureBuffer(picture_id); | |
593 assigned_picture_ids_.clear(); | |
594 available_picture_ids_.clear(); | |
595 | |
596 // Request new pictures. | |
597 client_->ProvidePictureBuffers( | |
598 kNumPictureBuffers, image_size, GL_TEXTURE_RECTANGLE_ARB); | |
599 picture_size_ = image_size; | |
600 } | |
601 } | |
602 | |
529 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { | 603 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { |
530 DCHECK(CalledOnValidThread()); | 604 DCHECK(CalledOnValidThread()); |
531 DCHECK(!decoded_frames_.empty()); | 605 DCHECK(!decoded_frames_.empty()); |
532 | 606 |
533 // TODO(sandersd): Store the actual last sent bitstream ID? | 607 // TODO(sandersd): Store the actual last sent bitstream ID? |
534 int32_t last_sent_bitstream_id = -1; | 608 int32_t last_sent_bitstream_id = -1; |
535 | 609 |
610 last_sent_bitstream_id = | |
611 ProcessDroppedFrames(last_sent_bitstream_id, up_to_bitstream_id); | |
612 if (last_sent_bitstream_id == up_to_bitstream_id || decoded_frames_.empty()) | |
613 return last_sent_bitstream_id; | |
614 | |
615 ProcessSizeChange(); | |
536 if (available_picture_ids_.empty()) | 616 if (available_picture_ids_.empty()) |
537 return last_sent_bitstream_id; | 617 return last_sent_bitstream_id; |
538 | 618 |
539 if (!make_context_current_.Run()) { | 619 if (!make_context_current_.Run()) { |
540 LOG(ERROR) << "Failed to make GL context current"; | 620 LOG(ERROR) << "Failed to make GL context current"; |
541 NotifyError(PLATFORM_FAILURE); | 621 NotifyError(PLATFORM_FAILURE); |
542 return last_sent_bitstream_id; | 622 return last_sent_bitstream_id; |
543 } | 623 } |
544 | 624 |
545 glEnable(GL_TEXTURE_RECTANGLE_ARB); | 625 glEnable(GL_TEXTURE_RECTANGLE_ARB); |
546 while (!available_picture_ids_.empty() && | 626 while (!available_picture_ids_.empty() && !has_error_) { |
547 !decoded_frames_.empty() && | 627 DCHECK_NE(last_sent_bitstream_id, up_to_bitstream_id); |
548 last_sent_bitstream_id != up_to_bitstream_id && | 628 DCHECK(!decoded_frames_.empty()); |
Pawel Osciak
2014/11/07 10:09:38
Can this ever fail given l612 and l668?
sandersd (OOO until July 31)
2014/11/07 19:01:11
No, I just make it explicit because the next line
| |
549 !has_error_) { | 629 |
550 // We don't pop |frame| until it is consumed, which won't happen if an | 630 // We don't pop |frame| or |picture_id| until they are consumed, which may |
551 // error occurs. Conveniently, this also removes some refcounting. | 631 // not happen if an error occurs. Conveniently, this also removes some |
632 // refcounting. | |
552 const DecodedFrame& frame = decoded_frames_.front(); | 633 const DecodedFrame& frame = decoded_frames_.front(); |
553 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); | 634 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); |
554 | 635 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 | 636 |
561 CVImageBufferRef image_buffer = frame.image_buffer.get(); | 637 CVImageBufferRef image_buffer = frame.image_buffer.get(); |
562 if (image_buffer) { | 638 IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer); |
563 IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer); | |
564 | 639 |
565 gfx::ScopedTextureBinder | 640 gfx::ScopedTextureBinder |
566 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); | 641 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); |
567 CGLError status = CGLTexImageIOSurface2D( | 642 CGLError status = CGLTexImageIOSurface2D( |
568 cgl_context_, // ctx | 643 cgl_context_, // ctx |
569 GL_TEXTURE_RECTANGLE_ARB, // target | 644 GL_TEXTURE_RECTANGLE_ARB, // target |
570 GL_RGB, // internal_format | 645 GL_RGB, // internal_format |
571 texture_size_.width(), // width | 646 picture_size_.width(), // width |
572 texture_size_.height(), // height | 647 picture_size_.height(), // height |
573 GL_YCBCR_422_APPLE, // format | 648 GL_YCBCR_422_APPLE, // format |
574 GL_UNSIGNED_SHORT_8_8_APPLE, // type | 649 GL_UNSIGNED_SHORT_8_8_APPLE, // type |
575 surface, // io_surface | 650 surface, // io_surface |
576 0); // plane | 651 0); // plane |
577 if (status != kCGLNoError) { | 652 if (status != kCGLNoError) { |
578 NOTIFY_STATUS("CGLTexImageIOSurface2D()", status); | 653 NOTIFY_STATUS("CGLTexImageIOSurface2D()", status); |
579 break; | 654 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 } | 655 } |
587 | 656 |
657 picture_bindings_[picture_id] = frame.image_buffer; | |
658 client_->PictureReady(media::Picture( | |
659 picture_id, frame.bitstream_id, gfx::Rect(picture_size_))); | |
660 available_picture_ids_.pop_back(); | |
588 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | 661 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
589 last_sent_bitstream_id = frame.bitstream_id; | 662 last_sent_bitstream_id = frame.bitstream_id; |
590 decoded_frames_.pop(); | 663 decoded_frames_.pop(); |
591 pending_bitstream_ids_.pop(); | 664 pending_bitstream_ids_.pop(); |
665 | |
666 last_sent_bitstream_id = | |
667 ProcessDroppedFrames(last_sent_bitstream_id, up_to_bitstream_id); | |
668 if (last_sent_bitstream_id == up_to_bitstream_id || decoded_frames_.empty()) | |
669 break; | |
670 | |
671 ProcessSizeChange(); | |
592 } | 672 } |
593 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 673 glDisable(GL_TEXTURE_RECTANGLE_ARB); |
594 | 674 |
595 return last_sent_bitstream_id; | 675 return last_sent_bitstream_id; |
596 } | 676 } |
597 | 677 |
598 void VTVideoDecodeAccelerator::FlushTask() { | 678 void VTVideoDecodeAccelerator::FlushTask() { |
599 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 679 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
600 OSStatus status = VTDecompressionSessionFinishDelayedFrames(session_); | 680 OSStatus status = VTDecompressionSessionFinishDelayedFrames(session_); |
601 if (status) | 681 if (status) |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
661 pending_bitstream_ids_.pop(); | 741 pending_bitstream_ids_.pop(); |
662 } | 742 } |
663 QueueAction(ACTION_DESTROY); | 743 QueueAction(ACTION_DESTROY); |
664 } | 744 } |
665 | 745 |
666 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 746 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
667 return false; | 747 return false; |
668 } | 748 } |
669 | 749 |
670 } // namespace content | 750 } // namespace content |
OLD | NEW |