video_frame.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
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 "media/base/limits.h"
15#include "media/base/video_util.h"
16#include "third_party/skia/include/core/SkBitmap.h"
17
18namespace media {
19
20// static
21scoped_refptr<VideoFrame> VideoFrame::CreateFrame(
22    VideoFrame::Format format,
23    const gfx::Size& coded_size,
24    const gfx::Rect& visible_rect,
25    const gfx::Size& natural_size,
26    base::TimeDelta timestamp) {
27  DCHECK(IsValidConfig(format, coded_size, visible_rect, natural_size));
28  scoped_refptr<VideoFrame> frame(new VideoFrame(
29      format, coded_size, visible_rect, natural_size, timestamp));
30  switch (format) {
31    case VideoFrame::RGB32:
32      frame->AllocateRGB(4u);
33      break;
34    case VideoFrame::YV12:
35    case VideoFrame::YV12A:
36    case VideoFrame::YV16:
37      frame->AllocateYUV();
38      break;
39    default:
40      LOG(FATAL) << "Unsupported frame format: " << format;
41  }
42  return frame;
43}
44
45// static
46std::string VideoFrame::FormatToString(VideoFrame::Format format) {
47  switch (format) {
48    case VideoFrame::INVALID:
49      return "INVALID";
50    case VideoFrame::RGB32:
51      return "RGB32";
52    case VideoFrame::YV12:
53      return "YV12";
54    case VideoFrame::YV16:
55      return "YV16";
56    case VideoFrame::EMPTY:
57      return "EMPTY";
58    case VideoFrame::I420:
59      return "I420";
60    case VideoFrame::NATIVE_TEXTURE:
61      return "NATIVE_TEXTURE";
62#if defined(GOOGLE_TV)
63    case VideoFrame::HOLE:
64      return "HOLE";
65#endif
66    case VideoFrame::YV12A:
67      return "YV12A";
68  }
69  NOTREACHED() << "Invalid videoframe format provided: " << format;
70  return "";
71}
72
73// static
74bool VideoFrame::IsValidConfig(VideoFrame::Format format,
75                               const gfx::Size& coded_size,
76                               const gfx::Rect& visible_rect,
77                               const gfx::Size& natural_size) {
78  return (format != VideoFrame::INVALID &&
79          !coded_size.IsEmpty() &&
80          coded_size.GetArea() <= limits::kMaxCanvas &&
81          coded_size.width() <= limits::kMaxDimension &&
82          coded_size.height() <= limits::kMaxDimension &&
83          !visible_rect.IsEmpty() &&
84          visible_rect.x() >= 0 && visible_rect.y() >= 0 &&
85          visible_rect.right() <= coded_size.width() &&
86          visible_rect.bottom() <= coded_size.height() &&
87          !natural_size.IsEmpty() &&
88          natural_size.GetArea() <= limits::kMaxCanvas &&
89          natural_size.width() <= limits::kMaxDimension &&
90          natural_size.height() <= limits::kMaxDimension);
91}
92
93// static
94scoped_refptr<VideoFrame> VideoFrame::WrapNativeTexture(
95    const scoped_refptr<MailboxHolder>& mailbox_holder,
96    uint32 texture_target,
97    const gfx::Size& coded_size,
98    const gfx::Rect& visible_rect,
99    const gfx::Size& natural_size,
100    base::TimeDelta timestamp,
101    const ReadPixelsCB& read_pixels_cb,
102    const base::Closure& no_longer_needed_cb) {
103  scoped_refptr<VideoFrame> frame(new VideoFrame(
104      NATIVE_TEXTURE, coded_size, visible_rect, natural_size, timestamp));
105  frame->texture_mailbox_holder_ = mailbox_holder;
106  frame->texture_target_ = texture_target;
107  frame->read_pixels_cb_ = read_pixels_cb;
108  frame->no_longer_needed_cb_ = no_longer_needed_cb;
109
110  return frame;
111}
112
113void VideoFrame::ReadPixelsFromNativeTexture(const SkBitmap& pixels) {
114  DCHECK_EQ(format_, NATIVE_TEXTURE);
115  if (!read_pixels_cb_.is_null())
116    read_pixels_cb_.Run(pixels);
117}
118
119// static
120scoped_refptr<VideoFrame> VideoFrame::WrapExternalYuvData(
121    Format format,
122    const gfx::Size& coded_size,
123    const gfx::Rect& visible_rect,
124    const gfx::Size& natural_size,
125    int32 y_stride, int32 u_stride, int32 v_stride,
126    uint8* y_data, uint8* u_data, uint8* v_data,
127    base::TimeDelta timestamp,
128    base::SharedMemoryHandle shm_handle,
129    const base::Closure& no_longer_needed_cb) {
130  DCHECK(format == YV12 || format == YV16 || format == I420) << format;
131  scoped_refptr<VideoFrame> frame(new VideoFrame(
132      format, coded_size, visible_rect, natural_size, timestamp));
133  frame->shared_memory_handle_ = shm_handle;
134  frame->strides_[kYPlane] = y_stride;
135  frame->strides_[kUPlane] = u_stride;
136  frame->strides_[kVPlane] = v_stride;
137  frame->data_[kYPlane] = y_data;
138  frame->data_[kUPlane] = u_data;
139  frame->data_[kVPlane] = v_data;
140  frame->no_longer_needed_cb_ = no_longer_needed_cb;
141  return frame;
142}
143
144// static
145scoped_refptr<VideoFrame> VideoFrame::CreateEmptyFrame() {
146  return new VideoFrame(
147      VideoFrame::EMPTY, gfx::Size(), gfx::Rect(), gfx::Size(),
148      base::TimeDelta());
149}
150
151// static
152scoped_refptr<VideoFrame> VideoFrame::CreateColorFrame(
153    const gfx::Size& size,
154    uint8 y, uint8 u, uint8 v,
155    base::TimeDelta timestamp) {
156  DCHECK(IsValidConfig(VideoFrame::YV12, size, gfx::Rect(size), size));
157  scoped_refptr<VideoFrame> frame = VideoFrame::CreateFrame(
158      VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
159  FillYUV(frame.get(), y, u, v);
160  return frame;
161}
162
163// static
164scoped_refptr<VideoFrame> VideoFrame::CreateBlackFrame(const gfx::Size& size) {
165  const uint8 kBlackY = 0x00;
166  const uint8 kBlackUV = 0x80;
167  const base::TimeDelta kZero;
168  return CreateColorFrame(size, kBlackY, kBlackUV, kBlackUV, kZero);
169}
170
171#if defined(GOOGLE_TV)
172// This block and other blocks wrapped around #if defined(GOOGLE_TV) is not
173// maintained by the general compositor team. Please contact the following
174// people instead:
175//
176// wonsik@chromium.org
177// ycheo@chromium.org
178
179// static
180scoped_refptr<VideoFrame> VideoFrame::CreateHoleFrame(
181    const gfx::Size& size) {
182  DCHECK(IsValidConfig(VideoFrame::HOLE, size, gfx::Rect(size), size));
183  scoped_refptr<VideoFrame> frame(new VideoFrame(
184      VideoFrame::HOLE, size, gfx::Rect(size), size, base::TimeDelta()));
185  return frame;
186}
187#endif
188
189// static
190size_t VideoFrame::NumPlanes(Format format) {
191  switch (format) {
192    case VideoFrame::NATIVE_TEXTURE:
193#if defined(GOOGLE_TV)
194    case VideoFrame::HOLE:
195#endif
196      return 0;
197    case VideoFrame::RGB32:
198      return 1;
199    case VideoFrame::YV12:
200    case VideoFrame::YV16:
201      return 3;
202    case VideoFrame::YV12A:
203      return 4;
204    case VideoFrame::EMPTY:
205    case VideoFrame::I420:
206    case VideoFrame::INVALID:
207      break;
208  }
209  NOTREACHED() << "Unsupported video frame format: " << format;
210  return 0;
211}
212
213static inline size_t RoundUp(size_t value, size_t alignment) {
214  // Check that |alignment| is a power of 2.
215  DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1)));
216  return ((value + (alignment - 1)) & ~(alignment-1));
217}
218
219// Release data allocated by AllocateRGB() or AllocateYUV().
220static void ReleaseData(uint8* data) {
221  DCHECK(data);
222  base::AlignedFree(data);
223}
224
225void VideoFrame::AllocateRGB(size_t bytes_per_pixel) {
226  // Round up to align at least at a 16-byte boundary for each row.
227  // This is sufficient for MMX and SSE2 reads (movq/movdqa).
228  size_t bytes_per_row = RoundUp(coded_size_.width(),
229                                 kFrameSizeAlignment) * bytes_per_pixel;
230  size_t aligned_height = RoundUp(coded_size_.height(), kFrameSizeAlignment);
231  strides_[VideoFrame::kRGBPlane] = bytes_per_row;
232  data_[VideoFrame::kRGBPlane] = reinterpret_cast<uint8*>(
233      base::AlignedAlloc(bytes_per_row * aligned_height + kFrameSizePadding,
234                         kFrameAddressAlignment));
235  no_longer_needed_cb_ = base::Bind(&ReleaseData, data_[VideoFrame::kRGBPlane]);
236  DCHECK(!(reinterpret_cast<intptr_t>(data_[VideoFrame::kRGBPlane]) & 7));
237  COMPILE_ASSERT(0 == VideoFrame::kRGBPlane, RGB_data_must_be_index_0);
238}
239
240void VideoFrame::AllocateYUV() {
241  DCHECK(format_ == VideoFrame::YV12 || format_ == VideoFrame::YV16 ||
242         format_ == VideoFrame::YV12A);
243  // Align Y rows at least at 16 byte boundaries.  The stride for both
244  // YV12 and YV16 is 1/2 of the stride of Y.  For YV12, every row of bytes for
245  // U and V applies to two rows of Y (one byte of UV for 4 bytes of Y), so in
246  // the case of YV12 the strides are identical for the same width surface, but
247  // the number of bytes allocated for YV12 is 1/2 the amount for U & V as
248  // YV16. We also round the height of the surface allocated to be an even
249  // number to avoid any potential of faulting by code that attempts to access
250  // the Y values of the final row, but assumes that the last row of U & V
251  // applies to a full two rows of Y. YV12A is the same as YV12, but with an
252  // additional alpha plane that has the same size and alignment as the Y plane.
253
254  size_t y_stride = RoundUp(row_bytes(VideoFrame::kYPlane),
255                            kFrameSizeAlignment);
256  size_t uv_stride = RoundUp(row_bytes(VideoFrame::kUPlane),
257                             kFrameSizeAlignment);
258  // The *2 here is because some formats (e.g. h264) allow interlaced coding,
259  // and then the size needs to be a multiple of two macroblocks (vertically).
260  // See libavcodec/utils.c:avcodec_align_dimensions2().
261  size_t y_height = RoundUp(coded_size_.height(), kFrameSizeAlignment * 2);
262  size_t uv_height = (format_ == VideoFrame::YV12 ||
263                      format_ == VideoFrame::YV12A) ?
264                              y_height / 2 : y_height;
265  size_t y_bytes = y_height * y_stride;
266  size_t uv_bytes = uv_height * uv_stride;
267  size_t a_bytes = format_ == VideoFrame::YV12A ? y_bytes : 0;
268
269  // The extra line of UV being allocated is because h264 chroma MC
270  // overreads by one line in some cases, see libavcodec/utils.c:
271  // avcodec_align_dimensions2() and libavcodec/x86/h264_chromamc.asm:
272  // put_h264_chroma_mc4_ssse3().
273  uint8* data = reinterpret_cast<uint8*>(
274      base::AlignedAlloc(
275          y_bytes + (uv_bytes * 2 + uv_stride) + a_bytes + kFrameSizePadding,
276          kFrameAddressAlignment));
277  no_longer_needed_cb_ = base::Bind(&ReleaseData, data);
278  COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0);
279  data_[VideoFrame::kYPlane] = data;
280  data_[VideoFrame::kUPlane] = data + y_bytes;
281  data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes;
282  strides_[VideoFrame::kYPlane] = y_stride;
283  strides_[VideoFrame::kUPlane] = uv_stride;
284  strides_[VideoFrame::kVPlane] = uv_stride;
285  if (format_ == YV12A) {
286    data_[VideoFrame::kAPlane] = data + y_bytes + (2 * uv_bytes);
287    strides_[VideoFrame::kAPlane] = y_stride;
288  }
289}
290
291VideoFrame::VideoFrame(VideoFrame::Format format,
292                       const gfx::Size& coded_size,
293                       const gfx::Rect& visible_rect,
294                       const gfx::Size& natural_size,
295                       base::TimeDelta timestamp)
296    : format_(format),
297      coded_size_(coded_size),
298      visible_rect_(visible_rect),
299      natural_size_(natural_size),
300      texture_target_(0),
301      shared_memory_handle_(base::SharedMemory::NULLHandle()),
302      timestamp_(timestamp) {
303  memset(&strides_, 0, sizeof(strides_));
304  memset(&data_, 0, sizeof(data_));
305}
306
307VideoFrame::~VideoFrame() {
308  if (!no_longer_needed_cb_.is_null())
309    base::ResetAndReturn(&no_longer_needed_cb_).Run();
310}
311
312bool VideoFrame::IsValidPlane(size_t plane) const {
313  return (plane < NumPlanes(format_));
314}
315
316int VideoFrame::stride(size_t plane) const {
317  DCHECK(IsValidPlane(plane));
318  return strides_[plane];
319}
320
321int VideoFrame::row_bytes(size_t plane) const {
322  DCHECK(IsValidPlane(plane));
323  int width = coded_size_.width();
324  switch (format_) {
325    // 32bpp.
326    case RGB32:
327      return width * 4;
328
329    // Planar, 8bpp.
330    case YV12:
331    case YV16:
332    case YV12A:
333      if (plane == kYPlane || plane == kAPlane)
334        return width;
335      return RoundUp(width, 2) / 2;
336
337    default:
338      break;
339  }
340
341  // Intentionally leave out non-production formats.
342  NOTREACHED() << "Unsupported video frame format: " << format_;
343  return 0;
344}
345
346int VideoFrame::rows(size_t plane) const {
347  DCHECK(IsValidPlane(plane));
348  int height = coded_size_.height();
349  switch (format_) {
350    case RGB32:
351    case YV16:
352      return height;
353
354    case YV12:
355    case YV12A:
356      if (plane == kYPlane || plane == kAPlane)
357        return height;
358      return RoundUp(height, 2) / 2;
359
360    default:
361      break;
362  }
363
364  // Intentionally leave out non-production formats.
365  NOTREACHED() << "Unsupported video frame format: " << format_;
366  return 0;
367}
368
369uint8* VideoFrame::data(size_t plane) const {
370  DCHECK(IsValidPlane(plane));
371  return data_[plane];
372}
373
374const scoped_refptr<VideoFrame::MailboxHolder>& VideoFrame::texture_mailbox()
375    const {
376  DCHECK_EQ(format_, NATIVE_TEXTURE);
377  return texture_mailbox_holder_;
378}
379
380uint32 VideoFrame::texture_target() const {
381  DCHECK_EQ(format_, NATIVE_TEXTURE);
382  return texture_target_;
383}
384
385base::SharedMemoryHandle VideoFrame::shared_memory_handle() const {
386  return shared_memory_handle_;
387}
388
389bool VideoFrame::IsEndOfStream() const {
390  return format_ == VideoFrame::EMPTY;
391}
392
393void VideoFrame::HashFrameForTesting(base::MD5Context* context) {
394  for (int plane = 0; plane < kMaxPlanes; ++plane) {
395    if (!IsValidPlane(plane))
396      break;
397    for (int row = 0; row < rows(plane); ++row) {
398      base::MD5Update(context, base::StringPiece(
399          reinterpret_cast<char*>(data(plane) + stride(plane) * row),
400          row_bytes(plane)));
401    }
402  }
403}
404
405VideoFrame::MailboxHolder::MailboxHolder(
406    const gpu::Mailbox& mailbox,
407    unsigned sync_point,
408    const TextureNoLongerNeededCallback& release_callback)
409    : mailbox_(mailbox),
410      sync_point_(sync_point),
411      release_callback_(release_callback) {}
412
413VideoFrame::MailboxHolder::~MailboxHolder() {
414  if (!release_callback_.is_null())
415    release_callback_.Run(sync_point_);
416}
417
418}  // namespace media
419