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

Unified Diff: content/common/gpu/media/mac_video_decode_accelerator.mm

Issue 10388108: Implement media::VideoDecodeAccelerator on Mac (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: remove #if 0 Created 8 years, 7 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/mac_video_decode_accelerator.mm
diff --git a/content/common/gpu/media/mac_video_decode_accelerator.mm b/content/common/gpu/media/mac_video_decode_accelerator.mm
new file mode 100644
index 0000000000000000000000000000000000000000..8d572c8af1cd7954fa4051b09f9edd8733f13c72
--- /dev/null
+++ b/content/common/gpu/media/mac_video_decode_accelerator.mm
@@ -0,0 +1,310 @@
+// 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 "content/common/gpu/media/mac_video_decode_accelerator.h"
+
+#include "base/bind.h"
+#include "base/file_path.h"
+#import "base/mac/foundation_util.h"
+#import "base/memory/ref_counted_memory.h"
+#import "base/message_loop.h"
+#include "base/location.h"
+#include "base/native_library.h"
+#include "ui/surface/io_surface_support_mac.h"
+#include "ui/gfx/video_decode_acceleration_support_mac.h"
+
+namespace {
+
+enum { kNumPictureBuffers = 4 };
+
+class ScopedContextSetter {
+ public:
+ ScopedContextSetter(CGLContextObj context)
+ : old_context_(NULL),
+ did_succeed_(false) {
+ old_context_ = CGLGetCurrentContext();
+ did_succeed_ = CGLSetCurrentContext(context) == kCGLNoError;
+ }
+
+ ~ScopedContextSetter() {
+ if (did_succeed_)
+ CGLSetCurrentContext(old_context_);
+ }
+
+ bool did_succeed() const {
+ return did_succeed_;
+ }
+
+ private:
+ CGLContextObj old_context_;
+ bool did_succeed_;
+};
+
+} // namespace
+
+static bool BindImageToTexture(CGLContextObj context,
+ CVImageBufferRef image,
+ uint32 texture_id) {
+ ScopedContextSetter scoped_context_setter(context);
+ if (!scoped_context_setter.did_succeed()) {
+ DLOG(ERROR) << "Unable to set OpenGL context.";
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 s/DLOG(ERROR)/DVLOG(1)/ here and everywhere below
sail 2012/05/29 03:45:01 Done.
+ return false;
+ }
+
+ IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
+ DCHECK(io_surface_support);
+
+ CFTypeRef io_surface =
+ io_surface_support->CVPixelBufferGetIOSurface(image);
+ if (!io_surface) {
+ DLOG(ERROR) << "Unable to get IOSurface for CVPixelBuffer.";
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 I'm afraid I wasn't clear before. The problem wit
sail 2012/05/29 03:45:01 Done. To mark the MacVDA instance unusable I'm ju
sail 2012/05/29 23:22:57 Actually, I changed this to check for a client_ in
+ return false;
+ }
+
+ glEnable(GL_TEXTURE_RECTANGLE_ARB);
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id);
+ if (io_surface_support->CGLTexImageIOSurface2D(
+ context,
+ GL_TEXTURE_RECTANGLE_ARB,
+ GL_RGB,
+ io_surface_support->IOSurfaceGetWidth(io_surface),
+ io_surface_support->IOSurfaceGetHeight(io_surface),
+ GL_YCBCR_422_APPLE,
+ GL_UNSIGNED_SHORT_8_8_APPLE,
+ io_surface,
+ 0) != kCGLNoError) {
+ DLOG(ERROR) << "Failed to bind image to texture.";
+ return false;
+ }
+ return glGetError() == GL_NO_ERROR;
+}
+
+MacVideoDecodeAccelerator::MacVideoDecodeAccelerator(
+ media::VideoDecodeAccelerator::Client* client)
+ : client_(client),
+ cgl_context_(NULL),
+ nalu_len_field_size_(0),
+ did_request_pictures_(false) {
+}
+
+void MacVideoDecodeAccelerator::SetGLContext(void* gl_context) {
+ DCHECK(CalledOnValidThread());
+ cgl_context_ = static_cast<CGLContextObj>(gl_context);
+}
+
+bool MacVideoDecodeAccelerator::SetConfigInfo(
+ uint32_t frame_width,
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 This config info includes width & height, and the
sail 2012/05/29 03:45:01 So taking a step back. All the Mac video API clie
Ami GONE FROM CHROMIUM 2012/05/30 00:29:58 Presumably you need to request different textures,
sail 2012/05/30 20:13:46 Sounds good. I've filed bug 130352 to track this.
+ uint32_t frame_height,
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 Is your plan to land this first and then immediate
sail 2012/05/29 03:45:01 I'm planning to land the various CLs one after ano
+ const std::vector<uint8_t>& avc_data) {
+ DCHECK(CalledOnValidThread());
+ frame_size_ = gfx::Size(frame_width, frame_height);
+ nalu_len_field_size_ = (avc_data[4] & 0x03) + 1;
+
+ DCHECK(!vda_support_.get());
+ vda_support_ = new gfx::VideoDecodeAccelerationSupport();
+ return vda_support_->Create(frame_size_.width(), frame_size_.height(),
+ kCVPixelFormatType_422YpCbCr8, &avc_data.front(), avc_data.size()) ==
+ gfx::VideoDecodeAccelerationSupport::SUCCESS;
+}
+
+bool MacVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile) {
+ DCHECK(CalledOnValidThread());
+
+ IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
+ if (!io_surface_support)
+ return false;
+
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &MacVideoDecodeAccelerator::NotifyInitializeDone, this));
+ return true;
+}
+
+void MacVideoDecodeAccelerator::Decode(
+ const media::BitstreamBuffer& bitstream_buffer) {
+ DCHECK(CalledOnValidThread());
+ base::SharedMemory memory(bitstream_buffer.handle(), true);
+ if (!memory.Map(bitstream_buffer.size())) {
+ LOG(ERROR) << "Failed to SharedMemory::Map().";
+ if (client_)
+ client_->NotifyError(UNREADABLE_INPUT);
+ return;
+ }
+
+ size_t buffer_size = bitstream_buffer.size();
+ if (buffer_size < nalu_len_field_size_ + 1) {
+ LOG(ERROR) << "Bitstream contains invalid data.";
+ if (client_)
+ client_->NotifyError(INVALID_ARGUMENT);
+ return;
+ }
+
+ // The decoder can only handle slice types 1-5.
+ const uint8_t* buffer = static_cast<const uint8_t*>(memory.memory());
+ uint8_t nalu_type = buffer[nalu_len_field_size_] & 0x1f;
+ if (nalu_type < 1 || nalu_type > 5) {
+ if (client_)
+ client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id());
+ return;
+ }
+
+ // Keep a ref counted copy of the buffer.
+ std::vector<uint8_t> vbuffer(buffer, buffer + buffer_size);
+ scoped_refptr<base::RefCountedBytes> bytes(
+ base::RefCountedBytes::TakeVector(&vbuffer));
+
+ // Store the buffer size at the beginning of the buffer as the decoder
+ // expects.
+ size_t frame_buffer_size = buffer_size - nalu_len_field_size_;
+ DCHECK(nalu_len_field_size_ <= 4);
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 DCHECK_LE
sail 2012/05/29 03:45:01 Done.
+ uint64_t max_frame_buffer_size = (1llu << (nalu_len_field_size_ * 8)) - 1;
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 s/uint64/int64/ s/llu/LL/
sail 2012/05/29 03:45:01 Done.
+ if (frame_buffer_size > max_frame_buffer_size) {
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 FWIW, writing this as if (frame_buffer_size >> (n
sail 2012/05/29 03:45:01 Unfortunately that overflows when nalu_len_field_s
Ami GONE FROM CHROMIUM 2012/05/30 00:29:58 WDYM "overflows"?
sail 2012/05/30 20:13:46 Sorry, not sure what the correct term is. If I do:
+ LOG(ERROR) << "Bitstream is too large.";
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 s/is/buffer is/
sail 2012/05/29 03:45:01 Done.
+ if (client_)
+ client_->NotifyError(INVALID_ARGUMENT);
+ return;
+ }
+ for (size_t i = 0; i < nalu_len_field_size_; ++i) {
+ size_t shift = nalu_len_field_size_ * 8 - (i + 1) * 8;
+ bytes->data()[i] = (frame_buffer_size >> shift) & 0xff;
+ }
+
+ if (vda_support_) {
+ vda_support_->Decode(bytes->front(), bytes->size(),
+ base::Bind(&MacVideoDecodeAccelerator::OnFrameReady,
+ this, bitstream_buffer.id(), bytes));
+ }
+
+ if (!did_request_pictures_) {
+ did_request_pictures_ = true;
+ if (client_)
+ client_->ProvidePictureBuffers(kNumPictureBuffers, frame_size_);
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 Again needs to be out-of-line to avoid re-entrancy
sail 2012/05/29 03:45:01 Done.
+ }
+}
+
+void MacVideoDecodeAccelerator::AssignPictureBuffers(
+ const std::vector<media::PictureBuffer>& buffers) {
+ DCHECK(CalledOnValidThread());
+ available_pictures_.insert(
+ available_pictures_.end(), buffers.begin(), buffers.end());
+ SendImages();
+}
+
+void MacVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
+ DCHECK(CalledOnValidThread());
+ std::map<int32, UsedPictureInfo>::iterator it =
+ used_pictures_.find(picture_buffer_id);
+ if (it == used_pictures_.end()) {
+ LOG(ERROR) << "Missing picture buffer id: " << picture_buffer_id;
+ if (client_)
+ client_->NotifyError(INVALID_ARGUMENT);
+ return;
+ }
+ UsedPictureInfo info = it->second;
+ used_pictures_.erase(it);
+ available_pictures_.push_back(info.picture_buffer);
+ SendImages();
+}
+
+void MacVideoDecodeAccelerator::Flush() {
+ DCHECK(CalledOnValidThread());
+ if (vda_support_)
+ vda_support_->Flush(true);
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 I'm surprised this can be done synchronously. In
sail 2012/05/29 03:45:01 Even though the flush is synchronous the decoded i
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &MacVideoDecodeAccelerator::NotifyFlushDone, this));
+}
+
+void MacVideoDecodeAccelerator::Reset() {
+ DCHECK(CalledOnValidThread());
+ if (vda_support_)
+ vda_support_->Flush(false);
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+ &MacVideoDecodeAccelerator::NotifyResetDone, this));
+}
+
+void MacVideoDecodeAccelerator::Destroy() {
+ DCHECK(CalledOnValidThread());
+ if (vda_support_) {
+ vda_support_->Destroy();
+ vda_support_ = NULL;
+ }
+ client_ = NULL;
+}
+
+MacVideoDecodeAccelerator::~MacVideoDecodeAccelerator() {
+ DCHECK(CalledOnValidThread());
+ Destroy();
+}
+
+void MacVideoDecodeAccelerator::OnFrameReady(
+ int32 bitstream_buffer_id,
+ scoped_refptr<base::RefCountedBytes> bytes,
+ CVImageBufferRef image,
+ int status) {
+ DCHECK(CalledOnValidThread());
+ if (image) {
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 Is this failing not a cause for notifying the clie
sail 2012/05/29 03:45:01 The image can be NULL if Reset() is called. I adde
+ DecodedImageInfo info;
+ info.image.reset(image, base::mac::RETAIN);
+ info.bitstream_buffer_id = bitstream_buffer_id;
+ decoded_images_.push_back(info);
+ SendImages();
+ }
+ if (client_)
+ client_->NotifyEndOfBitstreamBuffer(bitstream_buffer_id);
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 Add // TODO(sail): this assumes Decode() is handed
sail 2012/05/29 03:45:01 Done.
+}
+
+void MacVideoDecodeAccelerator::SendImages() {
+ if (!client_)
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 In this case do you not want to clear the queues?
sail 2012/05/29 03:45:01 I added code to clear the decoded image queue in D
+ return;
+
+ while (available_pictures_.size() && decoded_images_.size()) {
+ DecodedImageInfo info = decoded_images_.front();
+ decoded_images_.pop_front();
+ media::PictureBuffer picture_buffer = available_pictures_.front();
+ available_pictures_.pop_front();
+
+ if (!BindImageToTexture(cgl_context_, info.image,
+ picture_buffer.texture_id())) {
+ LOG(ERROR) << "Error binding image to texture.";
+ client_->NotifyError(PLATFORM_FAILURE);
+ return;
+ }
+
+ used_pictures_.insert(std::pair<int, UsedPictureInfo>(
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 what's the problem if you replace std::pair<int, U
sail 2012/05/29 03:45:01 Done.
+ picture_buffer.id(), UsedPictureInfo(picture_buffer, info.image)));
+ client_->PictureReady(
+ media::Picture(picture_buffer.id(), info.bitstream_buffer_id));
+ }
+}
+
+void MacVideoDecodeAccelerator::NotifyInitializeDone() {
+ if (client_)
+ client_->NotifyInitializeDone();
+}
+
+void MacVideoDecodeAccelerator::NotifyFlushDone() {
+ if (client_)
+ client_->NotifyFlushDone();
Ami GONE FROM CHROMIUM 2012/05/24 21:30:30 In that case in what sense is the flush "done"???
sail 2012/05/29 03:45:01 So it looks like there are a few solutions: #1
Ami GONE FROM CHROMIUM 2012/05/30 00:29:58 This is the only option that's compatible with the
sail 2012/05/30 20:13:46 Sounds good. I've filed bug 130357 to track this w
+}
+
+void MacVideoDecodeAccelerator::NotifyResetDone() {
+ decoded_images_.clear();
+ if (client_)
+ client_->NotifyResetDone();
+}
+
+MacVideoDecodeAccelerator::UsedPictureInfo::UsedPictureInfo(
+ const media::PictureBuffer& pic,
+ const base::mac::ScopedCFTypeRef<CVImageBufferRef>& image)
+ : picture_buffer(pic),
+ image(image, base::mac::RETAIN) {
+}
+
+MacVideoDecodeAccelerator::UsedPictureInfo::~UsedPictureInfo() {
+}
+
+MacVideoDecodeAccelerator::DecodedImageInfo::DecodedImageInfo() {
+}
+
+MacVideoDecodeAccelerator::DecodedImageInfo::~DecodedImageInfo() {
+}

Powered by Google App Engine
This is Rietveld 408576698