/* * Copyright (C) 2016 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 org.mozilla.thirdparty.com.google.android.exoplayer2.extractor; import androidx.annotation.Nullable; import org.mozilla.thirdparty.com.google.android.exoplayer2.C; import org.mozilla.thirdparty.com.google.android.exoplayer2.util.MimeTypes; /** * 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 = { 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000 }; private static final int[] BITRATE_V2_L1 = { 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000 }; private static final int[] BITRATE_V1_L2 = { 32000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000 }; private static final int[] BITRATE_V1_L3 = { 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000 }; private static final int[] BITRATE_V2 = { 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 144000, 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 (!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; } int bitrateIndex = (header >>> 12) & 15; if (bitrateIndex == 0 || bitrateIndex == 0xF) { // Disallow "free" bitrate. return C.LENGTH_UNSET; } int samplingRateIndex = (header >>> 10) & 3; if (samplingRateIndex == 3) { return C.LENGTH_UNSET; } 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 (12 * 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 144 * bitrate / samplingRate + padding; } else { // Version 2 or 2.5 return (layer == 1 ? 72 : 144) * bitrate / samplingRate + padding; } } /** * 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. * * @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 (!isMagicPresent(headerData)) { 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; int frameSize; 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; } 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]; frameSize = 144 * bitrate / sampleRate + padding; } else { // Version 2 or 2.5. bitrate = BITRATE_V2[bitrateIndex - 1]; frameSize = (layer == 1 ? 72 : 144) * 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; } 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. */ @Nullable 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 bit/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; } }