Commit bd8ee155 by andrewlewis Committed by kim-vde

Construct codecs string for AAC in MP4/TS/FLV

PiperOrigin-RevId: 297578984
parent c6a6e0d6
...@@ -23,11 +23,26 @@ import java.util.ArrayList; ...@@ -23,11 +23,26 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
/** /** Provides utilities for handling various types of codec-specific data. */
* Provides static utility methods for manipulating various types of codec specific data.
*/
public final class CodecSpecificDataUtil { public final class CodecSpecificDataUtil {
/** Holds sample format information for AAC audio. */
public static final class AacConfig {
/** The sample rate in Hertz. */
public final int sampleRateHz;
/** The number of channels. */
public final int channelCount;
/** The RFC 6381 codecs string. */
public final String codecs;
private AacConfig(int sampleRateHz, int channelCount, String codecs) {
this.sampleRateHz = sampleRateHz;
this.channelCount = channelCount;
this.codecs = codecs;
}
}
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1}; private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
private static final int AUDIO_SPECIFIC_CONFIG_FREQUENCY_INDEX_ARBITRARY = 0xF; private static final int AUDIO_SPECIFIC_CONFIG_FREQUENCY_INDEX_ARBITRARY = 0xF;
...@@ -70,14 +85,20 @@ public final class CodecSpecificDataUtil { ...@@ -70,14 +85,20 @@ public final class CodecSpecificDataUtil {
AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID
}; };
/**
* Prefix for the RFC 6381 codecs string for AAC formats. To form a full codecs string, suffix the
* decimal AudioObjectType.
*/
private static final String AAC_CODECS_STRING_PREFIX = "mp4a.40.";
// Advanced Audio Coding Low-Complexity profile. // Advanced Audio Coding Low-Complexity profile.
private static final int AUDIO_OBJECT_TYPE_AAC_LC = 2; private static final int AUDIO_OBJECT_TYPE_AAC_LC = 2;
// Spectral Band Replication. // Spectral Band Replication.
private static final int AUDIO_OBJECT_TYPE_SBR = 5; private static final int AUDIO_OBJECT_TYPE_AAC_SBR = 5;
// Error Resilient Bit-Sliced Arithmetic Coding. // Error Resilient Bit-Sliced Arithmetic Coding.
private static final int AUDIO_OBJECT_TYPE_ER_BSAC = 22; private static final int AUDIO_OBJECT_TYPE_AAC_ER_BSAC = 22;
// Parametric Stereo. // Parametric Stereo.
private static final int AUDIO_OBJECT_TYPE_PS = 29; private static final int AUDIO_OBJECT_TYPE_AAC_PS = 29;
// Escape code for extended audio object types. // Escape code for extended audio object types.
private static final int AUDIO_OBJECT_TYPE_ESCAPE = 31; private static final int AUDIO_OBJECT_TYPE_ESCAPE = 31;
...@@ -87,12 +108,13 @@ public final class CodecSpecificDataUtil { ...@@ -87,12 +108,13 @@ public final class CodecSpecificDataUtil {
* Parses an AAC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1 * Parses an AAC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1
* *
* @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse. * @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse.
* @return A pair consisting of the sample rate in Hz and the channel count. * @return The parsed configuration.
* @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported. * @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported.
*/ */
public static Pair<Integer, Integer> parseAacAudioSpecificConfig(byte[] audioSpecificConfig) public static AacConfig parseAacAudioSpecificConfig(byte[] audioSpecificConfig)
throws ParserException { throws ParserException {
return parseAacAudioSpecificConfig(new ParsableBitArray(audioSpecificConfig), false); return parseAacAudioSpecificConfig(
new ParsableBitArray(audioSpecificConfig), /* forceReadToEnd= */ false);
} }
/** /**
...@@ -102,23 +124,25 @@ public final class CodecSpecificDataUtil { ...@@ -102,23 +124,25 @@ public final class CodecSpecificDataUtil {
* position is advanced to the end of the AudioSpecificConfig. * position is advanced to the end of the AudioSpecificConfig.
* @param forceReadToEnd Whether the entire AudioSpecificConfig should be read. Required for * @param forceReadToEnd Whether the entire AudioSpecificConfig should be read. Required for
* knowing the length of the configuration payload. * knowing the length of the configuration payload.
* @return A pair consisting of the sample rate in Hz and the channel count. * @return The parsed configuration.
* @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported. * @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported.
*/ */
public static Pair<Integer, Integer> parseAacAudioSpecificConfig( public static AacConfig parseAacAudioSpecificConfig(
ParsableBitArray bitArray, boolean forceReadToEnd) throws ParserException { ParsableBitArray bitArray, boolean forceReadToEnd) throws ParserException {
int audioObjectType = getAacAudioObjectType(bitArray); int audioObjectType = getAacAudioObjectType(bitArray);
int sampleRate = getAacSamplingFrequency(bitArray); int sampleRateHz = getAacSamplingFrequency(bitArray);
int channelConfiguration = bitArray.readBits(4); int channelConfiguration = bitArray.readBits(4);
if (audioObjectType == AUDIO_OBJECT_TYPE_SBR || audioObjectType == AUDIO_OBJECT_TYPE_PS) { String codecs = AAC_CODECS_STRING_PREFIX + audioObjectType;
if (audioObjectType == AUDIO_OBJECT_TYPE_AAC_SBR
|| audioObjectType == AUDIO_OBJECT_TYPE_AAC_PS) {
// For an AAC bitstream using spectral band replication (SBR) or parametric stereo (PS) with // For an AAC bitstream using spectral band replication (SBR) or parametric stereo (PS) with
// explicit signaling, we return the extension sampling frequency as the sample rate of the // explicit signaling, we return the extension sampling frequency as the sample rate of the
// content; this is identical to the sample rate of the decoded output but may differ from // content; this is identical to the sample rate of the decoded output but may differ from
// the sample rate set above. // the sample rate set above.
// Use the extensionSamplingFrequencyIndex. // Use the extensionSamplingFrequencyIndex.
sampleRate = getAacSamplingFrequency(bitArray); sampleRateHz = getAacSamplingFrequency(bitArray);
audioObjectType = getAacAudioObjectType(bitArray); audioObjectType = getAacAudioObjectType(bitArray);
if (audioObjectType == AUDIO_OBJECT_TYPE_ER_BSAC) { if (audioObjectType == AUDIO_OBJECT_TYPE_AAC_ER_BSAC) {
// Use the extensionChannelConfiguration. // Use the extensionChannelConfiguration.
channelConfiguration = bitArray.readBits(4); channelConfiguration = bitArray.readBits(4);
} }
...@@ -160,7 +184,7 @@ public final class CodecSpecificDataUtil { ...@@ -160,7 +184,7 @@ public final class CodecSpecificDataUtil {
// For supported containers, bits_to_decode() is always 0. // For supported containers, bits_to_decode() is always 0.
int channelCount = AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[channelConfiguration]; int channelCount = AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[channelConfiguration];
Assertions.checkArgument(channelCount != AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID); Assertions.checkArgument(channelCount != AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID);
return Pair.create(sampleRate, channelCount); return new AacConfig(sampleRateHz, channelCount, codecs);
} }
/** /**
......
...@@ -15,12 +15,12 @@ ...@@ -15,12 +15,12 @@
*/ */
package com.google.android.exoplayer2.extractor.flv; package com.google.android.exoplayer2.extractor.flv;
import android.util.Pair;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil.AacConfig;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.Collections; import java.util.Collections;
...@@ -109,11 +109,16 @@ import java.util.Collections; ...@@ -109,11 +109,16 @@ import java.util.Collections;
// Parse the sequence header. // Parse the sequence header.
byte[] audioSpecificConfig = new byte[data.bytesLeft()]; byte[] audioSpecificConfig = new byte[data.bytesLeft()];
data.readBytes(audioSpecificConfig, 0, audioSpecificConfig.length); data.readBytes(audioSpecificConfig, 0, audioSpecificConfig.length);
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig( AacConfig aacConfig =
audioSpecificConfig); CodecSpecificDataUtil.parseAacAudioSpecificConfig(audioSpecificConfig);
Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, null, Format format =
Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first, new Format.Builder()
Collections.singletonList(audioSpecificConfig), null, 0, null); .setSampleMimeType(MimeTypes.AUDIO_AAC)
.setCodecs(aacConfig.codecs)
.setChannelCount(aacConfig.channelCount)
.setSampleRate(aacConfig.sampleRateHz)
.setInitializationData(Collections.singletonList(audioSpecificConfig))
.build();
output.format(format); output.format(format);
hasOutputFormat = true; hasOutputFormat = true;
return false; return false;
......
...@@ -29,6 +29,7 @@ import com.google.android.exoplayer2.extractor.GaplessInfoHolder; ...@@ -29,6 +29,7 @@ import com.google.android.exoplayer2.extractor.GaplessInfoHolder;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil.AacConfig;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
...@@ -1086,6 +1087,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1086,6 +1087,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
int channelCount; int channelCount;
int sampleRate; int sampleRate;
@C.PcmEncoding int pcmEncoding = Format.NO_VALUE; @C.PcmEncoding int pcmEncoding = Format.NO_VALUE;
@Nullable String codecs = null;
if (quickTimeSoundDescriptionVersion == 0 || quickTimeSoundDescriptionVersion == 1) { if (quickTimeSoundDescriptionVersion == 0 || quickTimeSoundDescriptionVersion == 1) {
channelCount = parent.readUnsignedShort(); channelCount = parent.readUnsignedShort();
...@@ -1182,10 +1184,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1182,10 +1184,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
if (MimeTypes.AUDIO_AAC.equals(mimeType) && initializationData != null) { if (MimeTypes.AUDIO_AAC.equals(mimeType) && initializationData != null) {
// Update sampleRate and channelCount from the AudioSpecificConfig initialization data, // Update sampleRate and channelCount from the AudioSpecificConfig initialization data,
// which is more reliable. See [Internal: b/10903778]. // which is more reliable. See [Internal: b/10903778].
Pair<Integer, Integer> audioSpecificConfig = AacConfig aacConfig =
CodecSpecificDataUtil.parseAacAudioSpecificConfig(initializationData); CodecSpecificDataUtil.parseAacAudioSpecificConfig(initializationData);
sampleRate = audioSpecificConfig.first; sampleRate = aacConfig.sampleRateHz;
channelCount = audioSpecificConfig.second; channelCount = aacConfig.channelCount;
codecs = aacConfig.codecs;
} }
} }
} else if (childAtomType == Atom.TYPE_dac3) { } else if (childAtomType == Atom.TYPE_dac3) {
...@@ -1237,10 +1240,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; ...@@ -1237,10 +1240,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
} }
if (out.format == null && mimeType != null) { if (out.format == null && mimeType != null) {
out.format = Format.createAudioSampleFormat(Integer.toString(trackId), mimeType, null, out.format =
Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRate, pcmEncoding, new Format.Builder()
initializationData == null ? null : Collections.singletonList(initializationData), .setId(Integer.toString(trackId))
drmInitData, 0, language); .setSampleMimeType(mimeType)
.setCodecs(codecs)
.setChannelCount(channelCount)
.setSampleRate(sampleRate)
.setPcmEncoding(pcmEncoding)
.setInitializationData(
initializationData == null ? null : Collections.singletonList(initializationData))
.setDrmInitData(drmInitData)
.setLanguage(language)
.build();
} }
} }
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.util.Pair;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -26,6 +25,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput; ...@@ -26,6 +25,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil.AacConfig;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
...@@ -469,12 +469,17 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -469,12 +469,17 @@ public final class AdtsReader implements ElementaryStreamReader {
byte[] audioSpecificConfig = byte[] audioSpecificConfig =
CodecSpecificDataUtil.buildAacAudioSpecificConfig( CodecSpecificDataUtil.buildAacAudioSpecificConfig(
audioObjectType, firstFrameSampleRateIndex, channelConfig); audioObjectType, firstFrameSampleRateIndex, channelConfig);
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig( AacConfig aacConfig = CodecSpecificDataUtil.parseAacAudioSpecificConfig(audioSpecificConfig);
audioSpecificConfig); Format format =
new Format.Builder()
Format format = Format.createAudioSampleFormat(formatId, MimeTypes.AUDIO_AAC, null, .setId(formatId)
Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first, .setSampleMimeType(MimeTypes.AUDIO_AAC)
Collections.singletonList(audioSpecificConfig), null, 0, language); .setCodecs(aacConfig.codecs)
.setChannelCount(aacConfig.channelCount)
.setSampleRate(aacConfig.sampleRateHz)
.setInitializationData(Collections.singletonList(audioSpecificConfig))
.setLanguage(language)
.build();
// In this class a sample is an access unit, but the MediaFormat sample rate specifies the // In this class a sample is an access unit, but the MediaFormat sample rate specifies the
// number of PCM audio samples per second. // number of PCM audio samples per second.
sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate; sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate;
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.util.Pair;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -25,6 +24,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput; ...@@ -25,6 +24,7 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil.AacConfig;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
...@@ -273,9 +273,10 @@ public final class LatmReader implements ElementaryStreamReader { ...@@ -273,9 +273,10 @@ public final class LatmReader implements ElementaryStreamReader {
private int parseAudioSpecificConfig(ParsableBitArray data) throws ParserException { private int parseAudioSpecificConfig(ParsableBitArray data) throws ParserException {
int bitsLeft = data.bitsLeft(); int bitsLeft = data.bitsLeft();
Pair<Integer, Integer> config = CodecSpecificDataUtil.parseAacAudioSpecificConfig(data, true); AacConfig config =
sampleRateHz = config.first; CodecSpecificDataUtil.parseAacAudioSpecificConfig(data, /* forceReadToEnd= */ true);
channelCount = config.second; sampleRateHz = config.sampleRateHz;
channelCount = config.channelCount;
return bitsLeft - data.bitsLeft(); return bitsLeft - data.bitsLeft();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment