// Copyright (c) 2016 The WebM project authors. All Rights Reserved. // // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file in the root of the source // tree. An additional intellectual property rights grant can be found // in the file PATENTS. All contributing project authors may // be found in the AUTHORS file in the root of the source tree. #include "common/vp9_header_parser.h" #include namespace vp9_parser { bool Vp9HeaderParser::SetFrame(const uint8_t* frame, size_t length) { if (!frame || length == 0) return false; frame_ = frame; frame_size_ = length; bit_offset_ = 0; profile_ = -1; show_existing_frame_ = 0; key_ = 0; altref_ = 0; error_resilient_mode_ = 0; intra_only_ = 0; reset_frame_context_ = 0; color_space_ = 0; color_range_ = 0; subsampling_x_ = 0; subsampling_y_ = 0; refresh_frame_flags_ = 0; return true; } bool Vp9HeaderParser::ParseUncompressedHeader(const uint8_t* frame, size_t length) { if (!SetFrame(frame, length)) return false; const int frame_marker = VpxReadLiteral(2); if (frame_marker != kVp9FrameMarker) { fprintf(stderr, "Invalid VP9 frame_marker:%d\n", frame_marker); return false; } profile_ = ReadBit(); profile_ |= ReadBit() << 1; if (profile_ > 2) profile_ += ReadBit(); // TODO(fgalligan): Decide how to handle show existing frames. show_existing_frame_ = ReadBit(); if (show_existing_frame_) return true; key_ = !ReadBit(); altref_ = !ReadBit(); error_resilient_mode_ = ReadBit(); if (key_) { if (!ValidateVp9SyncCode()) { fprintf(stderr, "Invalid Sync code!\n"); return false; } ParseColorSpace(); ParseFrameResolution(); ParseFrameParallelMode(); ParseTileInfo(); } else { intra_only_ = altref_ ? ReadBit() : 0; reset_frame_context_ = error_resilient_mode_ ? 0 : VpxReadLiteral(2); if (intra_only_) { if (!ValidateVp9SyncCode()) { fprintf(stderr, "Invalid Sync code!\n"); return false; } if (profile_ > 0) { ParseColorSpace(); } else { // NOTE: The intra-only frame header does not include the specification // of either the color format or color sub-sampling in profile 0. VP9 // specifies that the default color format should be YUV 4:2:0 in this // case (normative). color_space_ = kVpxCsBt601; color_range_ = kVpxCrStudioRange; subsampling_y_ = subsampling_x_ = 1; bit_depth_ = 8; } refresh_frame_flags_ = VpxReadLiteral(kRefFrames); ParseFrameResolution(); } else { refresh_frame_flags_ = VpxReadLiteral(kRefFrames); for (int i = 0; i < kRefsPerFrame; ++i) { VpxReadLiteral(kRefFrames_LOG2); // Consume ref. ReadBit(); // Consume ref sign bias. } bool found = false; for (int i = 0; i < kRefsPerFrame; ++i) { if (ReadBit()) { // Found previous reference, width and height did not change since // last frame. found = true; break; } } if (!found) ParseFrameResolution(); } } return true; } int Vp9HeaderParser::ReadBit() { const size_t off = bit_offset_; const size_t byte_offset = off >> 3; const int bit_shift = 7 - static_cast(off & 0x7); if (byte_offset < frame_size_) { const int bit = (frame_[byte_offset] >> bit_shift) & 1; bit_offset_++; return bit; } else { return 0; } } int Vp9HeaderParser::VpxReadLiteral(int bits) { int value = 0; for (int bit = bits - 1; bit >= 0; --bit) value |= ReadBit() << bit; return value; } bool Vp9HeaderParser::ValidateVp9SyncCode() { const int sync_code_0 = VpxReadLiteral(8); const int sync_code_1 = VpxReadLiteral(8); const int sync_code_2 = VpxReadLiteral(8); return (sync_code_0 == 0x49 && sync_code_1 == 0x83 && sync_code_2 == 0x42); } void Vp9HeaderParser::ParseColorSpace() { bit_depth_ = 0; if (profile_ >= 2) bit_depth_ = ReadBit() ? 12 : 10; else bit_depth_ = 8; color_space_ = VpxReadLiteral(3); if (color_space_ != kVpxCsSrgb) { color_range_ = ReadBit(); if (profile_ == 1 || profile_ == 3) { subsampling_x_ = ReadBit(); subsampling_y_ = ReadBit(); ReadBit(); } else { subsampling_y_ = subsampling_x_ = 1; } } else { color_range_ = kVpxCrFullRange; if (profile_ == 1 || profile_ == 3) { subsampling_y_ = subsampling_x_ = 0; ReadBit(); } } } void Vp9HeaderParser::ParseFrameResolution() { width_ = VpxReadLiteral(16) + 1; height_ = VpxReadLiteral(16) + 1; } void Vp9HeaderParser::ParseFrameParallelMode() { if (ReadBit()) { VpxReadLiteral(16); // display width VpxReadLiteral(16); // display height } if (!error_resilient_mode_) { ReadBit(); // Consume refresh frame context frame_parallel_mode_ = ReadBit(); } else { frame_parallel_mode_ = 1; } } void Vp9HeaderParser::ParseTileInfo() { VpxReadLiteral(2); // Consume frame context index // loopfilter VpxReadLiteral(6); // Consume filter level VpxReadLiteral(3); // Consume sharpness level const bool mode_ref_delta_enabled = ReadBit(); if (mode_ref_delta_enabled) { const bool mode_ref_delta_update = ReadBit(); if (mode_ref_delta_update) { const int kMaxRefLFDeltas = 4; for (int i = 0; i < kMaxRefLFDeltas; ++i) { if (ReadBit()) VpxReadLiteral(7); // Consume ref_deltas + sign } const int kMaxModeDeltas = 2; for (int i = 0; i < kMaxModeDeltas; ++i) { if (ReadBit()) VpxReadLiteral(7); // Consume mode_delta + sign } } } // quantization VpxReadLiteral(8); // Consume base_q SkipDeltaQ(); // y dc SkipDeltaQ(); // uv ac SkipDeltaQ(); // uv dc // segmentation const bool segmentation_enabled = ReadBit(); if (!segmentation_enabled) { const int aligned_width = AlignPowerOfTwo(width_, kMiSizeLog2); const int mi_cols = aligned_width >> kMiSizeLog2; const int aligned_mi_cols = AlignPowerOfTwo(mi_cols, kMiSizeLog2); const int sb_cols = aligned_mi_cols >> 3; // to_sbs(mi_cols); int min_log2_n_tiles, max_log2_n_tiles; for (max_log2_n_tiles = 0; (sb_cols >> max_log2_n_tiles) >= kMinTileWidthB64; max_log2_n_tiles++) { } max_log2_n_tiles--; if (max_log2_n_tiles < 0) max_log2_n_tiles = 0; for (min_log2_n_tiles = 0; (kMaxTileWidthB64 << min_log2_n_tiles) < sb_cols; min_log2_n_tiles++) { } // columns const int max_log2_tile_cols = max_log2_n_tiles; const int min_log2_tile_cols = min_log2_n_tiles; int max_ones = max_log2_tile_cols - min_log2_tile_cols; int log2_tile_cols = min_log2_tile_cols; while (max_ones-- && ReadBit()) log2_tile_cols++; // rows int log2_tile_rows = ReadBit(); if (log2_tile_rows) log2_tile_rows += ReadBit(); row_tiles_ = 1 << log2_tile_rows; column_tiles_ = 1 << log2_tile_cols; } } void Vp9HeaderParser::SkipDeltaQ() { if (ReadBit()) VpxReadLiteral(4); } int Vp9HeaderParser::AlignPowerOfTwo(int value, int n) { return (((value) + ((1 << (n)) - 1)) & ~((1 << (n)) - 1)); } } // namespace vp9_parser