diff options
author | kcwu <kcwu@chromium.org> | 2015-08-07 06:07:02 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-07 13:07:41 +0000 |
commit | 1c051bb37f94f150a7b268cbb9d66b6e49647b60 (patch) | |
tree | a4e05da44b0f55004bb820b8d1ab5f3ebcebcdfa | |
parent | d72d37dfbf6a2c2a7417ce6a6ed5d6231de096a7 (diff) | |
download | chromium_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.cc | 58 | ||||
-rw-r--r-- | media/BUILD.gn | 3 | ||||
-rw-r--r-- | media/filters/ivf_parser.cc | 89 | ||||
-rw-r--r-- | media/filters/ivf_parser.h | 86 | ||||
-rw-r--r-- | media/filters/ivf_parser_unittest.cc | 56 | ||||
-rw-r--r-- | media/filters/vp8_parser_unittest.cc | 42 | ||||
-rw-r--r-- | media/media.gyp | 3 |
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', |