Skip to content

Commit 3271632

Browse files
Merge pull request #2453 from nift4:fmp4metadata
PiperOrigin-RevId: 777751247
2 parents bb5da94 + e4149cb commit 3271632

File tree

162 files changed

+257
-83
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

162 files changed

+257
-83
lines changed

RELEASENOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
* Transformer:
1010
* Track Selection:
1111
* Extractors:
12+
* Parse metadata from fragmented MP4 files
13+
([#2084](https://github.com/androidx/media/issues/2084)).
1214
* JPEG: Support motion photos that don't have an Exif segment at the start
1315
([#2552](https://github.com/androidx/media/issues/2552)).
1416
* DataSource:

libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import androidx.media3.common.DrmInitData;
3333
import androidx.media3.common.DrmInitData.SchemeData;
3434
import androidx.media3.common.Format;
35+
import androidx.media3.common.Metadata;
3536
import androidx.media3.common.MimeTypes;
3637
import androidx.media3.common.ParserException;
3738
import androidx.media3.common.util.Log;
@@ -483,6 +484,8 @@ public void init(ExtractorOutput output) {
483484
enterReadingAtomHeaderState();
484485
initExtraTracks();
485486
if (sideloadedTrack != null) {
487+
Format.Builder formatBuilder = sideloadedTrack.format.buildUpon();
488+
formatBuilder.setContainerMimeType(getContainerMimeType(sideloadedTrack.format));
486489
TrackBundle bundle =
487490
new TrackBundle(
488491
extractorOutput.track(0, sideloadedTrack.type),
@@ -499,7 +502,7 @@ public void init(ExtractorOutput output) {
499502
/* duration= */ 0,
500503
/* size= */ 0,
501504
/* flags= */ 0),
502-
getContainerMimeType(sideloadedTrack.format));
505+
formatBuilder.build());
503506
trackBundles.put(0, bundle);
504507
extractorOutput.endTracks();
505508
}
@@ -642,6 +645,9 @@ private boolean readAtomHeader(ExtractorInput input) throws IOException {
642645

643646
if (shouldParseContainerAtom(atomType)) {
644647
long endPosition = input.getPosition() + atomSize - Mp4Box.HEADER_SIZE;
648+
if (atomSize != atomHeaderBytesRead && atomType == Mp4Box.TYPE_meta) {
649+
maybeSkipRemainingMetaAtomHeaderBytes(input);
650+
}
645651
containerAtoms.push(new ContainerBox(atomType, endPosition));
646652
if (atomSize == atomHeaderBytesRead) {
647653
processAtomEnded(endPosition);
@@ -674,6 +680,14 @@ private boolean readAtomHeader(ExtractorInput input) throws IOException {
674680
return true;
675681
}
676682

683+
private void maybeSkipRemainingMetaAtomHeaderBytes(ExtractorInput input) throws IOException {
684+
scratch.reset(Mp4Box.HEADER_SIZE);
685+
input.peekFully(scratch.getData(), 0, Mp4Box.HEADER_SIZE);
686+
BoxParser.maybeSkipRemainingMetaBoxHeaderBytes(scratch);
687+
input.skipFully(scratch.getPosition());
688+
input.resetPeekPosition();
689+
}
690+
677691
private void readAtomPayload(ExtractorInput input) throws IOException {
678692
int atomPayloadSize = (int) (atomSize - atomHeaderBytesRead);
679693
@Nullable ParsableByteArray atomData = this.atomData;
@@ -743,11 +757,27 @@ private void onMoovContainerAtomRead(ContainerBox moov) throws ParserException {
743757
}
744758
}
745759

760+
@Nullable Metadata mdtaMetadata = null;
761+
@Nullable Mp4Box.ContainerBox meta = moov.getContainerBoxOfType(Mp4Box.TYPE_meta);
762+
if (meta != null) {
763+
mdtaMetadata = BoxParser.parseMdtaFromMeta(meta);
764+
}
765+
GaplessInfoHolder gaplessInfoHolder = new GaplessInfoHolder();
766+
@Nullable Metadata udtaMetadata = null;
767+
@Nullable Mp4Box.LeafBox udta = moov.getLeafBoxOfType(Mp4Box.TYPE_udta);
768+
if (udta != null) {
769+
udtaMetadata = BoxParser.parseUdta(udta);
770+
gaplessInfoHolder.setFromMetadata(udtaMetadata);
771+
}
772+
Metadata mvhdMetadata =
773+
new Metadata(
774+
BoxParser.parseMvhd(checkNotNull(moov.getLeafBoxOfType(Mp4Box.TYPE_mvhd)).data));
775+
746776
// Construction of tracks and sample tables.
747777
List<TrackSampleTable> sampleTables =
748778
parseTraks(
749779
moov,
750-
new GaplessInfoHolder(),
780+
gaplessInfoHolder,
751781
duration,
752782
drmInitData,
753783
/* ignoreEditLists= */ (flags & FLAG_WORKAROUND_IGNORE_EDIT_LISTS) != 0,
@@ -763,12 +793,22 @@ private void onMoovContainerAtomRead(ContainerBox moov) throws ParserException {
763793
Track track = sampleTable.track;
764794
TrackOutput output = extractorOutput.track(i, track.type);
765795
output.durationUs(track.durationUs);
796+
Format.Builder formatBuilder = track.format.buildUpon();
797+
formatBuilder.setContainerMimeType(containerMimeType);
798+
MetadataUtil.setFormatGaplessInfo(track.type, gaplessInfoHolder, formatBuilder);
799+
MetadataUtil.setFormatMetadata(
800+
track.type,
801+
mdtaMetadata,
802+
formatBuilder,
803+
track.format.metadata,
804+
udtaMetadata,
805+
mvhdMetadata);
766806
TrackBundle trackBundle =
767807
new TrackBundle(
768808
output,
769809
sampleTable,
770810
getDefaultSampleValues(defaultSampleValuesArray, track.id),
771-
containerMimeType);
811+
formatBuilder.build());
772812
trackBundles.put(track.id, trackBundle);
773813
durationUs = max(durationUs, track.durationUs);
774814
}
@@ -1900,7 +1940,10 @@ private static boolean shouldParseLeafAtom(int atom) {
19001940
|| atom == Mp4Box.TYPE_sgpd
19011941
|| atom == Mp4Box.TYPE_elst
19021942
|| atom == Mp4Box.TYPE_mehd
1903-
|| atom == Mp4Box.TYPE_emsg;
1943+
|| atom == Mp4Box.TYPE_emsg
1944+
|| atom == Mp4Box.TYPE_udta
1945+
|| atom == Mp4Box.TYPE_keys
1946+
|| atom == Mp4Box.TYPE_ilst;
19041947
}
19051948

19061949
/** Returns whether the extractor should decode a container atom with type {@code atom}. */
@@ -1913,7 +1956,8 @@ private static boolean shouldParseContainerAtom(int atom) {
19131956
|| atom == Mp4Box.TYPE_moof
19141957
|| atom == Mp4Box.TYPE_traf
19151958
|| atom == Mp4Box.TYPE_mvex
1916-
|| atom == Mp4Box.TYPE_edts;
1959+
|| atom == Mp4Box.TYPE_edts
1960+
|| atom == Mp4Box.TYPE_meta;
19171961
}
19181962

19191963
/** Holds data corresponding to a metadata sample. */
@@ -1946,7 +1990,7 @@ private static final class TrackBundle {
19461990
public int currentTrackRunIndex;
19471991
public int firstSampleToOutputIndex;
19481992

1949-
private final String containerMimeType;
1993+
private final Format baseFormat;
19501994
private final ParsableByteArray encryptionSignalByte;
19511995
private final ParsableByteArray defaultInitializationVector;
19521996

@@ -1956,11 +2000,11 @@ public TrackBundle(
19562000
TrackOutput output,
19572001
TrackSampleTable moovSampleTable,
19582002
DefaultSampleValues defaultSampleValues,
1959-
String containerMimeType) {
2003+
Format baseFormat) {
19602004
this.output = output;
19612005
this.moovSampleTable = moovSampleTable;
19622006
this.defaultSampleValues = defaultSampleValues;
1963-
this.containerMimeType = containerMimeType;
2007+
this.baseFormat = baseFormat;
19642008
fragment = new TrackFragment();
19652009
scratch = new ParsableByteArray();
19662010
encryptionSignalByte = new ParsableByteArray(1);
@@ -1971,9 +2015,7 @@ public TrackBundle(
19712015
public void reset(TrackSampleTable moovSampleTable, DefaultSampleValues defaultSampleValues) {
19722016
this.moovSampleTable = moovSampleTable;
19732017
this.defaultSampleValues = defaultSampleValues;
1974-
Format format =
1975-
moovSampleTable.track.format.buildUpon().setContainerMimeType(containerMimeType).build();
1976-
output.format(format);
2018+
output.format(baseFormat);
19772019
resetFragmentInfo();
19782020
}
19792021

@@ -1984,14 +2026,7 @@ public void updateDrmInitData(DrmInitData drmInitData) {
19842026
castNonNull(fragment.header).sampleDescriptionIndex);
19852027
@Nullable String schemeType = encryptionBox != null ? encryptionBox.schemeType : null;
19862028
DrmInitData updatedDrmInitData = drmInitData.copyWithSchemeType(schemeType);
1987-
Format format =
1988-
moovSampleTable
1989-
.track
1990-
.format
1991-
.buildUpon()
1992-
.setContainerMimeType(containerMimeType)
1993-
.setDrmInitData(updatedDrmInitData)
1994-
.build();
2029+
Format format = baseFormat.buildUpon().setDrmInitData(updatedDrmInitData).build();
19952030
output.format(format);
19962031
}
19972032

libraries/extractor/src/main/java/androidx/media3/extractor/mp4/Mp4Extractor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,8 +1085,8 @@ private void processEndOfStreamReadingAtomHeader() {
10851085
}
10861086

10871087
private void maybeSkipRemainingMetaAtomHeaderBytes(ExtractorInput input) throws IOException {
1088-
scratch.reset(8);
1089-
input.peekFully(scratch.getData(), 0, 8);
1088+
scratch.reset(Mp4Box.HEADER_SIZE);
1089+
input.peekFully(scratch.getData(), 0, Mp4Box.HEADER_SIZE);
10901090
BoxParser.maybeSkipRemainingMetaBoxHeaderBytes(scratch);
10911091
input.skipFully(scratch.getPosition());
10921092
input.resetPeekPosition();

libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions.mp4.0.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ track 0:
2121
lumaBitdepth = 8
2222
chromaBitdepth = 8
2323
language = und
24+
metadata = entries=[TSSE: description=null: values=[Lavf60.16.100], Mp4Timestamp: creation time=0, modification time=0, timescale=1000]
2425
initializationData:
2526
data = length 27, hash 9F13E633
2627
data = length 8, hash 94643657

libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions.mp4.reading_within_gop_sample_dependencies.0.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ track 0:
2121
lumaBitdepth = 8
2222
chromaBitdepth = 8
2323
language = und
24+
metadata = entries=[TSSE: description=null: values=[Lavf60.16.100], Mp4Timestamp: creation time=0, modification time=0, timescale=1000]
2425
initializationData:
2526
data = length 27, hash 9F13E633
2627
data = length 8, hash 94643657

libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions.mp4.reading_within_gop_sample_dependencies.unknown_length.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ track 0:
2121
lumaBitdepth = 8
2222
chromaBitdepth = 8
2323
language = und
24+
metadata = entries=[TSSE: description=null: values=[Lavf60.16.100], Mp4Timestamp: creation time=0, modification time=0, timescale=1000]
2425
initializationData:
2526
data = length 27, hash 9F13E633
2627
data = length 8, hash 94643657

libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions.mp4.unknown_length.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ track 0:
2121
lumaBitdepth = 8
2222
chromaBitdepth = 8
2323
language = und
24+
metadata = entries=[TSSE: description=null: values=[Lavf60.16.100], Mp4Timestamp: creation time=0, modification time=0, timescale=1000]
2425
initializationData:
2526
data = length 27, hash 9F13E633
2627
data = length 8, hash 94643657

libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.0.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ track 0:
2424
lumaBitdepth = 8
2525
chromaBitdepth = 8
2626
language = und
27+
metadata = entries=[TSSE: description=null: values=[Lavf60.16.100], Mp4Timestamp: creation time=0, modification time=0, timescale=1000]
2728
initializationData:
2829
data = length 2426, hash 25737613
2930
sample 0:

libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.0.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ track 0:
2424
lumaBitdepth = 8
2525
chromaBitdepth = 8
2626
language = und
27+
metadata = entries=[TSSE: description=null: values=[Lavf60.16.100], Mp4Timestamp: creation time=0, modification time=0, timescale=1000]
2728
initializationData:
2829
data = length 2426, hash 25737613
2930
sample 0:

libraries/test_data/src/test/assets/extractordumps/mp4/fragmented_captions_h265.mp4.reading_within_gop_sample_dependencies.unknown_length.dump

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ track 0:
2424
lumaBitdepth = 8
2525
chromaBitdepth = 8
2626
language = und
27+
metadata = entries=[TSSE: description=null: values=[Lavf60.16.100], Mp4Timestamp: creation time=0, modification time=0, timescale=1000]
2728
initializationData:
2829
data = length 2426, hash 25737613
2930
sample 0:

0 commit comments

Comments
 (0)