mirror of
https://github.com/androidx/media.git
synced 2025-05-09 00:20:45 +08:00
Merge pull request #1803 from tresvecesseis/dev-v2-track_names
TsExtractor support for language code in the audio tracks.
This commit is contained in:
commit
127ebbe0f9
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user