diff options
author | kcwu <kcwu@chromium.org> | 2015-08-10 22:55:18 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-08-11 05:55:55 +0000 |
commit | 2c5ec1f7703801d794e8b50b1be5753daba187ff (patch) | |
tree | 7e9ad62376b8796dee75a259fe66df8be6a2b053 /media | |
parent | 5438a98ac376de45d277fa7ffbb65258577d0fe7 (diff) | |
download | chromium_src-2c5ec1f7703801d794e8b50b1be5753daba187ff.zip chromium_src-2c5ec1f7703801d794e8b50b1be5753daba187ff.tar.gz chromium_src-2c5ec1f7703801d794e8b50b1be5753daba187ff.tar.bz2 |
Add a Vp9Parser implementation
Add a class for parsing VP9 bitstreams as described in VP9 draft spec and libvpx.
Also add tests for it to media_unittests.
TEST=media_unittests for Vp9Parser
BUG=509500
Review URL: https://codereview.chromium.org/1258083004
Cr-Commit-Position: refs/heads/master@{#342792}
Diffstat (limited to 'media')
-rw-r--r-- | media/BUILD.gn | 3 | ||||
-rw-r--r-- | media/filters/vp9_parser.cc | 400 | ||||
-rw-r--r-- | media/filters/vp9_parser.h | 220 | ||||
-rw-r--r-- | media/filters/vp9_parser_unittest.cc | 147 | ||||
-rw-r--r-- | media/filters/vp9_raw_bits_reader.cc | 6 | ||||
-rw-r--r-- | media/filters/vp9_raw_bits_reader.h | 2 | ||||
-rw-r--r-- | media/filters/vp9_raw_bits_reader_unittest.cc | 20 | ||||
-rw-r--r-- | media/media.gyp | 3 |
8 files changed, 787 insertions, 14 deletions
diff --git a/media/BUILD.gn b/media/BUILD.gn index 310cf4c..abb4803 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -220,6 +220,8 @@ component("media") { "filters/vp8_bool_decoder.h", "filters/vp8_parser.cc", "filters/vp8_parser.h", + "filters/vp9_parser.cc", + "filters/vp9_parser.h", "filters/vp9_raw_bits_reader.cc", "filters/vp9_raw_bits_reader.h", "filters/webvtt_util.h", @@ -572,6 +574,7 @@ test("media_unittests") { "filters/video_renderer_algorithm_unittest.cc", "filters/vp8_bool_decoder_unittest.cc", "filters/vp8_parser_unittest.cc", + "filters/vp9_parser_unittest.cc", "filters/vp9_raw_bits_reader_unittest.cc", "formats/common/offset_byte_queue_unittest.cc", "formats/webm/cluster_builder.cc", diff --git a/media/filters/vp9_parser.cc b/media/filters/vp9_parser.cc new file mode 100644 index 0000000..78d3361 --- /dev/null +++ b/media/filters/vp9_parser.cc @@ -0,0 +1,400 @@ +// 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. +// +// This file contains an implementation of a VP9 bitstream parser. + +#include "media/filters/vp9_parser.h" + +#include "base/logging.h" + +namespace { + +// Helper function for Vp9Parser::ReadTiles. Defined as get_min_log2_tile_cols +// in spec. +int GetMinLog2TileCols(int sb64_cols) { + const int kMaxTileWidthB64 = 64; + int min_log2 = 0; + while ((kMaxTileWidthB64 << min_log2) < sb64_cols) + min_log2++; + return min_log2; +} + +// Helper function for Vp9Parser::ReadTiles. Defined as get_max_log2_tile_cols +// in spec. +int GetMaxLog2TileCols(int sb64_cols) { + const int kMinTileWidthB64 = 4; + int max_log2 = 1; + while ((sb64_cols >> max_log2) >= kMinTileWidthB64) + max_log2++; + return max_log2 - 1; +} + +} // namespace + +namespace media { + +Vp9Parser::Vp9Parser() : stream_(nullptr), size_(0) { + memset(&ref_slots_, 0, sizeof(ref_slots_)); +} + +uint8_t Vp9Parser::ReadProfile() { + uint8_t profile = 0; + + // LSB first. + if (reader_.ReadBool()) + profile |= 1; + if (reader_.ReadBool()) + profile |= 2; + if (profile > 2 && reader_.ReadBool()) + profile += 1; + return profile; +} + +bool Vp9Parser::VerifySyncCode() { + const int kSyncCode = 0x498342; + if (reader_.ReadLiteral(8 * 3) != kSyncCode) { + DVLOG(1) << "Invalid frame sync code"; + return false; + } + return true; +} + +bool Vp9Parser::ReadBitDepthColorSpaceSampling(Vp9FrameHeader* fhdr) { + if (fhdr->profile == 2 || fhdr->profile == 3) { + fhdr->bit_depth = reader_.ReadBool() ? 12 : 10; + } else { + fhdr->bit_depth = 8; + } + + fhdr->color_space = static_cast<Vp9ColorSpace>(reader_.ReadLiteral(3)); + if (fhdr->color_space != Vp9ColorSpace::SRGB) { + fhdr->yuv_range = reader_.ReadBool(); + if (fhdr->profile == 1 || fhdr->profile == 3) { + fhdr->subsampling_x = reader_.ReadBool() ? 1 : 0; + fhdr->subsampling_y = reader_.ReadBool() ? 1 : 0; + if (fhdr->subsampling_x == 1 && fhdr->subsampling_y == 1) { + DVLOG(1) << "4:2:0 color not supported in profile 1 or 3"; + return false; + } + bool reserved = reader_.ReadBool(); + if (reserved) { + DVLOG(1) << "reserved bit set"; + return false; + } + } else { + fhdr->subsampling_x = fhdr->subsampling_y = 1; + } + } else { + if (fhdr->profile == 1 || fhdr->profile == 3) { + fhdr->subsampling_x = fhdr->subsampling_y = 0; + + bool reserved = reader_.ReadBool(); + if (reserved) { + DVLOG(1) << "reserved bit set"; + return false; + } + } else { + DVLOG(1) << "4:4:4 color not supported in profile 0 or 2"; + return false; + } + } + + return true; +} + +void Vp9Parser::ReadFrameSize(Vp9FrameHeader* fhdr) { + fhdr->width = reader_.ReadLiteral(16) + 1; + fhdr->height = reader_.ReadLiteral(16) + 1; +} + +bool Vp9Parser::ReadFrameSizeFromRefs(Vp9FrameHeader* fhdr) { + for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { + if (reader_.ReadBool()) { + fhdr->width = ref_slots_[i].width; + fhdr->height = ref_slots_[i].height; + + const int kMaxDimension = 1 << 16; + if (fhdr->width == 0 || fhdr->width > kMaxDimension || + fhdr->height == 0 || fhdr->height > kMaxDimension) { + DVLOG(1) << "The size of reference frame is out of range: " + << ref_slots_[i].width << "," << ref_slots_[i].height; + return false; + } + return true; + } + } + + fhdr->width = reader_.ReadLiteral(16) + 1; + fhdr->height = reader_.ReadLiteral(16) + 1; + return true; +} + +void Vp9Parser::ReadDisplayFrameSize(Vp9FrameHeader* fhdr) { + if (reader_.ReadBool()) { + fhdr->display_width = reader_.ReadLiteral(16) + 1; + fhdr->display_height = reader_.ReadLiteral(16) + 1; + } else { + fhdr->display_width = fhdr->width; + fhdr->display_height = fhdr->height; + } +} + +Vp9InterpFilter Vp9Parser::ReadInterpFilter() { + if (reader_.ReadBool()) + return Vp9InterpFilter::INTERP_FILTER_SELECT; + + // The mapping table for next two bits. + const Vp9InterpFilter table[] = { + Vp9InterpFilter::EIGHTTAP_SMOOTH, Vp9InterpFilter::EIGHTTAP, + Vp9InterpFilter::EIGHTTAP_SHARP, Vp9InterpFilter::BILINEAR, + }; + return table[reader_.ReadLiteral(2)]; +} + +void Vp9Parser::ReadLoopFilter(Vp9LoopFilter* loop_filter) { + loop_filter->filter_level = reader_.ReadLiteral(6); + loop_filter->sharpness_level = reader_.ReadLiteral(3); + + loop_filter->mode_ref_delta_enabled = reader_.ReadBool(); + if (loop_filter->mode_ref_delta_enabled) { + loop_filter->mode_ref_delta_update = reader_.ReadBool(); + if (loop_filter->mode_ref_delta_update) { + for (size_t i = 0; i < Vp9LoopFilter::kNumRefDeltas; i++) { + loop_filter->update_ref_deltas[i] = reader_.ReadBool(); + if (loop_filter->update_ref_deltas[i]) + loop_filter->ref_deltas[i] = reader_.ReadSignedLiteral(6); + } + + for (size_t i = 0; i < Vp9LoopFilter::kNumModeDeltas; i++) { + loop_filter->update_mode_deltas[i] = reader_.ReadBool(); + if (loop_filter->update_mode_deltas[i]) + loop_filter->mode_deltas[i] = reader_.ReadLiteral(6); + } + } + } +} + +void Vp9Parser::ReadQuantization(Vp9QuantizationParams* quants) { + quants->base_qindex = reader_.ReadLiteral(8); + + if (reader_.ReadBool()) + quants->y_dc_delta = reader_.ReadSignedLiteral(4); + + if (reader_.ReadBool()) + quants->uv_ac_delta = reader_.ReadSignedLiteral(4); + + if (reader_.ReadBool()) + quants->uv_dc_delta = reader_.ReadSignedLiteral(4); +} + +void Vp9Parser::ReadSegmentationMap(Vp9Segmentation* segment) { + for (size_t i = 0; i < Vp9Segmentation::kNumTreeProbs; i++) { + segment->tree_probs[i] = + reader_.ReadBool() ? reader_.ReadLiteral(8) : kVp9MaxProb; + } + + for (size_t i = 0; i < Vp9Segmentation::kNumPredictionProbs; i++) + segment->pred_probs[i] = kVp9MaxProb; + + segment->temporal_update = reader_.ReadBool(); + if (segment->temporal_update) { + for (size_t i = 0; i < Vp9Segmentation::kNumPredictionProbs; i++) { + if (reader_.ReadBool()) + segment->pred_probs[i] = reader_.ReadLiteral(8); + } + } +} + +void Vp9Parser::ReadSegmentationData(Vp9Segmentation* segment) { + segment->abs_delta = reader_.ReadBool(); + + const int kFeatureDataBits[] = {7, 6, 2, 0}; + const bool kFeatureDataSigned[] = {true, true, false, false}; + + for (size_t i = 0; i < Vp9Segmentation::kNumSegments; i++) { + for (size_t j = 0; j < Vp9Segmentation::kNumFeatures; j++) { + int8_t data = 0; + segment->feature_enabled[i][j] = reader_.ReadBool(); + if (segment->feature_enabled[i][j]) { + data = reader_.ReadLiteral(kFeatureDataBits[j]); + if (kFeatureDataSigned[j]) + if (reader_.ReadBool()) + data = -data; + } + segment->feature_data[i][j] = data; + } + } +} + +void Vp9Parser::ReadSegmentation(Vp9Segmentation* segment) { + segment->enabled = reader_.ReadBool(); + + if (!segment->enabled) { + return; + } + + segment->update_map = reader_.ReadBool(); + if (segment->update_map) + ReadSegmentationMap(segment); + + segment->update_data = reader_.ReadBool(); + if (segment->update_data) + ReadSegmentationData(segment); +} + +void Vp9Parser::ReadTiles(Vp9FrameHeader* fhdr) { + int sb64_cols = (fhdr->width + 63) / 64; + + int min_log2_tile_cols = GetMinLog2TileCols(sb64_cols); + int max_log2_tile_cols = GetMaxLog2TileCols(sb64_cols); + + int max_ones = max_log2_tile_cols - min_log2_tile_cols; + fhdr->log2_tile_cols = min_log2_tile_cols; + while (max_ones-- && reader_.ReadBool()) + fhdr->log2_tile_cols++; + + if (reader_.ReadBool()) + fhdr->log2_tile_rows = reader_.ReadLiteral(2) - 1; +} + +bool Vp9Parser::ParseUncompressedHeader(Vp9FrameHeader* fhdr) { + reader_.Initialize(stream_, size_); + + // frame marker + if (reader_.ReadLiteral(2) != 0x2) + return false; + + fhdr->profile = ReadProfile(); + if (fhdr->profile >= kVp9MaxProfile) { + DVLOG(1) << "Unsupported bitstream profile"; + return false; + } + + fhdr->show_existing_frame = reader_.ReadBool(); + if (fhdr->show_existing_frame) { + fhdr->frame_to_show = reader_.ReadLiteral(3); + fhdr->show_frame = true; + + if (!reader_.IsValid()) { + DVLOG(1) << "parser reads beyond the end of buffer"; + return false; + } + fhdr->uncompressed_header_size = reader_.GetBytesRead(); + return true; + } + + fhdr->frame_type = static_cast<Vp9FrameHeader::FrameType>(reader_.ReadBool()); + fhdr->show_frame = reader_.ReadBool(); + fhdr->error_resilient_mode = reader_.ReadBool(); + + if (fhdr->IsKeyframe()) { + if (!VerifySyncCode()) + return false; + + if (!ReadBitDepthColorSpaceSampling(fhdr)) + return false; + + for (size_t i = 0; i < kVp9NumRefFrames; i++) + fhdr->refresh_flag[i] = true; + + ReadFrameSize(fhdr); + ReadDisplayFrameSize(fhdr); + } else { + if (!fhdr->show_frame) + fhdr->intra_only = reader_.ReadBool(); + + if (!fhdr->error_resilient_mode) + fhdr->reset_context = reader_.ReadLiteral(2); + + if (fhdr->intra_only) { + if (!VerifySyncCode()) + return false; + + if (fhdr->profile > 0) { + if (!ReadBitDepthColorSpaceSampling(fhdr)) + return false; + } else { + fhdr->bit_depth = 8; + fhdr->color_space = Vp9ColorSpace::BT_601; + fhdr->subsampling_x = fhdr->subsampling_y = 1; + } + + for (size_t i = 0; i < kVp9NumRefFrames; i++) + fhdr->refresh_flag[i] = reader_.ReadBool(); + ReadFrameSize(fhdr); + ReadDisplayFrameSize(fhdr); + } else { + for (size_t i = 0; i < kVp9NumRefFrames; i++) + fhdr->refresh_flag[i] = reader_.ReadBool(); + + for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { + fhdr->frame_refs[i] = reader_.ReadLiteral(kVp9NumRefFramesLog2); + fhdr->ref_sign_biases[i] = reader_.ReadBool(); + } + + if (!ReadFrameSizeFromRefs(fhdr)) + return false; + ReadDisplayFrameSize(fhdr); + + fhdr->allow_high_precision_mv = reader_.ReadBool(); + fhdr->interp_filter = ReadInterpFilter(); + } + } + + if (fhdr->error_resilient_mode) { + fhdr->frame_parallel_decoding_mode = true; + } else { + fhdr->refresh_frame_context = reader_.ReadBool(); + fhdr->frame_parallel_decoding_mode = reader_.ReadBool(); + } + + fhdr->frame_context_idx = reader_.ReadLiteral(2); + + ReadLoopFilter(&fhdr->loop_filter); + ReadQuantization(&fhdr->quant_params); + ReadSegmentation(&fhdr->segment); + + ReadTiles(fhdr); + + fhdr->first_partition_size = reader_.ReadLiteral(16); + if (fhdr->first_partition_size == 0) { + DVLOG(1) << "invalid header size"; + return false; + } + + if (!reader_.IsValid()) { + DVLOG(1) << "parser reads beyond the end of buffer"; + return false; + } + fhdr->uncompressed_header_size = reader_.GetBytesRead(); + + return true; +} + +void Vp9Parser::UpdateSlots(const Vp9FrameHeader* fhdr) { + for (size_t i = 0; i < kVp9NumRefFrames; i++) { + if (fhdr->refresh_flag[i]) { + ref_slots_[i].width = fhdr->width; + ref_slots_[i].height = fhdr->height; + } + } +} + +bool Vp9Parser::ParseFrame(const uint8_t* stream, + size_t frame_size, + Vp9FrameHeader* fhdr) { + DCHECK(stream); + stream_ = stream; + size_ = frame_size; + memset(fhdr, 0, sizeof(*fhdr)); + + if (!ParseUncompressedHeader(fhdr)) + return false; + + UpdateSlots(fhdr); + + return true; +} + +} // namespace media diff --git a/media/filters/vp9_parser.h b/media/filters/vp9_parser.h new file mode 100644 index 0000000..8770431 --- /dev/null +++ b/media/filters/vp9_parser.h @@ -0,0 +1,220 @@ +// 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. +// +// This file contains an implementation of a VP9 bitstream parser. The main +// purpose of this parser is to support hardware decode acceleration. Some +// accelerators, e.g. libva which implements VA-API, require the caller +// (chrome) to feed them parsed VP9 frame header. +// +// Example usage: +// { +// Vp9Parser parser; +// uint8_t* frame_stream; +// size_t frame_size; +// +// // Get frames from, say, WebM parser or IVF parser. +// while (GetVp9Frame(&frame_stream, &frame_size)) { +// Vp9FrameHeader header; +// if (!parser.ParseFrame(frame_stream, frame_size, &header)) { +// // Parse failed. +// return false; +// } +// // Got a frame parsed successfully. +// } +// } + +#ifndef MEDIA_FILTERS_VP9_PARSER_H_ +#define MEDIA_FILTERS_VP9_PARSER_H_ + +#include <stddef.h> +#include <stdint.h> + +#include "base/macros.h" +#include "media/base/media_export.h" +#include "media/filters/vp9_raw_bits_reader.h" + +namespace media { + +const int kVp9MaxProfile = 4; +const int kVp9NumRefFramesLog2 = 3; +const int kVp9NumRefFrames = 1 << kVp9NumRefFramesLog2; +const uint8_t kVp9MaxProb = 255; +const int kVp9NumRefsPerFrame = 3; + +enum class Vp9ColorSpace { + UNKNOWN = 0, + BT_601 = 1, + BT_709 = 2, + SMPTE_170 = 3, + SMPTE_240 = 4, + BT_2020 = 5, + RESERVED = 6, + SRGB = 7, +}; + +enum class Vp9InterpFilter { + INTERP_FILTER_SELECT = 0, + EIGHTTAP_SMOOTH = 1, + EIGHTTAP = 2, + EIGHTTAP_SHARP = 3, + BILINEAR = 4, +}; + +// Members of Vp9FrameHeader will be 0-initialized by Vp9Parser::ParseFrame. +struct MEDIA_EXPORT Vp9Segmentation { + static const int kNumSegments = 8; + static const int kNumTreeProbs = kNumSegments - 1; + static const int kNumPredictionProbs = 3; + static const int kNumFeatures = 4; + + bool enabled; + + bool update_map; + uint8_t tree_probs[kNumTreeProbs]; + bool temporal_update; + uint8_t pred_probs[kNumPredictionProbs]; + + bool update_data; + bool abs_delta; + bool feature_enabled[kNumSegments][kNumFeatures]; + int8_t feature_data[kNumSegments][kNumFeatures]; +}; + +// Members of Vp9FrameHeader will be 0-initialized by Vp9Parser::ParseFrame. +struct MEDIA_EXPORT Vp9LoopFilter { + static const int kNumRefDeltas = 4; + static const int kNumModeDeltas = 2; + + uint8_t filter_level; + uint8_t sharpness_level; + + bool mode_ref_delta_enabled; + bool mode_ref_delta_update; + bool update_ref_deltas[kNumRefDeltas]; + int8_t ref_deltas[kNumRefDeltas]; + bool update_mode_deltas[kNumModeDeltas]; + int8_t mode_deltas[kNumModeDeltas]; +}; + +// Members of Vp9FrameHeader will be 0-initialized by Vp9Parser::ParseFrame. +struct MEDIA_EXPORT Vp9QuantizationParams { + bool IsLossless() const { + return base_qindex == 0 && y_dc_delta == 0 && uv_dc_delta == 0 && + uv_ac_delta == 0; + } + + uint8_t base_qindex; + int8_t y_dc_delta; + int8_t uv_dc_delta; + int8_t uv_ac_delta; +}; + +// VP9 frame header. +struct MEDIA_EXPORT Vp9FrameHeader { + enum FrameType { + KEYFRAME = 0, + INTERFRAME = 1, + }; + + bool IsKeyframe() const { return frame_type == KEYFRAME; } + + uint8_t profile; + + bool show_existing_frame; + uint8_t frame_to_show; + + FrameType frame_type; + + bool show_frame; + bool error_resilient_mode; + + uint8_t bit_depth; + Vp9ColorSpace color_space; + bool yuv_range; + uint8_t subsampling_x; + uint8_t subsampling_y; + + // The range of width and height is 1..2^16. + uint32_t width; + uint32_t height; + uint32_t display_width; + uint32_t display_height; + + bool intra_only; + uint8_t reset_context; + bool refresh_flag[kVp9NumRefFrames]; + uint8_t frame_refs[kVp9NumRefsPerFrame]; + bool ref_sign_biases[kVp9NumRefsPerFrame]; + bool allow_high_precision_mv; + Vp9InterpFilter interp_filter; + + bool refresh_frame_context; + bool frame_parallel_decoding_mode; + uint8_t frame_context_idx; + + Vp9LoopFilter loop_filter; + Vp9QuantizationParams quant_params; + Vp9Segmentation segment; + + uint8_t log2_tile_cols; + uint8_t log2_tile_rows; + + // Size of compressed header in bytes. + size_t first_partition_size; + + // Size of uncompressed header in bytes. + size_t uncompressed_header_size; +}; + +// A parser for VP9 bitstream. +class MEDIA_EXPORT Vp9Parser { + public: + Vp9Parser(); + + // Parses one frame and fills parsing result to |fhdr|. Returns true on + // success, false otherwise. + // |stream| is the address of VP9 bitstream with |size|. + bool ParseFrame(const uint8_t* stream, size_t size, Vp9FrameHeader* fhdr); + + private: + // The parsing context to keep track of references. + struct ReferenceSlot { + uint32_t width; + uint32_t height; + }; + + uint8_t ReadProfile(); + bool VerifySyncCode(); + bool ReadBitDepthColorSpaceSampling(Vp9FrameHeader* fhdr); + void ReadFrameSize(Vp9FrameHeader* fhdr); + bool ReadFrameSizeFromRefs(Vp9FrameHeader* fhdr); + void ReadDisplayFrameSize(Vp9FrameHeader* fhdr); + Vp9InterpFilter ReadInterpFilter(); + void ReadLoopFilter(Vp9LoopFilter* loop_filter); + void ReadQuantization(Vp9QuantizationParams* quants); + void ReadSegmentationMap(Vp9Segmentation* segment); + void ReadSegmentationData(Vp9Segmentation* segment); + void ReadSegmentation(Vp9Segmentation* segment); + void ReadTiles(Vp9FrameHeader* fhdr); + bool ParseUncompressedHeader(Vp9FrameHeader* fhdr); + void UpdateSlots(const Vp9FrameHeader* fhdr); + + // Start address of VP9 bitstream buffer. + const uint8_t* stream_; + + // Size of |stream_| in bytes. + size_t size_; + + // Raw bits decoder for uncompressed frame header. + Vp9RawBitsReader reader_; + + // The parsing context to keep track of references. + ReferenceSlot ref_slots_[kVp9NumRefFrames]; + + DISALLOW_COPY_AND_ASSIGN(Vp9Parser); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_VP9_PARSER_H_ diff --git a/media/filters/vp9_parser_unittest.cc b/media/filters/vp9_parser_unittest.cc new file mode 100644 index 0000000..d14e757 --- /dev/null +++ b/media/filters/vp9_parser_unittest.cc @@ -0,0 +1,147 @@ +// 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 "base/logging.h" +#include "media/base/test_data_util.h" +#include "media/filters/ivf_parser.h" +#include "media/filters/vp9_parser.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +class Vp9ParserTest : public ::testing::Test { + protected: + void SetUp() override { + base::FilePath file_path = GetTestDataFilePath("test-25fps.vp9"); + + stream_.reset(new base::MemoryMappedFile()); + ASSERT_TRUE(stream_->Initialize(file_path)) << "Couldn't open stream file: " + << file_path.MaybeAsASCII(); + + IvfFileHeader ivf_file_header; + ASSERT_TRUE(ivf_parser_.Initialize(stream_->data(), stream_->length(), + &ivf_file_header)); + ASSERT_EQ(ivf_file_header.fourcc, 0x30395056u); // VP90 + } + + void TearDown() override { stream_.reset(); } + + IvfParser ivf_parser_; + scoped_ptr<base::MemoryMappedFile> stream_; +}; + +TEST_F(Vp9ParserTest, StreamFileParsing) { + // Number of frames in the test stream to be parsed. + const int num_frames = 250; + + Vp9Parser vp9_parser; + IvfFrameHeader ivf_frame_header; + int num_parsed_frames = 0; + const uint8_t* ivf_payload; + + // Parse until the end of stream/unsupported stream/error in stream is found. + while (ivf_parser_.ParseNextFrame(&ivf_frame_header, &ivf_payload)) { + Vp9FrameHeader fhdr; + + ASSERT_TRUE( + vp9_parser.ParseFrame(ivf_payload, ivf_frame_header.frame_size, &fhdr)); + ++num_parsed_frames; + } + + DVLOG(1) << "Number of successfully parsed frames before EOS: " + << num_parsed_frames; + + EXPECT_EQ(num_frames, num_parsed_frames); +} + +TEST_F(Vp9ParserTest, VerifyFirstFrame) { + Vp9Parser vp9_parser; + IvfFrameHeader ivf_frame_header; + const uint8_t* ivf_payload; + + ASSERT_TRUE(ivf_parser_.ParseNextFrame(&ivf_frame_header, &ivf_payload)); + Vp9FrameHeader fhdr; + + ASSERT_TRUE( + vp9_parser.ParseFrame(ivf_payload, ivf_frame_header.frame_size, &fhdr)); + + EXPECT_EQ(0, fhdr.profile); + EXPECT_FALSE(fhdr.show_existing_frame); + EXPECT_EQ(Vp9FrameHeader::KEYFRAME, fhdr.frame_type); + EXPECT_TRUE(fhdr.show_frame); + EXPECT_FALSE(fhdr.error_resilient_mode); + + EXPECT_EQ(8, fhdr.bit_depth); + EXPECT_EQ(Vp9ColorSpace::UNKNOWN, fhdr.color_space); + EXPECT_FALSE(fhdr.yuv_range); + EXPECT_EQ(1, fhdr.subsampling_x); + EXPECT_EQ(1, fhdr.subsampling_y); + + EXPECT_EQ(320u, fhdr.width); + EXPECT_EQ(240u, fhdr.height); + EXPECT_EQ(320u, fhdr.display_width); + EXPECT_EQ(240u, fhdr.display_height); + + EXPECT_TRUE(fhdr.refresh_frame_context); + EXPECT_TRUE(fhdr.frame_parallel_decoding_mode); + EXPECT_EQ(0, fhdr.frame_context_idx); + + EXPECT_EQ(9, fhdr.loop_filter.filter_level); + EXPECT_EQ(0, fhdr.loop_filter.sharpness_level); + + const Vp9LoopFilter& lf = fhdr.loop_filter; + EXPECT_TRUE(lf.mode_ref_delta_enabled); + EXPECT_TRUE(lf.mode_ref_delta_update); + EXPECT_TRUE(lf.update_ref_deltas[0]); + EXPECT_EQ(1, lf.ref_deltas[0]); + EXPECT_EQ(-1, lf.ref_deltas[2]); + EXPECT_EQ(-1, lf.ref_deltas[3]); + + const Vp9QuantizationParams& qp = fhdr.quant_params; + EXPECT_EQ(65, qp.base_qindex); + EXPECT_FALSE(qp.y_dc_delta); + EXPECT_FALSE(qp.uv_dc_delta); + EXPECT_FALSE(qp.uv_ac_delta); + EXPECT_FALSE(qp.IsLossless()); + + const Vp9Segmentation& seg = fhdr.segment; + EXPECT_FALSE(seg.enabled); + + EXPECT_EQ(0, fhdr.log2_tile_cols); + EXPECT_EQ(0, fhdr.log2_tile_rows); + + EXPECT_EQ(120u, fhdr.first_partition_size); + EXPECT_EQ(18u, fhdr.uncompressed_header_size); +} + +TEST_F(Vp9ParserTest, VerifyInterFrame) { + Vp9Parser vp9_parser; + Vp9FrameHeader fhdr; + + // To verify the second frame. + for (int i = 0; i < 2; i++) { + IvfFrameHeader ivf_frame_header; + const uint8_t* ivf_payload; + ASSERT_TRUE(ivf_parser_.ParseNextFrame(&ivf_frame_header, &ivf_payload)); + ASSERT_TRUE( + vp9_parser.ParseFrame(ivf_payload, ivf_frame_header.frame_size, &fhdr)); + } + + EXPECT_EQ(Vp9FrameHeader::INTERFRAME, fhdr.frame_type); + EXPECT_FALSE(fhdr.show_frame); + EXPECT_FALSE(fhdr.intra_only); + EXPECT_FALSE(fhdr.reset_context); + EXPECT_TRUE(fhdr.refresh_flag[5]); + EXPECT_EQ(0, fhdr.frame_refs[0]); + EXPECT_EQ(1, fhdr.frame_refs[1]); + EXPECT_EQ(2, fhdr.frame_refs[2]); + EXPECT_TRUE(fhdr.allow_high_precision_mv); + EXPECT_EQ(Vp9InterpFilter::EIGHTTAP, fhdr.interp_filter); + + EXPECT_EQ(48u, fhdr.first_partition_size); + EXPECT_EQ(11u, fhdr.uncompressed_header_size); +} + +} // namespace media diff --git a/media/filters/vp9_raw_bits_reader.cc b/media/filters/vp9_raw_bits_reader.cc index 47014fd..ccd21a9 100644 --- a/media/filters/vp9_raw_bits_reader.cc +++ b/media/filters/vp9_raw_bits_reader.cc @@ -21,11 +21,11 @@ void Vp9RawBitsReader::Initialize(const uint8_t* data, size_t size) { valid_ = true; } -int Vp9RawBitsReader::ReadBit() { +bool Vp9RawBitsReader::ReadBool() { DCHECK(reader_); int value = 0; valid_ = valid_ && reader_->ReadBits(1, &value); - return value; + return value == 1; } int Vp9RawBitsReader::ReadLiteral(int bits) { @@ -38,7 +38,7 @@ int Vp9RawBitsReader::ReadLiteral(int bits) { int Vp9RawBitsReader::ReadSignedLiteral(int bits) { int value = ReadLiteral(bits); - return ReadBit() ? -value : value; + return ReadBool() ? -value : value; } size_t Vp9RawBitsReader::GetBytesRead() const { diff --git a/media/filters/vp9_raw_bits_reader.h b/media/filters/vp9_raw_bits_reader.h index 38f54a3..5cc46e2 100644 --- a/media/filters/vp9_raw_bits_reader.h +++ b/media/filters/vp9_raw_bits_reader.h @@ -37,7 +37,7 @@ class MEDIA_EXPORT Vp9RawBitsReader { // Reads one bit. // If the read goes beyond the end of buffer, the return value is undefined. - int ReadBit(); + bool ReadBool(); // Reads a literal with |bits| bits. // If the read goes beyond the end of buffer, the return value is undefined. diff --git a/media/filters/vp9_raw_bits_reader_unittest.cc b/media/filters/vp9_raw_bits_reader_unittest.cc index d798f22..999dbe1 100644 --- a/media/filters/vp9_raw_bits_reader_unittest.cc +++ b/media/filters/vp9_raw_bits_reader_unittest.cc @@ -7,26 +7,26 @@ namespace media { -TEST(Vp9RawBitsReaderTest, ReadBit) { +TEST(Vp9RawBitsReaderTest, ReadBool) { uint8_t data[] = {0xf1}; Vp9RawBitsReader reader; reader.Initialize(data, 1); EXPECT_TRUE(reader.IsValid()); EXPECT_EQ(0u, reader.GetBytesRead()); - EXPECT_EQ(1, reader.ReadBit()); + EXPECT_TRUE(reader.ReadBool()); EXPECT_EQ(1u, reader.GetBytesRead()); - EXPECT_EQ(1, reader.ReadBit()); - EXPECT_EQ(1, reader.ReadBit()); - EXPECT_EQ(1, reader.ReadBit()); - EXPECT_EQ(0, reader.ReadBit()); - EXPECT_EQ(0, reader.ReadBit()); - EXPECT_EQ(0, reader.ReadBit()); - EXPECT_EQ(1, reader.ReadBit()); + EXPECT_TRUE(reader.ReadBool()); + EXPECT_TRUE(reader.ReadBool()); + EXPECT_TRUE(reader.ReadBool()); + EXPECT_FALSE(reader.ReadBool()); + EXPECT_FALSE(reader.ReadBool()); + EXPECT_FALSE(reader.ReadBool()); + EXPECT_TRUE(reader.ReadBool()); EXPECT_TRUE(reader.IsValid()); // The return value is undefined. - ignore_result(reader.ReadBit()); + ignore_result(reader.ReadBool()); EXPECT_FALSE(reader.IsValid()); EXPECT_EQ(1u, reader.GetBytesRead()); } diff --git a/media/media.gyp b/media/media.gyp index 3c53ad2..a19f8ff 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -561,6 +561,8 @@ 'filters/vp8_bool_decoder.h', 'filters/vp8_parser.cc', 'filters/vp8_parser.h', + 'filters/vp9_parser.cc', + 'filters/vp9_parser.h', 'filters/vp9_raw_bits_reader.cc', 'filters/vp9_raw_bits_reader.h', 'filters/vpx_video_decoder.cc', @@ -1267,6 +1269,7 @@ 'filters/video_renderer_algorithm_unittest.cc', 'filters/vp8_bool_decoder_unittest.cc', 'filters/vp8_parser_unittest.cc', + 'filters/vp9_parser_unittest.cc', 'filters/vp9_raw_bits_reader_unittest.cc', 'capture/webm_muxer_unittest.cc', 'formats/common/offset_byte_queue_unittest.cc', |