| 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 "content/common/gpu/media/vt_video_decode_accelerator_mac.h" | 5 #include "media/gpu/vt_video_decode_accelerator_mac.h" |
| 6 | 6 |
| 7 #include <CoreVideo/CoreVideo.h> | 7 #include <CoreVideo/CoreVideo.h> |
| 8 #include <OpenGL/CGLIOSurface.h> | 8 #include <OpenGL/CGLIOSurface.h> |
| 9 #include <OpenGL/gl.h> | 9 #include <OpenGL/gl.h> |
| 10 #include <stddef.h> | 10 #include <stddef.h> |
| 11 | 11 |
| 12 #include <algorithm> | 12 #include <algorithm> |
| 13 | 13 |
| 14 #include "base/bind.h" | 14 #include "base/bind.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/mac/mac_logging.h" | 16 #include "base/mac/mac_logging.h" |
| 17 #include "base/macros.h" | 17 #include "base/macros.h" |
| 18 #include "base/memory/ptr_util.h" | 18 #include "base/memory/ptr_util.h" |
| 19 #include "base/metrics/histogram_macros.h" | 19 #include "base/metrics/histogram_macros.h" |
| 20 #include "base/sys_byteorder.h" | 20 #include "base/sys_byteorder.h" |
| 21 #include "base/sys_info.h" | 21 #include "base/sys_info.h" |
| 22 #include "base/thread_task_runner_handle.h" | 22 #include "base/thread_task_runner_handle.h" |
| 23 #include "base/version.h" | 23 #include "base/version.h" |
| 24 #include "media/base/limits.h" | 24 #include "media/base/limits.h" |
| 25 #include "ui/gl/gl_context.h" | 25 #include "ui/gl/gl_context.h" |
| 26 #include "ui/gl/gl_image_io_surface.h" | 26 #include "ui/gl/gl_image_io_surface.h" |
| 27 #include "ui/gl/gl_implementation.h" | 27 #include "ui/gl/gl_implementation.h" |
| 28 #include "ui/gl/scoped_binders.h" | 28 #include "ui/gl/scoped_binders.h" |
| 29 | 29 |
| 30 using content_common_gpu_media::kModuleVt; | 30 using media_gpu::kModuleVt; |
| 31 using content_common_gpu_media::InitializeStubs; | 31 using media_gpu::InitializeStubs; |
| 32 using content_common_gpu_media::IsVtInitialized; | 32 using media_gpu::IsVtInitialized; |
| 33 using content_common_gpu_media::StubPathMap; | 33 using media_gpu::StubPathMap; |
| 34 | 34 |
| 35 #define NOTIFY_STATUS(name, status, session_failure) \ | 35 #define NOTIFY_STATUS(name, status, session_failure) \ |
| 36 do { \ | 36 do { \ |
| 37 OSSTATUS_DLOG(ERROR, status) << name; \ | 37 OSSTATUS_DLOG(ERROR, status) << name; \ |
| 38 NotifyError(PLATFORM_FAILURE, session_failure); \ | 38 NotifyError(PLATFORM_FAILURE, session_failure); \ |
| 39 } while (0) | 39 } while (0) |
| 40 | 40 |
| 41 namespace content { | 41 namespace media { |
| 42 | 42 |
| 43 // Only H.264 with 4:2:0 chroma sampling is supported. | 43 // Only H.264 with 4:2:0 chroma sampling is supported. |
| 44 static const media::VideoCodecProfile kSupportedProfiles[] = { | 44 static const media::VideoCodecProfile kSupportedProfiles[] = { |
| 45 media::H264PROFILE_BASELINE, | 45 media::H264PROFILE_BASELINE, media::H264PROFILE_MAIN, |
| 46 media::H264PROFILE_MAIN, | 46 media::H264PROFILE_EXTENDED, media::H264PROFILE_HIGH, |
| 47 media::H264PROFILE_EXTENDED, | 47 // TODO(hubbe): Try to re-enable this again somehow. Currently it seems |
| 48 media::H264PROFILE_HIGH, | 48 // that some codecs fail to check the profile during initialization and |
| 49 // TODO(hubbe): Try to re-enable this again somehow. Currently it seems | 49 // then fail on the first frame decode, which currently results in a |
| 50 // that some codecs fail to check the profile during initialization and | 50 // pipeline failure. |
| 51 // then fail on the first frame decode, which currently results in a | 51 // media::H264PROFILE_HIGH10PROFILE, |
| 52 // pipeline failure. | 52 media::H264PROFILE_SCALABLEBASELINE, media::H264PROFILE_SCALABLEHIGH, |
| 53 // media::H264PROFILE_HIGH10PROFILE, | 53 media::H264PROFILE_STEREOHIGH, media::H264PROFILE_MULTIVIEWHIGH, |
| 54 media::H264PROFILE_SCALABLEBASELINE, | |
| 55 media::H264PROFILE_SCALABLEHIGH, | |
| 56 media::H264PROFILE_STEREOHIGH, | |
| 57 media::H264PROFILE_MULTIVIEWHIGH, | |
| 58 }; | 54 }; |
| 59 | 55 |
| 60 // Size to use for NALU length headers in AVC format (can be 1, 2, or 4). | 56 // Size to use for NALU length headers in AVC format (can be 1, 2, or 4). |
| 61 static const int kNALUHeaderLength = 4; | 57 static const int kNALUHeaderLength = 4; |
| 62 | 58 |
| 63 // We request 5 picture buffers from the client, each of which has a texture ID | 59 // We request 5 picture buffers from the client, each of which has a texture ID |
| 64 // that we can bind decoded frames to. We need enough to satisfy preroll, and | 60 // that we can bind decoded frames to. We need enough to satisfy preroll, and |
| 65 // enough to avoid unnecessary stalling, but no more than that. The resource | 61 // enough to avoid unnecessary stalling, but no more than that. The resource |
| 66 // requirements are low, as we don't need the textures to be backed by storage. | 62 // requirements are low, as we don't need the textures to be backed by storage. |
| 67 static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1; | 63 static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1; |
| 68 | 64 |
| 69 // Maximum number of frames to queue for reordering before we stop asking for | 65 // Maximum number of frames to queue for reordering before we stop asking for |
| 70 // more. (NotifyEndOfBitstreamBuffer() is called when frames are moved into the | 66 // more. (NotifyEndOfBitstreamBuffer() is called when frames are moved into the |
| 71 // reorder queue.) | 67 // reorder queue.) |
| 72 static const int kMaxReorderQueueSize = 16; | 68 static const int kMaxReorderQueueSize = 16; |
| 73 | 69 |
| 74 // Build an |image_config| dictionary for VideoToolbox initialization. | 70 // Build an |image_config| dictionary for VideoToolbox initialization. |
| 75 static base::ScopedCFTypeRef<CFMutableDictionaryRef> | 71 static base::ScopedCFTypeRef<CFMutableDictionaryRef> BuildImageConfig( |
| 76 BuildImageConfig(CMVideoDimensions coded_dimensions) { | 72 CMVideoDimensions coded_dimensions) { |
| 77 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config; | 73 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config; |
| 78 | 74 |
| 79 // Note that 4:2:0 textures cannot be used directly as RGBA in OpenGL, but are | 75 // Note that 4:2:0 textures cannot be used directly as RGBA in OpenGL, but are |
| 80 // lower power than 4:2:2 when composited directly by CoreAnimation. | 76 // lower power than 4:2:2 when composited directly by CoreAnimation. |
| 81 int32_t pixel_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; | 77 int32_t pixel_format = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange; |
| 82 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | 78 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) |
| 83 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); | 79 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); |
| 84 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); | 80 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); |
| 85 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); | 81 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); |
| 86 #undef CFINT | 82 #undef CFINT |
| 87 if (!cf_pixel_format.get() || !cf_width.get() || !cf_height.get()) | 83 if (!cf_pixel_format.get() || !cf_width.get() || !cf_height.get()) |
| 88 return image_config; | 84 return image_config; |
| 89 | 85 |
| 90 image_config.reset( | 86 image_config.reset(CFDictionaryCreateMutable( |
| 91 CFDictionaryCreateMutable( | 87 kCFAllocatorDefault, |
| 92 kCFAllocatorDefault, | 88 3, // capacity |
| 93 3, // capacity | 89 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); |
| 94 &kCFTypeDictionaryKeyCallBacks, | |
| 95 &kCFTypeDictionaryValueCallBacks)); | |
| 96 if (!image_config.get()) | 90 if (!image_config.get()) |
| 97 return image_config; | 91 return image_config; |
| 98 | 92 |
| 99 CFDictionarySetValue(image_config, kCVPixelBufferPixelFormatTypeKey, | 93 CFDictionarySetValue(image_config, kCVPixelBufferPixelFormatTypeKey, |
| 100 cf_pixel_format); | 94 cf_pixel_format); |
| 101 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | 95 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
| 102 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | 96 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
| 103 | 97 |
| 104 return image_config; | 98 return image_config; |
| 105 } | 99 } |
| 106 | 100 |
| 107 // Create a VTDecompressionSession using the provided |pps| and |sps|. If | 101 // Create a VTDecompressionSession using the provided |pps| and |sps|. If |
| 108 // |require_hardware| is true, the session must uses real hardware decoding | 102 // |require_hardware| is true, the session must uses real hardware decoding |
| 109 // (as opposed to software decoding inside of VideoToolbox) to be considered | 103 // (as opposed to software decoding inside of VideoToolbox) to be considered |
| 110 // successful. | 104 // successful. |
| 111 // | 105 // |
| 112 // TODO(sandersd): Merge with ConfigureDecoder(), as the code is very similar. | 106 // TODO(sandersd): Merge with ConfigureDecoder(), as the code is very similar. |
| 113 static bool CreateVideoToolboxSession(const uint8_t* sps, size_t sps_size, | 107 static bool CreateVideoToolboxSession(const uint8_t* sps, |
| 114 const uint8_t* pps, size_t pps_size, | 108 size_t sps_size, |
| 109 const uint8_t* pps, |
| 110 size_t pps_size, |
| 115 bool require_hardware) { | 111 bool require_hardware) { |
| 116 const uint8_t* data_ptrs[] = {sps, pps}; | 112 const uint8_t* data_ptrs[] = {sps, pps}; |
| 117 const size_t data_sizes[] = {sps_size, pps_size}; | 113 const size_t data_sizes[] = {sps_size, pps_size}; |
| 118 | 114 |
| 119 base::ScopedCFTypeRef<CMFormatDescriptionRef> format; | 115 base::ScopedCFTypeRef<CMFormatDescriptionRef> format; |
| 120 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( | 116 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( |
| 121 kCFAllocatorDefault, | 117 kCFAllocatorDefault, |
| 122 2, // parameter_set_count | 118 2, // parameter_set_count |
| 123 data_ptrs, // ¶meter_set_pointers | 119 data_ptrs, // ¶meter_set_pointers |
| 124 data_sizes, // ¶meter_set_sizes | 120 data_sizes, // ¶meter_set_sizes |
| 125 kNALUHeaderLength, // nal_unit_header_length | 121 kNALUHeaderLength, // nal_unit_header_length |
| 126 format.InitializeInto()); | 122 format.InitializeInto()); |
| 127 if (status) { | 123 if (status) { |
| 128 OSSTATUS_DLOG(WARNING, status) | 124 OSSTATUS_DLOG(WARNING, status) |
| 129 << "Failed to create CMVideoFormatDescription"; | 125 << "Failed to create CMVideoFormatDescription"; |
| 130 return false; | 126 return false; |
| 131 } | 127 } |
| 132 | 128 |
| 133 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( | 129 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
| 134 CFDictionaryCreateMutable( | 130 CFDictionaryCreateMutable(kCFAllocatorDefault, |
| 135 kCFAllocatorDefault, | 131 1, // capacity |
| 136 1, // capacity | 132 &kCFTypeDictionaryKeyCallBacks, |
| 137 &kCFTypeDictionaryKeyCallBacks, | 133 &kCFTypeDictionaryValueCallBacks)); |
| 138 &kCFTypeDictionaryValueCallBacks)); | |
| 139 if (!decoder_config.get()) | 134 if (!decoder_config.get()) |
| 140 return false; | 135 return false; |
| 141 | 136 |
| 142 if (require_hardware) { | 137 if (require_hardware) { |
| 143 CFDictionarySetValue( | 138 CFDictionarySetValue( |
| 144 decoder_config, | 139 decoder_config, |
| 145 // kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder | 140 // kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder |
| 146 CFSTR("RequireHardwareAcceleratedVideoDecoder"), | 141 CFSTR("RequireHardwareAcceleratedVideoDecoder"), kCFBooleanTrue); |
| 147 kCFBooleanTrue); | |
| 148 } | 142 } |
| 149 | 143 |
| 150 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | 144 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
| 151 BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format))); | 145 BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format))); |
| 152 if (!image_config.get()) | 146 if (!image_config.get()) |
| 153 return false; | 147 return false; |
| 154 | 148 |
| 155 VTDecompressionOutputCallbackRecord callback = {0}; | 149 VTDecompressionOutputCallbackRecord callback = {0}; |
| 156 | 150 |
| 157 base::ScopedCFTypeRef<VTDecompressionSessionRef> session; | 151 base::ScopedCFTypeRef<VTDecompressionSessionRef> session; |
| 158 status = VTDecompressionSessionCreate( | 152 status = VTDecompressionSessionCreate( |
| 159 kCFAllocatorDefault, | 153 kCFAllocatorDefault, |
| 160 format, // video_format_description | 154 format, // video_format_description |
| 161 decoder_config, // video_decoder_specification | 155 decoder_config, // video_decoder_specification |
| 162 image_config, // destination_image_buffer_attributes | 156 image_config, // destination_image_buffer_attributes |
| 163 &callback, // output_callback | 157 &callback, // output_callback |
| 164 session.InitializeInto()); | 158 session.InitializeInto()); |
| 165 if (status) { | 159 if (status) { |
| 166 OSSTATUS_DLOG(WARNING, status) | 160 OSSTATUS_DLOG(WARNING, status) |
| 167 << "Failed to create VTDecompressionSession"; | 161 << "Failed to create VTDecompressionSession"; |
| 168 return false; | 162 return false; |
| 169 } | 163 } |
| 170 | 164 |
| 171 return true; | 165 return true; |
| 172 } | 166 } |
| 173 | 167 |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 227 | 221 |
| 228 if (!attempted) { | 222 if (!attempted) { |
| 229 attempted = true; | 223 attempted = true; |
| 230 succeeded = InitializeVideoToolboxInternal(); | 224 succeeded = InitializeVideoToolboxInternal(); |
| 231 } | 225 } |
| 232 | 226 |
| 233 return succeeded; | 227 return succeeded; |
| 234 } | 228 } |
| 235 | 229 |
| 236 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | 230 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
| 237 static void OutputThunk( | 231 static void OutputThunk(void* decompression_output_refcon, |
| 238 void* decompression_output_refcon, | 232 void* source_frame_refcon, |
| 239 void* source_frame_refcon, | 233 OSStatus status, |
| 240 OSStatus status, | 234 VTDecodeInfoFlags info_flags, |
| 241 VTDecodeInfoFlags info_flags, | 235 CVImageBufferRef image_buffer, |
| 242 CVImageBufferRef image_buffer, | 236 CMTime presentation_time_stamp, |
| 243 CMTime presentation_time_stamp, | 237 CMTime presentation_duration) { |
| 244 CMTime presentation_duration) { | |
| 245 VTVideoDecodeAccelerator* vda = | 238 VTVideoDecodeAccelerator* vda = |
| 246 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); | 239 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
| 247 vda->Output(source_frame_refcon, status, image_buffer); | 240 vda->Output(source_frame_refcon, status, image_buffer); |
| 248 } | 241 } |
| 249 | 242 |
| 250 VTVideoDecodeAccelerator::Task::Task(TaskType type) : type(type) { | 243 VTVideoDecodeAccelerator::Task::Task(TaskType type) : type(type) {} |
| 251 } | |
| 252 | 244 |
| 253 VTVideoDecodeAccelerator::Task::Task(const Task& other) = default; | 245 VTVideoDecodeAccelerator::Task::Task(const Task& other) = default; |
| 254 | 246 |
| 255 VTVideoDecodeAccelerator::Task::~Task() { | 247 VTVideoDecodeAccelerator::Task::~Task() {} |
| 256 } | |
| 257 | 248 |
| 258 VTVideoDecodeAccelerator::Frame::Frame(int32_t bitstream_id) | 249 VTVideoDecodeAccelerator::Frame::Frame(int32_t bitstream_id) |
| 259 : bitstream_id(bitstream_id), | 250 : bitstream_id(bitstream_id), |
| 260 pic_order_cnt(0), | 251 pic_order_cnt(0), |
| 261 is_idr(false), | 252 is_idr(false), |
| 262 reorder_window(0) { | 253 reorder_window(0) {} |
| 263 } | |
| 264 | 254 |
| 265 VTVideoDecodeAccelerator::Frame::~Frame() { | 255 VTVideoDecodeAccelerator::Frame::~Frame() {} |
| 266 } | |
| 267 | 256 |
| 268 VTVideoDecodeAccelerator::PictureInfo::PictureInfo(uint32_t client_texture_id, | 257 VTVideoDecodeAccelerator::PictureInfo::PictureInfo(uint32_t client_texture_id, |
| 269 uint32_t service_texture_id) | 258 uint32_t service_texture_id) |
| 270 : client_texture_id(client_texture_id), | 259 : client_texture_id(client_texture_id), |
| 271 service_texture_id(service_texture_id) {} | 260 service_texture_id(service_texture_id) {} |
| 272 | 261 |
| 273 VTVideoDecodeAccelerator::PictureInfo::~PictureInfo() { | 262 VTVideoDecodeAccelerator::PictureInfo::~PictureInfo() { |
| 274 if (gl_image) | 263 if (gl_image) |
| 275 gl_image->Destroy(false); | 264 gl_image->Destroy(false); |
| 276 } | 265 } |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 } | 330 } |
| 342 if (!profile_supported) | 331 if (!profile_supported) |
| 343 return false; | 332 return false; |
| 344 | 333 |
| 345 // Spawn a thread to handle parsing and calling VideoToolbox. | 334 // Spawn a thread to handle parsing and calling VideoToolbox. |
| 346 if (!decoder_thread_.Start()) | 335 if (!decoder_thread_.Start()) |
| 347 return false; | 336 return false; |
| 348 | 337 |
| 349 // Count the session as successfully initialized. | 338 // Count the session as successfully initialized. |
| 350 UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.SessionFailureReason", | 339 UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.SessionFailureReason", |
| 351 SFT_SUCCESSFULLY_INITIALIZED, | 340 SFT_SUCCESSFULLY_INITIALIZED, SFT_MAX + 1); |
| 352 SFT_MAX + 1); | |
| 353 return true; | 341 return true; |
| 354 } | 342 } |
| 355 | 343 |
| 356 bool VTVideoDecodeAccelerator::FinishDelayedFrames() { | 344 bool VTVideoDecodeAccelerator::FinishDelayedFrames() { |
| 357 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 345 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 358 if (session_) { | 346 if (session_) { |
| 359 OSStatus status = VTDecompressionSessionWaitForAsynchronousFrames(session_); | 347 OSStatus status = VTDecompressionSessionWaitForAsynchronousFrames(session_); |
| 360 if (status) { | 348 if (status) { |
| 361 NOTIFY_STATUS("VTDecompressionSessionWaitForAsynchronousFrames()", | 349 NOTIFY_STATUS("VTDecompressionSessionWaitForAsynchronousFrames()", status, |
| 362 status, SFT_PLATFORM_ERROR); | 350 SFT_PLATFORM_ERROR); |
| 363 return false; | 351 return false; |
| 364 } | 352 } |
| 365 } | 353 } |
| 366 return true; | 354 return true; |
| 367 } | 355 } |
| 368 | 356 |
| 369 bool VTVideoDecodeAccelerator::ConfigureDecoder() { | 357 bool VTVideoDecodeAccelerator::ConfigureDecoder() { |
| 370 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 358 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 371 DCHECK(!last_sps_.empty()); | 359 DCHECK(!last_sps_.empty()); |
| 372 DCHECK(!last_pps_.empty()); | 360 DCHECK(!last_pps_.empty()); |
| 373 | 361 |
| 374 // Build the configuration records. | 362 // Build the configuration records. |
| 375 std::vector<const uint8_t*> nalu_data_ptrs; | 363 std::vector<const uint8_t*> nalu_data_ptrs; |
| 376 std::vector<size_t> nalu_data_sizes; | 364 std::vector<size_t> nalu_data_sizes; |
| 377 nalu_data_ptrs.reserve(3); | 365 nalu_data_ptrs.reserve(3); |
| 378 nalu_data_sizes.reserve(3); | 366 nalu_data_sizes.reserve(3); |
| 379 nalu_data_ptrs.push_back(&last_sps_.front()); | 367 nalu_data_ptrs.push_back(&last_sps_.front()); |
| 380 nalu_data_sizes.push_back(last_sps_.size()); | 368 nalu_data_sizes.push_back(last_sps_.size()); |
| 381 if (!last_spsext_.empty()) { | 369 if (!last_spsext_.empty()) { |
| 382 nalu_data_ptrs.push_back(&last_spsext_.front()); | 370 nalu_data_ptrs.push_back(&last_spsext_.front()); |
| 383 nalu_data_sizes.push_back(last_spsext_.size()); | 371 nalu_data_sizes.push_back(last_spsext_.size()); |
| 384 } | 372 } |
| 385 nalu_data_ptrs.push_back(&last_pps_.front()); | 373 nalu_data_ptrs.push_back(&last_pps_.front()); |
| 386 nalu_data_sizes.push_back(last_pps_.size()); | 374 nalu_data_sizes.push_back(last_pps_.size()); |
| 387 | 375 |
| 388 // Construct a new format description from the parameter sets. | 376 // Construct a new format description from the parameter sets. |
| 389 format_.reset(); | 377 format_.reset(); |
| 390 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( | 378 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( |
| 391 kCFAllocatorDefault, | 379 kCFAllocatorDefault, |
| 392 nalu_data_ptrs.size(), // parameter_set_count | 380 nalu_data_ptrs.size(), // parameter_set_count |
| 393 &nalu_data_ptrs.front(), // ¶meter_set_pointers | 381 &nalu_data_ptrs.front(), // ¶meter_set_pointers |
| 394 &nalu_data_sizes.front(), // ¶meter_set_sizes | 382 &nalu_data_sizes.front(), // ¶meter_set_sizes |
| 395 kNALUHeaderLength, // nal_unit_header_length | 383 kNALUHeaderLength, // nal_unit_header_length |
| 396 format_.InitializeInto()); | 384 format_.InitializeInto()); |
| 397 if (status) { | 385 if (status) { |
| 398 NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()", | 386 NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()", |
| 399 status, SFT_PLATFORM_ERROR); | 387 status, SFT_PLATFORM_ERROR); |
| 400 return false; | 388 return false; |
| 401 } | 389 } |
| 402 | 390 |
| 403 // Store the new configuration data. | 391 // Store the new configuration data. |
| 404 // TODO(sandersd): Despite the documentation, this seems to return the visible | 392 // TODO(sandersd): Despite the documentation, this seems to return the visible |
| 405 // size. However, the output always appears to be top-left aligned, so it | 393 // size. However, the output always appears to be top-left aligned, so it |
| 406 // makes no difference. Re-verify this and update the variable name. | 394 // makes no difference. Re-verify this and update the variable name. |
| 407 CMVideoDimensions coded_dimensions = | 395 CMVideoDimensions coded_dimensions = |
| 408 CMVideoFormatDescriptionGetDimensions(format_); | 396 CMVideoFormatDescriptionGetDimensions(format_); |
| 409 coded_size_.SetSize(coded_dimensions.width, coded_dimensions.height); | 397 coded_size_.SetSize(coded_dimensions.width, coded_dimensions.height); |
| 410 | 398 |
| 411 // Prepare VideoToolbox configuration dictionaries. | 399 // Prepare VideoToolbox configuration dictionaries. |
| 412 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( | 400 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
| 413 CFDictionaryCreateMutable( | 401 CFDictionaryCreateMutable(kCFAllocatorDefault, |
| 414 kCFAllocatorDefault, | 402 1, // capacity |
| 415 1, // capacity | 403 &kCFTypeDictionaryKeyCallBacks, |
| 416 &kCFTypeDictionaryKeyCallBacks, | 404 &kCFTypeDictionaryValueCallBacks)); |
| 417 &kCFTypeDictionaryValueCallBacks)); | |
| 418 if (!decoder_config.get()) { | 405 if (!decoder_config.get()) { |
| 419 DLOG(ERROR) << "Failed to create CFMutableDictionary"; | 406 DLOG(ERROR) << "Failed to create CFMutableDictionary"; |
| 420 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); | 407 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); |
| 421 return false; | 408 return false; |
| 422 } | 409 } |
| 423 | 410 |
| 424 CFDictionarySetValue( | 411 CFDictionarySetValue( |
| 425 decoder_config, | 412 decoder_config, |
| 426 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | 413 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
| 427 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | 414 CFSTR("EnableHardwareAcceleratedVideoDecoder"), kCFBooleanTrue); |
| 428 kCFBooleanTrue); | |
| 429 | 415 |
| 430 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | 416 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
| 431 BuildImageConfig(coded_dimensions)); | 417 BuildImageConfig(coded_dimensions)); |
| 432 if (!image_config.get()) { | 418 if (!image_config.get()) { |
| 433 DLOG(ERROR) << "Failed to create decoder image configuration"; | 419 DLOG(ERROR) << "Failed to create decoder image configuration"; |
| 434 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); | 420 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); |
| 435 return false; | 421 return false; |
| 436 } | 422 } |
| 437 | 423 |
| 438 // Ensure that the old decoder emits all frames before the new decoder can | 424 // Ensure that the old decoder emits all frames before the new decoder can |
| 439 // emit any. | 425 // emit any. |
| 440 if (!FinishDelayedFrames()) | 426 if (!FinishDelayedFrames()) |
| 441 return false; | 427 return false; |
| 442 | 428 |
| 443 session_.reset(); | 429 session_.reset(); |
| 444 status = VTDecompressionSessionCreate( | 430 status = VTDecompressionSessionCreate( |
| 445 kCFAllocatorDefault, | 431 kCFAllocatorDefault, |
| 446 format_, // video_format_description | 432 format_, // video_format_description |
| 447 decoder_config, // video_decoder_specification | 433 decoder_config, // video_decoder_specification |
| 448 image_config, // destination_image_buffer_attributes | 434 image_config, // destination_image_buffer_attributes |
| 449 &callback_, // output_callback | 435 &callback_, // output_callback |
| 450 session_.InitializeInto()); | 436 session_.InitializeInto()); |
| 451 if (status) { | 437 if (status) { |
| 452 NOTIFY_STATUS("VTDecompressionSessionCreate()", status, | 438 NOTIFY_STATUS("VTDecompressionSessionCreate()", status, |
| 453 SFT_UNSUPPORTED_STREAM_PARAMETERS); | 439 SFT_UNSUPPORTED_STREAM_PARAMETERS); |
| 454 return false; | 440 return false; |
| 455 } | 441 } |
| 456 | 442 |
| 457 // Report whether hardware decode is being used. | 443 // Report whether hardware decode is being used. |
| 458 bool using_hardware = false; | 444 bool using_hardware = false; |
| 459 base::ScopedCFTypeRef<CFBooleanRef> cf_using_hardware; | 445 base::ScopedCFTypeRef<CFBooleanRef> cf_using_hardware; |
| 460 if (VTSessionCopyProperty( | 446 if (VTSessionCopyProperty( |
| 461 session_, | 447 session_, |
| 462 // kVTDecompressionPropertyKey_UsingHardwareAcceleratedVideoDecoder | 448 // kVTDecompressionPropertyKey_UsingHardwareAcceleratedVideoDecoder |
| 463 CFSTR("UsingHardwareAcceleratedVideoDecoder"), | 449 CFSTR("UsingHardwareAcceleratedVideoDecoder"), kCFAllocatorDefault, |
| 464 kCFAllocatorDefault, | |
| 465 cf_using_hardware.InitializeInto()) == 0) { | 450 cf_using_hardware.InitializeInto()) == 0) { |
| 466 using_hardware = CFBooleanGetValue(cf_using_hardware); | 451 using_hardware = CFBooleanGetValue(cf_using_hardware); |
| 467 } | 452 } |
| 468 UMA_HISTOGRAM_BOOLEAN("Media.VTVDA.HardwareAccelerated", using_hardware); | 453 UMA_HISTOGRAM_BOOLEAN("Media.VTVDA.HardwareAccelerated", using_hardware); |
| 469 | 454 |
| 470 return true; | 455 return true; |
| 471 } | 456 } |
| 472 | 457 |
| 473 void VTVideoDecodeAccelerator::DecodeTask( | 458 void VTVideoDecodeAccelerator::DecodeTask( |
| 474 const media::BitstreamBuffer& bitstream, | 459 const media::BitstreamBuffer& bitstream, |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 604 DLOG(ERROR) << "Unable to compute POC"; | 589 DLOG(ERROR) << "Unable to compute POC"; |
| 605 NotifyError(UNREADABLE_INPUT, SFT_INVALID_STREAM); | 590 NotifyError(UNREADABLE_INPUT, SFT_INVALID_STREAM); |
| 606 return; | 591 return; |
| 607 } | 592 } |
| 608 | 593 |
| 609 if (nalu.nal_unit_type == media::H264NALU::kIDRSlice) | 594 if (nalu.nal_unit_type == media::H264NALU::kIDRSlice) |
| 610 frame->is_idr = true; | 595 frame->is_idr = true; |
| 611 | 596 |
| 612 if (sps->vui_parameters_present_flag && | 597 if (sps->vui_parameters_present_flag && |
| 613 sps->bitstream_restriction_flag) { | 598 sps->bitstream_restriction_flag) { |
| 614 frame->reorder_window = std::min(sps->max_num_reorder_frames, | 599 frame->reorder_window = |
| 615 kMaxReorderQueueSize - 1); | 600 std::min(sps->max_num_reorder_frames, kMaxReorderQueueSize - 1); |
| 616 } | 601 } |
| 617 } | 602 } |
| 618 has_slice = true; | 603 has_slice = true; |
| 619 default: | 604 default: |
| 620 nalus.push_back(nalu); | 605 nalus.push_back(nalu); |
| 621 data_size += kNALUHeaderLength + nalu.size; | 606 data_size += kNALUHeaderLength + nalu.size; |
| 622 break; | 607 break; |
| 623 } | 608 } |
| 624 } | 609 } |
| 625 | 610 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 664 } | 649 } |
| 665 has_slice = false; | 650 has_slice = false; |
| 666 } | 651 } |
| 667 | 652 |
| 668 // If there is nothing to decode, drop the bitstream buffer by returning an | 653 // If there is nothing to decode, drop the bitstream buffer by returning an |
| 669 // empty frame. | 654 // empty frame. |
| 670 if (!has_slice) { | 655 if (!has_slice) { |
| 671 // Keep everything in order by flushing first. | 656 // Keep everything in order by flushing first. |
| 672 if (!FinishDelayedFrames()) | 657 if (!FinishDelayedFrames()) |
| 673 return; | 658 return; |
| 674 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 659 gpu_task_runner_->PostTask( |
| 675 &VTVideoDecodeAccelerator::DecodeDone, weak_this_, frame)); | 660 FROM_HERE, |
| 661 base::Bind(&VTVideoDecodeAccelerator::DecodeDone, weak_this_, frame)); |
| 676 return; | 662 return; |
| 677 } | 663 } |
| 678 | 664 |
| 679 // If the session is not configured by this point, fail. | 665 // If the session is not configured by this point, fail. |
| 680 if (!session_) { | 666 if (!session_) { |
| 681 DLOG(ERROR) << "Cannot decode without configuration"; | 667 DLOG(ERROR) << "Cannot decode without configuration"; |
| 682 NotifyError(INVALID_ARGUMENT, SFT_INVALID_STREAM); | 668 NotifyError(INVALID_ARGUMENT, SFT_INVALID_STREAM); |
| 683 return; | 669 return; |
| 684 } | 670 } |
| 685 | 671 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 713 NOTIFY_STATUS("CMBlockBufferAssureBlockMemory()", status, | 699 NOTIFY_STATUS("CMBlockBufferAssureBlockMemory()", status, |
| 714 SFT_PLATFORM_ERROR); | 700 SFT_PLATFORM_ERROR); |
| 715 return; | 701 return; |
| 716 } | 702 } |
| 717 | 703 |
| 718 // Copy NALU data into the CMBlockBuffer, inserting length headers. | 704 // Copy NALU data into the CMBlockBuffer, inserting length headers. |
| 719 size_t offset = 0; | 705 size_t offset = 0; |
| 720 for (size_t i = 0; i < nalus.size(); i++) { | 706 for (size_t i = 0; i < nalus.size(); i++) { |
| 721 media::H264NALU& nalu = nalus[i]; | 707 media::H264NALU& nalu = nalus[i]; |
| 722 uint32_t header = base::HostToNet32(static_cast<uint32_t>(nalu.size)); | 708 uint32_t header = base::HostToNet32(static_cast<uint32_t>(nalu.size)); |
| 723 status = CMBlockBufferReplaceDataBytes( | 709 status = |
| 724 &header, data, offset, kNALUHeaderLength); | 710 CMBlockBufferReplaceDataBytes(&header, data, offset, kNALUHeaderLength); |
| 725 if (status) { | 711 if (status) { |
| 726 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status, | 712 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status, |
| 727 SFT_PLATFORM_ERROR); | 713 SFT_PLATFORM_ERROR); |
| 728 return; | 714 return; |
| 729 } | 715 } |
| 730 offset += kNALUHeaderLength; | 716 offset += kNALUHeaderLength; |
| 731 status = CMBlockBufferReplaceDataBytes(nalu.data, data, offset, nalu.size); | 717 status = CMBlockBufferReplaceDataBytes(nalu.data, data, offset, nalu.size); |
| 732 if (status) { | 718 if (status) { |
| 733 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status, | 719 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status, |
| 734 SFT_PLATFORM_ERROR); | 720 SFT_PLATFORM_ERROR); |
| 735 return; | 721 return; |
| 736 } | 722 } |
| 737 offset += nalu.size; | 723 offset += nalu.size; |
| 738 } | 724 } |
| 739 | 725 |
| 740 // Package the data in a CMSampleBuffer. | 726 // Package the data in a CMSampleBuffer. |
| 741 base::ScopedCFTypeRef<CMSampleBufferRef> sample; | 727 base::ScopedCFTypeRef<CMSampleBufferRef> sample; |
| 742 status = CMSampleBufferCreate( | 728 status = CMSampleBufferCreate(kCFAllocatorDefault, |
| 743 kCFAllocatorDefault, | 729 data, // data_buffer |
| 744 data, // data_buffer | 730 true, // data_ready |
| 745 true, // data_ready | 731 nullptr, // make_data_ready_callback |
| 746 nullptr, // make_data_ready_callback | 732 nullptr, // make_data_ready_refcon |
| 747 nullptr, // make_data_ready_refcon | 733 format_, // format_description |
| 748 format_, // format_description | 734 1, // num_samples |
| 749 1, // num_samples | 735 0, // num_sample_timing_entries |
| 750 0, // num_sample_timing_entries | 736 nullptr, // &sample_timing_array |
| 751 nullptr, // &sample_timing_array | 737 1, // num_sample_size_entries |
| 752 1, // num_sample_size_entries | 738 &data_size, // &sample_size_array |
| 753 &data_size, // &sample_size_array | 739 sample.InitializeInto()); |
| 754 sample.InitializeInto()); | |
| 755 if (status) { | 740 if (status) { |
| 756 NOTIFY_STATUS("CMSampleBufferCreate()", status, SFT_PLATFORM_ERROR); | 741 NOTIFY_STATUS("CMSampleBufferCreate()", status, SFT_PLATFORM_ERROR); |
| 757 return; | 742 return; |
| 758 } | 743 } |
| 759 | 744 |
| 760 // Send the frame for decoding. | 745 // Send the frame for decoding. |
| 761 // Asynchronous Decompression allows for parallel submission of frames | 746 // Asynchronous Decompression allows for parallel submission of frames |
| 762 // (without it, DecodeFrame() does not return until the frame has been | 747 // (without it, DecodeFrame() does not return until the frame has been |
| 763 // decoded). We don't enable Temporal Processing so that frames are always | 748 // decoded). We don't enable Temporal Processing so that frames are always |
| 764 // returned in decode order; this makes it easier to avoid deadlock. | 749 // returned in decode order; this makes it easier to avoid deadlock. |
| 765 VTDecodeFrameFlags decode_flags = | 750 VTDecodeFrameFlags decode_flags = |
| 766 kVTDecodeFrame_EnableAsynchronousDecompression; | 751 kVTDecodeFrame_EnableAsynchronousDecompression; |
| 767 status = VTDecompressionSessionDecodeFrame( | 752 status = VTDecompressionSessionDecodeFrame( |
| 768 session_, | 753 session_, |
| 769 sample, // sample_buffer | 754 sample, // sample_buffer |
| 770 decode_flags, // decode_flags | 755 decode_flags, // decode_flags |
| 771 reinterpret_cast<void*>(frame), // source_frame_refcon | 756 reinterpret_cast<void*>(frame), // source_frame_refcon |
| 772 nullptr); // &info_flags_out | 757 nullptr); // &info_flags_out |
| 773 if (status) { | 758 if (status) { |
| 774 NOTIFY_STATUS("VTDecompressionSessionDecodeFrame()", status, | 759 NOTIFY_STATUS("VTDecompressionSessionDecodeFrame()", status, |
| 775 SFT_DECODE_ERROR); | 760 SFT_DECODE_ERROR); |
| 776 return; | 761 return; |
| 777 } | 762 } |
| 778 } | 763 } |
| 779 | 764 |
| 780 // This method may be called on any VideoToolbox thread. | 765 // This method may be called on any VideoToolbox thread. |
| 781 void VTVideoDecodeAccelerator::Output( | 766 void VTVideoDecodeAccelerator::Output(void* source_frame_refcon, |
| 782 void* source_frame_refcon, | 767 OSStatus status, |
| 783 OSStatus status, | 768 CVImageBufferRef image_buffer) { |
| 784 CVImageBufferRef image_buffer) { | |
| 785 if (status) { | 769 if (status) { |
| 786 NOTIFY_STATUS("Decoding", status, SFT_DECODE_ERROR); | 770 NOTIFY_STATUS("Decoding", status, SFT_DECODE_ERROR); |
| 787 return; | 771 return; |
| 788 } | 772 } |
| 789 | 773 |
| 790 // The type of |image_buffer| is CVImageBuffer, but we only handle | 774 // The type of |image_buffer| is CVImageBuffer, but we only handle |
| 791 // CVPixelBuffers. This should be guaranteed as we set | 775 // CVPixelBuffers. This should be guaranteed as we set |
| 792 // kCVPixelBufferOpenGLCompatibilityKey in |image_config|. | 776 // kCVPixelBufferOpenGLCompatibilityKey in |image_config|. |
| 793 // | 777 // |
| 794 // Sometimes, for unknown reasons (http://crbug.com/453050), |image_buffer| is | 778 // Sometimes, for unknown reasons (http://crbug.com/453050), |image_buffer| is |
| 795 // NULL, which causes CFGetTypeID() to crash. While the rest of the code would | 779 // NULL, which causes CFGetTypeID() to crash. While the rest of the code would |
| 796 // smoothly handle NULL as a dropped frame, we choose to fail permanantly here | 780 // smoothly handle NULL as a dropped frame, we choose to fail permanantly here |
| 797 // until the issue is better understood. | 781 // until the issue is better understood. |
| 798 if (!image_buffer || CFGetTypeID(image_buffer) != CVPixelBufferGetTypeID()) { | 782 if (!image_buffer || CFGetTypeID(image_buffer) != CVPixelBufferGetTypeID()) { |
| 799 DLOG(ERROR) << "Decoded frame is not a CVPixelBuffer"; | 783 DLOG(ERROR) << "Decoded frame is not a CVPixelBuffer"; |
| 800 NotifyError(PLATFORM_FAILURE, SFT_DECODE_ERROR); | 784 NotifyError(PLATFORM_FAILURE, SFT_DECODE_ERROR); |
| 801 return; | 785 return; |
| 802 } | 786 } |
| 803 | 787 |
| 804 Frame* frame = reinterpret_cast<Frame*>(source_frame_refcon); | 788 Frame* frame = reinterpret_cast<Frame*>(source_frame_refcon); |
| 805 frame->image.reset(image_buffer, base::scoped_policy::RETAIN); | 789 frame->image.reset(image_buffer, base::scoped_policy::RETAIN); |
| 806 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 790 gpu_task_runner_->PostTask( |
| 807 &VTVideoDecodeAccelerator::DecodeDone, weak_this_, frame)); | 791 FROM_HERE, |
| 792 base::Bind(&VTVideoDecodeAccelerator::DecodeDone, weak_this_, frame)); |
| 808 } | 793 } |
| 809 | 794 |
| 810 void VTVideoDecodeAccelerator::DecodeDone(Frame* frame) { | 795 void VTVideoDecodeAccelerator::DecodeDone(Frame* frame) { |
| 811 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 796 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| 812 DCHECK_EQ(1u, pending_frames_.count(frame->bitstream_id)); | 797 DCHECK_EQ(1u, pending_frames_.count(frame->bitstream_id)); |
| 813 Task task(TASK_FRAME); | 798 Task task(TASK_FRAME); |
| 814 task.frame = pending_frames_[frame->bitstream_id]; | 799 task.frame = pending_frames_[frame->bitstream_id]; |
| 815 pending_frames_.erase(frame->bitstream_id); | 800 pending_frames_.erase(frame->bitstream_id); |
| 816 task_queue_.push(task); | 801 task_queue_.push(task); |
| 817 ProcessWorkQueues(); | 802 ProcessWorkQueues(); |
| 818 } | 803 } |
| 819 | 804 |
| 820 void VTVideoDecodeAccelerator::FlushTask(TaskType type) { | 805 void VTVideoDecodeAccelerator::FlushTask(TaskType type) { |
| 821 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | 806 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); |
| 822 FinishDelayedFrames(); | 807 FinishDelayedFrames(); |
| 823 | 808 |
| 824 // Always queue a task, even if FinishDelayedFrames() fails, so that | 809 // Always queue a task, even if FinishDelayedFrames() fails, so that |
| 825 // destruction always completes. | 810 // destruction always completes. |
| 826 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 811 gpu_task_runner_->PostTask( |
| 827 &VTVideoDecodeAccelerator::FlushDone, weak_this_, type)); | 812 FROM_HERE, |
| 813 base::Bind(&VTVideoDecodeAccelerator::FlushDone, weak_this_, type)); |
| 828 } | 814 } |
| 829 | 815 |
| 830 void VTVideoDecodeAccelerator::FlushDone(TaskType type) { | 816 void VTVideoDecodeAccelerator::FlushDone(TaskType type) { |
| 831 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 817 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| 832 task_queue_.push(Task(type)); | 818 task_queue_.push(Task(type)); |
| 833 ProcessWorkQueues(); | 819 ProcessWorkQueues(); |
| 834 } | 820 } |
| 835 | 821 |
| 836 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 822 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
| 837 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 823 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 863 DCHECK_LE(1u, picture.texture_ids().size()); | 849 DCHECK_LE(1u, picture.texture_ids().size()); |
| 864 picture_info_map_.insert(std::make_pair( | 850 picture_info_map_.insert(std::make_pair( |
| 865 picture.id(), | 851 picture.id(), |
| 866 base::WrapUnique(new PictureInfo(picture.internal_texture_ids()[0], | 852 base::WrapUnique(new PictureInfo(picture.internal_texture_ids()[0], |
| 867 picture.texture_ids()[0])))); | 853 picture.texture_ids()[0])))); |
| 868 } | 854 } |
| 869 | 855 |
| 870 // Pictures are not marked as uncleared until after this method returns, and | 856 // Pictures are not marked as uncleared until after this method returns, and |
| 871 // they will be broken if they are used before that happens. So, schedule | 857 // they will be broken if they are used before that happens. So, schedule |
| 872 // future work after that happens. | 858 // future work after that happens. |
| 873 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 859 gpu_task_runner_->PostTask( |
| 874 &VTVideoDecodeAccelerator::ProcessWorkQueues, weak_this_)); | 860 FROM_HERE, |
| 861 base::Bind(&VTVideoDecodeAccelerator::ProcessWorkQueues, weak_this_)); |
| 875 } | 862 } |
| 876 | 863 |
| 877 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 864 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
| 878 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 865 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| 879 DCHECK(picture_info_map_.count(picture_id)); | 866 DCHECK(picture_info_map_.count(picture_id)); |
| 880 PictureInfo* picture_info = picture_info_map_.find(picture_id)->second.get(); | 867 PictureInfo* picture_info = picture_info_map_.find(picture_id)->second.get(); |
| 881 picture_info->cv_image.reset(); | 868 picture_info->cv_image.reset(); |
| 882 picture_info->gl_image->Destroy(false); | 869 picture_info->gl_image->Destroy(false); |
| 883 picture_info->gl_image = nullptr; | 870 picture_info->gl_image = nullptr; |
| 884 | 871 |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 969 bool VTVideoDecodeAccelerator::ProcessReorderQueue() { | 956 bool VTVideoDecodeAccelerator::ProcessReorderQueue() { |
| 970 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 957 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| 971 DCHECK_EQ(state_, STATE_DECODING); | 958 DCHECK_EQ(state_, STATE_DECODING); |
| 972 | 959 |
| 973 if (reorder_queue_.empty()) | 960 if (reorder_queue_.empty()) |
| 974 return false; | 961 return false; |
| 975 | 962 |
| 976 // If the next task is a flush (because there is a pending flush or becuase | 963 // If the next task is a flush (because there is a pending flush or becuase |
| 977 // the next frame is an IDR), then we don't need a full reorder buffer to send | 964 // the next frame is an IDR), then we don't need a full reorder buffer to send |
| 978 // the next frame. | 965 // the next frame. |
| 979 bool flushing = !task_queue_.empty() && | 966 bool flushing = |
| 980 (task_queue_.front().type != TASK_FRAME || | 967 !task_queue_.empty() && (task_queue_.front().type != TASK_FRAME || |
| 981 task_queue_.front().frame->is_idr); | 968 task_queue_.front().frame->is_idr); |
| 982 | 969 |
| 983 size_t reorder_window = std::max(0, reorder_queue_.top()->reorder_window); | 970 size_t reorder_window = std::max(0, reorder_queue_.top()->reorder_window); |
| 984 if (flushing || reorder_queue_.size() > reorder_window) { | 971 if (flushing || reorder_queue_.size() > reorder_window) { |
| 985 if (ProcessFrame(*reorder_queue_.top())) { | 972 if (ProcessFrame(*reorder_queue_.top())) { |
| 986 reorder_queue_.pop(); | 973 reorder_queue_.pop(); |
| 987 return true; | 974 return true; |
| 988 } | 975 } |
| 989 } | 976 } |
| 990 | 977 |
| 991 return false; | 978 return false; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1044 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); | 1031 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); |
| 1045 return false; | 1032 return false; |
| 1046 } | 1033 } |
| 1047 | 1034 |
| 1048 scoped_refptr<gl::GLImageIOSurface> gl_image( | 1035 scoped_refptr<gl::GLImageIOSurface> gl_image( |
| 1049 new gl::GLImageIOSurface(frame.coded_size, GL_BGRA_EXT)); | 1036 new gl::GLImageIOSurface(frame.coded_size, GL_BGRA_EXT)); |
| 1050 if (!gl_image->InitializeWithCVPixelBuffer( | 1037 if (!gl_image->InitializeWithCVPixelBuffer( |
| 1051 frame.image.get(), gfx::GenericSharedMemoryId(), | 1038 frame.image.get(), gfx::GenericSharedMemoryId(), |
| 1052 gfx::BufferFormat::YUV_420_BIPLANAR)) { | 1039 gfx::BufferFormat::YUV_420_BIPLANAR)) { |
| 1053 NOTIFY_STATUS("Failed to initialize GLImageIOSurface", PLATFORM_FAILURE, | 1040 NOTIFY_STATUS("Failed to initialize GLImageIOSurface", PLATFORM_FAILURE, |
| 1054 SFT_PLATFORM_ERROR); | 1041 SFT_PLATFORM_ERROR); |
| 1055 } | 1042 } |
| 1056 | 1043 |
| 1057 if (!bind_image_cb_.Run(picture_info->client_texture_id, | 1044 if (!bind_image_cb_.Run(picture_info->client_texture_id, |
| 1058 GL_TEXTURE_RECTANGLE_ARB, gl_image, false)) { | 1045 GL_TEXTURE_RECTANGLE_ARB, gl_image, false)) { |
| 1059 DLOG(ERROR) << "Failed to bind image"; | 1046 DLOG(ERROR) << "Failed to bind image"; |
| 1060 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); | 1047 NotifyError(PLATFORM_FAILURE, SFT_PLATFORM_ERROR); |
| 1061 return false; | 1048 return false; |
| 1062 } | 1049 } |
| 1063 | 1050 |
| 1064 // Assign the new image(s) to the the picture info. | 1051 // Assign the new image(s) to the the picture info. |
| 1065 picture_info->gl_image = gl_image; | 1052 picture_info->gl_image = gl_image; |
| 1066 picture_info->cv_image = frame.image; | 1053 picture_info->cv_image = frame.image; |
| 1067 available_picture_ids_.pop_back(); | 1054 available_picture_ids_.pop_back(); |
| 1068 | 1055 |
| 1069 // TODO(sandersd): Currently, the size got from | 1056 // TODO(sandersd): Currently, the size got from |
| 1070 // CMVideoFormatDescriptionGetDimensions is visible size. We pass it to | 1057 // CMVideoFormatDescriptionGetDimensions is visible size. We pass it to |
| 1071 // GpuVideoDecoder so that GpuVideoDecoder can use correct visible size in | 1058 // GpuVideoDecoder so that GpuVideoDecoder can use correct visible size in |
| 1072 // resolution changed. We should find the correct API to get the real | 1059 // resolution changed. We should find the correct API to get the real |
| 1073 // coded size and fix it. | 1060 // coded size and fix it. |
| 1074 client_->PictureReady(media::Picture(picture_id, frame.bitstream_id, | 1061 client_->PictureReady(media::Picture(picture_id, frame.bitstream_id, |
| 1075 gfx::Rect(frame.coded_size), | 1062 gfx::Rect(frame.coded_size), true)); |
| 1076 true)); | |
| 1077 return true; | 1063 return true; |
| 1078 } | 1064 } |
| 1079 | 1065 |
| 1080 void VTVideoDecodeAccelerator::NotifyError( | 1066 void VTVideoDecodeAccelerator::NotifyError( |
| 1081 Error vda_error_type, | 1067 Error vda_error_type, |
| 1082 VTVDASessionFailureType session_failure_type) { | 1068 VTVDASessionFailureType session_failure_type) { |
| 1083 DCHECK_LT(session_failure_type, SFT_MAX + 1); | 1069 DCHECK_LT(session_failure_type, SFT_MAX + 1); |
| 1084 if (!gpu_thread_checker_.CalledOnValidThread()) { | 1070 if (!gpu_thread_checker_.CalledOnValidThread()) { |
| 1085 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 1071 gpu_task_runner_->PostTask( |
| 1086 &VTVideoDecodeAccelerator::NotifyError, weak_this_, vda_error_type, | 1072 FROM_HERE, |
| 1087 session_failure_type)); | 1073 base::Bind(&VTVideoDecodeAccelerator::NotifyError, weak_this_, |
| 1074 vda_error_type, session_failure_type)); |
| 1088 } else if (state_ == STATE_DECODING) { | 1075 } else if (state_ == STATE_DECODING) { |
| 1089 state_ = STATE_ERROR; | 1076 state_ = STATE_ERROR; |
| 1090 UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.SessionFailureReason", | 1077 UMA_HISTOGRAM_ENUMERATION("Media.VTVDA.SessionFailureReason", |
| 1091 session_failure_type, | 1078 session_failure_type, SFT_MAX + 1); |
| 1092 SFT_MAX + 1); | |
| 1093 client_->NotifyError(vda_error_type); | 1079 client_->NotifyError(vda_error_type); |
| 1094 } | 1080 } |
| 1095 } | 1081 } |
| 1096 | 1082 |
| 1097 void VTVideoDecodeAccelerator::QueueFlush(TaskType type) { | 1083 void VTVideoDecodeAccelerator::QueueFlush(TaskType type) { |
| 1098 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 1084 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| 1099 pending_flush_tasks_.push(type); | 1085 pending_flush_tasks_.push(type); |
| 1100 decoder_thread_.task_runner()->PostTask( | 1086 decoder_thread_.task_runner()->PostTask( |
| 1101 FROM_HERE, base::Bind(&VTVideoDecodeAccelerator::FlushTask, | 1087 FROM_HERE, base::Bind(&VTVideoDecodeAccelerator::FlushTask, |
| 1102 base::Unretained(this), type)); | 1088 base::Unretained(this), type)); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1149 for (const auto& supported_profile : kSupportedProfiles) { | 1135 for (const auto& supported_profile : kSupportedProfiles) { |
| 1150 SupportedProfile profile; | 1136 SupportedProfile profile; |
| 1151 profile.profile = supported_profile; | 1137 profile.profile = supported_profile; |
| 1152 profile.min_resolution.SetSize(16, 16); | 1138 profile.min_resolution.SetSize(16, 16); |
| 1153 profile.max_resolution.SetSize(4096, 2160); | 1139 profile.max_resolution.SetSize(4096, 2160); |
| 1154 profiles.push_back(profile); | 1140 profiles.push_back(profile); |
| 1155 } | 1141 } |
| 1156 return profiles; | 1142 return profiles; |
| 1157 } | 1143 } |
| 1158 | 1144 |
| 1159 } // namespace content | 1145 } // namespace media |
| OLD | NEW |