mirror of
https://github.com/androidx/media.git
synced 2025-05-15 19:49:50 +08:00
Add NonNull at package level for extractor.mp4
PiperOrigin-RevId: 286191078
This commit is contained in:
parent
7a03e8edc0
commit
821d4fb13a
@ -125,14 +125,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
Pair<Long, String> mdhdData = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data);
|
||||
StsdData stsdData = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data, tkhdData.id,
|
||||
tkhdData.rotationDegrees, mdhdData.second, drmInitData, isQuickTime);
|
||||
long[] editListDurations = null;
|
||||
long[] editListMediaTimes = null;
|
||||
@Nullable long[] editListDurations = null;
|
||||
@Nullable long[] editListMediaTimes = null;
|
||||
if (!ignoreEditLists) {
|
||||
@Nullable
|
||||
Pair<long[], long[]> edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts));
|
||||
if (edtsData != null) {
|
||||
editListDurations = edtsData.first;
|
||||
editListMediaTimes = edtsData.second;
|
||||
@Nullable Atom.ContainerAtom edtsAtom = trak.getContainerAtomOfType(Atom.TYPE_edts);
|
||||
if (edtsAtom != null) {
|
||||
@Nullable Pair<long[], long[]> edtsData = parseEdts(edtsAtom);
|
||||
if (edtsData != null) {
|
||||
editListDurations = edtsData.first;
|
||||
editListMediaTimes = edtsData.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
return stsdData.format == null ? null
|
||||
@ -154,11 +156,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
Track track, Atom.ContainerAtom stblAtom, GaplessInfoHolder gaplessInfoHolder)
|
||||
throws ParserException {
|
||||
SampleSizeBox sampleSizeBox;
|
||||
Atom.LeafAtom stszAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stsz);
|
||||
@Nullable Atom.LeafAtom stszAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stsz);
|
||||
if (stszAtom != null) {
|
||||
sampleSizeBox = new StszSampleSizeBox(stszAtom);
|
||||
} else {
|
||||
Atom.LeafAtom stz2Atom = stblAtom.getLeafAtomOfType(Atom.TYPE_stz2);
|
||||
@Nullable Atom.LeafAtom stz2Atom = stblAtom.getLeafAtomOfType(Atom.TYPE_stz2);
|
||||
if (stz2Atom == null) {
|
||||
throw new ParserException("Track has no sample table size information");
|
||||
}
|
||||
@ -179,7 +181,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
|
||||
// Entries are byte offsets of chunks.
|
||||
boolean chunkOffsetsAreLongs = false;
|
||||
Atom.LeafAtom chunkOffsetsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stco);
|
||||
@Nullable Atom.LeafAtom chunkOffsetsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stco);
|
||||
if (chunkOffsetsAtom == null) {
|
||||
chunkOffsetsAreLongs = true;
|
||||
chunkOffsetsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_co64);
|
||||
@ -190,11 +192,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
// Entries are (number of samples, timestamp delta between those samples).
|
||||
ParsableByteArray stts = stblAtom.getLeafAtomOfType(Atom.TYPE_stts).data;
|
||||
// Entries are the indices of samples that are synchronization samples.
|
||||
Atom.LeafAtom stssAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stss);
|
||||
ParsableByteArray stss = stssAtom != null ? stssAtom.data : null;
|
||||
@Nullable Atom.LeafAtom stssAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_stss);
|
||||
@Nullable ParsableByteArray stss = stssAtom != null ? stssAtom.data : null;
|
||||
// Entries are (number of samples, timestamp offset).
|
||||
Atom.LeafAtom cttsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_ctts);
|
||||
ParsableByteArray ctts = cttsAtom != null ? cttsAtom.data : null;
|
||||
@Nullable Atom.LeafAtom cttsAtom = stblAtom.getLeafAtomOfType(Atom.TYPE_ctts);
|
||||
@Nullable ParsableByteArray ctts = cttsAtom != null ? cttsAtom.data : null;
|
||||
|
||||
// Prepare to read chunk information.
|
||||
ChunkIterator chunkIterator = new ChunkIterator(stsc, chunkOffsets, chunkOffsetsAreLongs);
|
||||
@ -542,9 +544,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
*/
|
||||
@Nullable
|
||||
public static Metadata parseMdtaFromMeta(Atom.ContainerAtom meta) {
|
||||
Atom.LeafAtom hdlrAtom = meta.getLeafAtomOfType(Atom.TYPE_hdlr);
|
||||
Atom.LeafAtom keysAtom = meta.getLeafAtomOfType(Atom.TYPE_keys);
|
||||
Atom.LeafAtom ilstAtom = meta.getLeafAtomOfType(Atom.TYPE_ilst);
|
||||
@Nullable Atom.LeafAtom hdlrAtom = meta.getLeafAtomOfType(Atom.TYPE_hdlr);
|
||||
@Nullable Atom.LeafAtom keysAtom = meta.getLeafAtomOfType(Atom.TYPE_keys);
|
||||
@Nullable Atom.LeafAtom ilstAtom = meta.getLeafAtomOfType(Atom.TYPE_ilst);
|
||||
if (hdlrAtom == null
|
||||
|| keysAtom == null
|
||||
|| ilstAtom == null
|
||||
@ -575,6 +577,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
int keyIndex = ilst.readInt() - 1;
|
||||
if (keyIndex >= 0 && keyIndex < keyNames.length) {
|
||||
String key = keyNames[keyIndex];
|
||||
@Nullable
|
||||
Metadata.Entry entry =
|
||||
MetadataUtil.parseMdtaMetadataEntryFromIlst(ilst, atomPosition + atomSize, key);
|
||||
if (entry != null) {
|
||||
@ -609,7 +612,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
ilst.skipBytes(Atom.HEADER_SIZE);
|
||||
ArrayList<Metadata.Entry> entries = new ArrayList<>();
|
||||
while (ilst.getPosition() < limit) {
|
||||
Metadata.Entry entry = MetadataUtil.parseIlstElement(ilst);
|
||||
@Nullable Metadata.Entry entry = MetadataUtil.parseIlstElement(ilst);
|
||||
if (entry != null) {
|
||||
entries.add(entry);
|
||||
}
|
||||
@ -817,12 +820,18 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
return out;
|
||||
}
|
||||
|
||||
private static void parseTextSampleEntry(ParsableByteArray parent, int atomType, int position,
|
||||
int atomSize, int trackId, String language, StsdData out) throws ParserException {
|
||||
private static void parseTextSampleEntry(
|
||||
ParsableByteArray parent,
|
||||
int atomType,
|
||||
int position,
|
||||
int atomSize,
|
||||
int trackId,
|
||||
String language,
|
||||
StsdData out) {
|
||||
parent.setPosition(position + Atom.HEADER_SIZE + StsdData.STSD_HEADER_SIZE);
|
||||
|
||||
// Default values.
|
||||
List<byte[]> initializationData = null;
|
||||
@Nullable List<byte[]> initializationData = null;
|
||||
long subsampleOffsetUs = Format.OFFSET_SAMPLE_RELATIVE;
|
||||
|
||||
String mimeType;
|
||||
@ -934,7 +943,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
initializationData = hevcConfig.initializationData;
|
||||
out.nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
|
||||
} else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
|
||||
DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
|
||||
@Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
|
||||
if (dolbyVisionConfig != null) {
|
||||
codecs = dolbyVisionConfig.codecs;
|
||||
mimeType = MimeTypes.VIDEO_DOLBY_VISION;
|
||||
@ -1021,11 +1030,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
*/
|
||||
@Nullable
|
||||
private static Pair<long[], long[]> parseEdts(Atom.ContainerAtom edtsAtom) {
|
||||
Atom.LeafAtom elst;
|
||||
if (edtsAtom == null || (elst = edtsAtom.getLeafAtomOfType(Atom.TYPE_elst)) == null) {
|
||||
@Nullable Atom.LeafAtom elstAtom = edtsAtom.getLeafAtomOfType(Atom.TYPE_elst);
|
||||
if (elstAtom == null) {
|
||||
return null;
|
||||
}
|
||||
ParsableByteArray elstData = elst.data;
|
||||
ParsableByteArray elstData = elstAtom.data;
|
||||
elstData.setPosition(Atom.HEADER_SIZE);
|
||||
int fullAtom = elstData.readInt();
|
||||
int version = Atom.parseFullAtomVersion(fullAtom);
|
||||
@ -1328,8 +1337,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
int childPosition = position + Atom.HEADER_SIZE;
|
||||
int schemeInformationBoxPosition = C.POSITION_UNSET;
|
||||
int schemeInformationBoxSize = 0;
|
||||
String schemeType = null;
|
||||
Integer dataFormat = null;
|
||||
@Nullable String schemeType = null;
|
||||
@Nullable Integer dataFormat = null;
|
||||
while (childPosition - position < size) {
|
||||
parent.setPosition(childPosition);
|
||||
int childAtomSize = parent.readInt();
|
||||
@ -1352,9 +1361,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
Assertions.checkStateNotNull(dataFormat, "frma atom is mandatory");
|
||||
Assertions.checkState(
|
||||
schemeInformationBoxPosition != C.POSITION_UNSET, "schi atom is mandatory");
|
||||
TrackEncryptionBox encryptionBox = parseSchiFromParent(parent, schemeInformationBoxPosition,
|
||||
schemeInformationBoxSize, schemeType);
|
||||
Assertions.checkStateNotNull(encryptionBox, "tenc atom is mandatory");
|
||||
TrackEncryptionBox encryptionBox =
|
||||
Assertions.checkStateNotNull(
|
||||
parseSchiFromParent(
|
||||
parent, schemeInformationBoxPosition, schemeInformationBoxSize, schemeType),
|
||||
"tenc atom is mandatory");
|
||||
return Pair.create(dataFormat, encryptionBox);
|
||||
} else {
|
||||
return null;
|
||||
|
@ -55,6 +55,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** Extracts data from the FMP4 container format. */
|
||||
@SuppressWarnings("ConstantField")
|
||||
@ -155,14 +156,14 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
private int atomType;
|
||||
private long atomSize;
|
||||
private int atomHeaderBytesRead;
|
||||
private ParsableByteArray atomData;
|
||||
@Nullable private ParsableByteArray atomData;
|
||||
private long endOfMdatPosition;
|
||||
private int pendingMetadataSampleBytes;
|
||||
private long pendingSeekTimeUs;
|
||||
|
||||
private long durationUs;
|
||||
private long segmentIndexEarliestPresentationTimeUs;
|
||||
private TrackBundle currentTrackBundle;
|
||||
@Nullable private TrackBundle currentTrackBundle;
|
||||
private int sampleSize;
|
||||
private int sampleBytesWritten;
|
||||
private int sampleCurrentNalBytesRemaining;
|
||||
@ -170,7 +171,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
private boolean isAc4HeaderRequired;
|
||||
|
||||
// Extractor output.
|
||||
private ExtractorOutput extractorOutput;
|
||||
@MonotonicNonNull private ExtractorOutput extractorOutput;
|
||||
private TrackOutput[] emsgTrackOutputs;
|
||||
private TrackOutput[] cea608TrackOutputs;
|
||||
|
||||
@ -495,6 +496,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
for (int i = 0; i < moovContainerChildrenSize; i++) {
|
||||
Atom.ContainerAtom atom = moov.containerChildren.get(i);
|
||||
if (atom.type == Atom.TYPE_trak) {
|
||||
@Nullable
|
||||
Track track =
|
||||
modifyTrack(
|
||||
AtomParsers.parseTrak(
|
||||
@ -712,7 +714,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
private static void parseTraf(ContainerAtom traf, SparseArray<TrackBundle> trackBundleArray,
|
||||
@Flags int flags, byte[] extendedTypeScratch) throws ParserException {
|
||||
LeafAtom tfhd = traf.getLeafAtomOfType(Atom.TYPE_tfhd);
|
||||
TrackBundle trackBundle = parseTfhd(tfhd.data, trackBundleArray);
|
||||
@Nullable TrackBundle trackBundle = parseTfhd(tfhd.data, trackBundleArray);
|
||||
if (trackBundle == null) {
|
||||
return;
|
||||
}
|
||||
@ -721,33 +723,34 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
long decodeTime = fragment.nextFragmentDecodeTime;
|
||||
trackBundle.reset();
|
||||
|
||||
LeafAtom tfdtAtom = traf.getLeafAtomOfType(Atom.TYPE_tfdt);
|
||||
@Nullable LeafAtom tfdtAtom = traf.getLeafAtomOfType(Atom.TYPE_tfdt);
|
||||
if (tfdtAtom != null && (flags & FLAG_WORKAROUND_IGNORE_TFDT_BOX) == 0) {
|
||||
decodeTime = parseTfdt(traf.getLeafAtomOfType(Atom.TYPE_tfdt).data);
|
||||
}
|
||||
|
||||
parseTruns(traf, trackBundle, decodeTime, flags);
|
||||
|
||||
TrackEncryptionBox encryptionBox = trackBundle.track
|
||||
.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
|
||||
@Nullable
|
||||
TrackEncryptionBox encryptionBox =
|
||||
trackBundle.track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
|
||||
|
||||
LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz);
|
||||
@Nullable LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz);
|
||||
if (saiz != null) {
|
||||
parseSaiz(encryptionBox, saiz.data, fragment);
|
||||
}
|
||||
|
||||
LeafAtom saio = traf.getLeafAtomOfType(Atom.TYPE_saio);
|
||||
@Nullable LeafAtom saio = traf.getLeafAtomOfType(Atom.TYPE_saio);
|
||||
if (saio != null) {
|
||||
parseSaio(saio.data, fragment);
|
||||
}
|
||||
|
||||
LeafAtom senc = traf.getLeafAtomOfType(Atom.TYPE_senc);
|
||||
@Nullable LeafAtom senc = traf.getLeafAtomOfType(Atom.TYPE_senc);
|
||||
if (senc != null) {
|
||||
parseSenc(senc.data, fragment);
|
||||
}
|
||||
|
||||
LeafAtom sbgp = traf.getLeafAtomOfType(Atom.TYPE_sbgp);
|
||||
LeafAtom sgpd = traf.getLeafAtomOfType(Atom.TYPE_sgpd);
|
||||
@Nullable LeafAtom sbgp = traf.getLeafAtomOfType(Atom.TYPE_sbgp);
|
||||
@Nullable LeafAtom sgpd = traf.getLeafAtomOfType(Atom.TYPE_sgpd);
|
||||
if (sbgp != null && sgpd != null) {
|
||||
parseSgpd(sbgp.data, sgpd.data, encryptionBox != null ? encryptionBox.schemeType : null,
|
||||
fragment);
|
||||
@ -863,13 +866,14 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
* @return The {@link TrackBundle} to which the {@link TrackFragment} belongs, or null if the tfhd
|
||||
* does not refer to any {@link TrackBundle}.
|
||||
*/
|
||||
@Nullable
|
||||
private static TrackBundle parseTfhd(
|
||||
ParsableByteArray tfhd, SparseArray<TrackBundle> trackBundles) {
|
||||
tfhd.setPosition(Atom.HEADER_SIZE);
|
||||
int fullAtom = tfhd.readInt();
|
||||
int atomFlags = Atom.parseFullAtomFlags(fullAtom);
|
||||
int trackId = tfhd.readInt();
|
||||
TrackBundle trackBundle = getTrackBundle(trackBundles, trackId);
|
||||
@Nullable TrackBundle trackBundle = getTrackBundle(trackBundles, trackId);
|
||||
if (trackBundle == null) {
|
||||
return null;
|
||||
}
|
||||
@ -1053,8 +1057,12 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
out.fillEncryptionData(senc);
|
||||
}
|
||||
|
||||
private static void parseSgpd(ParsableByteArray sbgp, ParsableByteArray sgpd, String schemeType,
|
||||
TrackFragment out) throws ParserException {
|
||||
private static void parseSgpd(
|
||||
ParsableByteArray sbgp,
|
||||
ParsableByteArray sgpd,
|
||||
@Nullable String schemeType,
|
||||
TrackFragment out)
|
||||
throws ParserException {
|
||||
sbgp.setPosition(Atom.HEADER_SIZE);
|
||||
int sbgpFullAtom = sbgp.readInt();
|
||||
if (sbgp.readInt() != SAMPLE_GROUP_TYPE_seig) {
|
||||
@ -1216,7 +1224,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
private boolean readSample(ExtractorInput input) throws IOException, InterruptedException {
|
||||
if (parserState == STATE_READING_SAMPLE_START) {
|
||||
if (currentTrackBundle == null) {
|
||||
TrackBundle currentTrackBundle = getNextFragmentRun(trackBundles);
|
||||
@Nullable TrackBundle currentTrackBundle = getNextFragmentRun(trackBundles);
|
||||
if (currentTrackBundle == null) {
|
||||
// We've run out of samples in the current mdat. Discard any trailing data and prepare to
|
||||
// read the header of the next atom.
|
||||
@ -1388,6 +1396,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
* Returns the {@link TrackBundle} whose fragment run has the earliest file position out of those
|
||||
* yet to be consumed, or null if all have been consumed.
|
||||
*/
|
||||
@Nullable
|
||||
private static TrackBundle getNextFragmentRun(SparseArray<TrackBundle> trackBundles) {
|
||||
TrackBundle nextTrackBundle = null;
|
||||
long nextTrackRunOffset = Long.MAX_VALUE;
|
||||
@ -1410,7 +1419,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
/** Returns DrmInitData from leaf atoms. */
|
||||
private static DrmInitData getDrmInitDataFromAtoms(List<Atom.LeafAtom> leafChildren) {
|
||||
ArrayList<SchemeData> schemeDatas = null;
|
||||
@Nullable ArrayList<SchemeData> schemeDatas = null;
|
||||
int leafChildrenSize = leafChildren.size();
|
||||
for (int i = 0; i < leafChildrenSize; i++) {
|
||||
LeafAtom child = leafChildren.get(i);
|
||||
@ -1419,7 +1428,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
schemeDatas = new ArrayList<>();
|
||||
}
|
||||
byte[] psshData = child.data.data;
|
||||
UUID uuid = PsshAtomUtil.parseUuid(psshData);
|
||||
@Nullable UUID uuid = PsshAtomUtil.parseUuid(psshData);
|
||||
if (uuid == null) {
|
||||
Log.w(TAG, "Skipped pssh atom (failed to extract uuid)");
|
||||
} else {
|
||||
@ -1496,9 +1505,10 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
}
|
||||
|
||||
public void updateDrmInitData(DrmInitData drmInitData) {
|
||||
@Nullable
|
||||
TrackEncryptionBox encryptionBox =
|
||||
track.getSampleDescriptionEncryptionBox(fragment.header.sampleDescriptionIndex);
|
||||
String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
|
||||
@Nullable String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
|
||||
output.format(track.format.copyWithDrmInitData(drmInitData.copyWithSchemeType(schemeType)));
|
||||
}
|
||||
|
||||
@ -1595,7 +1605,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
/** Skips the encryption data for the current sample. */
|
||||
private void skipSampleEncryptionData() {
|
||||
TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted();
|
||||
@Nullable TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted();
|
||||
if (encryptionBox == null) {
|
||||
return;
|
||||
}
|
||||
@ -1609,8 +1619,10 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TrackEncryptionBox getEncryptionBoxIfEncrypted() {
|
||||
int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
|
||||
@Nullable
|
||||
TrackEncryptionBox encryptionBox =
|
||||
fragment.trackEncryptionBox != null
|
||||
? fragment.trackEncryptionBox
|
||||
|
@ -47,8 +47,7 @@ public final class MdtaMetadataEntry implements Metadata.Entry {
|
||||
|
||||
private MdtaMetadataEntry(Parcel in) {
|
||||
key = Util.castNonNull(in.readString());
|
||||
value = new byte[in.readInt()];
|
||||
in.readByteArray(value);
|
||||
value = Util.castNonNull(in.createByteArray());
|
||||
localeIndicator = in.readInt();
|
||||
typeIndicator = in.readInt();
|
||||
}
|
||||
@ -88,7 +87,6 @@ public final class MdtaMetadataEntry implements Metadata.Entry {
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(key);
|
||||
dest.writeInt(value.length);
|
||||
dest.writeByteArray(value);
|
||||
dest.writeInt(localeIndicator);
|
||||
dest.writeInt(typeIndicator);
|
||||
|
@ -325,8 +325,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
@Nullable
|
||||
private static TextInformationFrame parseStandardGenreAttribute(ParsableByteArray data) {
|
||||
int genreCode = parseUint8AttributeValue(data);
|
||||
String genreString = (0 < genreCode && genreCode <= STANDARD_GENRES.length)
|
||||
? STANDARD_GENRES[genreCode - 1] : null;
|
||||
@Nullable
|
||||
String genreString =
|
||||
(0 < genreCode && genreCode <= STANDARD_GENRES.length)
|
||||
? STANDARD_GENRES[genreCode - 1]
|
||||
: null;
|
||||
if (genreString != null) {
|
||||
return new TextInformationFrame("TCON", /* description= */ null, genreString);
|
||||
}
|
||||
@ -341,7 +344,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
if (atomType == Atom.TYPE_data) {
|
||||
int fullVersionInt = data.readInt();
|
||||
int flags = Atom.parseFullAtomFlags(fullVersionInt);
|
||||
String mimeType = flags == 13 ? "image/jpeg" : flags == 14 ? "image/png" : null;
|
||||
@Nullable String mimeType = flags == 13 ? "image/jpeg" : flags == 14 ? "image/png" : null;
|
||||
if (mimeType == null) {
|
||||
Log.w(TAG, "Unrecognized cover art flags: " + flags);
|
||||
return null;
|
||||
@ -361,8 +364,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@Nullable
|
||||
private static Id3Frame parseInternalAttribute(ParsableByteArray data, int endPosition) {
|
||||
String domain = null;
|
||||
String name = null;
|
||||
@Nullable String domain = null;
|
||||
@Nullable String name = null;
|
||||
int dataAtomPosition = -1;
|
||||
int dataAtomSize = -1;
|
||||
while (data.getPosition() < endPosition) {
|
||||
|
@ -16,6 +16,7 @@
|
||||
package com.google.android.exoplayer2.extractor.mp4;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
@ -42,6 +43,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* Extracts data from the MP4 container format.
|
||||
@ -105,7 +107,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
private int atomType;
|
||||
private long atomSize;
|
||||
private int atomHeaderBytesRead;
|
||||
private ParsableByteArray atomData;
|
||||
@Nullable private ParsableByteArray atomData;
|
||||
|
||||
private int sampleTrackIndex;
|
||||
private int sampleBytesWritten;
|
||||
@ -113,7 +115,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
private boolean isAc4HeaderRequired;
|
||||
|
||||
// Extractor outputs.
|
||||
private ExtractorOutput extractorOutput;
|
||||
@MonotonicNonNull private ExtractorOutput extractorOutput;
|
||||
private Mp4Track[] tracks;
|
||||
private long[][] accumulatedSampleSizes;
|
||||
private int firstVideoTrackIndex;
|
||||
@ -290,8 +292,11 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
// The atom extends to the end of the file. Note that if the atom is within a container we can
|
||||
// work out its size even if the input length is unknown.
|
||||
long endPosition = input.getLength();
|
||||
if (endPosition == C.LENGTH_UNSET && !containerAtoms.isEmpty()) {
|
||||
endPosition = containerAtoms.peek().endPosition;
|
||||
if (endPosition == C.LENGTH_UNSET) {
|
||||
@Nullable ContainerAtom containerAtom = containerAtoms.peek();
|
||||
if (containerAtom != null) {
|
||||
endPosition = containerAtom.endPosition;
|
||||
}
|
||||
}
|
||||
if (endPosition != C.LENGTH_UNSET) {
|
||||
atomSize = endPosition - input.getPosition() + atomHeaderBytesRead;
|
||||
@ -386,17 +391,17 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
List<Mp4Track> tracks = new ArrayList<>();
|
||||
|
||||
// Process metadata.
|
||||
Metadata udtaMetadata = null;
|
||||
@Nullable Metadata udtaMetadata = null;
|
||||
GaplessInfoHolder gaplessInfoHolder = new GaplessInfoHolder();
|
||||
Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
|
||||
@Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
|
||||
if (udta != null) {
|
||||
udtaMetadata = AtomParsers.parseUdta(udta, isQuickTime);
|
||||
if (udtaMetadata != null) {
|
||||
gaplessInfoHolder.setFromMetadata(udtaMetadata);
|
||||
}
|
||||
}
|
||||
Metadata mdtaMetadata = null;
|
||||
Atom.ContainerAtom meta = moov.getContainerAtomOfType(Atom.TYPE_meta);
|
||||
@Nullable Metadata mdtaMetadata = null;
|
||||
@Nullable Atom.ContainerAtom meta = moov.getContainerAtomOfType(Atom.TYPE_meta);
|
||||
if (meta != null) {
|
||||
mdtaMetadata = AtomParsers.parseMdtaFromMeta(meta);
|
||||
}
|
||||
@ -453,6 +458,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
if (atom.type != Atom.TYPE_trak) {
|
||||
continue;
|
||||
}
|
||||
@Nullable
|
||||
Track track =
|
||||
AtomParsers.parseTrak(
|
||||
atom,
|
||||
|
@ -49,8 +49,6 @@ public final class PsshAtomUtil {
|
||||
* @param data The scheme specific data.
|
||||
* @return The PSSH atom.
|
||||
*/
|
||||
// dereference of possibly-null reference keyId
|
||||
@SuppressWarnings({"ParameterNotNullable", "nullness:dereference.of.nullable"})
|
||||
public static byte[] buildPsshAtom(
|
||||
UUID systemId, @Nullable UUID[] keyIds, @Nullable byte[] data) {
|
||||
int dataLength = data != null ? data.length : 0;
|
||||
@ -97,8 +95,9 @@ public final class PsshAtomUtil {
|
||||
* @return The parsed UUID. Null if the input is not a valid PSSH atom, or if the PSSH atom has an
|
||||
* unsupported version.
|
||||
*/
|
||||
public static @Nullable UUID parseUuid(byte[] atom) {
|
||||
PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||
@Nullable
|
||||
public static UUID parseUuid(byte[] atom) {
|
||||
@Nullable PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||
if (parsedAtom == null) {
|
||||
return null;
|
||||
}
|
||||
@ -115,7 +114,7 @@ public final class PsshAtomUtil {
|
||||
* an unsupported version.
|
||||
*/
|
||||
public static int parseVersion(byte[] atom) {
|
||||
PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||
@Nullable PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||
if (parsedAtom == null) {
|
||||
return -1;
|
||||
}
|
||||
@ -133,8 +132,9 @@ public final class PsshAtomUtil {
|
||||
* @return The parsed scheme specific data. Null if the input is not a valid PSSH atom, or if the
|
||||
* PSSH atom has an unsupported version, or if the PSSH atom does not match the passed UUID.
|
||||
*/
|
||||
public static @Nullable byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) {
|
||||
PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||
@Nullable
|
||||
public static byte[] parseSchemeSpecificData(byte[] atom, UUID uuid) {
|
||||
@Nullable PsshAtom parsedAtom = parsePsshAtom(atom);
|
||||
if (parsedAtom == null) {
|
||||
return null;
|
||||
}
|
||||
@ -153,7 +153,8 @@ public final class PsshAtomUtil {
|
||||
* has an unsupported version.
|
||||
*/
|
||||
// TODO: Support parsing of the key ids for version 1 PSSH atoms.
|
||||
private static @Nullable PsshAtom parsePsshAtom(byte[] atom) {
|
||||
@Nullable
|
||||
private static PsshAtom parsePsshAtom(byte[] atom) {
|
||||
ParsableByteArray atomData = new ParsableByteArray(atom);
|
||||
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
|
||||
// Data too short.
|
||||
|
@ -15,19 +15,19 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.extractor.mp4;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import java.io.IOException;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/**
|
||||
* A holder for information corresponding to a single fragment of an mp4 file.
|
||||
*/
|
||||
/* package */ final class TrackFragment {
|
||||
|
||||
/**
|
||||
* The default values for samples from the track fragment header.
|
||||
*/
|
||||
public DefaultSampleValues header;
|
||||
/** The default values for samples from the track fragment header. */
|
||||
@MonotonicNonNull public DefaultSampleValues header;
|
||||
/**
|
||||
* The position (byte offset) of the start of fragment.
|
||||
*/
|
||||
@ -81,20 +81,13 @@ import java.io.IOException;
|
||||
* Undefined otherwise.
|
||||
*/
|
||||
public boolean[] sampleHasSubsampleEncryptionTable;
|
||||
/**
|
||||
* Fragment specific track encryption. May be null.
|
||||
*/
|
||||
public TrackEncryptionBox trackEncryptionBox;
|
||||
/**
|
||||
* If {@link #definesEncryptionData} is true, indicates the length of the sample encryption data.
|
||||
* Undefined otherwise.
|
||||
*/
|
||||
public int sampleEncryptionDataLength;
|
||||
/** Fragment specific track encryption. May be null. */
|
||||
@Nullable public TrackEncryptionBox trackEncryptionBox;
|
||||
/**
|
||||
* If {@link #definesEncryptionData} is true, contains binary sample encryption data. Undefined
|
||||
* otherwise.
|
||||
*/
|
||||
public ParsableByteArray sampleEncryptionData;
|
||||
public final ParsableByteArray sampleEncryptionData;
|
||||
/**
|
||||
* Whether {@link #sampleEncryptionData} needs populating with the actual encryption data.
|
||||
*/
|
||||
@ -104,6 +97,17 @@ import java.io.IOException;
|
||||
*/
|
||||
public long nextFragmentDecodeTime;
|
||||
|
||||
public TrackFragment() {
|
||||
trunDataPosition = new long[0];
|
||||
trunLength = new int[0];
|
||||
sampleSizeTable = new int[0];
|
||||
sampleCompositionTimeOffsetTable = new int[0];
|
||||
sampleDecodingTimeTable = new long[0];
|
||||
sampleIsSyncFrameTable = new boolean[0];
|
||||
sampleHasSubsampleEncryptionTable = new boolean[0];
|
||||
sampleEncryptionData = new ParsableByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the fragment.
|
||||
* <p>
|
||||
@ -130,11 +134,11 @@ import java.io.IOException;
|
||||
public void initTables(int trunCount, int sampleCount) {
|
||||
this.trunCount = trunCount;
|
||||
this.sampleCount = sampleCount;
|
||||
if (trunLength == null || trunLength.length < trunCount) {
|
||||
if (trunLength.length < trunCount) {
|
||||
trunDataPosition = new long[trunCount];
|
||||
trunLength = new int[trunCount];
|
||||
}
|
||||
if (sampleSizeTable == null || sampleSizeTable.length < sampleCount) {
|
||||
if (sampleSizeTable.length < sampleCount) {
|
||||
// Size the tables 25% larger than needed, so as to make future resize operations less
|
||||
// likely. The choice of 25% is relatively arbitrary.
|
||||
int tableSize = (sampleCount * 125) / 100;
|
||||
@ -148,18 +152,14 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Configures the fragment to be one that defines encryption data of the specified length.
|
||||
* <p>
|
||||
* {@link #definesEncryptionData} is set to true, {@link #sampleEncryptionDataLength} is set to
|
||||
* the specified length, and {@link #sampleEncryptionData} is resized if necessary such that it
|
||||
* is at least this length.
|
||||
*
|
||||
* <p>{@link #definesEncryptionData} is set to true, and the {@link ParsableByteArray#limit()
|
||||
* limit} of {@link #sampleEncryptionData} is set to the specified length.
|
||||
*
|
||||
* @param length The length in bytes of the encryption data.
|
||||
*/
|
||||
public void initEncryptionData(int length) {
|
||||
if (sampleEncryptionData == null || sampleEncryptionData.limit() < length) {
|
||||
sampleEncryptionData = new ParsableByteArray(length);
|
||||
}
|
||||
sampleEncryptionDataLength = length;
|
||||
sampleEncryptionData.reset(length);
|
||||
definesEncryptionData = true;
|
||||
sampleEncryptionDataNeedsFill = true;
|
||||
}
|
||||
@ -170,7 +170,7 @@ import java.io.IOException;
|
||||
* @param input An {@link ExtractorInput} from which to read the encryption data.
|
||||
*/
|
||||
public void fillEncryptionData(ExtractorInput input) throws IOException, InterruptedException {
|
||||
input.readFully(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
|
||||
input.readFully(sampleEncryptionData.data, 0, sampleEncryptionData.limit());
|
||||
sampleEncryptionData.setPosition(0);
|
||||
sampleEncryptionDataNeedsFill = false;
|
||||
}
|
||||
@ -181,7 +181,7 @@ import java.io.IOException;
|
||||
* @param source A source from which to read the encryption data.
|
||||
*/
|
||||
public void fillEncryptionData(ParsableByteArray source) {
|
||||
source.readBytes(sampleEncryptionData.data, 0, sampleEncryptionDataLength);
|
||||
source.readBytes(sampleEncryptionData.data, 0, sampleEncryptionData.limit());
|
||||
sampleEncryptionData.setPosition(0);
|
||||
sampleEncryptionDataNeedsFill = false;
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
@NonNullApi
|
||||
package com.google.android.exoplayer2.extractor.mp4;
|
||||
|
||||
import com.google.android.exoplayer2.util.NonNullApi;
|
Loading…
x
Reference in New Issue
Block a user