diff options
Diffstat (limited to 'media/base')
-rw-r--r-- | media/base/buffers.h | 64 | ||||
-rw-r--r-- | media/base/video_frame.cc | 170 | ||||
-rw-r--r-- | media/base/video_frame.h | 111 | ||||
-rw-r--r-- | media/base/video_frame_impl.cc | 194 | ||||
-rw-r--r-- | media/base/video_frame_impl.h | 58 | ||||
-rw-r--r-- | media/base/video_frame_impl_unittest.cc | 187 | ||||
-rw-r--r-- | media/base/video_frame_unittest.cc | 176 |
7 files changed, 458 insertions, 502 deletions
diff --git a/media/base/buffers.h b/media/base/buffers.h index 6102ea9..d03717d 100644 --- a/media/base/buffers.h +++ b/media/base/buffers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2008-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -122,68 +122,6 @@ class WritableBuffer : public Buffer { virtual ~WritableBuffer() {} }; - -struct VideoSurface { - static const size_t kMaxPlanes = 3; - - static const size_t kNumRGBPlanes = 1; - static const size_t kRGBPlane = 0; - - static const size_t kNumYUVPlanes = 3; - static const size_t kYPlane = 0; - static const size_t kUPlane = 1; - static const size_t kVPlane = 2; - - // Surface formats roughly based on FOURCC labels, see: - // http://www.fourcc.org/rgb.php - // http://www.fourcc.org/yuv.php - enum Format { - INVALID, // Invalid format value. Used for error reporting. - RGB555, // 16bpp RGB packed 5:5:5 - RGB565, // 16bpp RGB packed 5:6:5 - RGB24, // 24bpp RGB packed 8:8:8 - RGB32, // 32bpp RGB packed with extra byte 8:8:8 - RGBA, // 32bpp RGBA packed 8:8:8:8 - YV12, // 12bpp YVU planar 1x1 Y, 2x2 VU samples - YV16, // 16bpp YVU planar 1x1 Y, 2x1 VU samples - EMPTY, // An empty frame. - }; - - // Surface format. - Format format; - - // Width and height of surface. - size_t width; - size_t height; - - // Number of planes, typically 1 for packed RGB formats and 3 for planar - // YUV formats. - size_t planes; - - // Array of strides for each plane, typically greater or equal to the width - // of the surface divided by the horizontal sampling period. Note that - // strides can be negative. - int32 strides[kMaxPlanes]; - - // Array of data pointers to each plane. - uint8* data[kMaxPlanes]; -}; - - -class VideoFrame : public StreamSample { - public: - // Locks the underlying surface and fills out the given VideoSurface and - // returns true if successful, false otherwise. Any additional calls to Lock - // will fail. - virtual bool Lock(VideoSurface* surface) = 0; - - // Unlocks the underlying surface, the VideoSurface acquired from Lock is no - // longer guaranteed to be valid. - virtual void Unlock() = 0; - - virtual bool IsEndOfStream() const = 0; -}; - } // namespace media #endif // MEDIA_BASE_BUFFERS_H_ diff --git a/media/base/video_frame.cc b/media/base/video_frame.cc new file mode 100644 index 0000000..0b8239c --- /dev/null +++ b/media/base/video_frame.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/video_frame.h" + +namespace media { + +// static +void VideoFrame::CreateFrame(VideoFrame::Format format, + size_t width, + size_t height, + base::TimeDelta timestamp, + base::TimeDelta duration, + scoped_refptr<VideoFrame>* frame_out) { + DCHECK(width > 0 && height > 0); + DCHECK(width * height < 100000000); + DCHECK(frame_out); + bool alloc_worked = false; + scoped_refptr<VideoFrame> frame = + new VideoFrame(format, width, height); + if (frame) { + frame->SetTimestamp(timestamp); + frame->SetDuration(duration); + switch (format) { + case VideoFrame::RGB555: + case VideoFrame::RGB565: + alloc_worked = frame->AllocateRGB(2u); + break; + case VideoFrame::RGB24: + alloc_worked = frame->AllocateRGB(3u); + break; + case VideoFrame::RGB32: + case VideoFrame::RGBA: + alloc_worked = frame->AllocateRGB(4u); + break; + case VideoFrame::YV12: + case VideoFrame::YV16: + alloc_worked = frame->AllocateYUV(); + break; + default: + NOTREACHED(); + alloc_worked = false; + break; + } + } + *frame_out = alloc_worked ? frame : NULL; +} + +// static +void VideoFrame::CreateEmptyFrame(scoped_refptr<VideoFrame>* frame_out) { + *frame_out = new VideoFrame(VideoFrame::EMPTY, 0, 0); +} + +// static +void VideoFrame::CreateBlackFrame(int width, int height, + scoped_refptr<VideoFrame>* frame_out) { + DCHECK_GT(width, 0); + DCHECK_GT(height, 0); + + // Create our frame. + scoped_refptr<VideoFrame> frame; + const base::TimeDelta kZero; + VideoFrame::CreateFrame(VideoFrame::YV12, width, height, kZero, kZero, + &frame); + DCHECK(frame); + + // Now set the data to YUV(0,128,128). + const uint8 kBlackY = 0x00; + const uint8 kBlackUV = 0x80; + + // Fill the Y plane. + uint8* y_plane = frame->data(VideoFrame::kYPlane); + for (size_t i = 0; i < frame->height_; ++i) { + memset(y_plane, kBlackY, frame->width_); + y_plane += frame->stride(VideoFrame::kYPlane); + } + + // Fill the U and V planes. + uint8* u_plane = frame->data(VideoFrame::kUPlane); + uint8* v_plane = frame->data(VideoFrame::kVPlane); + for (size_t i = 0; i < (frame->height_ / 2); ++i) { + memset(u_plane, kBlackUV, frame->width_ / 2); + memset(v_plane, kBlackUV, frame->width_ / 2); + u_plane += frame->stride(VideoFrame::kUPlane); + v_plane += frame->stride(VideoFrame::kVPlane); + } + + // Success! + *frame_out = frame; +} + +static inline size_t RoundUp(size_t value, size_t alignment) { + // Check that |alignment| is a power of 2. + DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); + return ((value + (alignment - 1)) & ~(alignment-1)); +} + +bool VideoFrame::AllocateRGB(size_t bytes_per_pixel) { + // Round up to align at a 64-bit (8 byte) boundary for each row. This + // is sufficient for MMX reads (movq). + size_t bytes_per_row = RoundUp(width_ * bytes_per_pixel, 8); + planes_ = VideoFrame::kNumRGBPlanes; + strides_[VideoFrame::kRGBPlane] = bytes_per_row; + data_[VideoFrame::kRGBPlane] = new uint8[bytes_per_row * height_]; + DCHECK(data_[VideoFrame::kRGBPlane]); + DCHECK(!(reinterpret_cast<intptr_t>(data_[VideoFrame::kRGBPlane]) & 7)); + COMPILE_ASSERT(0 == VideoFrame::kRGBPlane, RGB_data_must_be_index_0); + return (NULL != data_[VideoFrame::kRGBPlane]); +} + +bool VideoFrame::AllocateYUV() { + DCHECK(format_ == VideoFrame::YV12 || + format_ == VideoFrame::YV16); + // Align Y rows at 32-bit (4 byte) boundaries. The stride for both YV12 and + // YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for U and V + // applies to two rows of Y (one byte of UV for 4 bytes of Y), so in the + // case of YV12 the strides are identical for the same width surface, but the + // number of bytes allocated for YV12 is 1/2 the amount for U & V as YV16. + // We also round the height of the surface allocated to be an even number + // to avoid any potential of faulting by code that attempts to access the Y + // values of the final row, but assumes that the last row of U & V applies to + // a full two rows of Y. + size_t alloc_height = RoundUp(height_, 2); + size_t y_bytes_per_row = RoundUp(width_, 4); + size_t uv_stride = RoundUp(y_bytes_per_row / 2, 4); + size_t y_bytes = alloc_height * y_bytes_per_row; + size_t uv_bytes = alloc_height * uv_stride; + if (format_ == VideoFrame::YV12) { + uv_bytes /= 2; + } + uint8* data = new uint8[y_bytes + (uv_bytes * 2)]; + if (data) { + planes_ = VideoFrame::kNumYUVPlanes; + COMPILE_ASSERT(0 == VideoFrame::kYPlane, y_plane_data_must_be_index_0); + data_[VideoFrame::kYPlane] = data; + data_[VideoFrame::kUPlane] = data + y_bytes; + data_[VideoFrame::kVPlane] = data + y_bytes + uv_bytes; + strides_[VideoFrame::kYPlane] = y_bytes_per_row; + strides_[VideoFrame::kUPlane] = uv_stride; + strides_[VideoFrame::kVPlane] = uv_stride; + return true; + } + NOTREACHED(); + return false; +} + +VideoFrame::VideoFrame(VideoFrame::Format format, + size_t width, + size_t height) { + format_ = format; + width_ = width; + height_ = height; + planes_ = 0; + memset(&strides_, 0, sizeof(strides_)); + memset(&data_, 0, sizeof(data_)); +} + +VideoFrame::~VideoFrame() { + // In multi-plane allocations, only a single block of memory is allocated + // on the heap, and other |data| pointers point inside the same, single block + // so just delete index 0. + delete[] data_[0]; +} + +bool VideoFrame::IsEndOfStream() const { + return format_ == VideoFrame::EMPTY; +} + +} // namespace media diff --git a/media/base/video_frame.h b/media/base/video_frame.h new file mode 100644 index 0000000..b84d77c --- /dev/null +++ b/media/base/video_frame.h @@ -0,0 +1,111 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_VIDEO_FRAME_H_ +#define MEDIA_BASE_VIDEO_FRAME_H_ + +#include "media/base/buffers.h" + +namespace media { + +class VideoFrame : public StreamSample { + public: + static const size_t kMaxPlanes = 3; + + static const size_t kNumRGBPlanes = 1; + static const size_t kRGBPlane = 0; + + static const size_t kNumYUVPlanes = 3; + static const size_t kYPlane = 0; + static const size_t kUPlane = 1; + static const size_t kVPlane = 2; + + // Surface formats roughly based on FOURCC labels, see: + // http://www.fourcc.org/rgb.php + // http://www.fourcc.org/yuv.php + enum Format { + INVALID, // Invalid format value. Used for error reporting. + RGB555, // 16bpp RGB packed 5:5:5 + RGB565, // 16bpp RGB packed 5:6:5 + RGB24, // 24bpp RGB packed 8:8:8 + RGB32, // 32bpp RGB packed with extra byte 8:8:8 + RGBA, // 32bpp RGBA packed 8:8:8:8 + YV12, // 12bpp YVU planar 1x1 Y, 2x2 VU samples + YV16, // 16bpp YVU planar 1x1 Y, 2x1 VU samples + EMPTY, // An empty frame. + }; + + public: + // Creates a new frame with given parameters. Buffers for the frame are + // allocated but not initialized. + static void CreateFrame(Format format, + size_t width, + size_t height, + base::TimeDelta timestamp, + base::TimeDelta duration, + scoped_refptr<VideoFrame>* frame_out); + + // Creates a frame with format equals to VideoFrame::EMPTY, width, height + // timestamp and duration are all 0. + static void CreateEmptyFrame(scoped_refptr<VideoFrame>* frame_out); + + // Allocates YV12 frame based on |width| and |height|, and sets its data to + // the YUV equivalent of RGB(0,0,0). + static void CreateBlackFrame(int width, int height, + scoped_refptr<VideoFrame>* frame_out); + + Format format() const { return format_; } + + size_t width() const { return width_; } + + size_t height() const { return height_; } + + size_t planes() const { return planes_; } + + int32 stride(size_t plane) const { return strides_[plane]; } + + // Returns pointer to the buffer for a given plane. The memory is owned by + // VideoFrame object and must not be freed by the caller. + uint8* data(size_t plane) const { return data_[plane]; } + + // StreamSample interface. + virtual bool IsEndOfStream() const; + + private: + // Clients must use the static CreateFrame() method to create a new frame. + VideoFrame(Format format, + size_t video_width, + size_t video_height); + + virtual ~VideoFrame(); + + // Used internally by CreateFrame(). + bool AllocateRGB(size_t bytes_per_pixel); + bool AllocateYUV(); + + // Frame format. + Format format_; + + // Width and height of surface. + size_t width_; + size_t height_; + + // Number of planes, typically 1 for packed RGB formats and 3 for planar + // YUV formats. + size_t planes_; + + // Array of strides for each plane, typically greater or equal to the width + // of the surface divided by the horizontal sampling period. Note that + // strides can be negative. + int32 strides_[kMaxPlanes]; + + // Array of data pointers to each plane. + uint8* data_[kMaxPlanes]; + + DISALLOW_COPY_AND_ASSIGN(VideoFrame); +}; + +} // namespace media + +#endif // MEDIA_BASE_VIDEO_FRAME_H_ diff --git a/media/base/video_frame_impl.cc b/media/base/video_frame_impl.cc deleted file mode 100644 index 470e297..0000000 --- a/media/base/video_frame_impl.cc +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/base/video_frame_impl.h" - -namespace media { - -// static -void VideoFrameImpl::CreateFrame(VideoSurface::Format format, - size_t width, - size_t height, - base::TimeDelta timestamp, - base::TimeDelta duration, - scoped_refptr<VideoFrame>* frame_out) { - DCHECK(width > 0 && height > 0); - DCHECK(width * height < 100000000); - DCHECK(frame_out); - bool alloc_worked = false; - scoped_refptr<VideoFrameImpl> frame = - new VideoFrameImpl(format, width, height); - if (frame) { - frame->SetTimestamp(timestamp); - frame->SetDuration(duration); - switch (format) { - case VideoSurface::RGB555: - case VideoSurface::RGB565: - alloc_worked = frame->AllocateRGB(2u); - break; - case VideoSurface::RGB24: - alloc_worked = frame->AllocateRGB(3u); - break; - case VideoSurface::RGB32: - case VideoSurface::RGBA: - alloc_worked = frame->AllocateRGB(4u); - break; - case VideoSurface::YV12: - case VideoSurface::YV16: - alloc_worked = frame->AllocateYUV(); - break; - default: - NOTREACHED(); - alloc_worked = false; - break; - } - } - *frame_out = alloc_worked ? frame : NULL; -} - -// static -void VideoFrameImpl::CreateEmptyFrame(scoped_refptr<VideoFrame>* frame_out) { - *frame_out = new VideoFrameImpl(VideoSurface::EMPTY, 0, 0); -} - -// static -void VideoFrameImpl::CreateBlackFrame(int width, int height, - scoped_refptr<VideoFrame>* frame_out) { - DCHECK_GT(width, 0); - DCHECK_GT(height, 0); - - // Create our frame. - scoped_refptr<VideoFrame> frame; - const base::TimeDelta kZero; - VideoFrameImpl::CreateFrame(VideoSurface::YV12, width, height, kZero, kZero, - &frame); - DCHECK(frame); - - // Now set the data to YUV(0,128,128). - const uint8 kBlackY = 0x00; - const uint8 kBlackUV = 0x80; - VideoSurface surface; - frame->Lock(&surface); - DCHECK_EQ(VideoSurface::YV12, surface.format) << "Expected YV12 surface"; - - // Fill the Y plane. - for (size_t i = 0; i < surface.height; ++i) { - memset(surface.data[VideoSurface::kYPlane], kBlackY, surface.width); - surface.data[VideoSurface::kYPlane] - += surface.strides[VideoSurface::kYPlane]; - } - - // Fill the U and V planes. - for (size_t i = 0; i < (surface.height / 2); ++i) { - memset(surface.data[VideoSurface::kUPlane], kBlackUV, surface.width / 2); - memset(surface.data[VideoSurface::kVPlane], kBlackUV, surface.width / 2); - surface.data[VideoSurface::kUPlane] += - surface.strides[VideoSurface::kUPlane]; - surface.data[VideoSurface::kVPlane] += - surface.strides[VideoSurface::kVPlane]; - } - frame->Unlock(); - - // Success! - *frame_out = frame; -} - -static inline size_t RoundUp(size_t value, size_t alignment) { - // Check that |alignment| is a power of 2. - DCHECK((alignment + (alignment - 1)) == (alignment | (alignment - 1))); - return ((value + (alignment - 1)) & ~(alignment-1)); -} - -bool VideoFrameImpl::AllocateRGB(size_t bytes_per_pixel) { - // Round up to align at a 64-bit (8 byte) boundary for each row. This - // is sufficient for MMX reads (movq). - size_t bytes_per_row = RoundUp(surface_.width * bytes_per_pixel, 8); - surface_.planes = VideoSurface::kNumRGBPlanes; - surface_.strides[VideoSurface::kRGBPlane] = bytes_per_row; - surface_.data[VideoSurface::kRGBPlane] = new uint8[bytes_per_row * - surface_.height]; - DCHECK(surface_.data[VideoSurface::kRGBPlane]); - DCHECK(!(reinterpret_cast<intptr_t>( - surface_.data[VideoSurface::kRGBPlane]) & 7)); - COMPILE_ASSERT(0 == VideoSurface::kRGBPlane, RGB_data_must_be_index_0); - return (NULL != surface_.data[VideoSurface::kRGBPlane]); -} - -bool VideoFrameImpl::AllocateYUV() { - DCHECK(surface_.format == VideoSurface::YV12 || - surface_.format == VideoSurface::YV16); - // Align Y rows at 32-bit (4 byte) boundaries. The stride for both YV12 and - // YV16 is 1/2 of the stride of Y. For YV12, every row of bytes for U and V - // applies to two rows of Y (one byte of UV for 4 bytes of Y), so in the - // case of YV12 the strides are identical for the same width surface, but the - // number of bytes allocated for YV12 is 1/2 the amount for U & V as YV16. - // We also round the height of the surface allocated to be an even number - // to avoid any potential of faulting by code that attempts to access the Y - // values of the final row, but assumes that the last row of U & V applies to - // a full two rows of Y. - size_t alloc_height = RoundUp(surface_.height, 2); - size_t y_bytes_per_row = RoundUp(surface_.width, 4); - size_t uv_stride = RoundUp(y_bytes_per_row / 2, 4); - size_t y_bytes = alloc_height * y_bytes_per_row; - size_t uv_bytes = alloc_height * uv_stride; - if (surface_.format == VideoSurface::YV12) { - uv_bytes /= 2; - } - uint8* data = new uint8[y_bytes + (uv_bytes * 2)]; - if (data) { - surface_.planes = VideoSurface::kNumYUVPlanes; - COMPILE_ASSERT(0 == VideoSurface::kYPlane, y_plane_data_must_be_index_0); - surface_.data[VideoSurface::kYPlane] = data; - surface_.data[VideoSurface::kUPlane] = data + y_bytes; - surface_.data[VideoSurface::kVPlane] = data + y_bytes + uv_bytes; - surface_.strides[VideoSurface::kYPlane] = y_bytes_per_row; - surface_.strides[VideoSurface::kUPlane] = uv_stride; - surface_.strides[VideoSurface::kVPlane] = uv_stride; - return true; - } - NOTREACHED(); - return false; -} - -VideoFrameImpl::VideoFrameImpl(VideoSurface::Format format, - size_t width, - size_t height) { - locked_ = false; - memset(&surface_, 0, sizeof(surface_)); - surface_.format = format; - surface_.width = width; - surface_.height = height; -} - -VideoFrameImpl::~VideoFrameImpl() { - // In multi-plane allocations, only a single block of memory is allocated - // on the heap, and other |data| pointers point inside the same, single block - // so just delete index 0. - delete[] surface_.data[0]; -} - -bool VideoFrameImpl::Lock(VideoSurface* surface) { - DCHECK(!locked_); - DCHECK_NE(surface_.format, VideoSurface::EMPTY); - if (locked_) { - memset(surface, 0, sizeof(*surface)); - return false; - } - locked_ = true; - COMPILE_ASSERT(sizeof(*surface) == sizeof(surface_), surface_size_mismatch); - memcpy(surface, &surface_, sizeof(*surface)); - return true; -} - -void VideoFrameImpl::Unlock() { - DCHECK(locked_); - DCHECK_NE(surface_.format, VideoSurface::EMPTY); - locked_ = false; -} - -bool VideoFrameImpl::IsEndOfStream() const { - return surface_.format == VideoSurface::EMPTY; -} - -} // namespace media diff --git a/media/base/video_frame_impl.h b/media/base/video_frame_impl.h deleted file mode 100644 index cf660ea..0000000 --- a/media/base/video_frame_impl.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Simple class that implements the VideoFrame interface with memory allocated -// on the system heap. This class supports every format defined in the -// VideoSurface::Format enum. The implementation attempts to properly align -// allocations for maximum system bus efficency. -#ifndef MEDIA_BASE_VIDEO_FRAME_IMPL_H_ -#define MEDIA_BASE_VIDEO_FRAME_IMPL_H_ - -#include "media/base/buffers.h" - -namespace media { - -class VideoFrameImpl : public VideoFrame { - public: - static void CreateFrame(VideoSurface::Format format, - size_t width, - size_t height, - base::TimeDelta timestamp, - base::TimeDelta duration, - scoped_refptr<VideoFrame>* frame_out); - - // Creates a frame with format equals to VideoSurface::EMPTY, width, height - // timestamp and duration are all 0. - static void CreateEmptyFrame(scoped_refptr<VideoFrame>* frame_out); - - // Allocates YV12 frame based on |width| and |height|, and sets its data to - // the YUV equivalent of RGB(0,0,0). - static void CreateBlackFrame(int width, int height, - scoped_refptr<VideoFrame>* frame_out); - - // Implementation of VideoFrame. - virtual bool Lock(VideoSurface* surface); - virtual void Unlock(); - virtual bool IsEndOfStream() const; - - private: - // Clients must use the static CreateFrame() method to create a new frame. - VideoFrameImpl(VideoSurface::Format format, - size_t video_width, - size_t video_height); - - virtual ~VideoFrameImpl(); - - bool AllocateRGB(size_t bytes_per_pixel); - bool AllocateYUV(); - - bool locked_; - VideoSurface surface_; - - DISALLOW_COPY_AND_ASSIGN(VideoFrameImpl); -}; - -} // namespace media - -#endif // MEDIA_BASE_VIDEO_FRAME_IMPL_H_ diff --git a/media/base/video_frame_impl_unittest.cc b/media/base/video_frame_impl_unittest.cc deleted file mode 100644 index d50db36..0000000 --- a/media/base/video_frame_impl_unittest.cc +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/base/video_frame_impl.h" - -#include "base/format_macros.h" -#include "base/string_util.h" -#include "media/base/buffers.h" -#include "media/base/mock_filters.h" -#include "media/base/yuv_convert.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace media { - -// Helper function that initializes a YV12 frame with white and black scan -// lines based on the |white_to_black| parameter. If 0, then the entire -// frame will be black, if 1 then the entire frame will be white. -void InitializeYV12Frame(VideoFrame* frame, double white_to_black) { - VideoSurface surface; - if (!frame->Lock(&surface)) { - ADD_FAILURE(); - return; - } - EXPECT_EQ(VideoSurface::YV12, surface.format); - size_t first_black_row = static_cast<size_t>(surface.height * white_to_black); - uint8* y_plane = surface.data[VideoSurface::kYPlane]; - for (size_t row = 0; row < surface.height; ++row) { - int color = (row < first_black_row) ? 0xFF : 0x00; - memset(y_plane, color, surface.width); - y_plane += surface.strides[VideoSurface::kYPlane]; - } - uint8* u_plane = surface.data[VideoSurface::kUPlane]; - uint8* v_plane = surface.data[VideoSurface::kVPlane]; - for (size_t row = 0; row < surface.height; row += 2) { - memset(u_plane, 0x80, surface.width / 2); - memset(v_plane, 0x80, surface.width / 2); - u_plane += surface.strides[VideoSurface::kUPlane]; - v_plane += surface.strides[VideoSurface::kVPlane]; - } - frame->Unlock(); -} - -// Given a |yv12_frame| this method converts the YV12 frame to RGBA and -// makes sure that all the pixels of the RBG frame equal |expect_rgb_color|. -void ExpectFrameColor(media::VideoFrame* yv12_frame, uint32 expect_rgb_color) { - // On linux and mac builds if you directly compare using EXPECT_EQ and use - // the VideoSurface::kNumxxxPlanes constants, it generates an error when - // linking. These are declared so that we can compare against locals. - const size_t expect_yuv_planes = VideoSurface::kNumYUVPlanes; - const size_t expect_rgb_planes = VideoSurface::kNumRGBPlanes; - - VideoSurface yuv_surface; - ASSERT_TRUE(yv12_frame->Lock(&yuv_surface)); - ASSERT_EQ(VideoSurface::YV12, yuv_surface.format); - ASSERT_EQ(expect_yuv_planes, yuv_surface.planes); - ASSERT_EQ(yuv_surface.strides[VideoSurface::kUPlane], - yuv_surface.strides[VideoSurface::kVPlane]); - - scoped_refptr<media::VideoFrame> rgb_frame; - media::VideoFrameImpl::CreateFrame(VideoSurface::RGBA, - yuv_surface.width, - yuv_surface.height, - yv12_frame->GetTimestamp(), - yv12_frame->GetDuration(), - &rgb_frame); - media::VideoSurface rgb_surface; - ASSERT_TRUE(rgb_frame->Lock(&rgb_surface)); - ASSERT_EQ(yuv_surface.width, rgb_surface.width); - ASSERT_EQ(yuv_surface.height, rgb_surface.height); - ASSERT_EQ(expect_rgb_planes, rgb_surface.planes); - - media::ConvertYUVToRGB32(yuv_surface.data[VideoSurface::kYPlane], - yuv_surface.data[VideoSurface::kUPlane], - yuv_surface.data[VideoSurface::kVPlane], - rgb_surface.data[VideoSurface::kRGBPlane], - rgb_surface.width, - rgb_surface.height, - yuv_surface.strides[VideoSurface::kYPlane], - yuv_surface.strides[VideoSurface::kUPlane], - rgb_surface.strides[VideoSurface::kRGBPlane], - media::YV12); - - for (size_t row = 0; row < rgb_surface.height; ++row) { - uint32* rgb_row_data = reinterpret_cast<uint32*>( - rgb_surface.data[VideoSurface::kRGBPlane] + - (rgb_surface.strides[VideoSurface::kRGBPlane] * row)); - for (size_t col = 0; col < rgb_surface.width; ++col) { - SCOPED_TRACE(StringPrintf("Checking (%" PRIuS ", %" PRIuS ")", row, col)); - EXPECT_EQ(expect_rgb_color, rgb_row_data[col]); - } - } - rgb_frame->Unlock(); - yv12_frame->Unlock(); -} - -TEST(VideoFrameImpl, CreateFrame) { - const size_t kWidth = 64; - const size_t kHeight = 48; - const base::TimeDelta kTimestampA = base::TimeDelta::FromMicroseconds(1337); - const base::TimeDelta kDurationA = base::TimeDelta::FromMicroseconds(1667); - const base::TimeDelta kTimestampB = base::TimeDelta::FromMicroseconds(1234); - const base::TimeDelta kDurationB = base::TimeDelta::FromMicroseconds(5678); - - // Create a YV12 Video Frame. - scoped_refptr<media::VideoFrame> frame; - VideoFrameImpl::CreateFrame(media::VideoSurface::YV12, kWidth, kHeight, - kTimestampA, kDurationA, &frame); - ASSERT_TRUE(frame); - - // Test StreamSample implementation. - EXPECT_EQ(kTimestampA.InMicroseconds(), - frame->GetTimestamp().InMicroseconds()); - EXPECT_EQ(kDurationA.InMicroseconds(), frame->GetDuration().InMicroseconds()); - EXPECT_FALSE(frame->IsEndOfStream()); - EXPECT_FALSE(frame->IsDiscontinuous()); - frame->SetTimestamp(kTimestampB); - frame->SetDuration(kDurationB); - EXPECT_EQ(kTimestampB.InMicroseconds(), - frame->GetTimestamp().InMicroseconds()); - EXPECT_EQ(kDurationB.InMicroseconds(), frame->GetDuration().InMicroseconds()); - EXPECT_FALSE(frame->IsEndOfStream()); - frame->SetDiscontinuous(true); - EXPECT_TRUE(frame->IsDiscontinuous()); - frame->SetDiscontinuous(false); - EXPECT_FALSE(frame->IsDiscontinuous()); - - // Test VideoFrame implementation. - { - SCOPED_TRACE(""); - InitializeYV12Frame(frame, 0.0f); - ExpectFrameColor(frame, 0xFF000000); - } - { - SCOPED_TRACE(""); - InitializeYV12Frame(frame, 1.0f); - ExpectFrameColor(frame, 0xFFFFFFFF); - } - - // Test an empty frame. - VideoFrameImpl::CreateEmptyFrame(&frame); - EXPECT_TRUE(frame->IsEndOfStream()); -} - -TEST(VideoFrameImpl, CreateBlackFrame) { - const size_t kWidth = 2; - const size_t kHeight = 2; - const uint8 kExpectedYRow[] = { 0, 0 }; - const uint8 kExpectedUVRow[] = { 128 }; - - scoped_refptr<media::VideoFrame> frame; - VideoFrameImpl::CreateBlackFrame(kWidth, kHeight, &frame); - ASSERT_TRUE(frame); - - // Test basic properties. - EXPECT_EQ(0, frame->GetTimestamp().InMicroseconds()); - EXPECT_EQ(0, frame->GetDuration().InMicroseconds()); - EXPECT_FALSE(frame->IsEndOfStream()); - - // Test surface properties. - VideoSurface surface; - EXPECT_TRUE(frame->Lock(&surface)); - EXPECT_EQ(VideoSurface::YV12, surface.format); - EXPECT_EQ(kWidth, surface.width); - EXPECT_EQ(kHeight, surface.height); - EXPECT_EQ(3u, surface.planes); - - // Test surfaces themselves. - for (size_t y = 0; y < surface.height; ++y) { - EXPECT_EQ(0, memcmp(kExpectedYRow, surface.data[VideoSurface::kYPlane], - arraysize(kExpectedYRow))); - surface.data[VideoSurface::kYPlane] += - surface.strides[VideoSurface::kYPlane]; - } - for (size_t y = 0; y < surface.height / 2; ++y) { - EXPECT_EQ(0, memcmp(kExpectedUVRow, surface.data[VideoSurface::kUPlane], - arraysize(kExpectedUVRow))); - EXPECT_EQ(0, memcmp(kExpectedUVRow, surface.data[VideoSurface::kVPlane], - arraysize(kExpectedUVRow))); - surface.data[VideoSurface::kUPlane] += - surface.strides[VideoSurface::kUPlane]; - surface.data[VideoSurface::kVPlane] += - surface.strides[VideoSurface::kVPlane]; - } -} - -} // namespace media diff --git a/media/base/video_frame_unittest.cc b/media/base/video_frame_unittest.cc new file mode 100644 index 0000000..27e15dc --- /dev/null +++ b/media/base/video_frame_unittest.cc @@ -0,0 +1,176 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/video_frame.h" + +#include "base/format_macros.h" +#include "base/string_util.h" +#include "media/base/buffers.h" +#include "media/base/mock_filters.h" +#include "media/base/yuv_convert.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +// Helper function that initializes a YV12 frame with white and black scan +// lines based on the |white_to_black| parameter. If 0, then the entire +// frame will be black, if 1 then the entire frame will be white. +void InitializeYV12Frame(VideoFrame* frame, double white_to_black) { + EXPECT_EQ(VideoFrame::YV12, frame->format()); + size_t first_black_row = static_cast<size_t>(frame->height() * + white_to_black); + uint8* y_plane = frame->data(VideoFrame::kYPlane); + for (size_t row = 0; row < frame->height(); ++row) { + int color = (row < first_black_row) ? 0xFF : 0x00; + memset(y_plane, color, frame->width()); + y_plane += frame->stride(VideoFrame::kYPlane); + } + uint8* u_plane = frame->data(VideoFrame::kUPlane); + uint8* v_plane = frame->data(VideoFrame::kVPlane); + for (size_t row = 0; row < frame->height(); row += 2) { + memset(u_plane, 0x80, frame->width() / 2); + memset(v_plane, 0x80, frame->width() / 2); + u_plane += frame->stride(VideoFrame::kUPlane); + v_plane += frame->stride(VideoFrame::kVPlane); + } +} + +// Given a |yv12_frame| this method converts the YV12 frame to RGBA and +// makes sure that all the pixels of the RBG frame equal |expect_rgb_color|. +void ExpectFrameColor(media::VideoFrame* yv12_frame, uint32 expect_rgb_color) { + // On linux and mac builds if you directly compare using EXPECT_EQ and use + // the VideoFrame::kNumxxxPlanes constants, it generates an error when + // linking. These are declared so that we can compare against locals. + const size_t expect_yuv_planes = VideoFrame::kNumYUVPlanes; + const size_t expect_rgb_planes = VideoFrame::kNumRGBPlanes; + + ASSERT_EQ(VideoFrame::YV12, yv12_frame->format()); + ASSERT_EQ(expect_yuv_planes, yv12_frame->planes()); + ASSERT_EQ(yv12_frame->stride(VideoFrame::kUPlane), + yv12_frame->stride(VideoFrame::kVPlane)); + + scoped_refptr<media::VideoFrame> rgb_frame; + media::VideoFrame::CreateFrame(VideoFrame::RGBA, + yv12_frame->width(), + yv12_frame->height(), + yv12_frame->GetTimestamp(), + yv12_frame->GetDuration(), + &rgb_frame); + + ASSERT_EQ(yv12_frame->width(), rgb_frame->width()); + ASSERT_EQ(yv12_frame->height(), rgb_frame->height()); + ASSERT_EQ(expect_rgb_planes, rgb_frame->planes()); + + media::ConvertYUVToRGB32(yv12_frame->data(VideoFrame::kYPlane), + yv12_frame->data(VideoFrame::kUPlane), + yv12_frame->data(VideoFrame::kVPlane), + rgb_frame->data(VideoFrame::kRGBPlane), + rgb_frame->width(), + rgb_frame->height(), + yv12_frame->stride(VideoFrame::kYPlane), + yv12_frame->stride(VideoFrame::kUPlane), + rgb_frame->stride(VideoFrame::kRGBPlane), + media::YV12); + + for (size_t row = 0; row < rgb_frame->height(); ++row) { + uint32* rgb_row_data = reinterpret_cast<uint32*>( + rgb_frame->data(VideoFrame::kRGBPlane) + + (rgb_frame->stride(VideoFrame::kRGBPlane) * row)); + for (size_t col = 0; col < rgb_frame->width(); ++col) { + SCOPED_TRACE(StringPrintf("Checking (%" PRIuS ", %" PRIuS ")", + row, col)); + EXPECT_EQ(expect_rgb_color, rgb_row_data[col]); + } + } +} + +TEST(VideoFrame, CreateFrame) { + const size_t kWidth = 64; + const size_t kHeight = 48; + const base::TimeDelta kTimestampA = base::TimeDelta::FromMicroseconds(1337); + const base::TimeDelta kDurationA = base::TimeDelta::FromMicroseconds(1667); + const base::TimeDelta kTimestampB = base::TimeDelta::FromMicroseconds(1234); + const base::TimeDelta kDurationB = base::TimeDelta::FromMicroseconds(5678); + + // Create a YV12 Video Frame. + scoped_refptr<media::VideoFrame> frame; + VideoFrame::CreateFrame(media::VideoFrame::YV12, kWidth, kHeight, + kTimestampA, kDurationA, &frame); + ASSERT_TRUE(frame); + + // Test StreamSample implementation. + EXPECT_EQ(kTimestampA.InMicroseconds(), + frame->GetTimestamp().InMicroseconds()); + EXPECT_EQ(kDurationA.InMicroseconds(), + frame->GetDuration().InMicroseconds()); + EXPECT_FALSE(frame->IsEndOfStream()); + EXPECT_FALSE(frame->IsDiscontinuous()); + frame->SetTimestamp(kTimestampB); + frame->SetDuration(kDurationB); + EXPECT_EQ(kTimestampB.InMicroseconds(), + frame->GetTimestamp().InMicroseconds()); + EXPECT_EQ(kDurationB.InMicroseconds(), + frame->GetDuration().InMicroseconds()); + EXPECT_FALSE(frame->IsEndOfStream()); + frame->SetDiscontinuous(true); + EXPECT_TRUE(frame->IsDiscontinuous()); + frame->SetDiscontinuous(false); + EXPECT_FALSE(frame->IsDiscontinuous()); + + // Test VideoFrame implementation. + { + SCOPED_TRACE(""); + InitializeYV12Frame(frame, 0.0f); + ExpectFrameColor(frame, 0xFF000000); + } + { + SCOPED_TRACE(""); + InitializeYV12Frame(frame, 1.0f); + ExpectFrameColor(frame, 0xFFFFFFFF); + } + + // Test an empty frame. + VideoFrame::CreateEmptyFrame(&frame); + EXPECT_TRUE(frame->IsEndOfStream()); +} + +TEST(VideoFrame, CreateBlackFrame) { + const size_t kWidth = 2; + const size_t kHeight = 2; + const uint8 kExpectedYRow[] = { 0, 0 }; + const uint8 kExpectedUVRow[] = { 128 }; + + scoped_refptr<media::VideoFrame> frame; + VideoFrame::CreateBlackFrame(kWidth, kHeight, &frame); + ASSERT_TRUE(frame); + + // Test basic properties. + EXPECT_EQ(0, frame->GetTimestamp().InMicroseconds()); + EXPECT_EQ(0, frame->GetDuration().InMicroseconds()); + EXPECT_FALSE(frame->IsEndOfStream()); + + // Test |frame| properties. + EXPECT_EQ(VideoFrame::YV12, frame->format()); + EXPECT_EQ(kWidth, frame->width()); + EXPECT_EQ(kHeight, frame->height()); + EXPECT_EQ(3u, frame->planes()); + + // Test frames themselves. + uint8* y_plane = frame->data(VideoFrame::kYPlane); + for (size_t y = 0; y < frame->height(); ++y) { + EXPECT_EQ(0, memcmp(kExpectedYRow, y_plane, arraysize(kExpectedYRow))); + y_plane += frame->stride(VideoFrame::kYPlane); + } + + uint8* u_plane = frame->data(VideoFrame::kUPlane); + uint8* v_plane = frame->data(VideoFrame::kVPlane); + for (size_t y = 0; y < frame->height() / 2; ++y) { + EXPECT_EQ(0, memcmp(kExpectedUVRow, u_plane, arraysize(kExpectedUVRow))); + EXPECT_EQ(0, memcmp(kExpectedUVRow, v_plane, arraysize(kExpectedUVRow))); + u_plane += frame->stride(VideoFrame::kUPlane); + v_plane += frame->stride(VideoFrame::kVPlane); + } +} + +} // namespace media |