use of androidx.media3.extractor.mp4.Atom.ContainerAtom in project media by androidx.
the class Mp4Extractor method processMoovAtom.
/**
* Updates the stored track metadata to reflect the contents of the specified moov atom.
*/
private void processMoovAtom(ContainerAtom moov) throws ParserException {
int firstVideoTrackIndex = C.INDEX_UNSET;
long durationUs = C.TIME_UNSET;
List<Mp4Track> tracks = new ArrayList<>();
// Process metadata.
@Nullable Metadata udtaMetaMetadata = null;
@Nullable Metadata smtaMetadata = null;
boolean isQuickTime = fileType == FILE_TYPE_QUICKTIME;
GaplessInfoHolder gaplessInfoHolder = new GaplessInfoHolder();
@Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
if (udta != null) {
Pair<@NullableType Metadata, @NullableType Metadata> udtaMetadata = AtomParsers.parseUdta(udta);
udtaMetaMetadata = udtaMetadata.first;
smtaMetadata = udtaMetadata.second;
if (udtaMetaMetadata != null) {
gaplessInfoHolder.setFromMetadata(udtaMetaMetadata);
}
}
@Nullable Metadata mdtaMetadata = null;
@Nullable Atom.ContainerAtom meta = moov.getContainerAtomOfType(Atom.TYPE_meta);
if (meta != null) {
mdtaMetadata = AtomParsers.parseMdtaFromMeta(meta);
}
boolean ignoreEditLists = (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0;
List<TrackSampleTable> trackSampleTables = parseTraks(moov, gaplessInfoHolder, /* duration= */
C.TIME_UNSET, /* drmInitData= */
null, ignoreEditLists, isQuickTime, /* modifyTrackFunction= */
track -> track);
ExtractorOutput extractorOutput = checkNotNull(this.extractorOutput);
int trackCount = trackSampleTables.size();
for (int i = 0; i < trackCount; i++) {
TrackSampleTable trackSampleTable = trackSampleTables.get(i);
if (trackSampleTable.sampleCount == 0) {
continue;
}
Track track = trackSampleTable.track;
long trackDurationUs = track.durationUs != C.TIME_UNSET ? track.durationUs : trackSampleTable.durationUs;
durationUs = max(durationUs, trackDurationUs);
Mp4Track mp4Track = new Mp4Track(track, trackSampleTable, extractorOutput.track(i, track.type));
int maxInputSize;
if (MimeTypes.AUDIO_TRUEHD.equals(track.format.sampleMimeType)) {
// TrueHD groups samples per chunks of TRUEHD_RECHUNK_SAMPLE_COUNT samples.
maxInputSize = trackSampleTable.maximumSize * Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT;
} else {
// Each sample has up to three bytes of overhead for the start code that replaces its
// length. Allow ten source samples per output sample, like the platform extractor.
maxInputSize = trackSampleTable.maximumSize + 3 * 10;
}
Format.Builder formatBuilder = track.format.buildUpon();
formatBuilder.setMaxInputSize(maxInputSize);
if (track.type == C.TRACK_TYPE_VIDEO && trackDurationUs > 0 && trackSampleTable.sampleCount > 1) {
float frameRate = trackSampleTable.sampleCount / (trackDurationUs / 1000000f);
formatBuilder.setFrameRate(frameRate);
}
MetadataUtil.setFormatGaplessInfo(track.type, gaplessInfoHolder, formatBuilder);
MetadataUtil.setFormatMetadata(track.type, udtaMetaMetadata, mdtaMetadata, formatBuilder, smtaMetadata, slowMotionMetadataEntries.isEmpty() ? null : new Metadata(slowMotionMetadataEntries));
mp4Track.trackOutput.format(formatBuilder.build());
if (track.type == C.TRACK_TYPE_VIDEO && firstVideoTrackIndex == C.INDEX_UNSET) {
firstVideoTrackIndex = tracks.size();
}
tracks.add(mp4Track);
}
this.firstVideoTrackIndex = firstVideoTrackIndex;
this.durationUs = durationUs;
this.tracks = tracks.toArray(new Mp4Track[0]);
accumulatedSampleSizes = calculateAccumulatedSampleSizes(this.tracks);
extractorOutput.endTracks();
extractorOutput.seekMap(this);
}
use of androidx.media3.extractor.mp4.Atom.ContainerAtom in project media by androidx.
the class Mp4Extractor method readAtomHeader.
private boolean readAtomHeader(ExtractorInput input) throws IOException {
if (atomHeaderBytesRead == 0) {
// Read the standard length atom header.
if (!input.readFully(atomHeader.getData(), 0, Atom.HEADER_SIZE, true)) {
processEndOfStreamReadingAtomHeader();
return false;
}
atomHeaderBytesRead = Atom.HEADER_SIZE;
atomHeader.setPosition(0);
atomSize = atomHeader.readUnsignedInt();
atomType = atomHeader.readInt();
}
if (atomSize == Atom.DEFINES_LARGE_SIZE) {
// Read the large size.
int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
input.readFully(atomHeader.getData(), Atom.HEADER_SIZE, headerBytesRemaining);
atomHeaderBytesRead += headerBytesRemaining;
atomSize = atomHeader.readUnsignedLongToLong();
} else if (atomSize == Atom.EXTENDS_TO_END_SIZE) {
// 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.
long endPosition = input.getLength();
if (endPosition == C.LENGTH_UNSET) {
@Nullable ContainerAtom containerAtom = containerAtoms.peek();
if (containerAtom != null) {
endPosition = containerAtom.endPosition;
}
}
if (endPosition != C.LENGTH_UNSET) {
atomSize = endPosition - input.getPosition() + atomHeaderBytesRead;
}
}
if (atomSize < atomHeaderBytesRead) {
throw ParserException.createForUnsupportedContainerFeature("Atom size less than header length (unsupported).");
}
if (shouldParseContainerAtom(atomType)) {
long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead;
if (atomSize != atomHeaderBytesRead && atomType == Atom.TYPE_meta) {
maybeSkipRemainingMetaAtomHeaderBytes(input);
}
containerAtoms.push(new ContainerAtom(atomType, endPosition));
if (atomSize == atomHeaderBytesRead) {
processAtomEnded(endPosition);
} else {
// Start reading the first child atom.
enterReadingAtomHeaderState();
}
} else if (shouldParseLeafAtom(atomType)) {
// We don't support parsing of leaf atoms that define extended atom sizes, or that have
// lengths greater than Integer.MAX_VALUE.
Assertions.checkState(atomHeaderBytesRead == Atom.HEADER_SIZE);
Assertions.checkState(atomSize <= Integer.MAX_VALUE);
ParsableByteArray atomData = new ParsableByteArray((int) atomSize);
System.arraycopy(atomHeader.getData(), 0, atomData.getData(), 0, Atom.HEADER_SIZE);
this.atomData = atomData;
parserState = STATE_READING_ATOM_PAYLOAD;
} else {
processUnparsedAtom(input.getPosition() - atomHeaderBytesRead);
atomData = null;
parserState = STATE_READING_ATOM_PAYLOAD;
}
return true;
}
use of androidx.media3.extractor.mp4.Atom.ContainerAtom in project ExoPlayer by google.
the class FragmentedMp4Extractor method onMoovContainerAtomRead.
private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException {
checkState(sideloadedTrack == null, "Unexpected moov box.");
@Nullable DrmInitData drmInitData = getDrmInitDataFromAtoms(moov.leafChildren);
// Read declaration of track fragments in the moov box.
ContainerAtom mvex = checkNotNull(moov.getContainerAtomOfType(Atom.TYPE_mvex));
SparseArray<DefaultSampleValues> defaultSampleValuesArray = new SparseArray<>();
long duration = C.TIME_UNSET;
int mvexChildrenSize = mvex.leafChildren.size();
for (int i = 0; i < mvexChildrenSize; i++) {
Atom.LeafAtom atom = mvex.leafChildren.get(i);
if (atom.type == Atom.TYPE_trex) {
Pair<Integer, DefaultSampleValues> trexData = parseTrex(atom.data);
defaultSampleValuesArray.put(trexData.first, trexData.second);
} else if (atom.type == Atom.TYPE_mehd) {
duration = parseMehd(atom.data);
}
}
// Construction of tracks and sample tables.
List<TrackSampleTable> sampleTables = parseTraks(moov, new GaplessInfoHolder(), duration, drmInitData, /* ignoreEditLists= */
(flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0, /* isQuickTime= */
false, this::modifyTrack);
int trackCount = sampleTables.size();
if (trackBundles.size() == 0) {
// We need to create the track bundles.
for (int i = 0; i < trackCount; i++) {
TrackSampleTable sampleTable = sampleTables.get(i);
Track track = sampleTable.track;
TrackBundle trackBundle = new TrackBundle(extractorOutput.track(i, track.type), sampleTable, getDefaultSampleValues(defaultSampleValuesArray, track.id));
trackBundles.put(track.id, trackBundle);
durationUs = max(durationUs, track.durationUs);
}
extractorOutput.endTracks();
} else {
checkState(trackBundles.size() == trackCount);
for (int i = 0; i < trackCount; i++) {
TrackSampleTable sampleTable = sampleTables.get(i);
Track track = sampleTable.track;
trackBundles.get(track.id).reset(sampleTable, getDefaultSampleValues(defaultSampleValuesArray, track.id));
}
}
}
use of androidx.media3.extractor.mp4.Atom.ContainerAtom in project edx-app-android by edx.
the class FragmentedMp4Extractor method onMoovContainerAtomRead.
private void onMoovContainerAtomRead(ContainerAtom moov) {
List<Atom> moovChildren = moov.children;
int moovChildrenSize = moovChildren.size();
for (int i = 0; i < moovChildrenSize; i++) {
Atom child = moovChildren.get(i);
if (child.type == Atom.TYPE_pssh) {
ParsableByteArray psshAtom = ((LeafAtom) child).data;
psshAtom.setPosition(FULL_ATOM_HEADER_SIZE);
UUID uuid = new UUID(psshAtom.readLong(), psshAtom.readLong());
int dataSize = psshAtom.readInt();
byte[] data = new byte[dataSize];
psshAtom.readBytes(data, 0, dataSize);
psshData.put(uuid, data);
}
}
ContainerAtom mvex = moov.getContainerAtomOfType(Atom.TYPE_mvex);
extendsDefaults = parseTrex(mvex.getLeafAtomOfType(Atom.TYPE_trex).data);
track = parseTrak(moov.getContainerAtomOfType(Atom.TYPE_trak));
}
use of androidx.media3.extractor.mp4.Atom.ContainerAtom in project edx-app-android by edx.
the class FragmentedMp4Extractor method parseTrak.
/**
* Parses a trak atom (defined in 14496-12).
*/
private static Track parseTrak(ContainerAtom trak) {
ContainerAtom mdia = trak.getContainerAtomOfType(Atom.TYPE_mdia);
int trackType = parseHdlr(mdia.getLeafAtomOfType(Atom.TYPE_hdlr).data);
Assertions.checkState(trackType == Track.TYPE_AUDIO || trackType == Track.TYPE_VIDEO);
Pair<Integer, Long> header = parseTkhd(trak.getLeafAtomOfType(Atom.TYPE_tkhd).data);
int id = header.first;
// TODO: This value should be used to set a duration field on the Track object
// instantiated below, however we've found examples where the value is 0. Revisit whether we
// should set it anyway (and just have it be wrong for bad media streams).
// long duration = header.second;
long timescale = parseMdhd(mdia.getLeafAtomOfType(Atom.TYPE_mdhd).data);
ContainerAtom stbl = mdia.getContainerAtomOfType(Atom.TYPE_minf).getContainerAtomOfType(Atom.TYPE_stbl);
Pair<MediaFormat, TrackEncryptionBox[]> sampleDescriptions = parseStsd(stbl.getLeafAtomOfType(Atom.TYPE_stsd).data);
return new Track(id, trackType, timescale, sampleDescriptions.first, sampleDescriptions.second);
}
Aggregations