2
2
// Use of this source code is governed by a BSD-style license that can be
3
3
// found in the LICENSE file.
4
4
5
+ #include < CoreVideo/CoreVideo.h>
6
+ #include < OpenGL/CGLIOSurface.h>
7
+
8
+ #include " base/bind.h"
9
+ #include " base/thread_task_runner_handle.h"
5
10
#include " content/common/gpu/media/vt_video_decode_accelerator.h"
11
+ #include " media/filters/h264_parser.h"
12
+
13
+ using content_common_gpu_media::kModuleVt ;
14
+ using content_common_gpu_media::InitializeStubs;
15
+ using content_common_gpu_media::IsVtInitialized;
16
+ using content_common_gpu_media::StubPathMap;
6
17
7
18
namespace content {
8
19
20
+ // Size of length headers prepended to NALUs in MPEG-4 framing. (1, 2, or 4.)
21
+ static const int kNALUHeaderLength = 4 ;
22
+
23
+ // Route decoded frame callbacks back into the VTVideoDecodeAccelerator.
24
+ static void OutputThunk (
25
+ void * decompression_output_refcon,
26
+ void * source_frame_refcon,
27
+ OSStatus status,
28
+ VTDecodeInfoFlags info_flags,
29
+ CVImageBufferRef image_buffer,
30
+ CMTime presentation_time_stamp,
31
+ CMTime presentation_duration) {
32
+ VTVideoDecodeAccelerator* vda =
33
+ reinterpret_cast <VTVideoDecodeAccelerator*>(decompression_output_refcon);
34
+ int32_t * bitstream_id_ptr = reinterpret_cast <int32_t *>(source_frame_refcon);
35
+ int32_t bitstream_id = *bitstream_id_ptr;
36
+ delete bitstream_id_ptr;
37
+ CFRetain (image_buffer);
38
+ vda->Output (bitstream_id, status, info_flags, image_buffer);
39
+ }
40
+
9
41
VTVideoDecodeAccelerator::VTVideoDecodeAccelerator (CGLContextObj cgl_context)
10
- : loop_proxy_(base::MessageLoopProxy::current()),
11
- cgl_context_ (cgl_context),
42
+ : cgl_context_(cgl_context),
12
43
client_ (NULL ),
44
+ decoder_thread_(" VTDecoderThread" ),
45
+ format_(NULL ),
46
+ session_(NULL ),
13
47
weak_this_factory_(this ) {
48
+ callback_.decompressionOutputCallback = OutputThunk;
49
+ callback_.decompressionOutputRefCon = this ;
14
50
}
15
51
16
52
VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator () {
@@ -20,19 +56,154 @@ bool VTVideoDecodeAccelerator::Initialize(
20
56
media::VideoCodecProfile profile,
21
57
Client* client) {
22
58
DCHECK (CalledOnValidThread ());
23
- DVLOG (2 ) << __FUNCTION__;
24
59
client_ = client;
25
60
26
61
// Only H.264 is supported.
27
62
if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX)
28
63
return false ;
29
64
30
- // Prevent anyone from using VTVideoDecoder for now. http://crbug.com/133828
31
- return false ;
65
+ // TODO(sandersd): Move VideoToolbox library loading to sandbox startup;
66
+ // until then, --no-sandbox is required.
67
+ if (!IsVtInitialized ()) {
68
+ StubPathMap paths;
69
+ // CoreVideo is also required, but the loader stops after the first
70
+ // path is loaded. Instead we rely on the transitive dependency from
71
+ // VideoToolbox to CoreVideo.
72
+ // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox.
73
+ paths[kModuleVt ].push_back (FILE_PATH_LITERAL (
74
+ " /System/Library/Frameworks/VideoToolbox.framework/VideoToolbox" ));
75
+ if (!InitializeStubs (paths))
76
+ return false ;
77
+ }
78
+
79
+ // Spawn a thread to handle parsing and calling VideoToolbox.
80
+ if (!decoder_thread_.Start ())
81
+ return false ;
82
+
83
+ // Note that --ignore-gpu-blacklist is still required to get here.
84
+ return true ;
85
+ }
86
+
87
+ // TODO(sandersd): Proper error reporting instead of CHECKs.
88
+ void VTVideoDecodeAccelerator::ConfigureDecoder (
89
+ const std::vector<const uint8_t *>& nalu_data_ptrs,
90
+ const std::vector<size_t >& nalu_data_sizes) {
91
+ format_.reset ();
92
+ CHECK (!CMVideoFormatDescriptionCreateFromH264ParameterSets (
93
+ kCFAllocatorDefault ,
94
+ nalu_data_ptrs.size (), // parameter_set_count
95
+ &nalu_data_ptrs.front (), // ¶meter_set_pointers
96
+ &nalu_data_sizes.front (), // ¶meter_set_sizes
97
+ kNALUHeaderLength , // nal_unit_header_length
98
+ format_.InitializeInto ()
99
+ ));
100
+
101
+ // TODO(sandersd): Check if the size has changed and handle picture requests.
102
+ CMVideoDimensions coded_size = CMVideoFormatDescriptionGetDimensions (format_);
103
+ coded_size_.SetSize (coded_size.width , coded_size.height );
104
+
105
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config (
106
+ CFDictionaryCreateMutable (
107
+ kCFAllocatorDefault ,
108
+ 1 , // capacity
109
+ &kCFTypeDictionaryKeyCallBacks ,
110
+ &kCFTypeDictionaryValueCallBacks ));
111
+
112
+ CFDictionarySetValue (
113
+ decoder_config,
114
+ // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder
115
+ CFSTR (" EnableHardwareAcceleratedVideoDecoder" ),
116
+ kCFBooleanTrue );
117
+
118
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> image_config (
119
+ CFDictionaryCreateMutable (
120
+ kCFAllocatorDefault ,
121
+ 4 , // capacity
122
+ &kCFTypeDictionaryKeyCallBacks ,
123
+ &kCFTypeDictionaryValueCallBacks ));
124
+
125
+ // TODO(sandersd): ARGB for video that is not 4:2:0.
126
+ int32_t pixel_format = ' 2vuy' ;
127
+ #define CFINT (i ) CFNumberCreate(kCFAllocatorDefault , kCFNumberSInt32Type , &i)
128
+ base::ScopedCFTypeRef<CFNumberRef> cf_pixel_format (CFINT (pixel_format));
129
+ base::ScopedCFTypeRef<CFNumberRef> cf_width (CFINT (coded_size.width ));
130
+ base::ScopedCFTypeRef<CFNumberRef> cf_height (CFINT (coded_size.height ));
131
+ #undef CFINT
132
+ CFDictionarySetValue (
133
+ image_config, kCVPixelBufferPixelFormatTypeKey , cf_pixel_format);
134
+ CFDictionarySetValue (image_config, kCVPixelBufferWidthKey , cf_width);
135
+ CFDictionarySetValue (image_config, kCVPixelBufferHeightKey , cf_height);
136
+ CFDictionarySetValue (
137
+ image_config, kCVPixelBufferOpenGLCompatibilityKey , kCFBooleanTrue );
138
+
139
+ // TODO(sandersd): Skip if the session is compatible.
140
+ // TODO(sandersd): Flush frames when resetting.
141
+ session_.reset ();
142
+ CHECK (!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
+ ));
150
+ DVLOG (2 ) << " Created VTDecompressionSession" ;
32
151
}
33
152
34
153
void VTVideoDecodeAccelerator::Decode (const media::BitstreamBuffer& bitstream) {
35
154
DCHECK (CalledOnValidThread ());
155
+ decoder_thread_.message_loop_proxy ()->PostTask (FROM_HERE, base::Bind (
156
+ &VTVideoDecodeAccelerator::DecodeTask, base::Unretained (this ),
157
+ bitstream));
158
+ }
159
+
160
+ void VTVideoDecodeAccelerator::DecodeTask (
161
+ const media::BitstreamBuffer bitstream) {
162
+ DCHECK (decoder_thread_.message_loop_proxy ()->BelongsToCurrentThread ());
163
+
164
+ // Map the bitstream buffer.
165
+ base::SharedMemory memory (bitstream.handle (), true );
166
+ size_t size = bitstream.size ();
167
+ CHECK (memory.Map (size));
168
+ const uint8_t * buf = static_cast <uint8_t *>(memory.memory ());
169
+
170
+ // Locate relevant NALUs in the buffer.
171
+ size_t data_size = 0 ;
172
+ std::vector<media::H264NALU> nalus;
173
+ std::vector<const uint8_t *> config_nalu_data_ptrs;
174
+ std::vector<size_t > config_nalu_data_sizes;
175
+ parser_.SetStream (buf, size);
176
+ media::H264NALU nalu;
177
+ while (true ) {
178
+ media::H264Parser::Result result = parser_.AdvanceToNextNALU (&nalu);
179
+ if (result == media::H264Parser::kEOStream )
180
+ break ;
181
+ CHECK_EQ (result, media::H264Parser::kOk );
182
+ if (nalu.nal_unit_type == media::H264NALU::kSPS ||
183
+ nalu.nal_unit_type == media::H264NALU::kPPS ||
184
+ nalu.nal_unit_type == media::H264NALU::kSPSExt ) {
185
+ config_nalu_data_ptrs.push_back (nalu.data );
186
+ config_nalu_data_sizes.push_back (nalu.size );
187
+ }
188
+ nalus.push_back (nalu);
189
+ // Each NALU will have a 4-byte length header prepended.
190
+ data_size += kNALUHeaderLength + nalu.size ;
191
+ }
192
+
193
+ if (!config_nalu_data_ptrs.empty ())
194
+ ConfigureDecoder (config_nalu_data_ptrs, config_nalu_data_sizes);
195
+
196
+ // TODO(sandersd): Rewrite slice NALU headers and send for decoding.
197
+ }
198
+
199
+ // This method may be called on any VideoToolbox thread.
200
+ void VTVideoDecodeAccelerator::Output (
201
+ int32_t bitstream_id,
202
+ OSStatus status,
203
+ VTDecodeInfoFlags info_flags,
204
+ CVImageBufferRef image_buffer) {
205
+ // TODO(sandersd): Store the frame in a queue.
206
+ CFRelease (image_buffer);
36
207
}
37
208
38
209
void VTVideoDecodeAccelerator::AssignPictureBuffers (
@@ -46,14 +217,18 @@ void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) {
46
217
47
218
void VTVideoDecodeAccelerator::Flush () {
48
219
DCHECK (CalledOnValidThread ());
220
+ // TODO(sandersd): Trigger flush, sending frames.
49
221
}
50
222
51
223
void VTVideoDecodeAccelerator::Reset () {
52
224
DCHECK (CalledOnValidThread ());
225
+ // TODO(sandersd): Trigger flush, discarding frames.
53
226
}
54
227
55
228
void VTVideoDecodeAccelerator::Destroy () {
56
229
DCHECK (CalledOnValidThread ());
230
+ // TODO(sandersd): Trigger flush, discarding frames, and wait for them.
231
+ delete this ;
57
232
}
58
233
59
234
bool VTVideoDecodeAccelerator::CanDecodeOnIOThread () {
0 commit comments