Reading average and peak bitrates from esds boxes.

This provides better compatibility with MediaExtractor, which does read these fields; we also need them for being able to mux file contents into another mp4 file.

Also, there is a minor refactor included so that we have an actual type for esds box contents instead of a pair.

PiperOrigin-RevId: 438673825
This commit is contained in:
olly 2022-03-31 23:18:52 +01:00 committed by Ian Baker
parent 7bbcf1c4c1
commit 21d085f8a9
36 changed files with 116 additions and 16 deletions

View File

@ -24,6 +24,7 @@
Media Rating Council (MRC) recommendations. Media Rating Council (MRC) recommendations.
* Extractors: * Extractors:
* Matroska: Parse `DiscardPadding` for Opus tracks. * Matroska: Parse `DiscardPadding` for Opus tracks.
* Parse bitrates from `esds` boxes.
* UI: * UI:
* Fix delivery of events to `OnClickListener`s set on `PlayerView` and * Fix delivery of events to `OnClickListener`s set on `PlayerView` and
`LegacyPlayerView`, in the case that `useController=false` `LegacyPlayerView`, in the case that `useController=false`

View File

@ -47,6 +47,19 @@ public final class MediaFormatUtil {
// The constant value must not be changed, because it's also set by the framework MediaParser API. // The constant value must not be changed, because it's also set by the framework MediaParser API.
public static final String KEY_PCM_ENCODING_EXTENDED = "exo-pcm-encoding-int"; public static final String KEY_PCM_ENCODING_EXTENDED = "exo-pcm-encoding-int";
/**
* The {@link MediaFormat} key for the maximum bitrate in bits per second.
*
* <p>The associated value is an integer.
*
* <p>The key string constant is the same as {@code MediaFormat#KEY_MAX_BITRATE}. Values for it
* are already returned by the framework MediaExtractor; the key is a hidden field in {@code
* MediaFormat} though, which is why it's being replicated here.
*/
// The constant value must not be changed, because it's also set by the framework MediaParser and
// MediaExtractor APIs.
public static final String KEY_MAX_BIT_RATE = "max-bitrate";
private static final int MAX_POWER_OF_TWO_INT = 1 << 30; private static final int MAX_POWER_OF_TWO_INT = 1 << 30;
/** /**
@ -63,6 +76,7 @@ public final class MediaFormatUtil {
public static MediaFormat createMediaFormatFromFormat(Format format) { public static MediaFormat createMediaFormatFromFormat(Format format) {
MediaFormat result = new MediaFormat(); MediaFormat result = new MediaFormat();
maybeSetInteger(result, MediaFormat.KEY_BIT_RATE, format.bitrate); maybeSetInteger(result, MediaFormat.KEY_BIT_RATE, format.bitrate);
maybeSetInteger(result, KEY_MAX_BIT_RATE, format.peakBitrate);
maybeSetInteger(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount); maybeSetInteger(result, MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
maybeSetColorInfo(result, format.colorInfo); maybeSetColorInfo(result, format.colorInfo);

View File

@ -1116,6 +1116,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
@Nullable String codecs = null; @Nullable String codecs = null;
@Nullable byte[] projectionData = null; @Nullable byte[] projectionData = null;
@C.StereoMode int stereoMode = Format.NO_VALUE; @C.StereoMode int stereoMode = Format.NO_VALUE;
@Nullable EsdsData esdsData = null;
// HDR related metadata. // HDR related metadata.
@C.ColorSpace int colorSpace = Format.NO_VALUE; @C.ColorSpace int colorSpace = Format.NO_VALUE;
@ -1210,10 +1211,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
mimeType = MimeTypes.VIDEO_H263; mimeType = MimeTypes.VIDEO_H263;
} else if (childAtomType == Atom.TYPE_esds) { } else if (childAtomType == Atom.TYPE_esds) {
ExtractorUtil.checkContainerInput(mimeType == null, /* message= */ null); ExtractorUtil.checkContainerInput(mimeType == null, /* message= */ null);
Pair<@NullableType String, byte @NullableType []> mimeTypeAndInitializationDataBytes = esdsData = parseEsdsFromParent(parent, childStartPosition);
parseEsdsFromParent(parent, childStartPosition); mimeType = esdsData.mimeType;
mimeType = mimeTypeAndInitializationDataBytes.first; @Nullable byte[] initializationDataBytes = esdsData.initializationData;
@Nullable byte[] initializationDataBytes = mimeTypeAndInitializationDataBytes.second;
if (initializationDataBytes != null) { if (initializationDataBytes != null) {
initializationData = ImmutableList.of(initializationDataBytes); initializationData = ImmutableList.of(initializationDataBytes);
} }
@ -1301,6 +1301,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
colorTransfer, colorTransfer,
hdrStaticInfo != null ? hdrStaticInfo.array() : null)); hdrStaticInfo != null ? hdrStaticInfo.array() : null));
} }
if (esdsData != null) {
formatBuilder.setAverageBitrate(esdsData.bitrate).setPeakBitrate(esdsData.peakBitrate);
}
out.format = formatBuilder.build(); out.format = formatBuilder.build();
} }
@ -1391,6 +1396,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
int sampleRateMlp = 0; int sampleRateMlp = 0;
@C.PcmEncoding int pcmEncoding = Format.NO_VALUE; @C.PcmEncoding int pcmEncoding = Format.NO_VALUE;
@Nullable String codecs = null; @Nullable String codecs = null;
@Nullable EsdsData esdsData = null;
if (quickTimeSoundDescriptionVersion == 0 || quickTimeSoundDescriptionVersion == 1) { if (quickTimeSoundDescriptionVersion == 0 || quickTimeSoundDescriptionVersion == 1) {
channelCount = parent.readUnsignedShort(); channelCount = parent.readUnsignedShort();
@ -1507,10 +1513,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
? childPosition ? childPosition
: findBoxPosition(parent, Atom.TYPE_esds, childPosition, childAtomSize); : findBoxPosition(parent, Atom.TYPE_esds, childPosition, childAtomSize);
if (esdsAtomPosition != C.POSITION_UNSET) { if (esdsAtomPosition != C.POSITION_UNSET) {
Pair<@NullableType String, byte @NullableType []> mimeTypeAndInitializationData = esdsData = parseEsdsFromParent(parent, esdsAtomPosition);
parseEsdsFromParent(parent, esdsAtomPosition); mimeType = esdsData.mimeType;
mimeType = mimeTypeAndInitializationData.first; @Nullable byte[] initializationDataBytes = esdsData.initializationData;
@Nullable byte[] initializationDataBytes = mimeTypeAndInitializationData.second;
if (initializationDataBytes != null) { if (initializationDataBytes != null) {
if (MimeTypes.AUDIO_AAC.equals(mimeType)) { if (MimeTypes.AUDIO_AAC.equals(mimeType)) {
// Update sampleRate and channelCount from the AudioSpecificConfig initialization // Update sampleRate and channelCount from the AudioSpecificConfig initialization
@ -1591,7 +1596,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} }
if (out.format == null && mimeType != null) { if (out.format == null && mimeType != null) {
out.format = Format.Builder formatBuilder =
new Format.Builder() new Format.Builder()
.setId(trackId) .setId(trackId)
.setSampleMimeType(mimeType) .setSampleMimeType(mimeType)
@ -1601,8 +1606,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
.setPcmEncoding(pcmEncoding) .setPcmEncoding(pcmEncoding)
.setInitializationData(initializationData) .setInitializationData(initializationData)
.setDrmInitData(drmInitData) .setDrmInitData(drmInitData)
.setLanguage(language) .setLanguage(language);
.build();
if (esdsData != null) {
formatBuilder.setAverageBitrate(esdsData.bitrate).setPeakBitrate(esdsData.peakBitrate);
}
out.format = formatBuilder.build();
} }
} }
@ -1637,8 +1647,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} }
/** Returns codec-specific initialization data contained in an esds box. */ /** Returns codec-specific initialization data contained in an esds box. */
private static Pair<@NullableType String, byte @NullableType []> parseEsdsFromParent( private static EsdsData parseEsdsFromParent(ParsableByteArray parent, int position) {
ParsableByteArray parent, int position) {
parent.setPosition(position + Atom.HEADER_SIZE + 4); parent.setPosition(position + Atom.HEADER_SIZE + 4);
// Start of the ES_Descriptor (defined in ISO/IEC 14496-1) // Start of the ES_Descriptor (defined in ISO/IEC 14496-1)
parent.skipBytes(1); // ES_Descriptor tag parent.skipBytes(1); // ES_Descriptor tag
@ -1666,17 +1675,29 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
if (MimeTypes.AUDIO_MPEG.equals(mimeType) if (MimeTypes.AUDIO_MPEG.equals(mimeType)
|| MimeTypes.AUDIO_DTS.equals(mimeType) || MimeTypes.AUDIO_DTS.equals(mimeType)
|| MimeTypes.AUDIO_DTS_HD.equals(mimeType)) { || MimeTypes.AUDIO_DTS_HD.equals(mimeType)) {
return Pair.create(mimeType, null); return new EsdsData(
mimeType,
/* initializationData= */ null,
/* bitrate= */ Format.NO_VALUE,
/* peakBitrate= */ Format.NO_VALUE);
} }
parent.skipBytes(12); parent.skipBytes(4);
int peakBitrate = parent.readUnsignedIntToInt();
int bitrate = parent.readUnsignedIntToInt();
// Start of the DecoderSpecificInfo. // Start of the DecoderSpecificInfo.
parent.skipBytes(1); // DecoderSpecificInfo tag parent.skipBytes(1); // DecoderSpecificInfo tag
int initializationDataSize = parseExpandableClassSize(parent); int initializationDataSize = parseExpandableClassSize(parent);
byte[] initializationData = new byte[initializationDataSize]; byte[] initializationData = new byte[initializationDataSize];
parent.readBytes(initializationData, 0, initializationDataSize); parent.readBytes(initializationData, 0, initializationDataSize);
return Pair.create(mimeType, initializationData);
// Skipping zero values as unknown.
return new EsdsData(
mimeType,
/* initializationData= */ initializationData,
/* bitrate= */ bitrate > 0 ? bitrate : Format.NO_VALUE,
/* peakBitrate= */ peakBitrate > 0 ? peakBitrate : Format.NO_VALUE);
} }
/** /**
@ -1918,6 +1939,25 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} }
} }
/** Data parsed from an esds box. */
private static final class EsdsData {
private final @NullableType String mimeType;
private final byte @NullableType [] initializationData;
private final int bitrate;
private final int peakBitrate;
public EsdsData(
@NullableType String mimeType,
byte @NullableType [] initializationData,
int bitrate,
int peakBitrate) {
this.mimeType = mimeType;
this.initializationData = initializationData;
this.bitrate = bitrate;
this.peakBitrate = peakBitrate;
}
}
/** A box containing sample sizes (e.g. stsz, stz2). */ /** A box containing sample sizes (e.g. stsz, stz2). */
private interface SampleSizeBox { private interface SampleSizeBox {

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 9529 total output bytes = 9529
sample count = 45 sample count = 45
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 7464 total output bytes = 7464
sample count = 33 sample count = 33
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 4019 total output bytes = 4019
sample count = 18 sample count = 18
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 470 total output bytes = 470
sample count = 3 sample count = 3
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 9529 total output bytes = 9529
sample count = 45 sample count = 45
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -139,6 +139,7 @@ track 1:
total output bytes = 18257 total output bytes = 18257
sample count = 46 sample count = 46
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -139,6 +139,7 @@ track 1:
total output bytes = 18257 total output bytes = 18257
sample count = 46 sample count = 46
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -142,6 +142,8 @@ track 1:
total output bytes = 18257 total output bytes = 18257
sample count = 46 sample count = 46
format 0: format 0:
averageBitrate = 136736
peakBitrate = 145976
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -142,6 +142,8 @@ track 1:
total output bytes = 13359 total output bytes = 13359
sample count = 31 sample count = 31
format 0: format 0:
averageBitrate = 136736
peakBitrate = 145976
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -142,6 +142,8 @@ track 1:
total output bytes = 6804 total output bytes = 6804
sample count = 16 sample count = 16
format 0: format 0:
averageBitrate = 136736
peakBitrate = 145976
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -142,6 +142,8 @@ track 1:
total output bytes = 10 total output bytes = 10
sample count = 1 sample count = 1
format 0: format 0:
averageBitrate = 136736
peakBitrate = 145976
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -142,6 +142,8 @@ track 1:
total output bytes = 18257 total output bytes = 18257
sample count = 46 sample count = 46
format 0: format 0:
averageBitrate = 136736
peakBitrate = 145976
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -139,6 +139,7 @@ track 1:
total output bytes = 18257 total output bytes = 18257
sample count = 46 sample count = 46
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -139,6 +139,7 @@ track 1:
total output bytes = 18257 total output bytes = 18257
sample count = 46 sample count = 46
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 9529 total output bytes = 9529
sample count = 45 sample count = 45
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 7464 total output bytes = 7464
sample count = 33 sample count = 33
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 4019 total output bytes = 4019
sample count = 18 sample count = 18
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 470 total output bytes = 470
sample count = 3 sample count = 3
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -144,6 +144,7 @@ track 1:
total output bytes = 9529 total output bytes = 9529
sample count = 45 sample count = 45
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -139,6 +139,8 @@ track 1:
total output bytes = 10107 total output bytes = 10107
sample count = 45 sample count = 45
format 0: format 0:
averageBitrate = 1254
peakBitrate = 69000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -139,6 +139,8 @@ track 1:
total output bytes = 10107 total output bytes = 10107
sample count = 45 sample count = 45
format 0: format 0:
averageBitrate = 1254
peakBitrate = 69000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -65,6 +65,7 @@ track 1:
total output bytes = 5993 total output bytes = 5993
sample count = 15 sample count = 15
format 0: format 0:
peakBitrate = 192000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -65,6 +65,7 @@ track 1:
total output bytes = 4794 total output bytes = 4794
sample count = 11 sample count = 11
format 0: format 0:
peakBitrate = 192000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -65,6 +65,7 @@ track 1:
total output bytes = 3001 total output bytes = 3001
sample count = 7 sample count = 7
format 0: format 0:
peakBitrate = 192000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -65,6 +65,7 @@ track 1:
total output bytes = 1074 total output bytes = 1074
sample count = 3 sample count = 3
format 0: format 0:
peakBitrate = 192000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -65,6 +65,7 @@ track 1:
total output bytes = 5993 total output bytes = 5993
sample count = 15 sample count = 15
format 0: format 0:
peakBitrate = 192000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -265,6 +265,8 @@ track 1:
total output bytes = 16638 total output bytes = 16638
sample count = 44 sample count = 44
format 0: format 0:
averageBitrate = 130279
peakBitrate = 130279
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -265,6 +265,8 @@ track 1:
total output bytes = 11156 total output bytes = 11156
sample count = 30 sample count = 30
format 0: format 0:
averageBitrate = 130279
peakBitrate = 130279
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -265,6 +265,8 @@ track 1:
total output bytes = 5567 total output bytes = 5567
sample count = 15 sample count = 15
format 0: format 0:
averageBitrate = 130279
peakBitrate = 130279
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -265,6 +265,8 @@ track 1:
total output bytes = 374 total output bytes = 374
sample count = 1 sample count = 1
format 0: format 0:
averageBitrate = 130279
peakBitrate = 130279
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -265,6 +265,8 @@ track 1:
total output bytes = 16638 total output bytes = 16638
sample count = 44 sample count = 44
format 0: format 0:
averageBitrate = 130279
peakBitrate = 130279
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -1,5 +1,6 @@
containerMimeType = video/mp4 containerMimeType = video/mp4
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2

View File

@ -1,5 +1,6 @@
containerMimeType = video/mp4 containerMimeType = video/mp4
format 0: format 0:
peakBitrate = 200000
id = 2 id = 2
sampleMimeType = audio/mp4a-latm sampleMimeType = audio/mp4a-latm
codecs = mp4a.40.2 codecs = mp4a.40.2