1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/base/video_frame.h"
8 #include <GLES2/gl2ext.h>
16 #include "base/bits.h"
17 #include "base/functional/bind.h"
18 #include "base/functional/callback_helpers.h"
19 #include "base/logging.h"
20 #include "base/process/memory.h"
21 #include "base/strings/string_piece.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/synchronization/lock.h"
24 #include "build/build_config.h"
25 #include "media/base/color_plane_layout.h"
26 #include "media/base/format_utils.h"
27 #include "media/base/limits.h"
28 #include "media/base/timestamp_constants.h"
29 #include "media/base/video_util.h"
30 #include "ui/gfx/buffer_format_util.h"
31 #include "ui/gfx/geometry/point.h"
32 #include "ui/gfx/gpu_memory_buffer.h"
33 #if BUILDFLAG(IS_APPLE)
34 #include "ui/gfx/mac/io_surface.h"
36 #if defined(TIZEN_TBM_SUPPORT)
37 #include "base/threading/thread_task_runner_handle.h"
38 #include "base/trace_event/trace_event.h"
39 #include "gpu/GLES2/gl2extchromium.h"
40 #include "gpu/command_buffer/client/gles2_interface.h"
41 #include "media/base/bind_to_current_loop.h"
48 VideoFrame::ID GetNextID() {
49 static std::atomic_uint64_t counter(1u);
50 return VideoFrame::ID::FromUnsafeValue(
51 counter.fetch_add(1u, std::memory_order_relaxed));
54 // Helper to provide gfx::Rect::Intersect() as an expression.
55 gfx::Rect Intersection(gfx::Rect a, const gfx::Rect& b) {
60 void ReleaseMailboxAndDropGpuMemoryBuffer(
61 VideoFrame::ReleaseMailboxCB cb,
62 const gpu::SyncToken& sync_token,
63 std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer) {
64 std::move(cb).Run(sync_token);
67 VideoFrame::ReleaseMailboxAndGpuMemoryBufferCB WrapReleaseMailboxCB(
68 VideoFrame::ReleaseMailboxCB cb) {
70 return VideoFrame::ReleaseMailboxAndGpuMemoryBufferCB();
71 return base::BindOnce(&ReleaseMailboxAndDropGpuMemoryBuffer, std::move(cb));
77 std::string VideoFrame::StorageTypeToString(
78 const VideoFrame::StorageType storage_type) {
79 switch (storage_type) {
80 case VideoFrame::STORAGE_UNKNOWN:
82 case VideoFrame::STORAGE_OPAQUE:
84 case VideoFrame::STORAGE_UNOWNED_MEMORY:
85 return "UNOWNED_MEMORY";
86 case VideoFrame::STORAGE_OWNED_MEMORY:
87 return "OWNED_MEMORY";
88 case VideoFrame::STORAGE_SHMEM:
90 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
91 case VideoFrame::STORAGE_DMABUFS:
94 #if defined(TIZEN_TBM_SUPPORT)
95 case VideoFrame::STORAGE_TBM_SURFACE:
98 #if defined(TIZEN_VIDEO_HOLE)
99 case VideoFrame::STORAGE_HOLE:
102 case VideoFrame::STORAGE_GPU_MEMORY_BUFFER:
103 return "GPU_MEMORY_BUFFER";
106 NOTREACHED() << "Invalid StorageType provided: " << storage_type;
111 bool VideoFrame::IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
113 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
114 // This is not strictly needed but makes explicit that, at VideoFrame
115 // level, DmaBufs are not mappable from userspace.
116 storage_type != VideoFrame::STORAGE_DMABUFS &&
118 // GpuMemoryBuffer is not mappable at VideoFrame level. In most places
119 // GpuMemoryBuffer is opaque to the CPU, and for places that really need
120 // to access the data on CPU they can get the buffer with
121 // GetGpuMemoryBuffer() and call gfx::GpuMemoryBuffer::Map().
122 (storage_type == VideoFrame::STORAGE_UNOWNED_MEMORY ||
123 storage_type == VideoFrame::STORAGE_OWNED_MEMORY ||
124 storage_type == VideoFrame::STORAGE_SHMEM);
128 bool VideoFrame::IsValidPlane(VideoPixelFormat format, size_t plane) {
129 DCHECK_LE(NumPlanes(format), static_cast<size_t>(kMaxPlanes));
130 return plane < NumPlanes(format);
134 gfx::Size VideoFrame::SampleSize(VideoPixelFormat format, size_t plane) {
135 DCHECK(IsValidPlane(format, plane));
138 case kYPlane: // and kARGBPlane:
140 return gfx::Size(1, 1);
142 case kUPlane: // and kUVPlane:
143 case kVPlane: // and kAPlaneTriPlanar:
145 case PIXEL_FORMAT_I444:
146 case PIXEL_FORMAT_YUV444P9:
147 case PIXEL_FORMAT_YUV444P10:
148 case PIXEL_FORMAT_YUV444P12:
149 case PIXEL_FORMAT_Y16:
150 case PIXEL_FORMAT_I444A:
151 case PIXEL_FORMAT_YUV444AP10:
152 return gfx::Size(1, 1);
154 case PIXEL_FORMAT_I422:
155 case PIXEL_FORMAT_YUV422P9:
156 case PIXEL_FORMAT_YUV422P10:
157 case PIXEL_FORMAT_YUV422P12:
158 case PIXEL_FORMAT_I422A:
159 case PIXEL_FORMAT_YUV422AP10:
160 return gfx::Size(2, 1);
162 case PIXEL_FORMAT_YV12:
163 case PIXEL_FORMAT_I420:
164 case PIXEL_FORMAT_I420A:
165 case PIXEL_FORMAT_NV12:
166 case PIXEL_FORMAT_NV21:
167 case PIXEL_FORMAT_YUV420P9:
168 case PIXEL_FORMAT_YUV420P10:
169 case PIXEL_FORMAT_YUV420P12:
170 case PIXEL_FORMAT_P016LE:
171 case PIXEL_FORMAT_YUV420AP10:
172 #if defined(TIZEN_TBM_SUPPORT)
173 case PIXEL_FORMAT_TBM_INTER_PROCESS_BUFFER:
175 return gfx::Size(2, 2);
177 case PIXEL_FORMAT_NV12A:
178 return plane == kUVPlane ? gfx::Size(2, 2) : gfx::Size(1, 1);
180 case PIXEL_FORMAT_UYVY:
181 case PIXEL_FORMAT_UNKNOWN:
182 case PIXEL_FORMAT_YUY2:
183 case PIXEL_FORMAT_ARGB:
184 case PIXEL_FORMAT_XRGB:
185 case PIXEL_FORMAT_RGB24:
186 case PIXEL_FORMAT_MJPEG:
187 case PIXEL_FORMAT_ABGR:
188 case PIXEL_FORMAT_XBGR:
189 case PIXEL_FORMAT_XR30:
190 case PIXEL_FORMAT_XB30:
191 case PIXEL_FORMAT_BGRA:
192 case PIXEL_FORMAT_RGBAF16:
193 #if BUILDFLAG(IS_TIZEN)
194 case PIXEL_FORMAT_ENCODED:
196 #if defined(TIZEN_TBM_SUPPORT)
197 case PIXEL_FORMAT_TBM_SURFACE:
202 NOTREACHED_NORETURN();
205 // Checks if |source_format| can be wrapped into a |target_format| frame.
206 static bool AreValidPixelFormatsForWrap(VideoPixelFormat source_format,
207 VideoPixelFormat target_format) {
208 return source_format == target_format ||
209 (source_format == PIXEL_FORMAT_I420A &&
210 target_format == PIXEL_FORMAT_I420) ||
211 (source_format == PIXEL_FORMAT_ARGB &&
212 target_format == PIXEL_FORMAT_XRGB) ||
213 (source_format == PIXEL_FORMAT_ABGR &&
214 target_format == PIXEL_FORMAT_XBGR);
217 // If it is required to allocate aligned to multiple-of-two size overall for the
218 // frame of pixel |format|.
219 static bool RequiresEvenSizeAllocation(VideoPixelFormat format) {
221 case PIXEL_FORMAT_ARGB:
222 case PIXEL_FORMAT_XRGB:
223 case PIXEL_FORMAT_RGB24:
224 case PIXEL_FORMAT_Y16:
225 case PIXEL_FORMAT_ABGR:
226 case PIXEL_FORMAT_XBGR:
227 case PIXEL_FORMAT_XR30:
228 case PIXEL_FORMAT_XB30:
229 case PIXEL_FORMAT_BGRA:
230 case PIXEL_FORMAT_RGBAF16:
231 #if BUILDFLAG(IS_TIZEN)
232 case PIXEL_FORMAT_ENCODED:
234 #if defined(TIZEN_TBM_SUPPORT)
235 case PIXEL_FORMAT_TBM_SURFACE:
236 case PIXEL_FORMAT_TBM_INTER_PROCESS_BUFFER:
239 case PIXEL_FORMAT_NV12:
240 case PIXEL_FORMAT_NV12A:
241 case PIXEL_FORMAT_NV21:
242 case PIXEL_FORMAT_I420:
243 case PIXEL_FORMAT_MJPEG:
244 case PIXEL_FORMAT_YUY2:
245 case PIXEL_FORMAT_YV12:
246 case PIXEL_FORMAT_I422:
247 case PIXEL_FORMAT_I444:
248 case PIXEL_FORMAT_YUV420P9:
249 case PIXEL_FORMAT_YUV422P9:
250 case PIXEL_FORMAT_YUV444P9:
251 case PIXEL_FORMAT_YUV420P10:
252 case PIXEL_FORMAT_YUV422P10:
253 case PIXEL_FORMAT_YUV444P10:
254 case PIXEL_FORMAT_YUV420P12:
255 case PIXEL_FORMAT_YUV422P12:
256 case PIXEL_FORMAT_YUV444P12:
257 case PIXEL_FORMAT_I420A:
258 case PIXEL_FORMAT_UYVY:
259 case PIXEL_FORMAT_P016LE:
260 case PIXEL_FORMAT_I422A:
261 case PIXEL_FORMAT_I444A:
262 case PIXEL_FORMAT_YUV420AP10:
263 case PIXEL_FORMAT_YUV422AP10:
264 case PIXEL_FORMAT_YUV444AP10:
266 case PIXEL_FORMAT_UNKNOWN:
269 NOTREACHED() << "Unsupported video frame format: " << format;
273 // Creates VideoFrameLayout for tightly packed frame.
274 static absl::optional<VideoFrameLayout> GetDefaultLayout(
275 VideoPixelFormat format,
276 const gfx::Size& coded_size) {
277 std::vector<ColorPlaneLayout> planes;
280 case PIXEL_FORMAT_I420: {
281 int uv_width = (coded_size.width() + 1) / 2;
282 int uv_height = (coded_size.height() + 1) / 2;
283 int uv_stride = uv_width;
284 int uv_size = uv_stride * uv_height;
285 planes = std::vector<ColorPlaneLayout>{
286 ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
287 ColorPlaneLayout(uv_stride, coded_size.GetArea(), uv_size),
288 ColorPlaneLayout(uv_stride, coded_size.GetArea() + uv_size, uv_size),
293 case PIXEL_FORMAT_Y16:
294 planes = std::vector<ColorPlaneLayout>{ColorPlaneLayout(
295 coded_size.width() * 2, 0, coded_size.GetArea() * 2)};
298 case PIXEL_FORMAT_ARGB:
299 case PIXEL_FORMAT_XRGB:
300 case PIXEL_FORMAT_ABGR:
301 case PIXEL_FORMAT_XBGR:
302 planes = std::vector<ColorPlaneLayout>{ColorPlaneLayout(
303 coded_size.width() * 4, 0, coded_size.GetArea() * 4)};
306 case PIXEL_FORMAT_NV12: {
307 int uv_width = (coded_size.width() + 1) / 2;
308 int uv_height = (coded_size.height() + 1) / 2;
309 int uv_stride = uv_width * 2;
310 int uv_size = uv_stride * uv_height;
311 planes = std::vector<ColorPlaneLayout>{
312 ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
313 ColorPlaneLayout(uv_stride, coded_size.GetArea(), uv_size),
318 case PIXEL_FORMAT_NV12A: {
319 int uv_width = (coded_size.width() + 1) / 2;
320 int uv_height = (coded_size.height() + 1) / 2;
321 int uv_stride = uv_width * 2;
322 int uv_size = uv_stride * uv_height;
323 planes = std::vector<ColorPlaneLayout>{
324 ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
325 ColorPlaneLayout(uv_stride, coded_size.GetArea(), uv_size),
326 ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
332 DLOG(ERROR) << "Unsupported pixel format"
333 << VideoPixelFormatToString(format);
334 return absl::nullopt;
337 return VideoFrameLayout::CreateWithPlanes(format, coded_size, planes);
340 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
341 // This class allows us to embed a vector<ScopedFD> into a scoped_refptr, and
342 // thus to have several VideoFrames share the same set of DMABUF FDs.
343 class VideoFrame::DmabufHolder
344 : public base::RefCountedThreadSafe<DmabufHolder> {
346 DmabufHolder() = default;
347 DmabufHolder(std::vector<base::ScopedFD>&& fds) : fds_(std::move(fds)) {}
349 const std::vector<base::ScopedFD>& fds() const { return fds_; }
350 size_t size() const { return fds_.size(); }
353 std::vector<base::ScopedFD> fds_;
355 friend class base::RefCountedThreadSafe<DmabufHolder>;
356 ~DmabufHolder() = default;
358 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
361 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
362 StorageType storage_type,
363 const gfx::Size& coded_size,
364 const gfx::Rect& visible_rect,
365 const gfx::Size& natural_size) {
366 return IsValidConfigInternal(format, FrameControlType::kNone, coded_size,
367 visible_rect, natural_size);
371 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
372 const gfx::Size& coded_size,
373 const gfx::Rect& visible_rect,
374 const gfx::Size& natural_size,
375 base::TimeDelta timestamp) {
376 return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
381 scoped_refptr<VideoFrame> VideoFrame::CreateVideoHoleFrame(
382 const base::UnguessableToken& overlay_plane_id,
383 const gfx::Size& natural_size,
384 base::TimeDelta timestamp) {
385 auto layout = VideoFrameLayout::Create(PIXEL_FORMAT_UNKNOWN, natural_size);
387 DLOG(ERROR) << "Invalid layout.";
390 scoped_refptr<VideoFrame> frame = new VideoFrame(
391 *layout, StorageType::STORAGE_OPAQUE, gfx::Rect(natural_size),
392 natural_size, timestamp, FrameControlType::kVideoHole);
393 frame->metadata().overlay_plane_id = overlay_plane_id;
398 scoped_refptr<VideoFrame> VideoFrame::CreateZeroInitializedFrame(
399 VideoPixelFormat format,
400 const gfx::Size& coded_size,
401 const gfx::Rect& visible_rect,
402 const gfx::Size& natural_size,
403 base::TimeDelta timestamp) {
404 return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
409 #if defined(TIZEN_TBM_SUPPORT)
410 scoped_refptr<VideoFrame> VideoFrame::WrapTBMSurface(
411 const gfx::Size& size,
412 base::TimeDelta timestamp,
413 gfx::TbmBufferHandle handle) {
414 const VideoPixelFormat format = PIXEL_FORMAT_TBM_SURFACE;
415 const StorageType storage = STORAGE_TBM_SURFACE;
416 const gfx::Rect visible_rect = gfx::Rect(size);
417 if (!IsValidConfig(format, storage, size, visible_rect, size)) {
418 LOG(ERROR) << __FUNCTION__ << " WrapTBMSurface Invalid config."
419 << ConfigToString(format, storage, size, visible_rect, size);
423 auto layout = VideoFrameLayout::Create(format, size);
425 LOG(ERROR) << "Invalid layout.";
429 scoped_refptr<VideoFrame> frame(
430 new VideoFrame(*layout, storage, gfx::Rect(size), size, timestamp));
431 frame->buffer_handle_ = handle;
435 #if BUILDFLAG(IS_TIZEN_TV)
436 scoped_refptr<VideoFrame> VideoFrame::WrapTBMInterProcessBuffer(
437 const gfx::Size& size,
438 base::TimeDelta timestamp,
439 gfx::TbmBufferHandle handle) {
440 const VideoPixelFormat format = PIXEL_FORMAT_TBM_INTER_PROCESS_BUFFER;
441 #if defined(TIZEN_VIDEO_HOLE)
442 const StorageType storage = STORAGE_HOLE;
444 const StorageType storage = STORAGE_UNKNOWN;
446 const gfx::Rect visible_rect = gfx::Rect(size);
447 if (!IsValidConfig(format, storage, size, visible_rect, size)) {
448 DLOG(ERROR) << __FUNCTION__ << " WrapTBMInterProcessBuffer Invalid config."
449 << ConfigToString(format, storage, size, visible_rect, size);
453 auto layout = VideoFrameLayout::CreateWithStrides(
454 format, size, {handle.strides[0], handle.strides[1]});
456 DLOG(ERROR) << "Invalid layout.";
460 scoped_refptr<VideoFrame> frame(
461 new VideoFrame(*layout, storage, gfx::Rect(size), size, timestamp));
462 frame->buffer_handle_ = handle;
469 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTextures(
470 VideoPixelFormat format,
471 const gpu::MailboxHolder (&mailbox_holders)[kMaxPlanes],
472 ReleaseMailboxCB mailbox_holder_release_cb,
473 const gfx::Size& coded_size,
474 const gfx::Rect& visible_rect,
475 const gfx::Size& natural_size,
476 base::TimeDelta timestamp) {
477 if (format != PIXEL_FORMAT_ARGB && format != PIXEL_FORMAT_XRGB &&
478 format != PIXEL_FORMAT_NV12 && format != PIXEL_FORMAT_NV12A &&
479 format != PIXEL_FORMAT_I420 && format != PIXEL_FORMAT_ABGR &&
480 format != PIXEL_FORMAT_XBGR && format != PIXEL_FORMAT_XR30 &&
481 format != PIXEL_FORMAT_XB30 && format != PIXEL_FORMAT_P016LE &&
482 format != PIXEL_FORMAT_RGBAF16 && format != PIXEL_FORMAT_YV12 &&
483 format != PIXEL_FORMAT_BGRA) {
484 DLOG(ERROR) << "Unsupported pixel format: "
485 << VideoPixelFormatToString(format);
488 const StorageType storage = STORAGE_OPAQUE;
489 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
490 DLOG(ERROR) << __func__ << " Invalid config."
491 << ConfigToString(format, storage, coded_size, visible_rect,
496 auto layout = VideoFrameLayout::Create(format, coded_size);
498 DLOG(ERROR) << "Invalid layout.";
502 scoped_refptr<VideoFrame> frame =
503 new VideoFrame(*layout, storage, visible_rect, natural_size, timestamp);
504 memcpy(&frame->mailbox_holders_, mailbox_holders,
505 sizeof(frame->mailbox_holders_));
506 frame->mailbox_holders_and_gmb_release_cb_ =
507 WrapReleaseMailboxCB(std::move(mailbox_holder_release_cb));
509 // Wrapping native textures should... have textures. https://crbug.com/864145.
510 DCHECK(frame->HasTextures());
511 DCHECK_GT(frame->NumTextures(), 0u);
517 scoped_refptr<VideoFrame> VideoFrame::WrapExternalData(
518 VideoPixelFormat format,
519 const gfx::Size& coded_size,
520 const gfx::Rect& visible_rect,
521 const gfx::Size& natural_size,
524 base::TimeDelta timestamp,
525 size_t encoded_data_size) {
526 absl::optional<VideoFrameLayout> layout;
527 if (format == PIXEL_FORMAT_ENCODED) {
528 auto plane = media::ColorPlaneLayout(encoded_data_size, 0, data_size);
529 layout = media::VideoFrameLayout::CreateWithPlanes(format, coded_size,
532 layout = GetDefaultLayout(format, coded_size);
537 return WrapExternalDataWithLayout(*layout, visible_rect, natural_size, data,
538 data_size, timestamp);
542 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDataWithLayout(
543 const VideoFrameLayout& layout,
544 const gfx::Rect& visible_rect,
545 const gfx::Size& natural_size,
548 base::TimeDelta timestamp) {
549 StorageType storage_type = STORAGE_UNOWNED_MEMORY;
551 if (!IsValidConfig(layout.format(), storage_type, layout.coded_size(),
552 visible_rect, natural_size) ||
553 !layout.FitsInContiguousBufferOfSize(data_size)) {
554 DLOG(ERROR) << "Invalid config: "
555 << ConfigToString(layout.format(), storage_type,
556 layout.coded_size(), visible_rect,
561 scoped_refptr<VideoFrame> frame = new VideoFrame(
562 layout, storage_type, visible_rect, natural_size, timestamp);
564 for (size_t i = 0; i < layout.planes().size(); ++i) {
565 frame->data_[i] = data + layout.planes()[i].offset;
572 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
573 VideoPixelFormat format,
574 const gfx::Size& coded_size,
575 const gfx::Rect& visible_rect,
576 const gfx::Size& natural_size,
580 const uint8_t* y_data,
581 const uint8_t* u_data,
582 const uint8_t* v_data,
583 base::TimeDelta timestamp) {
584 auto layout = VideoFrameLayout::CreateWithStrides(
585 format, coded_size, {y_stride, u_stride, v_stride});
587 DLOG(ERROR) << "Invalid layout.";
591 return WrapExternalYuvDataWithLayout(*layout, visible_rect, natural_size,
592 y_data, u_data, v_data, timestamp);
596 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvDataWithLayout(
597 const VideoFrameLayout& layout,
598 const gfx::Rect& visible_rect,
599 const gfx::Size& natural_size,
600 const uint8_t* y_data,
601 const uint8_t* u_data,
602 const uint8_t* v_data,
603 base::TimeDelta timestamp) {
604 const StorageType storage = STORAGE_UNOWNED_MEMORY;
605 const VideoPixelFormat format = layout.format();
606 if (!IsValidConfig(format, storage, layout.coded_size(), visible_rect,
608 DLOG(ERROR) << __func__ << " Invalid config."
609 << ConfigToString(format, storage, layout.coded_size(),
610 visible_rect, natural_size);
613 if (!IsYuvPlanar(format)) {
614 DLOG(ERROR) << __func__ << " Format is not YUV. " << format;
618 DCHECK_LE(NumPlanes(format), 3u);
619 scoped_refptr<VideoFrame> frame(
620 new VideoFrame(layout, storage, visible_rect, natural_size, timestamp));
621 frame->data_[kYPlane] = y_data;
622 frame->data_[kUPlane] = u_data;
623 frame->data_[kVPlane] = v_data;
628 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvaData(
629 VideoPixelFormat format,
630 const gfx::Size& coded_size,
631 const gfx::Rect& visible_rect,
632 const gfx::Size& natural_size,
637 const uint8_t* y_data,
638 const uint8_t* u_data,
639 const uint8_t* v_data,
640 const uint8_t* a_data,
641 base::TimeDelta timestamp) {
642 const StorageType storage = STORAGE_UNOWNED_MEMORY;
643 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
644 DLOG(ERROR) << __func__ << " Invalid config."
645 << ConfigToString(format, storage, coded_size, visible_rect,
650 if (NumPlanes(format) != 4) {
651 DLOG(ERROR) << "Expecting Y, U, V and A planes to be present for the video"
656 auto layout = VideoFrameLayout::CreateWithStrides(
657 format, coded_size, {y_stride, u_stride, v_stride, a_stride});
659 DLOG(ERROR) << "Invalid layout";
663 scoped_refptr<VideoFrame> frame(
664 new VideoFrame(*layout, storage, visible_rect, natural_size, timestamp));
665 frame->data_[kYPlane] = y_data;
666 frame->data_[kUPlane] = u_data;
667 frame->data_[kVPlane] = v_data;
668 frame->data_[kAPlane] = a_data;
673 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
674 VideoPixelFormat format,
675 const gfx::Size& coded_size,
676 const gfx::Rect& visible_rect,
677 const gfx::Size& natural_size,
680 const uint8_t* y_data,
681 const uint8_t* uv_data,
682 base::TimeDelta timestamp) {
683 const StorageType storage = STORAGE_UNOWNED_MEMORY;
684 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
685 DLOG(ERROR) << __func__ << " Invalid config."
686 << ConfigToString(format, storage, coded_size, visible_rect,
691 if (NumPlanes(format) != 2) {
692 DLOG(ERROR) << "Expecting Y, UV planes to be present for the video format.";
696 auto layout = VideoFrameLayout::CreateWithStrides(format, coded_size,
697 {y_stride, uv_stride});
699 DLOG(ERROR) << "Invalid layout";
703 scoped_refptr<VideoFrame> frame(
704 new VideoFrame(*layout, storage, visible_rect, natural_size, timestamp));
705 frame->data_[kYPlane] = y_data;
706 frame->data_[kUVPlane] = uv_data;
712 scoped_refptr<VideoFrame> VideoFrame::WrapExternalGpuMemoryBuffer(
713 const gfx::Rect& visible_rect,
714 const gfx::Size& natural_size,
715 std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer,
716 const gpu::MailboxHolder (&mailbox_holders)[kMaxPlanes],
717 ReleaseMailboxAndGpuMemoryBufferCB mailbox_holder_and_gmb_release_cb,
718 base::TimeDelta timestamp) {
719 const absl::optional<VideoPixelFormat> format =
720 GfxBufferFormatToVideoPixelFormat(gpu_memory_buffer->GetFormat());
723 constexpr StorageType storage = STORAGE_GPU_MEMORY_BUFFER;
724 const gfx::Size& coded_size = gpu_memory_buffer->GetSize();
725 if (!IsValidConfig(*format, storage, coded_size, visible_rect,
727 DLOG(ERROR) << __func__ << " Invalid config"
728 << ConfigToString(*format, storage, coded_size, visible_rect,
733 const size_t num_planes =
734 NumberOfPlanesForLinearBufferFormat(gpu_memory_buffer->GetFormat());
735 std::vector<ColorPlaneLayout> planes(num_planes);
736 for (size_t i = 0; i < num_planes; ++i)
737 planes[i].stride = gpu_memory_buffer->stride(i);
738 uint64_t modifier = gfx::NativePixmapHandle::kNoModifier;
739 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
740 if (gpu_memory_buffer->GetType() == gfx::NATIVE_PIXMAP) {
741 const auto gmb_handle = gpu_memory_buffer->CloneHandle();
742 if (gmb_handle.is_null() ||
743 gmb_handle.native_pixmap_handle.planes.empty()) {
744 DLOG(ERROR) << "Failed to clone the GpuMemoryBufferHandle";
747 if (gmb_handle.native_pixmap_handle.planes.size() != num_planes) {
748 DLOG(ERROR) << "Invalid number of planes="
749 << gmb_handle.native_pixmap_handle.planes.size()
750 << ", expected num_planes=" << num_planes;
753 for (size_t i = 0; i < num_planes; ++i) {
754 const auto& plane = gmb_handle.native_pixmap_handle.planes[i];
755 planes[i].stride = plane.stride;
756 planes[i].offset = plane.offset;
757 planes[i].size = plane.size;
759 modifier = gmb_handle.native_pixmap_handle.modifier;
763 const auto layout = VideoFrameLayout::CreateWithPlanes(
764 *format, coded_size, std::move(planes),
765 VideoFrameLayout::kBufferAddressAlignment, modifier);
767 DLOG(ERROR) << __func__ << " Invalid layout";
771 scoped_refptr<VideoFrame> frame =
772 new VideoFrame(*layout, storage, visible_rect, natural_size, timestamp);
774 DLOG(ERROR) << __func__ << " Couldn't create VideoFrame instance";
777 frame->gpu_memory_buffer_ = std::move(gpu_memory_buffer);
778 memcpy(&frame->mailbox_holders_, mailbox_holders,
779 sizeof(frame->mailbox_holders_));
780 frame->mailbox_holders_and_gmb_release_cb_ =
781 std::move(mailbox_holder_and_gmb_release_cb);
785 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
787 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
788 const VideoFrameLayout& layout,
789 const gfx::Rect& visible_rect,
790 const gfx::Size& natural_size,
791 std::vector<base::ScopedFD> dmabuf_fds,
792 base::TimeDelta timestamp) {
793 const StorageType storage = STORAGE_DMABUFS;
794 const VideoPixelFormat format = layout.format();
795 const gfx::Size& coded_size = layout.coded_size();
796 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
797 DLOG(ERROR) << __func__ << " Invalid config."
798 << ConfigToString(format, storage, coded_size, visible_rect,
803 if (dmabuf_fds.empty() || dmabuf_fds.size() > NumPlanes(format)) {
804 DLOG(ERROR) << __func__ << " Incorrect number of dmabuf fds provided, got: "
805 << dmabuf_fds.size() << ", expected 1 to " << NumPlanes(format);
809 gpu::MailboxHolder mailbox_holders[kMaxPlanes];
810 scoped_refptr<VideoFrame> frame =
811 new VideoFrame(layout, storage, visible_rect, natural_size, timestamp);
813 DLOG(ERROR) << __func__ << " Couldn't create VideoFrame instance.";
816 memcpy(&frame->mailbox_holders_, mailbox_holders,
817 sizeof(frame->mailbox_holders_));
818 frame->mailbox_holders_and_gmb_release_cb_ =
819 ReleaseMailboxAndGpuMemoryBufferCB();
821 base::MakeRefCounted<DmabufHolder>(std::move(dmabuf_fds));
822 DCHECK(frame->HasDmaBufs());
828 #if BUILDFLAG(IS_APPLE)
830 scoped_refptr<VideoFrame> VideoFrame::WrapUnacceleratedIOSurface(
831 gfx::GpuMemoryBufferHandle handle,
832 const gfx::Rect& visible_rect,
833 base::TimeDelta timestamp) {
834 if (handle.type != gfx::GpuMemoryBufferType::IO_SURFACE_BUFFER) {
835 DLOG(ERROR) << "Non-IOSurface handle.";
838 gfx::ScopedIOSurface io_surface = handle.io_surface;
843 // Only support NV12 IOSurfaces.
844 const OSType cv_pixel_format = IOSurfaceGetPixelFormat(io_surface.get());
845 if (cv_pixel_format != kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
846 DLOG(ERROR) << "Invalid (non-NV12) pixel format.";
849 const VideoPixelFormat pixel_format = PIXEL_FORMAT_NV12;
851 // Retrieve the layout parameters for |io_surface_|.
852 const size_t num_planes = IOSurfaceGetPlaneCount(io_surface.get());
853 const gfx::Size size(IOSurfaceGetWidth(io_surface.get()),
854 IOSurfaceGetHeight(io_surface.get()));
855 std::vector<int32_t> strides;
856 for (size_t i = 0; i < num_planes; ++i)
857 strides.push_back(IOSurfaceGetBytesPerRowOfPlane(io_surface.get(), i));
858 absl::optional<VideoFrameLayout> layout =
859 media::VideoFrameLayout::CreateWithStrides(pixel_format, size, strides);
861 DLOG(ERROR) << "Invalid layout.";
865 const StorageType storage_type = STORAGE_UNOWNED_MEMORY;
866 if (!IsValidConfig(pixel_format, storage_type, size, visible_rect, size)) {
867 DLOG(ERROR) << "Invalid config.";
871 // Lock the IOSurface for CPU read access. After the VideoFrame is created,
872 // add a destruction callback to unlock the IOSurface.
873 kern_return_t lock_result =
874 IOSurfaceLock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
875 if (lock_result != kIOReturnSuccess) {
876 DLOG(ERROR) << "Failed to lock IOSurface.";
880 [](base::apple::ScopedCFTypeRef<IOSurfaceRef> io_surface) {
881 IOSurfaceUnlock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
884 scoped_refptr<VideoFrame> frame =
885 new VideoFrame(*layout, storage_type, visible_rect, size, timestamp);
886 for (size_t i = 0; i < num_planes; ++i) {
887 frame->data_[i] = reinterpret_cast<uint8_t*>(
888 IOSurfaceGetBaseAddressOfPlane(io_surface.get(), i));
890 frame->AddDestructionObserver(
891 base::BindOnce(unlock_lambda, std::move(io_surface)));
896 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
897 CVPixelBufferRef cv_pixel_buffer,
898 base::TimeDelta timestamp) {
899 DCHECK(cv_pixel_buffer);
900 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
902 const OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
903 VideoPixelFormat format;
904 // There are very few compatible CV pixel formats, so just check each.
905 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
906 format = PIXEL_FORMAT_I420;
907 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
908 format = PIXEL_FORMAT_I444;
909 } else if (cv_format == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange) {
910 format = PIXEL_FORMAT_NV12;
911 } else if (cv_format ==
912 kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar) {
913 format = PIXEL_FORMAT_NV12A;
915 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
919 const gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
920 const gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
921 const gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
922 const StorageType storage = STORAGE_UNOWNED_MEMORY;
924 if (!IsValidConfig(format, storage, coded_size, visible_rect, natural_size)) {
925 DLOG(ERROR) << __func__ << " Invalid config."
926 << ConfigToString(format, storage, coded_size, visible_rect,
931 auto layout = VideoFrameLayout::Create(format, coded_size);
933 DLOG(ERROR) << "Invalid layout.";
937 scoped_refptr<VideoFrame> frame(
938 new VideoFrame(*layout, storage, visible_rect, natural_size, timestamp));
940 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
946 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
947 scoped_refptr<VideoFrame> frame,
948 VideoPixelFormat format,
949 const gfx::Rect& visible_rect,
950 const gfx::Size& natural_size) {
951 DCHECK(frame->visible_rect().Contains(visible_rect));
953 if (!AreValidPixelFormatsForWrap(frame->format(), format)) {
954 DLOG(ERROR) << __func__ << " Invalid format conversion."
955 << VideoPixelFormatToString(frame->format()) << " to "
956 << VideoPixelFormatToString(format);
960 if (!IsValidConfig(format, frame->storage_type(), frame->coded_size(),
961 visible_rect, natural_size)) {
962 DLOG(ERROR) << __func__ << " Invalid config."
963 << ConfigToString(format, frame->storage_type(),
964 frame->coded_size(), visible_rect,
969 size_t new_plane_count = NumPlanes(format);
970 absl::optional<VideoFrameLayout> new_layout;
971 if (format == frame->format()) {
972 new_layout = frame->layout();
974 std::vector<ColorPlaneLayout> new_planes = frame->layout().planes();
975 if (new_plane_count > new_planes.size()) {
976 DLOG(ERROR) << " Wrapping frame has more planes than old one."
977 << " old plane count: " << new_planes.size()
978 << " new plane count: " << new_plane_count;
981 new_planes.resize(new_plane_count);
982 new_layout = VideoFrameLayout::CreateWithPlanes(format, frame->coded_size(),
986 if (!new_layout.has_value()) {
987 DLOG(ERROR) << " Can't create layout for the wrapping frame";
991 scoped_refptr<VideoFrame> wrapping_frame(
992 new VideoFrame(new_layout.value(), frame->storage_type(), visible_rect,
993 natural_size, frame->timestamp()));
995 // Copy all metadata to the wrapped frame->
996 wrapping_frame->metadata().MergeMetadataFrom(frame->metadata());
997 wrapping_frame->set_color_space(frame->ColorSpace());
998 wrapping_frame->set_hdr_metadata(frame->hdr_metadata());
1000 if (frame->IsMappable()) {
1001 for (size_t i = 0; i < new_plane_count; ++i) {
1002 wrapping_frame->data_[i] = frame->data_[i];
1006 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1007 DCHECK(frame->dmabuf_fds_);
1008 // If there are any |dmabuf_fds_| plugged in, we should refer them too.
1009 wrapping_frame->dmabuf_fds_ = frame->dmabuf_fds_;
1012 if (frame->storage_type() == STORAGE_SHMEM) {
1013 DCHECK(frame->shm_region_ && frame->shm_region_->IsValid());
1014 wrapping_frame->BackWithSharedMemory(frame->shm_region_);
1017 // Don't let a Matryoshka doll of frames occur. Do this here instead of above
1018 // since |frame| may have different metadata than |frame->wrapped_frame_|.
1020 // We must still keep |frame| alive though since it may have destruction
1021 // observers which signal that the underlying resource is okay to reuse. E.g.,
1023 if (frame->wrapped_frame_) {
1024 wrapping_frame->AddDestructionObserver(base::DoNothingWithBoundArgs(frame));
1025 frame = frame->wrapped_frame_;
1028 #if defined(TIZEN_TBM_SUPPORT)
1029 if (frame->format() == PIXEL_FORMAT_TBM_INTER_PROCESS_BUFFER ||
1030 frame->format() == PIXEL_FORMAT_TBM_SURFACE) {
1031 wrapping_frame->buffer_handle_ = frame->buffer_handle_;
1035 wrapping_frame->wrapped_frame_ = std::move(frame);
1036 return wrapping_frame;
1040 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
1041 auto layout = VideoFrameLayout::Create(PIXEL_FORMAT_UNKNOWN, gfx::Size());
1043 DLOG(ERROR) << "Invalid layout.";
1046 scoped_refptr<VideoFrame> frame =
1047 new VideoFrame(*layout, STORAGE_UNKNOWN, gfx::Rect(), gfx::Size(),
1048 kNoTimestamp, FrameControlType::kEos);
1049 frame->metadata().end_of_stream = true;
1054 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
1055 const gfx::Size& size,
1059 base::TimeDelta timestamp) {
1060 scoped_refptr<VideoFrame> frame =
1061 CreateFrame(PIXEL_FORMAT_I420, size, gfx::Rect(size), size, timestamp);
1063 FillYUV(frame.get(), y, u, v);
1068 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
1069 const uint8_t kBlackY = 0x00;
1070 const uint8_t kBlackUV = 0x80;
1071 const base::TimeDelta kZero;
1072 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
1076 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
1077 const gfx::Size& size) {
1078 const uint8_t kBlackY = 0x00;
1079 const uint8_t kBlackUV = 0x00;
1080 const uint8_t kTransparentA = 0x00;
1081 const base::TimeDelta kZero;
1082 scoped_refptr<VideoFrame> frame =
1083 CreateFrame(PIXEL_FORMAT_I420A, size, gfx::Rect(size), size, kZero);
1085 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
1089 #if defined(TIZEN_VIDEO_HOLE)
1090 // This block and other blocks wrapped around #if defined(TIZEN_VIDEO_HOLE) is
1091 // not maintained by the general compositor team. Please contact
1092 // wonsik@chromium.org .
1095 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
1096 const gfx::Size& natural_size) {
1097 auto layout = VideoFrameLayout::Create(PIXEL_FORMAT_UNKNOWN, natural_size);
1098 scoped_refptr<VideoFrame> frame =
1099 new VideoFrame(*layout, StorageType::STORAGE_HOLE,
1100 gfx::Rect(natural_size), natural_size, base::TimeDelta());
1103 #endif // defined(TIZEN_VIDEO_HOLE)
1106 size_t VideoFrame::NumPlanes(VideoPixelFormat format) {
1107 return VideoFrameLayout::NumPlanes(format);
1111 size_t VideoFrame::AllocationSize(VideoPixelFormat format,
1112 const gfx::Size& coded_size) {
1114 for (size_t i = 0; i < NumPlanes(format); ++i)
1115 total += PlaneSize(format, i, coded_size).GetArea();
1120 gfx::Size VideoFrame::PlaneSize(VideoPixelFormat format,
1122 const gfx::Size& coded_size) {
1123 gfx::Size size = PlaneSizeInSamples(format, plane, coded_size);
1124 size.set_width(size.width() * BytesPerElement(format, plane));
1129 gfx::Size VideoFrame::PlaneSizeInSamples(VideoPixelFormat format,
1131 const gfx::Size& coded_size) {
1132 DCHECK(IsValidPlane(format, plane));
1134 int width = coded_size.width();
1135 int height = coded_size.height();
1136 if (RequiresEvenSizeAllocation(format)) {
1137 // Align to multiple-of-two size overall. This ensures that non-subsampled
1138 // planes can be addressed by pixel with the same scaling as the subsampled
1140 width = base::bits::AlignUp(width, 2);
1141 height = base::bits::AlignUp(height, 2);
1144 const gfx::Size subsample = SampleSize(format, plane);
1145 DCHECK(width % subsample.width() == 0);
1146 DCHECK(height % subsample.height() == 0);
1147 return gfx::Size(width / subsample.width(), height / subsample.height());
1151 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format,
1153 DCHECK(IsValidPlane(format, plane));
1154 const int bits_per_element = 8 * BytesPerElement(format, plane);
1155 const int horiz_pixels_per_element = SampleSize(format, plane).width();
1156 DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
1157 return bits_per_element / horiz_pixels_per_element;
1161 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format, size_t plane) {
1162 DCHECK(IsValidPlane(format, plane));
1163 return PlaneHorizontalBitsPerPixel(format, plane) /
1164 SampleSize(format, plane).height();
1168 size_t VideoFrame::RowBytes(size_t plane, VideoPixelFormat format, int width) {
1169 DCHECK(IsValidPlane(format, plane));
1170 return BytesPerElement(format, plane) * Columns(plane, format, width);
1174 int VideoFrame::BytesPerElement(VideoPixelFormat format, size_t plane) {
1175 DCHECK(IsValidPlane(format, plane));
1177 case PIXEL_FORMAT_RGBAF16:
1179 case PIXEL_FORMAT_ARGB:
1180 case PIXEL_FORMAT_BGRA:
1181 case PIXEL_FORMAT_XRGB:
1182 case PIXEL_FORMAT_ABGR:
1183 case PIXEL_FORMAT_XBGR:
1184 case PIXEL_FORMAT_XR30:
1185 case PIXEL_FORMAT_XB30:
1186 #if BUILDFLAG(IS_TIZEN)
1187 case PIXEL_FORMAT_ENCODED:
1190 case PIXEL_FORMAT_RGB24:
1192 case PIXEL_FORMAT_Y16:
1193 case PIXEL_FORMAT_UYVY:
1194 case PIXEL_FORMAT_YUY2:
1195 case PIXEL_FORMAT_YUV420P9:
1196 case PIXEL_FORMAT_YUV422P9:
1197 case PIXEL_FORMAT_YUV444P9:
1198 case PIXEL_FORMAT_YUV420P10:
1199 case PIXEL_FORMAT_YUV422P10:
1200 case PIXEL_FORMAT_YUV444P10:
1201 case PIXEL_FORMAT_YUV420P12:
1202 case PIXEL_FORMAT_YUV422P12:
1203 case PIXEL_FORMAT_YUV444P12:
1204 case PIXEL_FORMAT_YUV420AP10:
1205 case PIXEL_FORMAT_YUV422AP10:
1206 case PIXEL_FORMAT_YUV444AP10:
1208 #if defined(TIZEN_TBM_SUPPORT)
1209 case PIXEL_FORMAT_TBM_INTER_PROCESS_BUFFER:
1211 case PIXEL_FORMAT_NV12:
1212 case PIXEL_FORMAT_NV21: {
1213 static const int bytes_per_element[] = {1, 2};
1214 DCHECK_LT(plane, std::size(bytes_per_element));
1215 return bytes_per_element[plane];
1217 case PIXEL_FORMAT_NV12A: {
1218 static const int bytes_per_element[] = {1, 2, 1};
1219 DCHECK_LT(plane, std::size(bytes_per_element));
1220 return bytes_per_element[plane];
1222 case PIXEL_FORMAT_P016LE: {
1223 static const int bytes_per_element[] = {1, 2};
1224 DCHECK_LT(plane, std::size(bytes_per_element));
1225 return bytes_per_element[plane] * 2;
1227 case PIXEL_FORMAT_YV12:
1228 case PIXEL_FORMAT_I420:
1229 case PIXEL_FORMAT_I422:
1230 case PIXEL_FORMAT_I420A:
1231 case PIXEL_FORMAT_I444:
1232 case PIXEL_FORMAT_I422A:
1233 case PIXEL_FORMAT_I444A:
1235 case PIXEL_FORMAT_MJPEG:
1237 #if defined(TIZEN_TBM_SUPPORT)
1238 case PIXEL_FORMAT_TBM_SURFACE:
1240 case PIXEL_FORMAT_UNKNOWN:
1243 NOTREACHED_NORETURN();
1247 std::vector<int32_t> VideoFrame::ComputeStrides(VideoPixelFormat format,
1248 const gfx::Size& coded_size) {
1249 std::vector<int32_t> strides;
1250 const size_t num_planes = NumPlanes(format);
1251 if (num_planes == 1) {
1252 strides.push_back(RowBytes(0, format, coded_size.width()));
1254 for (size_t plane = 0; plane < num_planes; ++plane) {
1255 strides.push_back(base::bits::AlignUp(
1256 RowBytes(plane, format, coded_size.width()), kFrameAddressAlignment));
1263 size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) {
1264 DCHECK(IsValidPlane(format, plane));
1265 const int sample_height = SampleSize(format, plane).height();
1266 return base::bits::AlignUp(height, sample_height) / sample_height;
1270 size_t VideoFrame::Columns(size_t plane, VideoPixelFormat format, int width) {
1271 DCHECK(IsValidPlane(format, plane));
1272 const int sample_width = SampleSize(format, plane).width();
1273 return base::bits::AlignUp(width, sample_width) / sample_width;
1277 void VideoFrame::HashFrameForTesting(base::MD5Context* context,
1278 const VideoFrame& frame) {
1280 for (size_t plane = 0; plane < NumPlanes(frame.format()); ++plane) {
1281 for (int row = 0; row < frame.rows(plane); ++row) {
1282 base::MD5Update(context, base::StringPiece(reinterpret_cast<const char*>(
1284 frame.stride(plane) * row),
1285 frame.row_bytes(plane)));
1290 void VideoFrame::BackWithSharedMemory(
1291 const base::ReadOnlySharedMemoryRegion* region) {
1292 DCHECK(!shm_region_);
1293 DCHECK(!owned_shm_region_.IsValid());
1294 // Either we should be backing a frame created with WrapExternal*, or we are
1295 // wrapping an existing STORAGE_SHMEM, in which case the storage type has
1296 // already been set to STORAGE_SHMEM.
1297 DCHECK(storage_type_ == STORAGE_UNOWNED_MEMORY ||
1298 storage_type_ == STORAGE_SHMEM);
1299 DCHECK(region && region->IsValid());
1300 storage_type_ = STORAGE_SHMEM;
1301 shm_region_ = region;
1304 void VideoFrame::BackWithOwnedSharedMemory(
1305 base::ReadOnlySharedMemoryRegion region,
1306 base::ReadOnlySharedMemoryMapping mapping) {
1307 DCHECK(!shm_region_);
1308 DCHECK(!owned_shm_region_.IsValid());
1309 // We should be backing a frame created with WrapExternal*. We cannot be
1310 // wrapping an existing STORAGE_SHMEM, as the region is unowned in that case.
1311 DCHECK(storage_type_ == STORAGE_UNOWNED_MEMORY);
1312 storage_type_ = STORAGE_SHMEM;
1313 owned_shm_region_ = std::move(region);
1314 shm_region_ = &owned_shm_region_;
1315 owned_shm_mapping_ = std::move(mapping);
1318 bool VideoFrame::IsMappable() const {
1319 return IsStorageTypeMappable(storage_type_);
1322 bool VideoFrame::HasTextures() const {
1323 return wrapped_frame_ ? wrapped_frame_->HasTextures()
1324 : !mailbox_holders_[0].mailbox.IsZero();
1327 size_t VideoFrame::NumTextures() const {
1329 return wrapped_frame_->NumTextures();
1335 for (; i < NumPlanes(format()); ++i) {
1336 const auto& mailbox = mailbox_holders_[i].mailbox;
1337 if (mailbox.IsZero())
1343 bool VideoFrame::HasGpuMemoryBuffer() const {
1344 return wrapped_frame_ ? wrapped_frame_->HasGpuMemoryBuffer()
1345 : !!gpu_memory_buffer_;
1348 gfx::GpuMemoryBuffer* VideoFrame::GetGpuMemoryBuffer() const {
1349 return wrapped_frame_ ? wrapped_frame_->GetGpuMemoryBuffer()
1350 : gpu_memory_buffer_.get();
1353 bool VideoFrame::IsSameAllocation(VideoPixelFormat format,
1354 const gfx::Size& coded_size,
1355 const gfx::Rect& visible_rect,
1356 const gfx::Size& natural_size) const {
1357 // CreateFrameInternal() changes coded_size to new_coded_size. Match that
1359 const gfx::Size new_coded_size = DetermineAlignedSize(format, coded_size);
1360 return this->format() == format && this->coded_size() == new_coded_size &&
1361 visible_rect_ == visible_rect && natural_size_ == natural_size;
1364 gfx::ColorSpace VideoFrame::ColorSpace() const {
1365 return color_space_;
1368 bool VideoFrame::RequiresExternalSampler() const {
1369 const bool is_multiplanar_pixel_format = format() == PIXEL_FORMAT_NV12 ||
1370 format() == PIXEL_FORMAT_YV12 ||
1371 format() == PIXEL_FORMAT_P016LE;
1373 // With SharedImageFormats NumTextures() is always 1. Use
1374 // SharedImageFormatType to check for NumTextures for legacy formats and
1375 // kSharedImageFormatExternalSampler for SharedImageFormats. Note that
1376 // kSharedImageFormatExternalSampler is set only for multiplanar formats.
1377 const bool requires_external_sampler =
1378 shared_image_format_type() ==
1379 SharedImageFormatType::kSharedImageFormatExternalSampler ||
1380 (is_multiplanar_pixel_format &&
1381 (NumTextures() == 1 &&
1382 shared_image_format_type() == SharedImageFormatType::kLegacy));
1384 // The texture target can be 0 for Fuchsia.
1385 DCHECK(!requires_external_sampler ||
1386 (is_multiplanar_pixel_format &&
1387 (mailbox_holder(0).texture_target == GL_TEXTURE_EXTERNAL_OES ||
1388 mailbox_holder(0).texture_target == 0u)));
1389 return requires_external_sampler;
1392 int VideoFrame::row_bytes(size_t plane) const {
1393 return RowBytes(plane, format(), coded_size().width());
1396 int VideoFrame::rows(size_t plane) const {
1397 return Rows(plane, format(), coded_size().height());
1400 int VideoFrame::columns(size_t plane) const {
1401 return Columns(plane, format(), coded_size().width());
1404 template <typename T>
1405 T VideoFrame::GetVisibleDataInternal(T data, size_t plane) const {
1406 DCHECK(IsValidPlane(format(), plane));
1407 DCHECK(IsMappable());
1408 if (UNLIKELY(!data)) {
1412 // Calculate an offset that is properly aligned for all planes.
1413 const gfx::Size alignment = CommonAlignment(format());
1414 const gfx::Point offset(
1415 base::bits::AlignDown(visible_rect_.x(), alignment.width()),
1416 base::bits::AlignDown(visible_rect_.y(), alignment.height()));
1418 const gfx::Size subsample = SampleSize(format(), plane);
1419 DCHECK(offset.x() % subsample.width() == 0);
1420 DCHECK(offset.y() % subsample.height() == 0);
1422 #if defined(TIZEN_TBM_SUPPORT)
1423 const VideoPixelFormat fmt = format();
1424 if (PIXEL_FORMAT_TBM_INTER_PROCESS_BUFFER == fmt) {
1426 bufmgr_ = tbm_bufmgr_init(-1);
1428 base::AutoLock autolock(tbm_map_lock_);
1430 bo_[plane] = tbm_bo_import(bufmgr_, buffer_handle_.key[plane]);
1432 tbm_bo_map(bo_[plane], TBM_DEVICE_CPU, TBM_OPTION_READ);
1435 (uint8_t*)bo_handle_[plane].ptr +
1436 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
1437 BytesPerElement(format(), plane) * // Column offset.
1438 (offset.x() / subsample.width());
1445 stride(plane) * (offset.y() / subsample.height()) + // Row offset.
1446 BytesPerElement(format(), plane) * // Column offset.
1447 (offset.x() / subsample.width());
1450 const uint8_t* VideoFrame::visible_data(size_t plane) const {
1451 return GetVisibleDataInternal(data(plane), plane);
1454 uint8_t* VideoFrame::GetWritableVisibleData(size_t plane) {
1455 // TODO(crbug.com/1435549): Also CHECK that the storage type isn't
1456 // STORAGE_UNOWNED_MEMORY once non-compliant usages are fixed.
1457 CHECK_NE(storage_type_, STORAGE_SHMEM);
1458 return GetVisibleDataInternal(writable_data(plane), plane);
1461 const gpu::MailboxHolder& VideoFrame::mailbox_holder(
1462 size_t texture_index) const {
1463 #if defined(TIZEN_TBM_SUPPORT)
1464 DCHECK(IsTBMBackend() || HasTextures());
1466 DCHECK(HasTextures());
1468 DCHECK(IsValidPlane(format(), texture_index));
1469 return wrapped_frame_ ? wrapped_frame_->mailbox_holder(texture_index)
1470 : mailbox_holders_[texture_index];
1473 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1474 const std::vector<base::ScopedFD>& VideoFrame::DmabufFds() const {
1475 DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
1477 return dmabuf_fds_->fds();
1480 bool VideoFrame::HasDmaBufs() const {
1481 return dmabuf_fds_->size() > 0;
1484 bool VideoFrame::IsSameDmaBufsAs(const VideoFrame& frame) const {
1485 return storage_type_ == STORAGE_DMABUFS &&
1486 frame.storage_type_ == STORAGE_DMABUFS &&
1487 &DmabufFds() == &frame.DmabufFds();
1491 #if BUILDFLAG(IS_APPLE)
1492 CVPixelBufferRef VideoFrame::CvPixelBuffer() const {
1493 return cv_pixel_buffer_.get();
1497 void VideoFrame::SetReleaseMailboxCB(ReleaseMailboxCB release_mailbox_cb) {
1498 DCHECK(release_mailbox_cb);
1499 DCHECK(!mailbox_holders_and_gmb_release_cb_);
1500 // We don't relay SetReleaseMailboxCB to |wrapped_frame_| because the method
1501 // is not thread safe. This method should only be called by the owner of
1502 // |wrapped_frame_| directly.
1503 DCHECK(!wrapped_frame_);
1504 mailbox_holders_and_gmb_release_cb_ =
1505 WrapReleaseMailboxCB(std::move(release_mailbox_cb));
1508 void VideoFrame::SetReleaseMailboxAndGpuMemoryBufferCB(
1509 ReleaseMailboxAndGpuMemoryBufferCB release_mailbox_cb) {
1510 // See remarks in SetReleaseMailboxCB.
1511 DCHECK(release_mailbox_cb);
1512 DCHECK(!mailbox_holders_and_gmb_release_cb_);
1513 DCHECK(!wrapped_frame_);
1514 mailbox_holders_and_gmb_release_cb_ = std::move(release_mailbox_cb);
1517 bool VideoFrame::HasReleaseMailboxCB() const {
1518 return wrapped_frame_ ? wrapped_frame_->HasReleaseMailboxCB()
1519 : !!mailbox_holders_and_gmb_release_cb_;
1522 void VideoFrame::AddDestructionObserver(base::OnceClosure callback) {
1523 DCHECK(!callback.is_null());
1524 base::AutoLock lock(done_callbacks_lock_);
1525 done_callbacks_.push_back(std::move(callback));
1528 gpu::SyncToken VideoFrame::UpdateReleaseSyncToken(SyncTokenClient* client) {
1529 #if defined(TIZEN_TBM_SUPPORT)
1530 DCHECK(IsTBMBackend() || HasTextures());
1532 DCHECK(HasTextures());
1534 if (wrapped_frame_) {
1535 return wrapped_frame_->UpdateReleaseSyncToken(client);
1537 base::AutoLock locker(release_sync_token_lock_);
1538 // Must wait on the previous sync point before inserting a new sync point so
1539 // that |mailbox_holders_and_gmb_release_cb_| guarantees the previous sync
1540 // point occurred when it waits on |release_sync_token_|.
1541 if (release_sync_token_.HasData())
1542 client->WaitSyncToken(release_sync_token_);
1543 client->GenerateSyncToken(&release_sync_token_);
1544 return release_sync_token_;
1547 gpu::SyncToken VideoFrame::UpdateMailboxHolderSyncToken(
1549 SyncTokenClient* client) {
1550 DCHECK(HasOneRef());
1551 DCHECK(HasTextures());
1552 DCHECK(!wrapped_frame_);
1553 DCHECK_LT(plane, kMaxPlanes);
1555 // No lock is required due to the HasOneRef() check.
1556 auto& token = mailbox_holders_[plane].sync_token;
1557 if (token.HasData())
1558 client->WaitSyncToken(token);
1559 client->GenerateSyncToken(&token);
1563 std::string VideoFrame::AsHumanReadableString() const {
1564 if (metadata().end_of_stream)
1565 return "end of stream";
1567 std::ostringstream s;
1568 s << ConfigToString(format(), storage_type_, coded_size(), visible_rect_,
1570 << " timestamp:" << timestamp_.InMicroseconds();
1572 s << " textures: " << NumTextures();
1576 size_t VideoFrame::BitDepth() const {
1577 return media::BitDepth(format());
1580 VideoFrame::VideoFrame(const VideoFrameLayout& layout,
1581 StorageType storage_type,
1582 const gfx::Rect& visible_rect,
1583 const gfx::Size& natural_size,
1584 base::TimeDelta timestamp,
1585 FrameControlType frame_control_type)
1587 storage_type_(storage_type),
1588 visible_rect_(Intersection(visible_rect, gfx::Rect(layout.coded_size()))),
1589 natural_size_(natural_size),
1590 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
1591 dmabuf_fds_(base::MakeRefCounted<DmabufHolder>()),
1593 #if defined(TIZEN_TBM_SUPPORT)
1598 timestamp_(timestamp),
1599 unique_id_(GetNextID()) {
1600 DCHECK(IsValidConfigInternal(format(), frame_control_type, coded_size(),
1601 visible_rect_, natural_size_));
1602 DCHECK(visible_rect_ == visible_rect)
1603 << "visible_rect " << visible_rect.ToString() << " exceeds coded_size "
1604 << coded_size().ToString();
1605 memset(&mailbox_holders_, 0, sizeof(mailbox_holders_));
1606 memset(&data_, 0, sizeof(data_));
1607 #if defined(TIZEN_TBM_SUPPORT)
1608 memset(bo_handle_, 0, sizeof(tbm_bo_handle) * TBM_BO_NUM_MAX);
1612 VideoFrame::~VideoFrame() {
1613 if (mailbox_holders_and_gmb_release_cb_) {
1614 gpu::SyncToken release_sync_token;
1616 // To ensure that changes to |release_sync_token_| are visible on this
1617 // thread (imply a memory barrier).
1618 base::AutoLock locker(release_sync_token_lock_);
1619 release_sync_token = release_sync_token_;
1621 std::move(mailbox_holders_and_gmb_release_cb_)
1622 .Run(release_sync_token, std::move(gpu_memory_buffer_));
1625 std::vector<base::OnceClosure> done_callbacks;
1627 base::AutoLock lock(done_callbacks_lock_);
1628 done_callbacks = std::move(done_callbacks_);
1630 for (auto& callback : done_callbacks)
1631 std::move(callback).Run();
1633 #if defined(TIZEN_TBM_SUPPORT)
1634 for (int i = 0; i < TBM_BO_NUM_MAX; i++) {
1635 if (bo_handle_[i].ptr) {
1636 if (!tbm_bo_unmap(bo_[i]))
1637 LOG(WARNING) << "unmap bo failed!";
1638 tbm_bo_unref(bo_[i]);
1639 bo_handle_[i].ptr = nullptr;
1646 std::string VideoFrame::ConfigToString(const VideoPixelFormat format,
1647 const StorageType storage_type,
1648 const gfx::Size& coded_size,
1649 const gfx::Rect& visible_rect,
1650 const gfx::Size& natural_size) {
1651 return base::StringPrintf(
1652 "format:%s storage_type:%s coded_size:%s visible_rect:%s natural_size:%s",
1653 VideoPixelFormatToString(format).c_str(),
1654 StorageTypeToString(storage_type).c_str(), coded_size.ToString().c_str(),
1655 visible_rect.ToString().c_str(), natural_size.ToString().c_str());
1659 gfx::Size VideoFrame::DetermineAlignedSize(VideoPixelFormat format,
1660 const gfx::Size& dimensions) {
1661 const gfx::Size alignment = CommonAlignment(format);
1662 const gfx::Size adjusted =
1663 gfx::Size(base::bits::AlignUp(dimensions.width(), alignment.width()),
1664 base::bits::AlignUp(dimensions.height(), alignment.height()));
1665 DCHECK((adjusted.width() % alignment.width() == 0) &&
1666 (adjusted.height() % alignment.height() == 0));
1671 bool VideoFrame::IsValidSize(const gfx::Size& coded_size,
1672 const gfx::Rect& visible_rect,
1673 const gfx::Size& natural_size) {
1674 return IsValidCodedSize(coded_size) && IsValidCodedSize(natural_size) &&
1675 !(visible_rect.x() < 0 || visible_rect.y() < 0 ||
1676 visible_rect.right() > coded_size.width() ||
1677 visible_rect.bottom() > coded_size.height());
1681 bool VideoFrame::IsValidCodedSize(const gfx::Size& size) {
1682 const int size_area = size.GetCheckedArea().ValueOrDefault(INT_MAX);
1683 static_assert(limits::kMaxCanvas < INT_MAX, "");
1684 return size_area <= limits::kMaxCanvas &&
1685 size.width() <= limits::kMaxDimension &&
1686 size.height() <= limits::kMaxDimension;
1690 bool VideoFrame::IsValidConfigInternal(VideoPixelFormat format,
1691 FrameControlType frame_control_type,
1692 const gfx::Size& coded_size,
1693 const gfx::Rect& visible_rect,
1694 const gfx::Size& natural_size) {
1695 // Check maximum limits for all formats.
1696 if (!IsValidSize(coded_size, visible_rect, natural_size)) {
1700 switch (frame_control_type) {
1701 case FrameControlType::kNone:
1702 // Check that software-allocated buffer formats are not empty.
1703 return !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
1704 !natural_size.IsEmpty();
1705 case FrameControlType::kEos:
1706 DCHECK_EQ(format, PIXEL_FORMAT_UNKNOWN);
1707 return coded_size.IsEmpty() && visible_rect.IsEmpty() &&
1708 natural_size.IsEmpty();
1709 case FrameControlType::kVideoHole:
1710 DCHECK_EQ(format, PIXEL_FORMAT_UNKNOWN);
1711 return !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
1712 !natural_size.IsEmpty();
1717 absl::optional<VideoFrameLayout>
1718 VideoFrame::CreateFullySpecifiedLayoutWithStrides(VideoPixelFormat format,
1719 const gfx::Size& coded_size) {
1720 const gfx::Size new_coded_size = DetermineAlignedSize(format, coded_size);
1721 auto layout = VideoFrameLayout::CreateWithStrides(
1722 format, new_coded_size, ComputeStrides(format, new_coded_size));
1726 // This whole method would be in `VideoFrameLayout::CreateWithStrides()`
1727 // instead of here, except that we know how to calculate the plane sizes.
1728 // This should be refactored.
1729 auto plane_sizes = CalculatePlaneSize(*layout);
1730 // Fill in the offsets as well, since WrapExternalDataWithLayout() uses them
1731 // to figure out where the data is.
1733 const size_t num_planes = plane_sizes.size();
1734 std::vector<ColorPlaneLayout> new_planes;
1735 new_planes.reserve(num_planes);
1736 for (size_t plane = 0; plane < plane_sizes.size(); plane++) {
1737 new_planes.emplace_back(layout->planes()[plane].stride, offset,
1738 plane_sizes[plane]);
1739 offset += plane_sizes[plane];
1741 return VideoFrameLayout::CreateWithPlanes(format, new_coded_size,
1742 std::move(new_planes));
1746 scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
1747 VideoPixelFormat format,
1748 const gfx::Size& coded_size,
1749 const gfx::Rect& visible_rect,
1750 const gfx::Size& natural_size,
1751 base::TimeDelta timestamp,
1752 bool zero_initialize_memory) {
1753 // Since we're creating a new frame (and allocating memory for it
1754 // ourselves), we can pad the requested |coded_size| if necessary if the
1755 // request does not line up on sample boundaries. See discussion at
1756 // http://crrev.com/1240833003
1757 const gfx::Size new_coded_size = DetermineAlignedSize(format, coded_size);
1758 auto layout = VideoFrameLayout::CreateWithStrides(
1759 format, new_coded_size, ComputeStrides(format, new_coded_size));
1761 DLOG(ERROR) << "Invalid layout.";
1765 return CreateFrameWithLayout(*layout, visible_rect, natural_size, timestamp,
1766 zero_initialize_memory);
1769 scoped_refptr<VideoFrame> VideoFrame::CreateFrameWithLayout(
1770 const VideoFrameLayout& layout,
1771 const gfx::Rect& visible_rect,
1772 const gfx::Size& natural_size,
1773 base::TimeDelta timestamp,
1774 bool zero_initialize_memory) {
1775 const StorageType storage = STORAGE_OWNED_MEMORY;
1776 if (!IsValidConfig(layout.format(), storage, layout.coded_size(),
1777 visible_rect, natural_size)) {
1778 DLOG(ERROR) << __func__ << " Invalid config."
1779 << ConfigToString(layout.format(), storage, layout.coded_size(),
1780 visible_rect, natural_size);
1784 scoped_refptr<VideoFrame> frame(new VideoFrame(
1785 std::move(layout), storage, visible_rect, natural_size, timestamp));
1786 return frame->AllocateMemory(zero_initialize_memory) ? frame : nullptr;
1790 gfx::Size VideoFrame::CommonAlignment(VideoPixelFormat format) {
1791 int max_sample_width = 0;
1792 int max_sample_height = 0;
1793 for (size_t plane = 0; plane < NumPlanes(format); ++plane) {
1794 const gfx::Size sample_size = SampleSize(format, plane);
1795 max_sample_width = std::max(max_sample_width, sample_size.width());
1796 max_sample_height = std::max(max_sample_height, sample_size.height());
1798 return gfx::Size(max_sample_width, max_sample_height);
1801 bool VideoFrame::AllocateMemory(bool zero_initialize_memory) {
1802 DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
1803 static_assert(0 == kYPlane, "y plane data must be index 0");
1805 std::vector<size_t> plane_size = CalculatePlaneSize();
1806 const size_t buffer_size =
1807 std::accumulate(plane_size.begin(), plane_size.end(), 0u);
1808 const size_t allocation_size =
1809 buffer_size + (layout_.buffer_addr_align() - 1);
1811 uint8_t* data = nullptr;
1812 if (zero_initialize_memory) {
1813 if (!base::UncheckedCalloc(1, allocation_size,
1814 reinterpret_cast<void**>(&data)) ||
1819 if (!base::UncheckedMalloc(allocation_size,
1820 reinterpret_cast<void**>(&data)) ||
1825 private_data_.reset(data);
1827 data = base::bits::AlignUp(data, layout_.buffer_addr_align());
1828 DCHECK_LE(data + buffer_size, private_data_.get() + allocation_size);
1830 // Note that if layout.buffer_sizes is specified, color planes' layout is
1831 // the same as buffers'. See CalculatePlaneSize() for detail.
1832 for (size_t plane = 0, offset = 0; plane < NumPlanes(format()); ++plane) {
1833 data_[plane] = data + offset;
1834 offset += plane_size[plane];
1840 bool VideoFrame::IsValidSharedMemoryFrame() const {
1841 if (storage_type_ == STORAGE_SHMEM)
1842 return shm_region_ && shm_region_->IsValid();
1847 std::vector<size_t> VideoFrame::CalculatePlaneSize(
1848 const VideoFrameLayout& layout) {
1849 // We have two cases for plane size mapping:
1850 // 1) If plane size is specified: use planes' size.
1851 // 2) VideoFrameLayout::size is unassigned: use legacy calculation formula.
1853 const auto format = layout.format();
1854 const size_t num_planes = NumPlanes(format);
1855 const auto& planes = layout.planes();
1856 std::vector<size_t> plane_size(num_planes);
1857 bool plane_size_assigned = true;
1858 DCHECK_EQ(planes.size(), num_planes);
1859 for (size_t i = 0; i < num_planes; ++i) {
1860 plane_size[i] = planes[i].size;
1861 plane_size_assigned &= plane_size[i] != 0;
1864 if (plane_size_assigned)
1867 // Reset plane size.
1868 std::fill(plane_size.begin(), plane_size.end(), 0u);
1869 for (size_t plane = 0; plane < num_planes; ++plane) {
1870 // These values were chosen to mirror ffmpeg's get_video_buffer().
1871 // TODO(dalecurtis): This should be configurable; eventually ffmpeg wants
1872 // us to use av_cpu_max_align(), but... for now, they just hard-code 32.
1873 const size_t height = base::bits::AlignUp(
1874 static_cast<size_t>(Rows(plane, format, layout.coded_size().height())),
1875 kFrameAddressAlignment);
1876 const size_t width = std::abs(layout.planes()[plane].stride);
1877 plane_size[plane] = width * height;
1880 if (num_planes > 1) {
1881 // The extra line of UV being allocated is because h264 chroma MC
1882 // overreads by one line in some cases, see libavcodec/utils.c:
1883 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
1884 // put_h264_chroma_mc4_ssse3().
1885 DCHECK(IsValidPlane(format, kUPlane));
1886 DCHECK(kUPlane < num_planes);
1887 plane_size.back() +=
1888 std::abs(layout.planes()[kUPlane].stride) + kFrameSizePadding;
1893 #if defined(TIZEN_TBM_SUPPORT)
1894 void ReleaseTbmTexture(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
1895 gpu::gles2::GLES2Interface* gl,
1898 scoped_refptr<viz::ContextProvider> context_provider,
1899 const gpu::SyncToken& release_sync_point) {
1900 #if defined(USE_TTRACE)
1901 TTRACE(TTRACE_TAG_WEB, "ReleaseTbmTexture");
1903 DCHECK(task_runner->BelongsToCurrentThread());
1904 const uint32_t target = GL_TEXTURE_EXTERNAL_OES;
1905 LOG(INFO) << "VideoFrame > --ReleaseTbmTexture >"
1906 << ", img:" << image << ", txt:" << texture;
1907 if (release_sync_point.HasData())
1908 gl->WaitSyncTokenCHROMIUM(release_sync_point.GetConstData());
1910 gl->BindTexture(target, texture);
1911 gl->ReleaseTexImage2DCHROMIUM(target, image);
1912 gl->DestroyTizenImageCHROMIUM(image);
1914 gl->DeleteTextures(1, &texture);
1917 void VideoFrame::ReleaseTbm() {
1918 if (mailbox_holders_and_gmb_release_cb_) {
1919 gpu::SyncToken release_sync_token;
1921 // To ensure that changes to |release_sync_token_| are visible on this
1922 // thread (imply a memory barrier).
1923 base::AutoLock locker(release_sync_token_lock_);
1924 release_sync_token = release_sync_token_;
1926 std::move(mailbox_holders_and_gmb_release_cb_)
1927 .Run(release_sync_token, std::move(gpu_memory_buffer_));
1931 unsigned VideoFrame::CreateTbmTextureIfNeeded(gpu::gles2::GLES2Interface* gl) {
1932 #if defined(USE_TTRACE)
1933 TTRACE(TTRACE_TAG_WEB, "VideoFrameCompositor::CreateTbmTextureIfNeeded");
1935 base::AutoLock autolock(tbm_lock_);
1936 if (!gl || texture_id_)
1937 return (gl_ == gl) ? texture_id_ : 0;
1940 unsigned image = gl->CreateTizenImageCHROMIUM(
1941 buffer_handle_, visible_rect().width(), visible_rect().height(), GL_RGBA);
1942 gl->GenTextures(1, &texture_id_);
1943 gl->BindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id_);
1944 gl->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1945 gl->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1946 gl->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
1948 gl->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
1950 gl->BindTexImage2DCHROMIUM(GL_TEXTURE_EXTERNAL_OES, image);
1952 LOG(INFO) << "CreateTbmTextureIfNeeded img:" << image
1953 << ", txt:" << texture_id_;
1955 gpu::Mailbox mailbox;
1956 gl->ProduceTextureDirectCHROMIUM(texture_id_, mailbox.name);
1957 gl->ShallowFlushCHROMIUM();
1958 gpu::SyncToken sync_token;
1959 gl->GenSyncTokenCHROMIUM(sync_token.GetData());
1960 mailbox_holders_[kARGBPlane] =
1961 gpu::MailboxHolder(mailbox, sync_token, GL_TEXTURE_EXTERNAL_OES);
1963 SetReleaseMailboxCB(base::BindOnce(ReleaseTbmTexture,
1964 base::ThreadTaskRunnerHandle::Get(), gl,
1965 texture_id_, image, context_provider_));
1970 std::vector<size_t> VideoFrame::CalculatePlaneSize() const {
1971 return CalculatePlaneSize(layout_);
1974 } // namespace media