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

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

Issue 491163002: Implement flushing in VTVideoDecodeAccelerator. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 4 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 7
8 #include "base/bind.h" 8 #include "base/bind.h"
9 #include "base/command_line.h" 9 #include "base/command_line.h"
10 #include "base/sys_byteorder.h" 10 #include "base/sys_byteorder.h"
(...skipping 21 matching lines...) Expand all
32 32
33 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator. 33 // Route decoded frame callbacks back into the VTVideoDecodeAccelerator.
34 static void OutputThunk( 34 static void OutputThunk(
35 void* decompression_output_refcon, 35 void* decompression_output_refcon,
36 void* source_frame_refcon, 36 void* source_frame_refcon,
37 OSStatus status, 37 OSStatus status,
38 VTDecodeInfoFlags info_flags, 38 VTDecodeInfoFlags info_flags,
39 CVImageBufferRef image_buffer, 39 CVImageBufferRef image_buffer,
40 CMTime presentation_time_stamp, 40 CMTime presentation_time_stamp,
41 CMTime presentation_duration) { 41 CMTime presentation_duration) {
42 // TODO(sandersd): Implement flush-before-delete to guarantee validity.
43 VTVideoDecodeAccelerator* vda = 42 VTVideoDecodeAccelerator* vda =
44 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); 43 reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon);
45 int32_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); 44 int32_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon);
46 vda->Output(bitstream_id, status, image_buffer); 45 vda->Output(bitstream_id, status, image_buffer);
47 } 46 }
48 47
49 VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame( 48 VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame(
50 int32_t bitstream_id, 49 int32_t bitstream_id,
51 CVImageBufferRef image_buffer) 50 CVImageBufferRef image_buffer)
52 : bitstream_id(bitstream_id), 51 : bitstream_id(bitstream_id),
53 image_buffer(image_buffer) { 52 image_buffer(image_buffer) {
54 } 53 }
55 54
56 VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() { 55 VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() {
57 } 56 }
58 57
59 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) 58 VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context)
60 : cgl_context_(cgl_context), 59 : cgl_context_(cgl_context),
61 client_(NULL), 60 client_(NULL),
61 state_(NORMAL),
62 frames_pending_decode_(0),
62 format_(NULL), 63 format_(NULL),
63 session_(NULL), 64 session_(NULL),
64 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), 65 gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()),
65 weak_this_factory_(this), 66 weak_this_factory_(this),
66 decoder_thread_("VTDecoderThread") { 67 decoder_thread_("VTDecoderThread") {
67 callback_.decompressionOutputCallback = OutputThunk; 68 callback_.decompressionOutputCallback = OutputThunk;
68 callback_.decompressionOutputRefCon = this; 69 callback_.decompressionOutputRefCon = this;
69 } 70 }
70 71
71 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() { 72 VTVideoDecodeAccelerator::~VTVideoDecodeAccelerator() {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height)); 153 base::ScopedCFTypeRef<CFNumberRef> cf_height(CFINT(coded_dimensions.height));
153 #undef CFINT 154 #undef CFINT
154 CFDictionarySetValue( 155 CFDictionarySetValue(
155 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format); 156 image_config, kCVPixelBufferPixelFormatTypeKey, cf_pixel_format);
156 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width); 157 CFDictionarySetValue(image_config, kCVPixelBufferWidthKey, cf_width);
157 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height); 158 CFDictionarySetValue(image_config, kCVPixelBufferHeightKey, cf_height);
158 CFDictionarySetValue( 159 CFDictionarySetValue(
159 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); 160 image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue);
160 161
161 // TODO(sandersd): Check if the session is already compatible. 162 // TODO(sandersd): Check if the session is already compatible.
162 // TODO(sandersd): Flush.
163 session_.reset(); 163 session_.reset();
164 CHECK(!VTDecompressionSessionCreate( 164 CHECK(!VTDecompressionSessionCreate(
165 kCFAllocatorDefault, 165 kCFAllocatorDefault,
166 format_, // video_format_description 166 format_, // video_format_description
167 decoder_config, // video_decoder_specification 167 decoder_config, // video_decoder_specification
168 image_config, // destination_image_buffer_attributes 168 image_config, // destination_image_buffer_attributes
169 &callback_, // output_callback 169 &callback_, // output_callback
170 session_.InitializeInto())); 170 session_.InitializeInto()));
171 171
172 // If the size has changed, trigger a request for new picture buffers. 172 // If the size has changed, trigger a request for new picture buffers.
173 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height); 173 gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height);
174 if (coded_size_ != new_coded_size) { 174 if (coded_size_ != new_coded_size) {
175 coded_size_ = new_coded_size; 175 coded_size_ = new_coded_size;
176 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( 176 gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
177 &VTVideoDecodeAccelerator::SizeChangedTask, 177 &VTVideoDecodeAccelerator::SizeChangedTask,
178 weak_this_factory_.GetWeakPtr(), 178 weak_this_factory_.GetWeakPtr(),
179 coded_size_));; 179 coded_size_));;
180 } 180 }
181 } 181 }
182 182
183 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { 183 void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) {
184 DCHECK(CalledOnValidThread()); 184 DCHECK(CalledOnValidThread());
185 // TODO(sandersd): Test what happens if bitstream buffers are passed to VT out 185 frames_pending_decode_++;
Pawel Osciak 2014/08/27 08:25:20 Client will keep calling Decode()s after it calls
sandersd (OOO until July 31) 2014/08/27 21:40:43 video_decoder.h specifies that no decode calls are
Pawel Osciak 2014/08/28 12:09:12 This class implements VideoDecodeAccelerator, not
186 // of order.
187 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( 186 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
188 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), 187 &VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this),
189 bitstream)); 188 bitstream));
190 } 189 }
191 190
192 // TODO(sandersd): Proper error reporting instead of CHECKs. 191 // TODO(sandersd): Proper error reporting instead of CHECKs.
193 void VTVideoDecodeAccelerator::DecodeTask( 192 void VTVideoDecodeAccelerator::DecodeTask(
194 const media::BitstreamBuffer bitstream) { 193 const media::BitstreamBuffer bitstream) {
195 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); 194 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread());
196 195
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 CHECK_EQ(CFGetTypeID(image_buffer), CVPixelBufferGetTypeID()); 301 CHECK_EQ(CFGetTypeID(image_buffer), CVPixelBufferGetTypeID());
303 CFRetain(image_buffer); 302 CFRetain(image_buffer);
304 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( 303 gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
305 &VTVideoDecodeAccelerator::OutputTask, 304 &VTVideoDecodeAccelerator::OutputTask,
306 weak_this_factory_.GetWeakPtr(), 305 weak_this_factory_.GetWeakPtr(),
307 DecodedFrame(bitstream_id, image_buffer))); 306 DecodedFrame(bitstream_id, image_buffer)));
308 } 307 }
309 308
310 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { 309 void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) {
311 DCHECK(CalledOnValidThread()); 310 DCHECK(CalledOnValidThread());
312 decoded_frames_.push(frame); 311 if (state_ == RESETTING || state_ == DESTROYING) {
313 SendPictures(); 312 // Drop all decoded frames when resetting or destroying.
313 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id);
314 frames_pending_decode_--;
315 if (!frames_pending_decode_)
316 FlushDone();
317 } else {
318 decoded_frames_.push(frame);
319 SendPictures();
320 }
314 } 321 }
315 322
316 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { 323 void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) {
317 DCHECK(CalledOnValidThread()); 324 DCHECK(CalledOnValidThread());
318 texture_size_ = coded_size; 325 texture_size_ = coded_size;
319 // TODO(sandersd): Dismiss existing picture buffers. 326 // TODO(sandersd): Dismiss existing picture buffers.
320 client_->ProvidePictureBuffers( 327 client_->ProvidePictureBuffers(
321 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB); 328 kNumPictureBuffers, texture_size_, GL_TEXTURE_RECTANGLE_ARB);
322 } 329 }
323 330
324 void VTVideoDecodeAccelerator::AssignPictureBuffers( 331 void VTVideoDecodeAccelerator::AssignPictureBuffers(
325 const std::vector<media::PictureBuffer>& pictures) { 332 const std::vector<media::PictureBuffer>& pictures) {
326 DCHECK(CalledOnValidThread()); 333 DCHECK(CalledOnValidThread());
327 334
328 for (size_t i = 0; i < pictures.size(); i++) { 335 for (size_t i = 0; i < pictures.size(); i++) {
329 CHECK(!texture_ids_.count(pictures[i].id())); 336 CHECK(!texture_ids_.count(pictures[i].id()));
330 available_picture_ids_.push(pictures[i].id()); 337 available_picture_ids_.push(pictures[i].id());
331 texture_ids_[pictures[i].id()] = pictures[i].texture_id(); 338 texture_ids_[pictures[i].id()] = pictures[i].texture_id();
332 } 339 }
333 340
334 // Pictures are not marked as uncleared until this method returns. They will 341 // Pictures are not marked as uncleared until this method returns. They will
335 // become broken if they are used before that happens. 342 // become broken if they are used before that happens. So, instead of calling
343 // SendPictures(), arrange for it to be called later.
336 gpu_task_runner_->PostTask(FROM_HERE, base::Bind( 344 gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
337 &VTVideoDecodeAccelerator::SendPictures, 345 &VTVideoDecodeAccelerator::SendPictures,
338 weak_this_factory_.GetWeakPtr())); 346 weak_this_factory_.GetWeakPtr()));
339 } 347 }
340 348
341 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) { 349 void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) {
342 DCHECK(CalledOnValidThread()); 350 DCHECK(CalledOnValidThread());
343 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1); 351 DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1);
344 picture_bindings_.erase(picture_id); 352 picture_bindings_.erase(picture_id);
345 available_picture_ids_.push(picture_id); 353 available_picture_ids_.push(picture_id);
346 SendPictures(); 354 SendPictures();
347 } 355 }
348 356
349 // TODO(sandersd): Proper error reporting instead of CHECKs. 357 // TODO(sandersd): Proper error reporting instead of CHECKs.
350 void VTVideoDecodeAccelerator::SendPictures() { 358 void VTVideoDecodeAccelerator::SendPictures() {
351 DCHECK(CalledOnValidThread()); 359 DCHECK(CalledOnValidThread());
352 if (available_picture_ids_.empty() || decoded_frames_.empty()) 360 if (available_picture_ids_.empty() || decoded_frames_.empty())
353 return; 361 return;
354 362
355 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_); 363 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_);
356 glEnable(GL_TEXTURE_RECTANGLE_ARB); 364 glEnable(GL_TEXTURE_RECTANGLE_ARB);
357 365
358 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) { 366 while (!available_picture_ids_.empty() && !decoded_frames_.empty()) {
359 int32_t picture_id = available_picture_ids_.front(); 367 int32_t picture_id = available_picture_ids_.front();
360 available_picture_ids_.pop(); 368 available_picture_ids_.pop();
361 DecodedFrame frame = decoded_frames_.front(); 369 DecodedFrame frame = decoded_frames_.front();
362 decoded_frames_.pop(); 370 decoded_frames_.pop();
363 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); 371 IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer);
364 372
373 // TODO(sandersd): Find out why this fails due to no GL context sometimes.
365 gfx::ScopedTextureBinder 374 gfx::ScopedTextureBinder
366 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); 375 texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]);
367 CHECK(!CGLTexImageIOSurface2D( 376 CHECK(!CGLTexImageIOSurface2D(
368 cgl_context_, // ctx 377 cgl_context_, // ctx
369 GL_TEXTURE_RECTANGLE_ARB, // target 378 GL_TEXTURE_RECTANGLE_ARB, // target
370 GL_RGB, // internal_format 379 GL_RGB, // internal_format
371 texture_size_.width(), // width 380 texture_size_.width(), // width
372 texture_size_.height(), // height 381 texture_size_.height(), // height
373 GL_YCBCR_422_APPLE, // format 382 GL_YCBCR_422_APPLE, // format
374 GL_UNSIGNED_SHORT_8_8_APPLE, // type 383 GL_UNSIGNED_SHORT_8_8_APPLE, // type
375 surface, // io_surface 384 surface, // io_surface
376 0)); // plane 385 0)); // plane
377 386
378 picture_bindings_[picture_id] = frame.image_buffer; 387 picture_bindings_[picture_id] = frame.image_buffer;
379 client_->PictureReady(media::Picture(picture_id, frame.bitstream_id)); 388 client_->PictureReady(media::Picture(picture_id, frame.bitstream_id));
380 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); 389 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id);
390 frames_pending_decode_--;
381 } 391 }
382 392
383 glDisable(GL_TEXTURE_RECTANGLE_ARB); 393 glDisable(GL_TEXTURE_RECTANGLE_ARB);
394
395 if (state_ != NORMAL && !frames_pending_decode_)
396 FlushDone();
397 }
398
399 void VTVideoDecodeAccelerator::FlushStart(State state) {
400 DCHECK(CalledOnValidThread());
401 state_ = state;
402
403 // If resetting or destroying, drop all pending frames.
404 if (state_ == RESETTING || state_ == DESTROYING) {
405 while (!decoded_frames_.empty()) {
406 DecodedFrame frame = decoded_frames_.front();
407 decoded_frames_.pop();
408 client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id);
409 frames_pending_decode_--;
410 }
411 }
412
413 if (frames_pending_decode_) {
414 decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
415 &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this)));
416 } else {
417 FlushDone();
418 }
419 }
420
421 void VTVideoDecodeAccelerator::FlushTask() {
422 DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread());
423 if (session_)
424 CHECK(!VTDecompressionSessionFinishDelayedFrames(session_));
425 }
426
427 void VTVideoDecodeAccelerator::FlushDone() {
428 DCHECK(CalledOnValidThread());
429 switch (state_) {
430 case NORMAL:
431 NOTREACHED();
432 break;
433 case FLUSHING:
434 client_->NotifyFlushDone();
435 state_ = NORMAL;
436 break;
437 case RESETTING:
438 client_->NotifyResetDone();
439 state_ = NORMAL;
440 break;
441 case DESTROYING:
442 delete this;
443 break;
444 }
384 } 445 }
385 446
386 void VTVideoDecodeAccelerator::Flush() { 447 void VTVideoDecodeAccelerator::Flush() {
387 DCHECK(CalledOnValidThread()); 448 DCHECK(CalledOnValidThread());
388 // TODO(sandersd): Trigger flush, sending frames. 449 FlushStart(FLUSHING);
389 } 450 }
390 451
391 void VTVideoDecodeAccelerator::Reset() { 452 void VTVideoDecodeAccelerator::Reset() {
392 DCHECK(CalledOnValidThread()); 453 DCHECK(CalledOnValidThread());
393 // TODO(sandersd): Trigger flush, discarding frames. 454 FlushStart(RESETTING);
394 } 455 }
395 456
396 void VTVideoDecodeAccelerator::Destroy() { 457 void VTVideoDecodeAccelerator::Destroy() {
397 DCHECK(CalledOnValidThread()); 458 DCHECK(CalledOnValidThread());
398 // TODO(sandersd): Trigger flush, discarding frames, and wait for them. 459 FlushStart(DESTROYING);
399 delete this;
400 } 460 }
401 461
402 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { 462 bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() {
403 return false; 463 return false;
404 } 464 }
405 465
406 } // namespace content 466 } // 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