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