Index: media/filters/vp9_parser.cc |
diff --git a/media/filters/vp9_parser.cc b/media/filters/vp9_parser.cc |
index 0fa7dde63a1840a7029282ac818e961b517acf4a..d945a92da84e88a1962c14999844c380a22b520e 100644 |
--- a/media/filters/vp9_parser.cc |
+++ b/media/filters/vp9_parser.cc |
@@ -3,40 +3,22 @@ |
// found in the LICENSE file. |
// |
// This file contains an implementation of a VP9 bitstream parser. |
+// |
+// VERBOSE level: |
+// 1 something wrong in bitstream |
+// 2 parsing steps |
+// 3 parsed values (selected) |
#include "media/filters/vp9_parser.h" |
#include <algorithm> |
+#include "base/bind.h" |
#include "base/logging.h" |
#include "base/macros.h" |
#include "base/numerics/safe_conversions.h" |
- |
-namespace { |
- |
-const int kMaxLoopFilterLevel = 63; |
- |
-// 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 |
+#include "media/filters/vp9_compressed_header_parser.h" |
+#include "media/filters/vp9_uncompressed_header_parser.h" |
namespace media { |
@@ -47,413 +29,262 @@ bool Vp9FrameHeader::IsKeyframe() const { |
return !show_existing_frame && frame_type == KEYFRAME; |
} |
-Vp9Parser::FrameInfo::FrameInfo(const uint8_t* ptr, off_t size) |
- : ptr(ptr), size(size) {} |
- |
-Vp9Parser::Vp9Parser() { |
- Reset(); |
+bool Vp9FrameHeader::IsIntra() const { |
+ return !show_existing_frame && (frame_type == KEYFRAME || intra_only); |
} |
-Vp9Parser::~Vp9Parser() {} |
- |
-void Vp9Parser::SetStream(const uint8_t* stream, off_t stream_size) { |
- DCHECK(stream); |
- stream_ = stream; |
- bytes_left_ = stream_size; |
- frames_.clear(); |
-} |
+Vp9Parser::FrameInfo::FrameInfo(const uint8_t* ptr, off_t size) |
+ : ptr(ptr), size(size) {} |
-void Vp9Parser::Reset() { |
- stream_ = nullptr; |
- bytes_left_ = 0; |
- frames_.clear(); |
+Vp9FrameContextManager::Vp9FrameContextManager() |
+ : initialized_(false), need_update_(false), weak_ptr_factory_(this) {} |
- memset(&segmentation_, 0, sizeof(segmentation_)); |
- memset(&loop_filter_, 0, sizeof(loop_filter_)); |
- memset(&ref_slots_, 0, sizeof(ref_slots_)); |
-} |
+Vp9FrameContextManager::~Vp9FrameContextManager() {} |
-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"; |
+bool Vp9FrameContextManager::IsValidFrameContext( |
+ const Vp9FrameContext& context) { |
+ // probs should be in [1, 255] range. |
+ static_assert(sizeof(Vp9Prob) == 1, |
+ "following checks assuming Vp9Prob is single byte"); |
+ if (memchr(context.tx_probs_8x8, 0, sizeof(context.tx_probs_8x8))) |
+ return false; |
+ if (memchr(context.tx_probs_16x16, 0, sizeof(context.tx_probs_16x16))) |
+ return false; |
+ if (memchr(context.tx_probs_32x32, 0, sizeof(context.tx_probs_32x32))) |
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; |
+ for (auto& a : context.coef_probs) { |
+ for (auto& ai : a) { |
+ for (auto& aj : ai) { |
+ for (auto& ak : aj) { |
+ int max_l = (ak == aj[0]) ? 3 : 6; |
+ for (int l = 0; l < max_l; l++) { |
+ for (auto& x : ak[l]) { |
+ if (x == 0) |
+ return false; |
+ } |
+ } |
+ } |
} |
- } else { |
- DVLOG(1) << "4:4:4 color not supported in profile 0 or 2"; |
- return false; |
} |
} |
+ if (memchr(context.skip_prob, 0, sizeof(context.skip_prob))) |
+ return false; |
+ if (memchr(context.inter_mode_probs, 0, sizeof(context.inter_mode_probs))) |
+ return false; |
+ if (memchr(context.interp_filter_probs, 0, |
+ sizeof(context.interp_filter_probs))) |
+ return false; |
+ if (memchr(context.is_inter_prob, 0, sizeof(context.is_inter_prob))) |
+ return false; |
+ if (memchr(context.comp_mode_prob, 0, sizeof(context.comp_mode_prob))) |
+ return false; |
+ if (memchr(context.single_ref_prob, 0, sizeof(context.single_ref_prob))) |
+ return false; |
+ if (memchr(context.comp_ref_prob, 0, sizeof(context.comp_ref_prob))) |
+ return false; |
+ if (memchr(context.y_mode_probs, 0, sizeof(context.y_mode_probs))) |
+ return false; |
+ if (memchr(context.uv_mode_probs, 0, sizeof(context.uv_mode_probs))) |
+ return false; |
+ if (memchr(context.partition_probs, 0, sizeof(context.partition_probs))) |
+ return false; |
+ if (memchr(context.mv_joint_probs, 0, sizeof(context.mv_joint_probs))) |
+ return false; |
+ if (memchr(context.mv_sign_prob, 0, sizeof(context.mv_sign_prob))) |
+ return false; |
+ if (memchr(context.mv_class_probs, 0, sizeof(context.mv_class_probs))) |
+ return false; |
+ if (memchr(context.mv_class0_bit_prob, 0, sizeof(context.mv_class0_bit_prob))) |
+ return false; |
+ if (memchr(context.mv_bits_prob, 0, sizeof(context.mv_bits_prob))) |
+ return false; |
+ if (memchr(context.mv_class0_fr_probs, 0, sizeof(context.mv_class0_fr_probs))) |
+ return false; |
+ if (memchr(context.mv_fr_probs, 0, sizeof(context.mv_fr_probs))) |
+ return false; |
+ if (memchr(context.mv_class0_hp_prob, 0, sizeof(context.mv_class0_hp_prob))) |
+ return false; |
+ if (memchr(context.mv_hp_prob, 0, sizeof(context.mv_hp_prob))) |
+ 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; |
+const Vp9FrameContext& Vp9FrameContextManager::frame_context() const { |
+ DCHECK(initialized_); |
+ DCHECK(!need_update_); |
+ return frame_context_; |
} |
-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; |
- } |
+void Vp9FrameContextManager::Reset() { |
+ initialized_ = false; |
+ need_update_ = false; |
Pawel Osciak
2016/08/04 10:20:19
Should we also zero out frame_context_ ?
kcwu
2016/08/05 11:38:47
It's inteded not do so. It's not small (2k) and is
Pawel Osciak
2016/08/08 08:13:36
Acknowledged.
|
+ weak_ptr_factory_.InvalidateWeakPtrs(); |
} |
-Vp9InterpFilter Vp9Parser::ReadInterpFilter() { |
- if (reader_.ReadBool()) |
- return Vp9InterpFilter::SWICHABLE; |
+Vp9FrameContextManager::ContextRefreshCallback |
+Vp9FrameContextManager::SetNeedUpdate() { |
+ DCHECK(!need_update_); |
+ initialized_ = true; |
+ need_update_ = true; |
- // 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)]; |
+ return base::Bind(&Vp9FrameContextManager::Update, |
+ weak_ptr_factory_.GetWeakPtr()); |
} |
-void Vp9Parser::ReadLoopFilter() { |
- loop_filter_.filter_level = reader_.ReadLiteral(6); |
- loop_filter_.sharpness_level = reader_.ReadLiteral(3); |
- loop_filter_.mode_ref_delta_update = false; |
- |
- 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::VP9_FRAME_MAX; 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 Vp9FrameContextManager::Update(const Vp9FrameContext& frame_context) { |
+ if (need_update_) { |
+ DVLOG(2) << "Got external frame_context update"; |
+ // Verify values from driver explicitly. |
Pawel Osciak
2016/08/04 10:20:19
s/driver/client/
kcwu
2016/08/05 11:38:47
Done.
|
+ if (!IsValidFrameContext(frame_context)) { |
+ DLOG(ERROR) << "Invalid prob value in frame_context"; |
+ return; |
} |
+ } else { |
+ DCHECK(IsValidFrameContext(frame_context)); |
} |
+ need_update_ = false; |
+ initialized_ = true; |
+ frame_context_ = frame_context; |
+ |
+ // For frame context we are updating, it may be still awaiting previous |
+ // ContextRefreshCallback. Because we overwrite the value of context here and |
Pawel Osciak
2016/08/04 10:20:18
When would we not need a pending update? Is it pos
kcwu
2016/08/05 11:38:47
Theoretically it is possible. I don't know how oft
Pawel Osciak
2016/08/08 08:13:36
Acknowledged.
|
+ // previous ContextRefreshCallback no longer matters, invalidate the weak ptr |
+ // to prevent previous ContextRefreshCallback run. |
+ // With this optimization, we may be able to parse more frames while previous |
+ // are still decoding. |
+ weak_ptr_factory_.InvalidateWeakPtrs(); |
} |
-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_dc_delta = reader_.ReadSignedLiteral(4); |
- |
- if (reader_.ReadBool()) |
- quants->uv_ac_delta = reader_.ReadSignedLiteral(4); |
-} |
- |
-void Vp9Parser::ReadSegmentationMap() { |
- for (size_t i = 0; i < Vp9Segmentation::kNumTreeProbs; i++) { |
- segmentation_.tree_probs[i] = |
- reader_.ReadBool() ? reader_.ReadLiteral(8) : kVp9MaxProb; |
- } |
- |
- for (size_t i = 0; i < Vp9Segmentation::kNumPredictionProbs; i++) |
- segmentation_.pred_probs[i] = kVp9MaxProb; |
- |
- segmentation_.temporal_update = reader_.ReadBool(); |
- if (segmentation_.temporal_update) { |
- for (size_t i = 0; i < Vp9Segmentation::kNumPredictionProbs; i++) { |
- if (reader_.ReadBool()) |
- segmentation_.pred_probs[i] = reader_.ReadLiteral(8); |
- } |
- } |
+void Vp9Parser::Context::Reset() { |
+ memset(&segmentation, 0, sizeof(segmentation)); |
+ memset(&loop_filter, 0, sizeof(loop_filter)); |
+ memset(&ref_slots, 0, sizeof(ref_slots)); |
+ for (auto& manager : frame_context_managers) |
+ manager.Reset(); |
} |
-void Vp9Parser::ReadSegmentationData() { |
- segmentation_.abs_delta = reader_.ReadBool(); |
- |
- const int kFeatureDataBits[] = {8, 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::SEG_LVL_MAX; j++) { |
- int16_t data = 0; |
- segmentation_.feature_enabled[i][j] = reader_.ReadBool(); |
- if (segmentation_.feature_enabled[i][j]) { |
- data = reader_.ReadLiteral(kFeatureDataBits[j]); |
- if (kFeatureDataSigned[j]) |
- if (reader_.ReadBool()) |
- data = -data; |
- } |
- segmentation_.feature_data[i][j] = data; |
- } |
- } |
+Vp9Parser::Vp9Parser(bool parsing_compressed_header) |
+ : parsing_compressed_header_(parsing_compressed_header) { |
+ Reset(); |
} |
-void Vp9Parser::ReadSegmentation() { |
- segmentation_.update_map = false; |
- segmentation_.update_data = false; |
- |
- segmentation_.enabled = reader_.ReadBool(); |
- if (!segmentation_.enabled) |
- return; |
- |
- segmentation_.update_map = reader_.ReadBool(); |
- if (segmentation_.update_map) |
- ReadSegmentationMap(); |
+Vp9Parser::~Vp9Parser() {} |
- segmentation_.update_data = reader_.ReadBool(); |
- if (segmentation_.update_data) |
- ReadSegmentationData(); |
+void Vp9Parser::SetStream(const uint8_t* stream, off_t stream_size) { |
+ DCHECK(stream); |
+ stream_ = stream; |
+ bytes_left_ = stream_size; |
+ frames_.clear(); |
} |
-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++; |
+void Vp9Parser::Reset() { |
+ stream_ = nullptr; |
+ bytes_left_ = 0; |
+ frames_.clear(); |
- fhdr->log2_tile_rows = reader_.ReadBool() ? 1 : 0; |
- if (fhdr->log2_tile_rows > 0 && reader_.ReadBool()) |
- fhdr->log2_tile_rows++; |
+ context_.Reset(); |
} |
-bool Vp9Parser::ParseUncompressedHeader(const uint8_t* stream, |
- off_t frame_size, |
- Vp9FrameHeader* fhdr) { |
- reader_.Initialize(stream, frame_size); |
+Vp9Parser::Result Vp9Parser::ParseNextFrame( |
+ Vp9FrameHeader* fhdr, |
+ base::Callback<void(const Vp9FrameContext&)>* context_refresh_cb) { |
Pawel Osciak
2016/08/04 10:20:18
ContextRefreshCallback* ?
kcwu
2016/08/05 11:38:47
Done.
|
+ DCHECK(fhdr); |
+ DCHECK(!parsing_compressed_header_ || context_refresh_cb); |
+ DVLOG(2) << "ParseNextFrame"; |
- fhdr->data = stream; |
- fhdr->frame_size = frame_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 (frames_.empty()) { |
+ // No frames to be decoded, if there is no more stream, request more. |
+ if (!stream_) |
+ return kEOStream; |
- if (!reader_.IsValid()) { |
- DVLOG(1) << "parser reads beyond the end of buffer"; |
- return false; |
+ // New stream to be parsed, parse it and fill frames_. |
+ if (!ParseSuperframe()) { |
+ DVLOG(1) << "Failed parsing superframes"; |
+ return kInvalidStream; |
} |
- 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; |
- |
- fhdr->refresh_flags = 0xff; |
- |
- 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); |
+ DCHECK(!frames_.empty()); |
+ FrameInfo frame_info = frames_.front(); |
+ frames_.pop_front(); |
- if (fhdr->intra_only) { |
- if (!VerifySyncCode()) |
- return false; |
+ // TODO(kcwu): remove this debug info |
Pawel Osciak
2016/08/04 10:20:19
Marking with a comment so we don't forget to remov
kcwu
2016/08/05 11:38:47
Removed.
|
+ static int num = 0; |
+ DVLOG(1) << "num=" << num << ", size=" << frame_info.size; |
+ num++; |
- 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; |
- } |
- |
- fhdr->refresh_flags = reader_.ReadLiteral(8); |
+ memset(fhdr, 0, sizeof(*fhdr)); |
+ Vp9UncompressedHeaderParser uncompressed_parser(&context_); |
+ if (!uncompressed_parser.Parse(frame_info.ptr, frame_info.size, fhdr)) |
+ return kInvalidStream; |
- ReadFrameSize(fhdr); |
- ReadDisplayFrameSize(fhdr); |
- } else { |
- fhdr->refresh_flags = reader_.ReadLiteral(8); |
+ SetupSegmentationDequant(fhdr); |
+ SetupLoopFilter(); |
+ UpdateSlots(fhdr); |
- for (size_t i = 0; i < kVp9NumRefsPerFrame; i++) { |
- fhdr->frame_refs[i] = reader_.ReadLiteral(kVp9NumRefFramesLog2); |
- fhdr->ref_sign_biases[i] = reader_.ReadBool(); |
+ if (fhdr->header_size_in_bytes == 0) { |
+ // Verify padding bits are zero. |
+ for (int i = fhdr->uncompressed_header_size; i < frame_info.size; i++) { |
Pawel Osciak
2016/08/04 10:20:19
s/int/size_t/
kcwu
2016/08/05 11:38:47
Done.
|
+ if (frame_info.ptr[i] != 0) { |
+ DVLOG(1) << "Padding bits are not zeros."; |
+ return kInvalidStream; |
} |
- |
- if (!ReadFrameSizeFromRefs(fhdr)) |
- return false; |
- ReadDisplayFrameSize(fhdr); |
- |
- fhdr->allow_high_precision_mv = reader_.ReadBool(); |
- fhdr->interp_filter = ReadInterpFilter(); |
} |
+ return kOk; |
} |
- |
- 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); |
- |
- if (fhdr->IsKeyframe() || fhdr->intra_only) |
- SetupPastIndependence(); |
- |
- ReadLoopFilter(); |
- ReadQuantization(&fhdr->quant_params); |
- ReadSegmentation(); |
- |
- 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; |
+ if (fhdr->uncompressed_header_size + fhdr->header_size_in_bytes > |
+ static_cast<size_t>(frame_info.size)) { |
+ DVLOG(1) << "header_size_in_bytes=" << fhdr->header_size_in_bytes |
+ << " is larger than bytes left in buffer: " |
+ << frame_info.size - fhdr->uncompressed_header_size; |
+ return kInvalidStream; |
} |
- fhdr->uncompressed_header_size = reader_.GetBytesRead(); |
- SetupSegmentationDequant(fhdr->quant_params); |
- SetupLoopFilter(); |
- |
- UpdateSlots(fhdr); |
- |
- return true; |
-} |
- |
-void Vp9Parser::UpdateSlots(const Vp9FrameHeader* fhdr) { |
- for (size_t i = 0; i < kVp9NumRefFrames; i++) { |
- if (fhdr->RefreshFlag(i)) { |
- ref_slots_[i].width = fhdr->width; |
- ref_slots_[i].height = fhdr->height; |
+ if (parsing_compressed_header_) { |
+ Vp9FrameContextManager& context_to_load = |
+ context_.frame_context_managers[fhdr->frame_context_idx]; |
+ if (!context_to_load.initialized()) { |
+ // 8.2 Frame order constraints |
+ // must load an initialized set of probabilities. |
+ DVLOG(1) << "loading uninitialized frame context, index=" |
+ << fhdr->frame_context_idx; |
+ return kInvalidStream; |
} |
- } |
-} |
- |
-Vp9Parser::Result Vp9Parser::ParseNextFrame(Vp9FrameHeader* fhdr) { |
- if (frames_.empty()) { |
- // No frames to be decoded, if there is no more stream, request more. |
- if (!stream_) |
- return kEOStream; |
+ if (context_to_load.need_update()) { |
+ DVLOG(3) << "waiting frame_context_idx=" |
+ << static_cast<int>(fhdr->frame_context_idx) << " to update"; |
Pawel Osciak
2016/08/04 10:20:19
Is the cast needed?
kcwu
2016/08/05 11:38:47
Yes, otherwise it is uint8_t and DVLOG() will prin
|
+ return kAwaitingRefresh; |
+ } |
+ fhdr->initial_frame_context = fhdr->frame_context = |
+ context_to_load.frame_context(); |
- // New stream to be parsed, parse it and fill frames_. |
- if (!ParseSuperframe()) { |
- DVLOG(1) << "Failed parsing superframes"; |
+ Vp9CompressedHeaderParser compressed_parser; |
+ if (!compressed_parser.Parse( |
+ frame_info.ptr + fhdr->uncompressed_header_size, |
+ fhdr->header_size_in_bytes, fhdr)) { |
return kInvalidStream; |
} |
- } |
- DCHECK(!frames_.empty()); |
- FrameInfo frame_info = frames_.front(); |
- frames_.pop_front(); |
+ if (fhdr->refresh_frame_context) { |
+ Vp9FrameContextManager& frame_context_manager = |
+ context_.frame_context_managers[fhdr->frame_context_idx]; |
- memset(fhdr, 0, sizeof(*fhdr)); |
- if (!ParseUncompressedHeader(frame_info.ptr, frame_info.size, fhdr)) |
- return kInvalidStream; |
+ // In frame parallel mode, we can refresh the context without decoding |
+ // tile data. |
+ if (fhdr->frame_parallel_decoding_mode) { |
+ frame_context_manager.Update(fhdr->frame_context); |
+ } else { |
+ *context_refresh_cb = frame_context_manager.SetNeedUpdate(); |
+ } |
+ } |
+ } |
return kOk; |
} |
+// Annex B Superframes |
bool Vp9Parser::ParseSuperframe() { |
const uint8_t* stream = stream_; |
off_t bytes_left = bytes_left_; |
@@ -518,25 +349,7 @@ bool Vp9Parser::ParseSuperframe() { |
return true; |
} |
-void Vp9Parser::ResetLoopfilter() { |
- loop_filter_.mode_ref_delta_enabled = true; |
- loop_filter_.mode_ref_delta_update = true; |
- |
- const int8_t default_ref_deltas[] = {1, 0, -1, -1}; |
- static_assert( |
- arraysize(default_ref_deltas) == arraysize(loop_filter_.ref_deltas), |
- "ref_deltas arrays of incorrect size"); |
- for (size_t i = 0; i < arraysize(loop_filter_.ref_deltas); ++i) |
- loop_filter_.ref_deltas[i] = default_ref_deltas[i]; |
- |
- memset(loop_filter_.mode_deltas, 0, sizeof(loop_filter_.mode_deltas)); |
-} |
- |
-void Vp9Parser::SetupPastIndependence() { |
- memset(&segmentation_, 0, sizeof(segmentation_)); |
- ResetLoopfilter(); |
-} |
- |
+// 8.6.1 |
const size_t QINDEX_RANGE = 256; |
const int16_t kDcQLookup[QINDEX_RANGE] = { |
4, 8, 8, 9, 10, 11, 12, 12, |
@@ -611,83 +424,122 @@ const int16_t kAcQLookup[QINDEX_RANGE] = { |
static_assert(arraysize(kDcQLookup) == arraysize(kAcQLookup), |
"quantizer lookup arrays of incorrect size"); |
-#define CLAMP_Q(q) \ |
- std::min(std::max(static_cast<size_t>(0), q), arraysize(kDcQLookup) - 1) |
+static size_t ClampQ(size_t q) { |
+ return std::min(std::max(static_cast<size_t>(0), q), |
+ arraysize(kDcQLookup) - 1); |
+} |
+// 8.6.1 Dequantization functions |
size_t Vp9Parser::GetQIndex(const Vp9QuantizationParams& quant, |
size_t segid) const { |
- if (segmentation_.FeatureEnabled(segid, Vp9Segmentation::SEG_LVL_ALT_Q)) { |
+ const Vp9SegmentationParams& segmentation = context_.segmentation; |
+ |
+ if (segmentation.FeatureEnabled(segid, |
+ Vp9SegmentationParams::SEG_LVL_ALT_Q)) { |
int16_t feature_data = |
- segmentation_.FeatureData(segid, Vp9Segmentation::SEG_LVL_ALT_Q); |
- size_t q_index = segmentation_.abs_delta ? feature_data |
- : quant.base_qindex + feature_data; |
- return CLAMP_Q(q_index); |
+ segmentation.FeatureData(segid, Vp9SegmentationParams::SEG_LVL_ALT_Q); |
+ size_t q_index = segmentation.abs_or_delta_update |
+ ? feature_data |
+ : quant.base_q_idx + feature_data; |
+ return ClampQ(q_index); |
} |
- return quant.base_qindex; |
+ return quant.base_q_idx; |
} |
-void Vp9Parser::SetupSegmentationDequant(const Vp9QuantizationParams& quant) { |
- if (segmentation_.enabled) { |
- for (size_t i = 0; i < Vp9Segmentation::kNumSegments; ++i) { |
+// 8.6.1 Dequantization functions |
+void Vp9Parser::SetupSegmentationDequant(const Vp9FrameHeader* fhdr) { |
+ const Vp9QuantizationParams& quant = fhdr->quant_params; |
+ Vp9SegmentationParams& segmentation = context_.segmentation; |
+ |
+ DLOG_IF(ERROR, fhdr->bit_depth > 8) << "bit_depth > 8 is not supported " |
+ "yet, kDcQLookup and kAcQLookup " |
+ "need extended"; |
+ if (segmentation.enabled) { |
+ for (size_t i = 0; i < Vp9SegmentationParams::kNumSegments; ++i) { |
const size_t q_index = GetQIndex(quant, i); |
- segmentation_.y_dequant[i][0] = |
- kDcQLookup[CLAMP_Q(q_index + quant.y_dc_delta)]; |
- segmentation_.y_dequant[i][1] = kAcQLookup[CLAMP_Q(q_index)]; |
- segmentation_.uv_dequant[i][0] = |
- kDcQLookup[CLAMP_Q(q_index + quant.uv_dc_delta)]; |
- segmentation_.uv_dequant[i][1] = |
- kAcQLookup[CLAMP_Q(q_index + quant.uv_ac_delta)]; |
+ segmentation.y_dequant[i][0] = |
+ kDcQLookup[ClampQ(q_index + quant.delta_q_y_dc)]; |
+ segmentation.y_dequant[i][1] = kAcQLookup[ClampQ(q_index)]; |
+ segmentation.uv_dequant[i][0] = |
+ kDcQLookup[ClampQ(q_index + quant.delta_q_uv_dc)]; |
+ segmentation.uv_dequant[i][1] = |
+ kAcQLookup[ClampQ(q_index + quant.delta_q_uv_ac)]; |
} |
} else { |
- const size_t q_index = quant.base_qindex; |
- segmentation_.y_dequant[0][0] = |
- kDcQLookup[CLAMP_Q(q_index + quant.y_dc_delta)]; |
- segmentation_.y_dequant[0][1] = kAcQLookup[CLAMP_Q(q_index)]; |
- segmentation_.uv_dequant[0][0] = |
- kDcQLookup[CLAMP_Q(q_index + quant.uv_dc_delta)]; |
- segmentation_.uv_dequant[0][1] = |
- kAcQLookup[CLAMP_Q(q_index + quant.uv_ac_delta)]; |
+ const size_t q_index = quant.base_q_idx; |
+ segmentation.y_dequant[0][0] = |
+ kDcQLookup[ClampQ(q_index + quant.delta_q_y_dc)]; |
+ segmentation.y_dequant[0][1] = kAcQLookup[ClampQ(q_index)]; |
+ segmentation.uv_dequant[0][0] = |
+ kDcQLookup[ClampQ(q_index + quant.delta_q_uv_dc)]; |
+ segmentation.uv_dequant[0][1] = |
+ kAcQLookup[ClampQ(q_index + quant.delta_q_uv_ac)]; |
} |
} |
-#undef CLAMP_Q |
-#define CLAMP_LF(l) std::min(std::max(0, l), kMaxLoopFilterLevel) |
+static int ClampLf(int lf) { |
+ const int kMaxLoopFilterLevel = 63; |
+ return std::min(std::max(0, lf), kMaxLoopFilterLevel); |
+} |
+ |
+// 8.8.1 Loop filter frame init process |
void Vp9Parser::SetupLoopFilter() { |
- if (!loop_filter_.filter_level) |
+ Vp9LoopFilterParams& loop_filter = context_.loop_filter; |
+ Vp9SegmentationParams& segmentation = context_.segmentation; |
Pawel Osciak
2016/08/04 10:20:19
Please move down to before the fist use site.
kcwu
2016/08/05 11:38:47
Done.
|
+ if (!loop_filter.level) |
return; |
- int scale = loop_filter_.filter_level < 32 ? 1 : 2; |
+ int scale = loop_filter.level < 32 ? 1 : 2; |
- for (size_t i = 0; i < Vp9Segmentation::kNumSegments; ++i) { |
- int level = loop_filter_.filter_level; |
+ for (size_t i = 0; i < Vp9SegmentationParams::kNumSegments; ++i) { |
+ int level = loop_filter.level; |
- if (segmentation_.FeatureEnabled(i, Vp9Segmentation::SEG_LVL_ALT_LF)) { |
+ if (segmentation.FeatureEnabled(i, Vp9SegmentationParams::SEG_LVL_ALT_LF)) { |
int feature_data = |
- segmentation_.FeatureData(i, Vp9Segmentation::SEG_LVL_ALT_LF); |
- level = CLAMP_LF(segmentation_.abs_delta ? feature_data |
- : level + feature_data); |
+ segmentation.FeatureData(i, Vp9SegmentationParams::SEG_LVL_ALT_LF); |
+ level = ClampLf(segmentation.abs_or_delta_update ? feature_data |
+ : level + feature_data); |
} |
- if (!loop_filter_.mode_ref_delta_enabled) { |
- memset(loop_filter_.lvl[i], level, sizeof(loop_filter_.lvl[i])); |
+ if (!loop_filter.delta_enabled) { |
+ memset(loop_filter.lvl[i], level, sizeof(loop_filter.lvl[i])); |
} else { |
- loop_filter_.lvl[i][Vp9LoopFilter::VP9_FRAME_INTRA][0] = CLAMP_LF( |
+ loop_filter.lvl[i][Vp9FrameType::VP9_FRAME_INTRA][0] = ClampLf( |
level + |
- loop_filter_.ref_deltas[Vp9LoopFilter::VP9_FRAME_INTRA] * scale); |
- loop_filter_.lvl[i][Vp9LoopFilter::VP9_FRAME_INTRA][1] = 0; |
- |
- for (size_t type = Vp9LoopFilter::VP9_FRAME_LAST; |
- type < Vp9LoopFilter::VP9_FRAME_MAX; ++type) { |
- for (size_t mode = 0; mode < Vp9LoopFilter::kNumModeDeltas; ++mode) { |
- loop_filter_.lvl[i][type][mode] = |
- CLAMP_LF(level + loop_filter_.ref_deltas[type] * scale + |
- loop_filter_.mode_deltas[mode] * scale); |
+ loop_filter.ref_deltas[Vp9FrameType::VP9_FRAME_INTRA] * scale); |
+ loop_filter.lvl[i][Vp9FrameType::VP9_FRAME_INTRA][1] = 0; |
+ |
+ for (size_t type = Vp9FrameType::VP9_FRAME_LAST; |
+ type < Vp9FrameType::VP9_FRAME_MAX; ++type) { |
+ for (size_t mode = 0; mode < Vp9LoopFilterParams::kNumModeDeltas; |
+ ++mode) { |
+ loop_filter.lvl[i][type][mode] = |
+ ClampLf(level + loop_filter.ref_deltas[type] * scale + |
+ loop_filter.mode_deltas[mode] * scale); |
} |
} |
} |
} |
} |
-#undef CLAMP_LF |
+ |
+void Vp9Parser::UpdateSlots(const Vp9FrameHeader* fhdr) { |
+ // 8.10 Reference frame update process |
+ for (size_t i = 0; i < kVp9NumRefFrames; i++) { |
+ if (fhdr->RefreshFlag(i)) { |
+ ReferenceSlot& ref = context_.ref_slots[i]; |
+ ref.initialized = true; |
+ |
+ ref.frame_width = fhdr->frame_width; |
+ ref.frame_height = fhdr->frame_height; |
+ ref.subsampling_x = fhdr->subsampling_x; |
+ ref.subsampling_y = fhdr->subsampling_y; |
+ ref.bit_depth = fhdr->bit_depth; |
+ |
+ ref.profile = fhdr->profile; |
+ ref.color_space = fhdr->color_space; |
+ } |
+ } |
+} |
} // namespace media |