Parse EMSG boxes in FragmentedMp4Extractor

Issue: #2176

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=143776526
This commit is contained in:
olly 2017-01-06 08:23:26 -08:00 committed by Oliver Woodman
parent f2634d11f5
commit 1da9e31d68

View File

@ -44,6 +44,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Stack; import java.util.Stack;
import java.util.UUID; import java.util.UUID;
@ -123,6 +124,7 @@ public final class FragmentedMp4Extractor implements Extractor {
private final ParsableByteArray atomHeader; private final ParsableByteArray atomHeader;
private final byte[] extendedTypeScratch; private final byte[] extendedTypeScratch;
private final Stack<ContainerAtom> containerAtoms; private final Stack<ContainerAtom> containerAtoms;
private final LinkedList<MetadataSampleInfo> pendingMetadataSampleInfos;
private int parserState; private int parserState;
private int atomType; private int atomType;
@ -130,8 +132,10 @@ public final class FragmentedMp4Extractor implements Extractor {
private int atomHeaderBytesRead; private int atomHeaderBytesRead;
private ParsableByteArray atomData; private ParsableByteArray atomData;
private long endOfMdatPosition; private long endOfMdatPosition;
private int pendingMetadataSampleBytes;
private long durationUs; private long durationUs;
private long segmentIndexEarliestPresentationTimeUs;
private TrackBundle currentTrackBundle; private TrackBundle currentTrackBundle;
private int sampleSize; private int sampleSize;
private int sampleBytesWritten; private int sampleBytesWritten;
@ -139,6 +143,7 @@ public final class FragmentedMp4Extractor implements Extractor {
// Extractor output. // Extractor output.
private ExtractorOutput extractorOutput; private ExtractorOutput extractorOutput;
private TrackOutput metadataTrackOutput;
// Whether extractorOutput.seekMap has been called. // Whether extractorOutput.seekMap has been called.
private boolean haveOutputSeekMap; private boolean haveOutputSeekMap;
@ -172,8 +177,10 @@ public final class FragmentedMp4Extractor implements Extractor {
encryptionSignalByte = new ParsableByteArray(1); encryptionSignalByte = new ParsableByteArray(1);
extendedTypeScratch = new byte[16]; extendedTypeScratch = new byte[16];
containerAtoms = new Stack<>(); containerAtoms = new Stack<>();
pendingMetadataSampleInfos = new LinkedList<>();
trackBundles = new SparseArray<>(); trackBundles = new SparseArray<>();
durationUs = C.TIME_UNSET; durationUs = C.TIME_UNSET;
segmentIndexEarliestPresentationTimeUs = C.TIME_UNSET;
enterReadingAtomHeaderState(); enterReadingAtomHeaderState();
} }
@ -199,6 +206,8 @@ public final class FragmentedMp4Extractor implements Extractor {
for (int i = 0; i < trackCount; i++) { for (int i = 0; i < trackCount; i++) {
trackBundles.valueAt(i).reset(); trackBundles.valueAt(i).reset();
} }
pendingMetadataSampleInfos.clear();
pendingMetadataSampleBytes = 0;
containerAtoms.clear(); containerAtoms.clear();
enterReadingAtomHeaderState(); enterReadingAtomHeaderState();
} }
@ -336,11 +345,12 @@ public final class FragmentedMp4Extractor implements Extractor {
if (!containerAtoms.isEmpty()) { if (!containerAtoms.isEmpty()) {
containerAtoms.peek().add(leaf); containerAtoms.peek().add(leaf);
} else if (leaf.type == Atom.TYPE_sidx) { } else if (leaf.type == Atom.TYPE_sidx) {
ChunkIndex segmentIndex = parseSidx(leaf.data, inputPosition); Pair<Long, ChunkIndex> result = parseSidx(leaf.data, inputPosition);
extractorOutput.seekMap(segmentIndex); segmentIndexEarliestPresentationTimeUs = result.first;
extractorOutput.seekMap(result.second);
haveOutputSeekMap = true; haveOutputSeekMap = true;
} else if (leaf.type == Atom.TYPE_emsg) { } else if (leaf.type == Atom.TYPE_emsg) {
onEmsgLeafAtomRead(leaf); onEmsgLeafAtomRead(leaf.data);
} }
} }
@ -419,8 +429,37 @@ public final class FragmentedMp4Extractor implements Extractor {
} }
} }
private void onEmsgLeafAtomRead(LeafAtom emsg) { /**
// Do nothing. * Handles an emsg atom (defined in 23009-1).
*/
private void onEmsgLeafAtomRead(ParsableByteArray atom) {
// TODO: Enable metadata output.
if (metadataTrackOutput == null) {
return;
}
// Parse the event's presentation time delta.
atom.setPosition(Atom.FULL_HEADER_SIZE);
atom.readNullTerminatedString(); // schemeIdUri
atom.readNullTerminatedString(); // value
long timescale = atom.readUnsignedInt();
long presentationTimeDeltaUs =
Util.scaleLargeTimestamp(atom.readUnsignedInt(), C.MICROS_PER_SECOND, timescale);
// Output the sample data.
atom.setPosition(Atom.FULL_HEADER_SIZE);
int sampleSize = atom.bytesLeft();
metadataTrackOutput.sampleData(atom, sampleSize);
// Output the sample metadata.
if (segmentIndexEarliestPresentationTimeUs != C.TIME_UNSET) {
// We can output the sample metadata immediately.
metadataTrackOutput.sampleMetadata(
segmentIndexEarliestPresentationTimeUs + presentationTimeDeltaUs,
C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0 /* offset */, null);
} else {
// We need the first sample timestamp in the segment before we can output the metadata.
pendingMetadataSampleInfos.addLast(
new MetadataSampleInfo(presentationTimeDeltaUs, sampleSize));
pendingMetadataSampleBytes += sampleSize;
}
} }
/** /**
@ -838,8 +877,13 @@ public final class FragmentedMp4Extractor implements Extractor {
/** /**
* Parses a sidx atom (defined in 14496-12). * Parses a sidx atom (defined in 14496-12).
*
* @param atom The atom data.
* @param inputPosition The input position of the first byte after the atom.
* @return A pair consisting of the earliest presentation time in microseconds, and the parsed
* {@link ChunkIndex}.
*/ */
private static ChunkIndex parseSidx(ParsableByteArray atom, long inputPosition) private static Pair<Long, ChunkIndex> parseSidx(ParsableByteArray atom, long inputPosition)
throws ParserException { throws ParserException {
atom.setPosition(Atom.HEADER_SIZE); atom.setPosition(Atom.HEADER_SIZE);
int fullAtom = atom.readInt(); int fullAtom = atom.readInt();
@ -856,6 +900,8 @@ public final class FragmentedMp4Extractor implements Extractor {
earliestPresentationTime = atom.readUnsignedLongToLong(); earliestPresentationTime = atom.readUnsignedLongToLong();
offset += atom.readUnsignedLongToLong(); offset += atom.readUnsignedLongToLong();
} }
long earliestPresentationTimeUs = Util.scaleLargeTimestamp(earliestPresentationTime,
C.MICROS_PER_SECOND, timescale);
atom.skipBytes(2); atom.skipBytes(2);
@ -866,7 +912,7 @@ public final class FragmentedMp4Extractor implements Extractor {
long[] timesUs = new long[referenceCount]; long[] timesUs = new long[referenceCount];
long time = earliestPresentationTime; long time = earliestPresentationTime;
long timeUs = Util.scaleLargeTimestamp(time, C.MICROS_PER_SECOND, timescale); long timeUs = earliestPresentationTimeUs;
for (int i = 0; i < referenceCount; i++) { for (int i = 0; i < referenceCount; i++) {
int firstInt = atom.readInt(); int firstInt = atom.readInt();
@ -890,7 +936,8 @@ public final class FragmentedMp4Extractor implements Extractor {
offset += sizes[i]; offset += sizes[i];
} }
return new ChunkIndex(sizes, offsets, durationsUs, timesUs); return Pair.create(earliestPresentationTimeUs,
new ChunkIndex(sizes, offsets, durationsUs, timesUs));
} }
private void readEncryptionData(ExtractorInput input) throws IOException, InterruptedException { private void readEncryptionData(ExtractorInput input) throws IOException, InterruptedException {
@ -1031,6 +1078,14 @@ public final class FragmentedMp4Extractor implements Extractor {
} }
output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey); output.sampleMetadata(sampleTimeUs, sampleFlags, sampleSize, 0, encryptionKey);
while (!pendingMetadataSampleInfos.isEmpty()) {
MetadataSampleInfo sampleInfo = pendingMetadataSampleInfos.removeFirst();
pendingMetadataSampleBytes -= sampleInfo.size;
metadataTrackOutput.sampleMetadata(
sampleTimeUs + sampleInfo.presentationTimeDeltaUs,
C.BUFFER_FLAG_KEY_FRAME, sampleInfo.size, pendingMetadataSampleBytes, null);
}
currentTrackBundle.currentSampleIndex++; currentTrackBundle.currentSampleIndex++;
currentTrackBundle.currentSampleInTrackRun++; currentTrackBundle.currentSampleInTrackRun++;
if (currentTrackBundle.currentSampleInTrackRun if (currentTrackBundle.currentSampleInTrackRun
@ -1146,6 +1201,21 @@ public final class FragmentedMp4Extractor implements Extractor {
|| atom == Atom.TYPE_traf || atom == Atom.TYPE_mvex || atom == Atom.TYPE_edts; || atom == Atom.TYPE_traf || atom == Atom.TYPE_mvex || atom == Atom.TYPE_edts;
} }
/**
* Holds data corresponding to a metadata sample.
*/
private static final class MetadataSampleInfo {
public final long presentationTimeDeltaUs;
public final int size;
public MetadataSampleInfo(long presentationTimeDeltaUs, int size) {
this.presentationTimeDeltaUs = presentationTimeDeltaUs;
this.size = size;
}
}
/** /**
* Holds data corresponding to a single track. * Holds data corresponding to a single track.
*/ */