1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.util; 17 18 import android.util.Pair; 19 import androidx.annotation.Nullable; 20 import com.google.android.exoplayer2.C; 21 import java.util.ArrayList; 22 import java.util.Collections; 23 import java.util.List; 24 25 /** Provides utilities for handling various types of codec-specific data. */ 26 public final class CodecSpecificDataUtil { 27 28 private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1}; 29 30 /** 31 * Parses an ALAC AudioSpecificConfig (i.e. an <a 32 * href="https://github.com/macosforge/alac/blob/master/ALACMagicCookieDescription.txt">ALACSpecificConfig</a>). 33 * 34 * @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse. 35 * @return A pair consisting of the sample rate in Hz and the channel count. 36 */ parseAlacAudioSpecificConfig(byte[] audioSpecificConfig)37 public static Pair<Integer, Integer> parseAlacAudioSpecificConfig(byte[] audioSpecificConfig) { 38 ParsableByteArray byteArray = new ParsableByteArray(audioSpecificConfig); 39 byteArray.setPosition(9); 40 int channelCount = byteArray.readUnsignedByte(); 41 byteArray.setPosition(20); 42 int sampleRate = byteArray.readUnsignedIntToInt(); 43 return Pair.create(sampleRate, channelCount); 44 } 45 46 /** 47 * Returns initialization data for formats with MIME type {@link MimeTypes#APPLICATION_CEA708}. 48 * 49 * @param isWideAspectRatio Whether the CEA-708 closed caption service is formatted for displays 50 * with 16:9 aspect ratio. 51 * @return Initialization data for formats with MIME type {@link MimeTypes#APPLICATION_CEA708}. 52 */ buildCea708InitializationData(boolean isWideAspectRatio)53 public static List<byte[]> buildCea708InitializationData(boolean isWideAspectRatio) { 54 return Collections.singletonList(isWideAspectRatio ? new byte[] {1} : new byte[] {0}); 55 } 56 57 /** 58 * Returns whether the CEA-708 closed caption service with the given initialization data is 59 * formatted for displays with 16:9 aspect ratio. 60 * 61 * @param initializationData The initialization data to parse. 62 * @return Whether the CEA-708 closed caption service is formatted for displays with 16:9 aspect 63 * ratio. 64 */ parseCea708InitializationData(List<byte[]> initializationData)65 public static boolean parseCea708InitializationData(List<byte[]> initializationData) { 66 return initializationData.size() == 1 67 && initializationData.get(0).length == 1 68 && initializationData.get(0)[0] == 1; 69 } 70 71 /** 72 * Builds an RFC 6381 AVC codec string using the provided parameters. 73 * 74 * @param profileIdc The encoding profile. 75 * @param constraintsFlagsAndReservedZero2Bits The constraint flags followed by the reserved zero 76 * 2 bits, all contained in the least significant byte of the integer. 77 * @param levelIdc The encoding level. 78 * @return An RFC 6381 AVC codec string built using the provided parameters. 79 */ buildAvcCodecString( int profileIdc, int constraintsFlagsAndReservedZero2Bits, int levelIdc)80 public static String buildAvcCodecString( 81 int profileIdc, int constraintsFlagsAndReservedZero2Bits, int levelIdc) { 82 return String.format( 83 "avc1.%02X%02X%02X", profileIdc, constraintsFlagsAndReservedZero2Bits, levelIdc); 84 } 85 86 /** 87 * Constructs a NAL unit consisting of the NAL start code followed by the specified data. 88 * 89 * @param data An array containing the data that should follow the NAL start code. 90 * @param offset The start offset into {@code data}. 91 * @param length The number of bytes to copy from {@code data} 92 * @return The constructed NAL unit. 93 */ buildNalUnit(byte[] data, int offset, int length)94 public static byte[] buildNalUnit(byte[] data, int offset, int length) { 95 byte[] nalUnit = new byte[length + NAL_START_CODE.length]; 96 System.arraycopy(NAL_START_CODE, 0, nalUnit, 0, NAL_START_CODE.length); 97 System.arraycopy(data, offset, nalUnit, NAL_START_CODE.length, length); 98 return nalUnit; 99 } 100 101 /** 102 * Splits an array of NAL units. 103 * 104 * <p>If the input consists of NAL start code delimited units, then the returned array consists of 105 * the split NAL units, each of which is still prefixed with the NAL start code. For any other 106 * input, null is returned. 107 * 108 * @param data An array of data. 109 * @return The individual NAL units, or null if the input did not consist of NAL start code 110 * delimited units. 111 */ 112 @Nullable splitNalUnits(byte[] data)113 public static byte[][] splitNalUnits(byte[] data) { 114 if (!isNalStartCode(data, 0)) { 115 // data does not consist of NAL start code delimited units. 116 return null; 117 } 118 List<Integer> starts = new ArrayList<>(); 119 int nalUnitIndex = 0; 120 do { 121 starts.add(nalUnitIndex); 122 nalUnitIndex = findNalStartCode(data, nalUnitIndex + NAL_START_CODE.length); 123 } while (nalUnitIndex != C.INDEX_UNSET); 124 byte[][] split = new byte[starts.size()][]; 125 for (int i = 0; i < starts.size(); i++) { 126 int startIndex = starts.get(i); 127 int endIndex = i < starts.size() - 1 ? starts.get(i + 1) : data.length; 128 byte[] nal = new byte[endIndex - startIndex]; 129 System.arraycopy(data, startIndex, nal, 0, nal.length); 130 split[i] = nal; 131 } 132 return split; 133 } 134 135 /** 136 * Finds the next occurrence of the NAL start code from a given index. 137 * 138 * @param data The data in which to search. 139 * @param index The first index to test. 140 * @return The index of the first byte of the found start code, or {@link C#INDEX_UNSET}. 141 */ 142 private static int findNalStartCode(byte[] data, int index) { 143 int endIndex = data.length - NAL_START_CODE.length; 144 for (int i = index; i <= endIndex; i++) { 145 if (isNalStartCode(data, i)) { 146 return i; 147 } 148 } 149 return C.INDEX_UNSET; 150 } 151 152 /** 153 * Tests whether there exists a NAL start code at a given index. 154 * 155 * @param data The data. 156 * @param index The index to test. 157 * @return Whether there exists a start code that begins at {@code index}. 158 */ 159 private static boolean isNalStartCode(byte[] data, int index) { 160 if (data.length - index <= NAL_START_CODE.length) { 161 return false; 162 } 163 for (int j = 0; j < NAL_START_CODE.length; j++) { 164 if (data[index + j] != NAL_START_CODE[j]) { 165 return false; 166 } 167 } 168 return true; 169 } 170 171 private CodecSpecificDataUtil() {} 172 } 173