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

Unified 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: Move CompleteAction() into case ACTION_DESTROY. Created 6 years, 3 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 37d96b7bbeae0a38c85a10a1afc77002a3076800..8fb0b834bbfc0649e20ebf486f5a40964ea7035b 100644
--- a/content/common/gpu/media/vt_video_decode_accelerator.cc
+++ b/content/common/gpu/media/vt_video_decode_accelerator.cc
@@ -40,7 +40,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);
@@ -57,6 +56,16 @@ VTVideoDecodeAccelerator::DecodedFrame::DecodedFrame(
VTVideoDecodeAccelerator::DecodedFrame::~DecodedFrame() {
}
+VTVideoDecodeAccelerator::PendingAction::PendingAction(
+ Action action,
+ int32_t bitstream_id)
+ : action(action),
+ bitstream_id(bitstream_id) {
+}
+
+VTVideoDecodeAccelerator::PendingAction::~PendingAction() {
+}
+
VTVideoDecodeAccelerator::VTVideoDecodeAccelerator(CGLContextObj cgl_context)
: cgl_context_(cgl_context),
client_(NULL),
@@ -160,7 +169,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,
@@ -171,6 +179,8 @@ void VTVideoDecodeAccelerator::ConfigureDecoder(
session_.InitializeInto()));
// If the size has changed, trigger a request for new picture buffers.
+ // TODO(sandersd): Move to SendPictures(), and use this just as a hint for an
+ // upcoming size change.
gfx::Size new_coded_size(coded_dimensions.width, coded_dimensions.height);
if (coded_size_ != new_coded_size) {
coded_size_ = new_coded_size;
@@ -183,8 +193,8 @@ 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.
+ CHECK_GE(bitstream.id(), 0) << "Negative bitstream_id";
+ pending_bitstream_ids_.push(bitstream.id());
decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
&VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this),
bitstream));
@@ -236,6 +246,17 @@ void VTVideoDecodeAccelerator::DecodeTask(
if (!session_)
ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes);
+ // If there are no non-configuration units, immediately return an empty
+ // (ie. dropped) frame. It is an error to create a MemoryBlock with zero
+ // size.
+ if (!data_size) {
+ gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &VTVideoDecodeAccelerator::OutputTask,
+ weak_this_factory_.GetWeakPtr(),
+ DecodedFrame(bitstream.id(), NULL)));
+ return;
+ }
+
// 3. Allocate a memory-backed CMBlockBuffer for the translated data.
base::ScopedCFTypeRef<CMBlockBufferRef> data;
CHECK(!CMBlockBufferCreateWithMemoryBlock(
@@ -311,7 +332,7 @@ void VTVideoDecodeAccelerator::Output(
void VTVideoDecodeAccelerator::OutputTask(DecodedFrame frame) {
DCHECK(CalledOnValidThread());
decoded_frames_.push(frame);
- SendPictures();
+ ProcessDecodedFrames();
}
void VTVideoDecodeAccelerator::SizeChangedTask(gfx::Size coded_size) {
@@ -332,10 +353,11 @@ void VTVideoDecodeAccelerator::AssignPictureBuffers(
texture_ids_[pictures[i].id()] = pictures[i].texture_id();
}
- // Pictures are not marked as uncleared until this method returns. They will
- // become broken if they are used before that happens.
+ // Pictures are not marked as uncleared until after this method returns, and
+ // they will be broken if they are used before that happens. So, schedule
+ // future work after that happens.
gpu_task_runner_->PostTask(FROM_HERE, base::Bind(
- &VTVideoDecodeAccelerator::SendPictures,
+ &VTVideoDecodeAccelerator::ProcessDecodedFrames,
weak_this_factory_.GetWeakPtr()));
}
@@ -344,61 +366,186 @@ void VTVideoDecodeAccelerator::ReusePictureBuffer(int32_t picture_id) {
DCHECK_EQ(CFGetRetainCount(picture_bindings_[picture_id]), 1);
picture_bindings_.erase(picture_id);
available_picture_ids_.push(picture_id);
- SendPictures();
+ ProcessDecodedFrames();
}
-// TODO(sandersd): Proper error reporting instead of CHECKs.
-void VTVideoDecodeAccelerator::SendPictures() {
+void VTVideoDecodeAccelerator::CompleteAction(Action action) {
DCHECK(CalledOnValidThread());
- if (available_picture_ids_.empty() || decoded_frames_.empty())
- return;
+ switch (action) {
+ case ACTION_FLUSH:
+ client_->NotifyFlushDone();
+ break;
+ case ACTION_RESET:
+ client_->NotifyResetDone();
+ break;
+ case ACTION_DESTROY:
+ delete this;
+ break;
+ }
+}
+
+void VTVideoDecodeAccelerator::CompleteActions(int32_t bitstream_id) {
+ DCHECK(CalledOnValidThread());
+ while (!pending_actions_.empty() &&
+ pending_actions_.front().bitstream_id == bitstream_id) {
+ CompleteAction(pending_actions_.front().action);
+ pending_actions_.pop();
+ }
+}
+
+void VTVideoDecodeAccelerator::ProcessDecodedFrames() {
+ DCHECK(CalledOnValidThread());
+
+ while (!decoded_frames_.empty()) {
+ if (pending_actions_.empty()) {
+ // No pending actions; send frames normally.
+ SendPictures(pending_bitstream_ids_.back());
+ return;
+ }
+
+ int32_t next_action_bitstream_id = pending_actions_.front().bitstream_id;
+ int32_t last_sent_bitstream_id = -1;
+ switch (pending_actions_.front().action) {
+ case ACTION_FLUSH:
+ // Send frames normally.
+ last_sent_bitstream_id = SendPictures(next_action_bitstream_id);
+ break;
+
+ case ACTION_RESET:
+ // Drop decoded frames.
+ while (!decoded_frames_.empty() &&
+ last_sent_bitstream_id != next_action_bitstream_id) {
+ last_sent_bitstream_id = decoded_frames_.front().bitstream_id;
+ decoded_frames_.pop();
+ DCHECK_EQ(pending_bitstream_ids_.front(), last_sent_bitstream_id);
+ pending_bitstream_ids_.pop();
+ client_->NotifyEndOfBitstreamBuffer(last_sent_bitstream_id);
+ }
+ break;
+
+ case ACTION_DESTROY:
+ // Drop decoded frames, without bookkeeping.
+ while (!decoded_frames_.empty()) {
+ last_sent_bitstream_id = decoded_frames_.front().bitstream_id;
+ decoded_frames_.pop();
+ }
+
+ // Handle completing the action specially, as it is important not to
+ // access |this| after calling CompleteAction().
+ if (last_sent_bitstream_id == next_action_bitstream_id)
+ CompleteAction(ACTION_DESTROY);
+
+ // Either |this| was deleted or no more progress can be made.
+ return;
+ }
+
+ // If we ran out of buffers (or pictures), no more progress can be made
+ // until more frames are decoded.
+ if (last_sent_bitstream_id != next_action_bitstream_id)
+ return;
+
+ // Complete all actions pending for this |bitstream_id|, then loop to see
+ // if progress can be made on the next action.
+ CompleteActions(next_action_bitstream_id);
+ }
+}
+
+int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!decoded_frames_.empty());
+
+ if (available_picture_ids_.empty())
+ return -1;
gfx::ScopedCGLSetCurrentContext scoped_set_current_context(cgl_context_);
glEnable(GL_TEXTURE_RECTANGLE_ARB);
- while (!available_picture_ids_.empty() && !decoded_frames_.empty()) {
- int32_t picture_id = available_picture_ids_.front();
- available_picture_ids_.pop();
+ int32_t last_sent_bitstream_id = -1;
+ while (!available_picture_ids_.empty() &&
+ !decoded_frames_.empty() &&
+ last_sent_bitstream_id != up_to_bitstream_id) {
DecodedFrame frame = decoded_frames_.front();
decoded_frames_.pop();
- IOSurfaceRef surface = CVPixelBufferGetIOSurface(frame.image_buffer);
-
- gfx::ScopedTextureBinder
- texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]);
- CHECK(!CGLTexImageIOSurface2D(
- cgl_context_, // ctx
- GL_TEXTURE_RECTANGLE_ARB, // target
- GL_RGB, // internal_format
- texture_size_.width(), // width
- texture_size_.height(), // height
- GL_YCBCR_422_APPLE, // format
- GL_UNSIGNED_SHORT_8_8_APPLE, // type
- surface, // io_surface
- 0)); // plane
-
- picture_bindings_[picture_id] = frame.image_buffer;
- client_->PictureReady(media::Picture(
- picture_id, frame.bitstream_id, gfx::Rect(texture_size_)));
+ DCHECK_EQ(pending_bitstream_ids_.front(), frame.bitstream_id);
+ pending_bitstream_ids_.pop();
+ int32_t picture_id = available_picture_ids_.front();
+ available_picture_ids_.pop();
+
+ CVImageBufferRef image_buffer = frame.image_buffer.get();
+ if (image_buffer) {
+ IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer);
+
+ // TODO(sandersd): Find out why this sometimes fails due to no GL context.
+ gfx::ScopedTextureBinder
+ texture_binder(GL_TEXTURE_RECTANGLE_ARB, texture_ids_[picture_id]);
+ CHECK(!CGLTexImageIOSurface2D(
+ cgl_context_, // ctx
+ GL_TEXTURE_RECTANGLE_ARB, // target
+ GL_RGB, // internal_format
+ texture_size_.width(), // width
+ texture_size_.height(), // height
+ GL_YCBCR_422_APPLE, // format
+ GL_UNSIGNED_SHORT_8_8_APPLE, // type
+ surface, // io_surface
+ 0)); // plane
+
+ picture_bindings_[picture_id] = frame.image_buffer;
+ client_->PictureReady(media::Picture(
+ picture_id, frame.bitstream_id, gfx::Rect(texture_size_)));
+ }
+
client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id);
+ last_sent_bitstream_id = frame.bitstream_id;
}
glDisable(GL_TEXTURE_RECTANGLE_ARB);
+ return last_sent_bitstream_id;
+}
+
+void VTVideoDecodeAccelerator::FlushTask() {
+ DCHECK(decoder_thread_.message_loop_proxy()->BelongsToCurrentThread());
+ CHECK(!VTDecompressionSessionFinishDelayedFrames(session_));
+}
+
+void VTVideoDecodeAccelerator::QueueAction(Action action) {
+ DCHECK(CalledOnValidThread());
+ if (pending_bitstream_ids_.empty()) {
+ // If there are no pending frames, all actions complete immediately.
+ CompleteAction(action);
+ } else {
+ // Otherwise, queue the action.
+ pending_actions_.push(PendingAction(action, pending_bitstream_ids_.back()));
+
+ // Request a flush to make sure the action will eventually complete.
+ decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
+ &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this)));
+
+ // See if we can make progress now that there is a new pending action.
+ ProcessDecodedFrames();
+ }
}
void VTVideoDecodeAccelerator::Flush() {
DCHECK(CalledOnValidThread());
- // TODO(sandersd): Trigger flush, sending frames.
+ QueueAction(ACTION_FLUSH);
}
void VTVideoDecodeAccelerator::Reset() {
DCHECK(CalledOnValidThread());
- // TODO(sandersd): Trigger flush, discarding frames.
+ QueueAction(ACTION_RESET);
}
void VTVideoDecodeAccelerator::Destroy() {
DCHECK(CalledOnValidThread());
- // TODO(sandersd): Trigger flush, discarding frames, and wait for them.
- delete this;
+ // Drop any other pending actions.
+ while (!pending_actions_.empty())
+ pending_actions_.pop();
+ // Return all bitstream buffers.
+ while (!pending_bitstream_ids_.empty()) {
+ client_->NotifyEndOfBitstreamBuffer(pending_bitstream_ids_.front());
+ pending_bitstream_ids_.pop();
+ }
+ QueueAction(ACTION_DESTROY);
}
bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() {
« 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