mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Rename Atom
to Mp4Box
, and move it to container
module
This makes the class available to custom MP4-parsing implementations, while also allowing it to be used by `muxer` in future. 'Box' is the term used throughout the ISO 14496-12 spec, while the 'Atom' nomenclature was used in an earlier form of the spec (Quicktime). This change moves it from `extractor.mp4.Atom` to `container.Mp4Box`, to be consistent with existing MP4-specific types in the `container` module like `Mp4TimestampData`. PiperOrigin-RevId: 663274752
This commit is contained in:
parent
4df5ecb045
commit
ee27334f06
@ -13,30 +13,33 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.extractor.mp4;
|
||||
package androidx.media3.container;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/** A representation of an MP4 box (aka atom). */
|
||||
@SuppressWarnings("ConstantField")
|
||||
/* package */ abstract class Atom {
|
||||
@UnstableApi
|
||||
public abstract class Mp4Box {
|
||||
|
||||
/** Size of an atom header, in bytes. */
|
||||
/** Size of a box header, in bytes. */
|
||||
public static final int HEADER_SIZE = 8;
|
||||
|
||||
/** Size of a full atom header, in bytes. */
|
||||
/** Size of a full box header, in bytes. */
|
||||
public static final int FULL_HEADER_SIZE = 12;
|
||||
|
||||
/** Size of a long atom header, in bytes. */
|
||||
/** Size of a long box header, in bytes. */
|
||||
public static final int LONG_HEADER_SIZE = 16;
|
||||
|
||||
/** Value for the size field in an atom that defines its size in the largesize field. */
|
||||
/** Value for the size field in a box that defines its size in the largesize field. */
|
||||
public static final int DEFINES_LARGE_SIZE = 1;
|
||||
|
||||
/** Value for the size field in an atom that extends to the end of the file. */
|
||||
/** Value for the size field in a box that extends to the end of the file. */
|
||||
public static final int EXTENDS_TO_END_SIZE = 0;
|
||||
|
||||
@SuppressWarnings("ConstantCaseForConstants")
|
||||
@ -451,43 +454,44 @@ import java.util.List;
|
||||
|
||||
public final int type;
|
||||
|
||||
public Atom(int type) {
|
||||
// private to only allow sub-classing from within this file.
|
||||
private Mp4Box(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getAtomTypeString(type);
|
||||
return getBoxTypeString(type);
|
||||
}
|
||||
|
||||
/** An MP4 atom that is a leaf. */
|
||||
/* package */ static final class LeafAtom extends Atom {
|
||||
/** An MP4 box that is a leaf. */
|
||||
public static final class LeafBox extends Mp4Box {
|
||||
|
||||
/** The atom data. */
|
||||
/** The box data. */
|
||||
public final ParsableByteArray data;
|
||||
|
||||
/**
|
||||
* @param type The type of the atom.
|
||||
* @param data The atom data.
|
||||
* @param type The type of the box.
|
||||
* @param data The box data.
|
||||
*/
|
||||
public LeafAtom(int type, ParsableByteArray data) {
|
||||
public LeafBox(int type, ParsableByteArray data) {
|
||||
super(type);
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
/** An MP4 atom that has child atoms. */
|
||||
/* package */ static final class ContainerAtom extends Atom {
|
||||
/** An MP4 box that has child boxes. */
|
||||
public static final class ContainerBox extends Mp4Box {
|
||||
|
||||
public final long endPosition;
|
||||
public final List<LeafAtom> leafChildren;
|
||||
public final List<ContainerAtom> containerChildren;
|
||||
public final List<LeafBox> leafChildren;
|
||||
public final List<ContainerBox> containerChildren;
|
||||
|
||||
/**
|
||||
* @param type The type of the atom.
|
||||
* @param endPosition The position of the first byte after the end of the atom.
|
||||
* @param type The type of the box.
|
||||
* @param endPosition The position of the first byte after the end of the box.
|
||||
*/
|
||||
public ContainerAtom(int type, long endPosition) {
|
||||
public ContainerBox(int type, long endPosition) {
|
||||
super(type);
|
||||
this.endPosition = endPosition;
|
||||
leafChildren = new ArrayList<>();
|
||||
@ -497,19 +501,19 @@ import java.util.List;
|
||||
/**
|
||||
* Adds a child leaf to this container.
|
||||
*
|
||||
* @param atom The child to add.
|
||||
* @param box The child to add.
|
||||
*/
|
||||
public void add(LeafAtom atom) {
|
||||
leafChildren.add(atom);
|
||||
public void add(LeafBox box) {
|
||||
leafChildren.add(box);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child container to this container.
|
||||
*
|
||||
* @param atom The child to add.
|
||||
* @param box The child to add.
|
||||
*/
|
||||
public void add(ContainerAtom atom) {
|
||||
containerChildren.add(atom);
|
||||
public void add(ContainerBox box) {
|
||||
containerChildren.add(box);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -522,12 +526,12 @@ import java.util.List;
|
||||
* @return The child leaf of the given type, or null if no such child exists.
|
||||
*/
|
||||
@Nullable
|
||||
public LeafAtom getLeafAtomOfType(int type) {
|
||||
public LeafBox getLeafBoxOfType(int type) {
|
||||
int childrenSize = leafChildren.size();
|
||||
for (int i = 0; i < childrenSize; i++) {
|
||||
LeafAtom atom = leafChildren.get(i);
|
||||
if (atom.type == type) {
|
||||
return atom;
|
||||
LeafBox box = leafChildren.get(i);
|
||||
if (box.type == type) {
|
||||
return box;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -543,12 +547,12 @@ import java.util.List;
|
||||
* @return The child container of the given type, or null if no such child exists.
|
||||
*/
|
||||
@Nullable
|
||||
public ContainerAtom getContainerAtomOfType(int type) {
|
||||
public ContainerBox getContainerBoxOfType(int type) {
|
||||
int childrenSize = containerChildren.size();
|
||||
for (int i = 0; i < childrenSize; i++) {
|
||||
ContainerAtom atom = containerChildren.get(i);
|
||||
if (atom.type == type) {
|
||||
return atom;
|
||||
ContainerBox box = containerChildren.get(i);
|
||||
if (box.type == type) {
|
||||
return box;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -556,7 +560,7 @@ import java.util.List;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getAtomTypeString(type)
|
||||
return getBoxTypeString(type)
|
||||
+ " leaves: "
|
||||
+ Arrays.toString(leafChildren.toArray())
|
||||
+ " containers: "
|
||||
@ -565,12 +569,12 @@ import java.util.List;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a numeric atom type to the corresponding four character string.
|
||||
* Converts a numeric box type to the corresponding four character string.
|
||||
*
|
||||
* @param type The numeric atom type.
|
||||
* @param type The numeric box type.
|
||||
* @return The corresponding four character string.
|
||||
*/
|
||||
public static String getAtomTypeString(int type) {
|
||||
public static String getBoxTypeString(int type) {
|
||||
return ""
|
||||
+ (char) ((type >> 24) & 0xFF)
|
||||
+ (char) ((type >> 16) & 0xFF)
|
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,9 @@ import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.TimestampAdjuster;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.container.Mp4Box;
|
||||
import androidx.media3.container.Mp4Box.ContainerBox;
|
||||
import androidx.media3.container.Mp4Box.LeafBox;
|
||||
import androidx.media3.container.NalUnitUtil;
|
||||
import androidx.media3.container.ReorderingSeiMessageQueue;
|
||||
import androidx.media3.extractor.Ac4Util;
|
||||
@ -54,8 +57,6 @@ import androidx.media3.extractor.SniffFailure;
|
||||
import androidx.media3.extractor.TrackOutput;
|
||||
import androidx.media3.extractor.metadata.emsg.EventMessage;
|
||||
import androidx.media3.extractor.metadata.emsg.EventMessageEncoder;
|
||||
import androidx.media3.extractor.mp4.Atom.ContainerAtom;
|
||||
import androidx.media3.extractor.mp4.Atom.LeafAtom;
|
||||
import androidx.media3.extractor.text.SubtitleParser;
|
||||
import androidx.media3.extractor.text.SubtitleTranscodingExtractorOutput;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -208,7 +209,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
// Parser state.
|
||||
private final ParsableByteArray atomHeader;
|
||||
private final ArrayDeque<ContainerAtom> containerAtoms;
|
||||
private final ArrayDeque<ContainerBox> containerAtoms;
|
||||
private final ArrayDeque<MetadataSampleInfo> pendingMetadataSampleInfos;
|
||||
private final ReorderingSeiMessageQueue reorderingSeiMessageQueue;
|
||||
@Nullable private final TrackOutput additionalEmsgTrackOutput;
|
||||
@ -402,7 +403,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
this.closedCaptionFormats = Collections.unmodifiableList(closedCaptionFormats);
|
||||
this.additionalEmsgTrackOutput = additionalEmsgTrackOutput;
|
||||
eventMessageEncoder = new EventMessageEncoder();
|
||||
atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE);
|
||||
atomHeader = new ParsableByteArray(Mp4Box.LONG_HEADER_SIZE);
|
||||
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
|
||||
nalPrefix = new ParsableByteArray(5);
|
||||
nalBuffer = new ParsableByteArray();
|
||||
@ -517,22 +518,22 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
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)) {
|
||||
if (!input.readFully(atomHeader.getData(), 0, Mp4Box.HEADER_SIZE, true)) {
|
||||
return false;
|
||||
}
|
||||
atomHeaderBytesRead = Atom.HEADER_SIZE;
|
||||
atomHeaderBytesRead = Mp4Box.HEADER_SIZE;
|
||||
atomHeader.setPosition(0);
|
||||
atomSize = atomHeader.readUnsignedInt();
|
||||
atomType = atomHeader.readInt();
|
||||
}
|
||||
|
||||
if (atomSize == Atom.DEFINES_LARGE_SIZE) {
|
||||
if (atomSize == Mp4Box.DEFINES_LARGE_SIZE) {
|
||||
// Read the large size.
|
||||
int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
|
||||
input.readFully(atomHeader.getData(), Atom.HEADER_SIZE, headerBytesRemaining);
|
||||
int headerBytesRemaining = Mp4Box.LONG_HEADER_SIZE - Mp4Box.HEADER_SIZE;
|
||||
input.readFully(atomHeader.getData(), Mp4Box.HEADER_SIZE, headerBytesRemaining);
|
||||
atomHeaderBytesRead += headerBytesRemaining;
|
||||
atomSize = atomHeader.readUnsignedLongToLong();
|
||||
} else if (atomSize == Atom.EXTENDS_TO_END_SIZE) {
|
||||
} else if (atomSize == Mp4Box.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();
|
||||
@ -550,7 +551,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
}
|
||||
|
||||
long atomPosition = input.getPosition() - atomHeaderBytesRead;
|
||||
if (atomType == Atom.TYPE_moof || atomType == Atom.TYPE_mdat) {
|
||||
if (atomType == Mp4Box.TYPE_moof || atomType == Mp4Box.TYPE_mdat) {
|
||||
if (!haveOutputSeekMap) {
|
||||
// This must be the first moof or mdat in the stream.
|
||||
extractorOutput.seekMap(new SeekMap.Unseekable(durationUs, atomPosition));
|
||||
@ -558,7 +559,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
}
|
||||
}
|
||||
|
||||
if (atomType == Atom.TYPE_moof) {
|
||||
if (atomType == Mp4Box.TYPE_moof) {
|
||||
// The data positions may be updated when parsing the tfhd/trun.
|
||||
int trackCount = trackBundles.size();
|
||||
for (int i = 0; i < trackCount; i++) {
|
||||
@ -569,7 +570,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
}
|
||||
}
|
||||
|
||||
if (atomType == Atom.TYPE_mdat) {
|
||||
if (atomType == Mp4Box.TYPE_mdat) {
|
||||
currentTrackBundle = null;
|
||||
endOfMdatPosition = atomPosition + atomSize;
|
||||
parserState = STATE_READING_ENCRYPTION_DATA;
|
||||
@ -577,8 +578,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
}
|
||||
|
||||
if (shouldParseContainerAtom(atomType)) {
|
||||
long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE;
|
||||
containerAtoms.push(new ContainerAtom(atomType, endPosition));
|
||||
long endPosition = input.getPosition() + atomSize - Mp4Box.HEADER_SIZE;
|
||||
containerAtoms.push(new ContainerBox(atomType, endPosition));
|
||||
if (atomSize == atomHeaderBytesRead) {
|
||||
processAtomEnded(endPosition);
|
||||
} else {
|
||||
@ -586,7 +587,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
enterReadingAtomHeaderState();
|
||||
}
|
||||
} else if (shouldParseLeafAtom(atomType)) {
|
||||
if (atomHeaderBytesRead != Atom.HEADER_SIZE) {
|
||||
if (atomHeaderBytesRead != Mp4Box.HEADER_SIZE) {
|
||||
throw ParserException.createForUnsupportedContainerFeature(
|
||||
"Leaf atom defines extended atom size (unsupported).");
|
||||
}
|
||||
@ -595,7 +596,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
"Leaf atom with length > 2147483647 (unsupported).");
|
||||
}
|
||||
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, Mp4Box.HEADER_SIZE);
|
||||
this.atomData = atomData;
|
||||
parserState = STATE_READING_ATOM_PAYLOAD;
|
||||
} else {
|
||||
@ -614,8 +615,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
int atomPayloadSize = (int) atomSize - atomHeaderBytesRead;
|
||||
@Nullable ParsableByteArray atomData = this.atomData;
|
||||
if (atomData != null) {
|
||||
input.readFully(atomData.getData(), Atom.HEADER_SIZE, atomPayloadSize);
|
||||
onLeafAtomRead(new LeafAtom(atomType, atomData), input.getPosition());
|
||||
input.readFully(atomData.getData(), Mp4Box.HEADER_SIZE, atomPayloadSize);
|
||||
onLeafAtomRead(new LeafBox(atomType, atomData), input.getPosition());
|
||||
} else {
|
||||
input.skipFully(atomPayloadSize);
|
||||
}
|
||||
@ -629,45 +630,45 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
enterReadingAtomHeaderState();
|
||||
}
|
||||
|
||||
private void onLeafAtomRead(LeafAtom leaf, long inputPosition) throws ParserException {
|
||||
private void onLeafAtomRead(LeafBox leaf, long inputPosition) throws ParserException {
|
||||
if (!containerAtoms.isEmpty()) {
|
||||
containerAtoms.peek().add(leaf);
|
||||
} else if (leaf.type == Atom.TYPE_sidx) {
|
||||
} else if (leaf.type == Mp4Box.TYPE_sidx) {
|
||||
Pair<Long, ChunkIndex> result = parseSidx(leaf.data, inputPosition);
|
||||
segmentIndexEarliestPresentationTimeUs = result.first;
|
||||
extractorOutput.seekMap(result.second);
|
||||
haveOutputSeekMap = true;
|
||||
} else if (leaf.type == Atom.TYPE_emsg) {
|
||||
} else if (leaf.type == Mp4Box.TYPE_emsg) {
|
||||
onEmsgLeafAtomRead(leaf.data);
|
||||
}
|
||||
}
|
||||
|
||||
private void onContainerAtomRead(ContainerAtom container) throws ParserException {
|
||||
if (container.type == Atom.TYPE_moov) {
|
||||
private void onContainerAtomRead(ContainerBox container) throws ParserException {
|
||||
if (container.type == Mp4Box.TYPE_moov) {
|
||||
onMoovContainerAtomRead(container);
|
||||
} else if (container.type == Atom.TYPE_moof) {
|
||||
} else if (container.type == Mp4Box.TYPE_moof) {
|
||||
onMoofContainerAtomRead(container);
|
||||
} else if (!containerAtoms.isEmpty()) {
|
||||
containerAtoms.peek().add(container);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMoovContainerAtomRead(ContainerAtom moov) throws ParserException {
|
||||
private void onMoovContainerAtomRead(ContainerBox 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));
|
||||
ContainerBox mvex = checkNotNull(moov.getContainerBoxOfType(Mp4Box.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) {
|
||||
LeafBox atom = mvex.leafChildren.get(i);
|
||||
if (atom.type == Mp4Box.TYPE_trex) {
|
||||
Pair<Integer, DefaultSampleValues> trexData = parseTrex(atom.data);
|
||||
defaultSampleValuesArray.put(trexData.first, trexData.second);
|
||||
} else if (atom.type == Atom.TYPE_mehd) {
|
||||
} else if (atom.type == Mp4Box.TYPE_mehd) {
|
||||
duration = parseMehd(atom.data);
|
||||
}
|
||||
}
|
||||
@ -725,7 +726,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
return checkNotNull(defaultSampleValuesArray.get(trackId));
|
||||
}
|
||||
|
||||
private void onMoofContainerAtomRead(ContainerAtom moof) throws ParserException {
|
||||
private void onMoofContainerAtomRead(ContainerBox moof) throws ParserException {
|
||||
parseMoof(moof, trackBundles, sideloadedTrack != null, flags, scratchBytes);
|
||||
|
||||
@Nullable DrmInitData drmInitData = getDrmInitDataFromAtoms(moof.leafChildren);
|
||||
@ -775,7 +776,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
if (emsgTrackOutputs.length == 0) {
|
||||
return;
|
||||
}
|
||||
atom.setPosition(Atom.HEADER_SIZE);
|
||||
atom.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = atom.readInt();
|
||||
int version = AtomParsers.parseFullAtomVersion(fullAtom);
|
||||
String schemeIdUri;
|
||||
@ -863,7 +864,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
/** Parses a trex atom (defined in 14496-12). */
|
||||
private static Pair<Integer, DefaultSampleValues> parseTrex(ParsableByteArray trex) {
|
||||
trex.setPosition(Atom.FULL_HEADER_SIZE);
|
||||
trex.setPosition(Mp4Box.FULL_HEADER_SIZE);
|
||||
int trackId = trex.readInt();
|
||||
int defaultSampleDescriptionIndex = trex.readInt() - 1;
|
||||
int defaultSampleDuration = trex.readInt();
|
||||
@ -881,14 +882,14 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
/** Parses an mehd atom (defined in 14496-12). */
|
||||
private static long parseMehd(ParsableByteArray mehd) {
|
||||
mehd.setPosition(Atom.HEADER_SIZE);
|
||||
mehd.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = mehd.readInt();
|
||||
int version = AtomParsers.parseFullAtomVersion(fullAtom);
|
||||
return version == 0 ? mehd.readUnsignedInt() : mehd.readUnsignedLongToLong();
|
||||
}
|
||||
|
||||
private static void parseMoof(
|
||||
ContainerAtom moof,
|
||||
ContainerBox moof,
|
||||
SparseArray<TrackBundle> trackBundles,
|
||||
boolean haveSideloadedTrack,
|
||||
@Flags int flags,
|
||||
@ -896,9 +897,9 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
throws ParserException {
|
||||
int moofContainerChildrenSize = moof.containerChildren.size();
|
||||
for (int i = 0; i < moofContainerChildrenSize; i++) {
|
||||
Atom.ContainerAtom child = moof.containerChildren.get(i);
|
||||
ContainerBox child = moof.containerChildren.get(i);
|
||||
// TODO: Support multiple traf boxes per track in a single moof.
|
||||
if (child.type == Atom.TYPE_traf) {
|
||||
if (child.type == Mp4Box.TYPE_traf) {
|
||||
parseTraf(child, trackBundles, haveSideloadedTrack, flags, extendedTypeScratch);
|
||||
}
|
||||
}
|
||||
@ -906,13 +907,13 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
/** Parses a traf atom (defined in 14496-12). */
|
||||
private static void parseTraf(
|
||||
ContainerAtom traf,
|
||||
ContainerBox traf,
|
||||
SparseArray<TrackBundle> trackBundles,
|
||||
boolean haveSideloadedTrack,
|
||||
@Flags int flags,
|
||||
byte[] extendedTypeScratch)
|
||||
throws ParserException {
|
||||
LeafAtom tfhd = checkNotNull(traf.getLeafAtomOfType(Atom.TYPE_tfhd));
|
||||
LeafBox tfhd = checkNotNull(traf.getLeafBoxOfType(Mp4Box.TYPE_tfhd));
|
||||
@Nullable TrackBundle trackBundle = parseTfhd(tfhd.data, trackBundles, haveSideloadedTrack);
|
||||
if (trackBundle == null) {
|
||||
return;
|
||||
@ -923,7 +924,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
boolean fragmentDecodeTimeIncludesMoov = fragment.nextFragmentDecodeTimeIncludesMoov;
|
||||
trackBundle.resetFragmentInfo();
|
||||
trackBundle.currentlyInFragment = true;
|
||||
@Nullable LeafAtom tfdtAtom = traf.getLeafAtomOfType(Atom.TYPE_tfdt);
|
||||
@Nullable LeafBox tfdtAtom = traf.getLeafBoxOfType(Mp4Box.TYPE_tfdt);
|
||||
if (tfdtAtom != null && (flags & FLAG_WORKAROUND_IGNORE_TFDT_BOX) == 0) {
|
||||
fragment.nextFragmentDecodeTime = parseTfdt(tfdtAtom.data);
|
||||
fragment.nextFragmentDecodeTimeIncludesMoov = true;
|
||||
@ -939,17 +940,17 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
trackBundle.moovSampleTable.track.getSampleDescriptionEncryptionBox(
|
||||
checkNotNull(fragment.header).sampleDescriptionIndex);
|
||||
|
||||
@Nullable LeafAtom saiz = traf.getLeafAtomOfType(Atom.TYPE_saiz);
|
||||
@Nullable LeafBox saiz = traf.getLeafBoxOfType(Mp4Box.TYPE_saiz);
|
||||
if (saiz != null) {
|
||||
parseSaiz(checkNotNull(encryptionBox), saiz.data, fragment);
|
||||
}
|
||||
|
||||
@Nullable LeafAtom saio = traf.getLeafAtomOfType(Atom.TYPE_saio);
|
||||
@Nullable LeafBox saio = traf.getLeafBoxOfType(Mp4Box.TYPE_saio);
|
||||
if (saio != null) {
|
||||
parseSaio(saio.data, fragment);
|
||||
}
|
||||
|
||||
@Nullable LeafAtom senc = traf.getLeafAtomOfType(Atom.TYPE_senc);
|
||||
@Nullable LeafBox senc = traf.getLeafBoxOfType(Mp4Box.TYPE_senc);
|
||||
if (senc != null) {
|
||||
parseSenc(senc.data, fragment);
|
||||
}
|
||||
@ -958,24 +959,24 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
int leafChildrenSize = traf.leafChildren.size();
|
||||
for (int i = 0; i < leafChildrenSize; i++) {
|
||||
LeafAtom atom = traf.leafChildren.get(i);
|
||||
if (atom.type == Atom.TYPE_uuid) {
|
||||
LeafBox atom = traf.leafChildren.get(i);
|
||||
if (atom.type == Mp4Box.TYPE_uuid) {
|
||||
parseUuid(atom.data, fragment, extendedTypeScratch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseTruns(ContainerAtom traf, TrackBundle trackBundle, @Flags int flags)
|
||||
private static void parseTruns(ContainerBox traf, TrackBundle trackBundle, @Flags int flags)
|
||||
throws ParserException {
|
||||
int trunCount = 0;
|
||||
int totalSampleCount = 0;
|
||||
List<LeafAtom> leafChildren = traf.leafChildren;
|
||||
List<LeafBox> leafChildren = traf.leafChildren;
|
||||
int leafChildrenSize = leafChildren.size();
|
||||
for (int i = 0; i < leafChildrenSize; i++) {
|
||||
LeafAtom atom = leafChildren.get(i);
|
||||
if (atom.type == Atom.TYPE_trun) {
|
||||
LeafBox atom = leafChildren.get(i);
|
||||
if (atom.type == Mp4Box.TYPE_trun) {
|
||||
ParsableByteArray trunData = atom.data;
|
||||
trunData.setPosition(Atom.FULL_HEADER_SIZE);
|
||||
trunData.setPosition(Mp4Box.FULL_HEADER_SIZE);
|
||||
int trunSampleCount = trunData.readUnsignedIntToInt();
|
||||
if (trunSampleCount > 0) {
|
||||
totalSampleCount += trunSampleCount;
|
||||
@ -991,8 +992,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
int trunIndex = 0;
|
||||
int trunStartPosition = 0;
|
||||
for (int i = 0; i < leafChildrenSize; i++) {
|
||||
LeafAtom trun = leafChildren.get(i);
|
||||
if (trun.type == Atom.TYPE_trun) {
|
||||
LeafBox trun = leafChildren.get(i);
|
||||
if (trun.type == Mp4Box.TYPE_trun) {
|
||||
trunStartPosition =
|
||||
parseTrun(trackBundle, trunIndex++, flags, trun.data, trunStartPosition);
|
||||
}
|
||||
@ -1003,7 +1004,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
TrackEncryptionBox encryptionBox, ParsableByteArray saiz, TrackFragment out)
|
||||
throws ParserException {
|
||||
int vectorSize = encryptionBox.perSampleIvSize;
|
||||
saiz.setPosition(Atom.HEADER_SIZE);
|
||||
saiz.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = saiz.readInt();
|
||||
int flags = AtomParsers.parseFullAtomFlags(fullAtom);
|
||||
if ((flags & 0x01) == 1) {
|
||||
@ -1047,7 +1048,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
* @param out The {@link TrackFragment} to populate with data from the saio atom.
|
||||
*/
|
||||
private static void parseSaio(ParsableByteArray saio, TrackFragment out) throws ParserException {
|
||||
saio.setPosition(Atom.HEADER_SIZE);
|
||||
saio.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = saio.readInt();
|
||||
int flags = AtomParsers.parseFullAtomFlags(fullAtom);
|
||||
if ((flags & 0x01) == 1) {
|
||||
@ -1081,7 +1082,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
@Nullable
|
||||
private static TrackBundle parseTfhd(
|
||||
ParsableByteArray tfhd, SparseArray<TrackBundle> trackBundles, boolean haveSideloadedTrack) {
|
||||
tfhd.setPosition(Atom.HEADER_SIZE);
|
||||
tfhd.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = tfhd.readInt();
|
||||
int atomFlags = AtomParsers.parseFullAtomFlags(fullAtom);
|
||||
int trackId = tfhd.readInt();
|
||||
@ -1130,7 +1131,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
* media, expressed in the media's timescale.
|
||||
*/
|
||||
private static long parseTfdt(ParsableByteArray tfdt) {
|
||||
tfdt.setPosition(Atom.HEADER_SIZE);
|
||||
tfdt.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = tfdt.readInt();
|
||||
int version = AtomParsers.parseFullAtomVersion(fullAtom);
|
||||
return version == 1 ? tfdt.readUnsignedLongToLong() : tfdt.readUnsignedInt();
|
||||
@ -1173,7 +1174,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
ParsableByteArray trun,
|
||||
int trackRunStart)
|
||||
throws ParserException {
|
||||
trun.setPosition(Atom.HEADER_SIZE);
|
||||
trun.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = trun.readInt();
|
||||
int atomFlags = AtomParsers.parseFullAtomFlags(fullAtom);
|
||||
|
||||
@ -1264,7 +1265,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
private static void parseUuid(
|
||||
ParsableByteArray uuid, TrackFragment out, byte[] extendedTypeScratch)
|
||||
throws ParserException {
|
||||
uuid.setPosition(Atom.HEADER_SIZE);
|
||||
uuid.setPosition(Mp4Box.HEADER_SIZE);
|
||||
uuid.readBytes(extendedTypeScratch, 0, 16);
|
||||
|
||||
// Currently this parser only supports Microsoft's PIFF SampleEncryptionBox.
|
||||
@ -1284,7 +1285,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
private static void parseSenc(ParsableByteArray senc, int offset, TrackFragment out)
|
||||
throws ParserException {
|
||||
senc.setPosition(Atom.HEADER_SIZE + offset);
|
||||
senc.setPosition(Mp4Box.HEADER_SIZE + offset);
|
||||
int fullAtom = senc.readInt();
|
||||
int flags = AtomParsers.parseFullAtomFlags(fullAtom);
|
||||
|
||||
@ -1315,20 +1316,20 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
}
|
||||
|
||||
private static void parseSampleGroups(
|
||||
ContainerAtom traf, @Nullable String schemeType, TrackFragment out) throws ParserException {
|
||||
ContainerBox traf, @Nullable String schemeType, TrackFragment out) throws ParserException {
|
||||
// Find sbgp and sgpd boxes with grouping_type == seig.
|
||||
@Nullable ParsableByteArray sbgp = null;
|
||||
@Nullable ParsableByteArray sgpd = null;
|
||||
for (int i = 0; i < traf.leafChildren.size(); i++) {
|
||||
LeafAtom leafAtom = traf.leafChildren.get(i);
|
||||
LeafBox leafAtom = traf.leafChildren.get(i);
|
||||
ParsableByteArray leafAtomData = leafAtom.data;
|
||||
if (leafAtom.type == Atom.TYPE_sbgp) {
|
||||
leafAtomData.setPosition(Atom.FULL_HEADER_SIZE);
|
||||
if (leafAtom.type == Mp4Box.TYPE_sbgp) {
|
||||
leafAtomData.setPosition(Mp4Box.FULL_HEADER_SIZE);
|
||||
if (leafAtomData.readInt() == SAMPLE_GROUP_TYPE_seig) {
|
||||
sbgp = leafAtomData;
|
||||
}
|
||||
} else if (leafAtom.type == Atom.TYPE_sgpd) {
|
||||
leafAtomData.setPosition(Atom.FULL_HEADER_SIZE);
|
||||
} else if (leafAtom.type == Mp4Box.TYPE_sgpd) {
|
||||
leafAtomData.setPosition(Mp4Box.FULL_HEADER_SIZE);
|
||||
if (leafAtomData.readInt() == SAMPLE_GROUP_TYPE_seig) {
|
||||
sgpd = leafAtomData;
|
||||
}
|
||||
@ -1338,7 +1339,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
return;
|
||||
}
|
||||
|
||||
sbgp.setPosition(Atom.HEADER_SIZE);
|
||||
sbgp.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int sbgpVersion = AtomParsers.parseFullAtomVersion(sbgp.readInt());
|
||||
sbgp.skipBytes(4); // grouping_type == seig.
|
||||
if (sbgpVersion == 1) {
|
||||
@ -1349,7 +1350,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
"Entry count in sbgp != 1 (unsupported).");
|
||||
}
|
||||
|
||||
sgpd.setPosition(Atom.HEADER_SIZE);
|
||||
sgpd.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int sgpdVersion = AtomParsers.parseFullAtomVersion(sgpd.readInt());
|
||||
sgpd.skipBytes(4); // grouping_type == seig.
|
||||
if (sgpdVersion == 1) {
|
||||
@ -1405,7 +1406,7 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
*/
|
||||
private static Pair<Long, ChunkIndex> parseSidx(ParsableByteArray atom, long inputPosition)
|
||||
throws ParserException {
|
||||
atom.setPosition(Atom.HEADER_SIZE);
|
||||
atom.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int fullAtom = atom.readInt();
|
||||
int version = AtomParsers.parseFullAtomVersion(fullAtom);
|
||||
|
||||
@ -1551,8 +1552,8 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
if (trackBundle.moovSampleTable.track.sampleTransformation
|
||||
== Track.TRANSFORMATION_CEA608_CDAT) {
|
||||
sampleSize -= Atom.HEADER_SIZE;
|
||||
input.skipFully(Atom.HEADER_SIZE);
|
||||
sampleSize -= Mp4Box.HEADER_SIZE;
|
||||
input.skipFully(Mp4Box.HEADER_SIZE);
|
||||
}
|
||||
|
||||
if (MimeTypes.AUDIO_AC4.equals(trackBundle.moovSampleTable.track.format.sampleMimeType)) {
|
||||
@ -1742,12 +1743,12 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
/** Returns DrmInitData from leaf atoms. */
|
||||
@Nullable
|
||||
private static DrmInitData getDrmInitDataFromAtoms(List<Atom.LeafAtom> leafChildren) {
|
||||
private static DrmInitData getDrmInitDataFromAtoms(List<LeafBox> leafChildren) {
|
||||
@Nullable ArrayList<SchemeData> schemeDatas = null;
|
||||
int leafChildrenSize = leafChildren.size();
|
||||
for (int i = 0; i < leafChildrenSize; i++) {
|
||||
LeafAtom child = leafChildren.get(i);
|
||||
if (child.type == Atom.TYPE_pssh) {
|
||||
LeafBox child = leafChildren.get(i);
|
||||
if (child.type == Mp4Box.TYPE_pssh) {
|
||||
if (schemeDatas == null) {
|
||||
schemeDatas = new ArrayList<>();
|
||||
}
|
||||
@ -1765,47 +1766,47 @@ public class FragmentedMp4Extractor implements Extractor {
|
||||
|
||||
/** Returns whether the extractor should decode a leaf atom with type {@code atom}. */
|
||||
private static boolean shouldParseLeafAtom(int atom) {
|
||||
return atom == Atom.TYPE_hdlr
|
||||
|| atom == Atom.TYPE_mdhd
|
||||
|| atom == Atom.TYPE_mvhd
|
||||
|| atom == Atom.TYPE_sidx
|
||||
|| atom == Atom.TYPE_stsd
|
||||
|| atom == Atom.TYPE_stts
|
||||
|| atom == Atom.TYPE_ctts
|
||||
|| atom == Atom.TYPE_stsc
|
||||
|| atom == Atom.TYPE_stsz
|
||||
|| atom == Atom.TYPE_stz2
|
||||
|| atom == Atom.TYPE_stco
|
||||
|| atom == Atom.TYPE_co64
|
||||
|| atom == Atom.TYPE_stss
|
||||
|| atom == Atom.TYPE_tfdt
|
||||
|| atom == Atom.TYPE_tfhd
|
||||
|| atom == Atom.TYPE_tkhd
|
||||
|| atom == Atom.TYPE_trex
|
||||
|| atom == Atom.TYPE_trun
|
||||
|| atom == Atom.TYPE_pssh
|
||||
|| atom == Atom.TYPE_saiz
|
||||
|| atom == Atom.TYPE_saio
|
||||
|| atom == Atom.TYPE_senc
|
||||
|| atom == Atom.TYPE_uuid
|
||||
|| atom == Atom.TYPE_sbgp
|
||||
|| atom == Atom.TYPE_sgpd
|
||||
|| atom == Atom.TYPE_elst
|
||||
|| atom == Atom.TYPE_mehd
|
||||
|| atom == Atom.TYPE_emsg;
|
||||
return atom == Mp4Box.TYPE_hdlr
|
||||
|| atom == Mp4Box.TYPE_mdhd
|
||||
|| atom == Mp4Box.TYPE_mvhd
|
||||
|| atom == Mp4Box.TYPE_sidx
|
||||
|| atom == Mp4Box.TYPE_stsd
|
||||
|| atom == Mp4Box.TYPE_stts
|
||||
|| atom == Mp4Box.TYPE_ctts
|
||||
|| atom == Mp4Box.TYPE_stsc
|
||||
|| atom == Mp4Box.TYPE_stsz
|
||||
|| atom == Mp4Box.TYPE_stz2
|
||||
|| atom == Mp4Box.TYPE_stco
|
||||
|| atom == Mp4Box.TYPE_co64
|
||||
|| atom == Mp4Box.TYPE_stss
|
||||
|| atom == Mp4Box.TYPE_tfdt
|
||||
|| atom == Mp4Box.TYPE_tfhd
|
||||
|| atom == Mp4Box.TYPE_tkhd
|
||||
|| atom == Mp4Box.TYPE_trex
|
||||
|| atom == Mp4Box.TYPE_trun
|
||||
|| atom == Mp4Box.TYPE_pssh
|
||||
|| atom == Mp4Box.TYPE_saiz
|
||||
|| atom == Mp4Box.TYPE_saio
|
||||
|| atom == Mp4Box.TYPE_senc
|
||||
|| atom == Mp4Box.TYPE_uuid
|
||||
|| atom == Mp4Box.TYPE_sbgp
|
||||
|| atom == Mp4Box.TYPE_sgpd
|
||||
|| atom == Mp4Box.TYPE_elst
|
||||
|| atom == Mp4Box.TYPE_mehd
|
||||
|| atom == Mp4Box.TYPE_emsg;
|
||||
}
|
||||
|
||||
/** Returns whether the extractor should decode a container atom with type {@code atom}. */
|
||||
private static boolean shouldParseContainerAtom(int atom) {
|
||||
return atom == Atom.TYPE_moov
|
||||
|| atom == Atom.TYPE_trak
|
||||
|| atom == Atom.TYPE_mdia
|
||||
|| atom == Atom.TYPE_minf
|
||||
|| atom == Atom.TYPE_stbl
|
||||
|| atom == Atom.TYPE_moof
|
||||
|| atom == Atom.TYPE_traf
|
||||
|| atom == Atom.TYPE_mvex
|
||||
|| atom == Atom.TYPE_edts;
|
||||
return atom == Mp4Box.TYPE_moov
|
||||
|| atom == Mp4Box.TYPE_trak
|
||||
|| atom == Mp4Box.TYPE_mdia
|
||||
|| atom == Mp4Box.TYPE_minf
|
||||
|| atom == Mp4Box.TYPE_stbl
|
||||
|| atom == Mp4Box.TYPE_moof
|
||||
|| atom == Mp4Box.TYPE_traf
|
||||
|| atom == Mp4Box.TYPE_mvex
|
||||
|| atom == Mp4Box.TYPE_edts;
|
||||
}
|
||||
|
||||
/** Holds data corresponding to a metadata sample. */
|
||||
|
@ -25,6 +25,7 @@ import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.NullableType;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.container.MdtaMetadataEntry;
|
||||
import androidx.media3.container.Mp4Box;
|
||||
import androidx.media3.extractor.GaplessInfoHolder;
|
||||
import androidx.media3.extractor.metadata.id3.ApicFrame;
|
||||
import androidx.media3.extractor.metadata.id3.CommentFrame;
|
||||
@ -203,7 +204,7 @@ import com.google.common.collect.ImmutableList;
|
||||
} else if (type == TYPE_INTERNAL) {
|
||||
return parseInternalAttribute(ilst, endPosition);
|
||||
}
|
||||
Log.d(TAG, "Skipped unknown metadata entry: " + Atom.getAtomTypeString(type));
|
||||
Log.d(TAG, "Skipped unknown metadata entry: " + Mp4Box.getBoxTypeString(type));
|
||||
return null;
|
||||
} finally {
|
||||
ilst.setPosition(endPosition);
|
||||
@ -225,7 +226,7 @@ import com.google.common.collect.ImmutableList;
|
||||
while ((atomPosition = ilst.getPosition()) < endPosition) {
|
||||
int atomSize = ilst.readInt();
|
||||
int atomType = ilst.readInt();
|
||||
if (atomType == Atom.TYPE_data) {
|
||||
if (atomType == Mp4Box.TYPE_data) {
|
||||
int typeIndicator = ilst.readInt();
|
||||
int localeIndicator = ilst.readInt();
|
||||
int dataSize = atomSize - 16;
|
||||
@ -264,12 +265,12 @@ import com.google.common.collect.ImmutableList;
|
||||
int type, String id, ParsableByteArray data) {
|
||||
int atomSize = data.readInt();
|
||||
int atomType = data.readInt();
|
||||
if (atomType == Atom.TYPE_data) {
|
||||
if (atomType == Mp4Box.TYPE_data) {
|
||||
data.skipBytes(8); // version (1), flags (3), empty (4)
|
||||
String value = data.readNullTerminatedString(atomSize - 16);
|
||||
return new TextInformationFrame(id, /* description= */ null, ImmutableList.of(value));
|
||||
}
|
||||
Log.w(TAG, "Failed to parse text attribute: " + Atom.getAtomTypeString(type));
|
||||
Log.w(TAG, "Failed to parse text attribute: " + Mp4Box.getBoxTypeString(type));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -277,12 +278,12 @@ import com.google.common.collect.ImmutableList;
|
||||
private static CommentFrame parseCommentAttribute(int type, ParsableByteArray data) {
|
||||
int atomSize = data.readInt();
|
||||
int atomType = data.readInt();
|
||||
if (atomType == Atom.TYPE_data) {
|
||||
if (atomType == Mp4Box.TYPE_data) {
|
||||
data.skipBytes(8); // version (1), flags (3), empty (4)
|
||||
String value = data.readNullTerminatedString(atomSize - 16);
|
||||
return new CommentFrame(C.LANGUAGE_UNDETERMINED, value, value);
|
||||
}
|
||||
Log.w(TAG, "Failed to parse comment attribute: " + Atom.getAtomTypeString(type));
|
||||
Log.w(TAG, "Failed to parse comment attribute: " + Mp4Box.getBoxTypeString(type));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -303,14 +304,14 @@ import com.google.common.collect.ImmutableList;
|
||||
id, /* description= */ null, ImmutableList.of(Integer.toString(value)))
|
||||
: new CommentFrame(C.LANGUAGE_UNDETERMINED, id, Integer.toString(value));
|
||||
}
|
||||
Log.w(TAG, "Failed to parse uint8 attribute: " + Atom.getAtomTypeString(type));
|
||||
Log.w(TAG, "Failed to parse uint8 attribute: " + Mp4Box.getBoxTypeString(type));
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int parseIntegerAttribute(ParsableByteArray data) {
|
||||
int atomSize = data.readInt();
|
||||
int atomType = data.readInt();
|
||||
if (atomType == Atom.TYPE_data) {
|
||||
if (atomType == Mp4Box.TYPE_data) {
|
||||
data.skipBytes(8); // version (1), flags (3), empty (4)
|
||||
switch (atomSize - 16) {
|
||||
case 1:
|
||||
@ -334,7 +335,7 @@ import com.google.common.collect.ImmutableList;
|
||||
int type, String attributeName, ParsableByteArray data) {
|
||||
int atomSize = data.readInt();
|
||||
int atomType = data.readInt();
|
||||
if (atomType == Atom.TYPE_data && atomSize >= 22) {
|
||||
if (atomType == Mp4Box.TYPE_data && atomSize >= 22) {
|
||||
data.skipBytes(10); // version (1), flags (3), empty (4), empty (2)
|
||||
int index = data.readUnsignedShort();
|
||||
if (index > 0) {
|
||||
@ -347,7 +348,7 @@ import com.google.common.collect.ImmutableList;
|
||||
attributeName, /* description= */ null, ImmutableList.of(value));
|
||||
}
|
||||
}
|
||||
Log.w(TAG, "Failed to parse index/count attribute: " + Atom.getAtomTypeString(type));
|
||||
Log.w(TAG, "Failed to parse index/count attribute: " + Mp4Box.getBoxTypeString(type));
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -369,7 +370,7 @@ import com.google.common.collect.ImmutableList;
|
||||
private static ApicFrame parseCoverArt(ParsableByteArray data) {
|
||||
int atomSize = data.readInt();
|
||||
int atomType = data.readInt();
|
||||
if (atomType == Atom.TYPE_data) {
|
||||
if (atomType == Mp4Box.TYPE_data) {
|
||||
int fullVersionInt = data.readInt();
|
||||
int flags = AtomParsers.parseFullAtomFlags(fullVersionInt);
|
||||
@Nullable String mimeType = flags == 13 ? "image/jpeg" : flags == 14 ? "image/png" : null;
|
||||
@ -401,12 +402,12 @@ import com.google.common.collect.ImmutableList;
|
||||
int atomSize = data.readInt();
|
||||
int atomType = data.readInt();
|
||||
data.skipBytes(4); // version (1), flags (3)
|
||||
if (atomType == Atom.TYPE_mean) {
|
||||
if (atomType == Mp4Box.TYPE_mean) {
|
||||
domain = data.readNullTerminatedString(atomSize - 12);
|
||||
} else if (atomType == Atom.TYPE_name) {
|
||||
} else if (atomType == Mp4Box.TYPE_name) {
|
||||
name = data.readNullTerminatedString(atomSize - 12);
|
||||
} else {
|
||||
if (atomType == Atom.TYPE_data) {
|
||||
if (atomType == Mp4Box.TYPE_data) {
|
||||
dataAtomPosition = atomPosition;
|
||||
dataAtomSize = atomSize;
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.container.MdtaMetadataEntry;
|
||||
import androidx.media3.container.Mp4Box;
|
||||
import androidx.media3.container.Mp4Box.ContainerBox;
|
||||
import androidx.media3.container.NalUnitUtil;
|
||||
import androidx.media3.extractor.Ac3Util;
|
||||
import androidx.media3.extractor.Ac4Util;
|
||||
@ -63,7 +65,6 @@ import androidx.media3.extractor.TrackOutput;
|
||||
import androidx.media3.extractor.TrueHdSampleRechunker;
|
||||
import androidx.media3.extractor.metadata.mp4.MotionPhotoMetadata;
|
||||
import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||
import androidx.media3.extractor.mp4.Atom.ContainerAtom;
|
||||
import androidx.media3.extractor.text.SubtitleParser;
|
||||
import androidx.media3.extractor.text.SubtitleTranscodingExtractorOutput;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -229,7 +230,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
private final ParsableByteArray scratch;
|
||||
|
||||
private final ParsableByteArray atomHeader;
|
||||
private final ArrayDeque<ContainerAtom> containerAtoms;
|
||||
private final ArrayDeque<ContainerBox> containerAtoms;
|
||||
private final SefReader sefReader;
|
||||
private final List<Metadata.Entry> slowMotionMetadataEntries;
|
||||
|
||||
@ -305,7 +306,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
((flags & FLAG_READ_SEF_DATA) != 0) ? STATE_READING_SEF : STATE_READING_ATOM_HEADER;
|
||||
sefReader = new SefReader();
|
||||
slowMotionMetadataEntries = new ArrayList<>();
|
||||
atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE);
|
||||
atomHeader = new ParsableByteArray(Mp4Box.LONG_HEADER_SIZE);
|
||||
containerAtoms = new ArrayDeque<>();
|
||||
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
|
||||
nalPrefix = new ParsableByteArray(5);
|
||||
@ -493,28 +494,28 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
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)) {
|
||||
if (!input.readFully(atomHeader.getData(), 0, Mp4Box.HEADER_SIZE, true)) {
|
||||
processEndOfStreamReadingAtomHeader();
|
||||
return false;
|
||||
}
|
||||
atomHeaderBytesRead = Atom.HEADER_SIZE;
|
||||
atomHeaderBytesRead = Mp4Box.HEADER_SIZE;
|
||||
atomHeader.setPosition(0);
|
||||
atomSize = atomHeader.readUnsignedInt();
|
||||
atomType = atomHeader.readInt();
|
||||
}
|
||||
|
||||
if (atomSize == Atom.DEFINES_LARGE_SIZE) {
|
||||
if (atomSize == Mp4Box.DEFINES_LARGE_SIZE) {
|
||||
// Read the large size.
|
||||
int headerBytesRemaining = Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE;
|
||||
input.readFully(atomHeader.getData(), Atom.HEADER_SIZE, headerBytesRemaining);
|
||||
int headerBytesRemaining = Mp4Box.LONG_HEADER_SIZE - Mp4Box.HEADER_SIZE;
|
||||
input.readFully(atomHeader.getData(), Mp4Box.HEADER_SIZE, headerBytesRemaining);
|
||||
atomHeaderBytesRead += headerBytesRemaining;
|
||||
atomSize = atomHeader.readUnsignedLongToLong();
|
||||
} else if (atomSize == Atom.EXTENDS_TO_END_SIZE) {
|
||||
} else if (atomSize == Mp4Box.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();
|
||||
@Nullable ContainerBox containerAtom = containerAtoms.peek();
|
||||
if (containerAtom != null) {
|
||||
endPosition = containerAtom.endPosition;
|
||||
}
|
||||
@ -531,10 +532,10 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
|
||||
if (shouldParseContainerAtom(atomType)) {
|
||||
long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead;
|
||||
if (atomSize != atomHeaderBytesRead && atomType == Atom.TYPE_meta) {
|
||||
if (atomSize != atomHeaderBytesRead && atomType == Mp4Box.TYPE_meta) {
|
||||
maybeSkipRemainingMetaAtomHeaderBytes(input);
|
||||
}
|
||||
containerAtoms.push(new ContainerAtom(atomType, endPosition));
|
||||
containerAtoms.push(new ContainerBox(atomType, endPosition));
|
||||
if (atomSize == atomHeaderBytesRead) {
|
||||
processAtomEnded(endPosition);
|
||||
} else {
|
||||
@ -544,10 +545,10 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
} 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(atomHeaderBytesRead == Mp4Box.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);
|
||||
System.arraycopy(atomHeader.getData(), 0, atomData.getData(), 0, Mp4Box.HEADER_SIZE);
|
||||
this.atomData = atomData;
|
||||
parserState = STATE_READING_ATOM_PAYLOAD;
|
||||
} else {
|
||||
@ -573,14 +574,14 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
@Nullable ParsableByteArray atomData = this.atomData;
|
||||
if (atomData != null) {
|
||||
input.readFully(atomData.getData(), atomHeaderBytesRead, (int) atomPayloadSize);
|
||||
if (atomType == Atom.TYPE_ftyp) {
|
||||
if (atomType == Mp4Box.TYPE_ftyp) {
|
||||
seenFtypAtom = true;
|
||||
fileType = processFtypAtom(atomData);
|
||||
} else if (!containerAtoms.isEmpty()) {
|
||||
containerAtoms.peek().add(new Atom.LeafAtom(atomType, atomData));
|
||||
containerAtoms.peek().add(new Mp4Box.LeafBox(atomType, atomData));
|
||||
}
|
||||
} else {
|
||||
if (!seenFtypAtom && atomType == Atom.TYPE_mdat) {
|
||||
if (!seenFtypAtom && atomType == Mp4Box.TYPE_mdat) {
|
||||
// The original QuickTime specification did not require files to begin with the ftyp atom.
|
||||
// See https://developer.apple.com/standards/qtff-2001.pdf.
|
||||
fileType = FILE_TYPE_QUICKTIME;
|
||||
@ -614,8 +615,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
|
||||
private void processAtomEnded(long atomEndPosition) throws ParserException {
|
||||
while (!containerAtoms.isEmpty() && containerAtoms.peek().endPosition == atomEndPosition) {
|
||||
Atom.ContainerAtom containerAtom = containerAtoms.pop();
|
||||
if (containerAtom.type == Atom.TYPE_moov) {
|
||||
ContainerBox containerAtom = containerAtoms.pop();
|
||||
if (containerAtom.type == Mp4Box.TYPE_moov) {
|
||||
// We've reached the end of the moov atom. Process it and prepare to read samples.
|
||||
processMoovAtom(containerAtom);
|
||||
containerAtoms.clear();
|
||||
@ -636,10 +637,10 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
*
|
||||
* <p>The processing is aborted if the edvd.moov atom needs to be processed instead.
|
||||
*/
|
||||
private void processMoovAtom(ContainerAtom moov) throws ParserException {
|
||||
private void processMoovAtom(ContainerBox moov) throws ParserException {
|
||||
// Process metadata first to determine whether to abort processing and seek to the edvd atom.
|
||||
@Nullable Metadata mdtaMetadata = null;
|
||||
@Nullable Atom.ContainerAtom meta = moov.getContainerAtomOfType(Atom.TYPE_meta);
|
||||
@Nullable Mp4Box.ContainerBox meta = moov.getContainerBoxOfType(Mp4Box.TYPE_meta);
|
||||
List<@C.AuxiliaryTrackType Integer> auxiliaryTrackTypesForEditableVideoTracks =
|
||||
new ArrayList<>();
|
||||
if (meta != null) {
|
||||
@ -654,7 +655,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int firstVideoTrackIndex = C.INDEX_UNSET;
|
||||
long durationUs = C.TIME_UNSET;
|
||||
List<Mp4Track> tracks = new ArrayList<>();
|
||||
@ -663,7 +663,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
boolean isQuickTime = fileType == FILE_TYPE_QUICKTIME;
|
||||
GaplessInfoHolder gaplessInfoHolder = new GaplessInfoHolder();
|
||||
@Nullable Metadata udtaMetadata = null;
|
||||
@Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
|
||||
@Nullable Mp4Box.LeafBox udta = moov.getLeafBoxOfType(Mp4Box.TYPE_udta);
|
||||
if (udta != null) {
|
||||
udtaMetadata = AtomParsers.parseUdta(udta);
|
||||
gaplessInfoHolder.setFromMetadata(udtaMetadata);
|
||||
@ -671,7 +671,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
|
||||
Metadata mvhdMetadata =
|
||||
new Metadata(
|
||||
AtomParsers.parseMvhd(checkNotNull(moov.getLeafAtomOfType(Atom.TYPE_mvhd)).data));
|
||||
AtomParsers.parseMvhd(checkNotNull(moov.getLeafBoxOfType(Mp4Box.TYPE_mvhd)).data));
|
||||
|
||||
boolean ignoreEditLists = (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0;
|
||||
List<TrackSampleTable> trackSampleTables =
|
||||
@ -863,8 +863,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
if (track.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
|
||||
// The sample information is contained in a cdat atom. The header must be discarded for
|
||||
// committing.
|
||||
skipAmount += Atom.HEADER_SIZE;
|
||||
sampleSize -= Atom.HEADER_SIZE;
|
||||
skipAmount += Mp4Box.HEADER_SIZE;
|
||||
sampleSize -= Mp4Box.HEADER_SIZE;
|
||||
}
|
||||
input.skipFully((int) skipAmount);
|
||||
// Treat all samples in non-H.264 codecs as depended on.
|
||||
@ -1050,7 +1050,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
|
||||
/** Processes an atom whose payload does not need to be parsed. */
|
||||
private void processUnparsedAtom(long atomStartPosition) {
|
||||
if (atomType == Atom.TYPE_mpvd) {
|
||||
if (atomType == Mp4Box.TYPE_mpvd) {
|
||||
// The input is an HEIC motion photo following the Google Photos Motion Photo File Format
|
||||
// V1.1.
|
||||
motionPhotoMetadata =
|
||||
@ -1148,7 +1148,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
* @return The {@link FileType}.
|
||||
*/
|
||||
private static @FileType int processFtypAtom(ParsableByteArray atomData) {
|
||||
atomData.setPosition(Atom.HEADER_SIZE);
|
||||
atomData.setPosition(Mp4Box.HEADER_SIZE);
|
||||
int majorBrand = atomData.readInt();
|
||||
@FileType int fileType = brandToFileType(majorBrand);
|
||||
if (fileType != FILE_TYPE_MP4) {
|
||||
@ -1177,36 +1177,36 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
||||
|
||||
/** Returns whether the extractor should decode a leaf atom with type {@code atom}. */
|
||||
private static boolean shouldParseLeafAtom(int atom) {
|
||||
return atom == Atom.TYPE_mdhd
|
||||
|| atom == Atom.TYPE_mvhd
|
||||
|| atom == Atom.TYPE_hdlr
|
||||
|| atom == Atom.TYPE_stsd
|
||||
|| atom == Atom.TYPE_stts
|
||||
|| atom == Atom.TYPE_stss
|
||||
|| atom == Atom.TYPE_ctts
|
||||
|| atom == Atom.TYPE_elst
|
||||
|| atom == Atom.TYPE_stsc
|
||||
|| atom == Atom.TYPE_stsz
|
||||
|| atom == Atom.TYPE_stz2
|
||||
|| atom == Atom.TYPE_stco
|
||||
|| atom == Atom.TYPE_co64
|
||||
|| atom == Atom.TYPE_tkhd
|
||||
|| atom == Atom.TYPE_ftyp
|
||||
|| atom == Atom.TYPE_udta
|
||||
|| atom == Atom.TYPE_keys
|
||||
|| atom == Atom.TYPE_ilst;
|
||||
return atom == Mp4Box.TYPE_mdhd
|
||||
|| atom == Mp4Box.TYPE_mvhd
|
||||
|| atom == Mp4Box.TYPE_hdlr
|
||||
|| atom == Mp4Box.TYPE_stsd
|
||||
|| atom == Mp4Box.TYPE_stts
|
||||
|| atom == Mp4Box.TYPE_stss
|
||||
|| atom == Mp4Box.TYPE_ctts
|
||||
|| atom == Mp4Box.TYPE_elst
|
||||
|| atom == Mp4Box.TYPE_stsc
|
||||
|| atom == Mp4Box.TYPE_stsz
|
||||
|| atom == Mp4Box.TYPE_stz2
|
||||
|| atom == Mp4Box.TYPE_stco
|
||||
|| atom == Mp4Box.TYPE_co64
|
||||
|| atom == Mp4Box.TYPE_tkhd
|
||||
|| atom == Mp4Box.TYPE_ftyp
|
||||
|| atom == Mp4Box.TYPE_udta
|
||||
|| atom == Mp4Box.TYPE_keys
|
||||
|| atom == Mp4Box.TYPE_ilst;
|
||||
}
|
||||
|
||||
/** Returns whether the extractor should decode a container atom with type {@code atom}. */
|
||||
private static boolean shouldParseContainerAtom(int atom) {
|
||||
return atom == Atom.TYPE_moov
|
||||
|| atom == Atom.TYPE_trak
|
||||
|| atom == Atom.TYPE_mdia
|
||||
|| atom == Atom.TYPE_minf
|
||||
|| atom == Atom.TYPE_stbl
|
||||
|| atom == Atom.TYPE_edts
|
||||
|| atom == Atom.TYPE_meta
|
||||
|| atom == Atom.TYPE_edvd;
|
||||
return atom == Mp4Box.TYPE_moov
|
||||
|| atom == Mp4Box.TYPE_trak
|
||||
|| atom == Mp4Box.TYPE_mdia
|
||||
|| atom == Mp4Box.TYPE_minf
|
||||
|| atom == Mp4Box.TYPE_stbl
|
||||
|| atom == Mp4Box.TYPE_edts
|
||||
|| atom == Mp4Box.TYPE_meta
|
||||
|| atom == Mp4Box.TYPE_edvd;
|
||||
}
|
||||
|
||||
private static final class Mp4Track {
|
||||
|
@ -19,6 +19,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.container.Mp4Box;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -52,13 +53,13 @@ public final class PsshAtomUtil {
|
||||
public static byte[] buildPsshAtom(
|
||||
UUID systemId, @Nullable UUID[] keyIds, @Nullable byte[] data) {
|
||||
int dataLength = data != null ? data.length : 0;
|
||||
int psshBoxLength = Atom.FULL_HEADER_SIZE + 16 /* SystemId */ + 4 /* DataSize */ + dataLength;
|
||||
int psshBoxLength = Mp4Box.FULL_HEADER_SIZE + 16 /* SystemId */ + 4 /* DataSize */ + dataLength;
|
||||
if (keyIds != null) {
|
||||
psshBoxLength += 4 /* KID_count */ + (keyIds.length * 16) /* KIDs */;
|
||||
}
|
||||
ByteBuffer psshBox = ByteBuffer.allocate(psshBoxLength);
|
||||
psshBox.putInt(psshBoxLength);
|
||||
psshBox.putInt(Atom.TYPE_pssh);
|
||||
psshBox.putInt(Mp4Box.TYPE_pssh);
|
||||
psshBox.putInt(keyIds != null ? 0x01000000 : 0 /* version=(buildV1Atom ? 1 : 0), flags=0 */);
|
||||
psshBox.putLong(systemId.getMostSignificantBits());
|
||||
psshBox.putLong(systemId.getLeastSignificantBits());
|
||||
@ -157,7 +158,7 @@ public final class PsshAtomUtil {
|
||||
@Nullable
|
||||
public static PsshAtom parsePsshAtom(byte[] atom) {
|
||||
ParsableByteArray atomData = new ParsableByteArray(atom);
|
||||
if (atomData.limit() < Atom.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
|
||||
if (atomData.limit() < Mp4Box.FULL_HEADER_SIZE + 16 /* UUID */ + 4 /* DataSize */) {
|
||||
// Data too short.
|
||||
return null;
|
||||
}
|
||||
@ -171,7 +172,7 @@ public final class PsshAtomUtil {
|
||||
return null;
|
||||
}
|
||||
int atomType = atomData.readInt();
|
||||
if (atomType != Atom.TYPE_pssh) {
|
||||
if (atomType != Mp4Box.TYPE_pssh) {
|
||||
Log.w(TAG, "Atom type is not pssh: " + atomType);
|
||||
return null;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import androidx.media3.common.C;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.container.Mp4Box;
|
||||
import androidx.media3.extractor.metadata.mp4.SmtaMetadataEntry;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -70,12 +71,12 @@ public final class SmtaAtomUtil {
|
||||
/** Parses metadata from a Samsung smta atom. */
|
||||
@Nullable
|
||||
public static Metadata parseSmta(ParsableByteArray smta, int limit) {
|
||||
smta.skipBytes(Atom.FULL_HEADER_SIZE);
|
||||
smta.skipBytes(Mp4Box.FULL_HEADER_SIZE);
|
||||
while (smta.getPosition() < limit) {
|
||||
int atomPosition = smta.getPosition();
|
||||
int atomSize = smta.readInt();
|
||||
int atomType = smta.readInt();
|
||||
if (atomType == Atom.TYPE_saut) {
|
||||
if (atomType == Mp4Box.TYPE_saut) {
|
||||
// Size (4), Type (4), Author (4), Recording mode (2), SVC layer count (2).
|
||||
if (atomSize < 16) {
|
||||
return null;
|
||||
@ -126,13 +127,13 @@ public final class SmtaAtomUtil {
|
||||
return C.RATE_UNSET_INT;
|
||||
}
|
||||
|
||||
if (smta.bytesLeft() < Atom.HEADER_SIZE || smta.getPosition() + Atom.HEADER_SIZE > limit) {
|
||||
if (smta.bytesLeft() < Mp4Box.HEADER_SIZE || smta.getPosition() + Mp4Box.HEADER_SIZE > limit) {
|
||||
return C.RATE_UNSET_INT;
|
||||
}
|
||||
|
||||
int atomSize = smta.readInt();
|
||||
int atomType = smta.readInt();
|
||||
if (atomSize < 12 || atomType != Atom.TYPE_srfr) {
|
||||
if (atomSize < 12 || atomType != Mp4Box.TYPE_srfr) {
|
||||
return C.RATE_UNSET_INT;
|
||||
}
|
||||
// Capture frame rate is in Q16 format.
|
||||
|
@ -18,6 +18,7 @@ package androidx.media3.extractor.mp4;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.container.Mp4Box;
|
||||
import androidx.media3.extractor.ExtractorInput;
|
||||
import androidx.media3.extractor.SniffFailure;
|
||||
import java.io.IOException;
|
||||
@ -118,7 +119,7 @@ import java.io.IOException;
|
||||
boolean isFragmented = false;
|
||||
while (bytesSearched < bytesToSearch) {
|
||||
// Read an atom header.
|
||||
int headerSize = Atom.HEADER_SIZE;
|
||||
int headerSize = Mp4Box.HEADER_SIZE;
|
||||
buffer.reset(headerSize);
|
||||
boolean success =
|
||||
input.peekFully(buffer.getData(), 0, headerSize, /* allowEndOfInput= */ true);
|
||||
@ -128,14 +129,14 @@ import java.io.IOException;
|
||||
}
|
||||
long atomSize = buffer.readUnsignedInt();
|
||||
int atomType = buffer.readInt();
|
||||
if (atomSize == Atom.DEFINES_LARGE_SIZE) {
|
||||
if (atomSize == Mp4Box.DEFINES_LARGE_SIZE) {
|
||||
// Read the large atom size.
|
||||
headerSize = Atom.LONG_HEADER_SIZE;
|
||||
headerSize = Mp4Box.LONG_HEADER_SIZE;
|
||||
input.peekFully(
|
||||
buffer.getData(), Atom.HEADER_SIZE, Atom.LONG_HEADER_SIZE - Atom.HEADER_SIZE);
|
||||
buffer.setLimit(Atom.LONG_HEADER_SIZE);
|
||||
buffer.getData(), Mp4Box.HEADER_SIZE, Mp4Box.LONG_HEADER_SIZE - Mp4Box.HEADER_SIZE);
|
||||
buffer.setLimit(Mp4Box.LONG_HEADER_SIZE);
|
||||
atomSize = buffer.readLong();
|
||||
} else if (atomSize == Atom.EXTENDS_TO_END_SIZE) {
|
||||
} else if (atomSize == Mp4Box.EXTENDS_TO_END_SIZE) {
|
||||
// The atom extends to the end of the file.
|
||||
long fileEndPosition = input.getLength();
|
||||
if (fileEndPosition != C.LENGTH_UNSET) {
|
||||
@ -149,7 +150,7 @@ import java.io.IOException;
|
||||
}
|
||||
bytesSearched += headerSize;
|
||||
|
||||
if (atomType == Atom.TYPE_moov) {
|
||||
if (atomType == Mp4Box.TYPE_moov) {
|
||||
// We have seen the moov atom. We increase the search size to make sure we don't miss an
|
||||
// mvex atom because the moov's size exceeds the search length.
|
||||
bytesToSearch += (int) atomSize;
|
||||
@ -161,13 +162,13 @@ import java.io.IOException;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (atomType == Atom.TYPE_moof || atomType == Atom.TYPE_mvex) {
|
||||
if (atomType == Mp4Box.TYPE_moof || atomType == Mp4Box.TYPE_mvex) {
|
||||
// The movie is fragmented. Stop searching as we must have read any ftyp atom already.
|
||||
isFragmented = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (atomType == Atom.TYPE_mdat) {
|
||||
if (atomType == Mp4Box.TYPE_mdat) {
|
||||
// The original QuickTime specification did not require files to begin with the ftyp atom.
|
||||
// See https://developer.apple.com/standards/qtff-2001.pdf.
|
||||
foundGoodFileType = true;
|
||||
@ -180,7 +181,7 @@ import java.io.IOException;
|
||||
|
||||
int atomDataSize = (int) (atomSize - headerSize);
|
||||
bytesSearched += atomDataSize;
|
||||
if (atomType == Atom.TYPE_ftyp) {
|
||||
if (atomType == Mp4Box.TYPE_ftyp) {
|
||||
// Parse the atom and check the file type/brand is compatible with the extractors.
|
||||
if (atomDataSize < 8) {
|
||||
return new AtomSizeTooSmallSniffFailure(atomType, atomDataSize, 8);
|
||||
|
@ -21,6 +21,7 @@ import androidx.media3.common.C;
|
||||
import androidx.media3.common.ParserException;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.container.Mp4Box;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -254,17 +255,18 @@ public final class AtomParsersTest {
|
||||
|
||||
@Test
|
||||
public void stz2Parsing4BitFieldSize() {
|
||||
verifyStz2Parsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(FOUR_BIT_STZ2)));
|
||||
verifyStz2Parsing(new Mp4Box.LeafBox(Mp4Box.TYPE_stsz, new ParsableByteArray(FOUR_BIT_STZ2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stz2Parsing8BitFieldSize() {
|
||||
verifyStz2Parsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(EIGHT_BIT_STZ2)));
|
||||
verifyStz2Parsing(new Mp4Box.LeafBox(Mp4Box.TYPE_stsz, new ParsableByteArray(EIGHT_BIT_STZ2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stz2Parsing16BitFieldSize() {
|
||||
verifyStz2Parsing(new Atom.LeafAtom(Atom.TYPE_stsz, new ParsableByteArray(SIXTEEN_BIT_STZ2)));
|
||||
verifyStz2Parsing(
|
||||
new Mp4Box.LeafBox(Mp4Box.TYPE_stsz, new ParsableByteArray(SIXTEEN_BIT_STZ2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -286,7 +288,7 @@ public final class AtomParsersTest {
|
||||
assertThat(vexuData.hasBothEyeViews()).isTrue();
|
||||
}
|
||||
|
||||
private static void verifyStz2Parsing(Atom.LeafAtom stz2Atom) {
|
||||
private static void verifyStz2Parsing(Mp4Box.LeafBox stz2Atom) {
|
||||
AtomParsers.Stz2SampleSizeBox box = new AtomParsers.Stz2SampleSizeBox(stz2Atom);
|
||||
assertThat(box.getSampleCount()).isEqualTo(4);
|
||||
assertThat(box.getFixedSampleSize()).isEqualTo(C.LENGTH_UNSET);
|
||||
|
@ -16,7 +16,7 @@
|
||||
package androidx.media3.extractor.mp4;
|
||||
|
||||
import static androidx.media3.common.C.WIDEVINE_UUID;
|
||||
import static androidx.media3.extractor.mp4.Atom.TYPE_pssh;
|
||||
import static androidx.media3.container.Mp4Box.TYPE_pssh;
|
||||
import static androidx.media3.extractor.mp4.AtomParsers.parseFullAtomFlags;
|
||||
import static androidx.media3.extractor.mp4.AtomParsers.parseFullAtomVersion;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
Loading…
x
Reference in New Issue
Block a user