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/callback_helpers.h" | |
10 #include "base/command_line.h" | 11 #include "base/command_line.h" |
11 #include "base/sys_byteorder.h" | 12 #include "base/sys_byteorder.h" |
12 #include "base/thread_task_runner_handle.h" | 13 #include "base/thread_task_runner_handle.h" |
13 #include "content/common/gpu/media/vt_video_decode_accelerator.h" | 14 #include "content/common/gpu/media/vt_video_decode_accelerator.h" |
14 #include "content/public/common/content_switches.h" | 15 #include "content/public/common/content_switches.h" |
15 #include "media/filters/h264_parser.h" | 16 #include "media/filters/h264_parser.h" |
16 #include "ui/gl/scoped_binders.h" | 17 #include "ui/gl/scoped_binders.h" |
17 #include "ui/gl/scoped_cgl.h" | 18 #include "ui/gl/scoped_cgl.h" |
18 | 19 |
19 using content_common_gpu_media::kModuleVt; | 20 using content_common_gpu_media::kModuleVt; |
20 using content_common_gpu_media::InitializeStubs; | 21 using content_common_gpu_media::InitializeStubs; |
21 using content_common_gpu_media::IsVtInitialized; | 22 using content_common_gpu_media::IsVtInitialized; |
22 using content_common_gpu_media::StubPathMap; | 23 using content_common_gpu_media::StubPathMap; |
23 | 24 |
25 #define NOTIFY_STATUS(name, status) \ | |
26 do { \ | |
27 LOG(ERROR) << name << " failed with status " << status; \ | |
28 NotifyError(PLATFORM_FAILURE); \ | |
29 } while (0) | |
30 | |
24 namespace content { | 31 namespace content { |
25 | 32 |
26 // Size of NALU length headers in AVCC/MPEG-4 format (can be 1, 2, or 4). | 33 // Size of NALU length headers in AVCC/MPEG-4 format (can be 1, 2, or 4). |
27 static const int kNALUHeaderLength = 4; | 34 static const int kNALUHeaderLength = 4; |
28 | 35 |
29 // We only request 5 picture buffers from the client which are used to hold the | 36 // We only request 5 picture buffers from the client which are used to hold the |
30 // decoded samples. These buffers are then reused when the client tells us that | 37 // decoded samples. These buffers are then reused when the client tells us that |
31 // it is done with the buffer. | 38 // it is done with the buffer. |
32 static const int kNumPictureBuffers = 5; | 39 static const int kNumPictureBuffers = 5; |
33 | 40 |
(...skipping 28 matching lines...) Expand all Loading... | |
62 : action(action), | 69 : action(action), |
63 bitstream_id(bitstream_id) { | 70 bitstream_id(bitstream_id) { |
64 } | 71 } |
65 | 72 |
66 VTVideoDecodeAccelerator::PendingAction::~PendingAction() { | 73 VTVideoDecodeAccelerator::PendingAction::~PendingAction() { |
67 } | 74 } |
68 | 75 |
69 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) | 76 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
70 : cgl_context_(cgl_context), | 77 : cgl_context_(cgl_context), |
71 client_(NULL), | 78 client_(NULL), |
79 has_error_(false), | |
72 format_(NULL), | 80 format_(NULL), |
73 session_(NULL), | 81 session_(NULL), |
74 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), | 82 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
75 weak_this_factory_(this), | 83 weak_this_factory_(this), |
76 decoder_thread_("VTDecoderThread") { | 84 decoder_thread_("VTDecoderThread") { |
77 callback_.decompressionOutputCallback = OutputThunk; | 85 callback_.decompressionOutputCallback = OutputThunk; |
78 callback_.decompressionOutputRefCon = this; | 86 callback_.decompressionOutputRefCon = this; |
79 } | 87 } |
80 | 88 |
81 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { | 89 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { |
(...skipping 26 matching lines...) Expand all Loading... | |
108 return false; | 116 return false; |
109 } | 117 } |
110 | 118 |
111 // Spawn a thread to handle parsing and calling VideoToolbox. | 119 // Spawn a thread to handle parsing and calling VideoToolbox. |
112 if (!decoder_thread_.Start()) | 120 if (!decoder_thread_.Start()) |
113 return false; | 121 return false; |
114 | 122 |
115 return true; | 123 return true; |
116 } | 124 } |
117 | 125 |
118 // TODO(sandersd): Proper error reporting instead of CHECKs. | 126 bool VTVideoDecodeAccelerator::ConfigureDecoder( |
119 void VTVideoDecodeAccelerator::ConfigureDecoder( | |
120 const std::vector<const uint8_t*>& nalu_data_ptrs, | 127 const std::vector<const uint8_t*>& nalu_data_ptrs, |
121 const std::vector<size_t>& nalu_data_sizes) { | 128 const std::vector<size_t>& nalu_data_sizes) { |
122 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 129 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
130 | |
123 // Construct a new format description from the parameter sets. | 131 // Construct a new format description from the parameter sets. |
124 // TODO(sandersd): Replace this with custom code to support OS X < 10.9. | 132 // TODO(sandersd): Replace this with custom code to support OS X < 10.9. |
125 format_.reset(); | 133 format_.reset(); |
126 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( | 134 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets( |
127 kCFAllocatorDefault, | 135 kCFAllocatorDefault, |
128 nalu_data_ptrs.size(), // parameter_set_count | 136 nalu_data_ptrs.size(), // parameter_set_count |
129 &nalu_data_ptrs.front(), // ¶meter_set_pointers | 137 &nalu_data_ptrs.front(), // ¶meter_set_pointers |
130 &nalu_data_sizes.front(), // ¶meter_set_sizes | 138 &nalu_data_sizes.front(), // ¶meter_set_sizes |
131 kNALUHeaderLength, // nal_unit_header_length | 139 kNALUHeaderLength, // nal_unit_header_length |
132 format_.InitializeInto())); | 140 format_.InitializeInto()); |
141 if (status) { | |
142 NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()", | |
143 status); | |
144 return false; | |
145 } | |
133 CMVideoDimensions coded_dimensions = | 146 CMVideoDimensions coded_dimensions = |
134 CMVideoFormatDescriptionGetDimensions(format_); | 147 CMVideoFormatDescriptionGetDimensions(format_); |
135 | 148 |
136 // Prepare VideoToolbox configuration dictionaries. | 149 // Prepare VideoToolbox configuration dictionaries. |
137 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( | 150 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( |
138 CFDictionaryCreateMutable( | 151 CFDictionaryCreateMutable( |
139 kCFAllocatorDefault, | 152 kCFAllocatorDefault, |
140 1, // capacity | 153 1, // capacity |
141 &kCFTypeDictionaryKeyCallBacks, | 154 &kCFTypeDictionaryKeyCallBacks, |
142 &kCFTypeDictionaryValueCallBacks)); | 155 &kCFTypeDictionaryValueCallBacks)); |
(...skipping 20 matching lines...) Expand all Loading... | |
163 #undef CFINT | 176 #undef CFINT |
164 CFDictionarySetValue( | 177 CFDictionarySetValue( |
165 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); | 178 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); |
166 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); | 179 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); |
167 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); | 180 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); |
168 CFDictionarySetValue( | 181 CFDictionarySetValue( |
169 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); | 182 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
170 | 183 |
171 // TODO(sandersd): Check if the session is already compatible. | 184 // TODO(sandersd): Check if the session is already compatible. |
172 session_.reset(); | 185 session_.reset(); |
173 CHECK(!VTDecompressionSessionCreate( | 186 status = VTDecompressionSessionCreate( |
174 kCFAllocatorDefault, | 187 kCFAllocatorDefault, |
175 format_, // video_format_description | 188 format_, // video_format_description |
176 decoder_config, // video_decoder_specification | 189 decoder_config, // video_decoder_specification |
177 image_config, // destination_image_buffer_attributes | 190 image_config, // destination_image_buffer_attributes |
178 &callback_, // output_callback | 191 &callback_, // output_callback |
179 session_.InitializeInto())); | 192 session_.InitializeInto()); |
193 if (status) { | |
194 NOTIFY_STATUS("VTDecompressionSessionCreate()", status); | |
195 return false; | |
196 } | |
180 | 197 |
181 // If the size has changed, trigger a request for new picture buffers. | 198 // If the size has changed, trigger a request for new picture buffers. |
182 // TODO(sandersd): Move to SendPictures(), and use this just as a hint for an | 199 // TODO(sandersd): Move to SendPictures(), and use this just as a hint for an |
183 // upcoming size change. | 200 // upcoming size change. |
184 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); | 201 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); |
185 if (coded_size_ != new_coded_size) { | 202 if (coded_size_ != new_coded_size) { |
186 coded_size_ = new_coded_size; | 203 coded_size_ = new_coded_size; |
187 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 204 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
188 &VTVideoDecodeAccelerator::SizeChangedTask, | 205 &VTVideoDecodeAccelerator::SizeChangedTask, |
189 weak_this_factory_.GetWeakPtr(), | 206 weak_this_factory_.GetWeakPtr(), |
190 coded_size_));; | 207 coded_size_));; |
191 } | 208 } |
209 | |
210 return true; | |
192 } | 211 } |
193 | 212 |
194 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { | 213 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
195 DCHECK(CalledOnValidThread()); | 214 DCHECK(CalledOnValidThread()); |
196 CHECK_GE(bitstream.id(), 0) << "Negative bitstream_id"; | 215 // Not actually a requirement of the VDA API, but we're lazy and use negative |
216 // values as flags internally. Revisit that if this actually happens. | |
217 if (bitstream.id() < 0) { | |
218 LOG(ERROR) << "Negative bitstream ID"; | |
219 NotifyError(INVALID_ARGUMENT); | |
220 client_->NotifyEndOfBitstreamBuffer(bitstream.id()); | |
221 return; | |
222 } | |
197 pending_bitstream_ids_.push(bitstream.id()); | 223 pending_bitstream_ids_.push(bitstream.id()); |
198 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | 224 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
199 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), | 225 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
200 bitstream)); | 226 bitstream)); |
201 } | 227 } |
202 | 228 |
203 // TODO(sandersd): Proper error reporting instead of CHECKs. | |
204 void VTVideoDecodeAccelerator::DecodeTask( | 229 void VTVideoDecodeAccelerator::DecodeTask( |
205 const media::BitstreamBuffer bitstream) { | 230 const media::BitstreamBuffer& bitstream) { |
206 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 231 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
207 | 232 |
233 // Once we have a bitstream buffer, we must either decode it or drop it. | |
234 // This construct ensures that the buffer is always dropped unless we call | |
235 // drop_bitstream.Release(). | |
236 base::ScopedClosureRunner drop_bitstream(base::Bind( | |
237 &VTVideoDecodeAccelerator::DropBitstream, base::Unretained(this), | |
238 bitstream.id())); | |
239 | |
208 // Map the bitstream buffer. | 240 // Map the bitstream buffer. |
209 base::SharedMemory memory(bitstream.handle(), true); | 241 base::SharedMemory memory(bitstream.handle(), true); |
210 size_t size = bitstream.size(); | 242 size_t size = bitstream.size(); |
211 CHECK(memory.Map(size)); | 243 if (!memory.Map(size)) { |
244 LOG(ERROR) << "Failed to map bitstream buffer"; | |
245 NotifyError(PLATFORM_FAILURE); | |
246 return; | |
247 } | |
212 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); | 248 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); |
213 | 249 |
214 // NALUs are stored with Annex B format in the bitstream buffer (start codes), | 250 // NALUs are stored with Annex B format in the bitstream buffer (start codes), |
215 // but VideoToolbox expects AVCC/MPEG-4 format (length headers), so we must | 251 // but VideoToolbox expects AVCC/MPEG-4 format (length headers), so we must |
216 // rewrite the data. | 252 // rewrite the data. |
217 // | 253 // |
218 // 1. Locate relevant NALUs and compute the size of the translated data. | 254 // 1. Locate relevant NALUs and compute the size of the translated data. |
219 // Also record any parameter sets for VideoToolbox initialization. | 255 // Also record any parameter sets for VideoToolbox initialization. |
220 size_t data_size = 0; | 256 size_t data_size = 0; |
221 std::vector<media::H264NALU> nalus; | 257 std::vector<media::H264NALU> nalus; |
222 std::vector<const uint8_t*> config_nalu_data_ptrs; | 258 std::vector<const uint8_t*> config_nalu_data_ptrs; |
223 std::vector<size_t> config_nalu_data_sizes; | 259 std::vector<size_t> config_nalu_data_sizes; |
224 parser_.SetStream(buf, size); | 260 parser_.SetStream(buf, size); |
225 media::H264NALU nalu; | 261 media::H264NALU nalu; |
226 while (true) { | 262 while (true) { |
227 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); | 263 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); |
228 if (result == media::H264Parser::kEOStream) | 264 if (result == media::H264Parser::kEOStream) |
229 break; | 265 break; |
230 CHECK_EQ(result, media::H264Parser::kOk); | 266 if (result != media::H264Parser::kOk) { |
267 LOG(ERROR) << "Failed to find H.264 NALU"; | |
268 NotifyError(PLATFORM_FAILURE); | |
269 return; | |
270 } | |
231 // TODO(sandersd): Check that these are only at the start. | 271 // TODO(sandersd): Check that these are only at the start. |
232 if (nalu.nal_unit_type == media::H264NALU::kSPS || | 272 if (nalu.nal_unit_type == media::H264NALU::kSPS || |
233 nalu.nal_unit_type == media::H264NALU::kPPS || | 273 nalu.nal_unit_type == media::H264NALU::kPPS || |
234 nalu.nal_unit_type == media::H264NALU::kSPSExt) { | 274 nalu.nal_unit_type == media::H264NALU::kSPSExt) { |
235 DVLOG(2) << "Parameter set " << nalu.nal_unit_type; | 275 DVLOG(2) << "Parameter set " << nalu.nal_unit_type; |
236 config_nalu_data_ptrs.push_back(nalu.data); | 276 config_nalu_data_ptrs.push_back(nalu.data); |
237 config_nalu_data_sizes.push_back(nalu.size); | 277 config_nalu_data_sizes.push_back(nalu.size); |
238 } else { | 278 } else { |
239 nalus.push_back(nalu); | 279 nalus.push_back(nalu); |
240 data_size += kNALUHeaderLength + nalu.size; | 280 data_size += kNALUHeaderLength + nalu.size; |
241 } | 281 } |
242 } | 282 } |
243 | 283 |
244 // 2. Initialize VideoToolbox. | 284 // 2. Initialize VideoToolbox. |
245 // TODO(sandersd): Reinitialize when there are new parameter sets. | 285 // TODO(sandersd): Reinitialize when there are new parameter sets. |
246 if (!session_) | 286 if (!session_) { |
247 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); | 287 // If configuring fails, ConfigureDecoder() already called NotifyError(). |
288 if (!ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes)) | |
289 return; | |
290 } | |
248 | 291 |
249 // If there are no non-configuration units, immediately return an empty | 292 // If there are no non-configuration units, immediately return an empty |
250 // (ie. dropped) frame. It is an error to create a MemoryBlock with zero | 293 // (ie. dropped) frame. It is an error to create a MemoryBlock with zero |
251 // size. | 294 // size. |
252 if (!data_size) { | 295 if (!data_size) |
253 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
254 &VTVideoDecodeAccelerator::OutputTask, | |
255 weak_this_factory_.GetWeakPtr(), | |
256 DecodedFrame(bitstream.id(), NULL))); | |
257 return; | 296 return; |
258 } | |
259 | 297 |
260 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. | 298 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. |
261 base::ScopedCFTypeRef<CMBlockBufferRef> data; | 299 base::ScopedCFTypeRef<CMBlockBufferRef> data; |
262 CHECK(!CMBlockBufferCreateWithMemoryBlock( | 300 OSStatus status = CMBlockBufferCreateWithMemoryBlock( |
263 kCFAllocatorDefault, | 301 kCFAllocatorDefault, |
264 NULL, // &memory_block | 302 NULL, // &memory_block |
265 data_size, // block_length | 303 data_size, // block_length |
266 kCFAllocatorDefault, // block_allocator | 304 kCFAllocatorDefault, // block_allocator |
267 NULL, // &custom_block_source | 305 NULL, // &custom_block_source |
268 0, // offset_to_data | 306 0, // offset_to_data |
269 data_size, // data_length | 307 data_size, // data_length |
270 0, // flags | 308 0, // flags |
271 data.InitializeInto())); | 309 data.InitializeInto()); |
310 if (status) { | |
311 NOTIFY_STATUS("CMBlockBufferCreateWithMemoryBlock()", status); | |
312 return; | |
313 } | |
272 | 314 |
273 // 4. Copy NALU data, inserting length headers. | 315 // 4. Copy NALU data, inserting length headers. |
274 size_t offset = 0; | 316 size_t offset = 0; |
275 for (size_t i = 0; i < nalus.size(); i++) { | 317 for (size_t i = 0; i < nalus.size(); i++) { |
276 media::H264NALU& nalu = nalus[i]; | 318 media::H264NALU& nalu = nalus[i]; |
277 uint32_t header = base::HostToNet32(static_cast<uint32_t>(nalu.size)); | 319 uint32_t header = base::HostToNet32(static_cast<uint32_t>(nalu.size)); |
278 CHECK(!CMBlockBufferReplaceDataBytes( | 320 status = CMBlockBufferReplaceDataBytes( |
279 &header, data, offset, kNALUHeaderLength)); | 321 &header, data, offset, kNALUHeaderLength); |
322 if (status) { | |
323 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status); | |
324 return; | |
325 } | |
280 offset += kNALUHeaderLength; | 326 offset += kNALUHeaderLength; |
281 CHECK(!CMBlockBufferReplaceDataBytes(nalu.data, data, offset, nalu.size)); | 327 status = CMBlockBufferReplaceDataBytes(nalu.data, data, offset, nalu.size); |
328 if (status) { | |
329 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status); | |
330 return; | |
331 } | |
282 offset += nalu.size; | 332 offset += nalu.size; |
283 } | 333 } |
284 | 334 |
285 // 5. Package the data for VideoToolbox and request decoding. | 335 // 5. Package the data for VideoToolbox and request decoding. |
286 base::ScopedCFTypeRef<CMSampleBufferRef> frame; | 336 base::ScopedCFTypeRef<CMSampleBufferRef> frame; |
287 CHECK(!CMSampleBufferCreate( | 337 status = CMSampleBufferCreate( |
288 kCFAllocatorDefault, | 338 kCFAllocatorDefault, |
289 data, // data_buffer | 339 data, // data_buffer |
290 true, // data_ready | 340 true, // data_ready |
291 NULL, // make_data_ready_callback | 341 NULL, // make_data_ready_callback |
292 NULL, // make_data_ready_refcon | 342 NULL, // make_data_ready_refcon |
293 format_, // format_description | 343 format_, // format_description |
294 1, // num_samples | 344 1, // num_samples |
295 0, // num_sample_timing_entries | 345 0, // num_sample_timing_entries |
296 NULL, // &sample_timing_array | 346 NULL, // &sample_timing_array |
297 0, // num_sample_size_entries | 347 0, // num_sample_size_entries |
298 NULL, // &sample_size_array | 348 NULL, // &sample_size_array |
299 frame.InitializeInto())); | 349 frame.InitializeInto()); |
350 if (status) { | |
351 NOTIFY_STATUS("CMSampleBufferCreate()", status); | |
352 return; | |
353 } | |
300 | 354 |
301 // Asynchronous Decompression allows for parallel submission of frames | 355 // Asynchronous Decompression allows for parallel submission of frames |
302 // (without it, DecodeFrame() does not return until the frame has been | 356 // (without it, DecodeFrame() does not return until the frame has been |
303 // decoded). We don't enable Temporal Processing so that frames are always | 357 // decoded). We don't enable Temporal Processing so that frames are always |
304 // returned in decode order; this makes it easier to avoid deadlock. | 358 // returned in decode order; this makes it easier to avoid deadlock. |
305 VTDecodeFrameFlags decode_flags = | 359 VTDecodeFrameFlags decode_flags = |
306 kVTDecodeFrame_EnableAsynchronousDecompression; | 360 kVTDecodeFrame_EnableAsynchronousDecompression; |
307 | 361 |
308 intptr_t bitstream_id = bitstream.id(); | 362 intptr_t bitstream_id = bitstream.id(); |
309 CHECK(!VTDecompressionSessionDecodeFrame( | 363 status = VTDecompressionSessionDecodeFrame( |
310 session_, | 364 session_, |
311 frame, // sample_buffer | 365 frame, // sample_buffer |
312 decode_flags, // decode_flags | 366 decode_flags, // decode_flags |
313 reinterpret_cast<void*>(bitstream_id), // source_frame_refcon | 367 reinterpret_cast<void*>(bitstream_id), // source_frame_refcon |
314 NULL)); // &info_flags_out | 368 NULL); // &info_flags_out |
369 if (status) { | |
370 NOTIFY_STATUS("VTDecompressionSessionDecodeFrame()", status); | |
371 return; | |
372 } | |
373 | |
374 // Now that the bitstream is decoding, don't drop it. | |
Pawel Osciak
2014/10/30 10:35:13
At this point decoding is not done yet? It is done
sandersd (OOO until July 31)
2014/10/30 22:17:03
The comment is correct, the frame has been submitt
| |
375 (void)drop_bitstream.Release(); | |
Pawel Osciak
2014/10/30 10:35:13
Do we need this (void) ?
sandersd (OOO until July 31)
2014/10/30 22:17:03
Release() is WARN_UNUSED_RESULT.
| |
315 } | 376 } |
316 | 377 |
317 // This method may be called on any VideoToolbox thread. | 378 // This method may be called on any VideoToolbox thread. |
318 // TODO(sandersd): Proper error reporting instead of CHECKs. | |
319 void VTVideoDecodeAccelerator::Output( | 379 void VTVideoDecodeAccelerator::Output( |
320 int32_t bitstream_id, | 380 int32_t bitstream_id, |
321 OSStatus status, | 381 OSStatus status, |
322 CVImageBufferRef image_buffer) { | 382 CVImageBufferRef image_buffer) { |
323 CHECK(!status); | 383 if (status) { |
324 CHECK_EQ(CFGetTypeID(image_buffer), CVPixelBufferGetTypeID()); | 384 // TODO(sandersd): Handle dropped frames. |
325 CFRetain(image_buffer); | 385 NOTIFY_STATUS("Decoding", status); |
386 image_buffer = NULL; | |
387 } else if (CFGetTypeID(image_buffer) != CVPixelBufferGetTypeID()) { | |
Pawel Osciak
2014/10/30 10:35:13
s/else if/else if/
sandersd (OOO until July 31)
2014/10/30 22:17:03
Done.
| |
388 LOG(ERROR) << "Decoded frame is not a CVPixelBuffer"; | |
389 NotifyError(PLATFORM_FAILURE); | |
390 image_buffer = NULL; | |
391 } else { | |
392 CFRetain(image_buffer); | |
393 } | |
326 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 394 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
327 &VTVideoDecodeAccelerator::OutputTask, | 395 &VTVideoDecodeAccelerator::OutputTask, |
328 weak_this_factory_.GetWeakPtr(), | 396 weak_this_factory_.GetWeakPtr(), |
329 DecodedFrame(bitstream_id, image_buffer))); | 397 DecodedFrame(bitstream_id, image_buffer))); |
330 } | 398 } |
331 | 399 |
332 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { | 400 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { |
333 DCHECK(CalledOnValidThread()); | 401 DCHECK(CalledOnValidThread()); |
334 decoded_frames_.push(frame); | 402 decoded_frames_.push(frame); |
335 ProcessDecodedFrames(); | 403 ProcessDecodedFrames(); |
336 } | 404 } |
337 | 405 |
338 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { | 406 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { |
339 DCHECK(CalledOnValidThread()); | 407 DCHECK(CalledOnValidThread()); |
340 texture_size_ = coded_size; | 408 texture_size_ = coded_size; |
341 // TODO(sandersd): Dismiss existing picture buffers. | 409 // TODO(sandersd): Dismiss existing picture buffers. |
342 client_->ProvidePictureBuffers( | 410 client_->ProvidePictureBuffers( |
343 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); | 411 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); |
344 } | 412 } |
345 | 413 |
346 void VTVideoDecodeAccelerator::AssignPictureBuffers( | 414 void VTVideoDecodeAccelerator::AssignPictureBuffers( |
347 const std::vector<media::PictureBuffer>& pictures) { | 415 const std::vector<media::PictureBuffer>& pictures) { |
348 DCHECK(CalledOnValidThread()); | 416 DCHECK(CalledOnValidThread()); |
349 | 417 |
350 for (size_t i = 0; i < pictures.size(); i++) { | 418 for (size_t i = 0; i < pictures.size(); i++) { |
351 CHECK(!texture_ids_.count(pictures[i].id())); | 419 DCHECK(!texture_ids_.count(pictures[i].id())); |
352 available_picture_ids_.push(pictures[i].id()); | 420 available_picture_ids_.push(pictures[i].id()); |
353 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); | 421 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); |
354 } | 422 } |
355 | 423 |
356 // Pictures are not marked as uncleared until after this method returns, and | 424 // Pictures are not marked as uncleared until after this method returns, and |
357 // they will be broken if they are used before that happens. So, schedule | 425 // they will be broken if they are used before that happens. So, schedule |
358 // future work after that happens. | 426 // future work after that happens. |
359 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | 427 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
360 &VTVideoDecodeAccelerator::ProcessDecodedFrames, | 428 &VTVideoDecodeAccelerator::ProcessDecodedFrames, |
361 weak_this_factory_.GetWeakPtr())); | 429 weak_this_factory_.GetWeakPtr())); |
(...skipping 30 matching lines...) Expand all Loading... | |
392 pending_actions_.pop(); | 460 pending_actions_.pop(); |
393 } | 461 } |
394 } | 462 } |
395 | 463 |
396 void VTVideoDecodeAccelerator::ProcessDecodedFrames() { | 464 void VTVideoDecodeAccelerator::ProcessDecodedFrames() { |
397 DCHECK(CalledOnValidThread()); | 465 DCHECK(CalledOnValidThread()); |
398 | 466 |
399 while (!decoded_frames_.empty()) { | 467 while (!decoded_frames_.empty()) { |
400 if (pending_actions_.empty()) { | 468 if (pending_actions_.empty()) { |
401 // No pending actions; send frames normally. | 469 // No pending actions; send frames normally. |
402 SendPictures(pending_bitstream_ids_.back()); | 470 if (!has_error_) |
471 SendPictures(pending_bitstream_ids_.back()); | |
403 return; | 472 return; |
404 } | 473 } |
405 | 474 |
406 int32_t next_action_bitstream_id = pending_actions_.front().bitstream_id; | 475 int32_t next_action_bitstream_id = pending_actions_.front().bitstream_id; |
407 int32_t last_sent_bitstream_id = -1; | 476 int32_t last_sent_bitstream_id = -1; |
408 switch (pending_actions_.front().action) { | 477 switch (pending_actions_.front().action) { |
409 case ACTION_FLUSH: | 478 case ACTION_FLUSH: |
410 // Send frames normally. | 479 // Send frames normally. |
480 if (has_error_) | |
481 return; | |
411 last_sent_bitstream_id = SendPictures(next_action_bitstream_id); | 482 last_sent_bitstream_id = SendPictures(next_action_bitstream_id); |
412 break; | 483 break; |
413 | 484 |
414 case ACTION_RESET: | 485 case ACTION_RESET: |
415 // Drop decoded frames. | 486 // Drop decoded frames. |
487 if (has_error_) | |
488 return; | |
416 while (!decoded_frames_.empty() && | 489 while (!decoded_frames_.empty() && |
417 last_sent_bitstream_id != next_action_bitstream_id) { | 490 last_sent_bitstream_id != next_action_bitstream_id) { |
418 last_sent_bitstream_id = decoded_frames_.front().bitstream_id; | 491 last_sent_bitstream_id = decoded_frames_.front().bitstream_id; |
419 decoded_frames_.pop(); | 492 decoded_frames_.pop(); |
420 DCHECK_EQ(pending_bitstream_ids_.front(), last_sent_bitstream_id); | 493 DCHECK_EQ(pending_bitstream_ids_.front(), last_sent_bitstream_id); |
421 pending_bitstream_ids_.pop(); | 494 pending_bitstream_ids_.pop(); |
422 client_->NotifyEndOfBitstreamBuffer(last_sent_bitstream_id); | 495 client_->NotifyEndOfBitstreamBuffer(last_sent_bitstream_id); |
423 } | 496 } |
424 break; | 497 break; |
425 | 498 |
(...skipping 21 matching lines...) Expand all Loading... | |
447 // Complete all actions pending for this |bitstream_id|, then loop to see | 520 // Complete all actions pending for this |bitstream_id|, then loop to see |
448 // if progress can be made on the next action. | 521 // if progress can be made on the next action. |
449 CompleteActions(next_action_bitstream_id); | 522 CompleteActions(next_action_bitstream_id); |
450 } | 523 } |
451 } | 524 } |
452 | 525 |
453 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { | 526 int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) { |
454 DCHECK(CalledOnValidThread()); | 527 DCHECK(CalledOnValidThread()); |
455 DCHECK(!decoded_frames_.empty()); | 528 DCHECK(!decoded_frames_.empty()); |
456 | 529 |
530 // TODO(sandersd): Store the actual last sent bitstream ID? | |
531 int32_t last_sent_bitstream_id = -1; | |
532 | |
457 if (available_picture_ids_.empty()) | 533 if (available_picture_ids_.empty()) |
458 return -1; | 534 return last_sent_bitstream_id; |
459 | 535 |
460 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); | 536 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); |
461 glEnable(GL_TEXTURE_RECTANGLE_ARB); | 537 glEnable(GL_TEXTURE_RECTANGLE_ARB); |
462 | |
463 int32_t last_sent_bitstream_id = -1; | |
464 while (!available_picture_ids_.empty() && | 538 while (!available_picture_ids_.empty() && |
465 !decoded_frames_.empty() && | 539 !decoded_frames_.empty() && |
466 last_sent_bitstream_id != up_to_bitstream_id) { | 540 last_sent_bitstream_id != up_to_bitstream_id && |
467 DecodedFrame frame = decoded_frames_.front(); | 541 !has_error_) { |
468 decoded_frames_.pop(); | 542 // We don't pop |frame| until it is consumed, which won't happen if an |
543 // error occurs. Conveniently, this also removes some refcounting. | |
544 const DecodedFrame& frame = decoded_frames_.front(); | |
469 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); | 545 DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id); |
470 pending_bitstream_ids_.pop(); | 546 |
547 // Likewise, |picture_id| won't be popped if |image_buffer| is NULL or an | |
548 // error occurs. | |
549 // TODO(sandersd): Don't block waiting for a |picture_id| when | |
550 // |image_buffer| is NULL. | |
471 int32_t picture_id = available_picture_ids_.front(); | 551 int32_t picture_id = available_picture_ids_.front(); |
472 available_picture_ids_.pop(); | |
473 | 552 |
474 CVImageBufferRef image_buffer = frame.image_buffer.get(); | 553 CVImageBufferRef image_buffer = frame.image_buffer.get(); |
475 if (image_buffer) { | 554 if (image_buffer) { |
476 IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer); | 555 IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer); |
477 | 556 |
478 // TODO(sandersd): Find out why this sometimes fails due to no GL context. | 557 // TODO(sandersd): Find out why this sometimes fails due to no GL context. |
479 gfx::ScopedTextureBinder | 558 gfx::ScopedTextureBinder |
480 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); | 559 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); |
481 CHECK(!CGLTexImageIOSurface2D( | 560 CGLError status = CGLTexImageIOSurface2D( |
482 cgl_context_, // ctx | 561 cgl_context_, // ctx |
483 GL_TEXTURE_RECTANGLE_ARB, // target | 562 GL_TEXTURE_RECTANGLE_ARB, // target |
484 GL_RGB, // internal_format | 563 GL_RGB, // internal_format |
485 texture_size_.width(), // width | 564 texture_size_.width(), // width |
486 texture_size_.height(), // height | 565 texture_size_.height(), // height |
487 GL_YCBCR_422_APPLE, // format | 566 GL_YCBCR_422_APPLE, // format |
488 GL_UNSIGNED_SHORT_8_8_APPLE, // type | 567 GL_UNSIGNED_SHORT_8_8_APPLE, // type |
489 surface, // io_surface | 568 surface, // io_surface |
490 0)); // plane | 569 0); // plane |
570 if (status != kCGLNoError) { | |
571 NOTIFY_STATUS("CGLTexImageIOSurface2D()", status); | |
572 break; | |
573 } | |
491 | 574 |
492 picture_bindings_[picture_id] = frame.image_buffer; | 575 picture_bindings_[picture_id] = frame.image_buffer; |
493 client_->PictureReady(media::Picture( | 576 client_->PictureReady(media::Picture( |
494 picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); | 577 picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); |
578 available_picture_ids_.pop(); | |
495 } | 579 } |
496 | 580 |
497 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); | 581 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
498 last_sent_bitstream_id = frame.bitstream_id; | 582 last_sent_bitstream_id = frame.bitstream_id; |
583 decoded_frames_.pop(); | |
584 pending_bitstream_ids_.pop(); | |
499 } | 585 } |
500 | |
501 glDisable(GL_TEXTURE_RECTANGLE_ARB); | 586 glDisable(GL_TEXTURE_RECTANGLE_ARB); |
502 return last_sent_bitstream_id; | 587 return last_sent_bitstream_id; |
503 } | 588 } |
504 | 589 |
505 void VTVideoDecodeAccelerator::FlushTask() { | 590 void VTVideoDecodeAccelerator::FlushTask() { |
506 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | 591 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
507 CHECK(!VTDecompressionSessionFinishDelayedFrames(session_)); | 592 OSStatus status = VTDecompressionSessionFinishDelayedFrames(session_); |
593 if (status) | |
594 NOTIFY_STATUS("VTDecompressionSessionFinishDelayedFrames()", status); | |
508 } | 595 } |
509 | 596 |
510 void VTVideoDecodeAccelerator::QueueAction(Action action) { | 597 void VTVideoDecodeAccelerator::QueueAction(Action action) { |
511 DCHECK(CalledOnValidThread()); | 598 DCHECK(CalledOnValidThread()); |
512 if (pending_bitstream_ids_.empty()) { | 599 if (pending_bitstream_ids_.empty()) { |
513 // If there are no pending frames, all actions complete immediately. | 600 // If there are no pending frames, all actions complete immediately. |
514 CompleteAction(action); | 601 CompleteAction(action); |
515 } else { | 602 } else { |
516 // Otherwise, queue the action. | 603 // Otherwise, queue the action. |
517 pending_actions_.push(PendingAction(action, pending_bitstream_ids_.back())); | 604 pending_actions_.push(PendingAction(action, pending_bitstream_ids_.back())); |
518 | 605 |
519 // Request a flush to make sure the action will eventually complete. | 606 // Request a flush to make sure the action will eventually complete. |
520 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( | 607 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
521 &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this))); | 608 &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this))); |
522 | 609 |
523 // See if we can make progress now that there is a new pending action. | 610 // See if we can make progress now that there is a new pending action. |
524 ProcessDecodedFrames(); | 611 ProcessDecodedFrames(); |
525 } | 612 } |
526 } | 613 } |
527 | 614 |
615 void VTVideoDecodeAccelerator::NotifyError(Error error) { | |
616 if (!CalledOnValidThread()) { | |
617 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
618 &VTVideoDecodeAccelerator::NotifyError, | |
619 weak_this_factory_.GetWeakPtr(), | |
620 error)); | |
621 return; | |
622 } | |
623 has_error_ = true; | |
624 client_->NotifyError(error); | |
625 } | |
626 | |
627 void VTVideoDecodeAccelerator::DropBitstream(int32_t bitstream_id) { | |
628 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); | |
629 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( | |
630 &VTVideoDecodeAccelerator::OutputTask, | |
631 weak_this_factory_.GetWeakPtr(), | |
632 DecodedFrame(bitstream_id, NULL))); | |
633 } | |
634 | |
528 void VTVideoDecodeAccelerator::Flush() { | 635 void VTVideoDecodeAccelerator::Flush() { |
529 DCHECK(CalledOnValidThread()); | 636 DCHECK(CalledOnValidThread()); |
530 QueueAction(ACTION_FLUSH); | 637 QueueAction(ACTION_FLUSH); |
531 } | 638 } |
532 | 639 |
533 void VTVideoDecodeAccelerator::Reset() { | 640 void VTVideoDecodeAccelerator::Reset() { |
534 DCHECK(CalledOnValidThread()); | 641 DCHECK(CalledOnValidThread()); |
535 QueueAction(ACTION_RESET); | 642 QueueAction(ACTION_RESET); |
536 } | 643 } |
537 | 644 |
538 void VTVideoDecodeAccelerator::Destroy() { | 645 void VTVideoDecodeAccelerator::Destroy() { |
539 DCHECK(CalledOnValidThread()); | 646 DCHECK(CalledOnValidThread()); |
540 // Drop any other pending actions. | 647 // Drop any other pending actions. |
541 while (!pending_actions_.empty()) | 648 while (!pending_actions_.empty()) |
542 pending_actions_.pop(); | 649 pending_actions_.pop(); |
543 // Return all bitstream buffers. | 650 // Return all bitstream buffers. |
544 while (!pending_bitstream_ids_.empty()) { | 651 while (!pending_bitstream_ids_.empty()) { |
545 client_->NotifyEndOfBitstreamBuffer(pending_bitstream_ids_.front()); | 652 client_->NotifyEndOfBitstreamBuffer(pending_bitstream_ids_.front()); |
546 pending_bitstream_ids_.pop(); | 653 pending_bitstream_ids_.pop(); |
547 } | 654 } |
548 QueueAction(ACTION_DESTROY); | 655 QueueAction(ACTION_DESTROY); |
549 } | 656 } |
550 | 657 |
551 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { | 658 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |
552 return false; | 659 return false; |
553 } | 660 } |
554 | 661 |
555 } // namespace content | 662 } // namespace content |
OLD | NEW |