mirror of
https://github.com/androidx/media.git
synced 2025-05-17 12:39:52 +08:00
Parse EMSG boxes in FragmentedMp4Extractor
Issue: #2176 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=143776526
This commit is contained in:
parent
f2634d11f5
commit
1da9e31d68
@ -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.
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user