Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <CoreVideo/CoreVideo.h> | 5 #include <CoreVideo/CoreVideo.h> |
| 6 #include <OpenGL/CGLIOSurface.h> | 6 #include <OpenGL/CGLIOSurface.h> |
| 7 #include <OpenGL/gl.h> | 7 #include <OpenGL/gl.h> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 | 31 |
| 32 // Size to use for NALU length headers in AVC format (can be 1, 2, or 4). | 32 // Size to use for NALU length headers in AVC format (can be 1, 2, or 4). |
| 33 static const int kNALUHeaderLength = 4; | 33 static const int kNALUHeaderLength = 4; |
| 34 | 34 |
| 35 // We request 5 picture buffers from the client, each of which has a texture ID | 35 // We request 5 picture buffers from the client, each of which has a texture ID |
| 36 // that we can bind decoded frames to. We need enough to satisfy preroll, and | 36 // that we can bind decoded frames to. We need enough to satisfy preroll, and |
| 37 // enough to avoid unnecessary stalling, but no more than that. The resource | 37 // enough to avoid unnecessary stalling, but no more than that. The resource |
| 38 // requirements are low, as we don't need the textures to be backed by storage. | 38 // requirements are low, as we don't need the textures to be backed by storage. |
| 39 static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1; | 39 static const int kNumPictureBuffers = media::limits::kMaxVideoFrames + 1; |
| 40 | 40 |
| 41 // Build an |image_config| dictionary for VideoToolbox initialization. | |
| 42 static CFMutableDictionaryRef BuildImageConfig( | |
| 43 CMVideoDimensions coded_dimensions) { | |
| 44 // TODO(sandersd): RGBA option for 4:4:4 video. | |
| 45 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; | |
| 46 | |
| 47 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | |
| 48 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); | |
| 49 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); | |
| 50 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); | |
| 51 #undef CFINT | |
| 52 | |
| 53 CFMutableDictionaryRef image_config = CFDictionaryCreateMutable( | |
| 54 kCFAllocatorDefault, | |
| 55 4, // capacity | |
| 56 &kCFTypeDictionaryKeyCallBacks, | |
| 57 &kCFTypeDictionaryValueCallBacks); | |
| 58 CFDictionarySetValue(image_config, kCVPixelBufferPixelFormatTypeKey, | |
| 59 cf_pixel_format); | |
| 60 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | |
| 61 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | |
| 62 CFDictionarySetValue(image_config, kCVPixelBufferOpenGLCompatibilityKey, | |
| 63 kCFBooleanTrue); | |
| 64 | |
| 65 return image_config; | |
| 66 } | |
| 67 | |
| 68 // The purpose of this function is to preload the generic and hardware-specific | |
| 69 // libraries required by VideoToolbox before the GPU sandbox is enabled. | |
| 70 // VideoToolbox normally loads the hardware-specific libraries lazily, so we | |
| 71 // must actually create a decompression session. | |
| 72 // | |
| 73 // If creating a decompression session fails, hardware decoding will be disabled | |
| 74 // (Initialize() will always return false). If it succeeds but a required | |
| 75 // library is not loaded yet (I have not experienced this, but the details are | |
| 76 // not documented), then VideoToolbox will fall back on software decoding | |
|
DaleCurtis
2014/11/21 21:00:49
Is there a way you record when this happens as a U
sandersd (OOO until July 31)
2014/11/21 21:20:19
It's possible, but is also expected when the resol
| |
| 77 // internally. If that happens, the likely solution is to expand the scope of | |
| 78 // this initialization. | |
| 79 void InitializeVideoToolbox() { | |
| 80 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 81 switches::kDisableAcceleratedVideoDecode)) { | |
| 82 return; | |
| 83 } | |
| 84 | |
| 85 if (!IsVtInitialized()) { | |
| 86 // CoreVideo is also required, but the loader stops after the first path is | |
| 87 // loaded. Instead we rely on the transitive dependency from VideoToolbox to | |
| 88 // CoreVideo. | |
| 89 // TODO(sandersd): Fallback to PrivateFrameworks. | |
| 90 StubPathMap paths; | |
| 91 paths[kModuleVt].push_back(FILE_PATH_LITERAL( | |
| 92 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); | |
| 93 if (!InitializeStubs(paths)) | |
| 94 return; | |
| 95 } | |
| 96 | |
| 97 // Create a decoding session. | |
| 98 // SPS and PPS data were taken from the 480p encoding of Big Buck Bunny. | |
| 99 const uint8_t sps[] = {0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x80, 0xd4, 0x3d, | |
| 100 0xa1, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, | |
| 101 0x00, 0x30, 0x8f, 0x16, 0x2d, 0x9a}; | |
| 102 const uint8_t pps[] = {0x68, 0xe9, 0x7b, 0xcb}; | |
| 103 const uint8_t* data_ptrs[] = {sps, pps}; | |
| 104 const size_t data_sizes[] = {arraysize(sps), arraysize(pps)}; | |
| 105 | |
| 106 base::ScopedCFTypeRef<CMFormatDescriptionRef> format; | |
| 107 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( | |
| 108 kCFAllocatorDefault, | |
| 109 2, // parameter_set_count | |
| 110 data_ptrs, // ¶meter_set_pointers | |
| 111 data_sizes, // ¶meter_set_sizes | |
| 112 kNALUHeaderLength, // nal_unit_header_length | |
| 113 format.InitializeInto()); | |
| 114 if (status) { | |
| 115 LOG(ERROR) << "Failed to create CMVideoFormatDescription while " | |
|
DaleCurtis
2014/11/21 21:00:49
Check out OSSTATUS_LOG and OSSTATUS_DLOG. You can
sandersd (OOO until July 31)
2014/11/21 21:20:19
Done.
| |
| 116 << "initializing VideoToolbox"; | |
| 117 content_common_gpu_media::UninitializeVt(); | |
| 118 return; | |
| 119 } | |
| 120 | |
| 121 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( | |
| 122 CFDictionaryCreateMutable( | |
| 123 kCFAllocatorDefault, | |
| 124 1, // capacity | |
| 125 &kCFTypeDictionaryKeyCallBacks, | |
| 126 &kCFTypeDictionaryValueCallBacks)); | |
| 127 | |
| 128 CFDictionarySetValue( | |
| 129 decoder_config, | |
| 130 // kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder | |
| 131 CFSTR("RequireHardwareAcceleratedVideoDecoder"), | |
| 132 kCFBooleanTrue); | |
| 133 | |
| 134 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | |
| 135 BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format))); | |
| 136 | |
| 137 VTDecompressionOutputCallbackRecord callback; | |
|
DaleCurtis
2014/11/21 21:00:49
Worth using = {0} to zero all potential fields?
sandersd (OOO until July 31)
2014/11/21 21:20:19
Done. (Although the regular path does not do this.
| |
| 138 callback.decompressionOutputCallback = NULL; | |
| 139 callback.decompressionOutputRefCon = NULL; | |
| 140 | |
| 141 base::ScopedCFTypeRef<VTDecompressionSessionRef> session; | |
| 142 status = VTDecompressionSessionCreate( | |
| 143 kCFAllocatorDefault, | |
| 144 format, // video_format_description | |
| 145 decoder_config, // video_decoder_specification | |
| 146 image_config, // destination_image_buffer_attributes | |
| 147 &callback, // output_callback | |
| 148 session.InitializeInto()); | |
| 149 if (status) { | |
| 150 LOG(ERROR) << "Initializing VideoToolbox failed with status " << status; | |
| 151 content_common_gpu_media::UninitializeVt(); | |
| 152 return; | |
| 153 } | |
|
DaleCurtis
2014/11/21 21:00:49
Is the session automatically dropped once all refs
sandersd (OOO until July 31)
2014/11/21 21:20:19
It is, VTDecompressionSessionInvalidate() just let
| |
| 154 } | |
| 155 | |
| 41 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | 156 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
| 42 static void OutputThunk( | 157 static void OutputThunk( |
| 43 void* decompression_output_refcon, | 158 void* decompression_output_refcon, |
| 44 void* source_frame_refcon, | 159 void* source_frame_refcon, |
| 45 OSStatus status, | 160 OSStatus status, |
| 46 VTDecodeInfoFlags info_flags, | 161 VTDecodeInfoFlags info_flags, |
| 47 CVImageBufferRef image_buffer, | 162 CVImageBufferRef image_buffer, |
| 48 CMTime presentation_time_stamp, | 163 CMTime presentation_time_stamp, |
| 49 CMTime presentation_duration) { | 164 CMTime presentation_duration) { |
| 50 VTVideoDecodeAccelerator* vda = | 165 VTVideoDecodeAccelerator* vda = |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 85 | 200 |
| 86 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 201 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
| 87 } | 202 } |
| 88 | 203 |
| 89 bool VTVideoDecodeAccelerator::Initialize( | 204 bool VTVideoDecodeAccelerator::Initialize( |
| 90 media::VideoCodecProfile profile, | 205 media::VideoCodecProfile profile, |
| 91 Client* client) { | 206 Client* client) { |
| 92 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 207 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| 93 client_ = client; | 208 client_ = client; |
| 94 | 209 |
| 210 if (!IsVtInitialized()) | |
| 211 return false; | |
| 212 | |
| 95 // Only H.264 is supported. | 213 // Only H.264 is supported. |
| 96 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) | 214 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
| 97 return false; | 215 return false; |
| 98 | 216 |
| 99 // Require --no-sandbox until VideoToolbox library loading is part of sandbox | |
| 100 // startup (and this VDA is ready for regular users). | |
| 101 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) | |
| 102 return false; | |
| 103 | |
| 104 if (!IsVtInitialized()) { | |
| 105 // CoreVideo is also required, but the loader stops after the first | |
| 106 // path is loaded. Instead we rely on the transitive dependency from | |
| 107 // VideoToolbox to CoreVideo. | |
| 108 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. | |
| 109 StubPathMap paths; | |
| 110 paths[kModuleVt].push_back(FILE_PATH_LITERAL( | |
| 111 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); | |
| 112 if (!InitializeStubs(paths)) | |
| 113 return false; | |
| 114 } | |
| 115 | |
| 116 // Spawn a thread to handle parsing and calling VideoToolbox. | 217 // Spawn a thread to handle parsing and calling VideoToolbox. |
| 117 if (!decoder_thread_.Start()) | 218 if (!decoder_thread_.Start()) |
| 118 return false; | 219 return false; |
| 119 | 220 |
| 120 return true; | 221 return true; |
| 121 } | 222 } |
| 122 | 223 |
| 123 bool VTVideoDecodeAccelerator::FinishDelayedFrames() { | 224 bool VTVideoDecodeAccelerator::FinishDelayedFrames() { |
| 124 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 225 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 125 if (session_) { | 226 if (session_) { |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 186 &kCFTypeDictionaryKeyCallBacks, | 287 &kCFTypeDictionaryKeyCallBacks, |
| 187 &kCFTypeDictionaryValueCallBacks)); | 288 &kCFTypeDictionaryValueCallBacks)); |
| 188 | 289 |
| 189 CFDictionarySetValue( | 290 CFDictionarySetValue( |
| 190 decoder_config, | 291 decoder_config, |
| 191 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | 292 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
| 192 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | 293 CFSTR("EnableHardwareAcceleratedVideoDecoder"), |
| 193 kCFBooleanTrue); | 294 kCFBooleanTrue); |
| 194 | 295 |
| 195 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | 296 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
| 196 CFDictionaryCreateMutable( | 297 BuildImageConfig(coded_dimensions)); |
| 197 kCFAllocatorDefault, | |
| 198 4, // capacity | |
| 199 &kCFTypeDictionaryKeyCallBacks, | |
| 200 &kCFTypeDictionaryValueCallBacks)); | |
| 201 | |
| 202 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | |
| 203 // TODO(sandersd): RGBA option for 4:4:4 video. | |
| 204 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; | |
| 205 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); | |
| 206 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); | |
| 207 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); | |
| 208 #undef CFINT | |
| 209 CFDictionarySetValue( | |
| 210 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | |
| 211 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | |
| 212 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | |
| 213 CFDictionarySetValue( | |
| 214 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | |
| 215 | 298 |
| 216 // TODO(sandersd): Does the old session need to be flushed first? | 299 // TODO(sandersd): Does the old session need to be flushed first? |
| 217 session_.reset(); | 300 session_.reset(); |
| 218 status = VTDecompressionSessionCreate( | 301 status = VTDecompressionSessionCreate( |
| 219 kCFAllocatorDefault, | 302 kCFAllocatorDefault, |
| 220 format_, // video_format_description | 303 format_, // video_format_description |
| 221 decoder_config, // video_decoder_specification | 304 decoder_config, // video_decoder_specification |
| 222 image_config, // destination_image_buffer_attributes | 305 image_config, // destination_image_buffer_attributes |
| 223 &callback_, // output_callback | 306 &callback_, // output_callback |
| 224 session_.InitializeInto()); | 307 session_.InitializeInto()); |
| (...skipping 447 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 672 assigned_bitstream_ids_.clear(); | 755 assigned_bitstream_ids_.clear(); |
| 673 state_ = STATE_DESTROYING; | 756 state_ = STATE_DESTROYING; |
| 674 QueueFlush(TASK_DESTROY); | 757 QueueFlush(TASK_DESTROY); |
| 675 } | 758 } |
| 676 | 759 |
| 677 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 760 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
| 678 return false; | 761 return false; |
| 679 } | 762 } |
| 680 | 763 |
| 681 } // namespace content | 764 } // namespace content |
| OLD | NEW |