Add support for playing DTS in HLS.
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=112774678
This commit is contained in:
parent
a02fd14c31
commit
a9720457d8
@ -18,6 +18,7 @@ package com.google.android.exoplayer.audio;
|
|||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.util.Ac3Util;
|
import com.google.android.exoplayer.util.Ac3Util;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
|
import com.google.android.exoplayer.util.DtsUtil;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
@ -965,10 +966,7 @@ public final class AudioTrack {
|
|||||||
|
|
||||||
private static int getFramesPerEncodedSample(int encoding, ByteBuffer buffer) {
|
private static int getFramesPerEncodedSample(int encoding, ByteBuffer buffer) {
|
||||||
if (encoding == C.ENCODING_DTS || encoding == C.ENCODING_DTS_HD) {
|
if (encoding == C.ENCODING_DTS || encoding == C.ENCODING_DTS_HD) {
|
||||||
// Calculate the sample size in frames as per ETSI TS 102 114 F.3.2.1.
|
return DtsUtil.parseDtsAudioSampleCount(buffer);
|
||||||
int nblks = ((buffer.get(buffer.position() + 4) & 0x01) << 6)
|
|
||||||
| ((buffer.get(buffer.position() + 5) & 0xFC) >> 2);
|
|
||||||
return (nblks + 1) * 32;
|
|
||||||
} else if (encoding == C.ENCODING_AC3) {
|
} else if (encoding == C.ENCODING_AC3) {
|
||||||
return Ac3Util.getAc3SyncframeAudioSampleCount();
|
return Ac3Util.getAc3SyncframeAudioSampleCount();
|
||||||
} else if (encoding == C.ENCODING_E_AC3) {
|
} else if (encoding == C.ENCODING_E_AC3) {
|
||||||
|
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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.extractor.ts;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.C;
|
||||||
|
import com.google.android.exoplayer.MediaFormat;
|
||||||
|
import com.google.android.exoplayer.extractor.TrackOutput;
|
||||||
|
import com.google.android.exoplayer.util.DtsUtil;
|
||||||
|
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a continuous DTS byte stream and extracts individual samples.
|
||||||
|
*/
|
||||||
|
/* package */ final class DtsReader extends ElementaryStreamReader {
|
||||||
|
|
||||||
|
private static final int STATE_FINDING_SYNC = 0;
|
||||||
|
private static final int STATE_READING_HEADER = 1;
|
||||||
|
private static final int STATE_READING_SAMPLE = 2;
|
||||||
|
|
||||||
|
private static final int HEADER_SIZE = 15;
|
||||||
|
private static final int SYNC_VALUE = 0x7FFE8001;
|
||||||
|
private static final int SYNC_VALUE_SIZE = 4;
|
||||||
|
|
||||||
|
private final ParsableByteArray headerScratchBytes;
|
||||||
|
|
||||||
|
private int state;
|
||||||
|
private int bytesRead;
|
||||||
|
|
||||||
|
// Used to find the header.
|
||||||
|
private int syncBytes;
|
||||||
|
|
||||||
|
// Used when parsing the header.
|
||||||
|
private long sampleDurationUs;
|
||||||
|
private MediaFormat mediaFormat;
|
||||||
|
private int sampleSize;
|
||||||
|
|
||||||
|
// Used when reading the samples.
|
||||||
|
private long timeUs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new reader for DTS elementary streams.
|
||||||
|
*
|
||||||
|
* @param output Track output for extracted samples.
|
||||||
|
*/
|
||||||
|
public DtsReader(TrackOutput output) {
|
||||||
|
super(output);
|
||||||
|
headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]);
|
||||||
|
headerScratchBytes.data[0] = (byte) ((SYNC_VALUE >> 24) & 0xFF);
|
||||||
|
headerScratchBytes.data[1] = (byte) ((SYNC_VALUE >> 16) & 0xFF);
|
||||||
|
headerScratchBytes.data[2] = (byte) ((SYNC_VALUE >> 8) & 0xFF);
|
||||||
|
headerScratchBytes.data[3] = (byte) (SYNC_VALUE & 0xFF);
|
||||||
|
state = STATE_FINDING_SYNC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void seek() {
|
||||||
|
state = STATE_FINDING_SYNC;
|
||||||
|
bytesRead = 0;
|
||||||
|
syncBytes = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void consume(ParsableByteArray data, long pesTimeUs, boolean startOfPacket) {
|
||||||
|
if (startOfPacket) {
|
||||||
|
timeUs = pesTimeUs;
|
||||||
|
}
|
||||||
|
while (data.bytesLeft() > 0) {
|
||||||
|
switch (state) {
|
||||||
|
case STATE_FINDING_SYNC:
|
||||||
|
if (skipToNextSync(data)) {
|
||||||
|
bytesRead = SYNC_VALUE_SIZE;
|
||||||
|
state = STATE_READING_HEADER;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_READING_HEADER:
|
||||||
|
if (continueRead(data, headerScratchBytes.data, HEADER_SIZE)) {
|
||||||
|
parseHeader();
|
||||||
|
headerScratchBytes.setPosition(0);
|
||||||
|
output.sampleData(headerScratchBytes, HEADER_SIZE);
|
||||||
|
state = STATE_READING_SAMPLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_READING_SAMPLE:
|
||||||
|
int bytesToRead = Math.min(data.bytesLeft(), sampleSize - bytesRead);
|
||||||
|
output.sampleData(data, bytesToRead);
|
||||||
|
bytesRead += bytesToRead;
|
||||||
|
if (bytesRead == sampleSize) {
|
||||||
|
output.sampleMetadata(timeUs, C.SAMPLE_FLAG_SYNC, sampleSize, 0, null);
|
||||||
|
timeUs += sampleDurationUs;
|
||||||
|
state = STATE_FINDING_SYNC;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void packetFinished() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Continues a read from the provided {@code source} into a given {@code target}. It's assumed
|
||||||
|
* that the data should be written into {@code target} starting from an offset of zero.
|
||||||
|
*
|
||||||
|
* @param source The source from which to read.
|
||||||
|
* @param target The target into which data is to be read.
|
||||||
|
* @param targetLength The target length of the read.
|
||||||
|
* @return Whether the target length was reached.
|
||||||
|
*/
|
||||||
|
private boolean continueRead(ParsableByteArray source, byte[] target, int targetLength) {
|
||||||
|
int bytesToRead = Math.min(source.bytesLeft(), targetLength - bytesRead);
|
||||||
|
source.readBytes(target, bytesRead, bytesToRead);
|
||||||
|
bytesRead += bytesToRead;
|
||||||
|
return bytesRead == targetLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates the next SYNC value in the buffer, advancing the position to the byte that immediately
|
||||||
|
* follows it. If SYNC was not located, the position is advanced to the limit.
|
||||||
|
*
|
||||||
|
* @param pesBuffer The buffer whose position should be advanced.
|
||||||
|
* @return True if SYNC was found. False otherwise.
|
||||||
|
*/
|
||||||
|
private boolean skipToNextSync(ParsableByteArray pesBuffer) {
|
||||||
|
while (pesBuffer.bytesLeft() > 0) {
|
||||||
|
syncBytes <<= 8;
|
||||||
|
syncBytes |= pesBuffer.readUnsignedByte();
|
||||||
|
if (syncBytes == SYNC_VALUE) {
|
||||||
|
syncBytes = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the sample header.
|
||||||
|
*/
|
||||||
|
private void parseHeader() {
|
||||||
|
byte[] frameData = headerScratchBytes.data;
|
||||||
|
if (mediaFormat == null) {
|
||||||
|
mediaFormat = DtsUtil.parseDtsFormat(frameData, null, C.UNKNOWN_TIME_US, null);
|
||||||
|
output.format(mediaFormat);
|
||||||
|
}
|
||||||
|
sampleSize = DtsUtil.getDtsFrameSize(frameData);
|
||||||
|
// In this class a sample is an access unit (frame in DTS), but the MediaFormat sample rate
|
||||||
|
// specifies the number of PCM audio samples per second.
|
||||||
|
sampleDurationUs = (int) (C.MICROS_PER_SECOND
|
||||||
|
* DtsUtil.parseDtsAudioSampleCount(frameData) / mediaFormat.sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -47,6 +47,7 @@ public final class TsExtractor implements Extractor {
|
|||||||
private static final int TS_STREAM_TYPE_AAC = 0x0F;
|
private static final int TS_STREAM_TYPE_AAC = 0x0F;
|
||||||
private static final int TS_STREAM_TYPE_AC3 = 0x81;
|
private static final int TS_STREAM_TYPE_AC3 = 0x81;
|
||||||
private static final int TS_STREAM_TYPE_DTS = 0x8A;
|
private static final int TS_STREAM_TYPE_DTS = 0x8A;
|
||||||
|
private static final int TS_STREAM_TYPE_HDMV_DTS = 0x82;
|
||||||
private static final int TS_STREAM_TYPE_E_AC3 = 0x87;
|
private static final int TS_STREAM_TYPE_E_AC3 = 0x87;
|
||||||
private static final int TS_STREAM_TYPE_H262 = 0x02;
|
private static final int TS_STREAM_TYPE_H262 = 0x02;
|
||||||
private static final int TS_STREAM_TYPE_H264 = 0x1B;
|
private static final int TS_STREAM_TYPE_H264 = 0x1B;
|
||||||
@ -345,6 +346,10 @@ public final class TsExtractor implements Extractor {
|
|||||||
case TS_STREAM_TYPE_E_AC3:
|
case TS_STREAM_TYPE_E_AC3:
|
||||||
pesPayloadReader = new Ac3Reader(output.track(TS_STREAM_TYPE_E_AC3), true);
|
pesPayloadReader = new Ac3Reader(output.track(TS_STREAM_TYPE_E_AC3), true);
|
||||||
break;
|
break;
|
||||||
|
case TS_STREAM_TYPE_DTS:
|
||||||
|
case TS_STREAM_TYPE_HDMV_DTS:
|
||||||
|
pesPayloadReader = new DtsReader(output.track(TS_STREAM_TYPE_DTS));
|
||||||
|
break;
|
||||||
case TS_STREAM_TYPE_H262:
|
case TS_STREAM_TYPE_H262:
|
||||||
pesPayloadReader = new H262Reader(output.track(TS_STREAM_TYPE_H262));
|
pesPayloadReader = new H262Reader(output.track(TS_STREAM_TYPE_H262));
|
||||||
break;
|
break;
|
||||||
|
@ -235,8 +235,6 @@ public final class Ac3Util {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Ac3Util() {
|
private Ac3Util() {}
|
||||||
// Prevent instantiation.
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.google.android.exoplayer.MediaFormat;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for parsing DTS frames.
|
||||||
|
*/
|
||||||
|
public final class DtsUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps AMODE to the number of channels. See ETSI TS 102 114 table 5.4.
|
||||||
|
*/
|
||||||
|
private static final int[] CHANNELS_BY_AMODE = new int[] {1, 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6, 6,
|
||||||
|
7, 8, 8};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps SFREQ to the sampling frequency in Hz. See ETSI TS 102 144 table 5.5.
|
||||||
|
*/
|
||||||
|
private static final int[] SAMPLE_RATE_BY_SFREQ = new int[] {-1, 8000, 16000, 32000, -1, -1,
|
||||||
|
11025, 22050, 44100, -1, -1, 12000, 24000, 48000, -1, -1};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps RATE to 2 * bitrate in kbit/s. See ETSI TS 102 144 table 5.7.
|
||||||
|
*/
|
||||||
|
private static final int[] TWICE_BITRATE_KBPS_BY_RATE = new int[] {64, 112, 128, 192, 224, 256,
|
||||||
|
384, 448, 512, 640, 768, 896, 1024, 1152, 1280, 1536, 1920, 2048, 2304, 2560, 2688, 2816,
|
||||||
|
2823, 2944, 3072, 3840, 4096, 6144, 7680};
|
||||||
|
|
||||||
|
private static final ParsableBitArray SCRATCH_BITS = new ParsableBitArray();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the DTS format given {@code data} containing the DTS frame according to ETSI TS 102 114
|
||||||
|
* subsections 5.3/5.4.
|
||||||
|
* <p>
|
||||||
|
* This method may only be called from one thread at a time.
|
||||||
|
*
|
||||||
|
* @param frame The DTS frame to parse.
|
||||||
|
* @param trackId The track identifier to set on the format, or null.
|
||||||
|
* @param durationUs The duration to set on the format, in microseconds.
|
||||||
|
* @param language The language to set on the format.
|
||||||
|
* @return The DTS format parsed from data in the header.
|
||||||
|
*/
|
||||||
|
public static MediaFormat parseDtsFormat(byte[] frame, String trackId, long durationUs,
|
||||||
|
String language) {
|
||||||
|
ParsableBitArray frameBits = SCRATCH_BITS;
|
||||||
|
frameBits.reset(frame);
|
||||||
|
frameBits.skipBits(4 * 8 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
|
||||||
|
int amode = frameBits.readBits(6);
|
||||||
|
int channelCount = CHANNELS_BY_AMODE[amode];
|
||||||
|
int sfreq = frameBits.readBits(4);
|
||||||
|
int sampleRate = SAMPLE_RATE_BY_SFREQ[sfreq];
|
||||||
|
int rate = frameBits.readBits(5);
|
||||||
|
int bitrate = rate >= TWICE_BITRATE_KBPS_BY_RATE.length ? MediaFormat.NO_VALUE
|
||||||
|
: TWICE_BITRATE_KBPS_BY_RATE[rate] * 1000 / 2;
|
||||||
|
frameBits.skipBits(10); // MIX, DYNF, TIMEF, AUXF, HDCD, EXT_AUDIO_ID, EXT_AUDIO, ASPF
|
||||||
|
channelCount += frameBits.readBits(2) > 0 ? 1 : 0; // LFF
|
||||||
|
return MediaFormat.createAudioFormat(trackId, MimeTypes.AUDIO_DTS, bitrate,
|
||||||
|
MediaFormat.NO_VALUE, durationUs, channelCount, sampleRate, null, language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of audio samples represented by the given DTS frame.
|
||||||
|
*
|
||||||
|
* @param data The frame to parse.
|
||||||
|
* @return The number of audio samples represented by the frame.
|
||||||
|
*/
|
||||||
|
public static int parseDtsAudioSampleCount(byte[] data) {
|
||||||
|
// See ETSI TS 102 114 subsection 5.4.1.
|
||||||
|
int nblks = ((data[4] & 0x01) << 6) | ((data[5] & 0xFC) >> 2);
|
||||||
|
return (nblks + 1) * 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like {@link #parseDtsAudioSampleCount(byte[])} but reads from a byte buffer. The buffer
|
||||||
|
* position is not modified.
|
||||||
|
*/
|
||||||
|
public static int parseDtsAudioSampleCount(ByteBuffer data) {
|
||||||
|
// See ETSI TS 102 114 subsection 5.4.1.
|
||||||
|
int position = data.position();
|
||||||
|
int nblks = ((data.get(position + 4) & 0x01) << 6)
|
||||||
|
| ((data.get(position + 5) & 0xFC) >> 2);
|
||||||
|
return (nblks + 1) * 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size in bytes of the given DTS frame.
|
||||||
|
*
|
||||||
|
* @param data The frame to parse.
|
||||||
|
* @return The frame's size in bytes.
|
||||||
|
*/
|
||||||
|
public static int getDtsFrameSize(byte[] data) {
|
||||||
|
return (((data[5] & 0x02) << 12)
|
||||||
|
| ((data[6] & 0xFF) << 4)
|
||||||
|
| ((data[7] & 0xF0) >> 4)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DtsUtil() {}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user