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> | |
6 #include <OpenGL/CGLIOSurface.h> | |
7 | |
8 #include "base/bind.h" | |
9 #include "base/thread_task_runner_handle.h" | |
5 #include "content/common/gpu/media/vt_video_decode_accelerator.h" | 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 namespace content { | 18 namespace content { |
8 | 19 |
20 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. | |
21 static void OutputThunk( | |
22 void* decompression_output_refcon, | |
23 void* source_frame_refcon, | |
24 OSStatus status, | |
25 VTDecodeInfoFlags info_flags, | |
26 CVImageBufferRef image_buffer, | |
27 CMTime presentation_time_stamp, | |
28 CMTime presentation_duration) { | |
29 VTVideoDecodeAccelerator* vda = | |
30 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); | |
31 int32_t* bitstream_id_ptr = reinterpret_cast<int32_t*>(source_frame_refcon); | |
32 int32_t bitstream_id = *bitstream_id_ptr; | |
33 delete bitstream_id_ptr; | |
34 CFRetain(image_buffer); | |
35 vda->Output(bitstream_id, status, info_flags, image_buffer); | |
36 } | |
37 | |
9 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 38 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
10 : loop_proxy_(base::MessageLoopProxy::current()), | 39 : gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
11 cgl_context_(cgl_context), | 40 cgl_context_(cgl_context), |
12 client_(NULL), | 41 client_(NULL), |
13 weak_this_factory_(this) { | 42 decoder_thread_("VTDecoderThread"), |
43 format_(NULL), | |
44 session_(NULL), | |
45 coded_width_(0), | |
46 coded_height_(0), | |
47 weak_factory_(this) { | |
48 callback_.decompressionOutputCallback = OutputThunk; | |
49 callback_.decompressionOutputRefCon = this; | |
14 } | 50 } |
15 | 51 |
16 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 52 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
53 // TODO(sandersd): Move these into scoped pointers. | |
scherkus (not reviewing)
2014/07/09 18:32:48
can we use base/mac/scoped_cftyperef.h now? if so
sandersd (OOO until July 31)
2014/07/09 20:07:11
Done.
| |
54 if (format_) | |
55 CFRelease(format_); | |
56 if (session_) | |
57 CFRelease(session_); | |
17 } | 58 } |
18 | 59 |
19 bool VTVideoDecodeAccelerator::Initialize( | 60 bool VTVideoDecodeAccelerator::Initialize( |
20 media::VideoCodecProfile profile, | 61 media::VideoCodecProfile profile, |
21 Client* client) { | 62 Client* client) { |
22 DCHECK(CalledOnValidThread()); | 63 DCHECK(CalledOnValidThread()); |
23 DVLOG(2) << __FUNCTION__; | |
24 client_ = client; | 64 client_ = client; |
25 | 65 |
26 // Only H.264 is supported. | 66 // Only H.264 is supported. |
27 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) | 67 if (profile < media::H264PROFILE_MIN || profile > media::H264PROFILE_MAX) |
28 return false; | 68 return false; |
29 | 69 |
30 // Prevent anyone from using VTVideoDecoder for now. http://crbug.com/133828 | 70 // TODO(sandersd): Move VideoToolbox library loading to sandbox startup; |
31 return false; | 71 // until then, --no-sandbox is required. |
72 if (!IsVtInitialized()) { | |
73 StubPathMap paths; | |
74 // CoreVideo is also required, but the loader stops after the first | |
75 // path is loaded. Instead we rely on the transitive dependency from | |
76 // VideoToolbox to CoreVideo. | |
77 // TODO(sandersd): Fallback to PrivateFrameworks for VideoToolbox. | |
78 paths[kModuleVt].push_back(FILE_PATH_LITERAL( | |
79 "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox")); | |
80 if (!InitializeStubs(paths)) | |
81 return false; | |
82 } | |
83 | |
84 // Spawn a thread to handle parsing and calling VideoToolbox. | |
85 if (!decoder_thread_.Start()) | |
86 return false; | |
87 decoder_task_runner_ = decoder_thread_.message_loop_proxy(); | |
88 | |
89 // Note that --ignore-gpu-blacklist is still required to get here. | |
90 return true; | |
91 } | |
92 | |
93 // TODO(sandersd): Proper error reporting instead of CHECKs. | |
94 void VTVideoDecodeAccelerator::ConfigureDecoder( | |
95 const std::vector<const uint8_t*>& nalu_data_ptrs, | |
96 const std::vector<size_t>& nalu_data_sizes) { | |
97 if (format_ != NULL) { | |
scherkus (not reviewing)
2014/07/09 18:32:48
nit: (!format_)
sandersd (OOO until July 31)
2014/07/09 20:07:11
Done.
| |
98 CFRelease(format_); | |
99 format_ = NULL; | |
100 } | |
101 | |
102 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( | |
103 kCFAllocatorDefault, | |
104 nalu_data_ptrs.size(), // parameter_set_count | |
105 &nalu_data_ptrs.front(), // ¶meter_set_pointers | |
106 &nalu_data_sizes.front(), // ¶meter_set_sizes | |
107 4, // nal_unit_header_length | |
108 &format_ | |
109 )); | |
110 | |
111 // TODO(sandersd): Check if the size has changed and handle picture requests. | |
112 CMVideoDimensions coded_size = CMVideoFormatDescriptionGetDimensions(format_); | |
113 coded_width_ = coded_size.width; | |
114 coded_height_ = coded_size.height; | |
115 | |
116 // TODO(sandersd): Scoped pointers. | |
scherkus (not reviewing)
2014/07/09 18:32:49
I'd go for scoped pointers right away
sandersd (OOO until July 31)
2014/07/09 20:07:11
Done.
| |
117 CFMutableDictionaryRef decoder_config = CFDictionaryCreateMutable( | |
118 kCFAllocatorDefault, | |
119 1, // capacity | |
120 &kCFTypeDictionaryKeyCallBacks, | |
121 &kCFTypeDictionaryValueCallBacks); | |
122 | |
123 CFDictionarySetValue( | |
124 decoder_config, | |
125 // kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder | |
126 CFSTR("EnableHardwareAcceleratedVideoDecoder"), | |
127 kCFBooleanTrue); | |
128 | |
129 CFMutableDictionaryRef image_config = CFDictionaryCreateMutable( | |
130 kCFAllocatorDefault, | |
131 4, // capacity | |
132 &kCFTypeDictionaryKeyCallBacks, | |
133 &kCFTypeDictionaryValueCallBacks); | |
134 | |
135 // TODO(sandersd): ARGB for video that is not 4:2:0. | |
136 int32_t pixel_format = '2vuy'; | |
137 #define CFINT(i) CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i) | |
138 CFNumberRef cf_pixel_format = CFINT(pixel_format); | |
139 CFNumberRef cf_width = CFINT(coded_width_); | |
140 CFNumberRef cf_height = CFINT(coded_height_); | |
141 CFDictionarySetValue( | |
142 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | |
scherkus (not reviewing)
2014/07/09 18:32:48
AFAIK (and you may want to verify this) you can di
sandersd (OOO until July 31)
2014/07/09 20:07:12
Sadly that would leak memory, but I've gone ahead
scherkus (not reviewing)
2014/07/09 20:37:44
Yeah I've seen both instances in the Chromium code
| |
143 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | |
144 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | |
145 CFDictionarySetValue( | |
146 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | |
147 CFRelease(cf_pixel_format); | |
148 CFRelease(cf_width); | |
149 CFRelease(cf_height); | |
150 | |
151 if (session_ != NULL) { | |
scherkus (not reviewing)
2014/07/09 18:32:49
!session_
sandersd (OOO until July 31)
2014/07/09 20:07:12
Done.
| |
152 // TODO(sandersd): Check if the session is compatable first. | |
153 // TODO(sandersd): Flush frames. | |
154 CFRelease(session_); | |
155 session_ = NULL; | |
156 } | |
157 | |
158 CHECK(!VTDecompressionSessionCreate( | |
159 kCFAllocatorDefault, | |
160 format_, // video_format_description | |
161 decoder_config, // video_decoder_specification | |
162 image_config, // destination_image_buffer_attributes | |
163 &callback_, // output_callback | |
164 &session_ | |
165 )); | |
166 DVLOG(2) << "Created VTDecompressionSession"; | |
167 | |
168 CFRelease(decoder_config); | |
169 CFRelease(image_config); | |
32 } | 170 } |
33 | 171 |
34 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 172 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
35 DCHECK(CalledOnValidThread()); | 173 DCHECK(CalledOnValidThread()); |
174 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( | |
175 &VTVideoDecodeAccelerator::DecodeTask, weak_factory_.GetWeakPtr(), | |
176 bitstream)); | |
177 } | |
178 | |
179 void VTVideoDecodeAccelerator::DecodeTask( | |
180 const media::BitstreamBuffer bitstream) { | |
181 // Map the bitstream buffer. | |
scherkus (not reviewing)
2014/07/09 18:32:48
DCHECK(decode_thread_.message_loop_proxy()->Belong
sandersd (OOO until July 31)
2014/07/09 20:07:11
Done.
| |
182 base::SharedMemory memory(bitstream.handle(), true); | |
183 size_t size = bitstream.size(); | |
184 CHECK(memory.Map(size)); | |
185 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); | |
186 | |
187 // Locate relevant NALUs in the buffer. | |
188 size_t data_size = 0; | |
189 std::vector<media::H264NALU> nalus; | |
190 std::vector<const uint8_t*> config_nalu_data_ptrs; | |
191 std::vector<size_t> config_nalu_data_sizes; | |
192 parser_.SetStream(buf, size); | |
193 media::H264NALU nalu; | |
194 while (true) { | |
195 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); | |
196 if (result == media::H264Parser::kEOStream) | |
197 break; | |
198 CHECK(result == media::H264Parser::kOk); | |
scherkus (not reviewing)
2014/07/09 18:32:48
CHECK_EQ
sandersd (OOO until July 31)
2014/07/09 20:07:12
Done.
| |
199 if (nalu.nal_unit_type == media::H264NALU::kSPS || | |
200 nalu.nal_unit_type == media::H264NALU::kPPS || | |
201 nalu.nal_unit_type == media::H264NALU::kSPSExt) { | |
202 config_nalu_data_ptrs.push_back(nalu.data); | |
203 config_nalu_data_sizes.push_back(nalu.size); | |
204 } | |
205 nalus.push_back(nalu); | |
206 data_size += 4 + nalu.size; | |
207 } | |
208 | |
209 if (!config_nalu_data_ptrs.empty()) | |
210 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); | |
211 | |
212 // TODO(sandersd): Rewrite slice NALU headers and send for decoding. | |
213 } | |
214 | |
215 // This method may be called on any VideoToolbox thread. | |
216 void VTVideoDecodeAccelerator::Output( | |
217 int32_t bitstream_id, | |
218 OSStatus status, | |
219 VTDecodeInfoFlags info_flags, | |
220 CVImageBufferRef image_buffer) { | |
221 // TODO(sandersd): Store the frame in a queue. | |
222 CFRelease(image_buffer); | |
36 } | 223 } |
37 | 224 |
38 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 225 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
39 const std::vector<media::PictureBuffer>& pictures) { | 226 const std::vector<media::PictureBuffer>& pictures) { |
40 DCHECK(CalledOnValidThread()); | 227 DCHECK(CalledOnValidThread()); |
41 } | 228 } |
42 | 229 |
43 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { | 230 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { |
44 DCHECK(CalledOnValidThread()); | 231 DCHECK(CalledOnValidThread()); |
45 } | 232 } |
46 | 233 |
47 void VTVideoDecodeAccelerator::Flush() { | 234 void VTVideoDecodeAccelerator::Flush() { |
48 DCHECK(CalledOnValidThread()); | 235 DCHECK(CalledOnValidThread()); |
236 // TODO(sandersd): Trigger flush, sending frames. | |
49 } | 237 } |
50 | 238 |
51 void VTVideoDecodeAccelerator::Reset() { | 239 void VTVideoDecodeAccelerator::Reset() { |
52 DCHECK(CalledOnValidThread()); | 240 DCHECK(CalledOnValidThread()); |
241 // TODO(sandersd): Trigger flush, discarding frames. | |
53 } | 242 } |
54 | 243 |
55 void VTVideoDecodeAccelerator::Destroy() { | 244 void VTVideoDecodeAccelerator::Destroy() { |
56 DCHECK(CalledOnValidThread()); | 245 DCHECK(CalledOnValidThread()); |
246 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. | |
247 delete this; | |
57 } | 248 } |
58 | 249 |
59 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 250 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
60 return false; | 251 return false; |
61 } | 252 } |
62 | 253 |
63 } // namespace content | 254 } // namespace content |
OLD | NEW |