Index: media/cdm/ppapi/cdm_helpers.h |
diff --git a/media/cdm/ppapi/cdm_helpers.h b/media/cdm/ppapi/cdm_helpers.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4e08510936254e22cfa8c316a9966365a13b3ffe |
--- /dev/null |
+++ b/media/cdm/ppapi/cdm_helpers.h |
@@ -0,0 +1,314 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
xhwang
2013/10/15 20:42:33
This file is copied from cdm_wrapper.cc, with smal
|
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifndef MEDIA_CDM_PPAPI_CDM_HELPERS_H_ |
+#define MEDIA_CDM_PPAPI_CDM_HELPERS_H_ |
+ |
+#include <map> |
+#include <utility> |
+ |
+#include "base/basictypes.h" |
+#include "base/compiler_specific.h" |
+#include "build/build_config.h" |
+#include "media/cdm/ppapi/api/content_decryption_module.h" |
+#include "ppapi/c/pp_errors.h" |
+#include "ppapi/c/pp_stdint.h" |
+#include "ppapi/cpp/core.h" |
+#include "ppapi/cpp/dev/buffer_dev.h" |
+#include "ppapi/cpp/instance.h" |
+#include "ppapi/cpp/logging.h" |
+#include "ppapi/cpp/module.h" |
xhwang
2013/10/15 20:42:33
Includes are trimmed to reflect what we use.
|
+ |
+namespace media { |
+ |
+// cdm::Buffer implementation that provides access to memory owned by a |
+// pp::Buffer_Dev. |
+// This class holds a reference to the Buffer_Dev throughout its lifetime. |
+// TODO(xhwang): Find a better name. It's confusing to have PpbBuffer, |
+// pp::Buffer_Dev and PPB_Buffer_Dev. |
+class PpbBuffer : public cdm::Buffer { |
+ public: |
+ static PpbBuffer* Create(const pp::Buffer_Dev& buffer, uint32_t buffer_id) { |
+ PP_DCHECK(buffer.data()); |
+ PP_DCHECK(buffer.size()); |
+ PP_DCHECK(buffer_id); |
+ return new PpbBuffer(buffer, buffer_id); |
+ } |
+ |
+ // cdm::Buffer implementation. |
+ virtual void Destroy() OVERRIDE { delete this; } |
+ |
+ virtual int32_t Capacity() const OVERRIDE { return buffer_.size(); } |
+ |
+ virtual uint8_t* Data() OVERRIDE { |
+ return static_cast<uint8_t*>(buffer_.data()); |
+ } |
+ |
+ virtual void SetSize(int32_t size) OVERRIDE { |
+ PP_DCHECK(size >= 0); |
+ PP_DCHECK(size < Capacity()); |
+ if (size < 0 || size > Capacity()) { |
+ size_ = 0; |
+ return; |
+ } |
+ |
+ size_ = size; |
+ } |
+ |
+ virtual int32_t Size() const OVERRIDE { return size_; } |
+ |
+ pp::Buffer_Dev buffer_dev() const { return buffer_; } |
+ |
+ uint32_t buffer_id() const { return buffer_id_; } |
+ |
+ private: |
+ PpbBuffer(pp::Buffer_Dev buffer, uint32_t buffer_id) |
+ : buffer_(buffer), |
+ buffer_id_(buffer_id), |
+ size_(0) {} |
+ virtual ~PpbBuffer() {} |
+ |
+ pp::Buffer_Dev buffer_; |
+ uint32_t buffer_id_; |
+ int32_t size_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PpbBuffer); |
+}; |
+ |
+class PpbBufferAllocator { |
+ public: |
+ explicit PpbBufferAllocator(pp::Instance* instance) |
+ : instance_(instance), |
+ next_buffer_id_(1) {} |
+ ~PpbBufferAllocator() {} |
+ |
+ cdm::Buffer* Allocate(int32_t capacity); |
+ |
+ // Releases the buffer with |buffer_id|. A buffer can be recycled after |
+ // it is released. |
+ void Release(uint32_t buffer_id); |
+ |
+ private: |
+ typedef std::map<uint32_t, pp::Buffer_Dev> AllocatedBufferMap; |
+ typedef std::multimap<int, std::pair<uint32_t, pp::Buffer_Dev> > |
+ FreeBufferMap; |
+ |
+ // Always pad new allocated buffer so that we don't need to reallocate |
+ // buffers frequently if requested sizes fluctuate slightly. |
+ static const int kBufferPadding = 512; |
+ |
+ // Maximum number of free buffers we can keep when allocating new buffers. |
+ static const int kFreeLimit = 3; |
+ |
+ pp::Buffer_Dev AllocateNewBuffer(int capacity); |
+ |
+ pp::Instance* const instance_; |
+ uint32_t next_buffer_id_; |
+ AllocatedBufferMap allocated_buffers_; |
+ FreeBufferMap free_buffers_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PpbBufferAllocator); |
+}; |
+ |
+cdm::Buffer* PpbBufferAllocator::Allocate(int32_t capacity) { |
+ PP_DCHECK(pp::Module::Get()->core()->IsMainThread()); |
xhwang
2013/10/15 20:42:33
This was IsMainThread() in cdm_wrapper.cc. I am no
|
+ |
+ if (capacity <= 0) |
+ return NULL; |
+ |
+ pp::Buffer_Dev buffer; |
+ uint32_t buffer_id = 0; |
+ |
+ // Reuse a buffer in the free list if there is one that fits |capacity|. |
+ // Otherwise, create a new one. |
+ FreeBufferMap::iterator found = free_buffers_.lower_bound(capacity); |
+ if (found == free_buffers_.end()) { |
+ // TODO(xhwang): Report statistics about how many new buffers are allocated. |
+ buffer = AllocateNewBuffer(capacity); |
+ if (buffer.is_null()) |
+ return NULL; |
+ buffer_id = next_buffer_id_++; |
+ } else { |
+ buffer = found->second.second; |
+ buffer_id = found->second.first; |
+ free_buffers_.erase(found); |
+ } |
+ |
+ allocated_buffers_.insert(std::make_pair(buffer_id, buffer)); |
+ |
+ return PpbBuffer::Create(buffer, buffer_id); |
+} |
+ |
+void PpbBufferAllocator::Release(uint32_t buffer_id) { |
+ if (!buffer_id) |
+ return; |
+ |
+ AllocatedBufferMap::iterator found = allocated_buffers_.find(buffer_id); |
+ if (found == allocated_buffers_.end()) |
+ return; |
+ |
+ pp::Buffer_Dev& buffer = found->second; |
+ free_buffers_.insert( |
+ std::make_pair(buffer.size(), std::make_pair(buffer_id, buffer))); |
+ |
+ allocated_buffers_.erase(found); |
+} |
+ |
+pp::Buffer_Dev PpbBufferAllocator::AllocateNewBuffer(int32_t capacity) { |
+ // Destroy the smallest buffer before allocating a new bigger buffer if the |
+ // number of free buffers exceeds a limit. This mechanism helps avoid ending |
+ // up with too many small buffers, which could happen if the size to be |
+ // allocated keeps increasing. |
+ if (free_buffers_.size() >= static_cast<uint32_t>(kFreeLimit)) |
+ free_buffers_.erase(free_buffers_.begin()); |
+ |
+ // Creation of pp::Buffer_Dev is expensive! It involves synchronous IPC calls. |
+ // That's why we try to avoid AllocateNewBuffer() as much as we can. |
+ return pp::Buffer_Dev(instance_, capacity + kBufferPadding); |
+} |
+ |
+class DecryptedBlockImpl : public cdm::DecryptedBlock { |
+ public: |
+ DecryptedBlockImpl() : buffer_(NULL), timestamp_(0) {} |
+ virtual ~DecryptedBlockImpl() { if (buffer_) buffer_->Destroy(); } |
+ |
+ virtual void SetDecryptedBuffer(cdm::Buffer* buffer) OVERRIDE { |
+ buffer_ = static_cast<PpbBuffer*>(buffer); |
+ } |
+ virtual cdm::Buffer* DecryptedBuffer() OVERRIDE { return buffer_; } |
+ |
+ virtual void SetTimestamp(int64_t timestamp) OVERRIDE { |
+ timestamp_ = timestamp; |
+ } |
+ virtual int64_t Timestamp() const OVERRIDE { return timestamp_; } |
+ |
+ private: |
+ PpbBuffer* buffer_; |
+ int64_t timestamp_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DecryptedBlockImpl); |
+}; |
+ |
+class VideoFrameImpl : public cdm::VideoFrame { |
+ public: |
+ VideoFrameImpl(); |
+ virtual ~VideoFrameImpl(); |
+ |
+ virtual void SetFormat(cdm::VideoFormat format) OVERRIDE { |
+ format_ = format; |
+ } |
+ virtual cdm::VideoFormat Format() const OVERRIDE { return format_; } |
+ |
+ virtual void SetSize(cdm::Size size) OVERRIDE { size_ = size; } |
+ virtual cdm::Size Size() const OVERRIDE { return size_; } |
+ |
+ virtual void SetFrameBuffer(cdm::Buffer* frame_buffer) OVERRIDE { |
+ frame_buffer_ = static_cast<PpbBuffer*>(frame_buffer); |
+ } |
+ virtual cdm::Buffer* FrameBuffer() OVERRIDE { return frame_buffer_; } |
+ |
+ virtual void SetPlaneOffset(cdm::VideoFrame::VideoPlane plane, |
+ int32_t offset) OVERRIDE { |
+ PP_DCHECK(0 <= plane && plane < kMaxPlanes); |
+ PP_DCHECK(offset >= 0); |
+ plane_offsets_[plane] = offset; |
+ } |
+ virtual int32_t PlaneOffset(VideoPlane plane) OVERRIDE { |
+ PP_DCHECK(0 <= plane && plane < kMaxPlanes); |
+ return plane_offsets_[plane]; |
+ } |
+ |
+ virtual void SetStride(VideoPlane plane, int32_t stride) OVERRIDE { |
+ PP_DCHECK(0 <= plane && plane < kMaxPlanes); |
+ strides_[plane] = stride; |
+ } |
+ virtual int32_t Stride(VideoPlane plane) OVERRIDE { |
+ PP_DCHECK(0 <= plane && plane < kMaxPlanes); |
+ return strides_[plane]; |
+ } |
+ |
+ virtual void SetTimestamp(int64_t timestamp) OVERRIDE { |
+ timestamp_ = timestamp; |
+ } |
+ virtual int64_t Timestamp() const OVERRIDE { return timestamp_; } |
+ |
+ private: |
+ // The video buffer format. |
+ cdm::VideoFormat format_; |
+ |
+ // Width and height of the video frame. |
+ cdm::Size size_; |
+ |
+ // The video frame buffer. |
+ PpbBuffer* frame_buffer_; |
+ |
+ // Array of data pointers to each plane in the video frame buffer. |
+ int32_t plane_offsets_[kMaxPlanes]; |
+ |
+ // Array of strides for each plane, typically greater or equal to the width |
+ // of the surface divided by the horizontal sampling period. Note that |
+ // strides can be negative. |
+ int32_t strides_[kMaxPlanes]; |
+ |
+ // Presentation timestamp in microseconds. |
+ int64_t timestamp_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(VideoFrameImpl); |
+}; |
+ |
+VideoFrameImpl::VideoFrameImpl() |
+ : format_(cdm::kUnknownVideoFormat), |
+ frame_buffer_(NULL), |
+ timestamp_(0) { |
+ for (int32_t i = 0; i < kMaxPlanes; ++i) { |
+ plane_offsets_[i] = 0; |
+ strides_[i] = 0; |
+ } |
+} |
+ |
+VideoFrameImpl::~VideoFrameImpl() { |
+ if (frame_buffer_) |
+ frame_buffer_->Destroy(); |
+} |
+ |
+class AudioFramesImpl : public cdm::AudioFrames_1, |
+ public cdm::AudioFrames_2 { |
+ public: |
+ AudioFramesImpl() : buffer_(NULL), format_(cdm::kAudioFormatS16) {} |
+ virtual ~AudioFramesImpl() { |
+ if (buffer_) |
+ buffer_->Destroy(); |
+ } |
+ |
+ // AudioFrames implementation. |
+ virtual void SetFrameBuffer(cdm::Buffer* buffer) OVERRIDE { |
+ buffer_ = static_cast<PpbBuffer*>(buffer); |
+ } |
+ virtual cdm::Buffer* FrameBuffer() OVERRIDE { |
+ return buffer_; |
+ } |
+ virtual void SetFormat(cdm::AudioFormat format) OVERRIDE { |
+ format_ = format; |
+ } |
+ virtual cdm::AudioFormat Format() const OVERRIDE { |
+ return format_; |
+ } |
+ |
+ cdm::Buffer* PassFrameBuffer() { |
xhwang
2013/10/15 20:42:33
Added this function so that we can transfer owners
|
+ PpbBuffer* temp_buffer = buffer_; |
+ if (buffer_) |
+ buffer_ = NULL; |
+ return temp_buffer; |
+ } |
+ |
+ private: |
+ PpbBuffer* buffer_; |
+ cdm::AudioFormat format_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AudioFramesImpl); |
+}; |
+ |
+} // namespace media |
+ |
+#endif // MEDIA_CDM_PPAPI_CDM_HELPERS_H_ |