summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorkcwu <kcwu@chromium.org>2015-08-10 22:55:18 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-11 05:55:55 +0000
commit2c5ec1f7703801d794e8b50b1be5753daba187ff (patch)
tree7e9ad62376b8796dee75a259fe66df8be6a2b053 /media
parent5438a98ac376de45d277fa7ffbb65258577d0fe7 (diff)
downloadchromium_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.gn3
-rw-r--r--media/filters/vp9_parser.cc400
-rw-r--r--media/filters/vp9_parser.h220
-rw-r--r--media/filters/vp9_parser_unittest.cc147
-rw-r--r--media/filters/vp9_raw_bits_reader.cc6
-rw-r--r--media/filters/vp9_raw_bits_reader.h2
-rw-r--r--media/filters/vp9_raw_bits_reader_unittest.cc20
-rw-r--r--media/media.gyp3
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',