#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "media/base/video_frame.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <algorithm>
#include <atomic>
#include <climits>
#include <numeric>
#include <string_view>
#include <utility>
#include "base/bits.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/process/memory.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "media/base/color_plane_layout.h"
#include "media/base/format_utils.h"
#include "media/base/limits.h"
#include "media/base/media_switches.h"
#include "media/base/timestamp_constants.h"
#include "media/base/video_util.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/gpu_memory_buffer.h"
#if BUILDFLAG(IS_APPLE)
#include "ui/gfx/mac/io_surface.h"
#endif
namespace media {
namespace {
VideoFrame::ID GetNextID() { … }
gfx::Rect Intersection(gfx::Rect a, const gfx::Rect& b) { … }
void ReleaseMailboxAndDropGpuMemoryBuffer(
VideoFrame::ReleaseMailboxCB cb,
const gpu::SyncToken& sync_token,
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer) { … }
VideoFrame::ReleaseMailboxAndGpuMemoryBufferCB WrapReleaseMailboxCB(
VideoFrame::ReleaseMailboxCB cb) { … }
}
std::string VideoFrame::StorageTypeToString(
const VideoFrame::StorageType storage_type) { … }
bool VideoFrame::IsStorageTypeMappable(VideoFrame::StorageType storage_type) { … }
bool VideoFrame::IsValidPlane(VideoPixelFormat format, size_t plane) { … }
gfx::Size VideoFrame::SampleSize(VideoPixelFormat format, size_t plane) { … }
static bool AreValidPixelFormatsForWrap(VideoPixelFormat source_format,
VideoPixelFormat target_format) { … }
static std::optional<VideoFrameLayout> GetDefaultLayout(
VideoPixelFormat format,
const gfx::Size& coded_size) { … }
bool VideoFrame::IsValidConfig(VideoPixelFormat format,
StorageType storage_type,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateVideoHoleFrame(
const base::UnguessableToken& overlay_plane_id,
const gfx::Size& natural_size,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateZeroInitializedFrame(
VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateFrameForNativeTexturesInternal(
VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame>
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) { … }
scoped_refptr<VideoFrame> VideoFrame::WrapNativeTextures(
VideoPixelFormat format,
const gpu::MailboxHolder (&mailbox_holders)[kMaxPlanes],
ReleaseMailboxCB mailbox_holder_release_cb,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> VideoFrame::WrapSharedImage(
VideoPixelFormat format,
scoped_refptr<gpu::ClientSharedImage> shared_image,
gpu::SyncToken sync_token,
uint32_t texture_target,
ReleaseMailboxCB mailbox_holder_release_cb,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> VideoFrame::WrapMappableSharedImage(
scoped_refptr<gpu::ClientSharedImage> shared_image,
gpu::SyncToken sync_token,
uint32_t texture_target,
ReleaseMailboxAndGpuMemoryBufferCB mailbox_holder_and_gmb_release_cb,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> 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) { … }
scoped_refptr<VideoFrame> 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) { … }
scoped_refptr<VideoFrame> 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,
const uint8_t* y_data,
const uint8_t* u_data,
const uint8_t* v_data,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> 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) { … }
scoped_refptr<VideoFrame> 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,
const uint8_t* y_data,
const uint8_t* u_data,
const uint8_t* v_data,
const uint8_t* a_data,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> 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 uv_stride,
const uint8_t* y_data,
const uint8_t* uv_data,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> VideoFrame::WrapExternalGpuMemoryBuffer(
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> 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,
uint32_t texture_target,
ReleaseMailboxAndGpuMemoryBufferCB mailbox_holder_and_gmb_release_cb,
base::TimeDelta timestamp) { … }
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
scoped_refptr<VideoFrame> 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)
scoped_refptr<VideoFrame> VideoFrame::WrapUnacceleratedIOSurface(
gfx::GpuMemoryBufferHandle handle,
const gfx::Rect& visible_rect,
base::TimeDelta timestamp) {
if (handle.type != gfx::GpuMemoryBufferType::IO_SURFACE_BUFFER) {
DLOG(ERROR) << "Non-IOSurface handle.";
return nullptr;
}
gfx::ScopedIOSurface io_surface = handle.io_surface;
if (!io_surface) {
return nullptr;
}
const OSType cv_pixel_format = IOSurfaceGetPixelFormat(io_surface.get());
if (cv_pixel_format != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
DLOG(ERROR) << "Invalid (non-NV12) pixel format.";
return nullptr;
}
const VideoPixelFormat pixel_format = PIXEL_FORMAT_NV12;
const size_t num_planes = IOSurfaceGetPlaneCount(io_surface.get());
const gfx::Size size(IOSurfaceGetWidth(io_surface.get()),
IOSurfaceGetHeight(io_surface.get()));
std::vector<int32_t> strides;
for (size_t i = 0; i < num_planes; ++i)
strides.push_back(IOSurfaceGetBytesPerRowOfPlane(io_surface.get(), i));
std::optional<VideoFrameLayout> layout =
media::VideoFrameLayout::CreateWithStrides(pixel_format, size, strides);
if (!layout) {
DLOG(ERROR) << "Invalid layout.";
return nullptr;
}
const StorageType storage_type = STORAGE_UNOWNED_MEMORY;
if (!IsValidConfig(pixel_format, storage_type, size, visible_rect, size)) {
DLOG(ERROR) << "Invalid config.";
return nullptr;
}
kern_return_t lock_result =
IOSurfaceLock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
if (lock_result != kIOReturnSuccess) {
DLOG(ERROR) << "Failed to lock IOSurface.";
return nullptr;
}
auto unlock_lambda =
[](base::apple::ScopedCFTypeRef<IOSurfaceRef> io_surface) {
IOSurfaceUnlock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
};
scoped_refptr<VideoFrame> frame =
new VideoFrame(*layout, storage_type, visible_rect, size, timestamp);
for (size_t i = 0; i < num_planes; ++i) {
frame->data_[i] = reinterpret_cast<uint8_t*>(
IOSurfaceGetBaseAddressOfPlane(io_surface.get(), i));
}
frame->AddDestructionObserver(
base::BindOnce(unlock_lambda, std::move(io_surface)));
return frame;
}
scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
CVPixelBufferRef cv_pixel_buffer,
base::TimeDelta timestamp) {
DCHECK(cv_pixel_buffer);
DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
VideoPixelFormat format;
if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
format = PIXEL_FORMAT_I420;
} else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
format = PIXEL_FORMAT_I444;
} else if (cv_format == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
format = PIXEL_FORMAT_NV12;
} else if (cv_format ==
kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar) {
format = PIXEL_FORMAT_NV12A;
} else {
DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
return nullptr;
}
const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
const StorageType storage = STORAGE_UNOWNED_MEMORY;
if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
DLOG(ERROR) << __func__ << " Invalid config."
<< ConfigToString(format, storage, coded_size, visible_rect,
natural_size);
return nullptr;
}
auto layout = VideoFrameLayout::Create(format, coded_size);
if (!layout) {
DLOG(ERROR) << "Invalid layout.";
return nullptr;
}
scoped_refptr<VideoFrame> frame(
new VideoFrame(*layout, storage, visible_rect, natural_size, timestamp));
frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
return frame;
}
#endif
scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
scoped_refptr<VideoFrame> frame,
VideoPixelFormat format,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() { … }
scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
const gfx::Size& size,
uint8_t y,
uint8_t u,
uint8_t v,
base::TimeDelta timestamp) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
const gfx::Size& size) { … }
size_t VideoFrame::NumPlanes(VideoPixelFormat format) { … }
size_t VideoFrame::AllocationSize(VideoPixelFormat format,
const gfx::Size& coded_size) { … }
gfx::Size VideoFrame::PlaneSize(VideoPixelFormat format,
size_t plane,
const gfx::Size& coded_size) { … }
gfx::Size VideoFrame::PlaneSizeInSamples(VideoPixelFormat format,
size_t plane,
const gfx::Size& coded_size) { … }
int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format,
size_t plane) { … }
int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format, size_t plane) { … }
size_t VideoFrame::RowBytes(size_t plane, VideoPixelFormat format, int width) { … }
int VideoFrame::BytesPerElement(VideoPixelFormat format, size_t plane) { … }
std::vector<int32_t> VideoFrame::ComputeStrides(VideoPixelFormat format,
const gfx::Size& coded_size) { … }
size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) { … }
size_t VideoFrame::Columns(size_t plane, VideoPixelFormat format, int width) { … }
void VideoFrame::HashFrameForTesting(base::MD5Context* context,
const VideoFrame& frame) { … }
void VideoFrame::BackWithSharedMemory(
const base::ReadOnlySharedMemoryRegion* region) { … }
void VideoFrame::BackWithOwnedSharedMemory(
base::ReadOnlySharedMemoryRegion region,
base::ReadOnlySharedMemoryMapping mapping) { … }
bool VideoFrame::IsMappable() const { … }
bool VideoFrame::HasTextures() const { … }
size_t VideoFrame::NumTextures() const { … }
bool VideoFrame::HasSharedImage() const { … }
bool VideoFrame::HasMappableGpuBuffer() const { … }
bool VideoFrame::HasNativeGpuMemoryBuffer() const { … }
gfx::GpuMemoryBuffer* VideoFrame::GetGpuMemoryBuffer() const { … }
std::unique_ptr<VideoFrame::ScopedMapping> VideoFrame::MapGMBOrSharedImage()
const { … }
gfx::GpuMemoryBufferHandle VideoFrame::GetGpuMemoryBufferHandle() const { … }
bool VideoFrame::IsSameAllocation(VideoPixelFormat format,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size) const { … }
gfx::ColorSpace VideoFrame::ColorSpace() const { … }
gfx::ColorSpace VideoFrame::CompatRGBColorSpace() const { … }
bool VideoFrame::RequiresExternalSampler() const { … }
int VideoFrame::row_bytes(size_t plane) const { … }
int VideoFrame::rows(size_t plane) const { … }
int VideoFrame::columns(size_t plane) const { … }
template <typename T>
T VideoFrame::GetVisibleDataInternal(T data, size_t plane) const { … }
const uint8_t* VideoFrame::visible_data(size_t plane) const { … }
uint8_t* VideoFrame::GetWritableVisibleData(size_t plane) { … }
const gpu::MailboxHolder& VideoFrame::mailbox_holder(
size_t texture_index) const { … }
scoped_refptr<gpu::ClientSharedImage> VideoFrame::shared_image() const { … }
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
size_t VideoFrame::NumDmabufFds() const { … }
bool VideoFrame::HasDmaBufs() const { … }
int VideoFrame::GetDmabufFd(size_t i) const { … }
#endif
#if BUILDFLAG(IS_APPLE)
CVPixelBufferRef VideoFrame::CvPixelBuffer() const {
return cv_pixel_buffer_.get();
}
#endif
void VideoFrame::SetReleaseMailboxCB(ReleaseMailboxCB release_mailbox_cb) { … }
void VideoFrame::SetReleaseMailboxAndGpuMemoryBufferCB(
ReleaseMailboxAndGpuMemoryBufferCB release_mailbox_cb) { … }
bool VideoFrame::HasReleaseMailboxCB() const { … }
void VideoFrame::AddDestructionObserver(base::OnceClosure callback) { … }
gpu::SyncToken VideoFrame::UpdateReleaseSyncToken(SyncTokenClient* client) { … }
gpu::SyncToken VideoFrame::UpdateMailboxHolderSyncToken(
size_t plane,
SyncTokenClient* client) { … }
std::string VideoFrame::AsHumanReadableString() const { … }
size_t VideoFrame::BitDepth() const { … }
VideoFrame::VideoFrame(const VideoFrameLayout& layout,
StorageType storage_type,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp,
FrameControlType frame_control_type)
: … { … }
VideoFrame::~VideoFrame() { … }
std::string VideoFrame::ConfigToString(const VideoPixelFormat format,
const StorageType storage_type,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size) { … }
gfx::Size VideoFrame::DetermineAlignedSize(VideoPixelFormat format,
const gfx::Size& dimensions) { … }
bool VideoFrame::IsValidSize(const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size) { … }
bool VideoFrame::IsValidCodedSize(const gfx::Size& size) { … }
bool VideoFrame::IsValidConfigInternal(VideoPixelFormat format,
FrameControlType frame_control_type,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size) { … }
std::optional<VideoFrameLayout>
VideoFrame::CreateFullySpecifiedLayoutWithStrides(VideoPixelFormat format,
const gfx::Size& coded_size) { … }
scoped_refptr<VideoFrame> 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) { … }
scoped_refptr<VideoFrame> VideoFrame::CreateFrameWithLayout(
const VideoFrameLayout& layout,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp,
bool zero_initialize_memory) { … }
gfx::Size VideoFrame::CommonAlignment(VideoPixelFormat format) { … }
bool VideoFrame::AllocateMemory(bool zero_initialize_memory) { … }
bool VideoFrame::IsValidSharedMemoryFrame() const { … }
std::vector<size_t> VideoFrame::CalculatePlaneSize(
const VideoFrameLayout& layout) { … }
std::vector<size_t> VideoFrame::CalculatePlaneSize() const { … }
VideoFrame::ScopedMapping::ScopedMapping(
gfx::GpuMemoryBuffer* gpu_memory_buffer,
std::unique_ptr<gpu::ClientSharedImage::ScopedMapping> scoped_mapping)
: … { … }
VideoFrame::ScopedMapping::~ScopedMapping() { … }
uint8_t* VideoFrame::ScopedMapping::Memory(uint32_t plane_index) { … }
size_t VideoFrame::ScopedMapping::Stride(uint32_t plane_index) { … }
gfx::Size VideoFrame::ScopedMapping::Size() { … }
}