diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java index a1de3d0be..53664881b 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/spoof/SpoofVideoStreamsPatch.java @@ -19,6 +19,14 @@ import app.revanced.extension.shared.spoof.requests.StreamingDataRequest; @SuppressWarnings("unused") public class SpoofVideoStreamsPatch { + + public static final class AudioStreamLanguageOverrideAvailability implements Setting.Availability { + @Override + public boolean isAvailable() { + return BaseSettings.SPOOF_VIDEO_STREAMS.get() && !preferredClient.useAuth; + } + } + /** * Domain used for internet connectivity verification. * It has an empty response body and is only used to check for a 204 response code. @@ -321,11 +329,4 @@ public class SpoofVideoStreamsPatch { return videoFormat; } - - public static final class AudioStreamLanguageOverrideAvailability implements Setting.Availability { - @Override - public boolean isAvailable() { - return BaseSettings.SPOOF_VIDEO_STREAMS.get() && !preferredClient.useAuth; - } - } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java index e9672d1ff..eaf2f3e14 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch.java @@ -8,29 +8,35 @@ import static app.revanced.extension.shared.spoof.ClientType.VISIONOS; import java.util.List; +import app.revanced.extension.shared.settings.Setting; import app.revanced.extension.shared.spoof.ClientType; import app.revanced.extension.youtube.settings.Settings; @SuppressWarnings("unused") public class SpoofVideoStreamsPatch { + public static final class SpoofClientAv1Availability implements Setting.Availability { + @Override + public boolean isAvailable() { + return Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.isAvailable() + && Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ANDROID_VR_1_43_32; + } + } + /** * Injection point. */ public static void setClientOrderToUse() { ClientType client = Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get(); - - if (Settings.FORCE_AVC_CODEC.get() && client == ANDROID_VR_1_61_48) { - // VR 1.61 uses VP9/AV1, and cannot force AVC. Use 1.43 instead. - client = ANDROID_VR_1_43_32; + if (client == ANDROID_VR_1_43_32 && Settings.SPOOF_VIDEO_STREAMS_AV1.get()) { + client = ANDROID_VR_1_61_48; } List availableClients = List.of( + ANDROID_CREATOR, ANDROID_VR_1_43_32, VISIONOS, - ANDROID_CREATOR, - ANDROID_VR_1_61_48, IPADOS); app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse( diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java index e8c21aca1..d131cfee2 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -21,6 +21,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType; import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability; import static app.revanced.extension.youtube.patches.components.PlayerFlyoutMenuItemsFilter.HideAudioFlyoutMenuAvailability; +import static app.revanced.extension.youtube.patches.spoof.SpoofVideoStreamsPatch.SpoofClientAv1Availability; import static app.revanced.extension.youtube.patches.theme.ThemePatch.SplashScreenAnimationStyle; import static app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController.SponsorBlockDuration; import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE; @@ -357,6 +358,8 @@ public class Settings extends BaseSettings { public static final BooleanSetting SPOOF_DEVICE_DIMENSIONS = new BooleanSetting("revanced_spoof_device_dimensions", FALSE, true, "revanced_spoof_device_dimensions_user_dialog_message"); public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_1_43_32, true, parent(SPOOF_VIDEO_STREAMS)); + public static final BooleanSetting SPOOF_VIDEO_STREAMS_AV1 = new BooleanSetting("revanced_spoof_video_streams_av1", FALSE, true, + "revanced_spoof_video_streams_av1_user_dialog_message", new SpoofClientAv1Availability()); public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, false, "revanced_debug_protobuffer_user_dialog_message", parent(BaseSettings.DEBUG)); @@ -524,6 +527,11 @@ public class Settings extends BaseSettings { SPOOF_APP_VERSION.resetToDefault(); } + // VR 1.61 is not selectable in the settings, and it's selected by spoof stream patch if needed. + if (SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_1_61_48) { + SPOOF_VIDEO_STREAMS_CLIENT_TYPE.resetToDefault(); + } + // RYD requires manually migrating old settings since the lack of // a "revanced_" on the old setting causes duplicate key exceptions during export. SharedPrefCategory revancedPrefs = Setting.preferences; diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java index 919aed6c6..bf66edbd4 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/SpoofStreamingDataSideEffectsPreference.java @@ -87,13 +87,9 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference { summary += '\n' + str("revanced_spoof_video_streams_about_no_stable_volume") + '\n' + str("revanced_spoof_video_streams_about_no_av1") + '\n' + str("revanced_spoof_video_streams_about_no_force_original_audio"); - case ANDROID_VR_1_43_32 -> - summary += '\n' + str("revanced_spoof_video_streams_about_no_stable_volume") - + '\n' + str("revanced_spoof_video_streams_about_no_av1"); - case ANDROID_VR_1_61_48 -> - summary = str("revanced_spoof_video_streams_about_dropped_frames") - + '\n' + summary - + '\n' + str("revanced_spoof_video_streams_about_no_stable_volume"); + // VR 1.61 is not exposed in the UI and should never be reached here. + case ANDROID_VR_1_43_32, ANDROID_VR_1_61_48 -> + summary += '\n' + str("revanced_spoof_video_streams_about_no_stable_volume"); case IPADOS -> summary = str("revanced_spoof_video_streams_about_playback_failure") + '\n' + str("revanced_spoof_video_streams_about_no_av1"); diff --git a/patches/api/patches.api b/patches/api/patches.api index 79abfd5ab..d45ee2c76 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -1026,11 +1026,6 @@ public final class app/revanced/patches/shared/misc/settings/preference/TextPref public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } -public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatchKt { - public static final fun spoofVideoStreamsPatch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch; - public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch; -} - public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt { public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt index b01f74cca..93c294480 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatch.kt @@ -1,6 +1,5 @@ package app.revanced.patches.music.misc.spoof -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch import app.revanced.patches.music.misc.extension.sharedExtensionPatch @@ -16,12 +15,13 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch;" - val spoofVideoStreamsPatch = spoofVideoStreamsPatch( - fixMediaFetchHotConfigChanges = { true }, - fixMediaFetchHotConfigAlternativeChanges = { is_8_11_or_greater && !is_8_15_or_greater }, + extensionClassDescriptor = "Lapp/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch;", + mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint, + fixMediaFetchHotConfig = { true }, + fixMediaFetchHotConfigAlternative = { is_8_11_or_greater && !is_8_15_or_greater }, fixParsePlaybackResponseFeatureFlag = { is_7_33_or_greater }, + block = { dependsOn( sharedExtensionPatch, @@ -38,6 +38,7 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( ) ) }, + executeBlock = { addResources("music", "misc.fix.playback.spoofVideoStreamsPatch") @@ -51,10 +52,5 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( ) ) ) - - musicActivityOnCreateFingerprint.method.addInstruction( - 0, - "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setClientOrderToUse()V" - ) } -) \ No newline at end of file +) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt index c03e8022c..7c1c58410 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch.kt @@ -1,5 +1,6 @@ package app.revanced.patches.shared.misc.spoof +import app.revanced.patcher.Fingerprint import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels @@ -36,11 +37,13 @@ internal const val EXTENSION_CLASS_DESCRIPTOR = private lateinit var buildRequestMethod: MutableMethod private var buildRequestMethodUrlRegister = -1 -fun spoofVideoStreamsPatch( - block: BytecodePatchBuilder.() -> Unit = {}, - fixMediaFetchHotConfigChanges: BytecodePatchBuilder.() -> Boolean = { false }, - fixMediaFetchHotConfigAlternativeChanges: BytecodePatchBuilder.() -> Boolean = { false }, +internal fun spoofVideoStreamsPatch( + extensionClassDescriptor: String, + mainActivityOnCreateFingerprint: Fingerprint, + fixMediaFetchHotConfig: BytecodePatchBuilder.() -> Boolean = { false }, + fixMediaFetchHotConfigAlternative: BytecodePatchBuilder.() -> Boolean = { false }, fixParsePlaybackResponseFeatureFlag: BytecodePatchBuilder.() -> Boolean = { false }, + block: BytecodePatchBuilder.() -> Unit = {}, executeBlock: BytecodePatchContext.() -> Unit = {}, ) = bytecodePatch( name = "Spoof video streams", @@ -53,6 +56,11 @@ fun spoofVideoStreamsPatch( execute { addResources("shared", "misc.fix.playback.spoofVideoStreamsPatch") + mainActivityOnCreateFingerprint.method.addInstruction( + 0, + "invoke-static { }, $extensionClassDescriptor->setClientOrderToUse()V" + ) + // region Enable extension helper method used by other patches patchIncludedExtensionMethodFingerprint.method.returnEarly(true) @@ -308,14 +316,14 @@ fun spoofVideoStreamsPatch( // region turn off stream config replacement feature flag. - if (fixMediaFetchHotConfigChanges()) { + if (fixMediaFetchHotConfig()) { mediaFetchHotConfigFingerprint.method.insertLiteralOverride( MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG, "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" ) } - if (fixMediaFetchHotConfigAlternativeChanges()) { + if (fixMediaFetchHotConfigAlternative()) { mediaFetchHotConfigAlternativeFingerprint.method.insertLiteralOverride( MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG, "$EXTENSION_CLASS_DESCRIPTOR->useMediaFetchHotConfigReplacement(Z)Z" diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt index 0131c9e59..2192f8016 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/spoof/SpoofVideoStreamsPatch.kt @@ -1,6 +1,5 @@ package app.revanced.patches.youtube.misc.spoof -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference @@ -16,9 +15,20 @@ import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint -private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;" - val spoofVideoStreamsPatch = spoofVideoStreamsPatch( + extensionClassDescriptor = "Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;", + mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, + fixMediaFetchHotConfig = { + is_19_34_or_greater + }, + fixMediaFetchHotConfigAlternative = { + // In 20.14 the flag was merged with 20.03 start playback flag. + is_20_10_or_greater && !is_20_14_or_greater + }, + fixParsePlaybackResponseFeatureFlag = { + is_20_03_or_greater + }, + block = { compatibleWith( "com.google.android.youtube"( @@ -35,16 +45,7 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( versionCheckPatch ) }, - fixMediaFetchHotConfigChanges = { - is_19_34_or_greater - }, - fixMediaFetchHotConfigAlternativeChanges = { - // In 20.14 the flag was merged with 20.03 start playback flag. - is_20_10_or_greater && !is_20_14_or_greater - }, - fixParsePlaybackResponseFeatureFlag = { - is_20_03_or_greater - }, + executeBlock = { addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch") @@ -61,6 +62,7 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( summaryKey = null, tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference" ), + SwitchPreference("revanced_spoof_video_streams_av1"), ListPreference( key = "revanced_spoof_video_streams_language", // Language strings are declared in Setting patch. @@ -72,10 +74,5 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch( ) ) ) - - mainActivityOnCreateFingerprint.method.addInstruction( - 0, - "invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setClientOrderToUse()V" - ) } ) diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index b2fa4a885..3faa7cdbe 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -137,15 +137,13 @@ - Android VR 1.43 - Android VR 1.61 + Android VR Android Studio visionOS iPadOS ANDROID_VR_1_43_32 - ANDROID_VR_1_61_48 ANDROID_CREATOR VISIONOS IPADOS diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 0bdd13f86..2461191bb 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -1681,13 +1681,20 @@ Limitations: Slide to seek is not enabled + Allow Android VR AV1 + "Video codec is AVC (H.264), VP9, or AV1 + +Playback may stutter or drop frames" + Video codec is AVC (H.264) or VP9 + "Enabling this setting may use software AV1 decoding. + +Video playback with AV1 may stutter or drop frames." Spoofing side effects • Experimental client and may stop working anytime • Video may stop at 1:00, or may not be available in some regions • Audio track menu is missing • No AV1 video codec • Stable volume is not available - • Playback may stutter or drop frames • Kids videos may not play when logged out or in incognito mode • Force original audio is not available