diff --git a/library/core/src/main/java/com/google/android/exoplayer2/C.java b/library/core/src/main/java/com/google/android/exoplayer2/C.java index 9661b7d072..e431b2d899 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/C.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/C.java @@ -151,9 +151,9 @@ public final class C { * Represents an audio encoding, or an invalid or unset value. One of {@link Format#NO_VALUE}, * {@link #ENCODING_INVALID}, {@link #ENCODING_PCM_8BIT}, {@link #ENCODING_PCM_16BIT}, {@link * #ENCODING_PCM_24BIT}, {@link #ENCODING_PCM_32BIT}, {@link #ENCODING_PCM_FLOAT}, {@link - * #ENCODING_PCM_MU_LAW}, {@link #ENCODING_PCM_A_LAW}, {@link #ENCODING_AC3}, {@link - * #ENCODING_E_AC3}, {@link #ENCODING_E_AC3_JOC}, {@link #ENCODING_AC4}, {@link #ENCODING_DTS}, - * {@link #ENCODING_DTS_HD} or {@link #ENCODING_DOLBY_TRUEHD}. + * #ENCODING_PCM_MU_LAW}, {@link #ENCODING_PCM_A_LAW}, {@link #ENCODING_MP3}, {@link + * #ENCODING_AC3}, {@link #ENCODING_E_AC3}, {@link #ENCODING_E_AC3_JOC}, {@link #ENCODING_AC4}, + * {@link #ENCODING_DTS}, {@link #ENCODING_DTS_HD} or {@link #ENCODING_DOLBY_TRUEHD}. */ @Documented @Retention(RetentionPolicy.SOURCE) @@ -167,6 +167,7 @@ public final class C { ENCODING_PCM_FLOAT, ENCODING_PCM_MU_LAW, ENCODING_PCM_A_LAW, + ENCODING_MP3, ENCODING_AC3, ENCODING_E_AC3, ENCODING_E_AC3_JOC, @@ -213,6 +214,8 @@ public final class C { public static final int ENCODING_PCM_MU_LAW = 0x10000000; /** Audio encoding for A-law. */ public static final int ENCODING_PCM_A_LAW = 0x20000000; + /** @see AudioFormat#ENCODING_MP3 */ + public static final int ENCODING_MP3 = AudioFormat.ENCODING_MP3; /** @see AudioFormat#ENCODING_AC3 */ public static final int ENCODING_AC3 = AudioFormat.ENCODING_AC3; /** @see AudioFormat#ENCODING_E_AC3 */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index 27823e3006..240a8554b7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -28,6 +28,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.audio.AudioProcessor.UnhandledAudioFormatException; +import com.google.android.exoplayer2.extractor.MpegAudioHeader; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; @@ -1158,22 +1159,27 @@ public final class DefaultAudioSink implements AudioSink { } private static int getFramesPerEncodedSample(@C.Encoding int encoding, ByteBuffer buffer) { - if (encoding == C.ENCODING_DTS || encoding == C.ENCODING_DTS_HD) { - return DtsUtil.parseDtsAudioSampleCount(buffer); - } else if (encoding == C.ENCODING_AC3) { - return Ac3Util.getAc3SyncframeAudioSampleCount(); - } else if (encoding == C.ENCODING_E_AC3 || encoding == C.ENCODING_E_AC3_JOC) { - return Ac3Util.parseEAc3SyncframeAudioSampleCount(buffer); - } else if (encoding == C.ENCODING_AC4) { - return Ac4Util.parseAc4SyncframeAudioSampleCount(buffer); - } else if (encoding == C.ENCODING_DOLBY_TRUEHD) { - int syncframeOffset = Ac3Util.findTrueHdSyncframeOffset(buffer); - return syncframeOffset == C.INDEX_UNSET - ? 0 - : (Ac3Util.parseTrueHdSyncframeAudioSampleCount(buffer, syncframeOffset) - * Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT); - } else { - throw new IllegalStateException("Unexpected audio encoding: " + encoding); + switch (encoding) { + case C.ENCODING_MP3: + return MpegAudioHeader.getFrameSampleCount(buffer.get(buffer.position())); + case C.ENCODING_DTS: + case C.ENCODING_DTS_HD: + return DtsUtil.parseDtsAudioSampleCount(buffer); + case C.ENCODING_AC3: + return Ac3Util.getAc3SyncframeAudioSampleCount(); + case C.ENCODING_E_AC3: + case C.ENCODING_E_AC3_JOC: + return Ac3Util.parseEAc3SyncframeAudioSampleCount(buffer); + case C.ENCODING_AC4: + return Ac4Util.parseAc4SyncframeAudioSampleCount(buffer); + case C.ENCODING_DOLBY_TRUEHD: + int syncframeOffset = Ac3Util.findTrueHdSyncframeOffset(buffer); + return syncframeOffset == C.INDEX_UNSET + ? 0 + : (Ac3Util.parseTrueHdSyncframeAudioSampleCount(buffer, syncframeOffset) + * Ac3Util.TRUEHD_RECHUNK_SAMPLE_COUNT); + default: + throw new IllegalStateException("Unexpected audio encoding: " + encoding); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/MpegAudioHeader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/MpegAudioHeader.java index 04d85b8bc5..b3155233d0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/MpegAudioHeader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/MpegAudioHeader.java @@ -56,12 +56,17 @@ public final class MpegAudioHeader { 160000 }; + private static final int SAMPLES_PER_FRAME_L1 = 384; + private static final int SAMPLES_PER_FRAME_L2 = 1152; + private static final int SAMPLES_PER_FRAME_L3_V1 = 1152; + private static final int SAMPLES_PER_FRAME_L3_V2 = 576; + /** * Returns the size of the frame associated with {@code header}, or {@link C#LENGTH_UNSET} if it * is invalid. */ public static int getFrameSize(int header) { - if ((header & 0xFFE00000) != 0xFFE00000) { + if (!isMagicPresent(header)) { return C.LENGTH_UNSET; } @@ -120,6 +125,36 @@ public final class MpegAudioHeader { } } + /** + * Returns the number of samples per frame associated with {@code header}, or {@link + * C#LENGTH_UNSET} if it is invalid. + */ + public static int getFrameSampleCount(int header) { + + if (!isMagicPresent(header)) { + return C.LENGTH_UNSET; + } + + int version = (header >>> 19) & 3; + if (version == 1) { + return C.LENGTH_UNSET; + } + + int layer = (header >>> 17) & 3; + if (layer == 0) { + return C.LENGTH_UNSET; + } + + // Those header values are not used but are checked for consistency with the other methods + int bitrateIndex = (header >>> 12) & 15; + int samplingRateIndex = (header >>> 10) & 3; + if (bitrateIndex == 0 || bitrateIndex == 0xF || samplingRateIndex == 3) { + return C.LENGTH_UNSET; + } + + return getFrameSizeInSamples(version, layer); + } + /** * Parses {@code headerData}, populating {@code header} with the parsed data. * @@ -129,7 +164,7 @@ public final class MpegAudioHeader { * is not a valid MPEG audio header. */ public static boolean populateHeader(int headerData, MpegAudioHeader header) { - if ((headerData & 0xFFE00000) != 0xFFE00000) { + if (!isMagicPresent(headerData)) { return false; } @@ -166,23 +201,20 @@ public final class MpegAudioHeader { int padding = (headerData >>> 9) & 1; int bitrate; int frameSize; - int samplesPerFrame; + int samplesPerFrame = getFrameSizeInSamples(version, layer); if (layer == 3) { // Layer I (layer == 3) bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1]; frameSize = (12 * bitrate / sampleRate + padding) * 4; - samplesPerFrame = 384; } else { // Layer II (layer == 2) or III (layer == 1) if (version == 3) { // Version 1 bitrate = layer == 2 ? BITRATE_V1_L2[bitrateIndex - 1] : BITRATE_V1_L3[bitrateIndex - 1]; - samplesPerFrame = 1152; frameSize = 144 * bitrate / sampleRate + padding; } else { // Version 2 or 2.5. bitrate = BITRATE_V2[bitrateIndex - 1]; - samplesPerFrame = layer == 1 ? 576 : 1152; frameSize = (layer == 1 ? 72 : 144) * bitrate / sampleRate + padding; } } @@ -193,6 +225,22 @@ public final class MpegAudioHeader { return true; } + private static boolean isMagicPresent(int header) { + return (header & 0xFFE00000) == 0xFFE00000; + } + + private static int getFrameSizeInSamples(int version, int layer) { + switch (layer) { + case 1: + return version == 3 ? SAMPLES_PER_FRAME_L3_V1 : SAMPLES_PER_FRAME_L3_V2; // Layer III + case 2: + return SAMPLES_PER_FRAME_L2; // Layer II + case 3: + return SAMPLES_PER_FRAME_L1; // Layer I + } + throw new IllegalArgumentException(); + } + /** MPEG audio header version. */ public int version; /** The mime type. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index de30cfd214..803ef6f41d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -345,6 +345,8 @@ public final class MimeTypes { */ public static @C.Encoding int getEncoding(String mimeType) { switch (mimeType) { + case MimeTypes.AUDIO_MPEG: + return C.ENCODING_MP3; case MimeTypes.AUDIO_AC3: return C.ENCODING_AC3; case MimeTypes.AUDIO_E_AC3: