mirror of
https://github.com/androidx/media.git
synced 2025-05-05 06:30:24 +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) {
|
||||
if (MimeTypes.isAudio(containerMimeType)) {
|
||||
return getAudioMediaMimeType(codecs);
|
||||
return MimeTypes.getAudioMediaMimeType(codecs);
|
||||
} else if (MimeTypes.isVideo(containerMimeType)) {
|
||||
return getVideoMediaMimeType(codecs);
|
||||
return MimeTypes.getVideoMediaMimeType(codecs);
|
||||
} else if (mimeTypeIsRawText(containerMimeType)) {
|
||||
return containerMimeType;
|
||||
} else if (MimeTypes.APPLICATION_MP4.equals(containerMimeType)) {
|
||||
@ -651,64 +651,6 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
||||
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.
|
||||
*
|
||||
|
@ -68,11 +68,11 @@ import java.util.List;
|
||||
// Scratch variables to avoid allocations.
|
||||
private final ParsableByteArray seiWrapper;
|
||||
|
||||
public H264Reader(TrackOutput output, SeiReader seiReader, boolean idrKeyframesOnly) {
|
||||
public H264Reader(TrackOutput output, SeiReader seiReader, boolean allowNonIdrKeyframes) {
|
||||
super(output);
|
||||
this.seiReader = seiReader;
|
||||
prefixFlags = new boolean[3];
|
||||
ifrParserBuffer = (idrKeyframesOnly) ? null : new IfrParserBuffer();
|
||||
ifrParserBuffer = allowNonIdrKeyframes ? new IfrParserBuffer() : null;
|
||||
sps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SPS, 128);
|
||||
pps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_PPS, 128);
|
||||
sei = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SEI, 128);
|
||||
|
@ -37,6 +37,10 @@ import java.io.IOException;
|
||||
*/
|
||||
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 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 final PtsTimestampAdjuster ptsTimestampAdjuster;
|
||||
private final boolean idrKeyframesOnly;
|
||||
private final int workaroundFlags;
|
||||
private final ParsableByteArray tsPacketBuffer;
|
||||
private final ParsableBitArray tsScratch;
|
||||
/* package */ final SparseArray<TsPayloadReader> tsPayloadReaders; // Indexed by pid
|
||||
@ -76,12 +80,12 @@ public final class TsExtractor implements Extractor {
|
||||
}
|
||||
|
||||
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.idrKeyframesOnly = idrKeyframesOnly;
|
||||
this.workaroundFlags = workaroundFlags;
|
||||
tsPacketBuffer = new ParsableByteArray(TS_PACKET_SIZE);
|
||||
tsScratch = new ParsableBitArray(new byte[3]);
|
||||
tsPayloadReaders = new SparseArray<>();
|
||||
@ -338,8 +342,8 @@ public final class TsExtractor implements Extractor {
|
||||
pesPayloadReader = new MpegAudioReader(output.track(TS_STREAM_TYPE_MPA_LSF));
|
||||
break;
|
||||
case TS_STREAM_TYPE_AAC:
|
||||
pesPayloadReader = new AdtsReader(output.track(TS_STREAM_TYPE_AAC),
|
||||
new DummyTrackOutput());
|
||||
pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_AAC_STREAM) != 0 ? null
|
||||
: new AdtsReader(output.track(TS_STREAM_TYPE_AAC), new DummyTrackOutput());
|
||||
break;
|
||||
case TS_STREAM_TYPE_AC3:
|
||||
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));
|
||||
break;
|
||||
case TS_STREAM_TYPE_H264:
|
||||
pesPayloadReader = new H264Reader(output.track(TS_STREAM_TYPE_H264),
|
||||
new SeiReader(output.track(TS_STREAM_TYPE_EIA608)), idrKeyframesOnly);
|
||||
pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_H264_STREAM) != 0 ? null
|
||||
: new H264Reader(output.track(TS_STREAM_TYPE_H264),
|
||||
new SeiReader(output.track(TS_STREAM_TYPE_EIA608)),
|
||||
(workaroundFlags & WORKAROUND_ALLOW_NON_IDR_KEYFRAMES) != 0);
|
||||
break;
|
||||
case 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.
|
||||
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,
|
||||
switchingVariantSpliced);
|
||||
} else {
|
||||
|
@ -107,6 +107,65 @@ public final class MimeTypes {
|
||||
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}.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user