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.extractor.flv; 17 18 import android.util.Pair; 19 import com.google.android.exoplayer2.C; 20 import com.google.android.exoplayer2.Format; 21 import com.google.android.exoplayer2.extractor.TrackOutput; 22 import com.google.android.exoplayer2.util.CodecSpecificDataUtil; 23 import com.google.android.exoplayer2.util.MimeTypes; 24 import com.google.android.exoplayer2.util.ParsableByteArray; 25 import java.util.Collections; 26 27 /** 28 * Parses audio tags from an FLV stream and extracts AAC frames. 29 */ 30 /* package */ final class AudioTagPayloadReader extends TagPayloadReader { 31 32 private static final int AUDIO_FORMAT_MP3 = 2; 33 private static final int AUDIO_FORMAT_ALAW = 7; 34 private static final int AUDIO_FORMAT_ULAW = 8; 35 private static final int AUDIO_FORMAT_AAC = 10; 36 37 private static final int AAC_PACKET_TYPE_SEQUENCE_HEADER = 0; 38 private static final int AAC_PACKET_TYPE_AAC_RAW = 1; 39 40 private static final int[] AUDIO_SAMPLING_RATE_TABLE = new int[] {5512, 11025, 22050, 44100}; 41 42 // State variables 43 private boolean hasParsedAudioDataHeader; 44 private boolean hasOutputFormat; 45 private int audioFormat; 46 AudioTagPayloadReader(TrackOutput output)47 public AudioTagPayloadReader(TrackOutput output) { 48 super(output); 49 } 50 51 @Override seek()52 public void seek() { 53 // Do nothing. 54 } 55 56 @Override parseHeader(ParsableByteArray data)57 protected boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException { 58 if (!hasParsedAudioDataHeader) { 59 int header = data.readUnsignedByte(); 60 audioFormat = (header >> 4) & 0x0F; 61 if (audioFormat == AUDIO_FORMAT_MP3) { 62 int sampleRateIndex = (header >> 2) & 0x03; 63 int sampleRate = AUDIO_SAMPLING_RATE_TABLE[sampleRateIndex]; 64 Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_MPEG, null, 65 Format.NO_VALUE, Format.NO_VALUE, 1, sampleRate, null, null, 0, null); 66 output.format(format); 67 hasOutputFormat = true; 68 } else if (audioFormat == AUDIO_FORMAT_ALAW || audioFormat == AUDIO_FORMAT_ULAW) { 69 String type = audioFormat == AUDIO_FORMAT_ALAW ? MimeTypes.AUDIO_ALAW 70 : MimeTypes.AUDIO_ULAW; 71 int pcmEncoding = (header & 0x01) == 1 ? C.ENCODING_PCM_16BIT : C.ENCODING_PCM_8BIT; 72 Format format = Format.createAudioSampleFormat(null, type, null, Format.NO_VALUE, 73 Format.NO_VALUE, 1, 8000, pcmEncoding, null, null, 0, null); 74 output.format(format); 75 hasOutputFormat = true; 76 } else if (audioFormat != AUDIO_FORMAT_AAC) { 77 throw new UnsupportedFormatException("Audio format not supported: " + audioFormat); 78 } 79 hasParsedAudioDataHeader = true; 80 } else { 81 // Skip header if it was parsed previously. 82 data.skipBytes(1); 83 } 84 return true; 85 } 86 87 @Override parsePayload(ParsableByteArray data, long timeUs)88 protected void parsePayload(ParsableByteArray data, long timeUs) { 89 if (audioFormat == AUDIO_FORMAT_MP3) { 90 int sampleSize = data.bytesLeft(); 91 output.sampleData(data, sampleSize); 92 output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null); 93 } else { 94 int packetType = data.readUnsignedByte(); 95 if (packetType == AAC_PACKET_TYPE_SEQUENCE_HEADER && !hasOutputFormat) { 96 // Parse the sequence header. 97 byte[] audioSpecificConfig = new byte[data.bytesLeft()]; 98 data.readBytes(audioSpecificConfig, 0, audioSpecificConfig.length); 99 Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig( 100 audioSpecificConfig); 101 Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, null, 102 Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first, 103 Collections.singletonList(audioSpecificConfig), null, 0, null); 104 output.format(format); 105 hasOutputFormat = true; 106 } else if (audioFormat != AUDIO_FORMAT_AAC || packetType == AAC_PACKET_TYPE_AAC_RAW) { 107 int sampleSize = data.bytesLeft(); 108 output.sampleData(data, sampleSize); 109 output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null); 110 } 111 } 112 } 113 114 } 115