Partial nullness annotations for AtomParsers

PiperOrigin-RevId: 284558886
This commit is contained in:
olly 2019-12-09 16:32:22 +00:00 committed by Oliver Woodman
parent 4b281cec3b
commit 539a1ac2e2

View File

@ -40,6 +40,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
/** Utility methods for parsing MP4 format atom payloads according to ISO 14496-12. */ /** Utility methods for parsing MP4 format atom payloads according to ISO 14496-12. */
@SuppressWarnings({"ConstantField"}) @SuppressWarnings({"ConstantField"})
@ -85,15 +86,21 @@ import java.util.List;
* *
* @param trak Atom to decode. * @param trak Atom to decode.
* @param mvhd Movie header atom, used to get the timescale. * @param mvhd Movie header atom, used to get the timescale.
* @param duration The duration in units of the timescale declared in the mvhd atom, or * @param duration The duration in units of the timescale declared in the mvhd atom, or {@link
* {@link C#TIME_UNSET} if the duration should be parsed from the tkhd atom. * C#TIME_UNSET} if the duration should be parsed from the tkhd atom.
* @param drmInitData {@link DrmInitData} to be included in the format. * @param drmInitData {@link DrmInitData} to be included in the format, or {@code null}.
* @param ignoreEditLists Whether to ignore any edit lists in the trak box. * @param ignoreEditLists Whether to ignore any edit lists in the trak box.
* @param isQuickTime True for QuickTime media. False otherwise. * @param isQuickTime True for QuickTime media. False otherwise.
* @return A {@link Track} instance, or {@code null} if the track's type isn't supported. * @return A {@link Track} instance, or {@code null} if the track's type isn't supported.
*/ */
public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, long duration, @Nullable
DrmInitData drmInitData, boolean ignoreEditLists, boolean isQuickTime) public static Track parseTrak(
Atom.ContainerAtom trak,
Atom.LeafAtom mvhd,
long duration,
@Nullable DrmInitData drmInitData,
boolean ignoreEditLists,
boolean isQuickTime)
throws ParserException { throws ParserException {
Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia); Atom.ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia);
int trackType = getTrackTypeForHdlr(parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data)); int trackType = getTrackTypeForHdlr(parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data));
@ -121,9 +128,12 @@ import java.util.List;
long[] editListDurations = null; long[] editListDurations = null;
long[] editListMediaTimes = null; long[] editListMediaTimes = null;
if (!ignoreEditLists) { if (!ignoreEditLists) {
@Nullable
Pair<long[], long[]> edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts)); Pair<long[], long[]> edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts));
editListDurations = edtsData.first; if (edtsData != null) {
editListMediaTimes = edtsData.second; editListDurations = edtsData.first;
editListMediaTimes = edtsData.second;
}
} }
return stsdData.format == null ? null return stsdData.format == null ? null
: new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs, : new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs,
@ -736,19 +746,25 @@ import java.util.List;
* @param trackId The track's identifier in its container. * @param trackId The track's identifier in its container.
* @param rotationDegrees The rotation of the track in degrees. * @param rotationDegrees The rotation of the track in degrees.
* @param language The language of the track. * @param language The language of the track.
* @param drmInitData {@link DrmInitData} to be included in the format. * @param drmInitData {@link DrmInitData} to be included in the format, or {@code null}.
* @param isQuickTime True for QuickTime media. False otherwise. * @param isQuickTime True for QuickTime media. False otherwise.
* @return An object containing the parsed data. * @return An object containing the parsed data.
*/ */
private static StsdData parseStsd(ParsableByteArray stsd, int trackId, int rotationDegrees, private static StsdData parseStsd(
String language, DrmInitData drmInitData, boolean isQuickTime) throws ParserException { ParsableByteArray stsd,
int trackId,
int rotationDegrees,
String language,
@Nullable DrmInitData drmInitData,
boolean isQuickTime)
throws ParserException {
stsd.setPosition(Atom.FULL_HEADER_SIZE); stsd.setPosition(Atom.FULL_HEADER_SIZE);
int numberOfEntries = stsd.readInt(); int numberOfEntries = stsd.readInt();
StsdData out = new StsdData(numberOfEntries); StsdData out = new StsdData(numberOfEntries);
for (int i = 0; i < numberOfEntries; i++) { for (int i = 0; i < numberOfEntries; i++) {
int childStartPosition = stsd.getPosition(); int childStartPosition = stsd.getPosition();
int childAtomSize = stsd.readInt(); int childAtomSize = stsd.readInt();
Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); Assertions.checkState(childAtomSize > 0, "childAtomSize should be positive");
int childAtomType = stsd.readInt(); int childAtomType = stsd.readInt();
if (childAtomType == Atom.TYPE_avc1 if (childAtomType == Atom.TYPE_avc1
|| childAtomType == Atom.TYPE_avc3 || childAtomType == Atom.TYPE_avc3
@ -846,9 +862,17 @@ import java.util.List;
initializationData); initializationData);
} }
private static void parseVideoSampleEntry(ParsableByteArray parent, int atomType, int position, private static void parseVideoSampleEntry(
int size, int trackId, int rotationDegrees, DrmInitData drmInitData, StsdData out, ParsableByteArray parent,
int entryIndex) throws ParserException { int atomType,
int position,
int size,
int trackId,
int rotationDegrees,
@Nullable DrmInitData drmInitData,
StsdData out,
int entryIndex)
throws ParserException {
parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE); parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE);
parent.skipBytes(16); parent.skipBytes(16);
@ -860,8 +884,9 @@ import java.util.List;
int childPosition = parent.getPosition(); int childPosition = parent.getPosition();
if (atomType == Atom.TYPE_encv) { if (atomType == Atom.TYPE_encv) {
Pair<Integer, TrackEncryptionBox> sampleEntryEncryptionData = parseSampleEntryEncryptionData( @Nullable
parent, position, size); Pair<Integer, TrackEncryptionBox> sampleEntryEncryptionData =
parseSampleEntryEncryptionData(parent, position, size);
if (sampleEntryEncryptionData != null) { if (sampleEntryEncryptionData != null) {
atomType = sampleEntryEncryptionData.first; atomType = sampleEntryEncryptionData.first;
drmInitData = drmInitData == null ? null drmInitData = drmInitData == null ? null
@ -875,10 +900,10 @@ import java.util.List;
// drmInitData = null; // drmInitData = null;
// } // }
List<byte[]> initializationData = null; @Nullable List<byte[]> initializationData = null;
String mimeType = null; @Nullable String mimeType = null;
String codecs = null; @Nullable String codecs = null;
byte[] projectionData = null; @Nullable byte[] projectionData = null;
@C.StereoMode @C.StereoMode
int stereoMode = Format.NO_VALUE; int stereoMode = Format.NO_VALUE;
while (childPosition - position < size) { while (childPosition - position < size) {
@ -889,7 +914,7 @@ import java.util.List;
// Handle optional terminating four zero bytes in MOV files. // Handle optional terminating four zero bytes in MOV files.
break; break;
} }
Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); Assertions.checkState(childAtomSize > 0, "childAtomSize should be positive");
int childAtomType = parent.readInt(); int childAtomType = parent.readInt();
if (childAtomType == Atom.TYPE_avcC) { if (childAtomType == Atom.TYPE_avcC) {
Assertions.checkState(mimeType == null); Assertions.checkState(mimeType == null);
@ -925,10 +950,13 @@ import java.util.List;
mimeType = MimeTypes.VIDEO_H263; mimeType = MimeTypes.VIDEO_H263;
} else if (childAtomType == Atom.TYPE_esds) { } else if (childAtomType == Atom.TYPE_esds) {
Assertions.checkState(mimeType == null); Assertions.checkState(mimeType == null);
Pair<String, byte[]> mimeTypeAndInitializationData = Pair<@NullableType String, byte @NullableType []> mimeTypeAndInitializationDataBytes =
parseEsdsFromParent(parent, childStartPosition); parseEsdsFromParent(parent, childStartPosition);
mimeType = mimeTypeAndInitializationData.first; mimeType = mimeTypeAndInitializationDataBytes.first;
initializationData = Collections.singletonList(mimeTypeAndInitializationData.second); @Nullable byte[] initializationDataBytes = mimeTypeAndInitializationDataBytes.second;
if (initializationDataBytes != null) {
initializationData = Collections.singletonList(initializationDataBytes);
}
} else if (childAtomType == Atom.TYPE_pasp) { } else if (childAtomType == Atom.TYPE_pasp) {
pixelWidthHeightRatio = parsePaspFromParent(parent, childStartPosition); pixelWidthHeightRatio = parsePaspFromParent(parent, childStartPosition);
pixelWidthHeightRatioFromPasp = true; pixelWidthHeightRatioFromPasp = true;
@ -988,13 +1016,14 @@ import java.util.List;
* Parses the edts atom (defined in 14496-12 subsection 8.6.5). * Parses the edts atom (defined in 14496-12 subsection 8.6.5).
* *
* @param edtsAtom edts (edit box) atom to decode. * @param edtsAtom edts (edit box) atom to decode.
* @return Pair of edit list durations and edit list media times, or a pair of nulls if they are * @return Pair of edit list durations and edit list media times, or {@code null} if they are not
* not present. * present.
*/ */
@Nullable
private static Pair<long[], long[]> parseEdts(Atom.ContainerAtom edtsAtom) { private static Pair<long[], long[]> parseEdts(Atom.ContainerAtom edtsAtom) {
Atom.LeafAtom elst; Atom.LeafAtom elst;
if (edtsAtom == null || (elst = edtsAtom.getLeafAtomOfType(Atom.TYPE_elst)) == null) { if (edtsAtom == null || (elst = edtsAtom.getLeafAtomOfType(Atom.TYPE_elst)) == null) {
return Pair.create(null, null); return null;
} }
ParsableByteArray elstData = elst.data; ParsableByteArray elstData = elst.data;
elstData.setPosition(Atom.HEADER_SIZE); elstData.setPosition(Atom.HEADER_SIZE);
@ -1024,9 +1053,18 @@ import java.util.List;
return (float) hSpacing / vSpacing; return (float) hSpacing / vSpacing;
} }
private static void parseAudioSampleEntry(ParsableByteArray parent, int atomType, int position, private static void parseAudioSampleEntry(
int size, int trackId, String language, boolean isQuickTime, DrmInitData drmInitData, ParsableByteArray parent,
StsdData out, int entryIndex) throws ParserException { int atomType,
int position,
int size,
int trackId,
String language,
boolean isQuickTime,
@Nullable DrmInitData drmInitData,
StsdData out,
int entryIndex)
throws ParserException {
parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE); parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE);
int quickTimeSoundDescriptionVersion = 0; int quickTimeSoundDescriptionVersion = 0;
@ -1064,8 +1102,9 @@ import java.util.List;
int childPosition = parent.getPosition(); int childPosition = parent.getPosition();
if (atomType == Atom.TYPE_enca) { if (atomType == Atom.TYPE_enca) {
Pair<Integer, TrackEncryptionBox> sampleEntryEncryptionData = parseSampleEntryEncryptionData( @Nullable
parent, position, size); Pair<Integer, TrackEncryptionBox> sampleEntryEncryptionData =
parseSampleEntryEncryptionData(parent, position, size);
if (sampleEntryEncryptionData != null) { if (sampleEntryEncryptionData != null) {
atomType = sampleEntryEncryptionData.first; atomType = sampleEntryEncryptionData.first;
drmInitData = drmInitData == null ? null drmInitData = drmInitData == null ? null
@ -1080,7 +1119,7 @@ import java.util.List;
// } // }
// If the atom type determines a MIME type, set it immediately. // If the atom type determines a MIME type, set it immediately.
String mimeType = null; @Nullable String mimeType = null;
if (atomType == Atom.TYPE_ac_3) { if (atomType == Atom.TYPE_ac_3) {
mimeType = MimeTypes.AUDIO_AC3; mimeType = MimeTypes.AUDIO_AC3;
} else if (atomType == Atom.TYPE_ec_3) { } else if (atomType == Atom.TYPE_ec_3) {
@ -1113,21 +1152,21 @@ import java.util.List;
mimeType = MimeTypes.AUDIO_FLAC; mimeType = MimeTypes.AUDIO_FLAC;
} }
byte[] initializationData = null; @Nullable byte[] initializationData = null;
while (childPosition - position < size) { while (childPosition - position < size) {
parent.setPosition(childPosition); parent.setPosition(childPosition);
int childAtomSize = parent.readInt(); int childAtomSize = parent.readInt();
Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); Assertions.checkState(childAtomSize > 0, "childAtomSize should be positive");
int childAtomType = parent.readInt(); int childAtomType = parent.readInt();
if (childAtomType == Atom.TYPE_esds || (isQuickTime && childAtomType == Atom.TYPE_wave)) { if (childAtomType == Atom.TYPE_esds || (isQuickTime && childAtomType == Atom.TYPE_wave)) {
int esdsAtomPosition = childAtomType == Atom.TYPE_esds ? childPosition int esdsAtomPosition = childAtomType == Atom.TYPE_esds ? childPosition
: findEsdsPosition(parent, childPosition, childAtomSize); : findEsdsPosition(parent, childPosition, childAtomSize);
if (esdsAtomPosition != C.POSITION_UNSET) { if (esdsAtomPosition != C.POSITION_UNSET) {
Pair<String, byte[]> mimeTypeAndInitializationData = Pair<@NullableType String, byte @NullableType []> mimeTypeAndInitializationData =
parseEsdsFromParent(parent, esdsAtomPosition); parseEsdsFromParent(parent, esdsAtomPosition);
mimeType = mimeTypeAndInitializationData.first; mimeType = mimeTypeAndInitializationData.first;
initializationData = mimeTypeAndInitializationData.second; initializationData = mimeTypeAndInitializationData.second;
if (MimeTypes.AUDIO_AAC.equals(mimeType)) { if (MimeTypes.AUDIO_AAC.equals(mimeType) && initializationData != null) {
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data, // Update sampleRate and channelCount from the AudioSpecificConfig initialization data,
// which is more reliable. See [Internal: b/10903778]. // which is more reliable. See [Internal: b/10903778].
Pair<Integer, Integer> audioSpecificConfig = Pair<Integer, Integer> audioSpecificConfig =
@ -1204,7 +1243,7 @@ import java.util.List;
while (childAtomPosition - position < size) { while (childAtomPosition - position < size) {
parent.setPosition(childAtomPosition); parent.setPosition(childAtomPosition);
int childAtomSize = parent.readInt(); int childAtomSize = parent.readInt();
Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); Assertions.checkState(childAtomSize > 0, "childAtomSize should be positive");
int childType = parent.readInt(); int childType = parent.readInt();
if (childType == Atom.TYPE_esds) { if (childType == Atom.TYPE_esds) {
return childAtomPosition; return childAtomPosition;
@ -1214,10 +1253,9 @@ import java.util.List;
return C.POSITION_UNSET; return C.POSITION_UNSET;
} }
/** /** Returns codec-specific initialization data contained in an esds box. */
* Returns codec-specific initialization data contained in an esds box. private static Pair<@NullableType String, byte @NullableType []> parseEsdsFromParent(
*/ ParsableByteArray parent, int position) {
private static Pair<String, byte[]> parseEsdsFromParent(ParsableByteArray parent, int position) {
parent.setPosition(position + Atom.HEADER_SIZE + 4); parent.setPosition(position + Atom.HEADER_SIZE + 4);
// Start of the ES_Descriptor (defined in 14496-1) // Start of the ES_Descriptor (defined in 14496-1)
parent.skipBytes(1); // ES_Descriptor tag parent.skipBytes(1); // ES_Descriptor tag
@ -1263,13 +1301,14 @@ import java.util.List;
* unencrypted atom type and a {@link TrackEncryptionBox}. Null is returned if no common * unencrypted atom type and a {@link TrackEncryptionBox}. Null is returned if no common
* encryption sinf atom was present. * encryption sinf atom was present.
*/ */
@Nullable
private static Pair<Integer, TrackEncryptionBox> parseSampleEntryEncryptionData( private static Pair<Integer, TrackEncryptionBox> parseSampleEntryEncryptionData(
ParsableByteArray parent, int position, int size) { ParsableByteArray parent, int position, int size) {
int childPosition = parent.getPosition(); int childPosition = parent.getPosition();
while (childPosition - position < size) { while (childPosition - position < size) {
parent.setPosition(childPosition); parent.setPosition(childPosition);
int childAtomSize = parent.readInt(); int childAtomSize = parent.readInt();
Assertions.checkArgument(childAtomSize > 0, "childAtomSize should be positive"); Assertions.checkState(childAtomSize > 0, "childAtomSize should be positive");
int childAtomType = parent.readInt(); int childAtomType = parent.readInt();
if (childAtomType == Atom.TYPE_sinf) { if (childAtomType == Atom.TYPE_sinf) {
Pair<Integer, TrackEncryptionBox> result = parseCommonEncryptionSinfFromParent(parent, Pair<Integer, TrackEncryptionBox> result = parseCommonEncryptionSinfFromParent(parent,
@ -1283,6 +1322,7 @@ import java.util.List;
return null; return null;
} }
@Nullable
/* package */ static Pair<Integer, TrackEncryptionBox> parseCommonEncryptionSinfFromParent( /* package */ static Pair<Integer, TrackEncryptionBox> parseCommonEncryptionSinfFromParent(
ParsableByteArray parent, int position, int size) { ParsableByteArray parent, int position, int size) {
int childPosition = position + Atom.HEADER_SIZE; int childPosition = position + Atom.HEADER_SIZE;
@ -1309,20 +1349,21 @@ import java.util.List;
if (C.CENC_TYPE_cenc.equals(schemeType) || C.CENC_TYPE_cbc1.equals(schemeType) if (C.CENC_TYPE_cenc.equals(schemeType) || C.CENC_TYPE_cbc1.equals(schemeType)
|| C.CENC_TYPE_cens.equals(schemeType) || C.CENC_TYPE_cbcs.equals(schemeType)) { || C.CENC_TYPE_cens.equals(schemeType) || C.CENC_TYPE_cbcs.equals(schemeType)) {
Assertions.checkArgument(dataFormat != null, "frma atom is mandatory"); Assertions.checkStateNotNull(dataFormat, "frma atom is mandatory");
Assertions.checkArgument(schemeInformationBoxPosition != C.POSITION_UNSET, Assertions.checkState(
"schi atom is mandatory"); schemeInformationBoxPosition != C.POSITION_UNSET, "schi atom is mandatory");
TrackEncryptionBox encryptionBox = parseSchiFromParent(parent, schemeInformationBoxPosition, TrackEncryptionBox encryptionBox = parseSchiFromParent(parent, schemeInformationBoxPosition,
schemeInformationBoxSize, schemeType); schemeInformationBoxSize, schemeType);
Assertions.checkArgument(encryptionBox != null, "tenc atom is mandatory"); Assertions.checkStateNotNull(encryptionBox, "tenc atom is mandatory");
return Pair.create(dataFormat, encryptionBox); return Pair.create(dataFormat, encryptionBox);
} else { } else {
return null; return null;
} }
} }
private static TrackEncryptionBox parseSchiFromParent(ParsableByteArray parent, int position, @Nullable
int size, String schemeType) { private static TrackEncryptionBox parseSchiFromParent(
ParsableByteArray parent, int position, int size, String schemeType) {
int childPosition = position + Atom.HEADER_SIZE; int childPosition = position + Atom.HEADER_SIZE;
while (childPosition - position < size) { while (childPosition - position < size) {
parent.setPosition(childPosition); parent.setPosition(childPosition);
@ -1359,9 +1400,8 @@ import java.util.List;
return null; return null;
} }
/** /** Parses the proj box from sv3d box, as specified by https://github.com/google/spatial-media. */
* Parses the proj box from sv3d box, as specified by https://github.com/google/spatial-media. @Nullable
*/
private static byte[] parseProjFromParent(ParsableByteArray parent, int position, int size) { private static byte[] parseProjFromParent(ParsableByteArray parent, int position, int size) {
int childPosition = position + Atom.HEADER_SIZE; int childPosition = position + Atom.HEADER_SIZE;
while (childPosition - position < size) { while (childPosition - position < size) {
@ -1477,10 +1517,9 @@ import java.util.List;
public final TrackEncryptionBox[] trackEncryptionBoxes; public final TrackEncryptionBox[] trackEncryptionBoxes;
public Format format; @Nullable public Format format;
public int nalUnitLengthFieldLength; public int nalUnitLengthFieldLength;
@Track.Transformation @Track.Transformation public int requiredSampleTransformation;
public int requiredSampleTransformation;
public StsdData(int numberOfEntries) { public StsdData(int numberOfEntries) {
trackEncryptionBoxes = new TrackEncryptionBox[numberOfEntries]; trackEncryptionBoxes = new TrackEncryptionBox[numberOfEntries];