| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef MEDIA_BASE_VIDEO_FRAME_H_ |
| #define MEDIA_BASE_VIDEO_FRAME_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <array> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/check_op.h" |
| #include "base/compiler_specific.h" |
| #include "base/containers/span.h" |
| #include "base/functional/callback.h" |
| #include "base/hash/md5.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/read_only_shared_memory_region.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/process/memory.h" |
| #include "base/synchronization/lock.h" |
| #include "base/thread_annotations.h" |
| #include "base/time/time.h" |
| #include "base/types/id_type.h" |
| #include "base/types/pass_key.h" |
| #include "base/unguessable_token.h" |
| #include "build/build_config.h" |
| #include "gpu/command_buffer/client/client_shared_image.h" |
| #include "gpu/command_buffer/common/mailbox_holder.h" |
| #include "gpu/ipc/common/vulkan_ycbcr_info.h" |
| #include "media/base/video_frame_layout.h" |
| #include "media/base/video_frame_metadata.h" |
| #include "media/base/video_types.h" |
| #include "ui/gfx/color_space.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/hdr_metadata.h" |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| #include "base/files/scoped_file.h" |
| #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| |
| namespace gfx { |
| class GpuMemoryBuffer; |
| struct GpuMemoryBufferHandle; |
| } |
| |
| namespace media { |
| |
| class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { |
| public: |
| REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); |
| |
| static constexpr size_t kFrameSizeAlignment = 16; |
| static constexpr size_t kFrameSizePadding = 16; |
| |
| static constexpr size_t kFrameAddressAlignment = |
| VideoFrameLayout::kBufferAddressAlignment; |
| |
| static constexpr size_t kMaxPlanes = 4; |
| |
| enum Plane : uint8_t { |
| kY = 0, |
| kARGB = kY, |
| kU = 1, |
| kUV = kU, |
| kV = 2, |
| kATriPlanar = kV, |
| kA = 3, |
| }; |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| // 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, // Backed by read-only shared memory. |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| STORAGE_DMABUFS = 5, // Each plane is stored into a DmaBuf. |
| #endif |
| STORAGE_GPU_MEMORY_BUFFER = 6, |
| STORAGE_MAX = STORAGE_GPU_MEMORY_BUFFER, |
| }; |
| |
| // CB to be called on the mailbox backing this frame and its GpuMemoryBuffers |
| // (if they exist) when the frame is destroyed. |
| using ReleaseMailboxCB = base::OnceCallback<void(const gpu::SyncToken&)>; |
| using ReleaseMailboxAndGpuMemoryBufferCB = |
| base::OnceCallback<void(const gpu::SyncToken&, |
| std::unique_ptr<gfx::GpuMemoryBuffer>)>; |
| |
| // Interface representing client operations on a SyncToken, i.e. insert one in |
| // the GPU Command Buffer and wait for it. |
| class SyncTokenClient { |
| public: |
| SyncTokenClient() = default; |
| SyncTokenClient(const SyncTokenClient&) = delete; |
| SyncTokenClient& operator=(const SyncTokenClient&) = delete; |
| |
| virtual void GenerateSyncToken(gpu::SyncToken* sync_token) = 0; |
| virtual void WaitSyncToken(const gpu::SyncToken& sync_token) = 0; |
| |
| protected: |
| virtual ~SyncTokenClient() = default; |
| }; |
| |
| // This class will allow VF clients to be able to get CPU mapped memory and |
| // other metadata either from GMB or MappableSI backing the VF. Note that this |
| // class will go away once GMB is removed. Clients can directly called a new |
| // method like VideoFrame::MapSharedImage() to get a |
| // ClientSharedImage::ScopedMapping object. |
| class MEDIA_EXPORT ScopedMapping { |
| public: |
| ~ScopedMapping(); |
| |
| // Returns a pointer to the beginning of the plane. |
| uint8_t* Memory(uint32_t plane_index); |
| |
| // Returns plane stride. |
| size_t Stride(uint32_t plane_index); |
| |
| // Returns the size of the buffer. |
| gfx::Size Size(); |
| |
| private: |
| friend class VideoFrame; |
| |
| ScopedMapping( |
| gfx::GpuMemoryBuffer* gpu_memory_buffer, |
| std::unique_ptr<gpu::ClientSharedImage::ScopedMapping> scoped_mapping); |
| |
| // RAW_PTR_EXCLUSION: Performance reasons (based on analysis of MotionMark). |
| RAW_PTR_EXCLUSION gfx::GpuMemoryBuffer* gpu_memory_buffer_ = nullptr; |
| std::unique_ptr<gpu::ClientSharedImage::ScopedMapping> scoped_mapping_; |
| }; |
| |
| enum class FrameControlType { |
| kNone, |
| kEos, |
| }; |
| |
| // Clients must use the static factory/wrapping methods to create a new frame. |
| VideoFrame(base::PassKey<VideoFrame>, |
| const VideoFrameLayout& layout, |
| StorageType storage_type, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::TimeDelta timestamp, |
| FrameControlType frame_control_type = FrameControlType::kNone); |
| VideoFrame() = delete; |
| VideoFrame(const VideoFrame&) = delete; |
| VideoFrame& operator=(const VideoFrame&) = delete; |
| |
| // Returns true if size is valid for a VideoFrame. |
| static bool IsValidSize(const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size); |
| |
| // Returns true if and only if the |size| is within limits, i.e., neither |
| // dimension exceeds limits::kMaxDimension and the total area doesn't exceed |
| // limits::kMaxCanvas. Prefer the overload that accepts the |coded_size|, |
| // |visible_rect|, and |natural_size| if trying to validate the VideoFrame. |
| static bool IsValidCodedSize(const gfx::Size& size); |
| |
| // Returns true if frame configuration is valid. |
| static bool IsValidConfig(VideoPixelFormat format, |
| StorageType storage_type, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size); |
| |
| // Compute a strided layout for `format` and `coded_size`, and return a fully |
| // specified layout including offsets and plane sizes. Except that VideoFrame |
| // knows how to compute plane sizes, this method should be in |
| // `VideoFrameLayout`, probably just folded into `CreateWithStrides()`. |
| static std::optional<VideoFrameLayout> CreateFullySpecifiedLayoutWithStrides( |
| VideoPixelFormat format, |
| const gfx::Size& coded_size); |
| |
| // Creates a new frame in system memory with given parameters. Buffers for the |
| // frame are allocated but not initialized. The caller must not make |
| // assumptions about the actual underlying size(s), but check the returned |
| // VideoFrame instead. |
| 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); |
| |
| // Used by Chromecast only. |
| // Create a new frame that doesn't contain any valid video content. This frame |
| // is meant to be sent to compositor to inform that the compositor should |
| // punch a transparent hole so the video underlay will be visible. |
| // |overlay_plane_id| is stored in metadata() as |tracking_token|. |
| static scoped_refptr<VideoFrame> CreateVideoHoleFrame( |
| const base::UnguessableToken& overlay_plane_id, |
| const gfx::Size& natural_size, |
| base::TimeDelta timestamp); |
| |
| // Creates a frame that doesn't contain any valid video content. |
| // |tracking_token| is stored in the frame metadata and can be used to look up |
| // the underlying resource or VideoFrame source. The resulting frame's |
| // |storage_type_| will be STORAGE_OPAQUE. |
| static scoped_refptr<VideoFrame> WrapTrackingToken( |
| VideoPixelFormat format, |
| const base::UnguessableToken& tracking_token, |
| 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); |
| |
| // Creates a new frame in system memory with given parameters. Buffers for the |
| // frame are allocated but not initialized. The caller should specify the |
| // physical buffer size and strides if needed in |layout| parameter. |
| static scoped_refptr<VideoFrame> CreateFrameWithLayout( |
| const VideoFrameLayout& layout, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::TimeDelta timestamp, |
| bool zero_initialize_memory); |
| |
| // Wraps a native texture shared image with a VideoFrame. |
| // |mailbox_holder_release_cb| will be called with a sync token as the |
| // argument when the VideoFrame is to be destroyed. |
| static scoped_refptr<VideoFrame> WrapSharedImage( |
| VideoPixelFormat format, |
| scoped_refptr<gpu::ClientSharedImage> shared_image, |
| gpu::SyncToken sync_token, |
| ReleaseMailboxCB mailbox_holder_release_cb, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::TimeDelta timestamp); |
| |
| // Wraps a mappable shared image with a VideoFrame. Mappable Shared Images are |
| // backed by CPU mappable gpu buffers or shared memory buffers. |
| // TODO(crbug.com/40263579): Once all VideoFrame clients are fully converted |
| // to use MappableSI, look into refactoring this method and |
| // ::WrapSharedImage() into one. |mailbox_holder_release_cb| will be called |
| // with a sync token as the argument when the VideoFrame is to be destroyed. |
| static scoped_refptr<VideoFrame> WrapMappableSharedImage( |
| scoped_refptr<gpu::ClientSharedImage> shared_image, |
| gpu::SyncToken sync_token, |
| ReleaseMailboxAndGpuMemoryBufferCB mailbox_holder_and_gmb_release_cb, |
| 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, |
| const uint8_t* data, |
| size_t data_size, |
| base::TimeDelta timestamp); |
| |
| static scoped_refptr<VideoFrame> WrapExternalData( |
| VideoPixelFormat format, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::span<const uint8_t> data, |
| base::TimeDelta timestamp); |
| |
| static scoped_refptr<VideoFrame> WrapExternalDataWithLayout( |
| const VideoFrameLayout& layout, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| const uint8_t* data, |
| size_t data_size, |
| base::TimeDelta timestamp); |
| |
| static scoped_refptr<VideoFrame> WrapExternalDataWithLayout( |
| const VideoFrameLayout& layout, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::span<const uint8_t> data, |
| 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, |
| size_t y_stride, |
| size_t u_stride, |
| size_t v_stride, |
| const uint8_t* y_data, |
| const uint8_t* u_data, |
| const uint8_t* v_data, |
| base::TimeDelta timestamp); |
| |
| static scoped_refptr<VideoFrame> WrapExternalYuvData( |
| VideoPixelFormat format, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| size_t y_stride, |
| size_t u_stride, |
| size_t v_stride, |
| base::span<const uint8_t> y_data, |
| base::span<const uint8_t> u_data, |
| base::span<const uint8_t> v_data, |
| base::TimeDelta timestamp); |
| |
| // Wraps external YUV data with VideoFrameLayout. The returned VideoFrame does |
| // not own the data passed in. |
| static scoped_refptr<VideoFrame> WrapExternalYuvDataWithLayout( |
| const VideoFrameLayout& layout, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| const uint8_t* y_data, |
| const uint8_t* u_data, |
| const uint8_t* v_data, |
| base::TimeDelta timestamp); |
| |
| static scoped_refptr<VideoFrame> WrapExternalYuvDataWithLayout( |
| const VideoFrameLayout& layout, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::span<const uint8_t> y_data, |
| base::span<const uint8_t> u_data, |
| base::span<const uint8_t> v_data, |
| 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, |
| size_t y_stride, |
| size_t u_stride, |
| size_t v_stride, |
| size_t a_stride, |
| const uint8_t* y_data, |
| const uint8_t* u_data, |
| const uint8_t* v_data, |
| const uint8_t* a_data, |
| base::TimeDelta timestamp); |
| |
| static scoped_refptr<VideoFrame> WrapExternalYuvaData( |
| VideoPixelFormat format, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| size_t y_stride, |
| size_t u_stride, |
| size_t v_stride, |
| size_t a_stride, |
| base::span<const uint8_t> y_data, |
| base::span<const uint8_t> u_data, |
| base::span<const uint8_t> v_data, |
| base::span<const uint8_t> a_data, |
| base::TimeDelta timestamp); |
| |
| // Wraps external NV12 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, |
| size_t y_stride, |
| size_t uv_stride, |
| const uint8_t* y_data, |
| const uint8_t* uv_data, |
| base::TimeDelta timestamp); |
| |
| // Wraps |gpu_memory_buffer|. This will transfer ownership of |
| // |gpu_memory_buffer| to the returned VideoFrame. |
| // For use in contexts where the GPUMemoryBuffer has no SharedImage |
| // associated with it. |
| // NOTE: Clients who want to set a callback on the VideoFrame being destroyed |
| // should call SetReleaseMailboxAndGpuMemoryBufferCB() after creating the |
| // VideoFrame via this entrypoint. |
| static scoped_refptr<VideoFrame> WrapExternalGpuMemoryBuffer( |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, |
| base::TimeDelta timestamp); |
| |
| // Wraps |gpu_memory_buffer| along with the shared image created from |
| // |gpu_memory_buffer|. This will transfer ownership of |gpu_memory_buffer| |
| // to the returned VideoFrame. |mailbox_holder_and_gmb_release_cb| will be |
| // called with a sync token and with |gpu_memory_buffer| as arguments when the |
| // VideoFrame is to be destroyed. |
| static scoped_refptr<VideoFrame> WrapExternalGpuMemoryBuffer( |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, |
| scoped_refptr<gpu::ClientSharedImage> shared_image, |
| const gpu::SyncToken& sync_token, |
| ReleaseMailboxAndGpuMemoryBufferCB mailbox_holder_and_gmb_release_cb, |
| base::TimeDelta timestamp); |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| // Wraps provided dmabufs |
| // (https://www.kernel.org/doc/html/latest/driver-api/dma-buf.html) with a |
| // VideoFrame. The frame will take ownership of |dmabuf_fds|, and will |
| // automatically close() them on destruction. Callers can duplicate the file |
| // descriptors if they need to retain a copy of the FDs for themselves. Note |
| // that the FDs are consumed even in case of failure. |
| // 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( |
| const VideoFrameLayout& layout, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| std::vector<base::ScopedFD> dmabuf_fds, |
| base::TimeDelta timestamp); |
| #endif |
| |
| #if BUILDFLAG(IS_APPLE) |
| // Wraps a provided IOSurface with a VideoFrame. The IOSurface is retained |
| // and locked for the lifetime of the VideoFrame. This is for unaccelerated |
| // (CPU-only) access to the IOSurface, and is not efficient. It is the path |
| // that video capture uses when hardware acceleration is disabled. |
| // https://crbug.com/1125879 |
| static scoped_refptr<VideoFrame> WrapUnacceleratedIOSurface( |
| gfx::GpuMemoryBufferHandle handle, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::TimeDelta timestamp); |
| #endif |
| |
| // Wraps |frame|. |visible_rect| must be a sub rect within |
| // frame->visible_rect(). |
| static scoped_refptr<VideoFrame> WrapVideoFrame( |
| 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 |dimensions| adjusted to appropriate boundaries based on |format|. |
| static gfx::Size DetermineAlignedSize(VideoPixelFormat format, |
| const gfx::Size& dimensions); |
| |
| // 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 the plane gfx::Size (in samples) for a plane of the given coded |
| // size and format. |
| static gfx::Size PlaneSizeInSamples(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 bytes per element for given |plane| and |format|. |
| static int BytesPerElement(VideoPixelFormat format, size_t plane); |
| |
| // Calculates strides for each plane based on |format| and |coded_size|. |
| static std::vector<size_t> ComputeStrides(VideoPixelFormat format, |
| const gfx::Size& coded_size); |
| |
| // 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 VideoFrame& frame); |
| |
| // Returns true if |frame| is accessible mapped in the VideoFrame memory |
| // space. |
| // static |
| static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type); |
| |
| // Returns true if |plane| is a valid plane index for the given |format|. |
| static bool IsValidPlane(VideoPixelFormat format, size_t plane); |
| |
| // 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 a human readable string of StorageType. |
| static std::string StorageTypeToString(VideoFrame::StorageType storage_type); |
| |
| // A video frame wrapping external data may be backed by a read-only shared |
| // memory region. These methods are used to appropriately transform a |
| // VideoFrame created with WrapExternalData, WrapExternalYuvaData, etc. The |
| // storage type of the Video Frame will be changed to STORAGE_READ_ONLY_SHMEM. |
| // Once the backing of a VideoFrame is set, it cannot be changed. |
| // |
| // The region is NOT owned by the video frame. Both the region and its |
| // associated mapping must outlive this instance. |
| void BackWithSharedMemory(const base::ReadOnlySharedMemoryRegion* region); |
| |
| // As above, but the VideoFrame owns the shared memory region as well as the |
| // mapping. They will be destroyed with their VideoFrame. |
| void BackWithOwnedSharedMemory(base::ReadOnlySharedMemoryRegion region, |
| base::ReadOnlySharedMemoryMapping mapping); |
| |
| // Valid for shared memory backed VideoFrames. |
| const base::ReadOnlySharedMemoryRegion* shm_region() const { |
| DCHECK(IsValidSharedMemoryFrame()); |
| DCHECK(storage_type_ == STORAGE_SHMEM); |
| return shm_region_; |
| } |
| |
| // 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 the video frame uses ClientSharedImage. |
| bool HasSharedImage() const; |
| |
| // Returns true if the |storage_type_| is STOAGE_GPU_MEMORY_BUFFER which |
| // indicates that the VideoFrame is backed by GMB or a MappableSharedImage |
| // when its enabled. |
| bool HasMappableGpuBuffer() const; |
| |
| // Returns true if the GpuMemoruBuffer backing the video frame is native |
| // buffer and not shared memory buffer. A native GPU memory buffer is a |
| // block of memory that is allocated and managed directly on the GPU's |
| // memory which allows for hardware acceleration. |
| bool HasNativeGpuMemoryBuffer() const; |
| |
| // Gets the GpuMemoryBuffer backing the VideoFrame. Meant to be only used by |
| // the tests until they are converted to use MappableSI. |
| gfx::GpuMemoryBuffer* GetGpuMemoryBufferForTesting() const; |
| |
| // Gets the ScopedMapping object which clients can use to access the CPU |
| // visible memory and other metadata for the gpu buffer backing this |
| // VideoFrame(via GpuMemoryBuffer or MappableSI). |
| // TODO(crbug.com/40263579): Note that once MappableSI is fully launched and |
| // enabled for VideoFrame, rename this method to MapSharedImage(). It can |
| // then directly return ClientSharedImage::ScopedMapping object instead. |
| std::unique_ptr<VideoFrame::ScopedMapping> MapGMBOrSharedImage() const; |
| |
| // Gets the ScopedMapping object which clients can use to access the CPU |
| // visible memory and other metadata for the gpu buffer backing this |
| // VideoFrame(via GpuMemoryBuffer or MappableSI). |
| // This isn't guaranteed to be always async. |
| // If 'AsyncMappingIsNonBlocking()' is 'false', this will run the callback |
| // in the current sequence. Otherwise, the callback will be invoked in the |
| // GpuMemoryThread. |
| // Note: the frame must not be destroyed before the result callback is |
| // executed. |
| // TODO(crbug.com/40263579): Note that once MappableSI is fully launched and |
| // enabled for VideoFrame, rename this method to MapSharedImageAsync(). It can |
| // then directly return ClientSharedImage::ScopedMapping object instead. |
| void MapGMBOrSharedImageAsync( |
| base::OnceCallback<void(std::unique_ptr<VideoFrame::ScopedMapping>)> |
| result_cb) const; |
| |
| // Returns true if the underlying SharedImage or GMB can be mapped truly |
| // asynchronously: with an unblocking request to the GPU process. |
| // Only call if `HasMappableGpuBuffer() == true`. |
| bool AsyncMappingIsNonBlocking() const; |
| |
| // Gets the GpuMemoryBufferHandle backing the VideoFrame. |
| gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() const; |
| |
| // Returns true if the video frame was created with the given parameters. |
| bool IsSameAllocation(VideoPixelFormat format, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size) const; |
| |
| // Returns the color space of this frame's content. |
| gfx::ColorSpace ColorSpace() const; |
| void set_color_space(const gfx::ColorSpace& color_space) { |
| color_space_ = color_space; |
| } |
| |
| // Return the full-range RGB component of the color space of this frame's |
| // content. This will replace several color spaces (Rec601, Rec709, and |
| // Apple's Rec709) with sRGB, for compatibility with existing behavior. |
| gfx::ColorSpace CompatRGBColorSpace() const; |
| |
| const std::optional<gfx::HDRMetadata>& hdr_metadata() const { |
| return hdr_metadata_; |
| } |
| |
| void set_hdr_metadata(const std::optional<gfx::HDRMetadata>& hdr_metadata) { |
| hdr_metadata_ = hdr_metadata; |
| } |
| |
| const VideoFrameLayout& layout() const { return layout_; } |
| |
| VideoPixelFormat format() const { return layout_.format(); } |
| StorageType storage_type() const { return storage_type_; } |
| |
| // The full dimensions of the video frame data. |
| const gfx::Size& coded_size() const { return layout_.coded_size(); } |
| // A subsection of [0, 0, coded_size().width(), coded_size.height()]. This |
| // can be set to "soft-apply" a cropping. It determines the pointers into |
| // the data returned by visible_data(). |
| const gfx::Rect& visible_rect() const { return visible_rect_; } |
| // Specifies that the |visible_rect| section of the frame is supposed to be |
| // scaled to this size when being presented. This can be used to represent |
| // anamorphic frames, or to "soft-apply" any custom scaling. |
| const gfx::Size& natural_size() const { return natural_size_; } |
| |
| size_t stride(size_t plane) const { |
| CHECK(IsValidPlane(format(), plane)); |
| CHECK_LT(plane, layout_.num_planes()); |
| return layout_.planes()[plane].stride; |
| } |
| |
| // 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 the number of columns for a given plane. |
| int columns(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 { |
| auto span = data_span(plane); |
| if (span.empty()) [[unlikely]] { |
| return nullptr; |
| } |
| return span.data(); |
| } |
| |
| base::span<const uint8_t> data_span(size_t plane) const { |
| CHECK(IsValidPlane(format(), plane)); |
| CHECK(IsMappable()); |
| return data_[plane]; |
| } |
| |
| uint8_t* writable_data(size_t plane) { |
| // TODO(crbug.com/40265179): Also CHECK that the storage type isn't |
| // STORAGE_UNOWNED_MEMORY once non-compliant usages are fixed. |
| CHECK_NE(storage_type_, STORAGE_SHMEM); |
| return const_cast<uint8_t*>(data(plane)); |
| } |
| |
| bool is_mappable_si_enabled() const { return is_mappable_si_enabled_; } |
| |
| const std::optional<gpu::VulkanYCbCrInfo>& ycbcr_info() const { |
| return wrapped_frame_ ? wrapped_frame_->ycbcr_info() : ycbcr_info_; |
| } |
| |
| // Returns pointer to the data in the visible region of the frame, for |
| // IsMappable() storage types. The returned pointer is offset 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* GetWritableVisibleData(size_t plane); |
| |
| // Returns spans of data in the visible region of the frame, for |
| // IsMappable() storage types. The returned span is offset into the |
| // plane buffer specified by visible_rect().origin(). |
| base::span<const uint8_t> GetVisiblePlaneData(size_t plane) const; |
| base::span<uint8_t> GetWritableVisiblePlaneData(size_t plane); |
| |
| // Returns the `acquire_sync_token_` |
| gpu::SyncToken acquire_sync_token() const; |
| |
| // Returns the ClientSharedImage. |
| // Only valid to call if this is a NATIVE_TEXTURE frame and contains valid |
| // ClientSharedImage pointer. Before using the shared_image, the caller must |
| // wait for the included sync point. |
| scoped_refptr<gpu::ClientSharedImage> shared_image() const; |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| // The number of DmaBufs will be equal or less than the number of planes of |
| // the frame. If there are less, this means that the last FD contains the |
| // remaining planes. Should be > 0 for STORAGE_DMABUFS. |
| size_t NumDmabufFds() const; |
| |
| // Returns true if |frame| has DmaBufs. |
| bool HasDmaBufs() const; |
| |
| // The returned FDs are still owned by the VideoFrame. This means that the |
| // caller shall not close them, or use them after the VideoFrame is destroyed. |
| // For such use cases, use dup() to obtain your own copy of the FDs. |
| int GetDmabufFd(size_t i) const; |
| #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| |
| // Sets the mailbox (and GpuMemoryBuffer, if desired) release callback. |
| // |
| // The callback may be run from ANY THREAD, and so it is up to the client to |
| // ensure thread safety. |
| // |
| // WARNING: This method is not thread safe; it should only be called if you |
| // are still the only owner of this VideoFrame. |
| void SetReleaseMailboxCB(ReleaseMailboxCB release_mailbox_cb); |
| void SetReleaseMailboxAndGpuMemoryBufferCB( |
| ReleaseMailboxAndGpuMemoryBufferCB release_mailbox_cb); |
| |
| // Tests whether a mailbox release callback is configured. |
| bool HasReleaseMailboxCB() const; |
| |
| // 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(base::OnceClosure 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. |
| const VideoFrameMetadata& metadata() const { return metadata_; } |
| VideoFrameMetadata& metadata() { return metadata_; } |
| void set_metadata(const VideoFrameMetadata& metadata) { |
| metadata_ = metadata; |
| } |
| |
| // Resets |metadata_|. |
| void clear_metadata() { set_metadata(VideoFrameMetadata()); } |
| |
| // 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 token and potentially waits on an |
| // older sync token. The final sync point will be used to release this |
| // VideoFrame. Also returns the new sync token. |
| // This method is thread safe. Both blink and compositor threads can call it. |
| gpu::SyncToken UpdateReleaseSyncToken(SyncTokenClient* client); |
| |
| // Similar to UpdateReleaseSyncToken() but operates on the gpu::SyncToken |
| // for mailbox. |
| void UpdateAcquireSyncToken(gpu::SyncToken new_acquire_sync_token); |
| |
| // Returns a human-readable string describing |*this|. |
| std::string AsHumanReadableString() const; |
| |
| // Unique identifier for this video frame generated at construction time. The |
| // first ID is 1. The identifier is unique within a process % overflows (which |
| // should be impossible in practice with a 64-bit unsigned integer). |
| // |
| // Note: callers may assume that ID will always correspond to a base::IdType |
| // but should not blindly assume that the underlying type will always be |
| // uint64_t (this is free to change in the future). |
| using ID = ::base::IdTypeU64<class VideoFrameIdTag>; |
| ID unique_id() const { return unique_id_; } |
| |
| // Returns the number of bits per channel. |
| size_t BitDepth() const; |
| |
| // Provide the sampler conversion information for the frame. |
| void set_ycbcr_info(const std::optional<gpu::VulkanYCbCrInfo>& ycbcr_info) { |
| ycbcr_info_ = ycbcr_info; |
| } |
| |
| // Tests can use this method to mark a VF as non-texturable. This is required |
| // when tests creates a VF with a mappable shared image but does not want to |
| // render it. In those cases only looking for ::HasSharedImage() does not |
| // provide enough info to make a decision if VF is texturable or not since VF |
| // will always have a shared image for MappableSI case. |
| void DisableTexturingForTesting() { |
| CHECK(is_mappable_si_enabled_); |
| is_texturable_for_testing_ = false; |
| } |
| |
| bool IsTexturableForTesting() const { return is_texturable_for_testing_; } |
| |
| protected: |
| friend class base::RefCountedThreadSafe<VideoFrame>; |
| 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); |
| |
| private: |
| // The constructor of VideoFrame should use IsValidConfigInternal() |
| // instead of the public IsValidConfig() to check the config, because we can |
| // create special video frames that won't pass the check by IsValidConfig(). |
| static bool IsValidConfigInternal(VideoPixelFormat format, |
| FrameControlType frame_control_type, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size); |
| |
| 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); |
| |
| static scoped_refptr<VideoFrame> CreateFrameForNativeTexturesInternal( |
| VideoPixelFormat format, |
| const gfx::Size& coded_size, |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| base::TimeDelta timestamp); |
| |
| // This method is used by ::WrapExternalGpuMemoryBuffer() as well as future |
| // apis added for MappableSI. ::WrapExternalGpuMemoryBuffer() can just pass |
| // |shared_image| param as nullptr here whereas MappableSharedImage apis will |
| // pass |gpu_memory_buffer| as nullptr. There are additional checks inside to |
| // ensure the correctness. |
| static scoped_refptr<VideoFrame> |
| CreateFrameForGpuMemoryBufferOrMappableSIInternal( |
| const gfx::Rect& visible_rect, |
| const gfx::Size& natural_size, |
| std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer, |
| scoped_refptr<gpu::ClientSharedImage> shared_image, |
| const bool enable_mappable_si, |
| ReleaseMailboxAndGpuMemoryBufferCB mailbox_holder_and_gmb_release_cb, |
| base::TimeDelta timestamp); |
| |
| void MakeScopedMappingForGpuMemoryBuffer( |
| base::OnceCallback<void(std::unique_ptr<VideoFrame::ScopedMapping>)> |
| result_cb, |
| bool success) const; |
| void WrapScopedSharedImageMapping( |
| base::OnceCallback<void(std::unique_ptr<VideoFrame::ScopedMapping>)> |
| result_cb, |
| std::unique_ptr<gpu::ClientSharedImage::ScopedMapping> mapping) const; |
| |
| // Return the alignment for the whole frame, calculated as the max of the |
| // alignment for each individual plane. |
| static gfx::Size CommonAlignment(VideoPixelFormat format); |
| |
| // Tries to allocate the requisite amount of memory for this frame. Returns |
| // false if this would cause an out of memory error. |
| [[nodiscard]] bool AllocateMemory(bool zero_initialize_memory); |
| |
| // Return plane sizes for the given layout. |
| // |
| // It first considers buffer size layout object provides. If layout's |
| // number of buffers equals to number of planes, and buffer size is assigned |
| // (non-zero), it returns buffers' size. |
| // |
| // Otherwise, it uses the first (num_buffers - 1) assigned buffers' size as |
| // plane size. Then for the rest unassigned planes, calculates their size |
| // based on format, coded size and stride for the plane. |
| static std::vector<size_t> CalculatePlaneSize(const VideoFrameLayout& layout); |
| |
| // Calculates plane size for `layout_`. |
| std::vector<size_t> CalculatePlaneSize() const; |
| |
| // Returns true iff the frame has a shared memory storage type, and the |
| // associated regions are valid. |
| bool IsValidSharedMemoryFrame() const; |
| |
| template <typename T> |
| base::span<T> GetVisibleDataInternal(base::span<T> data, size_t plane) const; |
| |
| // VideFrameLayout (includes format, coded_size, and strides). |
| const VideoFrameLayout layout_; |
| |
| // Set by WrapVideoFrame to soft-apply a new set of format, visible rectangle, |
| // and natural size on |wrapped_frame_| |
| scoped_refptr<VideoFrame> wrapped_frame_; |
| // This is set when WrapVideoFrame() was given an already wrapped frame, |
| // and it needs to be preserved for proper destruction later |
| // (e.g. calling |done_callbacks_|). |
| scoped_refptr<VideoFrame> intermediate_wrapped_frame_; |
| |
| // Storage type for the different planes. |
| StorageType storage_type_; // TODO(mcasas): make const |
| |
| // 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 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. |
| std::array<base::span<const uint8_t>, kMaxPlanes> data_; |
| |
| // Sync token associated with the `shared_image_`. |
| gpu::SyncToken acquire_sync_token_; |
| ReleaseMailboxAndGpuMemoryBufferCB mailbox_holder_and_gmb_release_cb_; |
| |
| // Native texture shared image that is only set when the VideoFrame is |
| // created via VideoFrame::WrapSharedImage(). |
| scoped_refptr<gpu::ClientSharedImage> shared_image_; |
| |
| // Shared memory handle, if this frame is STORAGE_SHMEM. The region pointed |
| // to is unowned. |
| raw_ptr<const base::ReadOnlySharedMemoryRegion> shm_region_ = nullptr; |
| |
| // Used if this is a STORAGE_SHMEM frame with owned shared memory. |
| // In that case, shm_region_ will refer to this region. |
| base::ReadOnlySharedMemoryRegion owned_shm_region_; |
| base::ReadOnlySharedMemoryMapping owned_shm_mapping_; |
| |
| // GPU memory buffer, if this frame is STORAGE_GPU_MEMORY_BUFFER. |
| std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer_; |
| |
| // This field will be set by clients when using MappableSI instead of |
| // GpuMemoryBuffers. Clients will set this flag while creating a VideoFrame. |
| bool is_mappable_si_enabled_ = false; |
| |
| #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| // Dmabufs for the frame, used when storage is STORAGE_DMABUFS. Size is either |
| // equal or less than the number of planes of the frame. If it is less, then |
| // the memory area represented by the last FD contains the remaining planes. |
| std::vector<base::ScopedFD> dmabuf_fds_; |
| #endif |
| |
| base::Lock done_callbacks_lock_; |
| std::vector<base::OnceClosure> done_callbacks_ |
| GUARDED_BY(done_callbacks_lock_); |
| |
| base::TimeDelta timestamp_; |
| |
| base::Lock release_sync_token_lock_; |
| gpu::SyncToken release_sync_token_ GUARDED_BY(release_sync_token_lock_); |
| |
| VideoFrameMetadata metadata_; |
| |
| // Generated at construction time. |
| const ID unique_id_; |
| |
| gfx::ColorSpace color_space_; |
| std::optional<gfx::HDRMetadata> hdr_metadata_; |
| |
| // Sampler conversion information which is used in vulkan context for android. |
| std::optional<gpu::VulkanYCbCrInfo> ycbcr_info_; |
| |
| // Allocation which makes up |data_| planes for self-allocated frames. |
| std::unique_ptr<uint8_t, base::UncheckedFreeDeleter> private_data_; |
| |
| // Only used by tests. |
| // Some tests creates VideoFrame with a Mappable shared image which it does |
| // not intend to render. Tests generally uses ::IsSharedImage() to identify |
| // if a frame is texture backed and can be rendered. This is not enough when |
| // MappableSI is used since a Mappable shared image can be created by tests |
| // only for mapping the underlying buffer to CPU visible memory for |
| // read/write and not necessarily for rendering. Tests which intends to do so |
| // must explicitly mark the VideoFrame as non texturable via |
| // ::DisableTexturingForTesting(). |
| bool is_texturable_for_testing_ = true; |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_BASE_VIDEO_FRAME_H_ |