Chromium Code Reviews| Index: content/common/gpu/media/video_decode_accelerator_mac.mm |
| diff --git a/content/common/gpu/media/video_decode_accelerator_mac.mm b/content/common/gpu/media/video_decode_accelerator_mac.mm |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c9a00e6d3344c9afccef37abc2f3006770eeb588 |
| --- /dev/null |
| +++ b/content/common/gpu/media/video_decode_accelerator_mac.mm |
| @@ -0,0 +1,273 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
|
Ami GONE FROM CHROMIUM
2012/05/15 17:55:22
I don't know anything about Objective-C so I'm goi
sail
2012/05/15 23:53:31
It would be great if you could look at the way I'm
|
| +// found in the LICENSE file. |
| + |
| +#include "content/common/gpu/media/video_decode_accelerator_mac.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_; |
| +}; |
| + |
| +bool BindImageToTexture(CGLContextObj context, |
| + CVImageBufferRef image, |
| + uint32 texture_id) { |
| + ScopedContextSetter scoped_context_setter(context); |
| + if (!scoped_context_setter.did_succeed()) |
| + return false; |
| + |
| + IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize(); |
| + if (!io_surface_support) |
| + return false; |
| + |
| + CFTypeRef io_surface = |
| + io_surface_support->CVPixelBufferGetIOSurface(image); |
| + if (!io_surface) |
| + 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) { |
| + return false; |
| + } |
| + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); |
| + glDisable(GL_TEXTURE_RECTANGLE_ARB); |
| + if (glGetError() != GL_NO_ERROR) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +VideoDecodeAcceleratorMac::VideoDecodeAcceleratorMac( |
| + media::VideoDecodeAccelerator::Client* client) |
| + : client_(client), |
| + cgl_context_(NULL), |
| + nalu_len_field_size_(0), |
| + frame_width_(0), |
| + frame_height_(0), |
| + did_request_pictures_(false) { |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::SetGLContext(void* gl_context) { |
| + cgl_context_ = static_cast<CGLContextObj>(gl_context); |
| +} |
| + |
| +bool VideoDecodeAcceleratorMac::SetConfigInfo( |
| + uint32_t frame_width, |
| + uint32_t frame_height, |
| + const std::vector<uint8_t>& avc_data) { |
| + frame_width_ = frame_width; |
| + frame_height_ = frame_height; |
| + nalu_len_field_size_ = (avc_data[4] & 0x03) + 1; |
| + |
| + DCHECK(!vda_.get()); |
| + vda_ = new gfx::VideoDecodeAccelerationSupport(); |
| + gfx::VideoDecodeAccelerationSupport::Status status = vda_->Create( |
| + frame_width_, frame_height_, |
| + kCVPixelFormatType_422YpCbCr8, &avc_data.front(), avc_data.size()); |
| + if (status != gfx::VideoDecodeAccelerationSupport::VDA_SUCCESS) |
| + return false; |
| + return true; |
| +} |
| + |
| +bool VideoDecodeAcceleratorMac::Initialize(media::VideoCodecProfile profile) { |
| + if (client_) |
| + client_->NotifyInitializeDone(); |
| + return true; |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::Decode( |
| + const media::BitstreamBuffer& bitstream_buffer) { |
| + base::SharedMemory memory(bitstream_buffer.handle(), true); |
| + if (!memory.Map(bitstream_buffer.size())) { |
| + CHECK(false); |
| + return; |
| + } |
| + |
| + size_t buffer_size = bitstream_buffer.size(); |
| + if (buffer_size < nalu_len_field_size_ + 1) { |
| + if (client_) |
| + client_->NotifyEndOfBitstreamBuffer(bitstream_buffer.id()); |
| + 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> vector(buffer, buffer + buffer_size); |
| + scoped_refptr<base::RefCountedBytes> bytes( |
| + base::RefCountedBytes::TakeVector(&vector)); |
| + |
| + // 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_; |
| + 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; |
| + } |
| + |
| + vda_->Decode(bytes->front(), bytes->size(), |
| + base::Bind(&VideoDecodeAcceleratorMac::OnFrameReady, this, |
| + bitstream_buffer.id(), bytes)); |
| + |
| + if (!did_request_pictures_) { |
| + did_request_pictures_ = true; |
| + if (client_) |
| + client_->ProvidePictureBuffers( |
| + kNumPictureBuffers, gfx::Size(frame_width_, frame_height_)); |
| + } |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::AssignPictureBuffers( |
| + const std::vector<media::PictureBuffer>& buffers) { |
| + available_pictures_.insert( |
| + available_pictures_.end(), buffers.begin(), buffers.end()); |
| + SendImages(); |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::ReusePictureBuffer(int32 picture_buffer_id) { |
| + std::map<int32, PendingPictureInfo>::iterator it = |
| + pending_pictures_.find(picture_buffer_id); |
| + DCHECK(it != pending_pictures_.end()); |
| + PendingPictureInfo info = it->second; |
| + pending_pictures_.erase(it); |
| + available_pictures_.push_back(info.picture_buffer); |
| + SendImages(); |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::Flush() { |
| + vda_->Flush(true); |
| + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| + &VideoDecodeAcceleratorMac::NotifyFlushDone, this)); |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::Reset() { |
| + vda_->Flush(false); |
| + MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
| + &VideoDecodeAcceleratorMac::NotifyResetDone, this)); |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::Destroy() { |
| + vda_->Destroy(); |
| + vda_ = NULL; |
| + client_ = NULL; |
| +} |
| + |
| +VideoDecodeAcceleratorMac::~VideoDecodeAcceleratorMac() { |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::OnFrameReady( |
| + int32 bitstream_buffer_id, |
| + scoped_refptr<base::RefCountedBytes> bytes, |
| + CVImageBufferRef image, |
| + int status) { |
| + if (image) { |
| + DecodedImageInfo info; |
| + info.image = image; |
| + info.bitstream_buffer_id = bitstream_buffer_id; |
| + decoded_images_.push_back(info); |
| + SendImages(); |
| + } |
| + if (client_) |
| + client_->NotifyEndOfBitstreamBuffer(bitstream_buffer_id); |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::SendImages() { |
| + if (available_pictures_.size() == 0 || decoded_images_.size() == 0) |
| + return; |
| + |
| + DecodedImageInfo info = decoded_images_.front(); |
| + decoded_images_.pop_front(); |
| + media::PictureBuffer picture_buffer = available_pictures_.front(); |
| + |
| + if (!BindImageToTexture(cgl_context_, info.image, |
| + picture_buffer.texture_id())) { |
| + LOG(ERROR) << "Error binding image to texture."; |
| + return; |
| + } |
| + |
| + available_pictures_.pop_front(); |
| + PendingPictureInfo pending_info(picture_buffer, info.image); |
| + pending_pictures_.insert(std::pair<int, PendingPictureInfo>( |
| + picture_buffer.id(), pending_info)); |
| + media::Picture picture(picture_buffer.id(), info.bitstream_buffer_id); |
| + if (client_) |
| + client_->PictureReady(picture); |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::NotifyFlushDone() { |
| + if (client_) |
| + client_->NotifyFlushDone(); |
| +} |
| + |
| +void VideoDecodeAcceleratorMac::NotifyResetDone() { |
| + decoded_images_.clear(); |
| + if (client_) |
| + client_->NotifyResetDone(); |
| +} |
| + |
| +VideoDecodeAcceleratorMac::PendingPictureInfo::PendingPictureInfo( |
| + media::PictureBuffer pic, |
| + CVImageBufferRef image) |
| + : picture_buffer(pic.id(), pic.size(), pic.texture_id()), |
| + image(image) { |
| +} |
| + |
| +VideoDecodeAcceleratorMac::PendingPictureInfo::~PendingPictureInfo() { |
| +} |
| + |
| +VideoDecodeAcceleratorMac::DecodedImageInfo::DecodedImageInfo() { |
| +} |
| + |
| +VideoDecodeAcceleratorMac::DecodedImageInfo::~DecodedImageInfo() { |
| +} |