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 |