/* * 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; import android.annotation.SuppressLint; import android.media.MediaCodecInfo.CodecProfileLevel; import android.util.Pair; import java.util.ArrayList; import java.util.List; /** * Provides static utility methods for manipulating various types of codec specific data. */ public final class CodecSpecificDataUtil { private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1}; private static final int[] AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE = new int[] { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 }; private static final int[] AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE = new int[] { 0, 1, 2, 3, 4, 5, 6, 8 }; private static final int SPS_NAL_UNIT_TYPE = 7; private CodecSpecificDataUtil() {} /** * Parses an AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1 * * @param audioSpecificConfig The AudioSpecificConfig to parse. * @return A pair consisting of the sample rate in Hz and the channel count. */ public static Pair<Integer, Integer> parseAudioSpecificConfig(byte[] audioSpecificConfig) { int audioObjectType = (audioSpecificConfig[0] >> 3) & 0x1F; int byteOffset = audioObjectType == 5 || audioObjectType == 29 ? 1 : 0; int frequencyIndex = (audioSpecificConfig[byteOffset] & 0x7) << 1 | ((audioSpecificConfig[byteOffset + 1] >> 7) & 0x1); Assertions.checkState(frequencyIndex < 13); int sampleRate = AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[frequencyIndex]; int channelCount = (audioSpecificConfig[byteOffset + 1] >> 3) & 0xF; return Pair.create(sampleRate, channelCount); } /** * Builds a simple AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1 * * @param audioObjectType The audio object type. * @param sampleRateIndex The sample rate index. * @param channelConfig The channel configuration. * @return The AudioSpecificConfig. */ public static byte[] buildAudioSpecificConfig(int audioObjectType, int sampleRateIndex, int channelConfig) { byte[] audioSpecificConfig = new byte[2]; audioSpecificConfig[0] = (byte) ((audioObjectType << 3) & 0xF8 | (sampleRateIndex >> 1) & 0x07); audioSpecificConfig[1] = (byte) ((sampleRateIndex << 7) & 0x80 | (channelConfig << 3) & 0x78); return audioSpecificConfig; } /** * Builds a simple HE-AAC LC AudioSpecificConfig, as defined in ISO 14496-3 1.6.2.1 * * @param sampleRate The sample rate in Hz. * @param numChannels The number of channels. * @return The AudioSpecificConfig. */ public static byte[] buildAudioSpecificConfig(int sampleRate, int numChannels) { int sampleRateIndex = -1; for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE.length; ++i) { if (sampleRate == AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[i]) { sampleRateIndex = i; } } int channelConfig = -1; for (int i = 0; i < AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE.length; ++i) { if (numChannels == AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[i]) { channelConfig = i; } } // The full specification for AudioSpecificConfig is stated in ISO 14496-3 Section 1.6.2.1 byte[] csd = new byte[2]; csd[0] = (byte) ((2 /* AAC LC */ << 3) | (sampleRateIndex >> 1)); csd[1] = (byte) (((sampleRateIndex & 0x1) << 7) | (channelConfig << 3)); return csd; } /** * Constructs a NAL unit consisting of the NAL start code followed by the specified data. * * @param data An array containing the data that should follow the NAL start code. * @param offset The start offset into {@code data}. * @param length The number of bytes to copy from {@code data} * @return The constructed NAL unit. */ public static byte[] buildNalUnit(byte[] data, int offset, int length) { byte[] nalUnit = new byte[length + NAL_START_CODE.length]; System.arraycopy(NAL_START_CODE, 0, nalUnit, 0, NAL_START_CODE.length); System.arraycopy(data, offset, nalUnit, NAL_START_CODE.length, length); return nalUnit; } /** * Splits an array of NAL units. * <p> * If the input consists of NAL start code delimited units, then the returned array consists of * the split NAL units, each of which is still prefixed with the NAL start code. For any other * input, null is returned. * * @param data An array of data. * @return The individual NAL units, or null if the input did not consist of NAL start code * delimited units. */ public static byte[][] splitNalUnits(byte[] data) { if (!isNalStartCode(data, 0)) { // data does not consist of NAL start code delimited units. return null; } List<Integer> starts = new ArrayList<Integer>(); int nalUnitIndex = 0; do { starts.add(nalUnitIndex); nalUnitIndex = findNalStartCode(data, nalUnitIndex + NAL_START_CODE.length); } while (nalUnitIndex != -1); byte[][] split = new byte[starts.size()][]; for (int i = 0; i < starts.size(); i++) { int startIndex = starts.get(i); int endIndex = i < starts.size() - 1 ? starts.get(i + 1) : data.length; byte[] nal = new byte[endIndex - startIndex]; System.arraycopy(data, startIndex, nal, 0, nal.length); split[i] = nal; } return split; } /** * Finds the next occurrence of the NAL start code from a given index. * * @param data The data in which to search. * @param index The first index to test. * @return The index of the first byte of the found start code, or -1. */ private static int findNalStartCode(byte[] data, int index) { int endIndex = data.length - NAL_START_CODE.length; for (int i = index; i <= endIndex; i++) { if (isNalStartCode(data, i)) { return i; } } return -1; } /** * Tests whether there exists a NAL start code at a given index. * * @param data The data. * @param index The index to test. * @return Whether there exists a start code that begins at {@code index}. */ private static boolean isNalStartCode(byte[] data, int index) { if (data.length - index <= NAL_START_CODE.length) { return false; } for (int j = 0; j < NAL_START_CODE.length; j++) { if (data[index + j] != NAL_START_CODE[j]) { return false; } } return true; } /** * Parses an SPS NAL unit. * * @param spsNalUnit The NAL unit. * @return A pair consisting of AVC profile and level constants, as defined in * {@link CodecProfileLevel}. Null if the input data was not an SPS NAL unit. */ public static Pair<Integer, Integer> parseSpsNalUnit(byte[] spsNalUnit) { // SPS NAL unit: // - Start prefix (4 bytes) // - Forbidden zero bit (1 bit) // - NAL ref idx (2 bits) // - NAL unit type (5 bits) // - Profile idc (8 bits) // - Constraint bits (3 bits) // - Reserved bits (5 bits) // - Level idx (8 bits) if (isNalStartCode(spsNalUnit, 0) && spsNalUnit.length == 8 && (spsNalUnit[5] & 0x1F) == SPS_NAL_UNIT_TYPE) { return Pair.create(parseAvcProfile(spsNalUnit), parseAvcLevel(spsNalUnit)); } return null; } @SuppressLint("InlinedApi") private static int parseAvcProfile(byte[] data) { int profileIdc = data[6] & 0xFF; switch (profileIdc) { case 0x42: return CodecProfileLevel.AVCProfileBaseline; case 0x4d: return CodecProfileLevel.AVCProfileMain; case 0x58: return CodecProfileLevel.AVCProfileExtended; case 0x64: return CodecProfileLevel.AVCProfileHigh; case 0x6e: return CodecProfileLevel.AVCProfileHigh10; case 0x7a: return CodecProfileLevel.AVCProfileHigh422; case 0xf4: return CodecProfileLevel.AVCProfileHigh444; default: return 0; } } @SuppressLint("InlinedApi") private static int parseAvcLevel(byte[] data) { int levelIdc = data[8] & 0xFF; switch (levelIdc) { case 9: return CodecProfileLevel.AVCLevel1b; case 10: return CodecProfileLevel.AVCLevel1; case 11: return CodecProfileLevel.AVCLevel11; case 12: return CodecProfileLevel.AVCLevel12; case 13: return CodecProfileLevel.AVCLevel13; case 20: return CodecProfileLevel.AVCLevel2; case 21: return CodecProfileLevel.AVCLevel21; case 22: return CodecProfileLevel.AVCLevel22; case 30: return CodecProfileLevel.AVCLevel3; case 31: return CodecProfileLevel.AVCLevel31; case 32: return CodecProfileLevel.AVCLevel32; case 40: return CodecProfileLevel.AVCLevel4; case 41: return CodecProfileLevel.AVCLevel41; case 42: return CodecProfileLevel.AVCLevel42; case 50: return CodecProfileLevel.AVCLevel5; case 51: return CodecProfileLevel.AVCLevel51; default: return 0; } } }