Mp4Extractor.java 源代码
package com.mbridge.msdk.playercommon.exoplayer2.extractor.mp4;
import com.mbridge.msdk.playercommon.exoplayer2.C;
import com.mbridge.msdk.playercommon.exoplayer2.Format;
import com.mbridge.msdk.playercommon.exoplayer2.ParserException;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.Extractor;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.ExtractorInput;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.ExtractorOutput;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.ExtractorsFactory;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.GaplessInfoHolder;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.PositionHolder;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.SeekMap;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.SeekPoint;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.TrackOutput;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.mp4.Atom;
import com.mbridge.msdk.playercommon.exoplayer2.extractor.mp4.AtomParsers;
import com.mbridge.msdk.playercommon.exoplayer2.metadata.Metadata;
import com.mbridge.msdk.playercommon.exoplayer2.util.Assertions;
import com.mbridge.msdk.playercommon.exoplayer2.util.NalUnitUtil;
import com.mbridge.msdk.playercommon.exoplayer2.util.ParsableByteArray;
import com.mbridge.msdk.playercommon.exoplayer2.util.Util;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
public final class Mp4Extractor implements Extractor, SeekMap {
public static final int FLAG_WORKAROUND_IGNORE_EDIT_LISTS = 1;
private static final long MAXIMUM_READ_AHEAD_BYTES_STREAM = 10485760;
private static final long RELOAD_MINIMUM_SEEK_DISTANCE = 262144;
private static final int STATE_READING_ATOM_HEADER = 0;
private static final int STATE_READING_ATOM_PAYLOAD = 1;
private static final int STATE_READING_SAMPLE = 2;
private long[][] accumulatedSampleSizes;
private ParsableByteArray atomData;
private final ParsableByteArray atomHeader;
private int atomHeaderBytesRead;
private long atomSize;
private int atomType;
private final ArrayDeque<Atom.ContainerAtom> containerAtoms;
private long durationUs;
private ExtractorOutput extractorOutput;
private int firstVideoTrackIndex;
private final int flags;
private boolean isQuickTime;
private final ParsableByteArray nalLength;
private final ParsableByteArray nalStartCode;
private int parserState;
private int sampleBytesWritten;
private int sampleCurrentNalBytesRemaining;
private int sampleTrackIndex;
private Mp4Track[] tracks;
public static final ExtractorsFactory FACTORY = new ExtractorsFactory() {
@Override
public final Extractor[] createExtractors() {
return new Extractor[]{new Mp4Extractor()};
}
};
private static final int BRAND_QUICKTIME = Util.getIntegerCodeForString("qt ");
public @interface Flags {
}
public static final class Mp4Track {
public int sampleIndex;
public final TrackSampleTable sampleTable;
public final Track track;
public final TrackOutput trackOutput;
public Mp4Track(Track track, TrackSampleTable trackSampleTable, TrackOutput trackOutput) {
this.track = track;
this.sampleTable = trackSampleTable;
this.trackOutput = trackOutput;
}
}
private @interface State {
}
public Mp4Extractor() {
this(0);
}
private static long[][] calculateAccumulatedSampleSizes(Mp4Track[] mp4TrackArr) {
long[][] jArr = new long[mp4TrackArr.length];
int[] iArr = new int[mp4TrackArr.length];
long[] jArr2 = new long[mp4TrackArr.length];
boolean[] zArr = new boolean[mp4TrackArr.length];
for (int i6 = 0; i6 < mp4TrackArr.length; i6++) {
jArr[i6] = new long[mp4TrackArr[i6].sampleTable.sampleCount];
jArr2[i6] = mp4TrackArr[i6].sampleTable.timestampsUs[0];
}
long j6 = 0;
int i7 = 0;
while (i7 < mp4TrackArr.length) {
long j7 = Long.MAX_VALUE;
int i8 = -1;
for (int i9 = 0; i9 < mp4TrackArr.length; i9++) {
if (!zArr[i9]) {
long j8 = jArr2[i9];
if (j8 <= j7) {
i8 = i9;
j7 = j8;
}
}
}
int i10 = iArr[i8];
long[] jArr3 = jArr[i8];
jArr3[i10] = j6;
TrackSampleTable trackSampleTable = mp4TrackArr[i8].sampleTable;
j6 += trackSampleTable.sizes[i10];
int i11 = i10 + 1;
iArr[i8] = i11;
if (i11 < jArr3.length) {
jArr2[i8] = trackSampleTable.timestampsUs[i11];
} else {
zArr[i8] = true;
i7++;
}
}
return jArr;
}
private void enterReadingAtomHeaderState() {
this.parserState = 0;
this.atomHeaderBytesRead = 0;
}
private static int getSynchronizationSampleIndex(TrackSampleTable trackSampleTable, long j6) {
int indexOfEarlierOrEqualSynchronizationSample = trackSampleTable.getIndexOfEarlierOrEqualSynchronizationSample(j6);
if (indexOfEarlierOrEqualSynchronizationSample == -1) {
return trackSampleTable.getIndexOfLaterOrEqualSynchronizationSample(j6);
}
return indexOfEarlierOrEqualSynchronizationSample;
}
private int getTrackIndexOfNextReadSample(long j6) {
boolean z6;
int i6 = -1;
int i7 = -1;
int i8 = 0;
long j7 = Long.MAX_VALUE;
boolean z7 = true;
long j8 = Long.MAX_VALUE;
boolean z8 = true;
long j9 = Long.MAX_VALUE;
while (true) {
Mp4Track[] mp4TrackArr = this.tracks;
if (i8 >= mp4TrackArr.length) {
break;
}
Mp4Track mp4Track = mp4TrackArr[i8];
int i9 = mp4Track.sampleIndex;
TrackSampleTable trackSampleTable = mp4Track.sampleTable;
if (i9 != trackSampleTable.sampleCount) {
long j10 = trackSampleTable.offsets[i9];
long j11 = this.accumulatedSampleSizes[i8][i9];
long j12 = j10 - j6;
if (j12 >= 0 && j12 < RELOAD_MINIMUM_SEEK_DISTANCE) {
z6 = false;
} else {
z6 = true;
}
if ((!z6 && z8) || (z6 == z8 && j12 < j9)) {
z8 = z6;
j9 = j12;
i7 = i8;
j8 = j11;
}
if (j11 < j7) {
z7 = z6;
i6 = i8;
j7 = j11;
}
}
i8++;
}
if (j7 == Long.MAX_VALUE || !z7 || j8 < j7 + MAXIMUM_READ_AHEAD_BYTES_STREAM) {
return i7;
}
return i6;
}
private ArrayList<TrackSampleTable> getTrackSampleTables(Atom.ContainerAtom containerAtom, GaplessInfoHolder gaplessInfoHolder, boolean z6) throws ParserException {
Track parseTrak;
ArrayList<TrackSampleTable> arrayList = new ArrayList<>();
for (int i6 = 0; i6 < containerAtom.containerChildren.size(); i6++) {
Atom.ContainerAtom containerAtom2 = containerAtom.containerChildren.get(i6);
if (containerAtom2.type == Atom.TYPE_trak && (parseTrak = AtomParsers.parseTrak(containerAtom2, containerAtom.getLeafAtomOfType(Atom.TYPE_mvhd), C.TIME_UNSET, null, z6, this.isQuickTime)) != null) {
TrackSampleTable parseStbl = AtomParsers.parseStbl(parseTrak, containerAtom2.getContainerAtomOfType(Atom.TYPE_mdia).getContainerAtomOfType(Atom.TYPE_minf).getContainerAtomOfType(Atom.TYPE_stbl), gaplessInfoHolder);
if (parseStbl.sampleCount != 0) {
arrayList.add(parseStbl);
}
}
}
return arrayList;
}
private static long maybeAdjustSeekOffset(TrackSampleTable trackSampleTable, long j6, long j7) {
int synchronizationSampleIndex = getSynchronizationSampleIndex(trackSampleTable, j6);
if (synchronizationSampleIndex == -1) {
return j7;
}
return Math.min(trackSampleTable.offsets[synchronizationSampleIndex], j7);
}
private void processAtomEnded(long j6) throws ParserException {
while (!this.containerAtoms.isEmpty() && this.containerAtoms.peek().endPosition == j6) {
Atom.ContainerAtom pop = this.containerAtoms.pop();
if (pop.type == Atom.TYPE_moov) {
processMoovAtom(pop);
this.containerAtoms.clear();
this.parserState = 2;
} else if (!this.containerAtoms.isEmpty()) {
this.containerAtoms.peek().add(pop);
}
}
if (this.parserState != 2) {
enterReadingAtomHeaderState();
}
}
private static boolean processFtypAtom(ParsableByteArray parsableByteArray) {
parsableByteArray.setPosition(8);
if (parsableByteArray.readInt() == BRAND_QUICKTIME) {
return true;
}
parsableByteArray.skipBytes(4);
while (parsableByteArray.bytesLeft() > 0) {
if (parsableByteArray.readInt() == BRAND_QUICKTIME) {
return true;
}
}
return false;
}
private void processMoovAtom(Atom.ContainerAtom containerAtom) throws ParserException {
Metadata metadata;
boolean z6;
ArrayList<TrackSampleTable> trackSampleTables;
ArrayList arrayList = new ArrayList();
GaplessInfoHolder gaplessInfoHolder = new GaplessInfoHolder();
Atom.LeafAtom leafAtomOfType = containerAtom.getLeafAtomOfType(Atom.TYPE_udta);
if (leafAtomOfType != null) {
metadata = AtomParsers.parseUdta(leafAtomOfType, this.isQuickTime);
if (metadata != null) {
gaplessInfoHolder.setFromMetadata(metadata);
}
} else {
metadata = null;
}
int i6 = 1;
int i7 = 0;
if ((this.flags & 1) != 0) {
z6 = true;
} else {
z6 = false;
}
try {
trackSampleTables = getTrackSampleTables(containerAtom, gaplessInfoHolder, z6);
} catch (AtomParsers.UnhandledEditListException unused) {
gaplessInfoHolder = new GaplessInfoHolder();
trackSampleTables = getTrackSampleTables(containerAtom, gaplessInfoHolder, true);
}
int size = trackSampleTables.size();
int i8 = -1;
long j6 = C.TIME_UNSET;
while (i7 < size) {
TrackSampleTable trackSampleTable = trackSampleTables.get(i7);
Track track = trackSampleTable.track;
Mp4Track mp4Track = new Mp4Track(track, trackSampleTable, this.extractorOutput.track(i7, track.type));
Format copyWithMaxInputSize = track.format.copyWithMaxInputSize(trackSampleTable.maximumSize + 30);
if (track.type == i6) {
if (gaplessInfoHolder.hasGaplessInfo()) {
copyWithMaxInputSize = copyWithMaxInputSize.copyWithGaplessInfo(gaplessInfoHolder.encoderDelay, gaplessInfoHolder.encoderPadding);
}
if (metadata != null) {
copyWithMaxInputSize = copyWithMaxInputSize.copyWithMetadata(metadata);
}
}
mp4Track.trackOutput.format(copyWithMaxInputSize);
long j7 = track.durationUs;
if (j7 == C.TIME_UNSET) {
j7 = trackSampleTable.durationUs;
}
j6 = Math.max(j6, j7);
if (track.type == 2 && i8 == -1) {
i8 = arrayList.size();
}
arrayList.add(mp4Track);
i7++;
i6 = 1;
}
this.firstVideoTrackIndex = i8;
this.durationUs = j6;
Mp4Track[] mp4TrackArr = (Mp4Track[]) arrayList.toArray(new Mp4Track[arrayList.size()]);
this.tracks = mp4TrackArr;
this.accumulatedSampleSizes = calculateAccumulatedSampleSizes(mp4TrackArr);
this.extractorOutput.endTracks();
this.extractorOutput.seekMap(this);
}
private boolean readAtomHeader(ExtractorInput extractorInput) throws IOException, InterruptedException {
boolean z6;
boolean z7;
if (this.atomHeaderBytesRead == 0) {
if (!extractorInput.readFully(this.atomHeader.data, 0, 8, true)) {
return false;
}
this.atomHeaderBytesRead = 8;
this.atomHeader.setPosition(0);
this.atomSize = this.atomHeader.readUnsignedInt();
this.atomType = this.atomHeader.readInt();
}
long j6 = this.atomSize;
if (j6 == 1) {
extractorInput.readFully(this.atomHeader.data, 8, 8);
this.atomHeaderBytesRead += 8;
this.atomSize = this.atomHeader.readUnsignedLongToLong();
} else if (j6 == 0) {
long length = extractorInput.getLength();
if (length == -1 && !this.containerAtoms.isEmpty()) {
length = this.containerAtoms.peek().endPosition;
}
if (length != -1) {
this.atomSize = (length - extractorInput.getPosition()) + this.atomHeaderBytesRead;
}
}
if (this.atomSize >= this.atomHeaderBytesRead) {
if (shouldParseContainerAtom(this.atomType)) {
long position = (extractorInput.getPosition() + this.atomSize) - this.atomHeaderBytesRead;
this.containerAtoms.push(new Atom.ContainerAtom(this.atomType, position));
if (this.atomSize == this.atomHeaderBytesRead) {
processAtomEnded(position);
} else {
enterReadingAtomHeaderState();
}
} else if (shouldParseLeafAtom(this.atomType)) {
if (this.atomHeaderBytesRead == 8) {
z6 = true;
} else {
z6 = false;
}
Assertions.checkState(z6);
if (this.atomSize <= 2147483647L) {
z7 = true;
} else {
z7 = false;
}
Assertions.checkState(z7);
ParsableByteArray parsableByteArray = new ParsableByteArray((int) this.atomSize);
this.atomData = parsableByteArray;
System.arraycopy(this.atomHeader.data, 0, parsableByteArray.data, 0, 8);
this.parserState = 1;
} else {
this.atomData = null;
this.parserState = 1;
}
return true;
}
throw new ParserException("Atom size less than header length (unsupported).");
}
private boolean readAtomPayload(ExtractorInput extractorInput, PositionHolder positionHolder) throws IOException, InterruptedException {
boolean z6;
long j6 = this.atomSize - this.atomHeaderBytesRead;
long position = extractorInput.getPosition() + j6;
ParsableByteArray parsableByteArray = this.atomData;
if (parsableByteArray != null) {
extractorInput.readFully(parsableByteArray.data, this.atomHeaderBytesRead, (int) j6);
if (this.atomType == Atom.TYPE_ftyp) {
this.isQuickTime = processFtypAtom(this.atomData);
} else if (!this.containerAtoms.isEmpty()) {
this.containerAtoms.peek().add(new Atom.LeafAtom(this.atomType, this.atomData));
}
} else if (j6 < RELOAD_MINIMUM_SEEK_DISTANCE) {
extractorInput.skipFully((int) j6);
} else {
positionHolder.position = extractorInput.getPosition() + j6;
z6 = true;
processAtomEnded(position);
if (!z6 && this.parserState != 2) {
return true;
}
return false;
}
z6 = false;
processAtomEnded(position);
if (!z6) {
}
return false;
}
private int readSample(ExtractorInput extractorInput, PositionHolder positionHolder) throws IOException, InterruptedException {
long position = extractorInput.getPosition();
if (this.sampleTrackIndex == -1) {
int trackIndexOfNextReadSample = getTrackIndexOfNextReadSample(position);
this.sampleTrackIndex = trackIndexOfNextReadSample;
if (trackIndexOfNextReadSample == -1) {
return -1;
}
}
Mp4Track mp4Track = this.tracks[this.sampleTrackIndex];
TrackOutput trackOutput = mp4Track.trackOutput;
int i6 = mp4Track.sampleIndex;
TrackSampleTable trackSampleTable = mp4Track.sampleTable;
long j6 = trackSampleTable.offsets[i6];
int i7 = trackSampleTable.sizes[i6];
long j7 = (j6 - position) + this.sampleBytesWritten;
if (j7 >= 0 && j7 < RELOAD_MINIMUM_SEEK_DISTANCE) {
if (mp4Track.track.sampleTransformation == 1) {
j7 += 8;
i7 -= 8;
}
extractorInput.skipFully((int) j7);
int i8 = mp4Track.track.nalUnitLengthFieldLength;
if (i8 == 0) {
while (true) {
int i9 = this.sampleBytesWritten;
if (i9 >= i7) {
break;
}
int sampleData = trackOutput.sampleData(extractorInput, i7 - i9, false);
this.sampleBytesWritten += sampleData;
this.sampleCurrentNalBytesRemaining -= sampleData;
}
} else {
byte[] bArr = this.nalLength.data;
bArr[0] = 0;
bArr[1] = 0;
bArr[2] = 0;
int i10 = 4 - i8;
while (this.sampleBytesWritten < i7) {
int i11 = this.sampleCurrentNalBytesRemaining;
if (i11 == 0) {
extractorInput.readFully(this.nalLength.data, i10, i8);
this.nalLength.setPosition(0);
this.sampleCurrentNalBytesRemaining = this.nalLength.readUnsignedIntToInt();
this.nalStartCode.setPosition(0);
trackOutput.sampleData(this.nalStartCode, 4);
this.sampleBytesWritten += 4;
i7 += i10;
} else {
int sampleData2 = trackOutput.sampleData(extractorInput, i11, false);
this.sampleBytesWritten += sampleData2;
this.sampleCurrentNalBytesRemaining -= sampleData2;
}
}
}
TrackSampleTable trackSampleTable2 = mp4Track.sampleTable;
trackOutput.sampleMetadata(trackSampleTable2.timestampsUs[i6], trackSampleTable2.flags[i6], i7, 0, null);
mp4Track.sampleIndex++;
this.sampleTrackIndex = -1;
this.sampleBytesWritten = 0;
this.sampleCurrentNalBytesRemaining = 0;
return 0;
}
positionHolder.position = j6;
return 1;
}
private static boolean shouldParseContainerAtom(int i6) {
if (i6 != Atom.TYPE_moov && i6 != Atom.TYPE_trak && i6 != Atom.TYPE_mdia && i6 != Atom.TYPE_minf && i6 != Atom.TYPE_stbl && i6 != Atom.TYPE_edts) {
return false;
}
return true;
}
private static boolean shouldParseLeafAtom(int i6) {
if (i6 != Atom.TYPE_mdhd && i6 != Atom.TYPE_mvhd && i6 != Atom.TYPE_hdlr && i6 != Atom.TYPE_stsd && i6 != Atom.TYPE_stts && i6 != Atom.TYPE_stss && i6 != Atom.TYPE_ctts && i6 != Atom.TYPE_elst && i6 != Atom.TYPE_stsc && i6 != Atom.TYPE_stsz && i6 != Atom.TYPE_stz2 && i6 != Atom.TYPE_stco && i6 != Atom.TYPE_co64 && i6 != Atom.TYPE_tkhd && i6 != Atom.TYPE_ftyp && i6 != Atom.TYPE_udta) {
return false;
}
return true;
}
private void updateSampleIndices(long j6) {
for (Mp4Track mp4Track : this.tracks) {
TrackSampleTable trackSampleTable = mp4Track.sampleTable;
int indexOfEarlierOrEqualSynchronizationSample = trackSampleTable.getIndexOfEarlierOrEqualSynchronizationSample(j6);
if (indexOfEarlierOrEqualSynchronizationSample == -1) {
indexOfEarlierOrEqualSynchronizationSample = trackSampleTable.getIndexOfLaterOrEqualSynchronizationSample(j6);
}
mp4Track.sampleIndex = indexOfEarlierOrEqualSynchronizationSample;
}
}
@Override
public final long getDurationUs() {
return this.durationUs;
}
@Override
public final SeekMap.SeekPoints getSeekPoints(long j6) {
long j7;
long j8;
int indexOfLaterOrEqualSynchronizationSample;
Mp4Track[] mp4TrackArr = this.tracks;
if (mp4TrackArr.length == 0) {
return new SeekMap.SeekPoints(SeekPoint.START);
}
int i6 = this.firstVideoTrackIndex;
long j9 = -1;
if (i6 != -1) {
TrackSampleTable trackSampleTable = mp4TrackArr[i6].sampleTable;
int synchronizationSampleIndex = getSynchronizationSampleIndex(trackSampleTable, j6);
if (synchronizationSampleIndex == -1) {
return new SeekMap.SeekPoints(SeekPoint.START);
}
long j10 = trackSampleTable.timestampsUs[synchronizationSampleIndex];
j7 = trackSampleTable.offsets[synchronizationSampleIndex];
if (j10 < j6 && synchronizationSampleIndex < trackSampleTable.sampleCount - 1 && (indexOfLaterOrEqualSynchronizationSample = trackSampleTable.getIndexOfLaterOrEqualSynchronizationSample(j6)) != -1 && indexOfLaterOrEqualSynchronizationSample != synchronizationSampleIndex) {
j8 = trackSampleTable.timestampsUs[indexOfLaterOrEqualSynchronizationSample];
j9 = trackSampleTable.offsets[indexOfLaterOrEqualSynchronizationSample];
} else {
j8 = -9223372036854775807L;
}
j6 = j10;
} else {
j7 = Long.MAX_VALUE;
j8 = -9223372036854775807L;
}
int i7 = 0;
while (true) {
Mp4Track[] mp4TrackArr2 = this.tracks;
if (i7 >= mp4TrackArr2.length) {
break;
}
if (i7 != this.firstVideoTrackIndex) {
TrackSampleTable trackSampleTable2 = mp4TrackArr2[i7].sampleTable;
long maybeAdjustSeekOffset = maybeAdjustSeekOffset(trackSampleTable2, j6, j7);
if (j8 != C.TIME_UNSET) {
j9 = maybeAdjustSeekOffset(trackSampleTable2, j8, j9);
}
j7 = maybeAdjustSeekOffset;
}
i7++;
}
SeekPoint seekPoint = new SeekPoint(j6, j7);
if (j8 == C.TIME_UNSET) {
return new SeekMap.SeekPoints(seekPoint);
}
return new SeekMap.SeekPoints(seekPoint, new SeekPoint(j8, j9));
}
@Override
public final void init(ExtractorOutput extractorOutput) {
this.extractorOutput = extractorOutput;
}
@Override
public final boolean isSeekable() {
return true;
}
@Override
public final int read(ExtractorInput extractorInput, PositionHolder positionHolder) throws IOException, InterruptedException {
while (true) {
int i6 = this.parserState;
if (i6 != 0) {
if (i6 != 1) {
if (i6 == 2) {
return readSample(extractorInput, positionHolder);
}
throw new IllegalStateException();
}
if (readAtomPayload(extractorInput, positionHolder)) {
return 1;
}
} else if (!readAtomHeader(extractorInput)) {
return -1;
}
}
}
@Override
public final void release() {
}
@Override
public final void seek(long j6, long j7) {
this.containerAtoms.clear();
this.atomHeaderBytesRead = 0;
this.sampleTrackIndex = -1;
this.sampleBytesWritten = 0;
this.sampleCurrentNalBytesRemaining = 0;
if (j6 == 0) {
enterReadingAtomHeaderState();
} else if (this.tracks != null) {
updateSampleIndices(j7);
}
}
@Override
public final boolean sniff(ExtractorInput extractorInput) throws IOException, InterruptedException {
return Sniffer.sniffUnfragmented(extractorInput);
}
public Mp4Extractor(int i6) {
this.flags = i6;
this.atomHeader = new ParsableByteArray(16);
this.containerAtoms = new ArrayDeque<>();
this.nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
this.nalLength = new ParsableByteArray(4);
this.sampleTrackIndex = -1;
}
}