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
5 #include "media/base/video_frame.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/memory/aligned_memory.h"
13 #include "base/strings/string_piece.h"
14 #include "gpu/command_buffer/common/mailbox_holder.h"
15 #include "media/base/limits.h"
16 #include "media/base/video_util.h"
17
18 #if !defined(MEDIA_FOR_CAST_IOS)
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #endif
21
22 namespace media {
23
RoundUp(size_t value,size_t alignment)24 static inline size_t RoundUp(size_t value, size_t alignment) {
25 // Check that |alignment| is a power of 2.
26 DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
27 return ((value + (alignment - 1)) & ~(alignment - 1));
28 }
29
30 // Rounds up |coded_size| if necessary for |format|.
AdjustCodedSize(VideoFrame::Format format,const gfx::Size & coded_size)31 static gfx::Size AdjustCodedSize(VideoFrame::Format format,
32 const gfx::Size& coded_size) {
33 gfx::Size new_coded_size(coded_size);
34 switch (format) {
35 case VideoFrame::YV12:
36 case VideoFrame::YV12A:
37 case VideoFrame::I420:
38 case VideoFrame::YV12J:
39 new_coded_size.set_height(RoundUp(new_coded_size.height(), 2));
40 // Fallthrough.
41 case VideoFrame::YV16:
42 new_coded_size.set_width(RoundUp(new_coded_size.width(), 2));
43 break;
44 default:
45 break;
46 }
47 return new_coded_size;
48 }
49
50 // static
CreateFrame(VideoFrame::Format format,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size,base::TimeDelta timestamp)51 scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
52 VideoFrame::Format format,
53 const gfx::Size& coded_size,
54 const gfx::Rect& visible_rect,
55 const gfx::Size& natural_size,
56 base::TimeDelta timestamp) {
57 DCHECK(format != VideoFrame::UNKNOWN &&
58 format != VideoFrame::NV12 &&
59 format != VideoFrame::NATIVE_TEXTURE);
60 #if defined(VIDEO_HOLE)
61 DCHECK(format != VideoFrame::HOLE);
62 #endif // defined(VIDEO_HOLE)
63
64 // Since we're creating a new YUV frame (and allocating memory for it
65 // ourselves), we can pad the requested |coded_size| if necessary if the
66 // request does not line up on sample boundaries.
67 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
68 DCHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
69
70 scoped_refptr<VideoFrame> frame(
71 new VideoFrame(format,
72 new_coded_size,
73 visible_rect,
74 natural_size,
75 scoped_ptr<gpu::MailboxHolder>(),
76 timestamp,
77 false));
78 frame->AllocateYUV();
79 return frame;
80 }
81
82 // static
FormatToString(VideoFrame::Format format)83 std::string VideoFrame::FormatToString(VideoFrame::Format format) {
84 switch (format) {
85 case VideoFrame::UNKNOWN:
86 return "UNKNOWN";
87 case VideoFrame::YV12:
88 return "YV12";
89 case VideoFrame::YV16:
90 return "YV16";
91 case VideoFrame::I420:
92 return "I420";
93 case VideoFrame::NATIVE_TEXTURE:
94 return "NATIVE_TEXTURE";
95 #if defined(VIDEO_HOLE)
96 case VideoFrame::HOLE:
97 return "HOLE";
98 #endif // defined(VIDEO_HOLE)
99 case VideoFrame::YV12A:
100 return "YV12A";
101 case VideoFrame::YV12J:
102 return "YV12J";
103 case VideoFrame::NV12:
104 return "NV12";
105 case VideoFrame::YV24:
106 return "YV24";
107 }
108 NOTREACHED() << "Invalid videoframe format provided: " << format;
109 return "";
110 }
111
112 // static
IsValidConfig(VideoFrame::Format format,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size)113 bool VideoFrame::IsValidConfig(VideoFrame::Format format,
114 const gfx::Size& coded_size,
115 const gfx::Rect& visible_rect,
116 const gfx::Size& natural_size) {
117 // Check maximum limits for all formats.
118 if (coded_size.GetArea() > limits::kMaxCanvas ||
119 coded_size.width() > limits::kMaxDimension ||
120 coded_size.height() > limits::kMaxDimension ||
121 visible_rect.x() < 0 || visible_rect.y() < 0 ||
122 visible_rect.right() > coded_size.width() ||
123 visible_rect.bottom() > coded_size.height() ||
124 natural_size.GetArea() > limits::kMaxCanvas ||
125 natural_size.width() > limits::kMaxDimension ||
126 natural_size.height() > limits::kMaxDimension)
127 return false;
128
129 // Check format-specific width/height requirements.
130 switch (format) {
131 case VideoFrame::UNKNOWN:
132 return (coded_size.IsEmpty() && visible_rect.IsEmpty() &&
133 natural_size.IsEmpty());
134 case VideoFrame::YV24:
135 break;
136 case VideoFrame::YV12:
137 case VideoFrame::YV12J:
138 case VideoFrame::I420:
139 case VideoFrame::YV12A:
140 case VideoFrame::NV12:
141 // Subsampled YUV formats have width/height requirements.
142 if (static_cast<size_t>(coded_size.height()) <
143 RoundUp(visible_rect.bottom(), 2))
144 return false;
145 // Fallthrough.
146 case VideoFrame::YV16:
147 if (static_cast<size_t>(coded_size.width()) <
148 RoundUp(visible_rect.right(), 2))
149 return false;
150 break;
151 case VideoFrame::NATIVE_TEXTURE:
152 #if defined(VIDEO_HOLE)
153 case VideoFrame::HOLE:
154 #endif // defined(VIDEO_HOLE)
155 // NATIVE_TEXTURE and HOLE have no software-allocated buffers and are
156 // allowed to skip the below check and be empty.
157 return true;
158 }
159
160 // Check that software-allocated buffer formats are not empty.
161 return (!coded_size.IsEmpty() && !visible_rect.IsEmpty() &&
162 !natural_size.IsEmpty());
163 }
164
165 // static
WrapNativeTexture(scoped_ptr<gpu::MailboxHolder> mailbox_holder,const ReleaseMailboxCB & mailbox_holder_release_cb,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size,base::TimeDelta timestamp,const ReadPixelsCB & read_pixels_cb)166 scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
167 scoped_ptr<gpu::MailboxHolder> mailbox_holder,
168 const ReleaseMailboxCB& mailbox_holder_release_cb,
169 const gfx::Size& coded_size,
170 const gfx::Rect& visible_rect,
171 const gfx::Size& natural_size,
172 base::TimeDelta timestamp,
173 const ReadPixelsCB& read_pixels_cb) {
174 scoped_refptr<VideoFrame> frame(new VideoFrame(NATIVE_TEXTURE,
175 coded_size,
176 visible_rect,
177 natural_size,
178 mailbox_holder.Pass(),
179 timestamp,
180 false));
181 frame->mailbox_holder_release_cb_ = mailbox_holder_release_cb;
182 frame->read_pixels_cb_ = read_pixels_cb;
183
184 return frame;
185 }
186
187 #if !defined(MEDIA_FOR_CAST_IOS)
ReadPixelsFromNativeTexture(const SkBitmap & pixels)188 void VideoFrame::ReadPixelsFromNativeTexture(const SkBitmap& pixels) {
189 DCHECK_EQ(format_, NATIVE_TEXTURE);
190 if (!read_pixels_cb_.is_null())
191 read_pixels_cb_.Run(pixels);
192 }
193 #endif
194
195 // static
WrapExternalPackedMemory(Format format,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size,uint8 * data,size_t data_size,base::SharedMemoryHandle handle,base::TimeDelta timestamp,const base::Closure & no_longer_needed_cb)196 scoped_refptr<VideoFrame> VideoFrame::WrapExternalPackedMemory(
197 Format format,
198 const gfx::Size& coded_size,
199 const gfx::Rect& visible_rect,
200 const gfx::Size& natural_size,
201 uint8* data,
202 size_t data_size,
203 base::SharedMemoryHandle handle,
204 base::TimeDelta timestamp,
205 const base::Closure& no_longer_needed_cb) {
206 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
207
208 if (!IsValidConfig(format, new_coded_size, visible_rect, natural_size))
209 return NULL;
210 if (data_size < AllocationSize(format, new_coded_size))
211 return NULL;
212
213 switch (format) {
214 case VideoFrame::I420: {
215 scoped_refptr<VideoFrame> frame(
216 new VideoFrame(format,
217 new_coded_size,
218 visible_rect,
219 natural_size,
220 scoped_ptr<gpu::MailboxHolder>(),
221 timestamp,
222 false));
223 frame->shared_memory_handle_ = handle;
224 frame->strides_[kYPlane] = new_coded_size.width();
225 frame->strides_[kUPlane] = new_coded_size.width() / 2;
226 frame->strides_[kVPlane] = new_coded_size.width() / 2;
227 frame->data_[kYPlane] = data;
228 frame->data_[kUPlane] = data + new_coded_size.GetArea();
229 frame->data_[kVPlane] = data + (new_coded_size.GetArea() * 5 / 4);
230 frame->no_longer_needed_cb_ = no_longer_needed_cb;
231 return frame;
232 }
233 default:
234 NOTIMPLEMENTED();
235 return NULL;
236 }
237 }
238
239 #if defined(OS_POSIX)
240 // static
WrapExternalDmabufs(Format format,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size,const std::vector<int> dmabuf_fds,base::TimeDelta timestamp,const base::Closure & no_longer_needed_cb)241 scoped_refptr<VideoFrame> VideoFrame::WrapExternalDmabufs(
242 Format format,
243 const gfx::Size& coded_size,
244 const gfx::Rect& visible_rect,
245 const gfx::Size& natural_size,
246 const std::vector<int> dmabuf_fds,
247 base::TimeDelta timestamp,
248 const base::Closure& no_longer_needed_cb) {
249 if (!IsValidConfig(format, coded_size, visible_rect, natural_size))
250 return NULL;
251
252 if (dmabuf_fds.size() != NumPlanes(format)) {
253 LOG(FATAL) << "Not enough dmabuf fds provided!";
254 return NULL;
255 }
256
257 scoped_refptr<VideoFrame> frame(
258 new VideoFrame(format,
259 coded_size,
260 visible_rect,
261 natural_size,
262 scoped_ptr<gpu::MailboxHolder>(),
263 timestamp,
264 false));
265
266 for (size_t i = 0; i < dmabuf_fds.size(); ++i) {
267 int duped_fd = HANDLE_EINTR(dup(dmabuf_fds[i]));
268 if (duped_fd == -1) {
269 // The already-duped in previous iterations fds will be closed when
270 // the partially-created frame drops out of scope here.
271 DLOG(ERROR) << "Failed duplicating a dmabuf fd";
272 return NULL;
273 }
274
275 frame->dmabuf_fds_[i].reset(duped_fd);
276 // Data is accessible only via fds.
277 frame->data_[i] = NULL;
278 frame->strides_[i] = 0;
279 }
280
281 frame->no_longer_needed_cb_ = no_longer_needed_cb;
282 return frame;
283 }
284 #endif
285
286 #if defined(OS_MACOSX)
287 // static
WrapCVPixelBuffer(CVPixelBufferRef cv_pixel_buffer,base::TimeDelta timestamp)288 scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
289 CVPixelBufferRef cv_pixel_buffer,
290 base::TimeDelta timestamp) {
291 DCHECK(cv_pixel_buffer);
292 DCHECK(CFGetTypeID(cv_pixel_buffer) == CVPixelBufferGetTypeID());
293
294 OSType cv_format = CVPixelBufferGetPixelFormatType(cv_pixel_buffer);
295 Format format;
296 // There are very few compatible CV pixel formats, so just check each.
297 if (cv_format == kCVPixelFormatType_420YpCbCr8Planar) {
298 format = Format::I420;
299 } else if (cv_format == kCVPixelFormatType_444YpCbCr8) {
300 format = Format::YV24;
301 } else if (cv_format == '420v') {
302 // TODO(jfroy): Use kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange when the
303 // minimum OS X and iOS SDKs permits it.
304 format = Format::NV12;
305 } else {
306 DLOG(ERROR) << "CVPixelBuffer format not supported: " << cv_format;
307 return NULL;
308 }
309
310 gfx::Size coded_size(CVImageBufferGetEncodedSize(cv_pixel_buffer));
311 gfx::Rect visible_rect(CVImageBufferGetCleanRect(cv_pixel_buffer));
312 gfx::Size natural_size(CVImageBufferGetDisplaySize(cv_pixel_buffer));
313
314 if (!IsValidConfig(format, coded_size, visible_rect, natural_size))
315 return NULL;
316
317 scoped_refptr<VideoFrame> frame(
318 new VideoFrame(format,
319 coded_size,
320 visible_rect,
321 natural_size,
322 scoped_ptr<gpu::MailboxHolder>(),
323 timestamp,
324 false));
325
326 frame->cv_pixel_buffer_.reset(cv_pixel_buffer, base::scoped_policy::RETAIN);
327 return frame;
328 }
329 #endif
330
331 // static
WrapExternalYuvData(Format format,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size,int32 y_stride,int32 u_stride,int32 v_stride,uint8 * y_data,uint8 * u_data,uint8 * v_data,base::TimeDelta timestamp,const base::Closure & no_longer_needed_cb)332 scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
333 Format format,
334 const gfx::Size& coded_size,
335 const gfx::Rect& visible_rect,
336 const gfx::Size& natural_size,
337 int32 y_stride,
338 int32 u_stride,
339 int32 v_stride,
340 uint8* y_data,
341 uint8* u_data,
342 uint8* v_data,
343 base::TimeDelta timestamp,
344 const base::Closure& no_longer_needed_cb) {
345 gfx::Size new_coded_size = AdjustCodedSize(format, coded_size);
346 CHECK(IsValidConfig(format, new_coded_size, visible_rect, natural_size));
347
348 scoped_refptr<VideoFrame> frame(
349 new VideoFrame(format,
350 new_coded_size,
351 visible_rect,
352 natural_size,
353 scoped_ptr<gpu::MailboxHolder>(),
354 timestamp,
355 false));
356 frame->strides_[kYPlane] = y_stride;
357 frame->strides_[kUPlane] = u_stride;
358 frame->strides_[kVPlane] = v_stride;
359 frame->data_[kYPlane] = y_data;
360 frame->data_[kUPlane] = u_data;
361 frame->data_[kVPlane] = v_data;
362 frame->no_longer_needed_cb_ = no_longer_needed_cb;
363 return frame;
364 }
365
366 // static
WrapVideoFrame(const scoped_refptr<VideoFrame> & frame,const gfx::Rect & visible_rect,const gfx::Size & natural_size,const base::Closure & no_longer_needed_cb)367 scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
368 const scoped_refptr<VideoFrame>& frame,
369 const gfx::Rect& visible_rect,
370 const gfx::Size& natural_size,
371 const base::Closure& no_longer_needed_cb) {
372 // NATIVE_TEXTURE frames need mailbox info propagated, and there's no support
373 // for that here yet, see http://crbug/362521.
374 CHECK_NE(frame->format(), NATIVE_TEXTURE);
375
376 DCHECK(frame->visible_rect().Contains(visible_rect));
377 scoped_refptr<VideoFrame> wrapped_frame(
378 new VideoFrame(frame->format(),
379 frame->coded_size(),
380 visible_rect,
381 natural_size,
382 scoped_ptr<gpu::MailboxHolder>(),
383 frame->timestamp(),
384 frame->end_of_stream()));
385
386 for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
387 wrapped_frame->strides_[i] = frame->stride(i);
388 wrapped_frame->data_[i] = frame->data(i);
389 }
390
391 wrapped_frame->no_longer_needed_cb_ = no_longer_needed_cb;
392 return wrapped_frame;
393 }
394
395 // static
CreateEOSFrame()396 scoped_refptr<VideoFrame> VideoFrame::CreateEOSFrame() {
397 return new VideoFrame(VideoFrame::UNKNOWN,
398 gfx::Size(),
399 gfx::Rect(),
400 gfx::Size(),
401 scoped_ptr<gpu::MailboxHolder>(),
402 kNoTimestamp(),
403 true);
404 }
405
406 // static
CreateColorFrame(const gfx::Size & size,uint8 y,uint8 u,uint8 v,base::TimeDelta timestamp)407 scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
408 const gfx::Size& size,
409 uint8 y, uint8 u, uint8 v,
410 base::TimeDelta timestamp) {
411 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
412 VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
413 FillYUV(frame.get(), y, u, v);
414 return frame;
415 }
416
417 // static
CreateBlackFrame(const gfx::Size & size)418 scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
419 const uint8 kBlackY = 0x00;
420 const uint8 kBlackUV = 0x80;
421 const base::TimeDelta kZero;
422 return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
423 }
424
425 // static
CreateTransparentFrame(const gfx::Size & size)426 scoped_refptr<VideoFrame> VideoFrame::CreateTransparentFrame(
427 const gfx::Size& size) {
428 const uint8 kBlackY = 0x00;
429 const uint8 kBlackUV = 0x00;
430 const uint8 kTransparentA = 0x00;
431 const base::TimeDelta kZero;
432 scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
433 VideoFrame::YV12A, size, gfx::Rect(size), size, kZero);
434 FillYUVA(frame.get(), kBlackY, kBlackUV, kBlackUV, kTransparentA);
435 return frame;
436 }
437
438 #if defined(VIDEO_HOLE)
439 // This block and other blocks wrapped around #if defined(VIDEO_HOLE) is not
440 // maintained by the general compositor team. Please contact the following
441 // people instead:
442 //
443 // wonsik@chromium.org
444 // ycheo@chromium.org
445
446 // static
CreateHoleFrame(const gfx::Size & size)447 scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
448 const gfx::Size& size) {
449 DCHECK(IsValidConfig(VideoFrame::HOLE, size, gfx::Rect(size), size));
450 scoped_refptr<VideoFrame> frame(
451 new VideoFrame(VideoFrame::HOLE,
452 size,
453 gfx::Rect(size),
454 size,
455 scoped_ptr<gpu::MailboxHolder>(),
456 base::TimeDelta(),
457 false));
458 return frame;
459 }
460 #endif // defined(VIDEO_HOLE)
461
462 // static
NumPlanes(Format format)463 size_t VideoFrame::NumPlanes(Format format) {
464 switch (format) {
465 case VideoFrame::NATIVE_TEXTURE:
466 #if defined(VIDEO_HOLE)
467 case VideoFrame::HOLE:
468 #endif // defined(VIDEO_HOLE)
469 return 0;
470 case VideoFrame::NV12:
471 return 2;
472 case VideoFrame::YV12:
473 case VideoFrame::YV16:
474 case VideoFrame::I420:
475 case VideoFrame::YV12J:
476 case VideoFrame::YV24:
477 return 3;
478 case VideoFrame::YV12A:
479 return 4;
480 case VideoFrame::UNKNOWN:
481 break;
482 }
483 NOTREACHED() << "Unsupported video frame format: " << format;
484 return 0;
485 }
486
487
488 // static
AllocationSize(Format format,const gfx::Size & coded_size)489 size_t VideoFrame::AllocationSize(Format format, const gfx::Size& coded_size) {
490 size_t total = 0;
491 for (size_t i = 0; i < NumPlanes(format); ++i)
492 total += PlaneAllocationSize(format, i, coded_size);
493 return total;
494 }
495
496 // static
PlaneSize(Format format,size_t plane,const gfx::Size & coded_size)497 gfx::Size VideoFrame::PlaneSize(Format format,
498 size_t plane,
499 const gfx::Size& coded_size) {
500 // Align to multiple-of-two size overall. This ensures that non-subsampled
501 // planes can be addressed by pixel with the same scaling as the subsampled
502 // planes.
503 const int width = RoundUp(coded_size.width(), 2);
504 const int height = RoundUp(coded_size.height(), 2);
505 switch (format) {
506 case VideoFrame::YV24:
507 switch (plane) {
508 case VideoFrame::kYPlane:
509 case VideoFrame::kUPlane:
510 case VideoFrame::kVPlane:
511 return gfx::Size(width, height);
512 default:
513 break;
514 }
515 break;
516 case VideoFrame::YV12:
517 case VideoFrame::YV12J:
518 case VideoFrame::I420:
519 switch (plane) {
520 case VideoFrame::kYPlane:
521 return gfx::Size(width, height);
522 case VideoFrame::kUPlane:
523 case VideoFrame::kVPlane:
524 return gfx::Size(width / 2, height / 2);
525 default:
526 break;
527 }
528 break;
529 case VideoFrame::YV12A:
530 switch (plane) {
531 case VideoFrame::kYPlane:
532 case VideoFrame::kAPlane:
533 return gfx::Size(width, height);
534 case VideoFrame::kUPlane:
535 case VideoFrame::kVPlane:
536 return gfx::Size(width / 2, height / 2);
537 default:
538 break;
539 }
540 break;
541 case VideoFrame::YV16:
542 switch (plane) {
543 case VideoFrame::kYPlane:
544 return gfx::Size(width, height);
545 case VideoFrame::kUPlane:
546 case VideoFrame::kVPlane:
547 return gfx::Size(width / 2, height);
548 default:
549 break;
550 }
551 break;
552 case VideoFrame::NV12:
553 switch (plane) {
554 case VideoFrame::kYPlane:
555 return gfx::Size(width, height);
556 case VideoFrame::kUVPlane:
557 return gfx::Size(width, height / 2);
558 default:
559 break;
560 }
561 break;
562 case VideoFrame::UNKNOWN:
563 case VideoFrame::NATIVE_TEXTURE:
564 #if defined(VIDEO_HOLE)
565 case VideoFrame::HOLE:
566 #endif // defined(VIDEO_HOLE)
567 break;
568 }
569 NOTREACHED() << "Unsupported video frame format/plane: "
570 << format << "/" << plane;
571 return gfx::Size();
572 }
573
PlaneAllocationSize(Format format,size_t plane,const gfx::Size & coded_size)574 size_t VideoFrame::PlaneAllocationSize(Format format,
575 size_t plane,
576 const gfx::Size& coded_size) {
577 // VideoFrame formats are (so far) all YUV and 1 byte per sample.
578 return PlaneSize(format, plane, coded_size).GetArea();
579 }
580
581 // static
PlaneHorizontalBitsPerPixel(Format format,size_t plane)582 int VideoFrame::PlaneHorizontalBitsPerPixel(Format format, size_t plane) {
583 switch (format) {
584 case VideoFrame::YV24:
585 switch (plane) {
586 case kYPlane:
587 case kUPlane:
588 case kVPlane:
589 return 8;
590 default:
591 break;
592 }
593 break;
594 case VideoFrame::YV12:
595 case VideoFrame::YV16:
596 case VideoFrame::I420:
597 case VideoFrame::YV12J:
598 switch (plane) {
599 case kYPlane:
600 return 8;
601 case kUPlane:
602 case kVPlane:
603 return 2;
604 default:
605 break;
606 }
607 break;
608 case VideoFrame::YV12A:
609 switch (plane) {
610 case kYPlane:
611 case kAPlane:
612 return 8;
613 case kUPlane:
614 case kVPlane:
615 return 2;
616 default:
617 break;
618 }
619 break;
620 case VideoFrame::NV12:
621 switch (plane) {
622 case kYPlane:
623 return 8;
624 case kUVPlane:
625 return 4;
626 default:
627 break;
628 }
629 break;
630 case VideoFrame::UNKNOWN:
631 #if defined(VIDEO_HOLE)
632 case VideoFrame::HOLE:
633 #endif // defined(VIDEO_HOLE)
634 case VideoFrame::NATIVE_TEXTURE:
635 break;
636 }
637 NOTREACHED() << "Unsupported video frame format/plane: "
638 << format << "/" << plane;
639 return 0;
640 }
641
642 // Release data allocated by AllocateYUV().
ReleaseData(uint8 * data)643 static void ReleaseData(uint8* data) {
644 DCHECK(data);
645 base::AlignedFree(data);
646 }
647
AllocateYUV()648 void VideoFrame::AllocateYUV() {
649 DCHECK(format_ == VideoFrame::YV12 || format_ == VideoFrame::YV16 ||
650 format_ == VideoFrame::YV12A || format_ == VideoFrame::I420 ||
651 format_ == VideoFrame::YV12J || format_ == VideoFrame::YV24);
652 // Align Y rows at least at 16 byte boundaries. The stride for both
653 // YV12 and YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for
654 // U and V applies to two rows of Y (one byte of UV for 4 bytes of Y), so in
655 // the case of YV12 the strides are identical for the same width surface, but
656 // the number of bytes allocated for YV12 is 1/2 the amount for U & V as
657 // YV16. We also round the height of the surface allocated to be an even
658 // number to avoid any potential of faulting by code that attempts to access
659 // the Y values of the final row, but assumes that the last row of U & V
660 // applies to a full two rows of Y. YV12A is the same as YV12, but with an
661 // additional alpha plane that has the same size and alignment as the Y plane.
662 size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane),
663 kFrameSizeAlignment);
664 size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane),
665 kFrameSizeAlignment);
666
667 // The *2 here is because some formats (e.g. h264) allow interlaced coding,
668 // and then the size needs to be a multiple of two macroblocks (vertically).
669 // See libavcodec/utils.c:avcodec_align_dimensions2().
670 size_t y_height = RoundUp(coded_size_.height(), kFrameSizeAlignment * 2);
671 size_t uv_height =
672 (format_ == VideoFrame::YV12 || format_ == VideoFrame::YV12A ||
673 format_ == VideoFrame::I420)
674 ? y_height / 2
675 : y_height;
676 size_t y_bytes = y_height * y_stride;
677 size_t uv_bytes = uv_height * uv_stride;
678 size_t a_bytes = format_ == VideoFrame::YV12A ? y_bytes : 0;
679
680 // The extra line of UV being allocated is because h264 chroma MC
681 // overreads by one line in some cases, see libavcodec/utils.c:
682 // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
683 // put_h264_chroma_mc4_ssse3().
684 const size_t data_size =
685 y_bytes + (uv_bytes * 2 + uv_stride) + a_bytes + kFrameSizePadding;
686 uint8* data = reinterpret_cast<uint8*>(
687 base::AlignedAlloc(data_size, kFrameAddressAlignment));
688 // FFmpeg expects the initialize allocation to be zero-initialized. Failure
689 // to do so can lead to unitialized value usage. See http://crbug.com/390941
690 memset(data, 0, data_size);
691 no_longer_needed_cb_ = base::Bind(&ReleaseData, data);
692 COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0);
693 data_[VideoFrame::kYPlane] = data;
694 data_[VideoFrame::kUPlane] = data + y_bytes;
695 data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes;
696 strides_[VideoFrame::kYPlane] = y_stride;
697 strides_[VideoFrame::kUPlane] = uv_stride;
698 strides_[VideoFrame::kVPlane] = uv_stride;
699 if (format_ == YV12A) {
700 data_[VideoFrame::kAPlane] = data + y_bytes + (2 * uv_bytes);
701 strides_[VideoFrame::kAPlane] = y_stride;
702 }
703 }
704
VideoFrame(VideoFrame::Format format,const gfx::Size & coded_size,const gfx::Rect & visible_rect,const gfx::Size & natural_size,scoped_ptr<gpu::MailboxHolder> mailbox_holder,base::TimeDelta timestamp,bool end_of_stream)705 VideoFrame::VideoFrame(VideoFrame::Format format,
706 const gfx::Size& coded_size,
707 const gfx::Rect& visible_rect,
708 const gfx::Size& natural_size,
709 scoped_ptr<gpu::MailboxHolder> mailbox_holder,
710 base::TimeDelta timestamp,
711 bool end_of_stream)
712 : format_(format),
713 coded_size_(coded_size),
714 visible_rect_(visible_rect),
715 natural_size_(natural_size),
716 mailbox_holder_(mailbox_holder.Pass()),
717 shared_memory_handle_(base::SharedMemory::NULLHandle()),
718 timestamp_(timestamp),
719 release_sync_point_(0),
720 end_of_stream_(end_of_stream) {
721 DCHECK(IsValidConfig(format_, coded_size_, visible_rect_, natural_size_));
722
723 memset(&strides_, 0, sizeof(strides_));
724 memset(&data_, 0, sizeof(data_));
725 }
726
~VideoFrame()727 VideoFrame::~VideoFrame() {
728 if (!mailbox_holder_release_cb_.is_null()) {
729 uint32 release_sync_point;
730 {
731 // To ensure that changes to |release_sync_point_| are visible on this
732 // thread (imply a memory barrier).
733 base::AutoLock locker(release_sync_point_lock_);
734 release_sync_point = release_sync_point_;
735 }
736 base::ResetAndReturn(&mailbox_holder_release_cb_).Run(release_sync_point);
737 }
738 if (!no_longer_needed_cb_.is_null())
739 base::ResetAndReturn(&no_longer_needed_cb_).Run();
740 }
741
742 // static
IsValidPlane(size_t plane,VideoFrame::Format format)743 bool VideoFrame::IsValidPlane(size_t plane, VideoFrame::Format format) {
744 return (plane < NumPlanes(format));
745 }
746
stride(size_t plane) const747 int VideoFrame::stride(size_t plane) const {
748 DCHECK(IsValidPlane(plane, format_));
749 return strides_[plane];
750 }
751
752 // static
RowBytes(size_t plane,VideoFrame::Format format,int width)753 size_t VideoFrame::RowBytes(size_t plane, VideoFrame::Format format,
754 int width) {
755 DCHECK(IsValidPlane(plane, format));
756 switch (format) {
757 case VideoFrame::YV24:
758 switch (plane) {
759 case kYPlane:
760 case kUPlane:
761 case kVPlane:
762 return width;
763 default:
764 break;
765 }
766 break;
767 case VideoFrame::YV12:
768 case VideoFrame::YV16:
769 case VideoFrame::I420:
770 case VideoFrame::YV12J:
771 switch (plane) {
772 case kYPlane:
773 return width;
774 case kUPlane:
775 case kVPlane:
776 return RoundUp(width, 2) / 2;
777 default:
778 break;
779 }
780 break;
781 case VideoFrame::YV12A:
782 switch (plane) {
783 case kYPlane:
784 case kAPlane:
785 return width;
786 case kUPlane:
787 case kVPlane:
788 return RoundUp(width, 2) / 2;
789 default:
790 break;
791 }
792 break;
793 case VideoFrame::NV12:
794 switch (plane) {
795 case kYPlane:
796 case kUVPlane:
797 return width;
798 default:
799 break;
800 }
801 break;
802 case VideoFrame::UNKNOWN:
803 #if defined(VIDEO_HOLE)
804 case VideoFrame::HOLE:
805 #endif // defined(VIDEO_HOLE)
806 case VideoFrame::NATIVE_TEXTURE:
807 break;
808 }
809 NOTREACHED() << "Unsupported video frame format/plane: " << format << "/"
810 << plane;
811 return 0;
812 }
813
row_bytes(size_t plane) const814 int VideoFrame::row_bytes(size_t plane) const {
815 return RowBytes(plane, format_, coded_size_.width());
816 }
817
818 // static
Rows(size_t plane,VideoFrame::Format format,int height)819 size_t VideoFrame::Rows(size_t plane, VideoFrame::Format format, int height) {
820 DCHECK(IsValidPlane(plane, format));
821 switch (format) {
822 case VideoFrame::YV24:
823 case VideoFrame::YV16:
824 switch (plane) {
825 case kYPlane:
826 case kUPlane:
827 case kVPlane:
828 return height;
829 default:
830 break;
831 }
832 break;
833 case VideoFrame::YV12:
834 case VideoFrame::YV12J:
835 case VideoFrame::I420:
836 switch (plane) {
837 case kYPlane:
838 return height;
839 case kUPlane:
840 case kVPlane:
841 return RoundUp(height, 2) / 2;
842 default:
843 break;
844 }
845 break;
846 case VideoFrame::YV12A:
847 switch (plane) {
848 case kYPlane:
849 case kAPlane:
850 return height;
851 case kUPlane:
852 case kVPlane:
853 return RoundUp(height, 2) / 2;
854 default:
855 break;
856 }
857 break;
858 case VideoFrame::NV12:
859 switch (plane) {
860 case kYPlane:
861 return height;
862 case kUVPlane:
863 return RoundUp(height, 2) / 2;
864 default:
865 break;
866 }
867 break;
868 case VideoFrame::UNKNOWN:
869 #if defined(VIDEO_HOLE)
870 case VideoFrame::HOLE:
871 #endif // defined(VIDEO_HOLE)
872 case VideoFrame::NATIVE_TEXTURE:
873 break;
874 }
875 NOTREACHED() << "Unsupported video frame format/plane: " << format << "/"
876 << plane;
877 return 0;
878 }
879
rows(size_t plane) const880 int VideoFrame::rows(size_t plane) const {
881 return Rows(plane, format_, coded_size_.height());
882 }
883
data(size_t plane) const884 uint8* VideoFrame::data(size_t plane) const {
885 DCHECK(IsValidPlane(plane, format_));
886 return data_[plane];
887 }
888
mailbox_holder() const889 const gpu::MailboxHolder* VideoFrame::mailbox_holder() const {
890 DCHECK_EQ(format_, NATIVE_TEXTURE);
891 return mailbox_holder_.get();
892 }
893
shared_memory_handle() const894 base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
895 return shared_memory_handle_;
896 }
897
UpdateReleaseSyncPoint(SyncPointClient * client)898 void VideoFrame::UpdateReleaseSyncPoint(SyncPointClient* client) {
899 DCHECK_EQ(format_, NATIVE_TEXTURE);
900 base::AutoLock locker(release_sync_point_lock_);
901 // Must wait on the previous sync point before inserting a new sync point so
902 // that |mailbox_holder_release_cb_| guarantees the previous sync point
903 // occurred when it waits on |release_sync_point_|.
904 if (release_sync_point_)
905 client->WaitSyncPoint(release_sync_point_);
906 release_sync_point_ = client->InsertSyncPoint();
907 }
908
909 #if defined(OS_POSIX)
dmabuf_fd(size_t plane) const910 int VideoFrame::dmabuf_fd(size_t plane) const {
911 return dmabuf_fds_[plane].get();
912 }
913 #endif
914
915 #if defined(OS_MACOSX)
cv_pixel_buffer() const916 CVPixelBufferRef VideoFrame::cv_pixel_buffer() const {
917 return cv_pixel_buffer_.get();
918 }
919 #endif
920
HashFrameForTesting(base::MD5Context * context)921 void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
922 for (int plane = 0; plane < kMaxPlanes; ++plane) {
923 if (!IsValidPlane(plane, format_))
924 break;
925 for (int row = 0; row < rows(plane); ++row) {
926 base::MD5Update(context, base::StringPiece(
927 reinterpret_cast<char*>(data(plane) + stride(plane) * row),
928 row_bytes(plane)));
929 }
930 }
931 }
932
933 } // namespace media
934