Merge pull request #1803 from tresvecesseis/dev-v2-track_names

TsExtractor support for language code in the audio tracks.
This commit is contained in:
Santiago Seifert 2016-09-08 14:45:01 +01:00 committed by GitHub
commit 127ebbe0f9
5 changed files with 103 additions and 31 deletions

View File

@ -33,6 +33,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private static final int HEADER_SIZE = 8;
private String language;
private final ParsableBitArray headerScratchBits;
private final ParsableByteArray headerScratchBytes;
@ -57,10 +59,21 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
* @param output Track output for extracted samples.
*/
public Ac3Reader(TrackOutput output) {
this(output, null);
}
/**
* Constructs a new reader for (E-)AC-3 elementary streams.
*
* @param output Track output for extracted samples.
* @param language Track language.
*/
public Ac3Reader(TrackOutput output, String language) {
super(output);
headerScratchBits = new ParsableBitArray(new byte[HEADER_SIZE]);
headerScratchBytes = new ParsableByteArray(headerScratchBits.data);
state = STATE_FINDING_SYNC;
this.language = language;
}
@Override
@ -163,8 +176,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
headerScratchBits.skipBits(40);
isEac3 = headerScratchBits.readBits(5) == 16;
headerScratchBits.setPosition(headerScratchBits.getPosition() - 45);
format = isEac3 ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, null, null, null)
: Ac3Util.parseAc3SyncframeFormat(headerScratchBits, null, null, null);
format = isEac3 ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, null, language, null)
: Ac3Util.parseAc3SyncframeFormat(headerScratchBits, null, language, null);
output.format(format);
}
sampleSize = isEac3 ? Ac3Util.parseEAc3SyncframeSize(headerScratchBits.data)

View File

@ -42,6 +42,8 @@ import java.util.Collections;
private static final int HEADER_SIZE = 5;
private static final int CRC_SIZE = 2;
private String language;
// Match states used while looking for the next sample
private static final int MATCH_STATE_VALUE_SHIFT = 8;
private static final int MATCH_STATE_START = 1 << MATCH_STATE_VALUE_SHIFT;
@ -80,6 +82,15 @@ import java.util.Collections;
* @param id3Output A {@link TrackOutput} to which ID3 samples should be written.
*/
public AdtsReader(TrackOutput output, TrackOutput id3Output) {
this(output, id3Output, null);
}
/**
* @param output A {@link TrackOutput} to which AAC samples should be written.
* @param id3Output A {@link TrackOutput} to which ID3 samples should be written.
* @param language Track language.
*/
public AdtsReader(TrackOutput output, TrackOutput id3Output, String language) {
super(output);
this.id3Output = id3Output;
id3Output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null,
@ -87,6 +98,7 @@ import java.util.Collections;
adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]);
id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE));
setFindingSampleState();
this.language = language;
}
@Override
@ -278,7 +290,7 @@ import java.util.Collections;
Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first,
Collections.singletonList(audioSpecificConfig), null, 0, null);
Collections.singletonList(audioSpecificConfig), null, 0, language);
// In this class a sample is an access unit, but the MediaFormat sample rate specifies the
// number of PCM audio samples per second.
sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate;

View File

@ -34,6 +34,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private static final int SYNC_VALUE = 0x7FFE8001;
private static final int SYNC_VALUE_SIZE = 4;
private String language;
private final ParsableByteArray headerScratchBytes;
private int state;
@ -56,6 +58,16 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
* @param output Track output for extracted samples.
*/
public DtsReader(TrackOutput output) {
this(output, null);
}
/**
* Constructs a new reader for DTS elementary streams.
*
* @param output Track output for extracted samples.
* @param language Track language.
*/
public DtsReader(TrackOutput output, String language) {
super(output);
headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]);
headerScratchBytes.data[0] = (byte) ((SYNC_VALUE >> 24) & 0xFF);
@ -63,6 +75,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
headerScratchBytes.data[2] = (byte) ((SYNC_VALUE >> 8) & 0xFF);
headerScratchBytes.data[3] = (byte) (SYNC_VALUE & 0xFF);
state = STATE_FINDING_SYNC;
this.language = language;
}
@Override
@ -155,7 +168,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private void parseHeader() {
byte[] frameData = headerScratchBytes.data;
if (format == null) {
format = DtsUtil.parseDtsFormat(frameData, null, null, null);
format = DtsUtil.parseDtsFormat(frameData, null, language, null);
output.format(format);
}
sampleSize = DtsUtil.getDtsFrameSize(frameData);

View File

@ -32,6 +32,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private static final int HEADER_SIZE = 4;
private String language;
private final ParsableByteArray headerScratch;
private final MpegAudioHeader header;
@ -50,12 +52,17 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private long timeUs;
public MpegAudioReader(TrackOutput output) {
this(output, null);
}
public MpegAudioReader(TrackOutput output, String language) {
super(output);
state = STATE_FINDING_HEADER;
// The first byte of an MPEG Audio frame header is always 0xFF.
headerScratch = new ParsableByteArray(4);
headerScratch.data[0] = (byte) 0xFF;
header = new MpegAudioHeader();
this.language = language;
}
@Override
@ -164,7 +171,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
Format format = Format.createAudioSampleFormat(null, header.mimeType, null, Format.NO_VALUE,
MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null, 0,
null);
language);
output.format(format);
hasOutputFormat = true;
}

View File

@ -334,6 +334,32 @@ public final class TsExtractor implements Extractor {
private int sectionBytesRead;
private int crc;
private static final int TS_PMT_DESC_REGISTRATION = 0x05;
private static final int TS_PMT_DESC_ISO639_LANG = 0x0A;
private static final int TS_PMT_DESC_VBI_DATA = 0x45;
private static final int TS_PMT_DESC_VBI_TELETEXT = 0x46;
private static final int TS_PMT_DESC_TELETEXT = 0x56;
private static final int TS_PMT_DESC_SUBTITLING = 0x59;
private static final int TS_PMT_DESC_AC3 = 0x6A;
private static final int TS_PMT_DESC_EAC3 = 0x7A;
private static final int TS_PMT_DESC_DTS = 0x7B;
private static final int TS_PMT_DESC_AAC = 0x7C;
class EsInfo {
int streamType;
String streamLanguage;
int audioType;
public EsInfo() {
// REGISTRATION
streamType = -1;
// ISO639LANG
streamLanguage = null;
audioType = -1;
}
}
public PmtReader() {
pmtScratch = new ParsableBitArray(new byte[5]);
sectionData = new ParsableByteArray();
@ -404,11 +430,9 @@ public final class TsExtractor implements Extractor {
int elementaryPid = pmtScratch.readBits(13);
pmtScratch.skipBits(4); // reserved
int esInfoLength = pmtScratch.readBits(12); // ES_info_length
EsInfo esInfo = readEsInfo(sectionData, esInfoLength);
if (streamType == 0x06) {
// Read descriptors in PES packets containing private data.
streamType = readPrivateDataStreamType(sectionData, esInfoLength);
} else {
sectionData.skipBytes(esInfoLength);
streamType = esInfo.streamType;
}
remainingEntriesLength -= esInfoLength + 5;
int trackId = (workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 ? streamType : elementaryPid;
@ -418,22 +442,22 @@ public final class TsExtractor implements Extractor {
ElementaryStreamReader pesPayloadReader;
switch (streamType) {
case TS_STREAM_TYPE_MPA:
pesPayloadReader = new MpegAudioReader(output.track(trackId));
pesPayloadReader = new MpegAudioReader(output.track(trackId), esInfo.streamLanguage);
break;
case TS_STREAM_TYPE_MPA_LSF:
pesPayloadReader = new MpegAudioReader(output.track(trackId));
pesPayloadReader = new MpegAudioReader(output.track(trackId), esInfo.streamLanguage);
break;
case TS_STREAM_TYPE_AAC:
pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_AAC_STREAM) != 0 ? null
: new AdtsReader(output.track(trackId), new DummyTrackOutput());
: new AdtsReader(output.track(trackId), new DummyTrackOutput(), esInfo.streamLanguage);
break;
case TS_STREAM_TYPE_AC3:
case TS_STREAM_TYPE_E_AC3:
pesPayloadReader = new Ac3Reader(output.track(trackId));
pesPayloadReader = new Ac3Reader(output.track(trackId), esInfo.streamLanguage);
break;
case TS_STREAM_TYPE_DTS:
case TS_STREAM_TYPE_HDMV_DTS:
pesPayloadReader = new DtsReader(output.track(trackId));
pesPayloadReader = new DtsReader(output.track(trackId), esInfo.streamLanguage);
break;
case TS_STREAM_TYPE_H262:
pesPayloadReader = new H262Reader(output.track(trackId));
@ -472,42 +496,45 @@ public final class TsExtractor implements Extractor {
}
/**
* Returns the stream type read from a registration descriptor in private data, or -1 if no
* stream type is present. Sets {@code data}'s position to the end of the descriptors.
* Returns the stream info read from the available descriptors, or -1 if no
* descriptors are present. Sets {@code data}'s position to the end of the descriptors.
*
* @param data A buffer with its position set to the start of the first descriptor.
* @param length The length of descriptors to read from the current position in {@code data}.
* @return The stream type read from a registration descriptor in private data, or -1 if no
* stream type is present.
* @return The stream info read from the available descriptors, or -1 if no
* descriptors are present.
*/
private int readPrivateDataStreamType(ParsableByteArray data, int length) {
int streamType = -1;
private EsInfo readEsInfo(ParsableByteArray data, int length) {
EsInfo esInfo = new EsInfo();
int descriptorsEndPosition = data.getPosition() + length;
while (data.getPosition() < descriptorsEndPosition) {
int descriptorTag = data.readUnsignedByte();
int descriptorLength = data.readUnsignedByte();
if (descriptorTag == 0x05) { // registration_descriptor
if (descriptorTag == TS_PMT_DESC_REGISTRATION) { // registration_descriptor
long formatIdentifier = data.readUnsignedInt();
if (formatIdentifier == AC3_FORMAT_IDENTIFIER) {
streamType = TS_STREAM_TYPE_AC3;
esInfo.streamType = TS_STREAM_TYPE_AC3;
} else if (formatIdentifier == E_AC3_FORMAT_IDENTIFIER) {
streamType = TS_STREAM_TYPE_E_AC3;
esInfo.streamType = TS_STREAM_TYPE_E_AC3;
} else if (formatIdentifier == HEVC_FORMAT_IDENTIFIER) {
streamType = TS_STREAM_TYPE_H265;
esInfo.streamType = TS_STREAM_TYPE_H265;
}
break;
} else if (descriptorTag == 0x6A) { // AC-3_descriptor in DVB (ETSI EN 300 468)
streamType = TS_STREAM_TYPE_AC3;
} else if (descriptorTag == 0x7A) { // enhanced_AC-3_descriptor
streamType = TS_STREAM_TYPE_E_AC3;
} else if (descriptorTag == 0x7B) { // DTS_descriptor
streamType = TS_STREAM_TYPE_DTS;
} else if (descriptorTag == TS_PMT_DESC_AC3) { // AC-3_descriptor in DVB (ETSI EN 300 468)
esInfo.streamType = TS_STREAM_TYPE_AC3;
} else if (descriptorTag == TS_PMT_DESC_EAC3) { // enhanced_AC-3_descriptor
esInfo.streamType = TS_STREAM_TYPE_E_AC3;
} else if (descriptorTag == TS_PMT_DESC_DTS) { // DTS_descriptor
esInfo.streamType = TS_STREAM_TYPE_DTS;
} else if (descriptorTag == TS_PMT_DESC_ISO639_LANG) {
esInfo.streamLanguage = new String(data.data, data.getPosition(), 3).trim();
esInfo.audioType = data.data[data.getPosition() + 3];
}
data.skipBytes(descriptorLength);
}
data.setPosition(descriptorsEndPosition);
return streamType;
return esInfo;
}
}