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 // TODO(sandersd): Also decode a frame if it turns out to be necessary for some | |
| 69 // hardware. | |
| 70 void InitializeVideoToolbox() { | |
|
DaleCurtis
2014/11/21 19:01:02
What's the impact on gpu process startup time with
sandersd (OOO until July 31)
2014/11/21 20:19:35
Based on the benchmarking I just did, the perf tes
| |
| 71 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 72 switches::kDisableAcceleratedVideoDecode)) { | |
| 73 return; | |
| 74 } | |
| 75 | |
| 76 if (!IsVtInitialized()) { | |
| 77 // CoreVideo is also required, but the loader stops after the first path is | |
| 78 // loaded. Instead we rely on the transitive dependency from VideoToolbox to | |
| 79 // CoreVideo. | |
| 80 // TODO(sandersd): Fallback to PrivateFrameworks. | |
| 81 StubPathMap paths; | |
| 82 paths[kModuleVt].push_back(FILE_PATH_LITERAL( | |
| 83 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); | |
| 84 if (!InitializeStubs(paths)) | |
| 85 return; | |
| 86 } | |
| 87 | |
| 88 // Create a decoding session. | |
|
DaleCurtis
2014/11/20 23:08:33
This seems a bit crazy, wouldn't we normally handl
sandersd (OOO until July 31)
2014/11/21 00:04:52
Yes, but we don't have actual data because no vide
DaleCurtis
2014/11/21 02:04:13
Is there any impact on the ability to play two vid
sandersd (OOO until July 31)
2014/11/21 02:07:07
No, this initialization is called once per GPU pro
DaleCurtis
2014/11/21 19:01:02
To be clear, you're running this here so that all
sandersd (OOO until July 31)
2014/11/21 20:19:35
Done.
| |
| 89 // SPS and PPS data were taken from the 480p encoding of Big Buck Bunny. | |
| 90 uint8_t sps[] = {0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x80, 0xd4, 0x3d, 0xa1, | |
|
DaleCurtis
2014/11/21 19:01:02
Will the API let you const all these?
sandersd (OOO until July 31)
2014/11/21 20:19:35
Done.
| |
| 91 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x30, | |
| 92 0x8f, 0x16, 0x2d, 0x9a}; | |
| 93 uint8_t pps[] = {0x68, 0xe9, 0x7b, 0xcb}; | |
| 94 uint8_t* data_ptrs[] = {sps, pps}; | |
| 95 size_t data_sizes[] = {arraysize(sps), arraysize(pps)}; | |
| 96 | |
| 97 base::ScopedCFTypeRef<CMFormatDescriptionRef> format; | |
| 98 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( | |
| 99 kCFAllocatorDefault, | |
| 100 2, // parameter_set_count | |
| 101 data_ptrs, // ¶meter_set_pointers | |
| 102 data_sizes, // ¶meter_set_sizes | |
| 103 kNALUHeaderLength, // nal_unit_header_length | |
| 104 format.InitializeInto()); | |
| 105 if (status) { | |
| 106 LOG(ERROR) << "Failed to create CMVideoFormatDescription while " | |
| 107 << "initializing VideoToolbox"; | |
| 108 UninitializeVt(); | |
| 109 return; | |
| 110 } | |
| 111 | |
| 112 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( | |
| 113 CFDictionaryCreateMutable( | |
| 114 kCFAllocatorDefault, | |
| 115 1, // capacity | |
| 116 &kCFTypeDictionaryKeyCallBacks, | |
| 117 &kCFTypeDictionaryValueCallBacks)); | |
| 118 | |
| 119 CFDictionarySetValue( | |
| 120 decoder_config, | |
| 121 // kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder | |
| 122 CFSTR("RequireHardwareAcceleratedVideoDecoder"), | |
| 123 kCFBooleanTrue); | |
| 124 | |
| 125 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | |
| 126 BuildImageConfig(CMVideoFormatDescriptionGetDimensions(format))); | |
| 127 | |
| 128 VTDecompressionOutputCallbackRecord callback; | |
| 129 callback.decompressionOutputCallback = NULL; | |
| 130 callback.decompressionOutputRefCon = NULL; | |
| 131 | |
| 132 base::ScopedCFTypeRef<VTDecompressionSessionRef> session; | |
| 133 status = VTDecompressionSessionCreate( | |
| 134 kCFAllocatorDefault, | |
| 135 format, // video_format_description | |
| 136 decoder_config, // video_decoder_specification | |
| 137 image_config, // destination_image_buffer_attributes | |
| 138 &callback, // output_callback | |
| 139 session.InitializeInto()); | |
| 140 if (status) { | |
| 141 LOG(ERROR) << "Initializing VideoToolbox failed with status " << status; | |
| 142 UninitializeVt(); | |
| 143 return; | |
| 144 } | |
| 145 } | |
| 146 | |
| 41 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | 147 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. |
| 42 static void OutputThunk( | 148 static void OutputThunk( |
| 43 void* decompression_output_refcon, | 149 void* decompression_output_refcon, |
| 44 void* source_frame_refcon, | 150 void* source_frame_refcon, |
| 45 OSStatus status, | 151 OSStatus status, |
| 46 VTDecodeInfoFlags info_flags, | 152 VTDecodeInfoFlags info_flags, |
| 47 CVImageBufferRef image_buffer, | 153 CVImageBufferRef image_buffer, |
| 48 CMTime presentation_time_stamp, | 154 CMTime presentation_time_stamp, |
| 49 CMTime presentation_duration) { | 155 CMTime presentation_duration) { |
| 50 VTVideoDecodeAccelerator* vda = | 156 VTVideoDecodeAccelerator* vda = |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 84 | 190 |
| 85 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 191 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
| 86 } | 192 } |
| 87 | 193 |
| 88 bool VTVideoDecodeAccelerator::Initialize( | 194 bool VTVideoDecodeAccelerator::Initialize( |
| 89 media::VideoCodecProfile profile, | 195 media::VideoCodecProfile profile, |
| 90 Client* client) { | 196 Client* client) { |
| 91 DCHECK(gpu_thread_checker_.CalledOnValidThread()); | 197 DCHECK(gpu_thread_checker_.CalledOnValidThread()); |
| 92 client_ = client; | 198 client_ = client; |
| 93 | 199 |
| 200 if (!IsVtInitialized()) | |
| 201 return false; | |
| 202 | |
| 94 // Only H.264 is supported. | 203 // Only H.264 is supported. |
| 95 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) | 204 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
| 96 return false; | 205 return false; |
| 97 | 206 |
| 98 // Require --no-sandbox until VideoToolbox library loading is part of sandbox | |
| 99 // startup (and this VDA is ready for regular users). | |
| 100 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoSandbox)) | |
| 101 return false; | |
| 102 | |
| 103 if (!IsVtInitialized()) { | |
| 104 // CoreVideo is also required, but the loader stops after the first | |
| 105 // path is loaded. Instead we rely on the transitive dependency from | |
| 106 // VideoToolbox to CoreVideo. | |
| 107 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. | |
| 108 StubPathMap paths; | |
| 109 paths[kModuleVt].push_back(FILE_PATH_LITERAL( | |
| 110 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); | |
| 111 if (!InitializeStubs(paths)) | |
| 112 return false; | |
| 113 } | |
| 114 | |
| 115 // Spawn a thread to handle parsing and calling VideoToolbox. | 207 // Spawn a thread to handle parsing and calling VideoToolbox. |
| 116 if (!decoder_thread_.Start()) | 208 if (!decoder_thread_.Start()) |
| 117 return false; | 209 return false; |
| 118 | 210 |
| 119 return true; | 211 return true; |
| 120 } | 212 } |
| 121 | 213 |
| 122 bool VTVideoDecodeAccelerator::FinishDelayedFrames() { | 214 bool VTVideoDecodeAccelerator::FinishDelayedFrames() { |
| 123 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 215 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
| 124 if (session_) { | 216 if (session_) { |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 185 &kCFTypeDictionaryKeyCallBacks, | 277 &kCFTypeDictionaryKeyCallBacks, |
| 186 &kCFTypeDictionaryValueCallBacks)); | 278 &kCFTypeDictionaryValueCallBacks)); |
| 187 | 279 |
| 188 CFDictionarySetValue( | 280 CFDictionarySetValue( |
| 189 decoder_config, | 281 decoder_config, |
| 190 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | 282 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder |
| 191 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | 283 CFSTR("EnableHardwareAcceleratedVideoDecoder"), |
| 192 kCFBooleanTrue); | 284 kCFBooleanTrue); |
| 193 | 285 |
| 194 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( | 286 base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config( |
| 195 CFDictionaryCreateMutable( | 287 BuildImageConfig(coded_dimensions)); |
| 196 kCFAllocatorDefault, | |
| 197 4, // capacity | |
| 198 &kCFTypeDictionaryKeyCallBacks, | |
| 199 &kCFTypeDictionaryValueCallBacks)); | |
| 200 | |
| 201 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | |
| 202 // TODO(sandersd): RGBA option for 4:4:4 video. | |
| 203 int32_t pixel_format = kCVPixelFormatType_422YpCbCr8; | |
| 204 base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format(CFINT(pixel_format)); | |
| 205 base::ScopedCFTypeRef<CFNumberRef> cf_width(CFINT(coded_dimensions.width)); | |
| 206 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); | |
| 207 #undef CFINT | |
| 208 CFDictionarySetValue( | |
| 209 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | |
| 210 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | |
| 211 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | |
| 212 CFDictionarySetValue( | |
| 213 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | |
| 214 | 288 |
| 215 // TODO(sandersd): Does the old session need to be flushed first? | 289 // TODO(sandersd): Does the old session need to be flushed first? |
| 216 session_.reset(); | 290 session_.reset(); |
| 217 status = VTDecompressionSessionCreate( | 291 status = VTDecompressionSessionCreate( |
| 218 kCFAllocatorDefault, | 292 kCFAllocatorDefault, |
| 219 format_, // video_format_description | 293 format_, // video_format_description |
| 220 decoder_config, // video_decoder_specification | 294 decoder_config, // video_decoder_specification |
| 221 image_config, // destination_image_buffer_attributes | 295 image_config, // destination_image_buffer_attributes |
| 222 &callback_, // output_callback | 296 &callback_, // output_callback |
| 223 session_.InitializeInto()); | 297 session_.InitializeInto()); |
| (...skipping 443 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 667 assigned_bitstream_ids_.clear(); | 741 assigned_bitstream_ids_.clear(); |
| 668 state_ = STATE_DESTROYING; | 742 state_ = STATE_DESTROYING; |
| 669 QueueFlush(TASK_DESTROY); | 743 QueueFlush(TASK_DESTROY); |
| 670 } | 744 } |
| 671 | 745 |
| 672 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 746 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
| 673 return false; | 747 return false; |
| 674 } | 748 } |
| 675 | 749 |
| 676 } // namespace content | 750 } // namespace content |
| OLD | NEW |