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

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: New state machine. 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
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..c83e2ef90c0589b9957a78f1f793a9db2e7fcda1 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,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.
+ pending_bitstream_ids_.push(bitstream.id());
decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
&VTVideoDecodeAccelerator::DecodeTask, base::Unretained(this),
bitstream));
@@ -236,6 +245,14 @@ void VTVideoDecodeAccelerator::DecodeTask(
if (!session_)
ConfigureDecoder(config_nalu_data_ptrs, config_nalu_data_sizes);
+ if (!data_size) {
Pawel Osciak 2014/09/25 01:27:29 Could you explain? If there is no data we want to
sandersd (OOO until July 31) 2014/09/25 19:18:52 Done. (And yes.)
+ 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 +328,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 +349,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 +362,173 @@ 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) {
Pawel Osciak 2014/09/25 01:27:29 Wouldn't you want to do <= instead of == here?
sandersd (OOO until July 31) 2014/09/25 19:18:53 No, CompleteActions() is called every time a pendi
Pawel Osciak 2014/09/26 04:25:07 Ah, lack of doc for the method confused me. And I
sandersd (OOO until July 31) 2014/09/26 04:56:41 Acknowledged.
+ CompleteAction(pending_actions_.front().action);
+ pending_actions_.pop();
+ }
+}
+
+void VTVideoDecodeAccelerator::ProcessDecodedFrames() {
+ DCHECK(CalledOnValidThread());
+
+ while (true) {
Pawel Osciak 2014/09/25 01:27:29 Maybe just while (!decoded_frames_.empty()) instea
sandersd (OOO until July 31) 2014/09/25 19:18:52 Done.
+ if (decoded_frames_.empty())
+ return;
+
+ if (pending_actions_.empty()) {
+ // No pending actions; send frames normally.
+ if (!available_picture_ids_.empty())
+ SendPictures(pending_bitstream_ids_.back());
+ return;
+ } else if (pending_actions_.front().action == ACTION_FLUSH) {
Pawel Osciak 2014/09/25 01:27:28 I think it could be more readable if you: if (pen
sandersd (OOO until July 31) 2014/09/25 19:18:52 I like this, thanks!
+ // Flushing; send frames normally, then complete any actions.
+ if (!available_picture_ids_.empty()) {
Pawel Osciak 2014/09/25 01:27:28 You make this check in SendPictures anyway. I'd re
sandersd (OOO until July 31) 2014/09/25 19:18:52 This check served a few purposes: - It avoids ne
+ int32_t last_sent_bitstream_id =
+ SendPictures(pending_actions_.front().bitstream_id);
+ CompleteActions(last_sent_bitstream_id);
+ // Loop again, as there may be work to do for a new pending action.
+ continue;
+ }
+ return;
Pawel Osciak 2014/09/25 01:27:29 I'd just remove continue and return and let the ex
sandersd (OOO until July 31) 2014/09/25 19:18:53 Acknowledged.
Pawel Osciak 2014/09/26 04:25:07 But not implemented?
sandersd (OOO until July 31) 2014/09/26 04:56:41 I thought the switch version made this clear enoug
Pawel Osciak 2014/09/26 05:20:30 My point is, continue is the default action. So yo
sandersd (OOO until July 31) 2014/09/26 06:56:02 Done.
+ } else if (pending_actions_.front().action == ACTION_RESET) {
+ // Reseting; drop a decoded frame and then complete any actions.
Pawel Osciak 2014/09/25 01:27:28 One frame? Not drop frames until the id for the ac
sandersd (OOO until July 31) 2014/09/25 19:18:53 It's the same, but I've switched it around.
+ int32_t bitstream_id = decoded_frames_.front().bitstream_id;
Pawel Osciak 2014/09/25 01:27:28 Why not do things until action's bitstream id? Thi
sandersd (OOO until July 31) 2014/09/25 19:18:53 Done.
+ decoded_frames_.pop();
+ DCHECK_EQ(pending_bitstream_ids_.front(), bitstream_id);
+ pending_bitstream_ids_.pop();
+ client_->NotifyEndOfBitstreamBuffer(bitstream_id);
+ CompleteActions(bitstream_id);
+ // Loop again, as there may be more frames to drop or work to do for a new
+ // pending action.
+ continue;
Pawel Osciak 2014/09/25 01:27:29 Not needed.
sandersd (OOO until July 31) 2014/09/25 19:18:52 Acknowledged.
Pawel Osciak 2014/09/26 04:25:07 But not removed?
sandersd (OOO until July 31) 2014/09/26 04:56:41 See above.
+ } else {
+ // Destroying; drop a decoded frame, then destroy if ready.
Pawel Osciak 2014/09/25 01:27:28 Why only one not up to action id? That would be si
sandersd (OOO until July 31) 2014/09/25 19:18:52 Done.
+ DCHECK_EQ(pending_actions_.front().action, ACTION_DESTROY);
+ int32_t bitstream_id = decoded_frames_.front().bitstream_id;
+ decoded_frames_.pop();
+ if (pending_actions_.front().bitstream_id == bitstream_id) {
+ CompleteAction(ACTION_DESTROY);
+ return;
+ }
+ // Loop again, as there may be more frames to drop.
+ continue;
+ }
+
+ NOTREACHED();
Pawel Osciak 2014/09/25 01:27:29 This would go to default: clause.
sandersd (OOO until July 31) 2014/09/25 19:18:52 Not quite, I want to be sure that every path at th
Pawel Osciak 2014/09/26 04:25:07 You can do that by removing continues and putting
sandersd (OOO until July 31) 2014/09/26 04:56:41 I don't believe that is the same. The compiler can
Pawel Osciak 2014/09/26 05:20:30 That's exactly the point I'm trying to make. Nobod
sandersd (OOO until July 31) 2014/09/26 06:56:02 Done.
+ }
+}
+
+int32_t VTVideoDecodeAccelerator::SendPictures(int32_t up_to_bitstream_id) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(!decoded_frames_.empty());
+ DCHECK(!available_picture_ids_.empty());
+ int32_t last_sent_bitstream_id = -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();
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) {
Pawel Osciak 2014/09/25 01:27:28 What happens if there is no image_buffer? Don't we
sandersd (OOO until July 31) 2014/09/25 19:18:52 This happens in if !data_size, or if the decoder d
+ IOSurfaceRef surface = CVPixelBufferGetIOSurface(image_buffer);
+
+ // TODO(sandersd): Find out why this somtimes fails due to no GL context.
Pawel Osciak 2014/09/25 01:27:28 s/somtimes/sometimes/ How do you know it failed?
sandersd (OOO until July 31) 2014/09/25 19:18:53 It fails when the CGLTexImageIOSurface2D() check f
Pawel Osciak 2014/09/26 04:25:07 Well, I'd really like to express my strong prefere
sandersd (OOO until July 31) 2014/09/26 04:56:41 Understood, replacing all the CHECKs is the next i
+ 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;
+ if (frame.bitstream_id == up_to_bitstream_id)
Pawel Osciak 2014/09/25 01:27:29 Make this a part of while() condition?
sandersd (OOO until July 31) 2014/09/25 19:18:53 Done.
+ break;
}
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()) {
+ CompleteAction(action);
Pawel Osciak 2014/09/25 01:27:29 Could you just let ProcessDecodedFrames() to this
sandersd (OOO until July 31) 2014/09/25 19:18:52 ProcessDecodedFrames() doesn't currently handle an
+ } else {
+ pending_actions_.push(PendingAction(action, pending_bitstream_ids_.back()));
+ ProcessDecodedFrames();
+ if (!pending_actions_.empty() && !pending_bitstream_ids_.empty()) {
+ decoder_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind(
+ &VTVideoDecodeAccelerator::FlushTask, base::Unretained(this)));
Pawel Osciak 2014/09/25 01:27:28 This requires an explanation...
sandersd (OOO until July 31) 2014/09/25 19:18:53 I think the condition was just wrong, as there wil
+ }
+ }
}
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());
Pawel Osciak 2014/09/25 01:27:29 If you stopped the decoder thread here, you wouldn
sandersd (OOO until July 31) 2014/09/25 19:18:52 This is indeed possible, as long as the FlushTask
Pawel Osciak 2014/09/26 04:25:07 I don't understand the part about FlushTask... Que
sandersd (OOO until July 31) 2014/09/26 04:56:41 Queued to the decoder thread message loop, before
Pawel Osciak 2014/09/26 05:20:30 If you decoder_thread_.Stop() from Destroy(), it w
sandersd (OOO until July 31) 2014/09/26 06:56:02 This makes sense, but I'd still rather hold off. I
Pawel Osciak 2014/09/26 15:30:49 The idea was to do away with ACTION_DESTROY. It wo
sandersd (OOO until July 31) 2014/09/26 17:28:51 Acknowledged.
- // 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());
Pawel Osciak 2014/09/25 01:27:29 You shouldn't have to do this on Destroy().
sandersd (OOO until July 31) 2014/09/25 19:18:53 It seems I do, GpuVideoDecoder DCHECKS that there
Pawel Osciak 2014/09/26 04:25:07 Acknowledged.
+ pending_bitstream_ids_.pop();
+ }
+ QueueAction(ACTION_DESTROY);
Pawel Osciak 2014/09/25 01:27:29 Do we actually need this? You dropped everything a
sandersd (OOO until July 31) 2014/09/25 19:18:52 We tell the client we are done with the buffers, b
Pawel Osciak 2014/09/26 04:25:07 How do you guarantee that during that client_ will
sandersd (OOO until July 31) 2014/09/26 04:56:41 During destroy, ProcessDecodedFrames() specificall
Pawel Osciak 2014/09/26 05:20:30 Could you tell me how this is achieved please?
sandersd (OOO until July 31) 2014/09/26 06:56:02 Because there is a queued ACTION_DESTROY, it alway
}
bool VTVideoDecodeAccelerator::CanDecodeOnIOThread() {

Powered by Google App Engine
This is Rietveld 408576698