Extend MP4 extractor implementation with complete parsing of mhaC and mhaP boxes

This commit is contained in:
Rohit Singh 2023-10-09 12:08:43 +02:00
parent 98e9022c0e
commit 94442291bd
4 changed files with 228 additions and 5 deletions

View file

@ -1,3 +1,28 @@
/***************************************************************************
Fraunhofer hereby grants to Google free of charge a worldwide, perpetual,
irrevocable, non-exclusive copyright license with the right to sublicense
through multiple tiers to use, copy, distribute, modify and create
derivative works of the Software Patches for Exoplayer in source code form
and/or object code versions of the software. For the avoidance of doubt,
this license does not include any license to any Fraunhofer patents or any
third-party patents. Since the license is granted without any charge,
Fraunhofer provides the Software Patches for Exoplayer, in accordance with
the laws of the Federal Republic of Germany, on an as is basis, WITHOUT
WARRANTIES or conditions of any kind, either express or implied, including,
without limitation, any warranties or conditions of title, non-infringement,
merchantability, or fitness for a particular purpose.
For the purpose of clarity, the provision of the Software Patches for
Exoplayer by Fraunhofer and the use of the same by Google shall be subject
solely to the license stated above.
This file was originally licensed under the Apache 2.0 license (see license
note below). Fraunhofer has modified this files and provides its copyright
in the modifications to Google under the above terms, which shall not be
considered a contribution under the Apache 2.0 license.
***************************************************************************/
/*
* Copyright (C) 2016 The Android Open Source Project
*
@ -1608,4 +1633,51 @@ public final class C {
int mediaDrmErrorCode) {
return Util.getErrorCodeForMediaDrmErrorCode(mediaDrmErrorCode);
}
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
Format.NO_VALUE,
MPEGH_PROFILE_MAIN_L1,
MPEGH_PROFILE_MAIN_L2,
MPEGH_PROFILE_MAIN_L3,
MPEGH_PROFILE_MAIN_L4,
MPEGH_PROFILE_MAIN_L5,
MPEGH_PROFILE_HIGH_L1,
MPEGH_PROFILE_HIGH_L2,
MPEGH_PROFILE_HIGH_L3,
MPEGH_PROFILE_HIGH_L4,
MPEGH_PROFILE_HIGH_L5,
MPEGH_PROFILE_LC_L1,
MPEGH_PROFILE_LC_L2,
MPEGH_PROFILE_LC_L3,
MPEGH_PROFILE_LC_L4,
MPEGH_PROFILE_LC_L5,
MPEGH_PROFILE_BL_L1,
MPEGH_PROFILE_BL_L2,
MPEGH_PROFILE_BL_L3,
MPEGH_PROFILE_BL_L4,
MPEGH_PROFILE_BL_L5
})
public @interface MpeghProfileLevelIndication {}
public static final int MPEGH_PROFILE_MAIN_L1 = 1;
public static final int MPEGH_PROFILE_MAIN_L2 = 2;
public static final int MPEGH_PROFILE_MAIN_L3 = 3;
public static final int MPEGH_PROFILE_MAIN_L4 = 4;
public static final int MPEGH_PROFILE_MAIN_L5 = 5;
public static final int MPEGH_PROFILE_HIGH_L1 = 6;
public static final int MPEGH_PROFILE_HIGH_L2 = 7;
public static final int MPEGH_PROFILE_HIGH_L3 = 8;
public static final int MPEGH_PROFILE_HIGH_L4 = 9;
public static final int MPEGH_PROFILE_HIGH_L5 = 10;
public static final int MPEGH_PROFILE_LC_L1 = 11;
public static final int MPEGH_PROFILE_LC_L2 = 12;
public static final int MPEGH_PROFILE_LC_L3 = 13;
public static final int MPEGH_PROFILE_LC_L4 = 14;
public static final int MPEGH_PROFILE_LC_L5 = 15;
public static final int MPEGH_PROFILE_BL_L1 = 16;
public static final int MPEGH_PROFILE_BL_L2 = 17;
public static final int MPEGH_PROFILE_BL_L3 = 18;
public static final int MPEGH_PROFILE_BL_L4 = 19;
public static final int MPEGH_PROFILE_BL_L5 = 20;
}

View file

@ -1,3 +1,28 @@
/***************************************************************************
Fraunhofer hereby grants to Google free of charge a worldwide, perpetual,
irrevocable, non-exclusive copyright license with the right to sublicense
through multiple tiers to use, copy, distribute, modify and create
derivative works of the Software Patches for Exoplayer in source code form
and/or object code versions of the software. For the avoidance of doubt,
this license does not include any license to any Fraunhofer patents or any
third-party patents. Since the license is granted without any charge,
Fraunhofer provides the Software Patches for Exoplayer, in accordance with
the laws of the Federal Republic of Germany, on an as is basis, WITHOUT
WARRANTIES or conditions of any kind, either express or implied, including,
without limitation, any warranties or conditions of title, non-infringement,
merchantability, or fitness for a particular purpose.
For the purpose of clarity, the provision of the Software Patches for
Exoplayer by Fraunhofer and the use of the same by Google shall be subject
solely to the license stated above.
This file was originally licensed under the Apache 2.0 license (see license
note below). Fraunhofer has modified this files and provides its copyright
in the modifications to Google under the above terms, which shall not be
considered a contribution under the Apache 2.0 license.
***************************************************************************/
/*
* Copyright (C) 2016 The Android Open Source Project
*
@ -176,6 +201,10 @@ public final class Format implements Bundleable {
private int encoderDelay;
private int encoderPadding;
@C.MpeghProfileLevelIndication private int mpeghProfileLevelIndication;
private int mpeghReferenceChannelLayout;
@Nullable @C.MpeghProfileLevelIndication private int[] mpeghCompatibleProfileLevelSet;
// Text specific.
private int accessibilityChannel;
@ -207,6 +236,8 @@ public final class Format implements Bundleable {
channelCount = NO_VALUE;
sampleRate = NO_VALUE;
pcmEncoding = NO_VALUE;
mpeghProfileLevelIndication = NO_VALUE;
mpeghReferenceChannelLayout = NO_VALUE;
// Text specific.
accessibilityChannel = NO_VALUE;
cueReplacementBehavior = CUE_REPLACEMENT_BEHAVIOR_MERGE;
@ -255,6 +286,9 @@ public final class Format implements Bundleable {
this.pcmEncoding = format.pcmEncoding;
this.encoderDelay = format.encoderDelay;
this.encoderPadding = format.encoderPadding;
this.mpeghReferenceChannelLayout = format.mpeghReferenceChannelLayout;
this.mpeghProfileLevelIndication = format.mpeghProfileLevelIndication;
this.mpeghCompatibleProfileLevelSet = format.mpeghCompatibleProfileLevelSet;
// Text specific.
this.accessibilityChannel = format.accessibilityChannel;
this.cueReplacementBehavior = format.cueReplacementBehavior;
@ -622,6 +656,19 @@ public final class Format implements Bundleable {
return this;
}
public Builder setMpeghProfileLevelIndication(@C.MpeghProfileLevelIndication int mpeghProfileLevelIndication) {
this.mpeghProfileLevelIndication = mpeghProfileLevelIndication;
return this;
}
public Builder setMpeghReferenceChannelLayout(int mpeghReferenceChannelLayout) {
this.mpeghReferenceChannelLayout = mpeghReferenceChannelLayout;
return this;
}
public Builder setMpeghCompatibleProfileLevelSet(@Nullable @C.MpeghProfileLevelIndication int[] mpeghCompatibleProfileLevelSet) {
this.mpeghCompatibleProfileLevelSet = mpeghCompatibleProfileLevelSet;
return this;
}
// Text specific.
/**
@ -895,6 +942,10 @@ public final class Format implements Bundleable {
*/
@UnstableApi public final int encoderPadding;
@C.MpeghProfileLevelIndication public final int mpeghProfileLevelIndication;
public final int mpeghReferenceChannelLayout;
@Nullable @C.MpeghProfileLevelIndication public final int[] mpeghCompatibleProfileLevelSet;
// Text specific.
/** The Accessibility channel, or {@link #NO_VALUE} if not known or applicable. */
@ -965,6 +1016,9 @@ public final class Format implements Bundleable {
pcmEncoding = builder.pcmEncoding;
encoderDelay = builder.encoderDelay == NO_VALUE ? 0 : builder.encoderDelay;
encoderPadding = builder.encoderPadding == NO_VALUE ? 0 : builder.encoderPadding;
mpeghProfileLevelIndication = builder.mpeghProfileLevelIndication;
mpeghReferenceChannelLayout = builder.mpeghReferenceChannelLayout;
mpeghCompatibleProfileLevelSet = builder.mpeghCompatibleProfileLevelSet;
// Text specific.
accessibilityChannel = builder.accessibilityChannel;
cueReplacementBehavior = builder.cueReplacementBehavior;
@ -1138,6 +1192,9 @@ public final class Format implements Bundleable {
result = 31 * result + pcmEncoding;
result = 31 * result + encoderDelay;
result = 31 * result + encoderPadding;
result = 31 * result + mpeghProfileLevelIndication;
result = 31 * result + mpeghReferenceChannelLayout;
// [Omitted] mpeghCompatibleProfileLevelSet.
// Text specific.
result = 31 * result + accessibilityChannel;
// Image specific.
@ -1178,6 +1235,9 @@ public final class Format implements Bundleable {
&& pcmEncoding == other.pcmEncoding
&& encoderDelay == other.encoderDelay
&& encoderPadding == other.encoderPadding
&& mpeghReferenceChannelLayout == other.mpeghReferenceChannelLayout
&& mpeghProfileLevelIndication == other.mpeghProfileLevelIndication
&& Util.areEqual(mpeghCompatibleProfileLevelSet, other.mpeghCompatibleProfileLevelSet)
&& accessibilityChannel == other.accessibilityChannel
&& tileCountHorizontal == other.tileCountHorizontal
&& tileCountVertical == other.tileCountVertical
@ -1382,6 +1442,9 @@ public final class Format implements Bundleable {
private static final String FIELD_CRYPTO_TYPE = Util.intToStringMaxRadix(29);
private static final String FIELD_TILE_COUNT_HORIZONTAL = Util.intToStringMaxRadix(30);
private static final String FIELD_TILE_COUNT_VERTICAL = Util.intToStringMaxRadix(31);
private static final String FIELD_MPEGH_PROFILE_LEVEL_INDICATION = Util.intToStringMaxRadix(32);
private static final String FIELD_MPEGH_REFERENCE_CHANNEL_LAYOUT = Util.intToStringMaxRadix(33);
private static final String FIELD_MPEGH_COMPATIBLE_PROFILE_LEVEL_SET = Util.intToStringMaxRadix(34);
@UnstableApi
@Override
@ -1437,6 +1500,9 @@ public final class Format implements Bundleable {
bundle.putInt(FIELD_PCM_ENCODING, pcmEncoding);
bundle.putInt(FIELD_ENCODER_DELAY, encoderDelay);
bundle.putInt(FIELD_ENCODER_PADDING, encoderPadding);
bundle.putInt(FIELD_MPEGH_PROFILE_LEVEL_INDICATION, mpeghProfileLevelIndication);
bundle.putInt(FIELD_MPEGH_REFERENCE_CHANNEL_LAYOUT, mpeghReferenceChannelLayout);
bundle.putIntArray(FIELD_MPEGH_COMPATIBLE_PROFILE_LEVEL_SET, mpeghCompatibleProfileLevelSet);
// Text specific.
bundle.putInt(FIELD_ACCESSIBILITY_CHANNEL, accessibilityChannel);
// Image specific.
@ -1512,6 +1578,12 @@ public final class Format implements Bundleable {
.setPcmEncoding(bundle.getInt(FIELD_PCM_ENCODING, DEFAULT.pcmEncoding))
.setEncoderDelay(bundle.getInt(FIELD_ENCODER_DELAY, DEFAULT.encoderDelay))
.setEncoderPadding(bundle.getInt(FIELD_ENCODER_PADDING, DEFAULT.encoderPadding))
.setMpeghProfileLevelIndication(
bundle.getInt(FIELD_MPEGH_PROFILE_LEVEL_INDICATION, DEFAULT.mpeghProfileLevelIndication))
.setMpeghReferenceChannelLayout(
bundle.getInt(FIELD_MPEGH_REFERENCE_CHANNEL_LAYOUT, DEFAULT.mpeghReferenceChannelLayout))
.setMpeghCompatibleProfileLevelSet(
bundle.getIntArray(FIELD_MPEGH_COMPATIBLE_PROFILE_LEVEL_SET))
// Text specific.
.setAccessibilityChannel(
bundle.getInt(FIELD_ACCESSIBILITY_CHANNEL, DEFAULT.accessibilityChannel))

View file

@ -1,3 +1,28 @@
/***************************************************************************
Fraunhofer hereby grants to Google free of charge a worldwide, perpetual,
irrevocable, non-exclusive copyright license with the right to sublicense
through multiple tiers to use, copy, distribute, modify and create
derivative works of the Software Patches for Exoplayer in source code form
and/or object code versions of the software. For the avoidance of doubt,
this license does not include any license to any Fraunhofer patents or any
third-party patents. Since the license is granted without any charge,
Fraunhofer provides the Software Patches for Exoplayer, in accordance with
the laws of the Federal Republic of Germany, on an as is basis, WITHOUT
WARRANTIES or conditions of any kind, either express or implied, including,
without limitation, any warranties or conditions of title, non-infringement,
merchantability, or fitness for a particular purpose.
For the purpose of clarity, the provision of the Software Patches for
Exoplayer by Fraunhofer and the use of the same by Google shall be subject
solely to the license stated above.
This file was originally licensed under the Apache 2.0 license (see license
note below). Fraunhofer has modified this files and provides its copyright
in the modifications to Google under the above terms, which shall not be
considered a contribution under the Apache 2.0 license.
***************************************************************************/
/*
* Copyright (C) 2016 The Android Open Source Project
*
@ -125,6 +150,9 @@ import java.util.List;
@SuppressWarnings("ConstantCaseForConstants")
public static final int TYPE_mhaC = 0x6d686143;
@SuppressWarnings("ConstantCaseForConstants")
public static final int TYPE_mhaP = 0x6d686150;
@SuppressWarnings("ConstantCaseForConstants")
public static final int TYPE_wave = 0x77617665;

View file

@ -1,3 +1,28 @@
/***************************************************************************
Fraunhofer hereby grants to Google free of charge a worldwide, perpetual,
irrevocable, non-exclusive copyright license with the right to sublicense
through multiple tiers to use, copy, distribute, modify and create
derivative works of the Software Patches for Exoplayer in source code form
and/or object code versions of the software. For the avoidance of doubt,
this license does not include any license to any Fraunhofer patents or any
third-party patents. Since the license is granted without any charge,
Fraunhofer provides the Software Patches for Exoplayer, in accordance with
the laws of the Federal Republic of Germany, on an as is basis, WITHOUT
WARRANTIES or conditions of any kind, either express or implied, including,
without limitation, any warranties or conditions of title, non-infringement,
merchantability, or fitness for a particular purpose.
For the purpose of clarity, the provision of the Software Patches for
Exoplayer by Fraunhofer and the use of the same by Google shall be subject
solely to the license stated above.
This file was originally licensed under the Apache 2.0 license (see license
note below). Fraunhofer has modified this files and provides its copyright
in the modifications to Google under the above terms, which shall not be
considered a contribution under the Apache 2.0 license.
***************************************************************************/
/*
* Copyright (C) 2016 The Android Open Source Project
*
@ -1598,6 +1623,9 @@ import java.util.List;
int sampleRateMlp = 0;
@C.PcmEncoding int pcmEncoding = Format.NO_VALUE;
@Nullable String codecs = null;
@C.MpeghProfileLevelIndication int mpeghProfileLevelIndication = Format.NO_VALUE;
int mpeghReferenceChannelLayout = Format.NO_VALUE;
@Nullable @C.MpeghProfileLevelIndication int[] mpeghCompatibleProfileLevelSet = null;
@Nullable EsdsData esdsData = null;
if (quickTimeSoundDescriptionVersion == 0 || quickTimeSoundDescriptionVersion == 1) {
@ -1720,15 +1748,35 @@ import java.util.List;
ExtractorUtil.checkContainerInput(childAtomSize > 0, "childAtomSize must be positive");
int childAtomType = parent.readInt();
if (childAtomType == Atom.TYPE_mhaC) {
// See ISO_IEC_23008-3;2019 MHADecoderConfigurationRecord
// See ISO_IEC_23008-3;2022 MHADecoderConfigurationRecord
// The header consists of: size (4), boxtype 'mhaC' (4), configurationVersion (1),
// mpegh3daProfileLevelIndication (1), referenceChannelLayout (1), mpegh3daConfigLength (2).
int mhacHeaderSize = 13;
int childAtomBodySize = childAtomSize - mhacHeaderSize;
byte[] initializationDataBytes = new byte[childAtomBodySize];
parent.setPosition(childPosition + mhacHeaderSize);
parent.readBytes(initializationDataBytes, 0, childAtomBodySize);
parent.setPosition(childPosition + Atom.HEADER_SIZE);
int configurationVersion = parent.readUnsignedByte();
mpeghProfileLevelIndication = parent.readUnsignedByte();
mpeghReferenceChannelLayout = parent.readUnsignedByte();
if (mimeType.equals(MimeTypes.AUDIO_MPEGH_MHM1)) {
codecs = String.format("mhm1.0x%02X", mpeghProfileLevelIndication);
} else {
codecs = String.format("mha1.0x%02X", mpeghProfileLevelIndication);
}
int mpegh3daConfigLength = parent.readUnsignedShort();
byte[] initializationDataBytes = new byte[mpegh3daConfigLength];
parent.readBytes(initializationDataBytes, 0, mpegh3daConfigLength);
initializationData = ImmutableList.of(initializationDataBytes);
} else if (childAtomType == Atom.TYPE_mhaP) {
// See ISO_IEC_23008-3;2022 MHAProfileAndLevelCompatibilitySetBox
// The header consists of: size (4), boxtype 'mhaP' (4), numCompatibleSets (1).
int mhapHeaderSize = 9;
parent.setPosition(childPosition + Atom.HEADER_SIZE);
int numCompatibleSets = parent.readUnsignedByte();
if (numCompatibleSets > 0) {
mpeghCompatibleProfileLevelSet = new int[numCompatibleSets];
for (int i = 0; i < numCompatibleSets; i++) {
mpeghCompatibleProfileLevelSet[i] = parent.readUnsignedByte();
}
}
} else if (childAtomType == Atom.TYPE_esds
|| (isQuickTime && childAtomType == Atom.TYPE_wave)) {
int esdsAtomPosition =
@ -1835,6 +1883,9 @@ import java.util.List;
.setPcmEncoding(pcmEncoding)
.setInitializationData(initializationData)
.setDrmInitData(drmInitData)
.setMpeghProfileLevelIndication(mpeghProfileLevelIndication)
.setMpeghReferenceChannelLayout(mpeghReferenceChannelLayout)
.setMpeghCompatibleProfileLevelSet(mpeghCompatibleProfileLevelSet)
.setLanguage(language);
if (esdsData != null) {