mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Parse MP3 header to retrieve the nb of sample per frames
Add support for MP3 as an encoding format for passthrough. This change does not change the observable behavior of Exoplayer. Also name the magics. #exo-offload PiperOrigin-RevId: 286146539
This commit is contained in:
parent
3c56b113e4
commit
c111138ac2
@ -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 */
|
||||
|
@ -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,21 +1159,26 @@ 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) {
|
||||
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);
|
||||
} else if (encoding == C.ENCODING_AC3) {
|
||||
case C.ENCODING_AC3:
|
||||
return Ac3Util.getAc3SyncframeAudioSampleCount();
|
||||
} else if (encoding == C.ENCODING_E_AC3 || encoding == C.ENCODING_E_AC3_JOC) {
|
||||
case C.ENCODING_E_AC3:
|
||||
case C.ENCODING_E_AC3_JOC:
|
||||
return Ac3Util.parseEAc3SyncframeAudioSampleCount(buffer);
|
||||
} else if (encoding == C.ENCODING_AC4) {
|
||||
case C.ENCODING_AC4:
|
||||
return Ac4Util.parseAc4SyncframeAudioSampleCount(buffer);
|
||||
} else if (encoding == C.ENCODING_DOLBY_TRUEHD) {
|
||||
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);
|
||||
} else {
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected audio encoding: " + encoding);
|
||||
}
|
||||
}
|
||||
|
@ -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. */
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user