Merge pull request #973 from jan-varecka-signageos-io:feat/extractTsAudioType

PiperOrigin-RevId: 599547201
This commit is contained in:
Copybara-Service 2024-01-18 10:25:47 -08:00
commit c403df116b
18 changed files with 817 additions and 20 deletions

View File

@ -19,6 +19,9 @@
than if we ignore it and assume the file is CBR. than if we ignore it and assume the file is CBR.
* MPEG2-TS: Add DTS, DTS-LBR and DTS:X Profile2 support * MPEG2-TS: Add DTS, DTS-LBR and DTS:X Profile2 support
([#275](https://github.com/androidx/media/pull/275)). ([#275](https://github.com/androidx/media/pull/275)).
* Extract audio types from TS descriptors and map them to role flags,
allowing users to make better-informed audio track selections
([#973](https://github.com/androidx/media/pull/973)).
* Audio: * Audio:
* Video: * Video:
* Text: * Text:

View File

@ -258,6 +258,7 @@ public final class DtsUtil {
* @param frame The DTS Core frame to parse. * @param frame The DTS Core frame to parse.
* @param trackId The track identifier to set on the format. * @param trackId The track identifier to set on the format.
* @param language The language to set on the format. * @param language The language to set on the format.
* @param roleFlags The role flags to set on the format.
* @param drmInitData {@link DrmInitData} to be included in the format. * @param drmInitData {@link DrmInitData} to be included in the format.
* @return The DTS format parsed from data in the header. * @return The DTS format parsed from data in the header.
*/ */
@ -265,6 +266,7 @@ public final class DtsUtil {
byte[] frame, byte[] frame,
@Nullable String trackId, @Nullable String trackId,
@Nullable String language, @Nullable String language,
@C.RoleFlags int roleFlags,
@Nullable DrmInitData drmInitData) { @Nullable DrmInitData drmInitData) {
ParsableBitArray frameBits = getNormalizedFrame(frame); ParsableBitArray frameBits = getNormalizedFrame(frame);
frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
@ -287,6 +289,7 @@ public final class DtsUtil {
.setSampleRate(sampleRate) .setSampleRate(sampleRate)
.setDrmInitData(drmInitData) .setDrmInitData(drmInitData)
.setLanguage(language) .setLanguage(language)
.setRoleFlags(roleFlags)
.build(); .build();
} }

View File

@ -60,6 +60,7 @@ public final class Ac3Reader implements ElementaryStreamReader {
private final ParsableBitArray headerScratchBits; private final ParsableBitArray headerScratchBits;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
@Nullable private final String language; @Nullable private final String language;
private final @C.RoleFlags int roleFlags;
private @MonotonicNonNull String formatId; private @MonotonicNonNull String formatId;
private @MonotonicNonNull TrackOutput output; private @MonotonicNonNull TrackOutput output;
@ -80,20 +81,22 @@ public final class Ac3Reader implements ElementaryStreamReader {
/** Constructs a new reader for (E-)AC-3 elementary streams. */ /** Constructs a new reader for (E-)AC-3 elementary streams. */
public Ac3Reader() { public Ac3Reader() {
this(null); this(null, /* roleFlags= */ 0);
} }
/** /**
* Constructs a new reader for (E-)AC-3 elementary streams. * Constructs a new reader for (E-)AC-3 elementary streams.
* *
* @param language Track language. * @param language Track language.
* @param roleFlags Track role flags.
*/ */
public Ac3Reader(@Nullable String language) { public Ac3Reader(@Nullable String language, @C.RoleFlags int roleFlags) {
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;
timeUs = C.TIME_UNSET; timeUs = C.TIME_UNSET;
this.language = language; this.language = language;
this.roleFlags = roleFlags;
} }
@Override @Override
@ -216,6 +219,7 @@ public final class Ac3Reader implements ElementaryStreamReader {
.setChannelCount(frameInfo.channelCount) .setChannelCount(frameInfo.channelCount)
.setSampleRate(frameInfo.sampleRate) .setSampleRate(frameInfo.sampleRate)
.setLanguage(language) .setLanguage(language)
.setRoleFlags(roleFlags)
.setPeakBitrate(frameInfo.bitrate); .setPeakBitrate(frameInfo.bitrate);
// AC3 has constant bitrate, so averageBitrate = peakBitrate // AC3 has constant bitrate, so averageBitrate = peakBitrate
if (MimeTypes.AUDIO_AC3.equals(frameInfo.mimeType)) { if (MimeTypes.AUDIO_AC3.equals(frameInfo.mimeType)) {

View File

@ -57,6 +57,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
private final ParsableBitArray headerScratchBits; private final ParsableBitArray headerScratchBits;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
@Nullable private final String language; @Nullable private final String language;
private final @C.RoleFlags int roleFlags;
private @MonotonicNonNull String formatId; private @MonotonicNonNull String formatId;
private @MonotonicNonNull TrackOutput output; private @MonotonicNonNull TrackOutput output;
@ -78,15 +79,16 @@ public final class Ac4Reader implements ElementaryStreamReader {
/** Constructs a new reader for AC-4 elementary streams. */ /** Constructs a new reader for AC-4 elementary streams. */
public Ac4Reader() { public Ac4Reader() {
this(null); this(null, /* roleFlags= */ 0);
} }
/** /**
* Constructs a new reader for AC-4 elementary streams. * Constructs a new reader for AC-4 elementary streams.
* *
* @param language Track language. * @param language Track language.
* @param roleFlags Track role flags.
*/ */
public Ac4Reader(@Nullable String language) { public Ac4Reader(@Nullable String language, @C.RoleFlags int roleFlags) {
headerScratchBits = new ParsableBitArray(new byte[Ac4Util.HEADER_SIZE_FOR_PARSER]); headerScratchBits = new ParsableBitArray(new byte[Ac4Util.HEADER_SIZE_FOR_PARSER]);
headerScratchBytes = new ParsableByteArray(headerScratchBits.data); headerScratchBytes = new ParsableByteArray(headerScratchBits.data);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
@ -95,6 +97,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
hasCRC = false; hasCRC = false;
timeUs = C.TIME_UNSET; timeUs = C.TIME_UNSET;
this.language = language; this.language = language;
this.roleFlags = roleFlags;
} }
@Override @Override
@ -217,6 +220,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
.setChannelCount(frameInfo.channelCount) .setChannelCount(frameInfo.channelCount)
.setSampleRate(frameInfo.sampleRate) .setSampleRate(frameInfo.sampleRate)
.setLanguage(language) .setLanguage(language)
.setRoleFlags(roleFlags)
.build(); .build();
output.format(format); output.format(format);
} }

View File

@ -71,6 +71,7 @@ public final class AdtsReader implements ElementaryStreamReader {
private final ParsableBitArray adtsScratch; private final ParsableBitArray adtsScratch;
private final ParsableByteArray id3HeaderBuffer; private final ParsableByteArray id3HeaderBuffer;
@Nullable private final String language; @Nullable private final String language;
private final @C.RoleFlags int roleFlags;
private @MonotonicNonNull String formatId; private @MonotonicNonNull String formatId;
private @MonotonicNonNull TrackOutput output; private @MonotonicNonNull TrackOutput output;
@ -105,14 +106,15 @@ public final class AdtsReader implements ElementaryStreamReader {
* @param exposeId3 True if the reader should expose ID3 information. * @param exposeId3 True if the reader should expose ID3 information.
*/ */
public AdtsReader(boolean exposeId3) { public AdtsReader(boolean exposeId3) {
this(exposeId3, null); this(exposeId3, null, /* roleFlags= */ 0);
} }
/** /**
* @param exposeId3 True if the reader should expose ID3 information. * @param exposeId3 True if the reader should expose ID3 information.
* @param language Track language. * @param language Track language.
* @param roleFlags Track role flags.
*/ */
public AdtsReader(boolean exposeId3, @Nullable String language) { public AdtsReader(boolean exposeId3, @Nullable String language, @C.RoleFlags int roleFlags) {
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();
@ -122,6 +124,7 @@ public final class AdtsReader implements ElementaryStreamReader {
timeUs = C.TIME_UNSET; timeUs = C.TIME_UNSET;
this.exposeId3 = exposeId3; this.exposeId3 = exposeId3;
this.language = language; this.language = language;
this.roleFlags = roleFlags;
} }
/** Returns whether an integer matches an ADTS SYNC word. */ /** Returns whether an integer matches an ADTS SYNC word. */
@ -510,6 +513,7 @@ public final class AdtsReader implements ElementaryStreamReader {
.setSampleRate(aacConfig.sampleRateHz) .setSampleRate(aacConfig.sampleRateHz)
.setInitializationData(Collections.singletonList(audioSpecificConfig)) .setInitializationData(Collections.singletonList(audioSpecificConfig))
.setLanguage(language) .setLanguage(language)
.setRoleFlags(roleFlags)
.build(); .build();
// 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.

View File

@ -151,20 +151,20 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
switch (streamType) { switch (streamType) {
case TsExtractor.TS_STREAM_TYPE_MPA: case TsExtractor.TS_STREAM_TYPE_MPA:
case TsExtractor.TS_STREAM_TYPE_MPA_LSF: case TsExtractor.TS_STREAM_TYPE_MPA_LSF:
return new PesReader(new MpegAudioReader(esInfo.language)); return new PesReader(new MpegAudioReader(esInfo.language, esInfo.getRoleFlags()));
case TsExtractor.TS_STREAM_TYPE_AAC_ADTS: case TsExtractor.TS_STREAM_TYPE_AAC_ADTS:
return isSet(FLAG_IGNORE_AAC_STREAM) return isSet(FLAG_IGNORE_AAC_STREAM)
? null ? null
: new PesReader(new AdtsReader(false, esInfo.language)); : new PesReader(new AdtsReader(false, esInfo.language, esInfo.getRoleFlags()));
case TsExtractor.TS_STREAM_TYPE_AAC_LATM: case TsExtractor.TS_STREAM_TYPE_AAC_LATM:
return isSet(FLAG_IGNORE_AAC_STREAM) return isSet(FLAG_IGNORE_AAC_STREAM)
? null ? null
: new PesReader(new LatmReader(esInfo.language)); : new PesReader(new LatmReader(esInfo.language, esInfo.getRoleFlags()));
case TsExtractor.TS_STREAM_TYPE_AC3: case TsExtractor.TS_STREAM_TYPE_AC3:
case TsExtractor.TS_STREAM_TYPE_E_AC3: case TsExtractor.TS_STREAM_TYPE_E_AC3:
return new PesReader(new Ac3Reader(esInfo.language)); return new PesReader(new Ac3Reader(esInfo.language, esInfo.getRoleFlags()));
case TsExtractor.TS_STREAM_TYPE_AC4: case TsExtractor.TS_STREAM_TYPE_AC4:
return new PesReader(new Ac4Reader(esInfo.language)); return new PesReader(new Ac4Reader(esInfo.language, esInfo.getRoleFlags()));
case TsExtractor.TS_STREAM_TYPE_HDMV_DTS: case TsExtractor.TS_STREAM_TYPE_HDMV_DTS:
if (!isSet(FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS)) { if (!isSet(FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS)) {
return null; return null;
@ -172,9 +172,11 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
// Fall through. // Fall through.
case TsExtractor.TS_STREAM_TYPE_DTS: case TsExtractor.TS_STREAM_TYPE_DTS:
case TsExtractor.TS_STREAM_TYPE_DTS_HD: case TsExtractor.TS_STREAM_TYPE_DTS_HD:
return new PesReader(new DtsReader(esInfo.language, DtsReader.EXTSS_HEADER_SIZE_MAX)); return new PesReader(
new DtsReader(esInfo.language, esInfo.getRoleFlags(), DtsReader.EXTSS_HEADER_SIZE_MAX));
case TsExtractor.TS_STREAM_TYPE_DTS_UHD: case TsExtractor.TS_STREAM_TYPE_DTS_UHD:
return new PesReader(new DtsReader(esInfo.language, DtsReader.FTOC_MAX_HEADER_SIZE)); return new PesReader(
new DtsReader(esInfo.language, esInfo.getRoleFlags(), DtsReader.FTOC_MAX_HEADER_SIZE));
case TsExtractor.TS_STREAM_TYPE_H262: case TsExtractor.TS_STREAM_TYPE_H262:
case TsExtractor.TS_STREAM_TYPE_DC2_H262: case TsExtractor.TS_STREAM_TYPE_DC2_H262:
return new PesReader(new H262Reader(buildUserDataReader(esInfo))); return new PesReader(new H262Reader(buildUserDataReader(esInfo)));

View File

@ -68,6 +68,7 @@ public final class DtsReader implements ElementaryStreamReader {
private final AtomicInteger uhdAudioChunkId; private final AtomicInteger uhdAudioChunkId;
@Nullable private final String language; @Nullable private final String language;
private final @C.RoleFlags int roleFlags;
private @MonotonicNonNull String formatId; private @MonotonicNonNull String formatId;
private @MonotonicNonNull TrackOutput output; private @MonotonicNonNull TrackOutput output;
@ -93,9 +94,10 @@ public final class DtsReader implements ElementaryStreamReader {
* Constructs a new reader for DTS elementary streams. * Constructs a new reader for DTS elementary streams.
* *
* @param language Track language. * @param language Track language.
* @param roleFlags Track role flags.
* @param maxHeaderSize Maximum size of the header in a frame. * @param maxHeaderSize Maximum size of the header in a frame.
*/ */
public DtsReader(@Nullable String language, int maxHeaderSize) { public DtsReader(@Nullable String language, @C.RoleFlags int roleFlags, int maxHeaderSize) {
headerScratchBytes = new ParsableByteArray(new byte[maxHeaderSize]); headerScratchBytes = new ParsableByteArray(new byte[maxHeaderSize]);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
timeUs = C.TIME_UNSET; timeUs = C.TIME_UNSET;
@ -103,6 +105,7 @@ public final class DtsReader implements ElementaryStreamReader {
extensionSubstreamHeaderSize = C.LENGTH_UNSET; extensionSubstreamHeaderSize = C.LENGTH_UNSET;
uhdHeaderSize = C.LENGTH_UNSET; uhdHeaderSize = C.LENGTH_UNSET;
this.language = language; this.language = language;
this.roleFlags = roleFlags;
} }
@Override @Override
@ -263,7 +266,7 @@ public final class DtsReader implements ElementaryStreamReader {
private void parseCoreHeader() { private void parseCoreHeader() {
byte[] frameData = headerScratchBytes.getData(); byte[] frameData = headerScratchBytes.getData();
if (format == null) { if (format == null) {
format = DtsUtil.parseDtsFormat(frameData, formatId, language, null); format = DtsUtil.parseDtsFormat(frameData, formatId, language, roleFlags, null);
output.format(format); output.format(format);
} }
sampleSize = DtsUtil.getDtsFrameSize(frameData); sampleSize = DtsUtil.getDtsFrameSize(frameData);
@ -314,6 +317,7 @@ public final class DtsReader implements ElementaryStreamReader {
.setChannelCount(dtsHeader.channelCount) .setChannelCount(dtsHeader.channelCount)
.setSampleRate(dtsHeader.sampleRate) .setSampleRate(dtsHeader.sampleRate)
.setLanguage(language) .setLanguage(language)
.setRoleFlags(roleFlags)
.build(); .build();
output.format(format); output.format(format);
} }

View File

@ -49,6 +49,7 @@ public final class LatmReader implements ElementaryStreamReader {
private static final int SYNC_BYTE_SECOND = 0xE0; private static final int SYNC_BYTE_SECOND = 0xE0;
@Nullable private final String language; @Nullable private final String language;
private final @C.RoleFlags int roleFlags;
private final ParsableByteArray sampleDataBuffer; private final ParsableByteArray sampleDataBuffer;
private final ParsableBitArray sampleBitArray; private final ParsableBitArray sampleBitArray;
@ -78,9 +79,11 @@ public final class LatmReader implements ElementaryStreamReader {
/** /**
* @param language Track language. * @param language Track language.
* @param roleFlags Track role flags.
*/ */
public LatmReader(@Nullable String language) { public LatmReader(@Nullable String language, @C.RoleFlags int roleFlags) {
this.language = language; this.language = language;
this.roleFlags = roleFlags;
sampleDataBuffer = new ParsableByteArray(INITIAL_BUFFER_SIZE); sampleDataBuffer = new ParsableByteArray(INITIAL_BUFFER_SIZE);
sampleBitArray = new ParsableBitArray(sampleDataBuffer.getData()); sampleBitArray = new ParsableBitArray(sampleDataBuffer.getData());
timeUs = C.TIME_UNSET; timeUs = C.TIME_UNSET;
@ -217,6 +220,7 @@ public final class LatmReader implements ElementaryStreamReader {
.setSampleRate(sampleRateHz) .setSampleRate(sampleRateHz)
.setInitializationData(Collections.singletonList(initData)) .setInitializationData(Collections.singletonList(initData))
.setLanguage(language) .setLanguage(language)
.setRoleFlags(roleFlags)
.build(); .build();
if (!format.equals(this.format)) { if (!format.equals(this.format)) {
this.format = format; this.format = format;

View File

@ -44,6 +44,7 @@ public final class MpegAudioReader implements ElementaryStreamReader {
private final ParsableByteArray headerScratch; private final ParsableByteArray headerScratch;
private final MpegAudioUtil.Header header; private final MpegAudioUtil.Header header;
@Nullable private final String language; @Nullable private final String language;
private final @C.RoleFlags int roleFlags;
private @MonotonicNonNull TrackOutput output; private @MonotonicNonNull TrackOutput output;
private @MonotonicNonNull String formatId; private @MonotonicNonNull String formatId;
@ -63,10 +64,10 @@ public final class MpegAudioReader implements ElementaryStreamReader {
private long timeUs; private long timeUs;
public MpegAudioReader() { public MpegAudioReader() {
this(null); this(null, /* roleFlags= */ 0);
} }
public MpegAudioReader(@Nullable String language) { public MpegAudioReader(@Nullable String language, @C.RoleFlags int roleFlags) {
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);
@ -74,6 +75,7 @@ public final class MpegAudioReader implements ElementaryStreamReader {
header = new MpegAudioUtil.Header(); header = new MpegAudioUtil.Header();
timeUs = C.TIME_UNSET; timeUs = C.TIME_UNSET;
this.language = language; this.language = language;
this.roleFlags = roleFlags;
} }
@Override @Override
@ -200,6 +202,7 @@ public final class MpegAudioReader implements ElementaryStreamReader {
.setChannelCount(header.channels) .setChannelCount(header.channels)
.setSampleRate(header.sampleRate) .setSampleRate(header.sampleRate)
.setLanguage(language) .setLanguage(language)
.setRoleFlags(roleFlags)
.build(); .build();
output.format(format); output.format(format);
hasOutputFormat = true; hasOutputFormat = true;

View File

@ -15,6 +15,7 @@
*/ */
package androidx.media3.extractor.ts; package androidx.media3.extractor.ts;
import static androidx.media3.extractor.ts.TsPayloadReader.EsInfo.AUDIO_TYPE_UNDEFINED;
import static androidx.media3.extractor.ts.TsPayloadReader.FLAG_PAYLOAD_UNIT_START_INDICATOR; import static androidx.media3.extractor.ts.TsPayloadReader.FLAG_PAYLOAD_UNIT_START_INDICATOR;
import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.ElementType.TYPE_USE;
@ -752,7 +753,8 @@ public final class TsExtractor implements Extractor {
if (mode == MODE_HLS && id3Reader == null) { if (mode == MODE_HLS && id3Reader == null) {
// Setup an ID3 track regardless of whether there's a corresponding entry, in case one // Setup an ID3 track regardless of whether there's a corresponding entry, in case one
// appears intermittently during playback. See [Internal: b/20261500]. // appears intermittently during playback. See [Internal: b/20261500].
EsInfo id3EsInfo = new EsInfo(TS_STREAM_TYPE_ID3, null, null, Util.EMPTY_BYTE_ARRAY); EsInfo id3EsInfo =
new EsInfo(TS_STREAM_TYPE_ID3, null, AUDIO_TYPE_UNDEFINED, null, Util.EMPTY_BYTE_ARRAY);
id3Reader = payloadReaderFactory.createPayloadReader(TS_STREAM_TYPE_ID3, id3EsInfo); id3Reader = payloadReaderFactory.createPayloadReader(TS_STREAM_TYPE_ID3, id3EsInfo);
if (id3Reader != null) { if (id3Reader != null) {
id3Reader.init( id3Reader.init(
@ -842,6 +844,7 @@ public final class TsExtractor implements Extractor {
int descriptorsEndPosition = descriptorsStartPosition + length; int descriptorsEndPosition = descriptorsStartPosition + length;
int streamType = -1; int streamType = -1;
String language = null; String language = null;
@EsInfo.AudioType int audioType = AUDIO_TYPE_UNDEFINED;
List<DvbSubtitleInfo> dvbSubtitleInfos = null; List<DvbSubtitleInfo> dvbSubtitleInfos = null;
while (data.getPosition() < descriptorsEndPosition) { while (data.getPosition() < descriptorsEndPosition) {
int descriptorTag = data.readUnsignedByte(); int descriptorTag = data.readUnsignedByte();
@ -883,7 +886,7 @@ public final class TsExtractor implements Extractor {
streamType = TS_STREAM_TYPE_DTS; streamType = TS_STREAM_TYPE_DTS;
} else if (descriptorTag == TS_PMT_DESC_ISO639_LANG) { } else if (descriptorTag == TS_PMT_DESC_ISO639_LANG) {
language = data.readString(3).trim(); language = data.readString(3).trim();
// Audio type is ignored. audioType = data.readUnsignedByte();
} else if (descriptorTag == TS_PMT_DESC_DVBSUBS) { } else if (descriptorTag == TS_PMT_DESC_DVBSUBS) {
streamType = TS_STREAM_TYPE_DVBSUBS; streamType = TS_STREAM_TYPE_DVBSUBS;
dvbSubtitleInfos = new ArrayList<>(); dvbSubtitleInfos = new ArrayList<>();
@ -905,6 +908,7 @@ public final class TsExtractor implements Extractor {
return new EsInfo( return new EsInfo(
streamType, streamType,
language, language,
audioType,
dvbSubtitleInfos, dvbSubtitleInfos,
Arrays.copyOfRange(data.getData(), descriptorsStartPosition, descriptorsEndPosition)); Arrays.copyOfRange(data.getData(), descriptorsStartPosition, descriptorsEndPosition));
} }

View File

@ -16,10 +16,12 @@
package androidx.media3.extractor.ts; package androidx.media3.extractor.ts;
import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.ParserException; import androidx.media3.common.ParserException;
import androidx.media3.common.util.ParsableByteArray; import androidx.media3.common.util.ParsableByteArray;
import androidx.media3.common.util.TimestampAdjuster; import androidx.media3.common.util.TimestampAdjuster;
@ -65,8 +67,48 @@ public interface TsPayloadReader {
/** Holds information associated with a PMT entry. */ /** Holds information associated with a PMT entry. */
final class EsInfo { final class EsInfo {
/**
* The audio type of the stream, as defined by ISO/IEC 13818-1, section 2.6.18.
*
* <p>One of {@link #AUDIO_TYPE_UNDEFINED}, {@link #AUDIO_TYPE_CLEAN_EFFECTS}, {@link
* #AUDIO_TYPE_HEARING_IMPAIRED} or {@link #AUDIO_TYPE_VISUAL_IMPAIRED_COMMENTARY}.
*/
@Documented
@Retention(SOURCE)
@Target(TYPE_USE)
@IntDef({
AUDIO_TYPE_UNDEFINED,
AUDIO_TYPE_CLEAN_EFFECTS,
AUDIO_TYPE_HEARING_IMPAIRED,
AUDIO_TYPE_VISUAL_IMPAIRED_COMMENTARY
})
public @interface AudioType {}
public static final int AUDIO_TYPE_UNDEFINED = 0;
/** Indicates the track has no language. */
public static final int AUDIO_TYPE_CLEAN_EFFECTS = 1;
/** Indicates the track is prepared for the hearing impaired. */
public static final int AUDIO_TYPE_HEARING_IMPAIRED = 2;
/** Indicates the track is prepared for the visually impaired viewer. */
public static final int AUDIO_TYPE_VISUAL_IMPAIRED_COMMENTARY = 3;
public @C.RoleFlags int getRoleFlags() {
switch (audioType) {
case AUDIO_TYPE_HEARING_IMPAIRED:
return C.ROLE_FLAG_ENHANCED_DIALOG_INTELLIGIBILITY;
case AUDIO_TYPE_VISUAL_IMPAIRED_COMMENTARY:
return C.ROLE_FLAG_DESCRIBES_VIDEO;
default:
return 0;
}
}
public final int streamType; public final int streamType;
@Nullable public final String language; @Nullable public final String language;
public final @AudioType int audioType;
public final List<DvbSubtitleInfo> dvbSubtitleInfos; public final List<DvbSubtitleInfo> dvbSubtitleInfos;
public final byte[] descriptorBytes; public final byte[] descriptorBytes;
@ -74,16 +116,19 @@ public interface TsPayloadReader {
* @param streamType The type of the stream as defined by the {@link TsExtractor}{@code * @param streamType The type of the stream as defined by the {@link TsExtractor}{@code
* .TS_STREAM_TYPE_*}. * .TS_STREAM_TYPE_*}.
* @param language The language of the stream, as defined by ISO/IEC 13818-1, section 2.6.18. * @param language The language of the stream, as defined by ISO/IEC 13818-1, section 2.6.18.
* @param audioType The audio type of the stream, as defined by ISO/IEC 13818-1, section 2.6.18.
* @param dvbSubtitleInfos Information about DVB subtitles associated to the stream. * @param dvbSubtitleInfos Information about DVB subtitles associated to the stream.
* @param descriptorBytes The descriptor bytes associated to the stream. * @param descriptorBytes The descriptor bytes associated to the stream.
*/ */
public EsInfo( public EsInfo(
int streamType, int streamType,
@Nullable String language, @Nullable String language,
@AudioType int audioType,
@Nullable List<DvbSubtitleInfo> dvbSubtitleInfos, @Nullable List<DvbSubtitleInfo> dvbSubtitleInfos,
byte[] descriptorBytes) { byte[] descriptorBytes) {
this.streamType = streamType; this.streamType = streamType;
this.language = language; this.language = language;
this.audioType = audioType;
this.dvbSubtitleInfos = this.dvbSubtitleInfos =
dvbSubtitleInfos == null dvbSubtitleInfos == null
? Collections.emptyList() ? Collections.emptyList()

View File

@ -237,6 +237,14 @@ public final class TsExtractorTest {
simulationConfig); simulationConfig);
} }
@Test
public void streamWithAudioTypeOfMpegAudio() throws Exception {
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_mpeg_audio_with_audio_type.ts",
simulationConfig);
}
@Test @Test
public void customPesReader() throws Exception { public void customPesReader() throws Exception {
CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false); CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false);

View File

@ -0,0 +1,188 @@
seekMap:
isSeekable = true
duration = 720000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(360000) = [[timeUs=360000, position=4544]]
getPosition(720000) = [[timeUs=720000, position=9277]]
numberOfTracks = 1
track 256:
total output bytes = 8064
sample count = 42
format 0:
id = 1/256
sampleMimeType = audio/mpeg-L2
maxInputSize = 4096
channelCount = 1
sampleRate = 48000
roleFlags = [describes-video]
language = en
sample 0:
time = 0
flags = 1
data = length 192, hash 6EF2DD4B
sample 1:
time = 24000
flags = 1
data = length 192, hash 6EF2DD4B
sample 2:
time = 48000
flags = 1
data = length 192, hash 6EF2DD4B
sample 3:
time = 72000
flags = 1
data = length 192, hash 6EF2DD4B
sample 4:
time = 96000
flags = 1
data = length 192, hash 6EF2DD4B
sample 5:
time = 120000
flags = 1
data = length 192, hash 6EF2DD4B
sample 6:
time = 144000
flags = 1
data = length 192, hash 6EF2DD4B
sample 7:
time = 168000
flags = 1
data = length 192, hash 6EF2DD4B
sample 8:
time = 192000
flags = 1
data = length 192, hash 6EF2DD4B
sample 9:
time = 216000
flags = 1
data = length 192, hash 6EF2DD4B
sample 10:
time = 240000
flags = 1
data = length 192, hash 6EF2DD4B
sample 11:
time = 264000
flags = 1
data = length 192, hash 6EF2DD4B
sample 12:
time = 288000
flags = 1
data = length 192, hash 6EF2DD4B
sample 13:
time = 312000
flags = 1
data = length 192, hash 6EF2DD4B
sample 14:
time = 336000
flags = 1
data = length 192, hash 6EF2DD4B
sample 15:
time = 360000
flags = 1
data = length 192, hash 6EF2DD4B
sample 16:
time = 384000
flags = 1
data = length 192, hash 6EF2DD4B
sample 17:
time = 408000
flags = 1
data = length 192, hash 6EF2DD4B
sample 18:
time = 432000
flags = 1
data = length 192, hash 6EF2DD4B
sample 19:
time = 456000
flags = 1
data = length 192, hash 6EF2DD4B
sample 20:
time = 480000
flags = 1
data = length 192, hash 6EF2DD4B
sample 21:
time = 504000
flags = 1
data = length 192, hash 6EF2DD4B
sample 22:
time = 528000
flags = 1
data = length 192, hash 6EF2DD4B
sample 23:
time = 552000
flags = 1
data = length 192, hash 6EF2DD4B
sample 24:
time = 576000
flags = 1
data = length 192, hash 6EF2DD4B
sample 25:
time = 600000
flags = 1
data = length 192, hash 6EF2DD4B
sample 26:
time = 624000
flags = 1
data = length 192, hash 6EF2DD4B
sample 27:
time = 648000
flags = 1
data = length 192, hash 6EF2DD4B
sample 28:
time = 672000
flags = 1
data = length 192, hash 6EF2DD4B
sample 29:
time = 696000
flags = 1
data = length 192, hash 6EF2DD4B
sample 30:
time = 720000
flags = 1
data = length 192, hash 6EF2DD4B
sample 31:
time = 744000
flags = 1
data = length 192, hash 6EF2DD4B
sample 32:
time = 768000
flags = 1
data = length 192, hash 6EF2DD4B
sample 33:
time = 792000
flags = 1
data = length 192, hash 6EF2DD4B
sample 34:
time = 816000
flags = 1
data = length 192, hash 6EF2DD4B
sample 35:
time = 840000
flags = 1
data = length 192, hash 6EF2DD4B
sample 36:
time = 864000
flags = 1
data = length 192, hash 6EF2DD4B
sample 37:
time = 888000
flags = 1
data = length 192, hash 6EF2DD4B
sample 38:
time = 912000
flags = 1
data = length 192, hash 6EF2DD4B
sample 39:
time = 936000
flags = 1
data = length 192, hash 6EF2DD4B
sample 40:
time = 960000
flags = 1
data = length 192, hash 6EF2DD4B
sample 41:
time = 984000
flags = 1
data = length 192, hash 6EF2DD4B
tracksEnded = true

View File

@ -0,0 +1,188 @@
seekMap:
isSeekable = true
duration = 720000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(360000) = [[timeUs=360000, position=4544]]
getPosition(720000) = [[timeUs=720000, position=9277]]
numberOfTracks = 1
track 256:
total output bytes = 8064
sample count = 42
format 0:
id = 1/256
sampleMimeType = audio/mpeg-L2
maxInputSize = 4096
channelCount = 1
sampleRate = 48000
roleFlags = [describes-video]
language = en
sample 0:
time = 0
flags = 1
data = length 192, hash 6EF2DD4B
sample 1:
time = 24000
flags = 1
data = length 192, hash 6EF2DD4B
sample 2:
time = 48000
flags = 1
data = length 192, hash 6EF2DD4B
sample 3:
time = 72000
flags = 1
data = length 192, hash 6EF2DD4B
sample 4:
time = 96000
flags = 1
data = length 192, hash 6EF2DD4B
sample 5:
time = 120000
flags = 1
data = length 192, hash 6EF2DD4B
sample 6:
time = 144000
flags = 1
data = length 192, hash 6EF2DD4B
sample 7:
time = 168000
flags = 1
data = length 192, hash 6EF2DD4B
sample 8:
time = 192000
flags = 1
data = length 192, hash 6EF2DD4B
sample 9:
time = 216000
flags = 1
data = length 192, hash 6EF2DD4B
sample 10:
time = 240000
flags = 1
data = length 192, hash 6EF2DD4B
sample 11:
time = 264000
flags = 1
data = length 192, hash 6EF2DD4B
sample 12:
time = 288000
flags = 1
data = length 192, hash 6EF2DD4B
sample 13:
time = 312000
flags = 1
data = length 192, hash 6EF2DD4B
sample 14:
time = 336000
flags = 1
data = length 192, hash 6EF2DD4B
sample 15:
time = 360000
flags = 1
data = length 192, hash 6EF2DD4B
sample 16:
time = 384000
flags = 1
data = length 192, hash 6EF2DD4B
sample 17:
time = 408000
flags = 1
data = length 192, hash 6EF2DD4B
sample 18:
time = 432000
flags = 1
data = length 192, hash 6EF2DD4B
sample 19:
time = 456000
flags = 1
data = length 192, hash 6EF2DD4B
sample 20:
time = 480000
flags = 1
data = length 192, hash 6EF2DD4B
sample 21:
time = 504000
flags = 1
data = length 192, hash 6EF2DD4B
sample 22:
time = 528000
flags = 1
data = length 192, hash 6EF2DD4B
sample 23:
time = 552000
flags = 1
data = length 192, hash 6EF2DD4B
sample 24:
time = 576000
flags = 1
data = length 192, hash 6EF2DD4B
sample 25:
time = 600000
flags = 1
data = length 192, hash 6EF2DD4B
sample 26:
time = 624000
flags = 1
data = length 192, hash 6EF2DD4B
sample 27:
time = 648000
flags = 1
data = length 192, hash 6EF2DD4B
sample 28:
time = 672000
flags = 1
data = length 192, hash 6EF2DD4B
sample 29:
time = 696000
flags = 1
data = length 192, hash 6EF2DD4B
sample 30:
time = 720000
flags = 1
data = length 192, hash 6EF2DD4B
sample 31:
time = 744000
flags = 1
data = length 192, hash 6EF2DD4B
sample 32:
time = 768000
flags = 1
data = length 192, hash 6EF2DD4B
sample 33:
time = 792000
flags = 1
data = length 192, hash 6EF2DD4B
sample 34:
time = 816000
flags = 1
data = length 192, hash 6EF2DD4B
sample 35:
time = 840000
flags = 1
data = length 192, hash 6EF2DD4B
sample 36:
time = 864000
flags = 1
data = length 192, hash 6EF2DD4B
sample 37:
time = 888000
flags = 1
data = length 192, hash 6EF2DD4B
sample 38:
time = 912000
flags = 1
data = length 192, hash 6EF2DD4B
sample 39:
time = 936000
flags = 1
data = length 192, hash 6EF2DD4B
sample 40:
time = 960000
flags = 1
data = length 192, hash 6EF2DD4B
sample 41:
time = 984000
flags = 1
data = length 192, hash 6EF2DD4B
tracksEnded = true

View File

@ -0,0 +1,128 @@
seekMap:
isSeekable = true
duration = 720000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(360000) = [[timeUs=360000, position=4544]]
getPosition(720000) = [[timeUs=720000, position=9277]]
numberOfTracks = 1
track 256:
total output bytes = 5184
sample count = 27
format 0:
id = 1/256
sampleMimeType = audio/mpeg-L2
maxInputSize = 4096
channelCount = 1
sampleRate = 48000
roleFlags = [describes-video]
language = en
sample 0:
time = 360000
flags = 1
data = length 192, hash 6EF2DD4B
sample 1:
time = 384000
flags = 1
data = length 192, hash 6EF2DD4B
sample 2:
time = 408000
flags = 1
data = length 192, hash 6EF2DD4B
sample 3:
time = 432000
flags = 1
data = length 192, hash 6EF2DD4B
sample 4:
time = 456000
flags = 1
data = length 192, hash 6EF2DD4B
sample 5:
time = 480000
flags = 1
data = length 192, hash 6EF2DD4B
sample 6:
time = 504000
flags = 1
data = length 192, hash 6EF2DD4B
sample 7:
time = 528000
flags = 1
data = length 192, hash 6EF2DD4B
sample 8:
time = 552000
flags = 1
data = length 192, hash 6EF2DD4B
sample 9:
time = 576000
flags = 1
data = length 192, hash 6EF2DD4B
sample 10:
time = 600000
flags = 1
data = length 192, hash 6EF2DD4B
sample 11:
time = 624000
flags = 1
data = length 192, hash 6EF2DD4B
sample 12:
time = 648000
flags = 1
data = length 192, hash 6EF2DD4B
sample 13:
time = 672000
flags = 1
data = length 192, hash 6EF2DD4B
sample 14:
time = 696000
flags = 1
data = length 192, hash 6EF2DD4B
sample 15:
time = 720000
flags = 1
data = length 192, hash 6EF2DD4B
sample 16:
time = 744000
flags = 1
data = length 192, hash 6EF2DD4B
sample 17:
time = 768000
flags = 1
data = length 192, hash 6EF2DD4B
sample 18:
time = 792000
flags = 1
data = length 192, hash 6EF2DD4B
sample 19:
time = 816000
flags = 1
data = length 192, hash 6EF2DD4B
sample 20:
time = 840000
flags = 1
data = length 192, hash 6EF2DD4B
sample 21:
time = 864000
flags = 1
data = length 192, hash 6EF2DD4B
sample 22:
time = 888000
flags = 1
data = length 192, hash 6EF2DD4B
sample 23:
time = 912000
flags = 1
data = length 192, hash 6EF2DD4B
sample 24:
time = 936000
flags = 1
data = length 192, hash 6EF2DD4B
sample 25:
time = 960000
flags = 1
data = length 192, hash 6EF2DD4B
sample 26:
time = 984000
flags = 1
data = length 192, hash 6EF2DD4B
tracksEnded = true

View File

@ -0,0 +1,20 @@
seekMap:
isSeekable = true
duration = 720000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(360000) = [[timeUs=360000, position=4544]]
getPosition(720000) = [[timeUs=720000, position=9277]]
numberOfTracks = 1
track 256:
total output bytes = 0
sample count = 0
format 0:
id = 1/256
sampleMimeType = audio/mpeg-L2
maxInputSize = 4096
channelCount = 1
sampleRate = 48000
roleFlags = [describes-video]
language = en
tracksEnded = true

View File

@ -0,0 +1,185 @@
seekMap:
isSeekable = false
duration = UNSET TIME
getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 1
track 256:
total output bytes = 8064
sample count = 42
format 0:
id = 1/256
sampleMimeType = audio/mpeg-L2
maxInputSize = 4096
channelCount = 1
sampleRate = 48000
roleFlags = [describes-video]
language = en
sample 0:
time = 0
flags = 1
data = length 192, hash 6EF2DD4B
sample 1:
time = 24000
flags = 1
data = length 192, hash 6EF2DD4B
sample 2:
time = 48000
flags = 1
data = length 192, hash 6EF2DD4B
sample 3:
time = 72000
flags = 1
data = length 192, hash 6EF2DD4B
sample 4:
time = 96000
flags = 1
data = length 192, hash 6EF2DD4B
sample 5:
time = 120000
flags = 1
data = length 192, hash 6EF2DD4B
sample 6:
time = 144000
flags = 1
data = length 192, hash 6EF2DD4B
sample 7:
time = 168000
flags = 1
data = length 192, hash 6EF2DD4B
sample 8:
time = 192000
flags = 1
data = length 192, hash 6EF2DD4B
sample 9:
time = 216000
flags = 1
data = length 192, hash 6EF2DD4B
sample 10:
time = 240000
flags = 1
data = length 192, hash 6EF2DD4B
sample 11:
time = 264000
flags = 1
data = length 192, hash 6EF2DD4B
sample 12:
time = 288000
flags = 1
data = length 192, hash 6EF2DD4B
sample 13:
time = 312000
flags = 1
data = length 192, hash 6EF2DD4B
sample 14:
time = 336000
flags = 1
data = length 192, hash 6EF2DD4B
sample 15:
time = 360000
flags = 1
data = length 192, hash 6EF2DD4B
sample 16:
time = 384000
flags = 1
data = length 192, hash 6EF2DD4B
sample 17:
time = 408000
flags = 1
data = length 192, hash 6EF2DD4B
sample 18:
time = 432000
flags = 1
data = length 192, hash 6EF2DD4B
sample 19:
time = 456000
flags = 1
data = length 192, hash 6EF2DD4B
sample 20:
time = 480000
flags = 1
data = length 192, hash 6EF2DD4B
sample 21:
time = 504000
flags = 1
data = length 192, hash 6EF2DD4B
sample 22:
time = 528000
flags = 1
data = length 192, hash 6EF2DD4B
sample 23:
time = 552000
flags = 1
data = length 192, hash 6EF2DD4B
sample 24:
time = 576000
flags = 1
data = length 192, hash 6EF2DD4B
sample 25:
time = 600000
flags = 1
data = length 192, hash 6EF2DD4B
sample 26:
time = 624000
flags = 1
data = length 192, hash 6EF2DD4B
sample 27:
time = 648000
flags = 1
data = length 192, hash 6EF2DD4B
sample 28:
time = 672000
flags = 1
data = length 192, hash 6EF2DD4B
sample 29:
time = 696000
flags = 1
data = length 192, hash 6EF2DD4B
sample 30:
time = 720000
flags = 1
data = length 192, hash 6EF2DD4B
sample 31:
time = 744000
flags = 1
data = length 192, hash 6EF2DD4B
sample 32:
time = 768000
flags = 1
data = length 192, hash 6EF2DD4B
sample 33:
time = 792000
flags = 1
data = length 192, hash 6EF2DD4B
sample 34:
time = 816000
flags = 1
data = length 192, hash 6EF2DD4B
sample 35:
time = 840000
flags = 1
data = length 192, hash 6EF2DD4B
sample 36:
time = 864000
flags = 1
data = length 192, hash 6EF2DD4B
sample 37:
time = 888000
flags = 1
data = length 192, hash 6EF2DD4B
sample 38:
time = 912000
flags = 1
data = length 192, hash 6EF2DD4B
sample 39:
time = 936000
flags = 1
data = length 192, hash 6EF2DD4B
sample 40:
time = 960000
flags = 1
data = length 192, hash 6EF2DD4B
sample 41:
time = 984000
flags = 1
data = length 192, hash 6EF2DD4B
tracksEnded = true