summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkcwu <kcwu@chromium.org>2015-08-07 06:07:02 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-07 13:07:41 +0000
commit1c051bb37f94f150a7b268cbb9d66b6e49647b60 (patch)
treea4e05da44b0f55004bb820b8d1ab5f3ebcebcdfa
parentd72d37dfbf6a2c2a7417ce6a6ed5d6231de096a7 (diff)
downloadchromium_src-1c051bb37f94f150a7b268cbb9d66b6e49647b60.zip
chromium_src-1c051bb37f94f150a7b268cbb9d66b6e49647b60.tar.gz
chromium_src-1c051bb37f94f150a7b268cbb9d66b6e49647b60.tar.bz2
Add an IVF parser
The IVF parser would be shared among VP8 and VP9 unittests. BUG=509500 Review URL: https://codereview.chromium.org/1269473002 Cr-Commit-Position: refs/heads/master@{#342341}
-rw-r--r--content/common/gpu/media/video_encode_accelerator_unittest.cc58
-rw-r--r--media/BUILD.gn3
-rw-r--r--media/filters/ivf_parser.cc89
-rw-r--r--media/filters/ivf_parser.h86
-rw-r--r--media/filters/ivf_parser_unittest.cc56
-rw-r--r--media/filters/vp8_parser_unittest.cc42
-rw-r--r--media/media.gyp3
7 files changed, 270 insertions, 67 deletions
diff --git a/content/common/gpu/media/video_encode_accelerator_unittest.cc b/content/common/gpu/media/video_encode_accelerator_unittest.cc
index 0e1e863..caf41c8 100644
--- a/content/common/gpu/media/video_encode_accelerator_unittest.cc
+++ b/content/common/gpu/media/video_encode_accelerator_unittest.cc
@@ -17,7 +17,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
-#include "base/sys_byteorder.h"
#include "base/threading/thread.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
@@ -27,6 +26,7 @@
#include "media/base/bitstream_buffer.h"
#include "media/base/test_data_util.h"
#include "media/filters/h264_parser.h"
+#include "media/filters/ivf_parser.h"
#include "media/video/fake_video_encode_accelerator.h"
#include "media/video/video_encode_accelerator.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -114,26 +114,6 @@ bool g_fake_encoder = false;
class VideoEncodeAcceleratorTestEnvironment;
VideoEncodeAcceleratorTestEnvironment* g_env;
-struct IvfFileHeader {
- char signature[4]; // signature: 'DKIF'
- uint16_t version; // version (should be 0)
- uint16_t header_size; // size of header in bytes
- uint32_t fourcc; // codec FourCC (e.g., 'VP80')
- uint16_t width; // width in pixels
- uint16_t height; // height in pixels
- uint32_t framerate; // frame rate per seconds
- uint32_t timescale; // time scale. For example, if framerate is 30 and
- // timescale is 2, the unit of IvfFrameHeader.timestamp
- // is 2/30 seconds.
- uint32_t num_frames; // number of frames in file
- uint32_t unused; // unused
-} __attribute__((packed));
-
-struct IvfFrameHeader {
- uint32_t frame_size; // Size of frame in bytes (not including the header)
- uint64_t timestamp; // 64-bit presentation timestamp
-} __attribute__((packed));
-
// The number of frames to be encoded. This variable is set by the switch
// "--num_frames_to_encode". Ignored if 0.
int g_num_frames_to_encode = 0;
@@ -1285,23 +1265,21 @@ void VEAClient::VerifyStreamProperties() {
}
void VEAClient::WriteIvfFileHeader() {
- IvfFileHeader header;
+ media::IvfFileHeader header = {};
- memset(&header, 0, sizeof(header));
- header.signature[0] = 'D';
- header.signature[1] = 'K';
- header.signature[2] = 'I';
- header.signature[3] = 'F';
+ memcpy(header.signature, media::kIvfHeaderSignature,
+ sizeof(header.signature));
header.version = 0;
- header.header_size = base::ByteSwapToLE16(sizeof(header));
- header.fourcc = base::ByteSwapToLE32(0x30385056); // VP80
- header.width = base::ByteSwapToLE16(
- base::checked_cast<uint16_t>(test_stream_->visible_size.width()));
- header.height = base::ByteSwapToLE16(
- base::checked_cast<uint16_t>(test_stream_->visible_size.height()));
- header.framerate = base::ByteSwapToLE32(requested_framerate_);
- header.timescale = base::ByteSwapToLE32(1);
- header.num_frames = base::ByteSwapToLE32(num_frames_to_encode_);
+ header.header_size = sizeof(header);
+ header.fourcc = 0x30385056; // VP80
+ header.width =
+ base::checked_cast<uint16_t>(test_stream_->visible_size.width());
+ header.height =
+ base::checked_cast<uint16_t>(test_stream_->visible_size.height());
+ header.timebase_denum = requested_framerate_;
+ header.timebase_num = 1;
+ header.num_frames = num_frames_to_encode_;
+ header.ByteSwap();
EXPECT_TRUE(base::AppendToFile(
base::FilePath::FromUTF8Unsafe(test_stream_->out_filename),
@@ -1309,11 +1287,11 @@ void VEAClient::WriteIvfFileHeader() {
}
void VEAClient::WriteIvfFrameHeader(int frame_index, size_t frame_size) {
- IvfFrameHeader header;
+ media::IvfFrameHeader header = {};
- memset(&header, 0, sizeof(header));
- header.frame_size = base::ByteSwapToLE32(frame_size);
- header.timestamp = base::ByteSwapToLE64(frame_index);
+ header.frame_size = frame_size;
+ header.timestamp = frame_index;
+ header.ByteSwap();
EXPECT_TRUE(base::AppendToFile(
base::FilePath::FromUTF8Unsafe(test_stream_->out_filename),
reinterpret_cast<char*>(&header), sizeof(header)));
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 744dab9..6861395 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -200,6 +200,8 @@ component("media") {
"filters/h264_bit_reader.h",
"filters/h264_parser.cc",
"filters/h264_parser.h",
+ "filters/ivf_parser.cc",
+ "filters/ivf_parser.h",
"filters/jpeg_parser.cc",
"filters/jpeg_parser.h",
"filters/source_buffer_platform.cc",
@@ -559,6 +561,7 @@ test("media_unittests") {
"filters/frame_processor_unittest.cc",
"filters/h264_bit_reader_unittest.cc",
"filters/h264_parser_unittest.cc",
+ "filters/ivf_parser_unittest.cc",
"filters/jpeg_parser_unittest.cc",
"filters/source_buffer_stream_unittest.cc",
"filters/video_cadence_estimator_unittest.cc",
diff --git a/media/filters/ivf_parser.cc b/media/filters/ivf_parser.cc
new file mode 100644
index 0000000..b6160fb
--- /dev/null
+++ b/media/filters/ivf_parser.cc
@@ -0,0 +1,89 @@
+// Copyright 2015 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 "base/logging.h"
+#include "base/sys_byteorder.h"
+#include "media/filters/ivf_parser.h"
+
+namespace media {
+
+void IvfFileHeader::ByteSwap() {
+ version = base::ByteSwapToLE16(version);
+ header_size = base::ByteSwapToLE16(header_size);
+ fourcc = base::ByteSwapToLE32(fourcc);
+ width = base::ByteSwapToLE16(width);
+ height = base::ByteSwapToLE16(height);
+ timebase_denum = base::ByteSwapToLE32(timebase_denum);
+ timebase_num = base::ByteSwapToLE32(timebase_num);
+ num_frames = base::ByteSwapToLE32(num_frames);
+ unused = base::ByteSwapToLE32(unused);
+}
+
+void IvfFrameHeader::ByteSwap() {
+ frame_size = base::ByteSwapToLE32(frame_size);
+ timestamp = base::ByteSwapToLE64(timestamp);
+}
+
+IvfParser::IvfParser() : ptr_(nullptr), end_(nullptr) {}
+
+bool IvfParser::Initialize(const uint8_t* stream,
+ size_t size,
+ IvfFileHeader* file_header) {
+ DCHECK(stream);
+ DCHECK(file_header);
+ ptr_ = stream;
+ end_ = stream + size;
+
+ if (size < sizeof(IvfFileHeader)) {
+ DLOG(ERROR) << "EOF before file header";
+ return false;
+ }
+
+ memcpy(file_header, ptr_, sizeof(IvfFileHeader));
+ file_header->ByteSwap();
+
+ if (memcmp(file_header->signature, kIvfHeaderSignature,
+ sizeof(file_header->signature)) != 0) {
+ DLOG(ERROR) << "IVF signature mismatch";
+ return false;
+ }
+ DLOG_IF(WARNING, file_header->version != 0)
+ << "IVF version unknown: " << file_header->version
+ << ", the parser may not be able to parse correctly";
+ if (file_header->header_size != sizeof(IvfFileHeader)) {
+ DLOG(ERROR) << "IVF file header size mismatch";
+ return false;
+ }
+
+ ptr_ += sizeof(IvfFileHeader);
+
+ return true;
+}
+
+bool IvfParser::ParseNextFrame(IvfFrameHeader* frame_header,
+ const uint8_t** payload) {
+ DCHECK(ptr_);
+ DCHECK(payload);
+
+ if (end_ < ptr_ + sizeof(IvfFrameHeader)) {
+ DLOG_IF(ERROR, ptr_ != end_) << "Incomplete frame header";
+ return false;
+ }
+
+ memcpy(frame_header, ptr_, sizeof(IvfFrameHeader));
+ frame_header->ByteSwap();
+ ptr_ += sizeof(IvfFrameHeader);
+
+ if (end_ < ptr_ + frame_header->frame_size) {
+ DLOG(ERROR) << "Not enough frame data";
+ return false;
+ }
+
+ *payload = ptr_;
+ ptr_ += frame_header->frame_size;
+
+ return true;
+}
+
+} // namespace media
diff --git a/media/filters/ivf_parser.h b/media/filters/ivf_parser.h
new file mode 100644
index 0000000..fe234aa
--- /dev/null
+++ b/media/filters/ivf_parser.h
@@ -0,0 +1,86 @@
+// Copyright 2015 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_FILTERS_IVF_PARSER_H_
+#define MEDIA_FILTERS_IVF_PARSER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+const char kIvfHeaderSignature[] = "DKIF";
+
+#pragma pack(push, 1)
+struct MEDIA_EXPORT IvfFileHeader {
+ // Byte swap interger fields between native and (on disk) little endian.
+ void ByteSwap();
+
+ char signature[4]; // signature: 'DKIF'
+ uint16_t version; // version (should be 0)
+ uint16_t header_size; // size of header in bytes
+ uint32_t fourcc; // codec FourCC (e.g., 'VP80')
+ uint16_t width; // width in pixels
+ uint16_t height; // height in pixels
+ uint32_t timebase_denum; // timebase denumerator
+ uint32_t timebase_num; // timebase numerator. For example, if
+ // timebase_denum is 30 and timebase_num is 2, the
+ // unit of IvfFrameHeader.timestamp is 2/30
+ // seconds.
+ uint32_t num_frames; // number of frames in file
+ uint32_t unused; // unused
+};
+static_assert(
+ sizeof(IvfFileHeader) == 32,
+ "sizeof(IvfFileHeader) must be fixed since it will be used with file IO");
+
+struct MEDIA_EXPORT IvfFrameHeader {
+ // Byte swap interger fields between native and (on disk) little endian.
+ void ByteSwap();
+
+ uint32_t frame_size; // Size of frame in bytes (not including the header)
+ uint64_t timestamp; // 64-bit presentation timestamp in unit timebase,
+ // which is defined in IvfFileHeader.
+};
+static_assert(
+ sizeof(IvfFrameHeader) == 12,
+ "sizeof(IvfFrameHeader) must be fixed since it will be used with file IO");
+#pragma pack(pop)
+
+// IVF is a simple container format for video frame. It is used by libvpx to
+// transport VP8 and VP9 bitstream.
+class MEDIA_EXPORT IvfParser {
+ public:
+ IvfParser();
+
+ // Initializes the parser for IVF |stream| with size |size| and parses the
+ // file header. Returns true on success.
+ bool Initialize(const uint8_t* stream,
+ size_t size,
+ IvfFileHeader* file_header);
+
+ // Parses the next frame. Returns true if the next frame is parsed without
+ // error. |frame_header| will be filled with the frame header and |payload|
+ // will point to frame payload (inside the |stream| buffer given to
+ // Initialize.)
+ bool ParseNextFrame(IvfFrameHeader* frame_header, const uint8_t** payload);
+
+ private:
+ bool ParseFileHeader(IvfFileHeader* file_header);
+
+ // Current reading position of input stream.
+ const uint8_t* ptr_;
+
+ // The end position of input stream.
+ const uint8_t* end_;
+
+ DISALLOW_COPY_AND_ASSIGN(IvfParser);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_IVF_PARSER_H_
diff --git a/media/filters/ivf_parser_unittest.cc b/media/filters/ivf_parser_unittest.cc
new file mode 100644
index 0000000..2d89dc2
--- /dev/null
+++ b/media/filters/ivf_parser_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2015 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 "base/files/memory_mapped_file.h"
+#include "media/base/test_data_util.h"
+#include "media/filters/ivf_parser.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+TEST(IvfParserTest, StreamFileParsing) {
+ base::FilePath file_path = GetTestDataFilePath("test-25fps.vp8");
+
+ base::MemoryMappedFile stream;
+ ASSERT_TRUE(stream.Initialize(file_path)) << "Couldn't open stream file: "
+ << file_path.MaybeAsASCII();
+
+ IvfParser parser;
+ IvfFileHeader file_header = {};
+
+ EXPECT_TRUE(parser.Initialize(stream.data(), stream.length(), &file_header));
+
+ // Check file header fields.
+ EXPECT_EQ(0, memcmp(file_header.signature, kIvfHeaderSignature,
+ sizeof(file_header.signature)));
+ EXPECT_EQ(0, file_header.version);
+ EXPECT_EQ(sizeof(IvfFileHeader), file_header.header_size);
+ EXPECT_EQ(0x30385056u, file_header.fourcc); // VP80
+ EXPECT_EQ(320u, file_header.width);
+ EXPECT_EQ(240u, file_header.height);
+ EXPECT_EQ(50u, file_header.timebase_denum);
+ EXPECT_EQ(2u, file_header.timebase_num);
+ EXPECT_EQ(250u, file_header.num_frames);
+
+ IvfFrameHeader frame_header;
+ size_t num_parsed_frames = 0;
+ const uint8_t* payload = nullptr;
+ while (parser.ParseNextFrame(&frame_header, &payload)) {
+ ++num_parsed_frames;
+ EXPECT_TRUE(payload != nullptr);
+
+ // Only check the first frame.
+ if (num_parsed_frames == 1u) {
+ EXPECT_EQ(14788u, frame_header.frame_size);
+ EXPECT_EQ(0u, frame_header.timestamp);
+ EXPECT_EQ(
+ static_cast<ptrdiff_t>(sizeof(file_header) + sizeof(frame_header)),
+ payload - stream.data());
+ }
+ }
+
+ EXPECT_EQ(file_header.num_frames, num_parsed_frames);
+}
+
+} // namespace media
diff --git a/media/filters/vp8_parser_unittest.cc b/media/filters/vp8_parser_unittest.cc
index 39a2a80..3087ed0 100644
--- a/media/filters/vp8_parser_unittest.cc
+++ b/media/filters/vp8_parser_unittest.cc
@@ -2,12 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/command_line.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/sys_byteorder.h"
#include "media/base/test_data_util.h"
+#include "media/filters/ivf_parser.h"
#include "media/filters/vp8_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -15,45 +13,35 @@ namespace media {
TEST(Vp8ParserTest, StreamFileParsing) {
base::FilePath file_path = GetTestDataFilePath("test-25fps.vp8");
- // Number of frames in the test stream to be parsed.
- const int num_frames = 250;
-
base::MemoryMappedFile stream;
ASSERT_TRUE(stream.Initialize(file_path))
<< "Couldn't open stream file: " << file_path.MaybeAsASCII();
- Vp8Parser parser;
+ IvfParser ivf_parser;
+ IvfFileHeader ivf_file_header = {};
+ ASSERT_TRUE(
+ ivf_parser.Initialize(stream.data(), stream.length(), &ivf_file_header));
+ ASSERT_EQ(ivf_file_header.fourcc, 0x30385056u); // VP80
+
+ Vp8Parser vp8_parser;
+ IvfFrameHeader ivf_frame_header = {};
+ size_t num_parsed_frames = 0;
// Parse until the end of stream/unsupported stream/error in stream is found.
- int num_parsed_frames = 0;
- const uint8_t* stream_ptr = stream.data();
- size_t bytes_left = stream.length();
- // Skip IVF file header.
- const size_t kIvfStreamHeaderLen = 32;
- CHECK_GE(bytes_left, kIvfStreamHeaderLen);
- stream_ptr += kIvfStreamHeaderLen;
- bytes_left -= kIvfStreamHeaderLen;
-
- const size_t kIvfFrameHeaderLen = 12;
- while (bytes_left > kIvfFrameHeaderLen) {
+ const uint8_t* payload = nullptr;
+ while (ivf_parser.ParseNextFrame(&ivf_frame_header, &payload)) {
Vp8FrameHeader fhdr;
- uint32_t frame_size =
- base::ByteSwapToLE32(*reinterpret_cast<const uint32_t*>(stream_ptr));
- // Skip IVF frame header.
- stream_ptr += kIvfFrameHeaderLen;
- bytes_left -= kIvfFrameHeaderLen;
- ASSERT_TRUE(parser.ParseFrame(stream_ptr, frame_size, &fhdr));
+ ASSERT_TRUE(
+ vp8_parser.ParseFrame(payload, ivf_frame_header.frame_size, &fhdr));
- stream_ptr += frame_size;
- bytes_left -= frame_size;
++num_parsed_frames;
}
DVLOG(1) << "Number of successfully parsed frames before EOS: "
<< num_parsed_frames;
- EXPECT_EQ(num_frames, num_parsed_frames);
+ EXPECT_EQ(ivf_file_header.num_frames, num_parsed_frames);
}
} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index 667c39d..a6bfe01 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -539,6 +539,8 @@
'filters/h264_bit_reader.h',
'filters/h264_parser.cc',
'filters/h264_parser.h',
+ 'filters/ivf_parser.cc',
+ 'filters/ivf_parser.h',
'filters/in_memory_url_protocol.cc',
'filters/in_memory_url_protocol.h',
'filters/jpeg_parser.cc',
@@ -1254,6 +1256,7 @@
'filters/h264_bit_reader_unittest.cc',
'filters/h264_parser_unittest.cc',
'filters/in_memory_url_protocol_unittest.cc',
+ 'filters/ivf_parser_unittest.cc',
'filters/jpeg_parser_unittest.cc',
'filters/source_buffer_stream_unittest.cc',
'filters/video_cadence_estimator_unittest.cc',