Skip to content
This repository has been archived by the owner on Dec 11, 2024. It is now read-only.

Commit

Permalink
feat(YouTube - Spoof client): Allow forcing AVC codec with iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
inotia00 authored and Francesco146 committed Aug 28, 2024
1 parent 60b0674 commit 8dfbdd4
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public static int getClientTypeId(int originalClientTypeId) {
*/
public static String getClientVersion(String originalClientVersion) {
if (SPOOF_CLIENT_ENABLED) {
return getSpoofClientType().version;
return getSpoofClientType().appVersion;
}

return originalClientVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

import app.revanced.integrations.shared.requests.Requester;
import app.revanced.integrations.shared.requests.Route;
import app.revanced.integrations.shared.settings.Setting;
import app.revanced.integrations.shared.utils.Logger;
import app.revanced.integrations.shared.utils.PackageUtils;
import app.revanced.integrations.youtube.settings.Settings;

public final class PlayerRoutes {
public static final Route.CompiledRoute GET_STORYBOARD_SPEC_RENDERER = new Route(
Expand Down Expand Up @@ -115,19 +117,30 @@ public final class PlayerRoutes {
* Store page of the YouTube app</a>, in the {@code What’s New} section.
* </p>
*/
private static final String IOS_CLIENT_VERSION = "19.30.2";
private static final String IOS_CLIENT_VERSION = "19.16.3";
/**
* The device machine id for the iPhone 14 Pro Max (iPhone15,3), used to get 60fps.
* The device machine id for the iPhone XS Max (iPhone11,4), used to get 60fps.
* The device machine id for the iPhone 15 Pro Max (iPhone16,2), used to get HDR with AV1 hardware decoding.
*
* <p>
* See <a href="https://gist.github.com/adamawolf/3048717">this GitHub Gist</a> for more
* information.
* </p>
*/
private static final String IOS_DEVICE_MODEL = deviceHasAV1HardwareDecoding() ? "iPhone16,2" : "iPhone15,3";
private static final String IOS_OS_VERSION = "17.6.21G80";
private static final String IOS_USER_AGENT_VERSION = "17_6";
private static final String IOS_DEVICE_MODEL = DeviceHardwareSupport.allowAV1()
? "iPhone16,2"
: "iPhone11,4";

/**
* The minimum supported OS version for the iOS YouTube client is iOS 14.0.
* Using an invalid OS version will use the AVC codec.
*/
private static final String IOS_OS_VERSION = DeviceHardwareSupport.allowVP9()
? "17.6.1.21G101"
: "13.7.17H35";
private static final String IOS_USER_AGENT_VERSION = DeviceHardwareSupport.allowVP9()
? "17_6_1"
: "13_7";
private static final String IOS_USER_AGENT = "com.google.ios.youtube/" +
IOS_CLIENT_VERSION +
"(" +
Expand Down Expand Up @@ -346,28 +359,61 @@ public final class PlayerRoutes {
WEB_INNER_TUBE_BODY = webInnerTubeBody.toString();
}

private static boolean deviceHasAV1HardwareDecoding() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Must check for device features in a separate class and cannot place this code inside
// the Patch or ClientType enum due to cyclic Setting references.
static class DeviceHardwareSupport {
private static final boolean DEVICE_HAS_HARDWARE_DECODING_VP9 = deviceHasVP9HardwareDecoding();
private static final boolean DEVICE_HAS_HARDWARE_DECODING_AV1 = deviceHasAV1HardwareDecoding();

private static boolean deviceHasVP9HardwareDecoding() {
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);

for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
if (codecInfo.isHardwareAccelerated() && !codecInfo.isEncoder()) {
String[] supportedTypes = codecInfo.getSupportedTypes();
for (String type : supportedTypes) {
if (type.equalsIgnoreCase("video/av01")) {
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(type);
if (capabilities != null) {
final boolean isHardwareAccelerated = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
? codecInfo.isHardwareAccelerated()
: !codecInfo.getName().startsWith("OMX.google"); // Software decoder.
if (isHardwareAccelerated && !codecInfo.isEncoder()) {
for (String type : codecInfo.getSupportedTypes()) {
if (type.equalsIgnoreCase("video/x-vnd.on2.vp9")) {
Logger.printDebug(() -> "Device supports VP9 hardware decoding.");
return true;
}
}
}
}

Logger.printDebug(() -> "Device does not support VP9 hardware decoding.");
return false;
}

private static boolean deviceHasAV1HardwareDecoding() {
// It appears all devices with hardware AV1 are also Android 10 or newer.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);

for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
if (codecInfo.isHardwareAccelerated() && !codecInfo.isEncoder()) {
for (String type : codecInfo.getSupportedTypes()) {
if (type.equalsIgnoreCase("video/av01")) {
Logger.printDebug(() -> "Device supports AV1 hardware decoding.");
return true;
}
}
}
}
}

Logger.printDebug(() -> "Device does not support AV1 hardware decoding.");
return false;
}

Logger.printDebug(() -> "Device does not support AV1 hardware decoding.");
return false;
static boolean allowVP9() {
return DEVICE_HAS_HARDWARE_DECODING_VP9 && !Settings.SPOOF_CLIENT_IOS_FORCE_AVC.get();
}

static boolean allowAV1() {
return allowVP9() && DEVICE_HAS_HARDWARE_DECODING_AV1;
}
}

private PlayerRoutes() {
Expand All @@ -391,6 +437,21 @@ public static HttpURLConnection getPlayerResponseConnectionFromRoute(Route.Compi
return connection;
}

public static final class SpoofingToIOSAvailability implements Setting.Availability {
public static boolean clientTypeIOSEnabled() {
final ClientType clientTypeIOS = ClientType.IOS;
return Settings.SPOOF_CLIENT_GENERAL.get() == clientTypeIOS ||
Settings.SPOOF_CLIENT_LIVESTREAM.get() == clientTypeIOS ||
Settings.SPOOF_CLIENT_SHORTS.get() == clientTypeIOS ||
Settings.SPOOF_CLIENT_FALLBACK.get() == clientTypeIOS;
}

@Override
public boolean isAvailable() {
return clientTypeIOSEnabled();
}
}

public enum ClientType {
ANDROID(3, ANDROID_DEVICE_MODEL, ANDROID_CLIENT_VERSION, ANDROID_INNER_TUBE_BODY, ANDROID_OS_RELEASE_VERSION, ANDROID_USER_AGENT),
ANDROID_EMBEDDED_PLAYER(55, ANDROID_DEVICE_MODEL, ANDROID_CLIENT_VERSION, ANDROID_EMBED_INNER_TUBE_BODY, ANDROID_OS_RELEASE_VERSION, ANDROID_USER_AGENT),
Expand All @@ -406,17 +467,17 @@ public enum ClientType {
public final String friendlyName;
public final int id;
public final String model;
public final String version;
public final String appVersion;
public final String innerTubeBody;
public final String osVersion;
public final String userAgent;

ClientType(int id, String model, String version, String innerTubeBody,
ClientType(int id, String model, String appVersion, String innerTubeBody,
String osVersion, String userAgent) {
this.friendlyName = str("revanced_spoof_client_options_entry_" + name().toLowerCase());
this.id = id;
this.model = model;
this.version = version;
this.appVersion = appVersion;
this.innerTubeBody = innerTubeBody;
this.osVersion = osVersion;
this.userAgent = userAgent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting OVERLAY_BUTTON_COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("revanced_overlay_button_copy_video_url_timestamp", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_MUTE_VOLUME = new BooleanSetting("revanced_overlay_button_mute_volume", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_overlay_button_external_downloader", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_SPEED_DIALOG = new BooleanSetting("revanced_overlay_button_speed_dialog", TRUE);
public static final BooleanSetting OVERLAY_BUTTON_SPEED_DIALOG = new BooleanSetting("revanced_overlay_button_speed_dialog", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_TIME_ORDERED_PLAYLIST = new BooleanSetting("revanced_overlay_button_time_ordered_playlist", FALSE);
public static final BooleanSetting OVERLAY_BUTTON_WHITELIST = new BooleanSetting("revanced_overlay_button_whitelist", FALSE);

Expand Down Expand Up @@ -455,7 +455,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting ENABLE_EXTERNAL_BROWSER = new BooleanSetting("revanced_enable_external_browser", TRUE, true);
public static final BooleanSetting ENABLE_OPEN_LINKS_DIRECTLY = new BooleanSetting("revanced_enable_open_links_directly", TRUE);
public static final BooleanSetting DISABLE_QUIC_PROTOCOL = new BooleanSetting("revanced_disable_quic_protocol", FALSE, true);
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", FALSE, true, "revanced_spoof_client_user_dialog_message");
public static final BooleanSetting SPOOF_CLIENT = new BooleanSetting("revanced_spoof_client", TRUE, true, "revanced_spoof_client_user_dialog_message");
public static final BooleanSetting SPOOF_CLIENT_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_client_ios_force_avc", FALSE, true, "revanced_spoof_client_ios_force_avc_user_dialog_message", new SpoofingToIOSAvailability());
public static final EnumSetting<ClientType> SPOOF_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_client_type", ClientType.IOS, true, parent(SPOOF_CLIENT));
public static final BooleanSetting SPOOF_CLIENT_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_client_stats_for_nerds", TRUE, parent(SPOOF_CLIENT));
public static final EnumSetting<ClientType> SPOOF_CLIENT_GENERAL = new EnumSetting<>("revanced_spoof_client_general",
ClientType.IOS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import app.revanced.integrations.shared.settings.Setting;
import app.revanced.integrations.shared.utils.Utils;
import app.revanced.integrations.youtube.patches.misc.WatchHistoryPatch.WatchHistoryType;
import app.revanced.integrations.youtube.patches.misc.requests.PlayerRoutes.ClientType;
import app.revanced.integrations.youtube.patches.misc.requests.PlayerRoutes.SpoofingToIOSAvailability;
import app.revanced.integrations.youtube.settings.Settings;

@SuppressWarnings("unused")
Expand Down Expand Up @@ -65,13 +65,8 @@ protected void onPrepareForRemoval() {
}

private void updateUI() {
final ClientType clientTypeIOS = ClientType.IOS;
final boolean spoofClientEnabled = SpoofClient() && Settings.SPOOF_CLIENT.get();
final boolean containsClientTypeIOS =
Settings.SPOOF_CLIENT_GENERAL.get() == clientTypeIOS ||
Settings.SPOOF_CLIENT_LIVESTREAM.get() == clientTypeIOS ||
Settings.SPOOF_CLIENT_SHORTS.get() == clientTypeIOS ||
Settings.SPOOF_CLIENT_FALLBACK.get() == clientTypeIOS;
final boolean containsClientTypeIOS = SpoofingToIOSAvailability.clientTypeIOSEnabled();

final WatchHistoryType watchHistoryType = Settings.WATCH_HISTORY_TYPE.get();
final boolean blockWatchHistory = watchHistoryType == WatchHistoryType.BLOCK;
Expand Down

0 comments on commit 8dfbdd4

Please sign in to comment.