Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(145)

Side by Side Diff: content/common/gpu/media/vt_video_decode_accelerator.cc

Issue 642453003: Use NotifyError() to report errors in VTVideoDecodeAccelerator. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Don't call DropFrame() from an arbitrary thread. Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/common/gpu/media/vt_video_decode_accelerator.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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(), // &parameter_set_pointers 137 &nalu_data_ptrs.front(), // &parameter_set_pointers
130 &nalu_data_sizes.front(), // &parameter_set_sizes 138 &nalu_data_sizes.front(), // &parameter_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
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
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
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
OLDNEW
« no previous file with comments | « content/common/gpu/media/vt_video_decode_accelerator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698