mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
AV1 sequence header parsing
PiperOrigin-RevId: 719200478
This commit is contained in:
parent
a7a5d6e92b
commit
dddcdf1613
@ -15,6 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.container;
|
package androidx.media3.container;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.util.ParsableBitArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -111,6 +115,153 @@ public final class ObuParser {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** An AV1 Sequence header. */
|
||||||
|
public static final class SequenceHeader {
|
||||||
|
/** See {@code reduced_still_picture_header}. */
|
||||||
|
public final boolean reducedStillPictureHeader;
|
||||||
|
|
||||||
|
/** See {@code decoder_model_info_present_flag}. */
|
||||||
|
public final boolean decoderModelInfoPresentFlag;
|
||||||
|
|
||||||
|
/** See {@code frame_id_numbers_present_flag}. */
|
||||||
|
public final boolean frameIdNumbersPresentFlag;
|
||||||
|
|
||||||
|
/** See {@code seq_force_screen_content_tools}. */
|
||||||
|
public final boolean seqForceScreenContentTools;
|
||||||
|
|
||||||
|
/** See {@code seq_force_integer_mv}. */
|
||||||
|
public final boolean seqForceIntegerMv;
|
||||||
|
|
||||||
|
/** See {@code OrderHintBits}. */
|
||||||
|
public final int orderHintBits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link SequenceHeader} parsed from the input {@link #OBU_SEQUENCE_HEADER}.
|
||||||
|
*
|
||||||
|
* <p>Returns {@code null} if the AV1 bitstream is not yet supported.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static SequenceHeader parse(Obu obu) {
|
||||||
|
try {
|
||||||
|
return new SequenceHeader(obu);
|
||||||
|
} catch (NotYetImplementedException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parses a {@link #OBU_SEQUENCE_HEADER} and creates an instance. */
|
||||||
|
private SequenceHeader(Obu obu) throws NotYetImplementedException {
|
||||||
|
checkArgument(obu.type == OBU_SEQUENCE_HEADER);
|
||||||
|
byte[] data = new byte[obu.payload.remaining()];
|
||||||
|
// Do not modify obu.payload as we read.
|
||||||
|
obu.payload.asReadOnlyBuffer().get(data);
|
||||||
|
ParsableBitArray obuData = new ParsableBitArray(data);
|
||||||
|
obuData.skipBits(4); // seq_profile and still_picture
|
||||||
|
reducedStillPictureHeader = obuData.readBit();
|
||||||
|
throwWhenFeatureRequired(reducedStillPictureHeader);
|
||||||
|
boolean timingInfoPresentFlag = obuData.readBit();
|
||||||
|
if (timingInfoPresentFlag) {
|
||||||
|
skipTimingInfo(obuData);
|
||||||
|
decoderModelInfoPresentFlag = obuData.readBit();
|
||||||
|
if (decoderModelInfoPresentFlag) {
|
||||||
|
// skip decoder_model_info()
|
||||||
|
obuData.skipBits(47);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decoderModelInfoPresentFlag = false;
|
||||||
|
}
|
||||||
|
boolean initialDisplayDelayPresentFlag = obuData.readBit();
|
||||||
|
int operatingPointsCntMinus1 = obuData.readBits(5);
|
||||||
|
for (int i = 0; i <= operatingPointsCntMinus1; i++) {
|
||||||
|
obuData.skipBits(12); // operating_point_idc[ i ]
|
||||||
|
int seqLevelIdx = obuData.readBits(5);
|
||||||
|
if (seqLevelIdx > 7) {
|
||||||
|
obuData.skipBit(); // seq_tier[ i ]
|
||||||
|
}
|
||||||
|
throwWhenFeatureRequired(decoderModelInfoPresentFlag);
|
||||||
|
if (initialDisplayDelayPresentFlag) {
|
||||||
|
boolean initialDisplayDelayPresentForThisOpFlag = obuData.readBit();
|
||||||
|
if (initialDisplayDelayPresentForThisOpFlag) {
|
||||||
|
obuData.skipBits(4); // initial_display_delay_minus_1[ i ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int frameWidthBitsMinus1 = obuData.readBits(4);
|
||||||
|
int frameHeightBitsMinus1 = obuData.readBits(4);
|
||||||
|
obuData.skipBits(frameWidthBitsMinus1 + 1); // max_frame_width_minus_1
|
||||||
|
obuData.skipBits(frameHeightBitsMinus1 + 1); // max_frame_height_minus_1
|
||||||
|
frameIdNumbersPresentFlag = obuData.readBit();
|
||||||
|
throwWhenFeatureRequired(frameIdNumbersPresentFlag);
|
||||||
|
// use_128x128_superblock, enable_filter_intra, and enable_intra_edge_filter
|
||||||
|
obuData.skipBits(3);
|
||||||
|
// enable_interintra_compound, enable_masked_compound, enable_warped_motion, and
|
||||||
|
// enable_dual_filter
|
||||||
|
obuData.skipBits(4);
|
||||||
|
boolean enableOrderHint = obuData.readBit();
|
||||||
|
if (enableOrderHint) {
|
||||||
|
obuData.skipBits(2); // enable_jnt_comp and enable_ref_frame_mvs
|
||||||
|
}
|
||||||
|
boolean seqChooseScreenContentTools = obuData.readBit();
|
||||||
|
if (seqChooseScreenContentTools) {
|
||||||
|
seqForceScreenContentTools = true;
|
||||||
|
} else {
|
||||||
|
seqForceScreenContentTools = obuData.readBit();
|
||||||
|
}
|
||||||
|
if (seqForceScreenContentTools) {
|
||||||
|
boolean seqChooseIntegerMv = obuData.readBit();
|
||||||
|
if (seqChooseIntegerMv) {
|
||||||
|
seqForceIntegerMv = true;
|
||||||
|
} else {
|
||||||
|
seqForceIntegerMv = obuData.readBit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
seqForceIntegerMv = true;
|
||||||
|
}
|
||||||
|
if (enableOrderHint) {
|
||||||
|
int orderHintBitsMinus1 = obuData.readBits(3);
|
||||||
|
orderHintBits = orderHintBitsMinus1 + 1;
|
||||||
|
} else {
|
||||||
|
orderHintBits = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Advances the bit array by skipping the {@code timing_info()} syntax element. */
|
||||||
|
private static void skipTimingInfo(ParsableBitArray parsableBitArray) {
|
||||||
|
parsableBitArray.skipBits(64); // num_units_in_display_tick and time_scale
|
||||||
|
boolean equalPictureInterval = parsableBitArray.readBit();
|
||||||
|
if (equalPictureInterval) {
|
||||||
|
skipUvlc(parsableBitArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Advances the bit array by skipping the {@code uvlc()} process. */
|
||||||
|
private static void skipUvlc(ParsableBitArray parsableBitArray) {
|
||||||
|
int leadingZeros = 0;
|
||||||
|
while (true) {
|
||||||
|
boolean done = parsableBitArray.readBit();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
leadingZeros++;
|
||||||
|
}
|
||||||
|
// 32 or more leading zeros returns (1 << 32) - 1 from the uvlc() process without reading more
|
||||||
|
// bits.
|
||||||
|
if (leadingZeros < 32) {
|
||||||
|
parsableBitArray.skipBits(leadingZeros);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Full AV1 bitstream parsing is not yet implemented. */
|
||||||
|
private static void throwWhenFeatureRequired(boolean expression)
|
||||||
|
throws NotYetImplementedException {
|
||||||
|
if (expression) {
|
||||||
|
throw new NotYetImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NotYetImplementedException extends Exception {}
|
||||||
|
|
||||||
private ObuParser() {
|
private ObuParser() {
|
||||||
// Prevent instantiation.
|
// Prevent instantiation.
|
||||||
}
|
}
|
||||||
|
@ -68,4 +68,18 @@ public class ObuParserTest {
|
|||||||
assertThat(obuList.get(2).type).isEqualTo(OBU_PADDING);
|
assertThat(obuList.get(2).type).isEqualTo(OBU_PADDING);
|
||||||
assertThat(obuList.get(2).payload.remaining()).isEqualTo(3);
|
assertThat(obuList.get(2).payload.remaining()).isEqualTo(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sequenceHeader_parses() {
|
||||||
|
ObuParser.Obu sequenceHeaderObu = ObuParser.split(SEQUENCE_HEADER_AND_FRAME).get(0);
|
||||||
|
|
||||||
|
ObuParser.SequenceHeader sequenceHeader = ObuParser.SequenceHeader.parse(sequenceHeaderObu);
|
||||||
|
|
||||||
|
assertThat(sequenceHeader.reducedStillPictureHeader).isFalse();
|
||||||
|
assertThat(sequenceHeader.decoderModelInfoPresentFlag).isFalse();
|
||||||
|
assertThat(sequenceHeader.frameIdNumbersPresentFlag).isFalse();
|
||||||
|
assertThat(sequenceHeader.seqForceScreenContentTools).isTrue();
|
||||||
|
assertThat(sequenceHeader.seqForceIntegerMv).isTrue();
|
||||||
|
assertThat(sequenceHeader.orderHintBits).isEqualTo(7);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user