From f6bfd8ec2e676c9313b54bc9eaad97342893abd9 Mon Sep 17 00:00:00 2001 From: FineFindus Date: Fri, 31 Oct 2025 16:42:51 +0100 Subject: [PATCH 1/3] [YouTube] support extracting DRC audio Implements support for checking whether an extracted audio format is using dynamic range compression (DRC). Ref: https://en.wikipedia.org/wiki/Dynamic_range_compression --- .../extractor/services/youtube/ItagItem.java | 28 +++++++++++++++++++ .../extractors/YoutubeStreamExtractor.java | 1 + 2 files changed, 29 insertions(+) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java index 8c63d8a6ba..0cba49c152 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java @@ -266,6 +266,7 @@ public MediaFormat getMediaFormat() { private AudioTrackType audioTrackType; @Nullable private Locale audioLocale; + private boolean isDrc; public int getBitrate() { return bitrate; @@ -647,4 +648,31 @@ public Locale getAudioLocale() { public void setAudioLocale(@Nullable final Locale audioLocale) { this.audioLocale = audioLocale; } + + /** + * Whether the audio is using dynamic range compression (DRC). + * + *

+ * https://en.wikipedia.org/wiki/Dynamic_range_compression + *

+ * + * @return whether the audio is using DRC + */ + public Boolean isDrc() { + return isDrc; + } + + /** + * Sets whether the audio is using dynamic range compression (DRC). + * + *

+ * https://en.wikipedia.org/wiki/Dynamic_range_compression + *

+ * + * @param isDrc whether the audio has DRC applied + */ + public void setIsDrc(final Boolean isDrc) { + this.isDrc = isDrc; + } + } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java index ba23eb6463..0a95fc3f91 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java @@ -1421,6 +1421,7 @@ private ItagInfo buildAndAddItagInfoToList( itagItem.setIndexEnd(Integer.parseInt(indexRange.getString("end", "-1"))); itagItem.setQuality(formatData.getString("quality")); itagItem.setCodec(codec); + itagItem.setIsDrc(formatData.getBoolean("isDrc", false)); if (streamType == StreamType.LIVE_STREAM || streamType == StreamType.POST_LIVE_STREAM) { itagItem.setTargetDurationSec(formatData.getInt("targetDurationSec")); From a431a90025a9e10bd4bbb316362e9d8add101e10 Mon Sep 17 00:00:00 2001 From: FineFindus Date: Mon, 3 Nov 2025 19:02:51 +0100 Subject: [PATCH 2/3] [YouTube] Extract `lastModified` format data Extracts the `lastModified` field of format, which is need for initiating SABR playback. --- .../extractor/services/youtube/ItagItem.java | 26 +++++++++++++++++++ .../extractors/YoutubeStreamExtractor.java | 1 + 2 files changed, 27 insertions(+) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java index 0cba49c152..4d3a51cee7 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java @@ -134,6 +134,7 @@ public static ItagItem getItag(final int itagId) throws ParsingException { public static final int AUDIO_CHANNELS_NOT_APPLICABLE_OR_UNKNOWN = -1; public static final long CONTENT_LENGTH_UNKNOWN = -1; public static final long APPROX_DURATION_MS_UNKNOWN = -1; + public static final long LAST_MODIFIED_UNKOWN = -1; /*////////////////////////////////////////////////////////////////////////// // Constructors and misc @@ -267,6 +268,7 @@ public MediaFormat getMediaFormat() { @Nullable private Locale audioLocale; private boolean isDrc; + private long lastModified; public int getBitrate() { return bitrate; @@ -675,4 +677,28 @@ public void setIsDrc(final Boolean isDrc) { this.isDrc = isDrc; } + + /** + * When the stream was last modified. + * + *

+ * If the timestamp is unknown, {@link #LAST_MODIFIED_UNKOWN} is returned. + *

+ * + * @return unix timestamp of when the stream was last modified or + * {@link #LAST_MODIFIED_UNKOWN} if the timestamp is unknown. + */ + public long getLastModified() { + return lastModified; + } + + /** + * Sets the timestamp when the stream was last modified. + * + * @param lastModified unix timestamp of when the stream was last modified + */ + public void setLastModified(final long lastModified) { + this.lastModified = lastModified; + } + } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java index 0a95fc3f91..95f867dae9 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java @@ -1422,6 +1422,7 @@ private ItagInfo buildAndAddItagInfoToList( itagItem.setQuality(formatData.getString("quality")); itagItem.setCodec(codec); itagItem.setIsDrc(formatData.getBoolean("isDrc", false)); + itagItem.setLastModified(Long.parseLong(formatData.getString("lastModified", "-1"))); if (streamType == StreamType.LIVE_STREAM || streamType == StreamType.POST_LIVE_STREAM) { itagItem.setTargetDurationSec(formatData.getInt("targetDurationSec")); From 0617c8834c4988b5bd657ccc275e3efbde13f373 Mon Sep 17 00:00:00 2001 From: FineFindus Date: Mon, 3 Nov 2025 20:29:16 +0100 Subject: [PATCH 3/3] [YouTube] Extract `xtags` of formats Extracts the `xtags` field, which encodes additional data of the format, such as if the format is using dynamic range compression. This is required for using SABR. --- .../extractor/services/youtube/ItagItem.java | 23 +++++++++++++++++++ .../extractors/YoutubeStreamExtractor.java | 1 + 2 files changed, 24 insertions(+) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java index 4d3a51cee7..79858ae591 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/ItagItem.java @@ -269,6 +269,7 @@ public MediaFormat getMediaFormat() { private Locale audioLocale; private boolean isDrc; private long lastModified; + private String xtags; public int getBitrate() { return bitrate; @@ -701,4 +702,26 @@ public void setLastModified(final long lastModified) { this.lastModified = lastModified; } + /** + * Extra tags about the stream. + * + *

+ * Contains a Base64 encoded protobuf key-value list of additional tags for the stream, + * such as whether the stream is using {@link #isDrc()}. + *

+ * + * @return Base64-encoded extra tags. + */ + public String getXtags() { + return xtags; + } + + /** + * Sets extra tags of the stream. + * + * @param xtags extra tags of the stream + */ + public void setXtags(final String xtags) { + this.xtags = xtags; + } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java index 95f867dae9..b89823a985 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/youtube/extractors/YoutubeStreamExtractor.java @@ -1423,6 +1423,7 @@ private ItagInfo buildAndAddItagInfoToList( itagItem.setCodec(codec); itagItem.setIsDrc(formatData.getBoolean("isDrc", false)); itagItem.setLastModified(Long.parseLong(formatData.getString("lastModified", "-1"))); + itagItem.setXtags(formatData.getString("xtags")); if (streamType == StreamType.LIVE_STREAM || streamType == StreamType.POST_LIVE_STREAM) { itagItem.setTargetDurationSec(formatData.getInt("targetDurationSec"));