blob: 0ca65e4de539e0a4865a51047c5a3409e8e35f35 [file] [log] [blame]
/*
* 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 com.google.android.exoplayer2.extractor.flv;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.audio.AacUtil;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.Collections;
/**
* Parses audio tags from an FLV stream and extracts AAC frames.
*/
/* package */ final class AudioTagPayloadReader extends TagPayloadReader {
private static final int AUDIO_FORMAT_MP3 = 2;
private static final int AUDIO_FORMAT_ALAW = 7;
private static final int AUDIO_FORMAT_ULAW = 8;
private static final int AUDIO_FORMAT_AAC = 10;
private static final int AAC_PACKET_TYPE_SEQUENCE_HEADER = 0;
private static final int AAC_PACKET_TYPE_AAC_RAW = 1;
private static final int[] AUDIO_SAMPLING_RATE_TABLE = new int[] {5512, 11025, 22050, 44100};
// State variables
private boolean hasParsedAudioDataHeader;
private boolean hasOutputFormat;
private int audioFormat;
public AudioTagPayloadReader(TrackOutput output) {
super(output);
}
@Override
public void seek() {
// Do nothing.
}
@Override
protected boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException {
if (!hasParsedAudioDataHeader) {
int header = data.readUnsignedByte();
audioFormat = (header >> 4) & 0x0F;
if (audioFormat == AUDIO_FORMAT_MP3) {
int sampleRateIndex = (header >> 2) & 0x03;
int sampleRate = AUDIO_SAMPLING_RATE_TABLE[sampleRateIndex];
Format format =
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_MPEG)
.setChannelCount(1)
.setSampleRate(sampleRate)
.build();
output.format(format);
hasOutputFormat = true;
} else if (audioFormat == AUDIO_FORMAT_ALAW || audioFormat == AUDIO_FORMAT_ULAW) {
String mimeType =
audioFormat == AUDIO_FORMAT_ALAW ? MimeTypes.AUDIO_ALAW : MimeTypes.AUDIO_MLAW;
Format format =
new Format.Builder()
.setSampleMimeType(mimeType)
.setChannelCount(1)
.setSampleRate(8000)
.build();
output.format(format);
hasOutputFormat = true;
} else if (audioFormat != AUDIO_FORMAT_AAC) {
throw new UnsupportedFormatException("Audio format not supported: " + audioFormat);
}
hasParsedAudioDataHeader = true;
} else {
// Skip header if it was parsed previously.
data.skipBytes(1);
}
return true;
}
@Override
protected boolean parsePayload(ParsableByteArray data, long timeUs) throws ParserException {
if (audioFormat == AUDIO_FORMAT_MP3) {
int sampleSize = data.bytesLeft();
output.sampleData(data, sampleSize);
output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
return true;
} else {
int packetType = data.readUnsignedByte();
if (packetType == AAC_PACKET_TYPE_SEQUENCE_HEADER && !hasOutputFormat) {
// Parse the sequence header.
byte[] audioSpecificConfig = new byte[data.bytesLeft()];
data.readBytes(audioSpecificConfig, 0, audioSpecificConfig.length);
AacUtil.Config aacConfig = AacUtil.parseAudioSpecificConfig(audioSpecificConfig);
Format format =
new Format.Builder()
.setSampleMimeType(MimeTypes.AUDIO_AAC)
.setCodecs(aacConfig.codecs)
.setChannelCount(aacConfig.channelCount)
.setSampleRate(aacConfig.sampleRateHz)
.setInitializationData(Collections.singletonList(audioSpecificConfig))
.build();
output.format(format);
hasOutputFormat = true;
return false;
} else if (audioFormat != AUDIO_FORMAT_AAC || packetType == AAC_PACKET_TYPE_AAC_RAW) {
int sampleSize = data.bytesLeft();
output.sampleData(data, sampleSize);
output.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
return true;
} else {
return false;
}
}
}
}