mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add nullness annotations to FragmentedMp4Extractor
PiperOrigin-RevId: 327797428
This commit is contained in:
parent
d881a675d0
commit
6960dde8a8
3 changed files with 106 additions and 75 deletions
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.util;
|
package com.google.android.exoplayer2.util;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
@ -219,11 +220,12 @@ public final class NalUnitUtil {
|
||||||
* Returns whether the NAL unit with the specified header contains supplemental enhancement
|
* Returns whether the NAL unit with the specified header contains supplemental enhancement
|
||||||
* information.
|
* information.
|
||||||
*
|
*
|
||||||
* @param mimeType The sample MIME type.
|
* @param mimeType The sample MIME type, or {@code null} if unknown.
|
||||||
* @param nalUnitHeaderFirstByte The first byte of nal_unit().
|
* @param nalUnitHeaderFirstByte The first byte of nal_unit().
|
||||||
* @return Whether the NAL unit with the specified header is an SEI NAL unit.
|
* @return Whether the NAL unit with the specified header is an SEI NAL unit. False is returned if
|
||||||
|
* the {@code MimeType} is {@code null}.
|
||||||
*/
|
*/
|
||||||
public static boolean isNalUnitSei(String mimeType, byte nalUnitHeaderFirstByte) {
|
public static boolean isNalUnitSei(@Nullable String mimeType, byte nalUnitHeaderFirstByte) {
|
||||||
return (MimeTypes.VIDEO_H264.equals(mimeType)
|
return (MimeTypes.VIDEO_H264.equals(mimeType)
|
||||||
&& (nalUnitHeaderFirstByte & 0x1F) == H264_NAL_UNIT_TYPE_SEI)
|
&& (nalUnitHeaderFirstByte & 0x1F) == H264_NAL_UNIT_TYPE_SEI)
|
||||||
|| (MimeTypes.VIDEO_H265.equals(mimeType)
|
|| (MimeTypes.VIDEO_H265.equals(mimeType)
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,34 @@ package com.google.android.exoplayer2.extractor;
|
||||||
*/
|
*/
|
||||||
public interface ExtractorOutput {
|
public interface ExtractorOutput {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Placeholder {@link ExtractorOutput} implementation throwing an {@link
|
||||||
|
* UnsupportedOperationException} in each method.
|
||||||
|
*/
|
||||||
|
ExtractorOutput PLACEHOLDER =
|
||||||
|
new ExtractorOutput() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TrackOutput track(int id, int type) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endTracks() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seekMap(SeekMap seekMap) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the {@link Extractor} to get the {@link TrackOutput} for a specific track.
|
* Called by the {@link Extractor} to get the {@link TrackOutput} for a specific track.
|
||||||
* <p>
|
*
|
||||||
* The same {@link TrackOutput} is returned if multiple calls are made with the same {@code id}.
|
* <p>The same {@link TrackOutput} is returned if multiple calls are made with the same {@code
|
||||||
|
* id}.
|
||||||
*
|
*
|
||||||
* @param id A track identifier.
|
* @param id A track identifier.
|
||||||
* @param type The type of the track. Typically one of the {@link com.google.android.exoplayer2.C}
|
* @param type The type of the track. Typically one of the {@link com.google.android.exoplayer2.C}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@
|
||||||
package com.google.android.exoplayer2.extractor.mp4;
|
package com.google.android.exoplayer2.extractor.mp4;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.extractor.mp4.AtomParsers.parseTraks;
|
import static com.google.android.exoplayer2.extractor.mp4.AtomParsers.parseTraks;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||||
|
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||||
|
import static com.google.android.exoplayer2.util.Util.nullSafeArrayCopy;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
@ -42,7 +46,6 @@ import com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom;
|
||||||
import com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom;
|
import com.google.android.exoplayer2.extractor.mp4.Atom.LeafAtom;
|
||||||
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
||||||
import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder;
|
import com.google.android.exoplayer2.metadata.emsg.EventMessageEncoder;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
|
||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.NalUnitUtil;
|
import com.google.android.exoplayer2.util.NalUnitUtil;
|
||||||
|
|
@ -59,7 +62,6 @@ 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")
|
||||||
|
|
@ -175,7 +177,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
private boolean processSeiNalUnitPayload;
|
private boolean processSeiNalUnitPayload;
|
||||||
|
|
||||||
// Outputs.
|
// Outputs.
|
||||||
private @MonotonicNonNull ExtractorOutput extractorOutput;
|
private ExtractorOutput extractorOutput;
|
||||||
private TrackOutput[] emsgTrackOutputs;
|
private TrackOutput[] emsgTrackOutputs;
|
||||||
private TrackOutput[] ceaTrackOutputs;
|
private TrackOutput[] ceaTrackOutputs;
|
||||||
|
|
||||||
|
|
@ -270,9 +272,9 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
durationUs = C.TIME_UNSET;
|
durationUs = C.TIME_UNSET;
|
||||||
pendingSeekTimeUs = C.TIME_UNSET;
|
pendingSeekTimeUs = C.TIME_UNSET;
|
||||||
segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET;
|
segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET;
|
||||||
|
extractorOutput = ExtractorOutput.PLACEHOLDER;
|
||||||
emsgTrackOutputs = new TrackOutput[0];
|
emsgTrackOutputs = new TrackOutput[0];
|
||||||
ceaTrackOutputs = new TrackOutput[0];
|
ceaTrackOutputs = new TrackOutput[0];
|
||||||
enterReadingAtomHeaderState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -283,6 +285,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
@Override
|
@Override
|
||||||
public void init(ExtractorOutput output) {
|
public void init(ExtractorOutput output) {
|
||||||
extractorOutput = output;
|
extractorOutput = output;
|
||||||
|
enterReadingAtomHeaderState();
|
||||||
initExtraTracks();
|
initExtraTracks();
|
||||||
if (sideloadedTrack != null) {
|
if (sideloadedTrack != null) {
|
||||||
TrackBundle bundle =
|
TrackBundle bundle =
|
||||||
|
|
@ -429,8 +432,9 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
if (atomSize > Integer.MAX_VALUE) {
|
if (atomSize > Integer.MAX_VALUE) {
|
||||||
throw new ParserException("Leaf atom with length > 2147483647 (unsupported).");
|
throw new ParserException("Leaf atom with length > 2147483647 (unsupported).");
|
||||||
}
|
}
|
||||||
atomData = new ParsableByteArray((int) atomSize);
|
ParsableByteArray atomData = new ParsableByteArray((int) atomSize);
|
||||||
System.arraycopy(atomHeader.getData(), 0, atomData.getData(), 0, Atom.HEADER_SIZE);
|
System.arraycopy(atomHeader.getData(), 0, atomData.getData(), 0, Atom.HEADER_SIZE);
|
||||||
|
this.atomData = atomData;
|
||||||
parserState = STATE_READING_ATOM_PAYLOAD;
|
parserState = STATE_READING_ATOM_PAYLOAD;
|
||||||
} else {
|
} else {
|
||||||
if (atomSize > Integer.MAX_VALUE) {
|
if (atomSize > Integer.MAX_VALUE) {
|
||||||
|
|
@ -445,6 +449,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
|
|
||||||
private void readAtomPayload(ExtractorInput input) throws IOException {
|
private void readAtomPayload(ExtractorInput input) throws IOException {
|
||||||
int atomPayloadSize = (int) atomSize - atomHeaderBytesRead;
|
int atomPayloadSize = (int) atomSize - atomHeaderBytesRead;
|
||||||
|
@Nullable ParsableByteArray atomData = this.atomData;
|
||||||
if (atomData != null) {
|
if (atomData != null) {
|
||||||
input.readFully(atomData.getData(), Atom.HEADER_SIZE, atomPayloadSize);
|
input.readFully(atomData.getData(), Atom.HEADER_SIZE, atomPayloadSize);
|
||||||
onLeafAtomRead(new LeafAtom(atomType, atomData), input.getPosition());
|
onLeafAtomRead(new LeafAtom(atomType, atomData), input.getPosition());
|
||||||
|
|
@ -485,12 +490,12 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException {
|
private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException {
|
||||||
Assertions.checkState(sideloadedTrack == null, "Unexpected moov box.");
|
checkState(sideloadedTrack == null, "Unexpected moov box.");
|
||||||
|
|
||||||
@Nullable DrmInitData drmInitData = getDrmInitDataFromAtoms(moov.leafChildren);
|
@Nullable DrmInitData drmInitData = getDrmInitDataFromAtoms(moov.leafChildren);
|
||||||
|
|
||||||
// Read declaration of track fragments in the Moov box.
|
// Read declaration of track fragments in the moov box.
|
||||||
ContainerAtom mvex = moov.getContainerAtomOfType(Atom.TYPE_mvex);
|
ContainerAtom mvex = checkNotNull(moov.getContainerAtomOfType(Atom.TYPE_mvex));
|
||||||
SparseArray<DefaultSampleValues> defaultSampleValuesArray = new SparseArray<>();
|
SparseArray<DefaultSampleValues> defaultSampleValuesArray = new SparseArray<>();
|
||||||
long duration = C.TIME_UNSET;
|
long duration = C.TIME_UNSET;
|
||||||
int mvexChildrenSize = mvex.leafChildren.size();
|
int mvexChildrenSize = mvex.leafChildren.size();
|
||||||
|
|
@ -531,7 +536,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
extractorOutput.endTracks();
|
extractorOutput.endTracks();
|
||||||
} else {
|
} else {
|
||||||
Assertions.checkState(trackBundles.size() == trackCount);
|
checkState(trackBundles.size() == trackCount);
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
TrackSampleTable sampleTable = sampleTables.get(i);
|
TrackSampleTable sampleTable = sampleTables.get(i);
|
||||||
Track track = sampleTable.track;
|
Track track = sampleTable.track;
|
||||||
|
|
@ -554,7 +559,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
// See https://github.com/google/ExoPlayer/issues/4477.
|
// See https://github.com/google/ExoPlayer/issues/4477.
|
||||||
return defaultSampleValuesArray.valueAt(/* index= */ 0);
|
return defaultSampleValuesArray.valueAt(/* index= */ 0);
|
||||||
}
|
}
|
||||||
return Assertions.checkNotNull(defaultSampleValuesArray.get(trackId));
|
return checkNotNull(defaultSampleValuesArray.get(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMoofContainerAtomRead(ContainerAtom moof) throws ParserException {
|
private void onMoofContainerAtomRead(ContainerAtom moof) throws ParserException {
|
||||||
|
|
@ -589,7 +594,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
emsgTrackOutputs[emsgTrackOutputCount++] =
|
emsgTrackOutputs[emsgTrackOutputCount++] =
|
||||||
extractorOutput.track(nextExtraTrackId++, C.TRACK_TYPE_METADATA);
|
extractorOutput.track(nextExtraTrackId++, C.TRACK_TYPE_METADATA);
|
||||||
}
|
}
|
||||||
emsgTrackOutputs = Arrays.copyOf(emsgTrackOutputs, emsgTrackOutputCount);
|
emsgTrackOutputs = nullSafeArrayCopy(emsgTrackOutputs, emsgTrackOutputCount);
|
||||||
for (TrackOutput eventMessageTrackOutput : emsgTrackOutputs) {
|
for (TrackOutput eventMessageTrackOutput : emsgTrackOutputs) {
|
||||||
eventMessageTrackOutput.format(EMSG_FORMAT);
|
eventMessageTrackOutput.format(EMSG_FORMAT);
|
||||||
}
|
}
|
||||||
|
|
@ -604,7 +609,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
|
|
||||||
/** Handles an emsg atom (defined in 23009-1). */
|
/** Handles an emsg atom (defined in 23009-1). */
|
||||||
private void onEmsgLeafAtomRead(ParsableByteArray atom) {
|
private void onEmsgLeafAtomRead(ParsableByteArray atom) {
|
||||||
if (emsgTrackOutputs == null || emsgTrackOutputs.length == 0) {
|
if (emsgTrackOutputs.length == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
atom.setPosition(Atom.HEADER_SIZE);
|
atom.setPosition(Atom.HEADER_SIZE);
|
||||||
|
|
@ -619,8 +624,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
long id;
|
long id;
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case 0:
|
case 0:
|
||||||
schemeIdUri = Assertions.checkNotNull(atom.readNullTerminatedString());
|
schemeIdUri = checkNotNull(atom.readNullTerminatedString());
|
||||||
value = Assertions.checkNotNull(atom.readNullTerminatedString());
|
value = checkNotNull(atom.readNullTerminatedString());
|
||||||
timescale = atom.readUnsignedInt();
|
timescale = atom.readUnsignedInt();
|
||||||
presentationTimeDeltaUs =
|
presentationTimeDeltaUs =
|
||||||
Util.scaleLargeTimestamp(atom.readUnsignedInt(), C.MICROS_PER_SECOND, timescale);
|
Util.scaleLargeTimestamp(atom.readUnsignedInt(), C.MICROS_PER_SECOND, timescale);
|
||||||
|
|
@ -638,8 +643,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
durationMs =
|
durationMs =
|
||||||
Util.scaleLargeTimestamp(atom.readUnsignedInt(), C.MILLIS_PER_SECOND, timescale);
|
Util.scaleLargeTimestamp(atom.readUnsignedInt(), C.MILLIS_PER_SECOND, timescale);
|
||||||
id = atom.readUnsignedInt();
|
id = atom.readUnsignedInt();
|
||||||
schemeIdUri = Assertions.checkNotNull(atom.readNullTerminatedString());
|
schemeIdUri = checkNotNull(atom.readNullTerminatedString());
|
||||||
value = Assertions.checkNotNull(atom.readNullTerminatedString());
|
value = checkNotNull(atom.readNullTerminatedString());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.w(TAG, "Skipping unsupported emsg version: " + version);
|
Log.w(TAG, "Skipping unsupported emsg version: " + version);
|
||||||
|
|
@ -717,7 +722,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 = checkNotNull(traf.getLeafAtomOfType(Atom.TYPE_tfhd));
|
||||||
@Nullable TrackBundle trackBundle = parseTfhd(tfhd.data, trackBundleArray);
|
@Nullable TrackBundle trackBundle = parseTfhd(tfhd.data, trackBundleArray);
|
||||||
if (trackBundle == null) {
|
if (trackBundle == null) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -730,7 +735,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
trackBundle.currentlyInFragment = true;
|
trackBundle.currentlyInFragment = true;
|
||||||
@Nullable 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) {
|
||||||
fragment.nextFragmentDecodeTime = parseTfdt(traf.getLeafAtomOfType(Atom.TYPE_tfdt).data);
|
fragment.nextFragmentDecodeTime = parseTfdt(tfdtAtom.data);
|
||||||
fragment.nextFragmentDecodeTimeIncludesMoov = true;
|
fragment.nextFragmentDecodeTimeIncludesMoov = true;
|
||||||
} else {
|
} else {
|
||||||
fragment.nextFragmentDecodeTime = fragmentDecodeTime;
|
fragment.nextFragmentDecodeTime = fragmentDecodeTime;
|
||||||
|
|
@ -742,11 +747,11 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
@Nullable
|
@Nullable
|
||||||
TrackEncryptionBox encryptionBox =
|
TrackEncryptionBox encryptionBox =
|
||||||
trackBundle.moovSampleTable.track.getSampleDescriptionEncryptionBox(
|
trackBundle.moovSampleTable.track.getSampleDescriptionEncryptionBox(
|
||||||
fragment.header.sampleDescriptionIndex);
|
checkNotNull(fragment.header).sampleDescriptionIndex);
|
||||||
|
|
||||||
@Nullable 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(checkNotNull(encryptionBox), saiz.data, fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable LeafAtom saio = traf.getLeafAtomOfType(Atom.TYPE_saio);
|
@Nullable LeafAtom saio = traf.getLeafAtomOfType(Atom.TYPE_saio);
|
||||||
|
|
@ -964,7 +969,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
|
|
||||||
Track track = trackBundle.moovSampleTable.track;
|
Track track = trackBundle.moovSampleTable.track;
|
||||||
TrackFragment fragment = trackBundle.fragment;
|
TrackFragment fragment = trackBundle.fragment;
|
||||||
DefaultSampleValues defaultSampleValues = fragment.header;
|
DefaultSampleValues defaultSampleValues = castNonNull(fragment.header);
|
||||||
|
|
||||||
fragment.trunLength[index] = trun.readUnsignedIntToInt();
|
fragment.trunLength[index] = trun.readUnsignedIntToInt();
|
||||||
fragment.trunDataPosition[index] = fragment.dataPosition;
|
fragment.trunDataPosition[index] = fragment.dataPosition;
|
||||||
|
|
@ -994,7 +999,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
&& track.editListDurations[0] == 0) {
|
&& track.editListDurations[0] == 0) {
|
||||||
edtsOffsetUs =
|
edtsOffsetUs =
|
||||||
Util.scaleLargeTimestamp(
|
Util.scaleLargeTimestamp(
|
||||||
track.editListMediaTimes[0], C.MICROS_PER_SECOND, track.timescale);
|
castNonNull(track.editListMediaTimes)[0], C.MICROS_PER_SECOND, track.timescale);
|
||||||
}
|
}
|
||||||
|
|
||||||
int[] sampleSizeTable = fragment.sampleSizeTable;
|
int[] sampleSizeTable = fragment.sampleSizeTable;
|
||||||
|
|
@ -1161,7 +1166,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
int perSampleIvSize = sgpd.readUnsignedByte();
|
int perSampleIvSize = sgpd.readUnsignedByte();
|
||||||
byte[] keyId = new byte[16];
|
byte[] keyId = new byte[16];
|
||||||
sgpd.readBytes(keyId, 0, keyId.length);
|
sgpd.readBytes(keyId, 0, keyId.length);
|
||||||
byte[] constantIv = null;
|
@Nullable byte[] constantIv = null;
|
||||||
if (perSampleIvSize == 0) {
|
if (perSampleIvSize == 0) {
|
||||||
int constantIvSize = sgpd.readUnsignedByte();
|
int constantIvSize = sgpd.readUnsignedByte();
|
||||||
constantIv = new byte[constantIvSize];
|
constantIv = new byte[constantIvSize];
|
||||||
|
|
@ -1238,7 +1243,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readEncryptionData(ExtractorInput input) throws IOException {
|
private void readEncryptionData(ExtractorInput input) throws IOException {
|
||||||
TrackBundle nextTrackBundle = null;
|
@Nullable TrackBundle nextTrackBundle = null;
|
||||||
long nextDataOffset = Long.MAX_VALUE;
|
long nextDataOffset = Long.MAX_VALUE;
|
||||||
int trackBundlesSize = trackBundles.size();
|
int trackBundlesSize = trackBundles.size();
|
||||||
for (int i = 0; i < trackBundlesSize; i++) {
|
for (int i = 0; i < trackBundlesSize; i++) {
|
||||||
|
|
@ -1277,71 +1282,70 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
* @throws IOException If an error occurs reading from the input.
|
* @throws IOException If an error occurs reading from the input.
|
||||||
*/
|
*/
|
||||||
private boolean readSample(ExtractorInput input) throws IOException {
|
private boolean readSample(ExtractorInput input) throws IOException {
|
||||||
if (parserState == STATE_READING_SAMPLE_START) {
|
@Nullable TrackBundle trackBundle = currentTrackBundle;
|
||||||
if (currentTrackBundle == null) {
|
if (trackBundle == null) {
|
||||||
@Nullable TrackBundle currentTrackBundle = getNextTrackBundle(trackBundles);
|
trackBundle = getNextTrackBundle(trackBundles);
|
||||||
if (currentTrackBundle == null) {
|
if (trackBundle == 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.
|
||||||
int bytesToSkip = (int) (endOfMdatPosition - input.getPosition());
|
int bytesToSkip = (int) (endOfMdatPosition - input.getPosition());
|
||||||
if (bytesToSkip < 0) {
|
|
||||||
throw new ParserException("Offset to end of mdat was negative.");
|
|
||||||
}
|
|
||||||
input.skipFully(bytesToSkip);
|
|
||||||
enterReadingAtomHeaderState();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long nextDataPosition = currentTrackBundle.getCurrentSampleOffset();
|
|
||||||
// We skip bytes preceding the next sample to read.
|
|
||||||
int bytesToSkip = (int) (nextDataPosition - input.getPosition());
|
|
||||||
if (bytesToSkip < 0) {
|
if (bytesToSkip < 0) {
|
||||||
// Assume the sample data must be contiguous in the mdat with no preceding data.
|
throw new ParserException("Offset to end of mdat was negative.");
|
||||||
Log.w(TAG, "Ignoring negative offset to sample data.");
|
|
||||||
bytesToSkip = 0;
|
|
||||||
}
|
}
|
||||||
input.skipFully(bytesToSkip);
|
input.skipFully(bytesToSkip);
|
||||||
this.currentTrackBundle = currentTrackBundle;
|
enterReadingAtomHeaderState();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleSize = currentTrackBundle.getCurrentSampleSize();
|
long nextDataPosition = trackBundle.getCurrentSampleOffset();
|
||||||
|
// We skip bytes preceding the next sample to read.
|
||||||
|
int bytesToSkip = (int) (nextDataPosition - input.getPosition());
|
||||||
|
if (bytesToSkip < 0) {
|
||||||
|
// Assume the sample data must be contiguous in the mdat with no preceding data.
|
||||||
|
Log.w(TAG, "Ignoring negative offset to sample data.");
|
||||||
|
bytesToSkip = 0;
|
||||||
|
}
|
||||||
|
input.skipFully(bytesToSkip);
|
||||||
|
currentTrackBundle = trackBundle;
|
||||||
|
}
|
||||||
|
if (parserState == STATE_READING_SAMPLE_START) {
|
||||||
|
sampleSize = trackBundle.getCurrentSampleSize();
|
||||||
|
|
||||||
if (currentTrackBundle.currentSampleIndex < currentTrackBundle.firstSampleToOutputIndex) {
|
if (trackBundle.currentSampleIndex < trackBundle.firstSampleToOutputIndex) {
|
||||||
input.skipFully(sampleSize);
|
input.skipFully(sampleSize);
|
||||||
currentTrackBundle.skipSampleEncryptionData();
|
trackBundle.skipSampleEncryptionData();
|
||||||
if (!currentTrackBundle.next()) {
|
if (!trackBundle.next()) {
|
||||||
currentTrackBundle = null;
|
currentTrackBundle = null;
|
||||||
}
|
}
|
||||||
parserState = STATE_READING_SAMPLE_START;
|
parserState = STATE_READING_SAMPLE_START;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTrackBundle.moovSampleTable.track.sampleTransformation
|
if (trackBundle.moovSampleTable.track.sampleTransformation
|
||||||
== Track.TRANSFORMATION_CEA608_CDAT) {
|
== Track.TRANSFORMATION_CEA608_CDAT) {
|
||||||
sampleSize -= Atom.HEADER_SIZE;
|
sampleSize -= Atom.HEADER_SIZE;
|
||||||
input.skipFully(Atom.HEADER_SIZE);
|
input.skipFully(Atom.HEADER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MimeTypes.AUDIO_AC4.equals(
|
if (MimeTypes.AUDIO_AC4.equals(trackBundle.moovSampleTable.track.format.sampleMimeType)) {
|
||||||
currentTrackBundle.moovSampleTable.track.format.sampleMimeType)) {
|
|
||||||
// AC4 samples need to be prefixed with a clear sample header.
|
// AC4 samples need to be prefixed with a clear sample header.
|
||||||
sampleBytesWritten =
|
sampleBytesWritten =
|
||||||
currentTrackBundle.outputSampleEncryptionData(sampleSize, Ac4Util.SAMPLE_HEADER_SIZE);
|
trackBundle.outputSampleEncryptionData(sampleSize, Ac4Util.SAMPLE_HEADER_SIZE);
|
||||||
Ac4Util.getAc4SampleHeader(sampleSize, scratch);
|
Ac4Util.getAc4SampleHeader(sampleSize, scratch);
|
||||||
currentTrackBundle.output.sampleData(scratch, Ac4Util.SAMPLE_HEADER_SIZE);
|
trackBundle.output.sampleData(scratch, Ac4Util.SAMPLE_HEADER_SIZE);
|
||||||
sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
|
sampleBytesWritten += Ac4Util.SAMPLE_HEADER_SIZE;
|
||||||
} else {
|
} else {
|
||||||
sampleBytesWritten =
|
sampleBytesWritten =
|
||||||
currentTrackBundle.outputSampleEncryptionData(sampleSize, /* clearHeaderSize= */ 0);
|
trackBundle.outputSampleEncryptionData(sampleSize, /* clearHeaderSize= */ 0);
|
||||||
}
|
}
|
||||||
sampleSize += sampleBytesWritten;
|
sampleSize += sampleBytesWritten;
|
||||||
parserState = STATE_READING_SAMPLE_CONTINUE;
|
parserState = STATE_READING_SAMPLE_CONTINUE;
|
||||||
sampleCurrentNalBytesRemaining = 0;
|
sampleCurrentNalBytesRemaining = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Track track = currentTrackBundle.moovSampleTable.track;
|
Track track = trackBundle.moovSampleTable.track;
|
||||||
TrackOutput output = currentTrackBundle.output;
|
TrackOutput output = trackBundle.output;
|
||||||
long sampleTimeUs = currentTrackBundle.getCurrentSamplePresentationTimeUs();
|
long sampleTimeUs = trackBundle.getCurrentSamplePresentationTimeUs();
|
||||||
if (timestampAdjuster != null) {
|
if (timestampAdjuster != null) {
|
||||||
sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
|
sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(sampleTimeUs);
|
||||||
}
|
}
|
||||||
|
|
@ -1407,11 +1411,11 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@C.BufferFlags int sampleFlags = currentTrackBundle.getCurrentSampleFlags();
|
@C.BufferFlags int sampleFlags = trackBundle.getCurrentSampleFlags();
|
||||||
|
|
||||||
// Encryption data.
|
// Encryption data.
|
||||||
TrackOutput.CryptoData cryptoData = null;
|
@Nullable TrackOutput.CryptoData cryptoData = null;
|
||||||
TrackEncryptionBox encryptionBox = currentTrackBundle.getEncryptionBoxIfEncrypted();
|
@Nullable TrackEncryptionBox encryptionBox = trackBundle.getEncryptionBoxIfEncrypted();
|
||||||
if (encryptionBox != null) {
|
if (encryptionBox != null) {
|
||||||
cryptoData = encryptionBox.cryptoData;
|
cryptoData = encryptionBox.cryptoData;
|
||||||
}
|
}
|
||||||
|
|
@ -1420,7 +1424,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
|
|
||||||
// After we have the sampleTimeUs, we can commit all the pending metadata samples
|
// After we have the sampleTimeUs, we can commit all the pending metadata samples
|
||||||
outputPendingMetadataSamples(sampleTimeUs);
|
outputPendingMetadataSamples(sampleTimeUs);
|
||||||
if (!currentTrackBundle.next()) {
|
if (!trackBundle.next()) {
|
||||||
currentTrackBundle = null;
|
currentTrackBundle = null;
|
||||||
}
|
}
|
||||||
parserState = STATE_READING_SAMPLE_START;
|
parserState = STATE_READING_SAMPLE_START;
|
||||||
|
|
@ -1452,7 +1456,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static TrackBundle getNextTrackBundle(SparseArray<TrackBundle> trackBundles) {
|
private static TrackBundle getNextTrackBundle(SparseArray<TrackBundle> trackBundles) {
|
||||||
TrackBundle nextTrackBundle = null;
|
@Nullable TrackBundle nextTrackBundle = null;
|
||||||
long nextSampleOffset = Long.MAX_VALUE;
|
long nextSampleOffset = Long.MAX_VALUE;
|
||||||
|
|
||||||
int trackBundlesSize = trackBundles.size();
|
int trackBundlesSize = trackBundles.size();
|
||||||
|
|
@ -1579,6 +1583,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
TrackSampleTable moovSampleTable,
|
TrackSampleTable moovSampleTable,
|
||||||
DefaultSampleValues defaultSampleValues) {
|
DefaultSampleValues defaultSampleValues) {
|
||||||
this.output = output;
|
this.output = output;
|
||||||
|
this.moovSampleTable = moovSampleTable;
|
||||||
|
this.defaultSampleValues = defaultSampleValues;
|
||||||
fragment = new TrackFragment();
|
fragment = new TrackFragment();
|
||||||
scratch = new ParsableByteArray();
|
scratch = new ParsableByteArray();
|
||||||
encryptionSignalByte = new ParsableByteArray(1);
|
encryptionSignalByte = new ParsableByteArray(1);
|
||||||
|
|
@ -1587,9 +1593,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset(TrackSampleTable moovSampleTable, DefaultSampleValues defaultSampleValues) {
|
public void reset(TrackSampleTable moovSampleTable, DefaultSampleValues defaultSampleValues) {
|
||||||
Assertions.checkNotNull(moovSampleTable.track);
|
|
||||||
this.moovSampleTable = moovSampleTable;
|
this.moovSampleTable = moovSampleTable;
|
||||||
this.defaultSampleValues = Assertions.checkNotNull(defaultSampleValues);
|
this.defaultSampleValues = defaultSampleValues;
|
||||||
output.format(moovSampleTable.track.format);
|
output.format(moovSampleTable.track.format);
|
||||||
resetFragmentInfo();
|
resetFragmentInfo();
|
||||||
}
|
}
|
||||||
|
|
@ -1598,7 +1603,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
@Nullable
|
@Nullable
|
||||||
TrackEncryptionBox encryptionBox =
|
TrackEncryptionBox encryptionBox =
|
||||||
moovSampleTable.track.getSampleDescriptionEncryptionBox(
|
moovSampleTable.track.getSampleDescriptionEncryptionBox(
|
||||||
fragment.header.sampleDescriptionIndex);
|
castNonNull(fragment.header).sampleDescriptionIndex);
|
||||||
@Nullable String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
|
@Nullable String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
|
||||||
DrmInitData updatedDrmInitData = drmInitData.copyWithSchemeType(schemeType);
|
DrmInitData updatedDrmInitData = drmInitData.copyWithSchemeType(schemeType);
|
||||||
Format format =
|
Format format =
|
||||||
|
|
@ -1706,7 +1711,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
* @return The number of written bytes.
|
* @return The number of written bytes.
|
||||||
*/
|
*/
|
||||||
public int outputSampleEncryptionData(int sampleSize, int clearHeaderSize) {
|
public int outputSampleEncryptionData(int sampleSize, int clearHeaderSize) {
|
||||||
TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted();
|
@Nullable TrackEncryptionBox encryptionBox = getEncryptionBoxIfEncrypted();
|
||||||
if (encryptionBox == null) {
|
if (encryptionBox == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1718,7 +1723,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
vectorSize = encryptionBox.perSampleIvSize;
|
vectorSize = encryptionBox.perSampleIvSize;
|
||||||
} else {
|
} else {
|
||||||
// The default initialization vector should be used.
|
// The default initialization vector should be used.
|
||||||
byte[] initVectorData = encryptionBox.defaultInitializationVector;
|
byte[] initVectorData = castNonNull(encryptionBox.defaultInitializationVector);
|
||||||
defaultInitializationVector.reset(initVectorData, initVectorData.length);
|
defaultInitializationVector.reset(initVectorData, initVectorData.length);
|
||||||
initializationVectorData = defaultInitializationVector;
|
initializationVectorData = defaultInitializationVector;
|
||||||
vectorSize = initVectorData.length;
|
vectorSize = initVectorData.length;
|
||||||
|
|
@ -1815,7 +1820,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||||
// Encryption is not supported yet for samples specified in the sample table.
|
// Encryption is not supported yet for samples specified in the sample table.
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
int sampleDescriptionIndex = fragment.header.sampleDescriptionIndex;
|
int sampleDescriptionIndex = castNonNull(fragment.header).sampleDescriptionIndex;
|
||||||
@Nullable
|
@Nullable
|
||||||
TrackEncryptionBox encryptionBox =
|
TrackEncryptionBox encryptionBox =
|
||||||
fragment.trackEncryptionBox != null
|
fragment.trackEncryptionBox != null
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue