external/libgav1: backport sequence header fixes | DO NOT MERGE
cl/289966078: Move initial_display_delay out of OperatingParamet
cl/290784565: Handle a change of sequence header parameters.
cl/291222461: Disallow change of sequence header during a frame.
Together these provide additional coverage for the previous subsampling
fix [1], additionally adding coverage for bitdepth changes mid-frame. As
a side effect they also prevent the file from b/147491764 from
progressing to the original point of failure [2].
[1] ce9b99c external/libgav1: backport prediction subsampling fix
[2] 22ec187 external/libgav1: backport prediction border extension fix
Bug: b/147491773,b/147491764
Change-Id: I3f949e17c12990fc313522d9b2633509ee4e59a2
Test: upstream test vectors, repro test cases
(cherry picked from commit 760fb0d6f0766ca38b19ef57f8f9229aaeccca07)
diff --git a/README.version b/README.version
index 65e27d6..8a3537a 100644
--- a/README.version
+++ b/README.version
@@ -5,3 +5,6 @@
- ab3390a external/libgav1,cosmetics: add license headers
- backport cl/281117442: Fully use the frame border for reference block.
- backport cl/289984918: convolve: Use the correct subsampling for ref frames
+- backport cl/289966078: Move initial_display_delay out of OperatingParamet
+- backport cl/290784565: Handle a change of sequence header parameters.
+- backport cl/291222461: Disallow change of sequence header during a frame.
diff --git a/libgav1/src/decoder_impl.cc b/libgav1/src/decoder_impl.cc
index 9448247..11f2682 100644
--- a/libgav1/src/decoder_impl.cc
+++ b/libgav1/src/decoder_impl.cc
@@ -50,6 +50,27 @@
} // namespace
+void DecoderState::UpdateReferenceFrames(int refresh_frame_flags) {
+ for (int ref_index = 0, mask = refresh_frame_flags; mask != 0;
+ ++ref_index, mask >>= 1) {
+ if ((mask & 1) != 0) {
+ reference_valid[ref_index] = true;
+ reference_frame_id[ref_index] = current_frame_id;
+ reference_frame[ref_index] = current_frame;
+ reference_order_hint[ref_index] = order_hint;
+ }
+ }
+}
+
+void DecoderState::ClearReferenceFrames() {
+ reference_valid = {};
+ reference_frame_id = {};
+ reference_order_hint = {};
+ for (int ref_index = 0; ref_index < kNumReferenceFrameTypes; ++ref_index) {
+ reference_frame[ref_index] = nullptr;
+ }
+}
+
// static
StatusCode DecoderImpl::Create(const DecoderSettings* settings,
std::unique_ptr<DecoderImpl>* output) {
@@ -173,7 +194,7 @@
return status;
}
}
- UpdateReferenceFrames(obu->frame_header().refresh_frame_flags);
+ state_.UpdateReferenceFrames(obu->frame_header().refresh_frame_flags);
if (obu->frame_header().show_frame ||
obu->frame_header().show_existing_frame) {
if (displayable_frame != nullptr) {
@@ -195,11 +216,11 @@
obu->frame_header().refresh_frame_flags == 0) {
// If show_existing_frame is true, then the current frame is a
// previously saved reference frame. If refresh_frame_flags is
- // nonzero, then the UpdateReferenceFrames() call above has saved the
- // current frame as a reference frame. Therefore, if both of these
- // conditions are false, then the current frame is not saved as a
- // reference frame. displayable_frame should hold the only reference
- // to the current frame.
+ // nonzero, then the state_.UpdateReferenceFrames() call above has
+ // saved the current frame as a reference frame. Therefore, if both
+ // of these conditions are false, then the current frame is not
+ // saved as a reference frame. displayable_frame should hold the
+ // only reference to the current frame.
assert(displayable_frame.use_count() == 1);
// Add film grain noise in place.
film_grain_frame = displayable_frame;
@@ -665,16 +686,4 @@
}
}
-void DecoderImpl::UpdateReferenceFrames(int refresh_frame_flags) {
- for (int ref_index = 0, mask = refresh_frame_flags; mask != 0;
- ++ref_index, mask >>= 1) {
- if ((mask & 1) != 0) {
- state_.reference_valid[ref_index] = true;
- state_.reference_frame_id[ref_index] = state_.current_frame_id;
- state_.reference_frame[ref_index] = state_.current_frame;
- state_.reference_order_hint[ref_index] = state_.order_hint;
- }
- }
-}
-
} // namespace libgav1
diff --git a/libgav1/src/decoder_impl.h b/libgav1/src/decoder_impl.h
index 4e4c6b5..e58a0da 100644
--- a/libgav1/src/decoder_impl.h
+++ b/libgav1/src/decoder_impl.h
@@ -41,6 +41,13 @@
};
struct DecoderState {
+ // Section 7.20. Updates frames in the reference_frame array with
+ // current_frame, based on the refresh_frame_flags bitmask.
+ void UpdateReferenceFrames(int refresh_frame_flags);
+
+ // Clears all the reference frames.
+ void ClearReferenceFrames();
+
ObuSequenceHeader sequence_header = {};
// If true, sequence_header is valid.
bool has_sequence_header = false;
@@ -64,8 +71,8 @@
//
// NOTE: When show_existing_frame is false, it is often more convenient to
// just use the order_hint field of the frame header as OrderHint. So this
- // field is mainly used to update the state_.reference_order_hint array in
- // DecoderImpl::UpdateReferenceFrames().
+ // field is mainly used to update the reference_order_hint array in
+ // UpdateReferenceFrames().
uint8_t order_hint = 0;
// reference_frame_sign_bias[i] (a boolean) specifies the intended direction
// of the motion vector in time for each reference frame.
@@ -118,9 +125,6 @@
// is handled in Tile::DecodeBlock().
void SetCurrentFrameSegmentationMap(const ObuFrameHeader& frame_header,
const SegmentationMap* prev_segment_ids);
- // Section 7.20. Updates frames in the state_.reference_frame array with
- // state_.current_frame, based on the refresh_frame_flags bitmask.
- void UpdateReferenceFrames(int refresh_frame_flags);
Queue<EncodedFrame> encoded_frames_;
DecoderState state_;
diff --git a/libgav1/src/obu_parser.cc b/libgav1/src/obu_parser.cc
index 644f1a5..ca251fa 100644
--- a/libgav1/src/obu_parser.cc
+++ b/libgav1/src/obu_parser.cc
@@ -4,6 +4,7 @@
#include <array>
#include <cassert>
#include <climits>
+#include <cstddef>
#include <cstdint>
#include <cstring>
@@ -60,6 +61,15 @@
} // namespace
+bool ObuSequenceHeader::ParametersChanged(const ObuSequenceHeader& old) const {
+ // Note that the operating_parameters field is not compared per Section 7.5:
+ // Within a particular coded video sequence, the contents of
+ // sequence_header_obu must be bit-identical each time the sequence header
+ // appears except for the contents of operating_parameters_info.
+ return memcmp(this, &old,
+ offsetof(ObuSequenceHeader, operating_parameters)) != 0;
+}
+
// Macros to avoid repeated error checks in the parser code.
#define OBU_LOG_AND_RETURN_FALSE \
do { \
@@ -247,7 +257,7 @@
return true;
}
-bool ObuParser::ParseSequenceHeader() {
+bool ObuParser::ParseSequenceHeader(bool seen_frame_header) {
ObuSequenceHeader sequence_header = {};
int64_t scratch;
OBU_READ_LITERAL_OR_FAIL(3);
@@ -307,8 +317,7 @@
OBU_READ_BIT_OR_FAIL;
if (static_cast<bool>(scratch)) {
OBU_READ_LITERAL_OR_FAIL(4);
- sequence_header.operating_parameters.initial_display_delay[i] =
- 1 + scratch;
+ sequence_header.initial_display_delay[i] = 1 + scratch;
}
}
}
@@ -400,7 +409,17 @@
if (!ParseColorConfig(&sequence_header)) return false;
OBU_READ_BIT_OR_FAIL;
sequence_header.film_grain_params_present = static_cast<bool>(scratch);
- // TODO(wtc): Compare new sequence header with old sequence header.
+ // Compare new sequence header with old sequence header.
+ if (has_sequence_header_ &&
+ sequence_header.ParametersChanged(sequence_header_)) {
+ // Between the frame header OBU and the last tile group OBU of the frame,
+ // do not allow the sequence header to change.
+ if (seen_frame_header) {
+ LIBGAV1_DLOG(ERROR, "Sequence header changed in the middle of a frame.");
+ return false;
+ }
+ decoder_state_.ClearReferenceFrames();
+ }
sequence_header_ = sequence_header;
has_sequence_header_ = true;
// Section 6.4.1: It is a requirement of bitstream conformance that if
@@ -1944,6 +1963,20 @@
}
}
}
+ // Validate frame_header_.primary_reference_frame.
+ if (frame_header_.primary_reference_frame != kPrimaryReferenceNone) {
+ const int index =
+ frame_header_
+ .reference_frame_index[frame_header_.primary_reference_frame];
+ if (decoder_state_.reference_frame[index] == nullptr) {
+ LIBGAV1_DLOG(ERROR,
+ "primary_ref_frame is %d but ref_frame_idx[%d] (%d) is "
+ "not a decoded frame.",
+ frame_header_.primary_reference_frame,
+ frame_header_.primary_reference_frame, index);
+ return false;
+ }
+ }
if (frame_header_.frame_size_override_flag &&
!frame_header_.error_resilient_mode) {
// Section 5.9.7.
@@ -2296,7 +2329,7 @@
case kObuTemporalDelimiter:
break;
case kObuSequenceHeader:
- if (!ParseSequenceHeader()) {
+ if (!ParseSequenceHeader(seen_frame_header)) {
LIBGAV1_DLOG(ERROR, "Failed to parse SequenceHeader OBU.");
return false;
}
diff --git a/libgav1/src/obu_parser.h b/libgav1/src/obu_parser.h
index 9d972b1..e973433 100644
--- a/libgav1/src/obu_parser.h
+++ b/libgav1/src/obu_parser.h
@@ -5,6 +5,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
+#include <type_traits>
#include "src/decoder_buffer.h"
#include "src/dsp/common.h"
@@ -166,13 +167,25 @@
};
struct OperatingParameters {
- uint8_t initial_display_delay[kMaxOperatingPoints];
uint32_t decoder_buffer_delay[kMaxOperatingPoints];
uint32_t encoder_buffer_delay[kMaxOperatingPoints];
bool low_delay_mode_flag[kMaxOperatingPoints];
};
struct ObuSequenceHeader {
+ // Section 7.5:
+ // Within a particular coded video sequence, the contents of
+ // sequence_header_obu must be bit-identical each time the sequence header
+ // appears except for the contents of operating_parameters_info. A new
+ // coded video sequence is required if the sequence header parameters
+ // change.
+ //
+ // IMPORTANT: ParametersChanged() is implemented with a memcmp() call. For
+ // this to work, this object and the |old| object must be initialized with
+ // an empty brace-enclosed list, which initializes any padding to zero bits.
+ // See https://en.cppreference.com/w/cpp/language/zero_initialization.
+ bool ParametersChanged(const ObuSequenceHeader& old) const;
+
BitstreamProfile profile;
bool still_picture;
bool reduced_still_picture_header;
@@ -213,11 +226,30 @@
bool decoder_model_info_present_flag;
DecoderModelInfo decoder_model_info;
bool decoder_model_present_for_operating_point[kMaxOperatingPoints];
- OperatingParameters operating_parameters;
bool initial_display_delay_present_flag;
+ uint8_t initial_display_delay[kMaxOperatingPoints];
bool film_grain_params_present;
+
+ // IMPORTANT: the operating_parameters member must be at the end of the
+ // struct so that ParametersChanged() can be implemented with a memcmp()
+ // call.
+ OperatingParameters operating_parameters;
};
+// Verify it is safe to use offsetof with ObuSequenceHeader and to use memcmp
+// to compare two ObuSequenceHeader objects.
+static_assert(std::is_standard_layout<ObuSequenceHeader>::value, "");
+// Verify operating_parameters is the last member of ObuSequenceHeader. The
+// second assertion assumes that ObuSequenceHeader has no padding after the
+// operating_parameters field. The first assertion is a sufficient condition
+// for ObuSequenceHeader to have no padding after the operating_parameters
+// field.
+static_assert(alignof(ObuSequenceHeader) == alignof(OperatingParameters), "");
+static_assert(sizeof(ObuSequenceHeader) ==
+ offsetof(ObuSequenceHeader, operating_parameters) +
+ sizeof(OperatingParameters),
+ "");
+
// Loop filter parameters:
//
// If level[0] and level[1] are both equal to 0, the loop filter process is
@@ -477,7 +509,7 @@
bool ParseDecoderModelInfo(ObuSequenceHeader* sequence_header); // 5.5.4.
bool ParseOperatingParameters(ObuSequenceHeader* sequence_header,
int index); // 5.5.5.
- bool ParseSequenceHeader(); // 5.5.1.
+ bool ParseSequenceHeader(bool seen_frame_header); // 5.5.1.
bool ParseFrameParameters(); // 5.9.2, 5.9.7 and 5.9.10.
void MarkInvalidReferenceFrames(); // 5.9.4.
bool ParseFrameSizeAndRenderSize(); // 5.9.5 and 5.9.6.