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

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: Grammar. 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/command_line.h" 10 #include "base/command_line.h"
11 #include "base/sys_byteorder.h" 11 #include "base/sys_byteorder.h"
12 #include "base/thread_task_runner_handle.h" 12 #include "base/thread_task_runner_handle.h"
13 #include "content/common/gpu/media/vt_video_decode_accelerator.h" 13 #include "content/common/gpu/media/vt_video_decode_accelerator.h"
14 #include "content/public/common/content_switches.h" 14 #include "content/public/common/content_switches.h"
15 #include "media/filters/h264_parser.h" 15 #include "media/filters/h264_parser.h"
16 #include "ui/gl/scoped_binders.h" 16 #include "ui/gl/scoped_binders.h"
17 #include "ui/gl/scoped_cgl.h" 17 #include "ui/gl/scoped_cgl.h"
18 18
19 using content_common_gpu_media::kModuleVt; 19 using content_common_gpu_media::kModuleVt;
20 using content_common_gpu_media::InitializeStubs; 20 using content_common_gpu_media::InitializeStubs;
21 using content_common_gpu_media::IsVtInitialized; 21 using content_common_gpu_media::IsVtInitialized;
22 using content_common_gpu_media::StubPathMap; 22 using content_common_gpu_media::StubPathMap;
23 23
24 #define NOTIFY_STATUS(name, status) \
Pawel Osciak 2014/10/22 10:45:21 I'd personally prefer s/STATUS/ERROR/, but up to y
sandersd (OOO until July 31) 2014/10/22 22:15:45 While this is a wrapper for NotifyError(), it is s
25 do { \
26 LOG(ERROR) << name << " failed with status " << status; \
27 NotifyError(PLATFORM_FAILURE); \
28 } while (0)
29
24 namespace content { 30 namespace content {
25 31
26 // Size of NALU length headers in AVCC/MPEG-4 format (can be 1, 2, or 4). 32 // Size of NALU length headers in AVCC/MPEG-4 format (can be 1, 2, or 4).
27 static const int kNALUHeaderLength = 4; 33 static const int kNALUHeaderLength = 4;
28 34
29 // We only request 5 picture buffers from the client which are used to hold the 35 // 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 36 // decoded samples. These buffers are then reused when the client tells us that
31 // it is done with the buffer. 37 // it is done with the buffer.
32 static const int kNumPictureBuffers = 5; 38 static const int kNumPictureBuffers = 5;
33 39
(...skipping 28 matching lines...) Expand all
62 : action(action), 68 : action(action),
63 bitstream_id(bitstream_id) { 69 bitstream_id(bitstream_id) {
64 } 70 }
65 71
66 VTVideoDecodeAccelerator::PendingAction::~PendingAction() { 72 VTVideoDecodeAccelerator::PendingAction::~PendingAction() {
67 } 73 }
68 74
69 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) 75 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context)
70 : cgl_context_(cgl_context), 76 : cgl_context_(cgl_context),
71 client_(NULL), 77 client_(NULL),
78 has_error_(false),
72 format_(NULL), 79 format_(NULL),
73 session_(NULL), 80 session_(NULL),
74 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), 81 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()),
75 weak_this_factory_(this), 82 weak_this_factory_(this),
76 decoder_thread_("VTDecoderThread") { 83 decoder_thread_("VTDecoderThread") {
77 callback_.decompressionOutputCallback = OutputThunk; 84 callback_.decompressionOutputCallback = OutputThunk;
78 callback_.decompressionOutputRefCon = this; 85 callback_.decompressionOutputRefCon = this;
79 } 86 }
80 87
81 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { 88 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() {
(...skipping 26 matching lines...) Expand all
108 return false; 115 return false;
109 } 116 }
110 117
111 // Spawn a thread to handle parsing and calling VideoToolbox. 118 // Spawn a thread to handle parsing and calling VideoToolbox.
112 if (!decoder_thread_.Start()) 119 if (!decoder_thread_.Start())
113 return false; 120 return false;
114 121
115 return true; 122 return true;
116 } 123 }
117 124
118 // TODO(sandersd): Proper error reporting instead of CHECKs. 125 bool VTVideoDecodeAccelerator::ConfigureDecoder(
119 void VTVideoDecodeAccelerator::ConfigureDecoder(
120 const std::vector<const uint8_t*>& nalu_data_ptrs, 126 const std::vector<const uint8_t*>& nalu_data_ptrs,
121 const std::vector<size_t>& nalu_data_sizes) { 127 const std::vector<size_t>& nalu_data_sizes) {
122 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); 128 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread());
129
123 // Construct a new format description from the parameter sets. 130 // Construct a new format description from the parameter sets.
124 // TODO(sandersd): Replace this with custom code to support OS X < 10.9. 131 // TODO(sandersd): Replace this with custom code to support OS X < 10.9.
125 format_.reset(); 132 format_.reset();
126 CHECK(!CMVideoFormatDescriptionCreateFromH264ParameterSets( 133 OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(
127 kCFAllocatorDefault, 134 kCFAllocatorDefault,
128 nalu_data_ptrs.size(), // parameter_set_count 135 nalu_data_ptrs.size(), // parameter_set_count
129 &nalu_data_ptrs.front(), // &parameter_set_pointers 136 &nalu_data_ptrs.front(), // &parameter_set_pointers
130 &nalu_data_sizes.front(), // &parameter_set_sizes 137 &nalu_data_sizes.front(), // &parameter_set_sizes
131 kNALUHeaderLength, // nal_unit_header_length 138 kNALUHeaderLength, // nal_unit_header_length
132 format_.InitializeInto())); 139 format_.InitializeInto());
140 if (status) {
141 NOTIFY_STATUS("CMVideoFormatDescriptionCreateFromH264ParameterSets()",
142 status);
143 return false;
144 }
133 CMVideoDimensions coded_dimensions = 145 CMVideoDimensions coded_dimensions =
134 CMVideoFormatDescriptionGetDimensions(format_); 146 CMVideoFormatDescriptionGetDimensions(format_);
135 147
136 // Prepare VideoToolbox configuration dictionaries. 148 // Prepare VideoToolbox configuration dictionaries.
137 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config( 149 base::ScopedCFTypeRef<CFMutableDictionaryRef> decoder_config(
138 CFDictionaryCreateMutable( 150 CFDictionaryCreateMutable(
139 kCFAllocatorDefault, 151 kCFAllocatorDefault,
140 1, // capacity 152 1, // capacity
141 &kCFTypeDictionaryKeyCallBacks, 153 &kCFTypeDictionaryKeyCallBacks,
142 &kCFTypeDictionaryValueCallBacks)); 154 &kCFTypeDictionaryValueCallBacks));
(...skipping 20 matching lines...) Expand all
163 #undef CFINT 175 #undef CFINT
164 CFDictionarySetValue( 176 CFDictionarySetValue(
165 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); 177 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format);
166 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); 178 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width);
167 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); 179 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height);
168 CFDictionarySetValue( 180 CFDictionarySetValue(
169 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); 181 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);
170 182
171 // TODO(sandersd): Check if the session is already compatible. 183 // TODO(sandersd): Check if the session is already compatible.
172 session_.reset(); 184 session_.reset();
173 CHECK(!VTDecompressionSessionCreate( 185 status = VTDecompressionSessionCreate(
174 kCFAllocatorDefault, 186 kCFAllocatorDefault,
175 format_, // video_format_description 187 format_, // video_format_description
176 decoder_config, // video_decoder_specification 188 decoder_config, // video_decoder_specification
177 image_config, // destination_image_buffer_attributes 189 image_config, // destination_image_buffer_attributes
178 &callback_, // output_callback 190 &callback_, // output_callback
179 session_.InitializeInto())); 191 session_.InitializeInto());
192 if (status) {
193 NOTIFY_STATUS("VTDecompressionSessionCreate()", status);
194 return false;
195 }
180 196
181 // If the size has changed, trigger a request for new picture buffers. 197 // 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 198 // TODO(sandersd): Move to SendPictures(), and use this just as a hint for an
183 // upcoming size change. 199 // upcoming size change.
184 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); 200 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height);
185 if (coded_size_ != new_coded_size) { 201 if (coded_size_ != new_coded_size) {
186 coded_size_ = new_coded_size; 202 coded_size_ = new_coded_size;
187 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( 203 gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
188 &VTVideoDecodeAccelerator::SizeChangedTask, 204 &VTVideoDecodeAccelerator::SizeChangedTask,
189 weak_this_factory_.GetWeakPtr(), 205 weak_this_factory_.GetWeakPtr(),
190 coded_size_));; 206 coded_size_));;
191 } 207 }
208
209 return true;
192 } 210 }
193 211
194 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { 212 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) {
195 DCHECK(CalledOnValidThread()); 213 DCHECK(CalledOnValidThread());
196 CHECK_GE(bitstream.id(), 0) << "Negative bitstream_id"; 214 // Not actually a requirement of the VDA API, but we're lazy and use negative
215 // values as flags internally. Revisit that if this actually happens.
216 if (bitstream.id() < 0) {
217 LOG(ERROR) << "Negative bitstream ID";
218 NotifyError(INVALID_ARGUMENT);
219 client_->NotifyEndOfBitstreamBuffer(bitstream.id());
220 return;
221 }
197 pending_bitstream_ids_.push(bitstream.id()); 222 pending_bitstream_ids_.push(bitstream.id());
198 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( 223 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
199 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), 224 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this),
200 bitstream)); 225 bitstream));
201 } 226 }
202 227
203 // TODO(sandersd): Proper error reporting instead of CHECKs.
204 void VTVideoDecodeAccelerator::DecodeTask( 228 void VTVideoDecodeAccelerator::DecodeTask(
205 const media::BitstreamBuffer bitstream) { 229 const media::BitstreamBuffer& bitstream) {
206 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); 230 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread());
207 231
208 // Map the bitstream buffer. 232 // Map the bitstream buffer.
209 base::SharedMemory memory(bitstream.handle(), true); 233 base::SharedMemory memory(bitstream.handle(), true);
210 size_t size = bitstream.size(); 234 size_t size = bitstream.size();
211 CHECK(memory.Map(size)); 235 if (!memory.Map(size)) {
236 LOG(ERROR) << "Failed to map bitstream buffer";
237 NotifyError(PLATFORM_FAILURE);
238 DropFrame(bitstream.id());
239 return;
240 }
212 const uint8_t* buf = static_cast<uint8_t*>(memory.memory()); 241 const uint8_t* buf = static_cast<uint8_t*>(memory.memory());
213 242
214 // NALUs are stored with Annex B format in the bitstream buffer (start codes), 243 // 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 244 // but VideoToolbox expects AVCC/MPEG-4 format (length headers), so we must
216 // rewrite the data. 245 // rewrite the data.
217 // 246 //
218 // 1. Locate relevant NALUs and compute the size of the translated data. 247 // 1. Locate relevant NALUs and compute the size of the translated data.
219 // Also record any parameter sets for VideoToolbox initialization. 248 // Also record any parameter sets for VideoToolbox initialization.
220 size_t data_size = 0; 249 size_t data_size = 0;
221 std::vector<media::H264NALU> nalus; 250 std::vector<media::H264NALU> nalus;
222 std::vector<const uint8_t*> config_nalu_data_ptrs; 251 std::vector<const uint8_t*> config_nalu_data_ptrs;
223 std::vector<size_t> config_nalu_data_sizes; 252 std::vector<size_t> config_nalu_data_sizes;
224 parser_.SetStream(buf, size); 253 parser_.SetStream(buf, size);
225 media::H264NALU nalu; 254 media::H264NALU nalu;
226 while (true) { 255 while (true) {
227 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu); 256 media::H264Parser::Result result = parser_.AdvanceToNextNALU(&nalu);
228 if (result == media::H264Parser::kEOStream) 257 if (result == media::H264Parser::kEOStream)
229 break; 258 break;
230 CHECK_EQ(result, media::H264Parser::kOk); 259 if (result != media::H264Parser::kOk) {
260 LOG(ERROR) << "Failed to find H.264 NALU";
261 NotifyError(PLATFORM_FAILURE);
262 DropFrame(bitstream.id());
263 return;
264 }
231 // TODO(sandersd): Check that these are only at the start. 265 // TODO(sandersd): Check that these are only at the start.
232 if (nalu.nal_unit_type == media::H264NALU::kSPS || 266 if (nalu.nal_unit_type == media::H264NALU::kSPS ||
233 nalu.nal_unit_type == media::H264NALU::kPPS || 267 nalu.nal_unit_type == media::H264NALU::kPPS ||
234 nalu.nal_unit_type == media::H264NALU::kSPSExt) { 268 nalu.nal_unit_type == media::H264NALU::kSPSExt) {
235 DVLOG(2) << "Parameter set " << nalu.nal_unit_type; 269 DVLOG(2) << "Parameter set " << nalu.nal_unit_type;
236 config_nalu_data_ptrs.push_back(nalu.data); 270 config_nalu_data_ptrs.push_back(nalu.data);
237 config_nalu_data_sizes.push_back(nalu.size); 271 config_nalu_data_sizes.push_back(nalu.size);
238 } else { 272 } else {
239 nalus.push_back(nalu); 273 nalus.push_back(nalu);
240 data_size += kNALUHeaderLength + nalu.size; 274 data_size += kNALUHeaderLength + nalu.size;
241 } 275 }
242 } 276 }
243 277
244 // 2. Initialize VideoToolbox. 278 // 2. Initialize VideoToolbox.
245 // TODO(sandersd): Reinitialize when there are new parameter sets. 279 // TODO(sandersd): Reinitialize when there are new parameter sets.
246 if (!session_) 280 if (!session_) {
247 ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes); 281 if (!ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes)) {
282 // ConfigureDecoder() already called NotifyError().
283 DropFrame(bitstream.id());
284 return;
285 }
286 }
248 287
249 // If there are no non-configuration units, immediately return an empty 288 // 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 289 // (ie. dropped) frame. It is an error to create a MemoryBlock with zero
251 // size. 290 // size.
252 if (!data_size) { 291 if (!data_size) {
253 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( 292 DropFrame(bitstream.id());
254 &VTVideoDecodeAccelerator::OutputTask,
255 weak_this_factory_.GetWeakPtr(),
256 DecodedFrame(bitstream.id(), NULL)));
257 return; 293 return;
258 } 294 }
259 295
260 // 3. Allocate a memory-backed CMBlockBuffer for the translated data. 296 // 3. Allocate a memory-backed CMBlockBuffer for the translated data.
261 base::ScopedCFTypeRef<CMBlockBufferRef> data; 297 base::ScopedCFTypeRef<CMBlockBufferRef> data;
262 CHECK(!CMBlockBufferCreateWithMemoryBlock( 298 OSStatus status = CMBlockBufferCreateWithMemoryBlock(
263 kCFAllocatorDefault, 299 kCFAllocatorDefault,
264 NULL, // &memory_block 300 NULL, // &memory_block
265 data_size, // block_length 301 data_size, // block_length
266 kCFAllocatorDefault, // block_allocator 302 kCFAllocatorDefault, // block_allocator
267 NULL, // &custom_block_source 303 NULL, // &custom_block_source
268 0, // offset_to_data 304 0, // offset_to_data
269 data_size, // data_length 305 data_size, // data_length
270 0, // flags 306 0, // flags
271 data.InitializeInto())); 307 data.InitializeInto());
308 if (status) {
309 NOTIFY_STATUS("CMBlockBufferCreateWithMemoryBlock()", status);
310 DropFrame(bitstream.id());
311 return;
312 }
272 313
273 // 4. Copy NALU data, inserting length headers. 314 // 4. Copy NALU data, inserting length headers.
274 size_t offset = 0; 315 size_t offset = 0;
275 for (size_t i = 0; i < nalus.size(); i++) { 316 for (size_t i = 0; i < nalus.size(); i++) {
276 media::H264NALU& nalu = nalus[i]; 317 media::H264NALU& nalu = nalus[i];
277 uint32_t header = base::HostToNet32(static_cast<uint32_t>(nalu.size)); 318 uint32_t header = base::HostToNet32(static_cast<uint32_t>(nalu.size));
278 CHECK(!CMBlockBufferReplaceDataBytes( 319 status = CMBlockBufferReplaceDataBytes(
279 &header, data, offset, kNALUHeaderLength)); 320 &header, data, offset, kNALUHeaderLength);
321 if (status) {
322 NOTIFY_STATUS("CMBlockBufferReplaceDataBytes()", status);
323 DropFrame(bitstream.id());
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 DropFrame(bitstream.id());
331 return;
332 }
282 offset += nalu.size; 333 offset += nalu.size;
283 } 334 }
284 335
285 // 5. Package the data for VideoToolbox and request decoding. 336 // 5. Package the data for VideoToolbox and request decoding.
286 base::ScopedCFTypeRef<CMSampleBufferRef> frame; 337 base::ScopedCFTypeRef<CMSampleBufferRef> frame;
287 CHECK(!CMSampleBufferCreate( 338 status = CMSampleBufferCreate(
288 kCFAllocatorDefault, 339 kCFAllocatorDefault,
289 data, // data_buffer 340 data, // data_buffer
290 true, // data_ready 341 true, // data_ready
291 NULL, // make_data_ready_callback 342 NULL, // make_data_ready_callback
292 NULL, // make_data_ready_refcon 343 NULL, // make_data_ready_refcon
293 format_, // format_description 344 format_, // format_description
294 1, // num_samples 345 1, // num_samples
295 0, // num_sample_timing_entries 346 0, // num_sample_timing_entries
296 NULL, // &sample_timing_array 347 NULL, // &sample_timing_array
297 0, // num_sample_size_entries 348 0, // num_sample_size_entries
298 NULL, // &sample_size_array 349 NULL, // &sample_size_array
299 frame.InitializeInto())); 350 frame.InitializeInto());
351 if (status) {
352 NOTIFY_STATUS("CMSampleBufferCreate()", status);
353 DropFrame(bitstream.id());
354 return;
355 }
300 356
301 // Asynchronous Decompression allows for parallel submission of frames 357 // Asynchronous Decompression allows for parallel submission of frames
302 // (without it, DecodeFrame() does not return until the frame has been 358 // (without it, DecodeFrame() does not return until the frame has been
303 // decoded). We don't enable Temporal Processing so that frames are always 359 // decoded). We don't enable Temporal Processing so that frames are always
304 // returned in decode order; this makes it easier to avoid deadlock. 360 // returned in decode order; this makes it easier to avoid deadlock.
305 VTDecodeFrameFlags decode_flags = 361 VTDecodeFrameFlags decode_flags =
306 kVTDecodeFrame_EnableAsynchronousDecompression; 362 kVTDecodeFrame_EnableAsynchronousDecompression;
307 363
308 intptr_t bitstream_id = bitstream.id(); 364 intptr_t bitstream_id = bitstream.id();
309 CHECK(!VTDecompressionSessionDecodeFrame( 365 status = VTDecompressionSessionDecodeFrame(
310 session_, 366 session_,
311 frame, // sample_buffer 367 frame, // sample_buffer
312 decode_flags, // decode_flags 368 decode_flags, // decode_flags
313 reinterpret_cast<void*>(bitstream_id), // source_frame_refcon 369 reinterpret_cast<void*>(bitstream_id), // source_frame_refcon
314 NULL)); // &info_flags_out 370 NULL); // &info_flags_out
371 if (status) {
372 NOTIFY_STATUS("VTDecompressionSessionDecodeFrame()", status);
373 DropFrame(bitstream.id());
374 return;
375 }
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 // TODO(sandersd): Handle dropped frames.
324 CHECK_EQ(CFGetTypeID(image_buffer), CVPixelBufferGetTypeID()); 384 if (status) {
385 NOTIFY_STATUS("Decoding", status);
386 DropFrame(bitstream_id);
387 return;
388 }
389 if (CFGetTypeID(image_buffer) != CVPixelBufferGetTypeID()) {
390 LOG(ERROR) << "Decoded frame is not a CVPixelBuffer";
391 NotifyError(PLATFORM_FAILURE);
392 DropFrame(bitstream_id);
393 return;
394 }
325 CFRetain(image_buffer); 395 CFRetain(image_buffer);
326 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( 396 gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
327 &VTVideoDecodeAccelerator::OutputTask, 397 &VTVideoDecodeAccelerator::OutputTask,
328 weak_this_factory_.GetWeakPtr(), 398 weak_this_factory_.GetWeakPtr(),
329 DecodedFrame(bitstream_id, image_buffer))); 399 DecodedFrame(bitstream_id, image_buffer)));
330 } 400 }
331 401
332 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { 402 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) {
333 DCHECK(CalledOnValidThread()); 403 DCHECK(CalledOnValidThread());
334 decoded_frames_.push(frame); 404 decoded_frames_.push(frame);
335 ProcessDecodedFrames(); 405 ProcessDecodedFrames();
336 } 406 }
337 407
338 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { 408 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) {
339 DCHECK(CalledOnValidThread()); 409 DCHECK(CalledOnValidThread());
340 texture_size_ = coded_size; 410 texture_size_ = coded_size;
341 // TODO(sandersd): Dismiss existing picture buffers. 411 // TODO(sandersd): Dismiss existing picture buffers.
342 client_->ProvidePictureBuffers( 412 client_->ProvidePictureBuffers(
343 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); 413 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB);
344 } 414 }
345 415
346 void VTVideoDecodeAccelerator::AssignPictureBuffers( 416 void VTVideoDecodeAccelerator::AssignPictureBuffers(
347 const std::vector<media::PictureBuffer>& pictures) { 417 const std::vector<media::PictureBuffer>& pictures) {
348 DCHECK(CalledOnValidThread()); 418 DCHECK(CalledOnValidThread());
349 419
350 for (size_t i = 0; i < pictures.size(); i++) { 420 for (size_t i = 0; i < pictures.size(); i++) {
351 CHECK(!texture_ids_.count(pictures[i].id())); 421 DCHECK(!texture_ids_.count(pictures[i].id()));
352 available_picture_ids_.push(pictures[i].id()); 422 available_picture_ids_.push(pictures[i].id());
353 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); 423 texture_ids_[pictures[i].id()] = pictures[i].texture_id();
354 } 424 }
355 425
356 // Pictures are not marked as uncleared until after this method returns, and 426 // 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 427 // they will be broken if they are used before that happens. So, schedule
358 // future work after that happens. 428 // future work after that happens.
359 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( 429 gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
360 &VTVideoDecodeAccelerator::ProcessDecodedFrames, 430 &VTVideoDecodeAccelerator::ProcessDecodedFrames,
361 weak_this_factory_.GetWeakPtr())); 431 weak_this_factory_.GetWeakPtr()));
(...skipping 30 matching lines...) Expand all
392 pending_actions_.pop(); 462 pending_actions_.pop();
393 } 463 }
394 } 464 }
395 465
396 void VTVideoDecodeAccelerator::ProcessDecodedFrames() { 466 void VTVideoDecodeAccelerator::ProcessDecodedFrames() {
397 DCHECK(CalledOnValidThread()); 467 DCHECK(CalledOnValidThread());
398 468
399 while (!decoded_frames_.empty()) { 469 while (!decoded_frames_.empty()) {
400 if (pending_actions_.empty()) { 470 if (pending_actions_.empty()) {
401 // No pending actions; send frames normally. 471 // No pending actions; send frames normally.
402 SendPictures(pending_bitstream_ids_.back()); 472 if (!has_error_)
473 SendPictures(pending_bitstream_ids_.back());
403 return; 474 return;
404 } 475 }
405 476
406 int32_t next_action_bitstream_id = pending_actions_.front().bitstream_id; 477 int32_t next_action_bitstream_id = pending_actions_.front().bitstream_id;
407 int32_t last_sent_bitstream_id = -1; 478 int32_t last_sent_bitstream_id = -1;
408 switch (pending_actions_.front().action) { 479 switch (pending_actions_.front().action) {
409 case ACTION_FLUSH: 480 case ACTION_FLUSH:
410 // Send frames normally. 481 // Send frames normally.
482 if (has_error_)
483 return;
411 last_sent_bitstream_id = SendPictures(next_action_bitstream_id); 484 last_sent_bitstream_id = SendPictures(next_action_bitstream_id);
412 break; 485 break;
413 486
414 case ACTION_RESET: 487 case ACTION_RESET:
Pawel Osciak 2014/10/22 10:45:21 Do we still want to handle this here if has_error_
sandersd (OOO until July 31) 2014/10/22 22:15:45 I couldn't tell what the correct behavior was here
415 // Drop decoded frames. 488 // Drop decoded frames.
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;
(...skipping 22 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::DropFrame(int32_t bitstream_id) {
Pawel Osciak 2014/10/22 10:45:21 There's a lot of DropFrame() around and it makes m
sandersd (OOO until July 31) 2014/10/22 22:15:45 I don't really see an alternative; either we succe
628 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread());
629 gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
Pawel Osciak 2014/10/22 10:45:21 It's not exactly clear to me why we need to dutifu
sandersd (OOO until July 31) 2014/10/22 22:15:45 There is a comment now where the ScopedClosureRunn
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