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 static final int HEADER_SIZE = 8;
private String language;
private final ParsableBitArray headerScratchBits; private final ParsableBitArray headerScratchBits;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
@ -57,10 +59,21 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
* @param output Track output for extracted samples. * @param output Track output for extracted samples.
*/ */
public Ac3Reader(TrackOutput output) { 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); super(output);
headerScratchBits = new ParsableBitArray(new byte[HEADER_SIZE]); headerScratchBits = new ParsableBitArray(new byte[HEADER_SIZE]);
headerScratchBytes = new ParsableByteArray(headerScratchBits.data); headerScratchBytes = new ParsableByteArray(headerScratchBits.data);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
this.language = language;
} }
@Override @Override
@ -163,8 +176,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
headerScratchBits.skipBits(40); headerScratchBits.skipBits(40);
isEac3 = headerScratchBits.readBits(5) == 16; isEac3 = headerScratchBits.readBits(5) == 16;
headerScratchBits.setPosition(headerScratchBits.getPosition() - 45); headerScratchBits.setPosition(headerScratchBits.getPosition() - 45);
format = isEac3 ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, null, null, null) format = isEac3 ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, null, language, null)
: Ac3Util.parseAc3SyncframeFormat(headerScratchBits, null, null, null); : Ac3Util.parseAc3SyncframeFormat(headerScratchBits, null, language, null);
output.format(format); output.format(format);
} }
sampleSize = isEac3 ? Ac3Util.parseEAc3SyncframeSize(headerScratchBits.data) 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 HEADER_SIZE = 5;
private static final int CRC_SIZE = 2; private static final int CRC_SIZE = 2;
private String language;
// Match states used while looking for the next sample // Match states used while looking for the next sample
private static final int MATCH_STATE_VALUE_SHIFT = 8; private static final int MATCH_STATE_VALUE_SHIFT = 8;
private static final int MATCH_STATE_START = 1 << MATCH_STATE_VALUE_SHIFT; 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. * @param id3Output A {@link TrackOutput} to which ID3 samples should be written.
*/ */
public AdtsReader(TrackOutput output, TrackOutput id3Output) { 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); super(output);
this.id3Output = id3Output; this.id3Output = id3Output;
id3Output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, 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]); adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]);
id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE)); id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE));
setFindingSampleState(); setFindingSampleState();
this.language = language;
} }
@Override @Override
@ -278,7 +290,7 @@ import java.util.Collections;
Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, null, Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, null,
Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first, 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 // In this class a sample is an access unit, but the MediaFormat sample rate specifies the
// number of PCM audio samples per second. // number of PCM audio samples per second.
sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate; 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 = 0x7FFE8001;
private static final int SYNC_VALUE_SIZE = 4; private static final int SYNC_VALUE_SIZE = 4;
private String language;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
private int state; private int state;
@ -56,6 +58,16 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
* @param output Track output for extracted samples. * @param output Track output for extracted samples.
*/ */
public DtsReader(TrackOutput output) { 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); super(output);
headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]); headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]);
headerScratchBytes.data[0] = (byte) ((SYNC_VALUE >> 24) & 0xFF); 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[2] = (byte) ((SYNC_VALUE >> 8) & 0xFF);
headerScratchBytes.data[3] = (byte) (SYNC_VALUE & 0xFF); headerScratchBytes.data[3] = (byte) (SYNC_VALUE & 0xFF);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
this.language = language;
} }
@Override @Override
@ -155,7 +168,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private void parseHeader() { private void parseHeader() {
byte[] frameData = headerScratchBytes.data; byte[] frameData = headerScratchBytes.data;
if (format == null) { if (format == null) {
format = DtsUtil.parseDtsFormat(frameData, null, null, null); format = DtsUtil.parseDtsFormat(frameData, null, language, null);
output.format(format); output.format(format);
} }
sampleSize = DtsUtil.getDtsFrameSize(frameData); 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 static final int HEADER_SIZE = 4;
private String language;
private final ParsableByteArray headerScratch; private final ParsableByteArray headerScratch;
private final MpegAudioHeader header; private final MpegAudioHeader header;
@ -50,12 +52,17 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
private long timeUs; private long timeUs;
public MpegAudioReader(TrackOutput output) { public MpegAudioReader(TrackOutput output) {
this(output, null);
}
public MpegAudioReader(TrackOutput output, String language) {
super(output); super(output);
state = STATE_FINDING_HEADER; state = STATE_FINDING_HEADER;
// The first byte of an MPEG Audio frame header is always 0xFF. // The first byte of an MPEG Audio frame header is always 0xFF.
headerScratch = new ParsableByteArray(4); headerScratch = new ParsableByteArray(4);
headerScratch.data[0] = (byte) 0xFF; headerScratch.data[0] = (byte) 0xFF;
header = new MpegAudioHeader(); header = new MpegAudioHeader();
this.language = language;
} }
@Override @Override
@ -164,7 +171,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate; frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
Format format = Format.createAudioSampleFormat(null, header.mimeType, null, Format.NO_VALUE, Format format = Format.createAudioSampleFormat(null, header.mimeType, null, Format.NO_VALUE,
MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null, 0, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null, 0,
null); language);
output.format(format); output.format(format);
hasOutputFormat = true; hasOutputFormat = true;
} }

View File

@ -334,6 +334,32 @@ public final class TsExtractor implements Extractor {
private int sectionBytesRead; private int sectionBytesRead;
private int crc; 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() { public PmtReader() {
pmtScratch = new ParsableBitArray(new byte[5]); pmtScratch = new ParsableBitArray(new byte[5]);
sectionData = new ParsableByteArray(); sectionData = new ParsableByteArray();
@ -404,11 +430,9 @@ public final class TsExtractor implements Extractor {
int elementaryPid = pmtScratch.readBits(13); int elementaryPid = pmtScratch.readBits(13);
pmtScratch.skipBits(4); // reserved pmtScratch.skipBits(4); // reserved
int esInfoLength = pmtScratch.readBits(12); // ES_info_length int esInfoLength = pmtScratch.readBits(12); // ES_info_length
EsInfo esInfo = readEsInfo(sectionData, esInfoLength);
if (streamType == 0x06) { if (streamType == 0x06) {
// Read descriptors in PES packets containing private data. streamType = esInfo.streamType;
streamType = readPrivateDataStreamType(sectionData, esInfoLength);
} else {
sectionData.skipBytes(esInfoLength);
} }
remainingEntriesLength -= esInfoLength + 5; remainingEntriesLength -= esInfoLength + 5;
int trackId = (workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 ? streamType : elementaryPid; int trackId = (workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 ? streamType : elementaryPid;
@ -418,22 +442,22 @@ public final class TsExtractor implements Extractor {
ElementaryStreamReader pesPayloadReader; ElementaryStreamReader pesPayloadReader;
switch (streamType) { switch (streamType) {
case TS_STREAM_TYPE_MPA: case TS_STREAM_TYPE_MPA:
pesPayloadReader = new MpegAudioReader(output.track(trackId)); pesPayloadReader = new MpegAudioReader(output.track(trackId), esInfo.streamLanguage);
break; break;
case TS_STREAM_TYPE_MPA_LSF: case TS_STREAM_TYPE_MPA_LSF:
pesPayloadReader = new MpegAudioReader(output.track(trackId)); pesPayloadReader = new MpegAudioReader(output.track(trackId), esInfo.streamLanguage);
break; break;
case TS_STREAM_TYPE_AAC: case TS_STREAM_TYPE_AAC:
pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_AAC_STREAM) != 0 ? null 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; break;
case TS_STREAM_TYPE_AC3: case TS_STREAM_TYPE_AC3:
case TS_STREAM_TYPE_E_AC3: case TS_STREAM_TYPE_E_AC3:
pesPayloadReader = new Ac3Reader(output.track(trackId)); pesPayloadReader = new Ac3Reader(output.track(trackId), esInfo.streamLanguage);
break; break;
case TS_STREAM_TYPE_DTS: case TS_STREAM_TYPE_DTS:
case TS_STREAM_TYPE_HDMV_DTS: case TS_STREAM_TYPE_HDMV_DTS:
pesPayloadReader = new DtsReader(output.track(trackId)); pesPayloadReader = new DtsReader(output.track(trackId), esInfo.streamLanguage);
break; break;
case TS_STREAM_TYPE_H262: case TS_STREAM_TYPE_H262:
pesPayloadReader = new H262Reader(output.track(trackId)); 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 * Returns the stream info read from the available descriptors, or -1 if no
* stream type is present. Sets {@code data}'s position to the end of the descriptors. * 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 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}. * @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 * @return The stream info read from the available descriptors, or -1 if no
* stream type is present. * descriptors are present.
*/ */
private int readPrivateDataStreamType(ParsableByteArray data, int length) { private EsInfo readEsInfo(ParsableByteArray data, int length) {
int streamType = -1; EsInfo esInfo = new EsInfo();
int descriptorsEndPosition = data.getPosition() + length; int descriptorsEndPosition = data.getPosition() + length;
while (data.getPosition() < descriptorsEndPosition) { while (data.getPosition() < descriptorsEndPosition) {
int descriptorTag = data.readUnsignedByte(); int descriptorTag = data.readUnsignedByte();
int descriptorLength = data.readUnsignedByte(); int descriptorLength = data.readUnsignedByte();
if (descriptorTag == 0x05) { // registration_descriptor if (descriptorTag == TS_PMT_DESC_REGISTRATION) { // registration_descriptor
long formatIdentifier = data.readUnsignedInt(); long formatIdentifier = data.readUnsignedInt();
if (formatIdentifier == AC3_FORMAT_IDENTIFIER) { if (formatIdentifier == AC3_FORMAT_IDENTIFIER) {
streamType = TS_STREAM_TYPE_AC3; esInfo.streamType = TS_STREAM_TYPE_AC3;
} else if (formatIdentifier == E_AC3_FORMAT_IDENTIFIER) { } 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) { } else if (formatIdentifier == HEVC_FORMAT_IDENTIFIER) {
streamType = TS_STREAM_TYPE_H265; esInfo.streamType = TS_STREAM_TYPE_H265;
} }
break; break;
} else if (descriptorTag == 0x6A) { // AC-3_descriptor in DVB (ETSI EN 300 468) } else if (descriptorTag == TS_PMT_DESC_AC3) { // AC-3_descriptor in DVB (ETSI EN 300 468)
streamType = TS_STREAM_TYPE_AC3; esInfo.streamType = TS_STREAM_TYPE_AC3;
} else if (descriptorTag == 0x7A) { // enhanced_AC-3_descriptor } else if (descriptorTag == TS_PMT_DESC_EAC3) { // enhanced_AC-3_descriptor
streamType = TS_STREAM_TYPE_E_AC3; esInfo.streamType = TS_STREAM_TYPE_E_AC3;
} else if (descriptorTag == 0x7B) { // DTS_descriptor } else if (descriptorTag == TS_PMT_DESC_DTS) { // DTS_descriptor
streamType = TS_STREAM_TYPE_DTS; 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.skipBytes(descriptorLength);
} }
data.setPosition(descriptorsEndPosition); data.setPosition(descriptorsEndPosition);
return streamType; return esInfo;
} }
} }