16
16
package androidx .media3 .container ;
17
17
18
18
import static androidx .media3 .common .util .Assertions .checkArgument ;
19
+ import static java .lang .Math .min ;
19
20
20
21
import androidx .annotation .Nullable ;
21
22
import androidx .media3 .common .util .ParsableBitArray ;
@@ -136,9 +137,10 @@ public static final class SequenceHeader {
136
137
public final int orderHintBits ;
137
138
138
139
/**
139
- * Returns a {@link SequenceHeader} parsed from the input {@link #OBU_SEQUENCE_HEADER}.
140
+ * Returns a {@link SequenceHeader} parsed from the input OBU, or {@code null} if the AV1
141
+ * bitstream is not yet supported.
140
142
*
141
- * Returns {@code null} if the AV1 bitstream is not yet supported
.
143
+ * @param obu The input OBU with type {@link #OBU_SEQUENCE_HEADER} .
142
144
*/
143
145
@ Nullable
144
146
public static SequenceHeader parse (Obu obu ) {
@@ -153,7 +155,7 @@ public static SequenceHeader parse(Obu obu) {
153
155
private SequenceHeader (Obu obu ) throws NotYetImplementedException {
154
156
checkArgument (obu .type == OBU_SEQUENCE_HEADER );
155
157
byte [] data = new byte [obu .payload .remaining ()];
156
- // Do not modify obu.payload as we read .
158
+ // Do not modify obu.payload while reading it .
157
159
obu .payload .asReadOnlyBuffer ().get (data );
158
160
ParsableBitArray obuData = new ParsableBitArray (data );
159
161
obuData .skipBits (4 ); // seq_profile and still_picture
@@ -252,6 +254,94 @@ private static void skipUvlc(ParsableBitArray parsableBitArray) {
252
254
}
253
255
}
254
256
257
+ /** An AV1 Frame Header. */
258
+ public static final class FrameHeader {
259
+ private static final int PROBE_BYTES = 4 ;
260
+
261
+ private static final int FRAME_TYPE_KEY_FRAME = 0 ;
262
+ private static final int FRAME_TYPE_INTRA_ONLY_FRAME = 2 ;
263
+ private static final int FRAME_TYPE_SWITCH_FRAME = 3 ;
264
+
265
+ private final boolean isDependedOn ;
266
+
267
+ /** Returns whether the frame header is depended on by subsequent frames. */
268
+ public boolean isDependedOn () {
269
+ return isDependedOn ;
270
+ }
271
+
272
+ /**
273
+ * Returns a {@link FrameHeader} parsed from the input OBU, or {@code null} if the AV1 bitstream
274
+ * is not yet supported.
275
+ *
276
+ * @param sequenceHeader The most recent sequence header before the frame header.
277
+ * @param obu The input OBU with type {@link #OBU_FRAME} or {@link #OBU_FRAME_HEADER}.
278
+ */
279
+ @ Nullable
280
+ public static FrameHeader parse (SequenceHeader sequenceHeader , Obu obu ) {
281
+ try {
282
+ return new FrameHeader (sequenceHeader , obu );
283
+ } catch (NotYetImplementedException ignored ) {
284
+ return null ;
285
+ }
286
+ }
287
+
288
+ private FrameHeader (SequenceHeader sequenceHeader , Obu obu ) throws NotYetImplementedException {
289
+ checkArgument (obu .type == OBU_FRAME || obu .type == OBU_FRAME_HEADER );
290
+ byte [] bytes = new byte [min (PROBE_BYTES , obu .payload .remaining ())];
291
+ // Do not modify obu.payload while reading it.
292
+ obu .payload .asReadOnlyBuffer ().get (bytes );
293
+ ParsableBitArray obuData = new ParsableBitArray (bytes );
294
+ throwWhenFeatureRequired (sequenceHeader .reducedStillPictureHeader );
295
+ boolean showExistingFrame = obuData .readBit ();
296
+ if (showExistingFrame ) {
297
+ // TODO: b/391108133 - Treat showExistingFrame as depended on. The picture was already
298
+ // decoded and the player may not save a lot of resources by rendering. Check if this
299
+ // assumption is correct!
300
+ isDependedOn = true ;
301
+ return ;
302
+ }
303
+ int frameType = obuData .readBits (2 );
304
+ boolean showFrame = obuData .readBit ();
305
+ throwWhenFeatureRequired (sequenceHeader .decoderModelInfoPresentFlag );
306
+ if (!showFrame ) {
307
+ // show_frame equal to 0 specifies that this frame should not be immediately output.
308
+ // If a frame is output later, then it is depended on.
309
+ isDependedOn = true ;
310
+ return ;
311
+ }
312
+ boolean errorResilientMode ;
313
+ if (frameType == FRAME_TYPE_SWITCH_FRAME || (frameType == FRAME_TYPE_KEY_FRAME )) {
314
+ errorResilientMode = true ;
315
+ } else {
316
+ errorResilientMode = obuData .readBit ();
317
+ }
318
+ obuData .skipBit (); // disable_cdf_update
319
+ throwWhenFeatureRequired (!sequenceHeader .seqForceScreenContentTools );
320
+ boolean allowScreenContentTools = obuData .readBit ();
321
+ if (allowScreenContentTools ) {
322
+ throwWhenFeatureRequired (!sequenceHeader .seqForceIntegerMv );
323
+ obuData .skipBit (); // force_integer_mv
324
+ }
325
+ throwWhenFeatureRequired (sequenceHeader .frameIdNumbersPresentFlag );
326
+ if (frameType != FRAME_TYPE_SWITCH_FRAME ) {
327
+ obuData .skipBit (); // frame_size_override_flag
328
+ }
329
+ obuData .skipBits (sequenceHeader .orderHintBits ); // order_hint
330
+ if (frameType != FRAME_TYPE_INTRA_ONLY_FRAME
331
+ && frameType != FRAME_TYPE_KEY_FRAME
332
+ && !errorResilientMode ) {
333
+ obuData .skipBits (3 ); // primary_ref_frame
334
+ }
335
+ int refreshFrameFlags ;
336
+ if (frameType == FRAME_TYPE_SWITCH_FRAME || (frameType == FRAME_TYPE_KEY_FRAME )) {
337
+ refreshFrameFlags = (1 << 8 ) - 1 ;
338
+ } else {
339
+ refreshFrameFlags = obuData .readBits (8 );
340
+ }
341
+ isDependedOn = refreshFrameFlags != 0 ;
342
+ }
343
+ }
344
+
255
345
/** Full AV1 bitstream parsing is not yet implemented. */
256
346
private static void throwWhenFeatureRequired (boolean expression )
257
347
throws NotYetImplementedException {
0 commit comments