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 |