Index: content/common/gpu/media/vt_video_decode_accelerator.cc |
diff --git a/content/common/gpu/media/vt_video_decode_accelerator.cc b/content/common/gpu/media/vt_video_decode_accelerator.cc |
index 5b916501b1b3fdbb532014842e48422584976874..3da6562fad7efed25c7d7964eeb673dfa8ebaa0e 100644 |
--- a/content/common/gpu/media/vt_video_decode_accelerator.cc |
+++ b/content/common/gpu/media/vt_video_decode_accelerator.cc |
@@ -39,7 +39,6 @@ static void OutputThunk( |
CVImageBufferRef image_buffer, |
CMTime presentation_time_stamp, |
CMTime presentation_duration) { |
- // TODO(sandersd): Implement flush-before-delete to guarantee validity. |
VTVideoDecodeAccelerator* vda = |
reinterpret_cast<VTVideoDecodeAccelerator*>(decompression_output_refcon); |
int32_t bitstream_id = reinterpret_cast<intptr_t>(source_frame_refcon); |
@@ -59,6 +58,8 @@ VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() { |
VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context) |
: cgl_context_(cgl_context), |
client_(NULL), |
+ state_(NORMAL), |
+ frames_pending_decode_(0), |
format_(NULL), |
session_(NULL), |
gpu_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
@@ -159,7 +160,6 @@ void VTVideoDecodeAccelerator::ConfigureDecoder( |
image_config, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); |
// TODO(sandersd): Check if the session is already compatible. |
- // TODO(sandersd): Flush. |
session_.reset(); |
CHECK(!VTDecompressionSessionCreate( |
kCFAllocatorDefault, |
@@ -182,8 +182,7 @@ void VTVideoDecodeAccelerator::ConfigureDecoder( |
void VTVideoDecodeAccelerator::Decode(const media::BitstreamBuffer& bitstream) { |
DCHECK(CalledOnValidThread()); |
- // TODO(sandersd): Test what happens if bitstream buffers are passed to VT out |
- // of order. |
+ 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
|
decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
&VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this), |
bitstream)); |
@@ -309,8 +308,16 @@ void VTVideoDecodeAccelerator::Output( |
void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) { |
DCHECK(CalledOnValidThread()); |
- decoded_frames_.push(frame); |
- SendPictures(); |
+ if (state_ == RESETTING || state_ == DESTROYING) { |
+ // Drop all decoded frames when resetting or destroying. |
+ client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
+ frames_pending_decode_--; |
+ if (!frames_pending_decode_) |
+ FlushDone(); |
+ } else { |
+ decoded_frames_.push(frame); |
+ SendPictures(); |
+ } |
} |
void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) { |
@@ -332,7 +339,8 @@ void VTVideoDecodeAccelerator::AssignPictureBuffers( |
} |
// Pictures are not marked as uncleared until this method returns. They will |
- // become broken if they are used before that happens. |
+ // become broken if they are used before that happens. So, instead of calling |
+ // SendPictures(), arrange for it to be called later. |
gpu_task_runner_->PostTask(FROM_HERE, base::Bind( |
&VTVideoDecodeAccelerator::SendPictures, |
weak_this_factory_.GetWeakPtr())); |
@@ -362,6 +370,7 @@ void VTVideoDecodeAccelerator::SendPictures() { |
decoded_frames_.pop(); |
IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer); |
+ // TODO(sandersd): Find out why this fails due to no GL context sometimes. |
gfx::ScopedTextureBinder |
texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]); |
CHECK(!CGLTexImageIOSurface2D( |
@@ -378,25 +387,76 @@ void VTVideoDecodeAccelerator::SendPictures() { |
picture_bindings_[picture_id] = frame.image_buffer; |
client_->PictureReady(media::Picture(picture_id, frame.bitstream_id)); |
client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
+ frames_pending_decode_--; |
} |
glDisable(GL_TEXTURE_RECTANGLE_ARB); |
+ |
+ if (state_ != NORMAL && !frames_pending_decode_) |
+ FlushDone(); |
+} |
+ |
+void VTVideoDecodeAccelerator::FlushStart(State state) { |
+ DCHECK(CalledOnValidThread()); |
+ state_ = state; |
+ |
+ // If resetting or destroying, drop all pending frames. |
+ if (state_ == RESETTING || state_ == DESTROYING) { |
+ while (!decoded_frames_.empty()) { |
+ DecodedFrame frame = decoded_frames_.front(); |
+ decoded_frames_.pop(); |
+ client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); |
+ frames_pending_decode_--; |
+ } |
+ } |
+ |
+ if (frames_pending_decode_) { |
+ decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( |
+ &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this))); |
+ } else { |
+ FlushDone(); |
+ } |
+} |
+ |
+void VTVideoDecodeAccelerator::FlushTask() { |
+ DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread()); |
+ if (session_) |
+ CHECK(!VTDecompressionSessionFinishDelayedFrames(session_)); |
+} |
+ |
+void VTVideoDecodeAccelerator::FlushDone() { |
+ DCHECK(CalledOnValidThread()); |
+ switch (state_) { |
+ case NORMAL: |
+ NOTREACHED(); |
+ break; |
+ case FLUSHING: |
+ client_->NotifyFlushDone(); |
+ state_ = NORMAL; |
+ break; |
+ case RESETTING: |
+ client_->NotifyResetDone(); |
+ state_ = NORMAL; |
+ break; |
+ case DESTROYING: |
+ delete this; |
+ break; |
+ } |
} |
void VTVideoDecodeAccelerator::Flush() { |
DCHECK(CalledOnValidThread()); |
- // TODO(sandersd): Trigger flush, sending frames. |
+ FlushStart(FLUSHING); |
} |
void VTVideoDecodeAccelerator::Reset() { |
DCHECK(CalledOnValidThread()); |
- // TODO(sandersd): Trigger flush, discarding frames. |
+ FlushStart(RESETTING); |
} |
void VTVideoDecodeAccelerator::Destroy() { |
DCHECK(CalledOnValidThread()); |
- // TODO(sandersd): Trigger flush, discarding frames, and wait for them. |
- delete this; |
+ FlushStart(DESTROYING); |
} |
bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() { |