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