mirror of
https://github.com/androidx/media.git
synced 2025-05-05 22:50:57 +08:00
HLS: Ignore AAC/H264 streams if we know they don't exist.
This is needed to support fully demuxed audio in HLS. For the sample I have the video (only) variant still declares an AAC stream. I suspect there's at least one toolchain out there that hardcodes H264 and AAC streams in TS output. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=117224224
This commit is contained in:
parent
9cfff0b028
commit
d9d6b8540d
@ -636,9 +636,9 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
*/
|
*/
|
||||||
private static String getSampleMimeType(String containerMimeType, String codecs) {
|
private static String getSampleMimeType(String containerMimeType, String codecs) {
|
||||||
if (MimeTypes.isAudio(containerMimeType)) {
|
if (MimeTypes.isAudio(containerMimeType)) {
|
||||||
return getAudioMediaMimeType(codecs);
|
return MimeTypes.getAudioMediaMimeType(codecs);
|
||||||
} else if (MimeTypes.isVideo(containerMimeType)) {
|
} else if (MimeTypes.isVideo(containerMimeType)) {
|
||||||
return getVideoMediaMimeType(codecs);
|
return MimeTypes.getVideoMediaMimeType(codecs);
|
||||||
} else if (mimeTypeIsRawText(containerMimeType)) {
|
} else if (mimeTypeIsRawText(containerMimeType)) {
|
||||||
return containerMimeType;
|
return containerMimeType;
|
||||||
} else if (MimeTypes.APPLICATION_MP4.equals(containerMimeType)) {
|
} else if (MimeTypes.APPLICATION_MP4.equals(containerMimeType)) {
|
||||||
@ -651,64 +651,6 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a video sample mimeType from a codecs attribute.
|
|
||||||
*
|
|
||||||
* @param codecs The codecs attribute.
|
|
||||||
* @return The derived video mimeType, or null if it could not be derived.
|
|
||||||
*/
|
|
||||||
private static String getVideoMediaMimeType(String codecs) {
|
|
||||||
if (codecs == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String[] codecList = codecs.split(",");
|
|
||||||
for (String codec : codecList) {
|
|
||||||
codec = codec.trim();
|
|
||||||
if (codec.startsWith("avc1") || codec.startsWith("avc3")) {
|
|
||||||
return MimeTypes.VIDEO_H264;
|
|
||||||
} else if (codec.startsWith("hev1") || codec.startsWith("hvc1")) {
|
|
||||||
return MimeTypes.VIDEO_H265;
|
|
||||||
} else if (codec.startsWith("vp9")) {
|
|
||||||
return MimeTypes.VIDEO_VP9;
|
|
||||||
} else if (codec.startsWith("vp8")) {
|
|
||||||
return MimeTypes.VIDEO_VP8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Derives a audio sample mimeType from a codecs attribute.
|
|
||||||
*
|
|
||||||
* @param codecs The codecs attribute.
|
|
||||||
* @return The derived audio mimeType, or null if it could not be derived.
|
|
||||||
*/
|
|
||||||
private static String getAudioMediaMimeType(String codecs) {
|
|
||||||
if (codecs == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String[] codecList = codecs.split(",");
|
|
||||||
for (String codec : codecList) {
|
|
||||||
codec = codec.trim();
|
|
||||||
if (codec.startsWith("mp4a")) {
|
|
||||||
return MimeTypes.AUDIO_AAC;
|
|
||||||
} else if (codec.startsWith("ac-3") || codec.startsWith("dac3")) {
|
|
||||||
return MimeTypes.AUDIO_AC3;
|
|
||||||
} else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) {
|
|
||||||
return MimeTypes.AUDIO_E_AC3;
|
|
||||||
} else if (codec.startsWith("dtsc") || codec.startsWith("dtse")) {
|
|
||||||
return MimeTypes.AUDIO_DTS;
|
|
||||||
} else if (codec.startsWith("dtsh") || codec.startsWith("dtsl")) {
|
|
||||||
return MimeTypes.AUDIO_DTS_HD;
|
|
||||||
} else if (codec.startsWith("opus")) {
|
|
||||||
return MimeTypes.AUDIO_OPUS;
|
|
||||||
} else if (codec.startsWith("vorbis")) {
|
|
||||||
return MimeTypes.AUDIO_VORBIS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether a mimeType is a text sample mimeType.
|
* Returns whether a mimeType is a text sample mimeType.
|
||||||
*
|
*
|
||||||
|
@ -68,11 +68,11 @@ import java.util.List;
|
|||||||
// Scratch variables to avoid allocations.
|
// Scratch variables to avoid allocations.
|
||||||
private final ParsableByteArray seiWrapper;
|
private final ParsableByteArray seiWrapper;
|
||||||
|
|
||||||
public H264Reader(TrackOutput output, SeiReader seiReader, boolean idrKeyframesOnly) {
|
public H264Reader(TrackOutput output, SeiReader seiReader, boolean allowNonIdrKeyframes) {
|
||||||
super(output);
|
super(output);
|
||||||
this.seiReader = seiReader;
|
this.seiReader = seiReader;
|
||||||
prefixFlags = new boolean[3];
|
prefixFlags = new boolean[3];
|
||||||
ifrParserBuffer = (idrKeyframesOnly) ? null : new IfrParserBuffer();
|
ifrParserBuffer = allowNonIdrKeyframes ? new IfrParserBuffer() : null;
|
||||||
sps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SPS, 128);
|
sps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SPS, 128);
|
||||||
pps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_PPS, 128);
|
pps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_PPS, 128);
|
||||||
sei = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SEI, 128);
|
sei = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SEI, 128);
|
||||||
|
@ -37,6 +37,10 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public final class TsExtractor implements Extractor {
|
public final class TsExtractor implements Extractor {
|
||||||
|
|
||||||
|
public static final int WORKAROUND_ALLOW_NON_IDR_KEYFRAMES = 1;
|
||||||
|
public static final int WORKAROUND_IGNORE_AAC_STREAM = 2;
|
||||||
|
public static final int WORKAROUND_IGNORE_H264_STREAM = 4;
|
||||||
|
|
||||||
private static final String TAG = "TsExtractor";
|
private static final String TAG = "TsExtractor";
|
||||||
|
|
||||||
private static final int TS_PACKET_SIZE = 188;
|
private static final int TS_PACKET_SIZE = 188;
|
||||||
@ -61,7 +65,7 @@ public final class TsExtractor implements Extractor {
|
|||||||
private static final long HEVC_FORMAT_IDENTIFIER = Util.getIntegerCodeForString("HEVC");
|
private static final long HEVC_FORMAT_IDENTIFIER = Util.getIntegerCodeForString("HEVC");
|
||||||
|
|
||||||
private final PtsTimestampAdjuster ptsTimestampAdjuster;
|
private final PtsTimestampAdjuster ptsTimestampAdjuster;
|
||||||
private final boolean idrKeyframesOnly;
|
private final int workaroundFlags;
|
||||||
private final ParsableByteArray tsPacketBuffer;
|
private final ParsableByteArray tsPacketBuffer;
|
||||||
private final ParsableBitArray tsScratch;
|
private final ParsableBitArray tsScratch;
|
||||||
/* package */ final SparseArray<TsPayloadReader> tsPayloadReaders; // Indexed by pid
|
/* package */ final SparseArray<TsPayloadReader> tsPayloadReaders; // Indexed by pid
|
||||||
@ -76,12 +80,12 @@ public final class TsExtractor implements Extractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TsExtractor(PtsTimestampAdjuster ptsTimestampAdjuster) {
|
public TsExtractor(PtsTimestampAdjuster ptsTimestampAdjuster) {
|
||||||
this(ptsTimestampAdjuster, true);
|
this(ptsTimestampAdjuster, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TsExtractor(PtsTimestampAdjuster ptsTimestampAdjuster, boolean idrKeyframesOnly) {
|
public TsExtractor(PtsTimestampAdjuster ptsTimestampAdjuster, int workaroundFlags) {
|
||||||
this.ptsTimestampAdjuster = ptsTimestampAdjuster;
|
this.ptsTimestampAdjuster = ptsTimestampAdjuster;
|
||||||
this.idrKeyframesOnly = idrKeyframesOnly;
|
this.workaroundFlags = workaroundFlags;
|
||||||
tsPacketBuffer = new ParsableByteArray(TS_PACKET_SIZE);
|
tsPacketBuffer = new ParsableByteArray(TS_PACKET_SIZE);
|
||||||
tsScratch = new ParsableBitArray(new byte[3]);
|
tsScratch = new ParsableBitArray(new byte[3]);
|
||||||
tsPayloadReaders = new SparseArray<>();
|
tsPayloadReaders = new SparseArray<>();
|
||||||
@ -338,8 +342,8 @@ public final class TsExtractor implements Extractor {
|
|||||||
pesPayloadReader = new MpegAudioReader(output.track(TS_STREAM_TYPE_MPA_LSF));
|
pesPayloadReader = new MpegAudioReader(output.track(TS_STREAM_TYPE_MPA_LSF));
|
||||||
break;
|
break;
|
||||||
case TS_STREAM_TYPE_AAC:
|
case TS_STREAM_TYPE_AAC:
|
||||||
pesPayloadReader = new AdtsReader(output.track(TS_STREAM_TYPE_AAC),
|
pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_AAC_STREAM) != 0 ? null
|
||||||
new DummyTrackOutput());
|
: new AdtsReader(output.track(TS_STREAM_TYPE_AAC), new DummyTrackOutput());
|
||||||
break;
|
break;
|
||||||
case TS_STREAM_TYPE_AC3:
|
case TS_STREAM_TYPE_AC3:
|
||||||
pesPayloadReader = new Ac3Reader(output.track(TS_STREAM_TYPE_AC3), false);
|
pesPayloadReader = new Ac3Reader(output.track(TS_STREAM_TYPE_AC3), false);
|
||||||
@ -355,8 +359,10 @@ public final class TsExtractor implements Extractor {
|
|||||||
pesPayloadReader = new H262Reader(output.track(TS_STREAM_TYPE_H262));
|
pesPayloadReader = new H262Reader(output.track(TS_STREAM_TYPE_H262));
|
||||||
break;
|
break;
|
||||||
case TS_STREAM_TYPE_H264:
|
case TS_STREAM_TYPE_H264:
|
||||||
pesPayloadReader = new H264Reader(output.track(TS_STREAM_TYPE_H264),
|
pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_H264_STREAM) != 0 ? null
|
||||||
new SeiReader(output.track(TS_STREAM_TYPE_EIA608)), idrKeyframesOnly);
|
: new H264Reader(output.track(TS_STREAM_TYPE_H264),
|
||||||
|
new SeiReader(output.track(TS_STREAM_TYPE_EIA608)),
|
||||||
|
(workaroundFlags & WORKAROUND_ALLOW_NON_IDR_KEYFRAMES) != 0);
|
||||||
break;
|
break;
|
||||||
case TS_STREAM_TYPE_H265:
|
case TS_STREAM_TYPE_H265:
|
||||||
pesPayloadReader = new H265Reader(output.track(TS_STREAM_TYPE_H265),
|
pesPayloadReader = new H265Reader(output.track(TS_STREAM_TYPE_H265),
|
||||||
|
@ -462,7 +462,20 @@ public class HlsChunkSource {
|
|||||||
// The master source has yet to instantiate an adjuster for the discontinuity sequence.
|
// The master source has yet to instantiate an adjuster for the discontinuity sequence.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Extractor extractor = new TsExtractor(timestampAdjuster);
|
int workaroundFlags = 0;
|
||||||
|
String codecs = enabledVariants[selectedVariantIndex].codecs;
|
||||||
|
if (!TextUtils.isEmpty(codecs)) {
|
||||||
|
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
|
||||||
|
// exist. If we know from the codec attribute that they don't exist, then we can explicitly
|
||||||
|
// ignore them even if they're declared.
|
||||||
|
if (MimeTypes.getAudioMediaMimeType(codecs) != MimeTypes.AUDIO_AAC) {
|
||||||
|
workaroundFlags |= TsExtractor.WORKAROUND_IGNORE_AAC_STREAM;
|
||||||
|
}
|
||||||
|
if (MimeTypes.getVideoMediaMimeType(codecs) != MimeTypes.VIDEO_H264) {
|
||||||
|
workaroundFlags |= TsExtractor.WORKAROUND_IGNORE_H264_STREAM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Extractor extractor = new TsExtractor(timestampAdjuster, workaroundFlags);
|
||||||
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
|
extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor,
|
||||||
switchingVariantSpliced);
|
switchingVariantSpliced);
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,6 +107,65 @@ public final class MimeTypes {
|
|||||||
return getTopLevelType(mimeType).equals(BASE_TYPE_APPLICATION);
|
return getTopLevelType(mimeType).equals(BASE_TYPE_APPLICATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives a video sample mimeType from a codecs attribute.
|
||||||
|
*
|
||||||
|
* @param codecs The codecs attribute.
|
||||||
|
* @return The derived video mimeType, or null if it could not be derived.
|
||||||
|
*/
|
||||||
|
public static String getVideoMediaMimeType(String codecs) {
|
||||||
|
if (codecs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] codecList = codecs.split(",");
|
||||||
|
for (String codec : codecList) {
|
||||||
|
codec = codec.trim();
|
||||||
|
if (codec.startsWith("avc1") || codec.startsWith("avc3")) {
|
||||||
|
return MimeTypes.VIDEO_H264;
|
||||||
|
} else if (codec.startsWith("hev1") || codec.startsWith("hvc1")) {
|
||||||
|
return MimeTypes.VIDEO_H265;
|
||||||
|
} else if (codec.startsWith("vp9")) {
|
||||||
|
return MimeTypes.VIDEO_VP9;
|
||||||
|
} else if (codec.startsWith("vp8")) {
|
||||||
|
return MimeTypes.VIDEO_VP8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives a audio sample mimeType from a codecs attribute.
|
||||||
|
*
|
||||||
|
* @param codecs The codecs attribute.
|
||||||
|
* @return The derived audio mimeType, or null if it could not be derived.
|
||||||
|
*/
|
||||||
|
public static String getAudioMediaMimeType(String codecs) {
|
||||||
|
if (codecs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String[] codecList = codecs.split(",");
|
||||||
|
for (String codec : codecList) {
|
||||||
|
codec = codec.trim();
|
||||||
|
if (codec.startsWith("mp4a")) {
|
||||||
|
return MimeTypes.AUDIO_AAC;
|
||||||
|
} else if (codec.startsWith("ac-3") || codec.startsWith("dac3")) {
|
||||||
|
return MimeTypes.AUDIO_AC3;
|
||||||
|
} else if (codec.startsWith("ec-3") || codec.startsWith("dec3")) {
|
||||||
|
return MimeTypes.AUDIO_E_AC3;
|
||||||
|
} else if (codec.startsWith("dtsc") || codec.startsWith("dtse")) {
|
||||||
|
return MimeTypes.AUDIO_DTS;
|
||||||
|
} else if (codec.startsWith("dtsh") || codec.startsWith("dtsl")) {
|
||||||
|
return MimeTypes.AUDIO_DTS_HD;
|
||||||
|
} else if (codec.startsWith("opus")) {
|
||||||
|
return MimeTypes.AUDIO_OPUS;
|
||||||
|
} else if (codec.startsWith("vorbis")) {
|
||||||
|
return MimeTypes.AUDIO_VORBIS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the top-level type of {@code mimeType}.
|
* Returns the top-level type of {@code mimeType}.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user