• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 // Note: ported from Chromium commit head: 602bc8fa60fa
5 // Note: only necessary functions are ported.
6 // Note: some shared memory-related functionality here is no longer present in
7 // Chromium.
8 
9 #include "video_frame.h"
10 
11 #include <algorithm>
12 #include <climits>
13 #include <limits>
14 #include <numeric>
15 #include <utility>
16 
17 #include "base/atomic_sequence_num.h"
18 #include "base/bind.h"
19 #include "base/bits.h"
20 #include "base/callback_helpers.h"
21 #include "base/logging.h"
22 #include "base/memory/aligned_memory.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string_piece.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/time/time.h"
27 #include "media_limits.h"
28 
29 namespace media {
30 
31 // Note: moved from Chromium media/base/timestamp_constants.h
32 // Indicates an invalid or missing timestamp.
33 constexpr base::TimeDelta kNoTimestamp =
34     base::TimeDelta::FromMicroseconds(std::numeric_limits<int64_t>::min());
35 
36 namespace {
37 
38 // Helper to provide Rect::Intersect() as an expression.
Intersection(Rect a,const Rect & b)39 Rect Intersection(Rect a, const Rect& b) {
40   a.Intersect(b);
41   return a;
42 }
43 
44 // Note: moved from Chromium base/bits.h which is not included in libchrome.
45 // Round down |size| to a multiple of alignment, which must be a power of two.
AlignDown(size_t size,size_t alignment)46 size_t AlignDown(size_t size, size_t alignment) {
47   DCHECK(base::bits::IsPowerOfTwo(alignment));
48   return size & ~(alignment - 1);
49 }
50 
51 }  // namespace
52 
53 // Static constexpr class for generating unique identifiers for each VideoFrame.
54 static base::AtomicSequenceNumber g_unique_id_generator;
55 
StorageTypeToString(const VideoFrame::StorageType storage_type)56 static std::string StorageTypeToString(
57     const VideoFrame::StorageType storage_type) {
58   switch (storage_type) {
59     case VideoFrame::STORAGE_UNKNOWN:
60       return "UNKNOWN";
61     case VideoFrame::STORAGE_OPAQUE:
62       return "OPAQUE";
63     case VideoFrame::STORAGE_UNOWNED_MEMORY:
64       return "UNOWNED_MEMORY";
65     case VideoFrame::STORAGE_OWNED_MEMORY:
66       return "OWNED_MEMORY";
67     case VideoFrame::STORAGE_SHMEM:
68       return "SHMEM";
69     case VideoFrame::STORAGE_DMABUFS:
70       return "DMABUFS";
71     case VideoFrame::STORAGE_MOJO_SHARED_BUFFER:
72       return "MOJO_SHARED_BUFFER";
73   }
74 
75   NOTREACHED() << "Invalid StorageType provided: " << storage_type;
76   return "INVALID";
77 }
78 
79 // static
IsStorageTypeMappable(VideoFrame::StorageType storage_type)80 bool VideoFrame::IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
81   return
82       // This is not strictly needed but makes explicit that, at VideoFrame
83       // level, DmaBufs are not mappable from userspace.
84       storage_type != VideoFrame::STORAGE_DMABUFS &&
85       (storage_type == VideoFrame::STORAGE_UNOWNED_MEMORY ||
86        storage_type == VideoFrame::STORAGE_OWNED_MEMORY ||
87        storage_type == VideoFrame::STORAGE_SHMEM ||
88        storage_type == VideoFrame::STORAGE_MOJO_SHARED_BUFFER);
89 }
90 
91 // If it is required to allocate aligned to multiple-of-two size overall for the
92 // frame of pixel |format|.
RequiresEvenSizeAllocation(VideoPixelFormat format)93 static bool RequiresEvenSizeAllocation(VideoPixelFormat format) {
94   switch (format) {
95     case PIXEL_FORMAT_ARGB:
96     case PIXEL_FORMAT_XRGB:
97     case PIXEL_FORMAT_RGB24:
98     case PIXEL_FORMAT_Y16:
99     case PIXEL_FORMAT_ABGR:
100     case PIXEL_FORMAT_XBGR:
101     case PIXEL_FORMAT_XR30:
102     case PIXEL_FORMAT_XB30:
103     case PIXEL_FORMAT_BGRA:
104       return false;
105     case PIXEL_FORMAT_NV12:
106     case PIXEL_FORMAT_NV21:
107     case PIXEL_FORMAT_I420:
108     case PIXEL_FORMAT_MJPEG:
109     case PIXEL_FORMAT_YUY2:
110     case PIXEL_FORMAT_YV12:
111     case PIXEL_FORMAT_I422:
112     case PIXEL_FORMAT_I444:
113     case PIXEL_FORMAT_YUV420P9:
114     case PIXEL_FORMAT_YUV422P9:
115     case PIXEL_FORMAT_YUV444P9:
116     case PIXEL_FORMAT_YUV420P10:
117     case PIXEL_FORMAT_YUV422P10:
118     case PIXEL_FORMAT_YUV444P10:
119     case PIXEL_FORMAT_YUV420P12:
120     case PIXEL_FORMAT_YUV422P12:
121     case PIXEL_FORMAT_YUV444P12:
122     case PIXEL_FORMAT_I420A:
123     case PIXEL_FORMAT_P016LE:
124       return true;
125     case PIXEL_FORMAT_UNKNOWN:
126       break;
127   }
128   NOTREACHED() << "Unsupported video frame format: " << format;
129   return false;
130 }
131 
132 // Creates VideoFrameLayout for tightly packed frame.
GetDefaultLayout(VideoPixelFormat format,const Size & coded_size)133 static base::Optional<VideoFrameLayout> GetDefaultLayout(
134     VideoPixelFormat format,
135     const Size& coded_size) {
136   std::vector<ColorPlaneLayout> planes;
137 
138   switch (format) {
139     case PIXEL_FORMAT_I420: {
140       int uv_width = (coded_size.width() + 1) / 2;
141       int uv_height = (coded_size.height() + 1) / 2;
142       int uv_stride = uv_width;
143       int uv_size = uv_stride * uv_height;
144       planes = std::vector<ColorPlaneLayout>{
145           ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
146           ColorPlaneLayout(uv_stride, coded_size.GetArea(), uv_size),
147           ColorPlaneLayout(uv_stride, coded_size.GetArea() + uv_size, uv_size),
148       };
149       break;
150     }
151 
152     case PIXEL_FORMAT_Y16:
153       planes = std::vector<ColorPlaneLayout>{ColorPlaneLayout(
154           coded_size.width() * 2, 0, coded_size.GetArea() * 2)};
155       break;
156 
157     case PIXEL_FORMAT_ARGB:
158       planes = std::vector<ColorPlaneLayout>{ColorPlaneLayout(
159           coded_size.width() * 4, 0, coded_size.GetArea() * 4)};
160       break;
161 
162     case PIXEL_FORMAT_NV12: {
163       int uv_width = (coded_size.width() + 1) / 2;
164       int uv_height = (coded_size.height() + 1) / 2;
165       int uv_stride = uv_width * 2;
166       int uv_size = uv_stride * uv_height;
167       planes = std::vector<ColorPlaneLayout>{
168           ColorPlaneLayout(coded_size.width(), 0, coded_size.GetArea()),
169           ColorPlaneLayout(uv_stride, coded_size.GetArea(), uv_size),
170       };
171       break;
172     }
173 
174     default:
175       // TODO(miu): This function should support any pixel format.
176       // http://crbug.com/555909 .
177       DLOG(ERROR)
178           << "Only PIXEL_FORMAT_I420, PIXEL_FORMAT_Y16, PIXEL_FORMAT_NV12, "
179              "and PIXEL_FORMAT_ARGB formats are supported: "
180           << VideoPixelFormatToString(format);
181       return base::nullopt;
182   }
183 
184   return VideoFrameLayout::CreateWithPlanes(format, coded_size, planes);
185 }
186 
187 // static
IsValidConfig(VideoPixelFormat format,StorageType storage_type,const Size & coded_size,const Rect & visible_rect,const Size & natural_size)188 bool VideoFrame::IsValidConfig(VideoPixelFormat format,
189                                StorageType storage_type,
190                                const Size& coded_size,
191                                const Rect& visible_rect,
192                                const Size& natural_size) {
193   // Check maximum limits for all formats.
194   int coded_size_area = coded_size.GetCheckedArea().ValueOrDefault(INT_MAX);
195   int natural_size_area = natural_size.GetCheckedArea().ValueOrDefault(INT_MAX);
196   static_assert(limits::kMaxCanvas < INT_MAX, "");
197   if (coded_size_area > limits::kMaxCanvas ||
198       coded_size.width() > limits::kMaxDimension ||
199       coded_size.height() > limits::kMaxDimension || visible_rect.x() < 0 ||
200       visible_rect.y() < 0 || visible_rect.right() > coded_size.width() ||
201       visible_rect.bottom() > coded_size.height() ||
202       natural_size_area > limits::kMaxCanvas ||
203       natural_size.width() > limits::kMaxDimension ||
204       natural_size.height() > limits::kMaxDimension) {
205     return false;
206   }
207 
208   // TODO(mcasas): Remove parameter |storage_type| when the opaque storage types
209   // comply with the checks below. Right now we skip them.
210   if (!IsStorageTypeMappable(storage_type))
211     return true;
212 
213   // Make sure new formats are properly accounted for in the method.
214   static_assert(PIXEL_FORMAT_MAX == 32,
215                 "Added pixel format, please review IsValidConfig()");
216 
217   if (format == PIXEL_FORMAT_UNKNOWN) {
218     return coded_size.IsEmpty() && visible_rect.IsEmpty() &&
219            natural_size.IsEmpty();
220   }
221 
222   // Check that software-allocated buffer formats are not empty.
223   return !coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
224          !natural_size.IsEmpty();
225 }
226 
227 // static
CreateFrame(VideoPixelFormat format,const Size & coded_size,const Rect & visible_rect,const Size & natural_size,base::TimeDelta timestamp)228 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(VideoPixelFormat format,
229                                                   const Size& coded_size,
230                                                   const Rect& visible_rect,
231                                                   const Size& natural_size,
232                                                   base::TimeDelta timestamp) {
233   return CreateFrameInternal(format, coded_size, visible_rect, natural_size,
234                              timestamp, false);
235 }
236 
237 // static
WrapExternalSharedMemory(VideoPixelFormat format,const Size & coded_size,const Rect & visible_rect,const Size & natural_size,uint8_t * data,size_t data_size,base::SharedMemoryHandle handle,size_t data_offset,base::TimeDelta timestamp)238 scoped_refptr<VideoFrame> VideoFrame::WrapExternalSharedMemory(
239     VideoPixelFormat format,
240     const Size& coded_size,
241     const Rect& visible_rect,
242     const Size& natural_size,
243     uint8_t* data,
244     size_t data_size,
245     base::SharedMemoryHandle handle,
246     size_t data_offset,
247     base::TimeDelta timestamp) {
248   auto layout = GetDefaultLayout(format, coded_size);
249   if (!layout)
250     return nullptr;
251   return WrapExternalStorage(STORAGE_SHMEM, *layout, visible_rect, natural_size,
252                              data, data_size, timestamp, nullptr, nullptr,
253                              handle, data_offset);
254 }
255 
256 // static
CreateEOSFrame()257 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
258   auto layout = VideoFrameLayout::Create(PIXEL_FORMAT_UNKNOWN, Size());
259   if (!layout) {
260     DLOG(ERROR) << "Invalid layout.";
261     return nullptr;
262   }
263   scoped_refptr<VideoFrame> frame =
264       new VideoFrame(*layout, STORAGE_UNKNOWN, Rect(), Size(), kNoTimestamp);
265   frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM, true);
266   return frame;
267 }
268 
269 // static
NumPlanes(VideoPixelFormat format)270 size_t VideoFrame::NumPlanes(VideoPixelFormat format) {
271   return VideoFrameLayout::NumPlanes(format);
272 }
273 
274 // static
AllocationSize(VideoPixelFormat format,const Size & coded_size)275 size_t VideoFrame::AllocationSize(VideoPixelFormat format,
276                                   const Size& coded_size) {
277   size_t total = 0;
278   for (size_t i = 0; i < NumPlanes(format); ++i)
279     total += PlaneSize(format, i, coded_size).GetArea();
280   return total;
281 }
282 
283 // static
PlaneSize(VideoPixelFormat format,size_t plane,const Size & coded_size)284 Size VideoFrame::PlaneSize(VideoPixelFormat format,
285                            size_t plane,
286                            const Size& coded_size) {
287   DCHECK(IsValidPlane(plane, format));
288 
289   int width = coded_size.width();
290   int height = coded_size.height();
291   if (RequiresEvenSizeAllocation(format)) {
292     // Align to multiple-of-two size overall. This ensures that non-subsampled
293     // planes can be addressed by pixel with the same scaling as the subsampled
294     // planes.
295     width = base::bits::Align(width, 2);
296     height = base::bits::Align(height, 2);
297   }
298 
299   const Size subsample = SampleSize(format, plane);
300   DCHECK(width % subsample.width() == 0);
301   DCHECK(height % subsample.height() == 0);
302   return Size(BytesPerElement(format, plane) * width / subsample.width(),
303               height / subsample.height());
304 }
305 
306 // static
PlaneHorizontalBitsPerPixel(VideoPixelFormat format,size_t plane)307 int VideoFrame::PlaneHorizontalBitsPerPixel(VideoPixelFormat format,
308                                             size_t plane) {
309   DCHECK(IsValidPlane(plane, format));
310   const int bits_per_element = 8 * BytesPerElement(format, plane);
311   const int horiz_pixels_per_element = SampleSize(format, plane).width();
312   DCHECK_EQ(bits_per_element % horiz_pixels_per_element, 0);
313   return bits_per_element / horiz_pixels_per_element;
314 }
315 
316 // static
PlaneBitsPerPixel(VideoPixelFormat format,size_t plane)317 int VideoFrame::PlaneBitsPerPixel(VideoPixelFormat format, size_t plane) {
318   DCHECK(IsValidPlane(plane, format));
319   return PlaneHorizontalBitsPerPixel(format, plane) /
320          SampleSize(format, plane).height();
321 }
322 
323 // static
RowBytes(size_t plane,VideoPixelFormat format,int width)324 size_t VideoFrame::RowBytes(size_t plane, VideoPixelFormat format, int width) {
325   DCHECK(IsValidPlane(plane, format));
326   return BytesPerElement(format, plane) * Columns(plane, format, width);
327 }
328 
329 // static
BytesPerElement(VideoPixelFormat format,size_t plane)330 int VideoFrame::BytesPerElement(VideoPixelFormat format, size_t plane) {
331   DCHECK(IsValidPlane(format, plane));
332   switch (format) {
333     case PIXEL_FORMAT_ARGB:
334     case PIXEL_FORMAT_BGRA:
335     case PIXEL_FORMAT_XRGB:
336     case PIXEL_FORMAT_ABGR:
337     case PIXEL_FORMAT_XBGR:
338     case PIXEL_FORMAT_XR30:
339     case PIXEL_FORMAT_XB30:
340       return 4;
341     case PIXEL_FORMAT_RGB24:
342       return 3;
343     case PIXEL_FORMAT_Y16:
344     case PIXEL_FORMAT_YUY2:
345     case PIXEL_FORMAT_YUV420P9:
346     case PIXEL_FORMAT_YUV422P9:
347     case PIXEL_FORMAT_YUV444P9:
348     case PIXEL_FORMAT_YUV420P10:
349     case PIXEL_FORMAT_YUV422P10:
350     case PIXEL_FORMAT_YUV444P10:
351     case PIXEL_FORMAT_YUV420P12:
352     case PIXEL_FORMAT_YUV422P12:
353     case PIXEL_FORMAT_YUV444P12:
354     case PIXEL_FORMAT_P016LE:
355       return 2;
356     case PIXEL_FORMAT_NV12:
357     case PIXEL_FORMAT_NV21: {
358       static const int bytes_per_element[] = {1, 2};
359       DCHECK_LT(plane, base::size(bytes_per_element));
360       return bytes_per_element[plane];
361     }
362     case PIXEL_FORMAT_YV12:
363     case PIXEL_FORMAT_I420:
364     case PIXEL_FORMAT_I422:
365     case PIXEL_FORMAT_I420A:
366     case PIXEL_FORMAT_I444:
367       return 1;
368     case PIXEL_FORMAT_MJPEG:
369       return 0;
370     case PIXEL_FORMAT_UNKNOWN:
371       break;
372   }
373   NOTREACHED();
374   return 0;
375 }
376 
377 // static
ComputeStrides(VideoPixelFormat format,const Size & coded_size)378 std::vector<int32_t> VideoFrame::ComputeStrides(VideoPixelFormat format,
379                                                 const Size& coded_size) {
380   std::vector<int32_t> strides;
381   const size_t num_planes = NumPlanes(format);
382   if (num_planes == 1) {
383     strides.push_back(RowBytes(0, format, coded_size.width()));
384   } else {
385     for (size_t plane = 0; plane < num_planes; ++plane) {
386       strides.push_back(base::bits::Align(
387           RowBytes(plane, format, coded_size.width()), kFrameAddressAlignment));
388     }
389   }
390   return strides;
391 }
392 
393 // static
Rows(size_t plane,VideoPixelFormat format,int height)394 size_t VideoFrame::Rows(size_t plane, VideoPixelFormat format, int height) {
395   DCHECK(IsValidPlane(plane, format));
396   const int sample_height = SampleSize(format, plane).height();
397   return base::bits::Align(height, sample_height) / sample_height;
398 }
399 
400 // static
Columns(size_t plane,VideoPixelFormat format,int width)401 size_t VideoFrame::Columns(size_t plane, VideoPixelFormat format, int width) {
402   DCHECK(IsValidPlane(plane, format));
403   const int sample_width = SampleSize(format, plane).width();
404   return base::bits::Align(width, sample_width) / sample_width;
405 }
406 
IsMappable() const407 bool VideoFrame::IsMappable() const {
408   return IsStorageTypeMappable(storage_type_);
409 }
410 
row_bytes(size_t plane) const411 int VideoFrame::row_bytes(size_t plane) const {
412   return RowBytes(plane, format(), coded_size().width());
413 }
414 
rows(size_t plane) const415 int VideoFrame::rows(size_t plane) const {
416   return Rows(plane, format(), coded_size().height());
417 }
418 
visible_data(size_t plane) const419 const uint8_t* VideoFrame::visible_data(size_t plane) const {
420   DCHECK(IsValidPlane(plane, format()));
421   DCHECK(IsMappable());
422 
423   // Calculate an offset that is properly aligned for all planes.
424   const Size alignment = CommonAlignment(format());
425   const int offset_x = AlignDown(visible_rect_.x(), alignment.width());
426   const int offset_y = AlignDown(visible_rect_.y(), alignment.height());
427 
428   const Size subsample = SampleSize(format(), plane);
429   DCHECK(offset_x % subsample.width() == 0);
430   DCHECK(offset_y % subsample.height() == 0);
431   return data(plane) +
432          stride(plane) * (offset_y / subsample.height()) +  // Row offset.
433          BytesPerElement(format(), plane) *                 // Column offset.
434              (offset_x / subsample.width());
435 }
436 
visible_data(size_t plane)437 uint8_t* VideoFrame::visible_data(size_t plane) {
438   return const_cast<uint8_t*>(
439       static_cast<const VideoFrame*>(this)->visible_data(plane));
440 }
441 
read_only_shared_memory_region() const442 base::ReadOnlySharedMemoryRegion* VideoFrame::read_only_shared_memory_region()
443     const {
444   DCHECK_EQ(storage_type_, STORAGE_SHMEM);
445   DCHECK(read_only_shared_memory_region_ &&
446          read_only_shared_memory_region_->IsValid());
447   return read_only_shared_memory_region_;
448 }
449 
unsafe_shared_memory_region() const450 base::UnsafeSharedMemoryRegion* VideoFrame::unsafe_shared_memory_region()
451     const {
452   DCHECK_EQ(storage_type_, STORAGE_SHMEM);
453   DCHECK(unsafe_shared_memory_region_ &&
454          unsafe_shared_memory_region_->IsValid());
455   return unsafe_shared_memory_region_;
456 }
457 
shared_memory_handle() const458 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
459   DCHECK_EQ(storage_type_, STORAGE_SHMEM);
460   DCHECK(shared_memory_handle_.IsValid());
461   return shared_memory_handle_;
462 }
463 
shared_memory_offset() const464 size_t VideoFrame::shared_memory_offset() const {
465   DCHECK_EQ(storage_type_, STORAGE_SHMEM);
466   DCHECK((read_only_shared_memory_region_ &&
467           read_only_shared_memory_region_->IsValid()) ||
468          (unsafe_shared_memory_region_ &&
469           unsafe_shared_memory_region_->IsValid()) ||
470          shared_memory_handle_.IsValid());
471   return shared_memory_offset_;
472 }
473 
DmabufFds() const474 const std::vector<base::ScopedFD>& VideoFrame::DmabufFds() const {
475   DCHECK_EQ(storage_type_, STORAGE_DMABUFS);
476 
477   return dmabuf_fds_;
478 }
479 
HasDmaBufs() const480 bool VideoFrame::HasDmaBufs() const {
481   return !dmabuf_fds_.empty();
482 }
483 
AddReadOnlySharedMemoryRegion(base::ReadOnlySharedMemoryRegion * region)484 void VideoFrame::AddReadOnlySharedMemoryRegion(
485     base::ReadOnlySharedMemoryRegion* region) {
486   storage_type_ = STORAGE_SHMEM;
487   DCHECK(SharedMemoryUninitialized());
488   DCHECK(region && region->IsValid());
489   read_only_shared_memory_region_ = region;
490 }
491 
AddUnsafeSharedMemoryRegion(base::UnsafeSharedMemoryRegion * region)492 void VideoFrame::AddUnsafeSharedMemoryRegion(
493     base::UnsafeSharedMemoryRegion* region) {
494   storage_type_ = STORAGE_SHMEM;
495   DCHECK(SharedMemoryUninitialized());
496   DCHECK(region && region->IsValid());
497   unsafe_shared_memory_region_ = region;
498 }
499 
AddSharedMemoryHandle(base::SharedMemoryHandle handle)500 void VideoFrame::AddSharedMemoryHandle(base::SharedMemoryHandle handle) {
501   storage_type_ = STORAGE_SHMEM;
502   DCHECK(SharedMemoryUninitialized());
503   shared_memory_handle_ = handle;
504 }
505 
AddDestructionObserver(base::OnceClosure callback)506 void VideoFrame::AddDestructionObserver(base::OnceClosure callback) {
507   DCHECK(!callback.is_null());
508   done_callbacks_.push_back(std::move(callback));
509 }
510 
AsHumanReadableString()511 std::string VideoFrame::AsHumanReadableString() {
512   if (metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM))
513     return "end of stream";
514 
515   std::ostringstream s;
516   s << ConfigToString(format(), storage_type_, coded_size(), visible_rect_,
517                       natural_size_)
518     << " timestamp:" << timestamp_.InMicroseconds();
519   return s.str();
520 }
521 
BitDepth() const522 size_t VideoFrame::BitDepth() const {
523   return media::BitDepth(format());
524 }
525 
526 // static
WrapExternalStorage(StorageType storage_type,const VideoFrameLayout & layout,const Rect & visible_rect,const Size & natural_size,uint8_t * data,size_t data_size,base::TimeDelta timestamp,base::ReadOnlySharedMemoryRegion * read_only_region,base::UnsafeSharedMemoryRegion * unsafe_region,base::SharedMemoryHandle handle,size_t data_offset)527 scoped_refptr<VideoFrame> VideoFrame::WrapExternalStorage(
528     StorageType storage_type,
529     const VideoFrameLayout& layout,
530     const Rect& visible_rect,
531     const Size& natural_size,
532     uint8_t* data,
533     size_t data_size,
534     base::TimeDelta timestamp,
535     base::ReadOnlySharedMemoryRegion* read_only_region,
536     base::UnsafeSharedMemoryRegion* unsafe_region,
537     base::SharedMemoryHandle handle,
538     size_t data_offset) {
539   DCHECK(IsStorageTypeMappable(storage_type));
540 
541   if (!IsValidConfig(layout.format(), storage_type, layout.coded_size(),
542                      visible_rect, natural_size)) {
543     DLOG(ERROR) << __func__ << " Invalid config."
544                 << ConfigToString(layout.format(), storage_type,
545                                   layout.coded_size(), visible_rect,
546                                   natural_size);
547     return nullptr;
548   }
549 
550   scoped_refptr<VideoFrame> frame = new VideoFrame(
551       layout, storage_type, visible_rect, natural_size, timestamp);
552 
553   for (size_t i = 0; i < layout.planes().size(); ++i) {
554     frame->data_[i] = data + layout.planes()[i].offset;
555   }
556 
557   if (storage_type == STORAGE_SHMEM) {
558     if (read_only_region || unsafe_region) {
559       DCHECK(!handle.IsValid());
560       DCHECK_NE(!!read_only_region, !!unsafe_region)
561           << "Expected exactly one read-only or unsafe region for "
562           << "STORAGE_SHMEM VideoFrame";
563       if (read_only_region) {
564         frame->read_only_shared_memory_region_ = read_only_region;
565         DCHECK(frame->read_only_shared_memory_region_->IsValid());
566       } else if (unsafe_region) {
567         frame->unsafe_shared_memory_region_ = unsafe_region;
568         DCHECK(frame->unsafe_shared_memory_region_->IsValid());
569       }
570       frame->shared_memory_offset_ = data_offset;
571     } else {
572       frame->AddSharedMemoryHandle(handle);
573       frame->shared_memory_offset_ = data_offset;
574     }
575   }
576 
577   return frame;
578 }
579 
VideoFrame(const VideoFrameLayout & layout,StorageType storage_type,const Rect & visible_rect,const Size & natural_size,base::TimeDelta timestamp)580 VideoFrame::VideoFrame(const VideoFrameLayout& layout,
581                        StorageType storage_type,
582                        const Rect& visible_rect,
583                        const Size& natural_size,
584                        base::TimeDelta timestamp)
585     : layout_(layout),
586       storage_type_(storage_type),
587       visible_rect_(Intersection(visible_rect, Rect(layout.coded_size()))),
588       natural_size_(natural_size),
589       shared_memory_offset_(0),
590       timestamp_(timestamp),
591       unique_id_(g_unique_id_generator.GetNext()) {
592   DCHECK(IsValidConfig(format(), storage_type, coded_size(), visible_rect_,
593                        natural_size_));
594   DCHECK(visible_rect_ == visible_rect)
595       << "visible_rect " << visible_rect.ToString() << " exceeds coded_size "
596       << coded_size().ToString();
597   memset(&data_, 0, sizeof(data_));
598 }
599 
~VideoFrame()600 VideoFrame::~VideoFrame() {
601   for (auto& callback : done_callbacks_)
602     std::move(callback).Run();
603 }
604 
605 // static
ConfigToString(const VideoPixelFormat format,const StorageType storage_type,const Size & coded_size,const Rect & visible_rect,const Size & natural_size)606 std::string VideoFrame::ConfigToString(const VideoPixelFormat format,
607                                        const StorageType storage_type,
608                                        const Size& coded_size,
609                                        const Rect& visible_rect,
610                                        const Size& natural_size) {
611   return base::StringPrintf(
612       "format:%s storage_type:%s coded_size:%s visible_rect:%s natural_size:%s",
613       VideoPixelFormatToString(format).c_str(),
614       StorageTypeToString(storage_type).c_str(), coded_size.ToString().c_str(),
615       visible_rect.ToString().c_str(), natural_size.ToString().c_str());
616 }
617 
618 // static
IsValidPlane(size_t plane,VideoPixelFormat format)619 bool VideoFrame::IsValidPlane(size_t plane, VideoPixelFormat format) {
620   DCHECK_LE(NumPlanes(format), static_cast<size_t>(kMaxPlanes));
621   return (plane < NumPlanes(format));
622 }
623 
624 // static
DetermineAlignedSize(VideoPixelFormat format,const Size & dimensions)625 Size VideoFrame::DetermineAlignedSize(VideoPixelFormat format,
626                                       const Size& dimensions) {
627   const Size alignment = CommonAlignment(format);
628   const Size adjusted =
629       Size(base::bits::Align(dimensions.width(), alignment.width()),
630            base::bits::Align(dimensions.height(), alignment.height()));
631   DCHECK((adjusted.width() % alignment.width() == 0) &&
632          (adjusted.height() % alignment.height() == 0));
633   return adjusted;
634 }
635 
636 // static
CreateFrameInternal(VideoPixelFormat format,const Size & coded_size,const Rect & visible_rect,const Size & natural_size,base::TimeDelta timestamp,bool zero_initialize_memory)637 scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
638     VideoPixelFormat format,
639     const Size& coded_size,
640     const Rect& visible_rect,
641     const Size& natural_size,
642     base::TimeDelta timestamp,
643     bool zero_initialize_memory) {
644   // Since we're creating a new frame (and allocating memory for it ourselves),
645   // we can pad the requested |coded_size| if necessary if the request does not
646   // line up on sample boundaries. See discussion at http://crrev.com/1240833003
647   const Size new_coded_size = DetermineAlignedSize(format, coded_size);
648   auto layout = VideoFrameLayout::CreateWithStrides(
649       format, new_coded_size, ComputeStrides(format, coded_size));
650   if (!layout) {
651     DLOG(ERROR) << "Invalid layout.";
652     return nullptr;
653   }
654 
655   return CreateFrameWithLayout(*layout, visible_rect, natural_size, timestamp,
656                                zero_initialize_memory);
657 }
658 
CreateFrameWithLayout(const VideoFrameLayout & layout,const Rect & visible_rect,const Size & natural_size,base::TimeDelta timestamp,bool zero_initialize_memory)659 scoped_refptr<VideoFrame> VideoFrame::CreateFrameWithLayout(
660     const VideoFrameLayout& layout,
661     const Rect& visible_rect,
662     const Size& natural_size,
663     base::TimeDelta timestamp,
664     bool zero_initialize_memory) {
665   const StorageType storage = STORAGE_OWNED_MEMORY;
666   if (!IsValidConfig(layout.format(), storage, layout.coded_size(),
667                      visible_rect, natural_size)) {
668     DLOG(ERROR) << __func__ << " Invalid config."
669                 << ConfigToString(layout.format(), storage, layout.coded_size(),
670                                   visible_rect, natural_size);
671     return nullptr;
672   }
673 
674   scoped_refptr<VideoFrame> frame(new VideoFrame(
675       std::move(layout), storage, visible_rect, natural_size, timestamp));
676   frame->AllocateMemory(zero_initialize_memory);
677   return frame;
678 }
679 
SharedMemoryUninitialized()680 bool VideoFrame::SharedMemoryUninitialized() {
681   return !read_only_shared_memory_region_ && !unsafe_shared_memory_region_ &&
682          !shared_memory_handle_.IsValid();
683 }
684 
685 // static
IsValidPlane(VideoPixelFormat format,size_t plane)686 bool VideoFrame::IsValidPlane(VideoPixelFormat format, size_t plane) {
687   DCHECK_LE(NumPlanes(format), static_cast<size_t>(kMaxPlanes));
688   return plane < NumPlanes(format);
689 }
690 
691 // static
SampleSize(VideoPixelFormat format,size_t plane)692 Size VideoFrame::SampleSize(VideoPixelFormat format, size_t plane) {
693   DCHECK(IsValidPlane(format, plane));
694 
695   switch (plane) {
696     case kYPlane:  // and kARGBPlane:
697     case kAPlane:
698       return Size(1, 1);
699 
700     case kUPlane:  // and kUVPlane:
701     case kVPlane:
702       switch (format) {
703         case PIXEL_FORMAT_I444:
704         case PIXEL_FORMAT_YUV444P9:
705         case PIXEL_FORMAT_YUV444P10:
706         case PIXEL_FORMAT_YUV444P12:
707         case PIXEL_FORMAT_Y16:
708           return Size(1, 1);
709 
710         case PIXEL_FORMAT_I422:
711         case PIXEL_FORMAT_YUV422P9:
712         case PIXEL_FORMAT_YUV422P10:
713         case PIXEL_FORMAT_YUV422P12:
714           return Size(2, 1);
715 
716         case PIXEL_FORMAT_YV12:
717         case PIXEL_FORMAT_I420:
718         case PIXEL_FORMAT_I420A:
719         case PIXEL_FORMAT_NV12:
720         case PIXEL_FORMAT_NV21:
721         case PIXEL_FORMAT_YUV420P9:
722         case PIXEL_FORMAT_YUV420P10:
723         case PIXEL_FORMAT_YUV420P12:
724         case PIXEL_FORMAT_P016LE:
725           return Size(2, 2);
726 
727         case PIXEL_FORMAT_UNKNOWN:
728         case PIXEL_FORMAT_YUY2:
729         case PIXEL_FORMAT_ARGB:
730         case PIXEL_FORMAT_XRGB:
731         case PIXEL_FORMAT_RGB24:
732         case PIXEL_FORMAT_MJPEG:
733         case PIXEL_FORMAT_ABGR:
734         case PIXEL_FORMAT_XBGR:
735         case PIXEL_FORMAT_XR30:
736         case PIXEL_FORMAT_XB30:
737         case PIXEL_FORMAT_BGRA:
738           break;
739       }
740   }
741   NOTREACHED();
742   return Size();
743 }
744 
745 // static
CommonAlignment(VideoPixelFormat format)746 Size VideoFrame::CommonAlignment(VideoPixelFormat format) {
747   int max_sample_width = 0;
748   int max_sample_height = 0;
749   for (size_t plane = 0; plane < NumPlanes(format); ++plane) {
750     const Size sample_size = SampleSize(format, plane);
751     max_sample_width = std::max(max_sample_width, sample_size.width());
752     max_sample_height = std::max(max_sample_height, sample_size.height());
753   }
754   return Size(max_sample_width, max_sample_height);
755 }
756 
AllocateMemory(bool zero_initialize_memory)757 void VideoFrame::AllocateMemory(bool zero_initialize_memory) {
758   DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
759   static_assert(0 == kYPlane, "y plane data must be index 0");
760 
761   std::vector<size_t> plane_size = CalculatePlaneSize();
762   const size_t total_buffer_size =
763       std::accumulate(plane_size.begin(), plane_size.end(), 0u);
764 
765   uint8_t* data = reinterpret_cast<uint8_t*>(
766       base::AlignedAlloc(total_buffer_size, layout_.buffer_addr_align()));
767   if (zero_initialize_memory) {
768     memset(data, 0, total_buffer_size);
769   }
770   AddDestructionObserver(base::BindOnce(&base::AlignedFree, data));
771 
772   // Note that if layout.buffer_sizes is specified, color planes' layout is the
773   // same as buffers'. See CalculatePlaneSize() for detail.
774   for (size_t plane = 0, offset = 0; plane < NumPlanes(format()); ++plane) {
775     data_[plane] = data + offset;
776     offset += plane_size[plane];
777   }
778 }
779 
CalculatePlaneSize() const780 std::vector<size_t> VideoFrame::CalculatePlaneSize() const {
781   // We have two cases for plane size mapping:
782   // 1) If plane size is specified: use planes' size.
783   // 2) VideoFrameLayout::size is unassigned: use legacy calculation formula.
784 
785   const size_t num_planes = NumPlanes(format());
786   const auto& planes = layout_.planes();
787   std::vector<size_t> plane_size(num_planes);
788   bool plane_size_assigned = true;
789   DCHECK_EQ(planes.size(), num_planes);
790   for (size_t i = 0; i < num_planes; ++i) {
791     plane_size[i] = planes[i].size;
792     plane_size_assigned &= plane_size[i] != 0;
793   }
794 
795   if (plane_size_assigned)
796     return plane_size;
797 
798   // Reset plane size.
799   std::fill(plane_size.begin(), plane_size.end(), 0u);
800   for (size_t plane = 0; plane < num_planes; ++plane) {
801     // These values were chosen to mirror ffmpeg's get_video_buffer().
802     // TODO(dalecurtis): This should be configurable; eventually ffmpeg wants
803     // us to use av_cpu_max_align(), but... for now, they just hard-code 32.
804     const size_t height =
805         base::bits::Align(rows(plane), kFrameAddressAlignment);
806     const size_t width = std::abs(stride(plane));
807     plane_size[plane] = width * height;
808   }
809 
810   if (num_planes > 1) {
811     // The extra line of UV being allocated is because h264 chroma MC
812     // overreads by one line in some cases, see libavcodec/utils.c:
813     // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
814     // put_h264_chroma_mc4_ssse3().
815     DCHECK(IsValidPlane(format(), kUPlane));
816     plane_size.back() += std::abs(stride(kUPlane)) + kFrameSizePadding;
817   }
818   return plane_size;
819 }
820 
821 }  // namespace media
822