mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Implement format to encoding for AAC
PiperOrigin-RevId: 310199693
This commit is contained in:
parent
a2ce75d836
commit
535e14cb4d
3 changed files with 110 additions and 13 deletions
|
|
@ -18,13 +18,29 @@ package com.google.android.exoplayer2.util;
|
|||
import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.audio.AacUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Defines common MIME types and helper methods.
|
||||
*/
|
||||
public final class MimeTypes {
|
||||
|
||||
/** An mp4a Object Type Indication (OTI) and its optional audio OTI is defined by RFC 6381. */
|
||||
public static final class Mp4aObjectType {
|
||||
/** The Object Type Indication of the mp4a codec. */
|
||||
public final int objectTypeIndication;
|
||||
/** The Audio Object Type Indication of the mp4a codec, or 0 if it is absent. */
|
||||
@AacUtil.AacAudioObjectType public final int audioObjectTypeIndication;
|
||||
|
||||
private Mp4aObjectType(int objectTypeIndication, int audioObjectTypeIndication) {
|
||||
this.objectTypeIndication = objectTypeIndication;
|
||||
this.audioObjectTypeIndication = audioObjectTypeIndication;
|
||||
}
|
||||
}
|
||||
|
||||
public static final String BASE_TYPE_VIDEO = "video";
|
||||
public static final String BASE_TYPE_AUDIO = "audio";
|
||||
public static final String BASE_TYPE_TEXT = "text";
|
||||
|
|
@ -106,6 +122,9 @@ public final class MimeTypes {
|
|||
|
||||
private static final ArrayList<CustomMimeType> customMimeTypes = new ArrayList<>();
|
||||
|
||||
private static final Pattern MP4A_RFC_6381_CODEC_PATTERN =
|
||||
Pattern.compile("^mp4a\\.([a-zA-Z0-9]{2})(?:\\.([0-9]{1,2}))?$");
|
||||
|
||||
/**
|
||||
* Registers a custom MIME type. Most applications do not need to call this method, as handling of
|
||||
* standard MIME types is built in. These built-in MIME types take precedence over any registered
|
||||
|
|
@ -275,15 +294,9 @@ public final class MimeTypes {
|
|||
} else if (codec.startsWith("mp4a")) {
|
||||
@Nullable String mimeType = null;
|
||||
if (codec.startsWith("mp4a.")) {
|
||||
String objectTypeString = codec.substring(5); // remove the 'mp4a.' prefix
|
||||
if (objectTypeString.length() >= 2) {
|
||||
try {
|
||||
String objectTypeHexString = Util.toUpperInvariant(objectTypeString.substring(0, 2));
|
||||
int objectTypeInt = Integer.parseInt(objectTypeHexString, 16);
|
||||
mimeType = getMimeTypeFromMp4ObjectType(objectTypeInt);
|
||||
} catch (NumberFormatException ignored) {
|
||||
// Ignored.
|
||||
}
|
||||
@Nullable Mp4aObjectType objectType = getObjectTypeFromMp4aRFC6381CodecString(codec);
|
||||
if (objectType != null) {
|
||||
mimeType = getMimeTypeFromMp4ObjectType(objectType.objectTypeIndication);
|
||||
}
|
||||
}
|
||||
return mimeType == null ? MimeTypes.AUDIO_AAC : mimeType;
|
||||
|
|
@ -407,13 +420,25 @@ public final class MimeTypes {
|
|||
* it is an encoded (non-PCM) audio format, or {@link C#ENCODING_INVALID} otherwise.
|
||||
*
|
||||
* @param mimeType The MIME type.
|
||||
* @return The {@link C}{@code .ENCODING_*} constant that corresponds to a specified MIME type, or
|
||||
* @param codecs Codecs of the format as described in RFC 6381, or null if unknown or not
|
||||
* applicable.
|
||||
* @return One of {@link C.Encoding} constants that corresponds to a specified MIME type, or
|
||||
* {@link C#ENCODING_INVALID}.
|
||||
*/
|
||||
public static @C.Encoding int getEncoding(String mimeType) {
|
||||
@C.Encoding
|
||||
public static int getEncoding(String mimeType, @Nullable String codecs) {
|
||||
switch (mimeType) {
|
||||
case MimeTypes.AUDIO_MPEG:
|
||||
return C.ENCODING_MP3;
|
||||
case MimeTypes.AUDIO_AAC:
|
||||
if (codecs == null) {
|
||||
return C.ENCODING_INVALID;
|
||||
}
|
||||
@Nullable Mp4aObjectType objectType = getObjectTypeFromMp4aRFC6381CodecString(codecs);
|
||||
if (objectType == null) {
|
||||
return C.ENCODING_INVALID;
|
||||
}
|
||||
return AacUtil.getEncodingForAudioObjectType(objectType.audioObjectTypeIndication);
|
||||
case MimeTypes.AUDIO_AC3:
|
||||
return C.ENCODING_AC3;
|
||||
case MimeTypes.AUDIO_E_AC3:
|
||||
|
|
@ -443,6 +468,40 @@ public final class MimeTypes {
|
|||
return getTrackType(getMediaMimeType(codec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object type of an mp4 audio codec from its string as defined in RFC 6381.
|
||||
*
|
||||
* <p>Per https://mp4ra.org/#/object_types and https://tools.ietf.org/html/rfc6381#section-3.3, an
|
||||
* mp4 codec string has the form: <code>
|
||||
* ~~~~~~~~~~~~~~ Object Type Indication (OTI) byte in hex
|
||||
* mp4a.[a-zA-Z0-9]{2}(.[0-9]{1,2})?
|
||||
* ~~~~~~~~~~ audio OTI, decimal. Only for certain OTI.
|
||||
* </code> For example: mp4a.40.2, has an OTI of 0x40 and an audio OTI of 2.
|
||||
*
|
||||
* @param codec The string as defined in RFC 6381 describing an mp4 audio codec.
|
||||
* @return The {@link Mp4aObjectType} or {@code null} if the input is invalid.
|
||||
*/
|
||||
@Nullable
|
||||
public static Mp4aObjectType getObjectTypeFromMp4aRFC6381CodecString(String codec) {
|
||||
Matcher matcher = MP4A_RFC_6381_CODEC_PATTERN.matcher(codec);
|
||||
if (!matcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
String objectTypeIndicationHex = Assertions.checkNotNull(matcher.group(1));
|
||||
@Nullable String audioObjectTypeIndicationDec = matcher.group(2);
|
||||
int objectTypeIndication;
|
||||
int audioObjectTypeIndication = 0;
|
||||
try {
|
||||
objectTypeIndication = Integer.parseInt(objectTypeIndicationHex, 16);
|
||||
if (audioObjectTypeIndicationDec != null) {
|
||||
audioObjectTypeIndication = Integer.parseInt(audioObjectTypeIndicationDec);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return new Mp4aObjectType(objectTypeIndication, audioObjectTypeIndication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top-level type of {@code mimeType}, or null if {@code mimeType} is null or does not
|
||||
* contain a forward slash character ({@code '/'}).
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.util;
|
|||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -133,4 +134,41 @@ public final class MimeTypesTest {
|
|||
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(0x01)).isNull();
|
||||
assertThat(MimeTypes.getMimeTypeFromMp4ObjectType(-1)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getObjectTypeFromMp4aRFC6381CodecString_onInvalidInput_returnsNull() {
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("abc")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.1")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.a")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.1g")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4v.20.9")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.100.1")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.10.")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.a.1")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.10,01")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.1f.f1")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.1a.a")).isNull();
|
||||
assertThat(MimeTypes.getObjectTypeFromMp4aRFC6381CodecString("mp4a.01.110")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getObjectTypeFromMp4aRFC6381CodecString_onValidInput_returnsCorrectObjectType() {
|
||||
assert_getObjectTypeFromMp4aRFC6381CodecString_for_returns("mp4a.00.0", 0x00, 0);
|
||||
assert_getObjectTypeFromMp4aRFC6381CodecString_for_returns("mp4a.01.01", 0x01, 1);
|
||||
assert_getObjectTypeFromMp4aRFC6381CodecString_for_returns("mp4a.10.10", 0x10, 10);
|
||||
assert_getObjectTypeFromMp4aRFC6381CodecString_for_returns("mp4a.a0.90", 0xa0, 90);
|
||||
assert_getObjectTypeFromMp4aRFC6381CodecString_for_returns("mp4a.Ff.99", 0xff, 99);
|
||||
assert_getObjectTypeFromMp4aRFC6381CodecString_for_returns("mp4a.D0.9", 0xd0, 9);
|
||||
}
|
||||
|
||||
private static void assert_getObjectTypeFromMp4aRFC6381CodecString_for_returns(
|
||||
String codec, int expectedObjectTypeIndicator, int expectedAudioObjectTypeIndicator) {
|
||||
@Nullable
|
||||
MimeTypes.Mp4aObjectType objectType = MimeTypes.getObjectTypeFromMp4aRFC6381CodecString(codec);
|
||||
assertThat(objectType).isNotNull();
|
||||
assertThat(objectType.objectTypeIndication).isEqualTo(expectedObjectTypeIndicator);
|
||||
assertThat(objectType.audioObjectTypeIndication).isEqualTo(expectedAudioObjectTypeIndicator);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -463,13 +463,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
|
|||
// E-AC3 JOC is object-based so the output channel count is arbitrary.
|
||||
if (audioSink.supportsOutput(
|
||||
/* channelCount= */ Format.NO_VALUE, format.sampleRate, C.ENCODING_E_AC3_JOC)) {
|
||||
return MimeTypes.getEncoding(MimeTypes.AUDIO_E_AC3_JOC);
|
||||
return MimeTypes.getEncoding(MimeTypes.AUDIO_E_AC3_JOC, format.codecs);
|
||||
}
|
||||
// E-AC3 receivers can decode JOC streams, but in 2-D rather than 3-D, so try to fall back.
|
||||
mimeType = MimeTypes.AUDIO_E_AC3;
|
||||
}
|
||||
|
||||
@C.Encoding int encoding = MimeTypes.getEncoding(mimeType);
|
||||
@C.Encoding int encoding = MimeTypes.getEncoding(mimeType, format.codecs);
|
||||
if (audioSink.supportsOutput(format.channelCount, format.sampleRate, encoding)) {
|
||||
return encoding;
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue