blob: c3b44b81d02e45e1290531d2638d76f06d8046ff [file] [log] [blame]
// 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.
#ifndef COBALT_MEDIA_BASE_VIDEO_FRAME_H_
#define COBALT_MEDIA_BASE_VIDEO_FRAME_H_
#include <memory>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/md5.h"
#include "base/memory/shared_memory.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"
#include "cobalt/media/base/color_space.h"
#include "cobalt/media/base/video_frame_metadata.h"
#include "cobalt/media/base/video_types.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "starboard/types.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#if defined(OS_MACOSX)
#include <CoreVideo/CVPixelBuffer.h>
#include "base/mac/scoped_cftyperef.h"
#endif
namespace cobalt {
namespace media {
class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
public:
enum {
kFrameSizeAlignment = 16,
kFrameSizePadding = 16,
kFrameAddressAlignment = 32
};
enum {
kMaxPlanes = 4,
kYPlane = 0,
kARGBPlane = kYPlane,
kUPlane = 1,
kUVPlane = kUPlane,
kVPlane = 2,
kAPlane = 3,
};
// Defines the pixel storage type. Differentiates between directly accessible
// |data_| and pixels that are only indirectly accessible and not via mappable
// memory.
// Note that VideoFrames of any StorageType can also have Texture backing,
// with "classical" GPU Driver-only textures identified as STORAGE_OPAQUE.
enum StorageType {
STORAGE_UNKNOWN = 0,
STORAGE_OPAQUE = 1, // We don't know how VideoFrame's pixels are stored.
STORAGE_UNOWNED_MEMORY = 2, // External, non owned data pointers.
STORAGE_OWNED_MEMORY = 3, // VideoFrame has allocated its own data buffer.
STORAGE_SHMEM = 4, // Pixels are backed by Shared Memory.
#if defined(OS_LINUX)
// TODO(mcasas): Consider turning this type into STORAGE_NATIVE or another
// meaningful name and handle it appropriately in all cases.
STORAGE_DMABUFS = 5, // Each plane is stored into a DmaBuf.
#endif
STORAGE_GPU_MEMORY_BUFFERS = 6,
STORAGE_MOJO_SHARED_BUFFER = 7,
STORAGE_LAST = STORAGE_MOJO_SHARED_BUFFER,
};
// CB to be called on the mailbox backing this frame when the frame is
// destroyed.
typedef base::Callback<void(const gpu::SyncToken&)> ReleaseMailboxCB;
// Interface representing client operations on a SyncToken, i.e. insert one in
// the GPU Command Buffer and wait for it.
class SyncTokenClient {
public:
SyncTokenClient() {}
virtual void GenerateSyncToken(gpu::SyncToken* sync_token) = 0;
virtual void WaitSyncToken(const gpu::SyncToken& sync_token) = 0;
protected:
virtual ~SyncTokenClient() {}
private:
DISALLOW_COPY_AND_ASSIGN(SyncTokenClient);
};
// Call prior to CreateFrame to ensure validity of frame configuration. Called
// automatically by VideoDecoderConfig::IsValidConfig().
static bool IsValidConfig(VideoPixelFormat format, StorageType storage_type,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size);
// Creates a new YUV frame in system memory with given parameters (|format|
// must be YUV). Buffers for the frame are allocated but not initialized. The
// caller most not make assumptions about the actual underlying size(s), but
// check the returned VideoFrame instead.
// TODO(mcasas): implement the RGB version of this factory method.
static scoped_refptr<VideoFrame> CreateFrame(VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp);
// Offers the same functionality as CreateFrame, and additionally zeroes out
// the initial allocated buffers.
static scoped_refptr<VideoFrame> CreateZeroInitializedFrame(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
base::TimeDelta timestamp);
// Wraps a set of native textures with a VideoFrame.
// |mailbox_holders_release_cb| will be called with a sync token as the
// argument when the VideoFrame is to be destroyed.
static scoped_refptr<VideoFrame> WrapNativeTextures(
VideoPixelFormat format,
const gpu::MailboxHolder(&mailbox_holder)[kMaxPlanes],
const ReleaseMailboxCB& mailbox_holders_release_cb,
const gfx::Size& coded_size, const gfx::Rect& visible_rect,
const gfx::Size& natural_size, base::TimeDelta timestamp);
// Wraps packed image data residing in a memory buffer with a VideoFrame.
// The image data resides in |data| and is assumed to be packed tightly in a
// buffer of logical dimensions |coded_size| with the appropriate bit depth
// and plane count as given by |format|. Returns NULL on failure.
static scoped_refptr<VideoFrame> WrapExternalData(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
uint8_t* data, size_t data_size, base::TimeDelta timestamp);
// Same as WrapExternalData() with SharedMemoryHandle and its offset.
static scoped_refptr<VideoFrame> WrapExternalSharedMemory(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
uint8_t* data, size_t data_size, base::SharedMemoryHandle handle,
size_t shared_memory_offset, base::TimeDelta timestamp);
// Wraps external YUV data of the given parameters with a VideoFrame.
// The returned VideoFrame does not own the data passed in.
static scoped_refptr<VideoFrame> WrapExternalYuvData(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
int32_t y_stride, int32_t u_stride, int32_t v_stride, uint8_t* y_data,
uint8_t* u_data, uint8_t* v_data, base::TimeDelta timestamp);
// Wraps external YUV data with the given parameters with a VideoFrame.
// The returned VideoFrame does not own the GpuMemoryBuffers passed in.
static scoped_refptr<VideoFrame> WrapExternalYuvGpuMemoryBuffers(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
int32_t y_stride, int32_t u_stride, int32_t v_stride, uint8_t* y_data,
uint8_t* u_data, uint8_t* v_data,
const gfx::GpuMemoryBufferHandle& y_handle,
const gfx::GpuMemoryBufferHandle& u_handle,
const gfx::GpuMemoryBufferHandle& v_handle, base::TimeDelta timestamp);
// Wraps external YUVA data of the given parameters with a VideoFrame.
// The returned VideoFrame does not own the data passed in.
static scoped_refptr<VideoFrame> WrapExternalYuvaData(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
int32_t y_stride, int32_t u_stride, int32_t v_stride, int32_t a_stride,
uint8_t* y_data, uint8_t* u_data, uint8_t* v_data, uint8_t* a_data,
base::TimeDelta timestamp);
#if defined(OS_LINUX)
// Wraps provided dmabufs
// (https://www.kernel.org/doc/Documentation/dma-buf-sharing.txt) with a
// VideoFrame. The dmabuf fds are dup()ed on creation, so that the VideoFrame
// retains a reference to them, and are automatically close()d on destruction,
// dropping the reference. The caller may safely close() its reference after
// calling WrapExternalDmabufs().
// The image data is only accessible via dmabuf fds, which are usually passed
// directly to a hardware device and/or to another process, or can also be
// mapped via mmap() for CPU access.
// Returns NULL on failure.
static scoped_refptr<VideoFrame> WrapExternalDmabufs(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
const std::vector<int>& dmabuf_fds, base::TimeDelta timestamp);
#endif
#if defined(OS_MACOSX)
// Wraps a provided CVPixelBuffer with a VideoFrame. The pixel buffer is
// retained for the lifetime of the VideoFrame and released upon destruction.
// The image data is only accessible via the pixel buffer, which could be
// backed by an IOSurface from another process. All the attributes of the
// VideoFrame are derived from the pixel buffer, with the exception of the
// timestamp. If information is missing or is incompatible (for example, a
// pixel format that has no VideoFrame match), NULL is returned.
// http://crbug.com/401308
static scoped_refptr<VideoFrame> WrapCVPixelBuffer(
CVPixelBufferRef cv_pixel_buffer, base::TimeDelta timestamp);
#endif
// Wraps |frame|. |visible_rect| must be a sub rect within
// frame->visible_rect().
static scoped_refptr<VideoFrame> WrapVideoFrame(
const scoped_refptr<VideoFrame>& frame, VideoPixelFormat format,
const gfx::Rect& visible_rect, const gfx::Size& natural_size);
// Creates a frame which indicates end-of-stream.
static scoped_refptr<VideoFrame> CreateEOSFrame();
// Allocates YV12 frame based on |size|, and sets its data to the YUV(y,u,v).
static scoped_refptr<VideoFrame> CreateColorFrame(const gfx::Size& size,
uint8_t y, uint8_t u,
uint8_t v,
base::TimeDelta timestamp);
// Allocates YV12 frame based on |size|, and sets its data to the YUV
// equivalent of RGB(0,0,0).
static scoped_refptr<VideoFrame> CreateBlackFrame(const gfx::Size& size);
// Allocates YV12A frame based on |size|, and sets its data to the YUVA
// equivalent of RGBA(0,0,0,0).
static scoped_refptr<VideoFrame> CreateTransparentFrame(
const gfx::Size& size);
static size_t NumPlanes(VideoPixelFormat format);
// Returns the required allocation size for a (tightly packed) frame of the
// given coded size and format.
static size_t AllocationSize(VideoPixelFormat format,
const gfx::Size& coded_size);
// Returns the plane gfx::Size (in bytes) for a plane of the given coded size
// and format.
static gfx::Size PlaneSize(VideoPixelFormat format, size_t plane,
const gfx::Size& coded_size);
// Returns horizontal bits per pixel for given |plane| and |format|.
static int PlaneHorizontalBitsPerPixel(VideoPixelFormat format, size_t plane);
// Returns bits per pixel for given |plane| and |format|.
static int PlaneBitsPerPixel(VideoPixelFormat format, size_t plane);
// Returns the number of bytes per row for the given plane, format, and width.
// The width may be aligned to format requirements.
static size_t RowBytes(size_t plane, VideoPixelFormat format, int width);
// Returns the number of rows for the given plane, format, and height.
// The height may be aligned to format requirements.
static size_t Rows(size_t plane, VideoPixelFormat format, int height);
// Returns the number of columns for the given plane, format, and width.
// The width may be aligned to format requirements.
static size_t Columns(size_t plane, VideoPixelFormat format, int width);
// Used to keep a running hash of seen frames. Expects an initialized MD5
// context. Calls MD5Update with the context and the contents of the frame.
static void HashFrameForTesting(base::MD5Context* context,
const scoped_refptr<VideoFrame>& frame);
// Returns true if |frame| is accessible and mapped in the VideoFrame memory
// space. If false, clients should refrain from accessing data(),
// visible_data() etc.
bool IsMappable() const;
// Returns true if |frame| has textures with any StorageType and should not be
// accessed via data(), visible_data() etc.
bool HasTextures() const;
// Returns the color space of this frame's content.
gfx::ColorSpace ColorSpace() const;
void set_color_space(const gfx::ColorSpace& color_space);
VideoPixelFormat format() const { return format_; }
StorageType storage_type() const { return storage_type_; }
const gfx::Size& coded_size() const { return coded_size_; }
const gfx::Rect& visible_rect() const { return visible_rect_; }
const gfx::Size& natural_size() const { return natural_size_; }
int stride(size_t plane) const;
// Returns the number of bytes per row and number of rows for a given plane.
//
// As opposed to stride(), row_bytes() refers to the bytes representing
// frame data scanlines (coded_size.width() pixels, without stride padding).
int row_bytes(size_t plane) const;
int rows(size_t plane) const;
// Returns pointer to the buffer for a given plane, if this is an
// IsMappable() frame type. The memory is owned by VideoFrame object and must
// not be freed by the caller.
const uint8_t* data(size_t plane) const;
uint8_t* data(size_t plane);
// Returns pointer to the data in the visible region of the frame, for
// IsMappable() storage types. The returned pointer is offsetted into the
// plane buffer specified by visible_rect().origin(). Memory is owned by
// VideoFrame object and must not be freed by the caller.
const uint8_t* visible_data(size_t plane) const;
uint8_t* visible_data(size_t plane);
// Returns a mailbox holder for a given texture.
// Only valid to call if this is a NATIVE_TEXTURE frame. Before using the
// mailbox, the caller must wait for the included sync point.
const gpu::MailboxHolder& mailbox_holder(size_t texture_index) const;
// Returns the shared-memory handle, if present
base::SharedMemoryHandle shared_memory_handle() const;
// Returns the offset into the shared memory where the frame data begins.
size_t shared_memory_offset() const;
// Returns the vector of GpuMemoryBuffer handles, if present.
const std::vector<gfx::GpuMemoryBufferHandle>& gpu_memory_buffer_handles()
const;
#if defined(OS_LINUX)
// Returns backing DmaBuf file descriptor for given |plane|, if present, or
// -1 if not.
// TODO(mcasas): Rename to DmabufFd() to comply with Style Guide.
int dmabuf_fd(size_t plane) const;
// Duplicates internally the |fds_in|, overwriting the current ones. Returns
// false if something goes wrong, and leaves all internal fds closed.
bool DuplicateFileDescriptors(const std::vector<int>& fds_in);
#endif
void AddSharedMemoryHandle(base::SharedMemoryHandle handle);
#if defined(OS_MACOSX)
// Returns the backing CVPixelBuffer, if present.
// TODO(mcasas): Rename to CvPixelBuffer() to comply with Style Guide.
CVPixelBufferRef cv_pixel_buffer() const;
#endif
// Adds a callback to be run when the VideoFrame is about to be destroyed.
// The callback may be run from ANY THREAD, and so it is up to the client to
// ensure thread safety. Although read-only access to the members of this
// VideoFrame is permitted while the callback executes (including
// VideoFrameMetadata), clients should not assume the data pointers are
// valid.
void AddDestructionObserver(const base::Closure& callback);
// Returns a dictionary of optional metadata. This contains information
// associated with the frame that downstream clients might use for frame-level
// logging, quality/performance optimizations, signaling, etc.
//
// TODO(miu): Move some of the "extra" members of VideoFrame (below) into
// here as a later clean-up step.
const VideoFrameMetadata* metadata() const { return &metadata_; }
VideoFrameMetadata* metadata() { return &metadata_; }
// The time span between the current frame and the first frame of the stream.
// This is the media timestamp, and not the reference time.
// See VideoFrameMetadata::REFERENCE_TIME for details.
base::TimeDelta timestamp() const { return timestamp_; }
void set_timestamp(base::TimeDelta timestamp) { timestamp_ = timestamp; }
// It uses |client| to insert a new sync point and potentially waits on a
// older sync point. The final sync point will be used to release this
// VideoFrame.
// This method is thread safe. Both blink and compositor threads can call it.
void UpdateReleaseSyncToken(SyncTokenClient* client);
// Returns a human-readable string describing |*this|.
std::string AsHumanReadableString();
// Unique identifier for this video frame; generated at construction time and
// guaranteed to be unique within a single process.
int unique_id() const { return unique_id_; }
protected:
friend class base::RefCountedThreadSafe<VideoFrame>;
// Clients must use the static factory/wrapping methods to create a new frame.
// Derived classes should create their own factory/wrapping methods, and use
// this constructor to do basic initialization.
VideoFrame(VideoPixelFormat format, StorageType storage_type,
const gfx::Size& coded_size, const gfx::Rect& visible_rect,
const gfx::Size& natural_size, base::TimeDelta timestamp);
virtual ~VideoFrame();
// Creates a summary of the configuration settings provided as parameters.
static std::string ConfigToString(const VideoPixelFormat format,
const VideoFrame::StorageType storage_type,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size);
// Returns true if |plane| is a valid plane index for the given |format|.
static bool IsValidPlane(size_t plane, VideoPixelFormat format);
// Returns |dimensions| adjusted to appropriate boundaries based on |format|.
static gfx::Size DetermineAlignedSize(VideoPixelFormat format,
const gfx::Size& dimensions);
void set_data(size_t plane, uint8_t* ptr);
void set_stride(size_t plane, int stride);
private:
// Clients must use the static factory/wrapping methods to create a new frame.
VideoFrame(VideoPixelFormat format, StorageType storage_type,
const gfx::Size& coded_size, const gfx::Rect& visible_rect,
const gfx::Size& natural_size, base::TimeDelta timestamp,
base::SharedMemoryHandle handle, size_t shared_memory_offset);
VideoFrame(VideoPixelFormat format, StorageType storage_type,
const gfx::Size& coded_size, const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
const ReleaseMailboxCB& mailbox_holder_release_cb,
base::TimeDelta timestamp);
static scoped_refptr<VideoFrame> WrapExternalStorage(
VideoPixelFormat format, StorageType storage_type,
const gfx::Size& coded_size, const gfx::Rect& visible_rect,
const gfx::Size& natural_size, uint8_t* data, size_t data_size,
base::TimeDelta timestamp, base::SharedMemoryHandle handle,
size_t data_offset);
static scoped_refptr<VideoFrame> CreateFrameInternal(
VideoPixelFormat format, const gfx::Size& coded_size,
const gfx::Rect& visible_rect, const gfx::Size& natural_size,
base::TimeDelta timestamp, bool zero_initialize_memory);
// Returns the pixel size of each subsample for a given |plane| and |format|.
// E.g. 2x2 for the U-plane in PIXEL_FORMAT_I420.
static gfx::Size SampleSize(VideoPixelFormat format, size_t plane);
// Returns the number of bytes per element for given |plane| and |format|.
static int BytesPerElement(VideoPixelFormat format, size_t plane);
// Return the alignment for the whole frame, calculated as the max of the
// alignment for each individual plane.
static gfx::Size CommonAlignment(VideoPixelFormat format);
void AllocateYUV(bool zero_initialize_memory);
// Frame format.
const VideoPixelFormat format_;
// Storage type for the different planes.
StorageType storage_type_; // TODO(mcasas): make const
// Width and height of the video frame, in pixels. This must include pixel
// data for the whole image; i.e. for YUV formats with subsampled chroma
// planes, in the case that the visible portion of the image does not line up
// on a sample boundary, |coded_size_| must be rounded up appropriately and
// the pixel data provided for the odd pixels.
const gfx::Size coded_size_;
// Width, height, and offsets of the visible portion of the video frame. Must
// be a subrect of |coded_size_|. Can be odd with respect to the sample
// boundaries, e.g. for formats with subsampled chroma.
const gfx::Rect visible_rect_;
// Width and height of the visible portion of the video frame
// (|visible_rect_.size()|) with aspect ratio taken into account.
const gfx::Size natural_size_;
// 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];
// Array of data pointers to each plane.
// TODO(mcasas): we don't know on ctor if we own |data_| or not. Change
// to std::unique_ptr<uint8_t, AlignedFreeDeleter> after refactoring
// VideoFrame.
uint8_t* data_[kMaxPlanes];
// Native texture mailboxes, if this is a IsTexture() frame.
gpu::MailboxHolder mailbox_holders_[kMaxPlanes];
ReleaseMailboxCB mailbox_holders_release_cb_;
// Shared memory handle and associated offset inside it, if this frame is
// a STORAGE_SHMEM one.
base::SharedMemoryHandle shared_memory_handle_;
size_t shared_memory_offset_;
// GpuMemoryBuffer handles attached to the video_frame.
std::vector<gfx::GpuMemoryBufferHandle> gpu_memory_buffer_handles_;
#if defined(OS_LINUX)
// Dmabufs for each plane. If set, this frame has DmaBuf backing in some way.
base::ScopedFD dmabuf_fds_[kMaxPlanes];
#endif
#if defined(OS_MACOSX)
// CVPixelBuffer, if this frame is wrapping one.
base::ScopedCFTypeRef<CVPixelBufferRef> cv_pixel_buffer_;
#endif
std::vector<base::Closure> done_callbacks_;
base::TimeDelta timestamp_;
base::Lock release_sync_token_lock_;
gpu::SyncToken release_sync_token_;
VideoFrameMetadata metadata_;
// Generated at construction time.
const int unique_id_;
gfx::ColorSpace color_space_;
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoFrame);
};
} // namespace media
} // namespace cobalt
#endif // COBALT_MEDIA_BASE_VIDEO_FRAME_H_