/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.util;
/**
* Representation of an MPEG audio frame header.
*/
public final class MpegAudioHeader {
/**
* Theoretical maximum frame size for an MPEG audio stream, which occurs when playing a Layer 2
* MPEG 2.5 audio stream at 16 kb/s (with padding). The size is 1152 sample/frame *
* 160000 bit/s / (8000 sample/s * 8 bit/byte) + 1 padding byte/frame = 2881 byte/frame.
* The next power of two size is 4 KiB.
*/
public static final int MAX_FRAME_SIZE_BYTES = 4096;
private static final String[] MIME_TYPE_BY_LAYER =
new String[] {MimeTypes.AUDIO_MPEG_L1, MimeTypes.AUDIO_MPEG_L2, MimeTypes.AUDIO_MPEG};
private static final int[] SAMPLING_RATE_V1 = {44100, 48000, 32000};
private static final int[] BITRATE_V1_L1 =
{32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448};
private static final int[] BITRATE_V2_L1 =
{32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256};
private static final int[] BITRATE_V1_L2 =
{32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384};
private static final int[] BITRATE_V1_L3 =
{32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320};
private static final int[] BITRATE_V2 =
{8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160};
/**
* Returns the size of the frame associated with {@code header}, or -1 if it is invalid.
*/
public static int getFrameSize(int header) {
if ((header & 0xFFE00000) != 0xFFE00000) {
return -1;
}
int version = (header >>> 19) & 3;
if (version == 1) {
return -1;
}
int layer = (header >>> 17) & 3;
if (layer == 0) {
return -1;
}
int bitrateIndex = (header >>> 12) & 15;
if (bitrateIndex == 0 || bitrateIndex == 0xF) {
// Disallow "free" bitrate.
return -1;
}
int samplingRateIndex = (header >>> 10) & 3;
if (samplingRateIndex == 3) {
return -1;
}
int samplingRate = SAMPLING_RATE_V1[samplingRateIndex];
if (version == 2) {
// Version 2
samplingRate /= 2;
} else if (version == 0) {
// Version 2.5
samplingRate /= 4;
}
int bitrate;
int padding = (header >>> 9) & 1;
if (layer == 3) {
// Layer I (layer == 3)
bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1];
return (12000 * bitrate / samplingRate + padding) * 4;
} else {
// Layer II (layer == 2) or III (layer == 1)
if (version == 3) {
bitrate = layer == 2 ? BITRATE_V1_L2[bitrateIndex - 1] : BITRATE_V1_L3[bitrateIndex - 1];
} else {
// Version 2 or 2.5.
bitrate = BITRATE_V2[bitrateIndex - 1];
}
}
if (version == 3) {
// Version 1
return 144000 * bitrate / samplingRate + padding;
} else {
// Version 2 or 2.5
return (layer == 1 ? 72000 : 144000) * bitrate / samplingRate + padding;
}
}
/**
* Parses {@code headerData}, populating {@code header} with the parsed data.
*
* @param headerData Header data to parse.
* @param header Header to populate with data from {@code headerData}.
* @return True if the header was populated. False otherwise, indicating that {@code headerData}
* is not a valid MPEG audio header.
*/
public static boolean populateHeader(int headerData, MpegAudioHeader header) {
if ((headerData & 0xFFE00000) != 0xFFE00000) {
return false;
}
int version = (headerData >>> 19) & 3;
if (version == 1) {
return false;
}
int layer = (headerData >>> 17) & 3;
if (layer == 0) {
return false;
}
int bitrateIndex = (headerData >>> 12) & 15;
if (bitrateIndex == 0 || bitrateIndex == 0xF) {
// Disallow "free" bitrate.
return false;
}
int samplingRateIndex = (headerData >>> 10) & 3;
if (samplingRateIndex == 3) {
return false;
}
int sampleRate = SAMPLING_RATE_V1[samplingRateIndex];
if (version == 2) {
// Version 2
sampleRate /= 2;
} else if (version == 0) {
// Version 2.5
sampleRate /= 4;
}
int padding = (headerData >>> 9) & 1;
int bitrate, frameSize, samplesPerFrame;
if (layer == 3) {
// Layer I (layer == 3)
bitrate = version == 3 ? BITRATE_V1_L1[bitrateIndex - 1] : BITRATE_V2_L1[bitrateIndex - 1];
frameSize = (12000 * 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 = 144000 * bitrate / sampleRate + padding;
} else {
// Version 2 or 2.5.
bitrate = BITRATE_V2[bitrateIndex - 1];
samplesPerFrame = layer == 1 ? 576 : 1152;
frameSize = (layer == 1 ? 72000 : 144000) * bitrate / sampleRate + padding;
}
}
String mimeType = MIME_TYPE_BY_LAYER[3 - layer];
int channels = ((headerData >> 6) & 3) == 3 ? 1 : 2;
header.setValues(version, mimeType, frameSize, sampleRate, channels, bitrate, samplesPerFrame);
return true;
}
/** MPEG audio header version. */
public int version;
/** The mime type. */
public String mimeType;
/** Size of the frame associated with this header, in bytes. */
public int frameSize;
/** Sample rate in samples per second. */
public int sampleRate;
/** Number of audio channels in the frame. */
public int channels;
/** Bitrate of the frame in kbit/s. */
public int bitrate;
/** Number of samples stored in the frame. */
public int samplesPerFrame;
private void setValues(int version, String mimeType, int frameSize,
int sampleRate, int channels, int bitrate, int samplesPerFrame) {
this.version = version;
this.mimeType = mimeType;
this.frameSize = frameSize;
this.sampleRate = sampleRate;
this.channels = channels;
this.bitrate = bitrate;
this.samplesPerFrame = samplesPerFrame;
}
}