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

Unified Diff: content/common/gpu/media/vaapi_video_decode_accelerator.cc

Issue 9814001: Add VAVDA, the VAAPI Video Decode Accelerator for Intel CPUs. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 9 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/vaapi_video_decode_accelerator.cc
diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.cc b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2430dea325dd6e7ba1a23ac47aed813ad93ef12f
--- /dev/null
+++ b/content/common/gpu/media/vaapi_video_decode_accelerator.cc
@@ -0,0 +1,445 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "vaapi_video_decode_accelerator.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/string_util.h"
+#include "base/synchronization/waitable_event.h"
+#include "gpu/command_buffer/service/gpu_switches.h"
+#include "content/public/common/content_switches.h"
+#include "content/common/gpu/gpu_channel.h"
+#include "media/video/picture.h"
+#include "third_party/libva/va/va.h"
+#include "ui/gfx/gl/gl_bindings.h"
+
+#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
+ do { \
+ if (!(result)) { \
+ DLOG(ERROR) << log; \
+ StopOnError(error_code); \
+ return ret; \
+ } \
+ } while (0)
+
+void VaapiVideoDecodeAccelerator::StopOnError(
+ media::VideoDecodeAccelerator::Error error) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ DLOG(ERROR) << "Stopping on error " << error;
+
+ decoder_thread_.Stop();
+
+ if (client_)
+ client_->NotifyError(error);
+ client_ = NULL;
+}
+
+VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
+ media::VideoDecodeAccelerator::Client* client)
+ : input_ready_(true, false), // manually Reset() and initially not signalled
+ message_loop_(MessageLoop::current()),
+ client_(client),
+ pictures_requested_(false),
+ after_reset_(false),
+ resetting_(false),
+ decoder_thread_("VaapiDecoderThread") {
+}
+
+VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+}
+
+bool VaapiVideoDecodeAccelerator::Initialize(Profile profile) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+ bool res = true;
+
+ DVLOG(2) << "Initializing VAVDA, profile: " << profile;
+
+ res = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableVaapiDecodeAcceleration);
+ if (!res) {
+ DVLOG(1) << "Vaapi HW acceleration disabled";
+ goto done;
+ }
+
+ res = decoder_.Initialize(profile, x_display_, glx_context_, message_loop_,
+ &VaapiVideoDecodeAccelerator::OutputPicCallback, this);
+ if (!res) {
+ DLOG(ERROR) << "Failed initializing decoder";
+ goto done;
+ }
+
+ res = decoder_thread_.Start();
+ if (!res)
+ DLOG(ERROR) << "Failed starting decoding thread";
+
+done:
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::NotifyInitializeDone, this));
+ return res;
+}
+
+void VaapiVideoDecodeAccelerator::NotifyInitializeDone() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+ if (client_)
+ client_->NotifyInitializeDone();
+}
+
+void VaapiVideoDecodeAccelerator::SetGlxState(Display* x_display,
+ GLXContext glx_context) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+ x_display_ = x_display;
+ glx_context_ = glx_context;
+}
+
+void VaapiVideoDecodeAccelerator::NotifyInputBufferRead(int input_buffer_id) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ DVLOG(4) << "Notifying end of input buffer " << input_buffer_id;
+ if (client_)
+ client_->NotifyEndOfBitstreamBuffer(input_buffer_id);
+}
+
+void VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady(int32 input_id,
+ int32 output_id) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ // Sync the contents of the texture.
+ if (!decoder_.PutPicToTexture(output_id)) {
+ DLOG(ERROR) << "Failed putting picture to texture";
+ StopOnError(PLATFORM_FAILURE);
+ return;
+ }
+
+ // And notify the client a picture is ready to be displayed.
+ media::Picture picture(output_id, input_id);
+ DVLOG(4) << "Notifying output picture id " << output_id
+ << " for input "<< input_id << " is ready";
+ if (client_)
+ client_->PictureReady(picture);
+}
+
+void VaapiVideoDecodeAccelerator::RequestPictureBuffers(int num_pics,
Ami GONE FROM CHROMIUM 2012/03/21 13:16:24 Why not pass the VDA::Client to the decoder, so it
Pawel Osciak 2012/03/21 18:40:35 I was really trying not to add additional complexi
Ami GONE FROM CHROMIUM 2012/03/22 17:01:36 I'm saying I think passing Client to the decoder w
Pawel Osciak 2012/04/05 10:37:20 VAVDA controls logic behind whether it's required
+ int width,
+ int height) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ DVLOG(1) << "Requesting " << num_pics << " pictures of size: "
+ << width << "x" << height;
+ if (client_)
+ client_->ProvidePictureBuffers(num_pics, gfx::Size(width, height));
+}
+
+void VaapiVideoDecodeAccelerator::TryGetNewInputBuffer() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+ DCHECK(!input_ready_.IsSignaled());
+
+ // If current buffer is still set, return it to the client.
+ if (curr_input_buffer_.get()) {
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &VaapiVideoDecodeAccelerator::NotifyInputBufferRead, this,
+ curr_input_buffer_->id));
+
+ curr_input_buffer_.reset();
+ }
+
+ // If there is no more input buffers ready, return. We will come back
+ // here the next time client provides us with a new input buffer.
+ if (input_buffers_.empty())
+ return;
+
+ // Otherwise pop a new input buffer and set it up for the decoder thread.
+ curr_input_buffer_.reset(input_buffers_.front());
+ input_buffers_.pop();
+
+ DVLOG(4) << "New current bitstream buffer, id: " << curr_input_buffer_->id
+ << " size: " << (int)curr_input_buffer_->size;
+
+ // Decoder is waiting on |input_ready_|, so we are safe to do this.
+ decoder_.SetStream((uint8*)curr_input_buffer_->shm->memory(),
+ curr_input_buffer_->size);
+
+ // Wake up the decoder thread and let it continue.
+ input_ready_.Signal();
+}
+
+void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
+ const media::BitstreamBuffer& bitstream_buffer) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
+ << " size: " << (int)bitstream_buffer.size();
+
+ scoped_ptr<base::SharedMemory> shm(
+ new base::SharedMemory(bitstream_buffer.handle(), true));
+ CHECK(shm.get());
+ RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()),
+ "Failed to map input buffer", UNREADABLE_INPUT,);
+
+ // Set up a new input buffer and queue it for later.
+ InputBuffer* input_buffer = new InputBuffer();
+ CHECK(input_buffer);
+ input_buffer->shm.reset(shm.release());
+ input_buffer->id = bitstream_buffer.id();
+ input_buffer->size = bitstream_buffer.size();
+ input_buffers_.push(input_buffer);
+}
+
+void VaapiVideoDecodeAccelerator::WaitForInput() {
+ DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
+
+ // Set us up for sleep, request new input buffer from the main thread
+ // and go to sleep waiting for good news.
+ input_ready_.Reset();
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::TryGetNewInputBuffer, this));
+ input_ready_.Wait();
+}
+
+void VaapiVideoDecodeAccelerator::DecodeTask() {
+ DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
+ VaapiH264Decoder::DecResult res;
+
+ // Main decode task.
+ DVLOG(4) << "Decode task";
+
+ DCHECK(input_ready_.IsSignaled());
+
+ if (resetting_) {
+ // Will get back here once we are done with reset. This is needed so we
+ // don't sleep indefinitely blocking the decoder from being forced
+ // to reset.
+ DVLOG(4) << "Resetting, leaving DecodeTask";
+ return;
+ }
+
+ // Could happen after reset, when we are still waiting for a new buffer.
+ if (!curr_input_buffer_.get())
+ return;
+
+ if (!pictures_requested_ || after_reset_) {
+ // Try to initialize or resume playback after reset.
+ res = decoder_.DecodeInitial(curr_input_buffer_->id);
+ switch (res) {
+ case VaapiH264Decoder::kReadyToDecode:
+ if (!pictures_requested_) {
+ // Decoder decoded initial stream information and is ready
+ // to receive output pictures. Request them from the client.
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &VaapiVideoDecodeAccelerator::RequestPictureBuffers, this,
+ decoder_.GetRequiredNumOfPictures(),
+ decoder_.pic_width(), decoder_.pic_height()));
+ pictures_requested_ = true;
+ } else {
+ // We are after reset and successfully found a point from which
+ // we can resume normal decoding, so post a task for that.
+ DCHECK(after_reset_);
+ after_reset_ = false;
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
+ }
+ break;
+
+ case VaapiH264Decoder::kNeedMoreStreamData:
+ DLOG(INFO) << "Waiting for more stream data to (re)initialize";
+ // May sleep.
+ WaitForInput();
+ break;
+
+ case VaapiH264Decoder::kDecError:
+ DLOG(ERROR) << "Error decoding stream";
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::StopOnError, this,
+ PLATFORM_FAILURE));
+ break;
+
+ default:
+ NOTREACHED() << "Unexpected result from the decoder";
+ }
+ } else {
+ // Try to decode what stream data is in the decoder until we get
+ // a frame or run out of input stream.
+ res = decoder_.DecodeOneFrame(curr_input_buffer_->id);
+ switch (res) {
+ case VaapiH264Decoder::kNeedMoreStreamData:
+ // May sleep.
+ WaitForInput();
+ // fallthrough
+ case VaapiH264Decoder::kDecodedFrame:
+ // (still) have more stream, try to decode more
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
+ break;
+
+ case VaapiH264Decoder::kNoOutputAvailable:
+ // The decoder does not have any output buffers available, so return.
+ // We will come back once the client gives us back a picture buffer
+ // after displaying it.
+ break;
+
+ case VaapiH264Decoder::kDecError:
+ DLOG(ERROR) << "Error decoding stream";
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::StopOnError, this,
+ PLATFORM_FAILURE));
+ break;
+
+ default:
+ NOTREACHED() << "Unexpected result from the decoder";
+ }
+ }
+}
+
+
+void VaapiVideoDecodeAccelerator::Decode(
+ const media::BitstreamBuffer& bitstream_buffer) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
+ bitstream_buffer.id());
+
+ // We got a new input buffer from the client, map it and queue for later use.
+ MapAndQueueNewInputBuffer(bitstream_buffer);
+
+ // Set it up already if there is no current one.
+ if (!curr_input_buffer_.get())
+ TryGetNewInputBuffer();
+
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
+}
+
+void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
+ const std::vector<media::PictureBuffer>& buffers) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ // Here we call sensitive decoder functions out of its thread, but
+ // the decoder thread is not running yet, so we can safely do this.
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ DVLOG(2) << "Assigning picture id " << buffers[i].id()
+ << " to texture id " << buffers[i].texture_id();
+ decoder_.AssignPictureBuffer(buffers[i].id(), buffers[i].texture_id());
+ }
+
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
+}
+
+void VaapiVideoDecodeAccelerator::ReusePictureTask(int32 picture_buffer_id) {
+ DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
+
+ decoder_.ReusePictureBuffer(picture_buffer_id);
+
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask, this));
+}
+
+void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+ TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer",
+ "Picture id", picture_buffer_id);
+
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::ReusePictureTask, this,
+ picture_buffer_id));
+}
+
+void VaapiVideoDecodeAccelerator::FlushTask() {
+ DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
+
+ decoder_.Flush();
+ decoder_.Reset();
+
+ message_loop_->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::NotifyFlushDone, this));
+
+ WaitForInput();
+}
+
+void VaapiVideoDecodeAccelerator::Flush() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ DVLOG(1) << "Got flush request";
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::FlushTask, this));
+}
+
+void VaapiVideoDecodeAccelerator::NotifyFlushDone() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+ if (client_)
+ client_->NotifyFlushDone();
+ DVLOG(1) << "Flush done";
+}
+
+void VaapiVideoDecodeAccelerator::ResetTask() {
+ DCHECK_EQ(decoder_thread_.message_loop(), MessageLoop::current());
+
+ // All the decoding tasks should be done by now, since the client
+ // does not give more input and we have scheduled ourselves after
+ // we got a reset request.
+ decoder_.Reset();
+
+ // Make sure next time decode tasks are run they will reinitialize.
+ after_reset_ = true;
+
+ // And let client know that we are done with reset.
+ message_loop_->PostTask(FROM_HERE, base::Bind(
+ &VaapiVideoDecodeAccelerator::NotifyResetDone, this));
+
+ //WaitForInput();
+}
+
+void VaapiVideoDecodeAccelerator::Reset() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ DVLOG(1) << "Got reset request";
+ decoder_thread_.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::ResetTask, this));
+ resetting_ = true;
+ // Release waiting decoder thread, if any. Any additional already posted
+ // decoding threads will exit immediately due to resetting_ set to true.
+ input_ready_.Signal();
+}
+
+void VaapiVideoDecodeAccelerator::NotifyResetDone() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ if (client_)
+ client_->NotifyResetDone();
+ resetting_ = false;
+ DVLOG(1) << "Reset done";
+}
+
+void VaapiVideoDecodeAccelerator::Destroy() {
+ DCHECK_EQ(message_loop_, MessageLoop::current());
+
+ decoder_thread_.Stop();
+ // No decoder tasks running anymore, so safe to destroy.
+ decoder_.Destroy();
+ client_ = NULL;
+}
+
+//static
+void VaapiVideoDecodeAccelerator::OutputPicCallback(void* arg,
+ int32 input_id,
+ int32 output_id) {
+ TRACE_EVENT2("Video Decoder", "VAVDA::OutputPicCallback",
+ "Input id", input_id, "Picture id", output_id);
+ DVLOG(4) << "Outputting picture, input id: " << input_id
+ << " output id: " << output_id;
+
+ // Forward the request to the main thread.
+ VaapiVideoDecodeAccelerator* vavda =
+ static_cast<VaapiVideoDecodeAccelerator*>(arg);
+ DCHECK_EQ(vavda->decoder_thread_.message_loop(), MessageLoop::current());
+ vavda->message_loop_->PostTask(FROM_HERE,
+ base::Bind(&VaapiVideoDecodeAccelerator::SyncAndNotifyPictureReady,
Ami GONE FROM CHROMIUM 2012/03/21 13:16:24 Wait, wat?? decoder tells VAVDA a pic is ready but
Pawel Osciak 2012/03/21 18:40:35 Decoder can't sync, because it has to be done on G
Ami GONE FROM CHROMIUM 2012/03/22 17:01:36 Then why not pass the decoder the GLX thread and h
Ami GONE FROM CHROMIUM 2012/03/22 17:01:36 Then why not pass the decoder the GLX thread and h
Pawel Osciak 2012/04/05 10:37:20 I'm not passing message_loop or anything related t
+ vavda, input_id, output_id));
+}
+

Powered by Google App Engine
This is Rietveld 408576698