Add support for MP4/QuickTime non-full meta atoms
#minor-release PiperOrigin-RevId: 357160215
This commit is contained in:
parent
158e6de25a
commit
b303eceafd
@ -11,6 +11,8 @@
|
|||||||
* DASH:
|
* DASH:
|
||||||
* Fix playback issue for multi-period DASH live streams
|
* Fix playback issue for multi-period DASH live streams
|
||||||
([#8537](https://github.com/google/ExoPlayer/issues/8537)).
|
([#8537](https://github.com/google/ExoPlayer/issues/8537)).
|
||||||
|
* Extractors:
|
||||||
|
* Add support for MP4 and QuickTime meta atoms that are not full atoms.
|
||||||
* UI:
|
* UI:
|
||||||
* Add builder for `PlayerNotificationManager`.
|
* Add builder for `PlayerNotificationManager`.
|
||||||
* IMA extension:
|
* IMA extension:
|
||||||
|
@ -145,12 +145,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
* Parses a udta atom.
|
* Parses a udta atom.
|
||||||
*
|
*
|
||||||
* @param udtaAtom The udta (user data) atom to decode.
|
* @param udtaAtom The udta (user data) atom to decode.
|
||||||
* @param isQuickTime True for QuickTime media. False otherwise.
|
|
||||||
* @return A {@link Pair} containing the metadata from the meta child atom as first value (if
|
* @return A {@link Pair} containing the metadata from the meta child atom as first value (if
|
||||||
* any), and the metadata from the smta child atom as second value (if any).
|
* any), and the metadata from the smta child atom as second value (if any).
|
||||||
*/
|
*/
|
||||||
public static Pair<@NullableType Metadata, @NullableType Metadata> parseUdta(
|
public static Pair<@NullableType Metadata, @NullableType Metadata> parseUdta(
|
||||||
Atom.LeafAtom udtaAtom, boolean isQuickTime) {
|
Atom.LeafAtom udtaAtom) {
|
||||||
ParsableByteArray udtaData = udtaAtom.data;
|
ParsableByteArray udtaData = udtaAtom.data;
|
||||||
udtaData.setPosition(Atom.HEADER_SIZE);
|
udtaData.setPosition(Atom.HEADER_SIZE);
|
||||||
@Nullable Metadata metaMetadata = null;
|
@Nullable Metadata metaMetadata = null;
|
||||||
@ -159,8 +158,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
int atomPosition = udtaData.getPosition();
|
int atomPosition = udtaData.getPosition();
|
||||||
int atomSize = udtaData.readInt();
|
int atomSize = udtaData.readInt();
|
||||||
int atomType = udtaData.readInt();
|
int atomType = udtaData.readInt();
|
||||||
// Meta boxes are regular boxes rather than full boxes in QuickTime. Ignore them for now.
|
if (atomType == Atom.TYPE_meta) {
|
||||||
if (atomType == Atom.TYPE_meta && !isQuickTime) {
|
|
||||||
udtaData.setPosition(atomPosition);
|
udtaData.setPosition(atomPosition);
|
||||||
metaMetadata = parseUdtaMeta(udtaData, atomPosition + atomSize);
|
metaMetadata = parseUdtaMeta(udtaData, atomPosition + atomSize);
|
||||||
} else if (atomType == Atom.TYPE_smta) {
|
} else if (atomType == Atom.TYPE_smta) {
|
||||||
@ -227,6 +225,28 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
return entries.isEmpty() ? null : new Metadata(entries);
|
return entries.isEmpty() ? null : new Metadata(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possibly skips the version and flags fields (1+3 byte) of a full meta atom.
|
||||||
|
*
|
||||||
|
* <p>Atoms of type {@link Atom#TYPE_meta} are defined to be full atoms which have four additional
|
||||||
|
* bytes for a version and a flags field (see 4.2 'Object Structure' in ISO/IEC 14496-12:2005).
|
||||||
|
* QuickTime do not have such a full box structure. Since some of these files are encoded wrongly,
|
||||||
|
* we can't rely on the file type though. Instead we must check the 4 bytes after the common
|
||||||
|
* header bytes ourselves.
|
||||||
|
*
|
||||||
|
* @param meta The 4 or more bytes following the meta atom size and type.
|
||||||
|
*/
|
||||||
|
public static void maybeSkipRemainingMetaAtomHeaderBytes(ParsableByteArray meta) {
|
||||||
|
int startPosition = meta.getPosition();
|
||||||
|
// The next 4 bytes can be either:
|
||||||
|
// (iso) 4 zero bytes (version + flags)
|
||||||
|
// (qt) 4 byte size of next atom
|
||||||
|
// In case of (iso) we need to skip the next 4 bytes.
|
||||||
|
if (meta.readInt() != 0) {
|
||||||
|
meta.setPosition(startPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a trak atom (defined in ISO/IEC 14496-12).
|
* Parses a trak atom (defined in ISO/IEC 14496-12).
|
||||||
*
|
*
|
||||||
@ -677,7 +697,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static Metadata parseUdtaMeta(ParsableByteArray meta, int limit) {
|
private static Metadata parseUdtaMeta(ParsableByteArray meta, int limit) {
|
||||||
meta.skipBytes(Atom.FULL_HEADER_SIZE);
|
meta.skipBytes(Atom.HEADER_SIZE);
|
||||||
|
maybeSkipRemainingMetaAtomHeaderBytes(meta);
|
||||||
while (meta.getPosition() < limit) {
|
while (meta.getPosition() < limit) {
|
||||||
int atomPosition = meta.getPosition();
|
int atomPosition = meta.getPosition();
|
||||||
int atomSize = meta.readInt();
|
int atomSize = meta.readInt();
|
||||||
|
@ -470,7 +470,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
@Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
|
@Nullable Atom.LeafAtom udta = moov.getLeafAtomOfType(Atom.TYPE_udta);
|
||||||
if (udta != null) {
|
if (udta != null) {
|
||||||
Pair<@NullableType Metadata, @NullableType Metadata> udtaMetadata =
|
Pair<@NullableType Metadata, @NullableType Metadata> udtaMetadata =
|
||||||
AtomParsers.parseUdta(udta, isQuickTime);
|
AtomParsers.parseUdta(udta);
|
||||||
udtaMetaMetadata = udtaMetadata.first;
|
udtaMetaMetadata = udtaMetadata.first;
|
||||||
smtaMetadata = udtaMetadata.second;
|
smtaMetadata = udtaMetadata.second;
|
||||||
if (udtaMetaMetadata != null) {
|
if (udtaMetaMetadata != null) {
|
||||||
@ -727,29 +727,12 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Possibly skips the version and flags fields (1+3 byte) of a full meta atom of the {@code
|
|
||||||
* input}.
|
|
||||||
*
|
|
||||||
* <p>Atoms of type {@link Atom#TYPE_meta} are defined to be full atoms which have four additional
|
|
||||||
* bytes for a version and a flags field (see 4.2 'Object Structure' in ISO/IEC 14496-12:2005).
|
|
||||||
* QuickTime do not have such a full box structure. Since some of these files are encoded wrongly,
|
|
||||||
* we can't rely on the file type though. Instead we must check the 8 bytes after the common
|
|
||||||
* header bytes ourselves.
|
|
||||||
*/
|
|
||||||
private void maybeSkipRemainingMetaAtomHeaderBytes(ExtractorInput input) throws IOException {
|
private void maybeSkipRemainingMetaAtomHeaderBytes(ExtractorInput input) throws IOException {
|
||||||
scratch.reset(8);
|
scratch.reset(4);
|
||||||
// Peek the next 8 bytes which can be either
|
input.peekFully(scratch.getData(), 0, 4);
|
||||||
// (iso) [1 byte version + 3 bytes flags][4 byte size of next atom]
|
AtomParsers.maybeSkipRemainingMetaAtomHeaderBytes(scratch);
|
||||||
// (qt) [4 byte size of next atom ][4 byte hdlr atom type ]
|
input.skipFully(scratch.getPosition());
|
||||||
// In case of (iso) we need to skip the next 4 bytes.
|
input.resetPeekPosition();
|
||||||
input.peekFully(scratch.getData(), 0, 8);
|
|
||||||
scratch.skipBytes(4);
|
|
||||||
if (scratch.readInt() == Atom.TYPE_hdlr) {
|
|
||||||
input.resetPeekPosition();
|
|
||||||
} else {
|
|
||||||
input.skipFully(4);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Processes an atom whose payload does not need to be parsed. */
|
/** Processes an atom whose payload does not need to be parsed. */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user