Skip to content

Commit 060bf8c

Browse files
backport CodecMod to sdk 16
1 parent 147d066 commit 060bf8c

File tree

8 files changed

+198
-54
lines changed

8 files changed

+198
-54
lines changed

CodecMod/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ android {
66
namespace = "com.programminghoch10.CodecMod"
77

88
defaultConfig {
9-
minSdk = 29
9+
minSdk = 16
1010
targetSdk = 35
1111
}
1212
}

CodecMod/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
android:name=".SettingsActivity"
77
android:exported="true"
88
android:label="@string/title_activity_settings"
9-
android:theme="@android:style/Theme.DeviceDefault.Settings">
9+
android:theme="@style/AppTheme">
1010
<intent-filter>
1111
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
1212
<category android:name="android.intent.category.DEFAULT" />

CodecMod/src/main/java/com/programminghoch10/CodecMod/CodecStore.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import android.content.Context;
66
import android.content.SharedPreferences;
7-
import android.media.MediaCodecInfo;
7+
import android.os.Build;
88

99
import java.util.LinkedList;
1010
import java.util.List;
@@ -26,15 +26,15 @@ public class CodecStore {
2626
this.sharedPreferences = new XSharedPreferences(BuildConfig.APPLICATION_ID, PREFERENCES);
2727
}
2828

29-
static String getKey(MediaCodecInfo mediaCodecInfo) {
29+
static String getKey(MediaCodecInfoWrapper mediaCodecInfo) {
3030
return "codec_" + mediaCodecInfo.getCanonicalName();
3131
}
3232

33-
boolean getCodecPreference(MediaCodecInfo mediaCodecInfo) {
33+
boolean getCodecPreference(MediaCodecInfoWrapper mediaCodecInfo) {
3434
return sharedPreferences.getBoolean(getKey(mediaCodecInfo), DEFAULT_VALUE);
3535
}
3636

37-
boolean setCodecPreference(MediaCodecInfo mediaCodecInfo, boolean enabled) {
37+
boolean setCodecPreference(MediaCodecInfoWrapper mediaCodecInfo, boolean enabled) {
3838
boolean success = false;
3939
if (REMOVE_DEFAULT_VALUE_FROM_CONFIG && enabled == DEFAULT_VALUE) {
4040
success = sharedPreferences.edit().remove(getKey(mediaCodecInfo)).commit();
@@ -47,25 +47,32 @@ boolean setCodecPreference(MediaCodecInfo mediaCodecInfo, boolean enabled) {
4747
return success;
4848
}
4949

50-
void registerOnCodecPreferenceChangedListener(MediaCodecInfo mediaCodecInfo, OnCodecPreferenceChangedListener onCodecPreferenceChangedListener) {
50+
void registerOnCodecPreferenceChangedListener(MediaCodecInfoWrapper mediaCodecInfo, OnCodecPreferenceChangedListener onCodecPreferenceChangedListener) {
5151
OnCodecPreferenceChangedListenerMeta listener = new OnCodecPreferenceChangedListenerMeta();
5252
listener.mediaCodecInfo = mediaCodecInfo;
5353
listener.callback = onCodecPreferenceChangedListener;
5454
receivers.add(listener);
5555
}
5656

57-
private void dispatchOnCodecPreferenceChanged(MediaCodecInfo mediaCodecInfo, boolean enabled) {
58-
receivers.stream()
59-
.filter(r -> getKey(r.mediaCodecInfo).equals(getKey(mediaCodecInfo)))
60-
.forEach(r -> r.callback.onCodecPreferenceChanged(enabled));
57+
private void dispatchOnCodecPreferenceChanged(MediaCodecInfoWrapper mediaCodecInfo, boolean enabled) {
58+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
59+
receivers.stream()
60+
.filter(r -> getKey(r.mediaCodecInfo).equals(getKey(mediaCodecInfo)))
61+
.forEach(r -> r.callback.onCodecPreferenceChanged(enabled));
62+
} else {
63+
for (OnCodecPreferenceChangedListenerMeta receiver : receivers) {
64+
if (getKey(receiver.mediaCodecInfo).equals(getKey(mediaCodecInfo)))
65+
receiver.callback.onCodecPreferenceChanged(enabled);
66+
}
67+
}
6168
}
6269

6370
interface OnCodecPreferenceChangedListener {
6471
void onCodecPreferenceChanged(boolean value);
6572
}
6673

6774
private class OnCodecPreferenceChangedListenerMeta {
68-
MediaCodecInfo mediaCodecInfo;
75+
MediaCodecInfoWrapper mediaCodecInfo;
6976
OnCodecPreferenceChangedListener callback;
7077
}
7178

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package com.programminghoch10.CodecMod;
22

3+
import android.annotation.TargetApi;
34
import android.media.MediaCodecInfo;
45
import android.media.MediaCodecList;
6+
import android.os.Build;
57

8+
import java.lang.reflect.InvocationTargetException;
69
import java.util.Arrays;
10+
import java.util.LinkedList;
11+
import java.util.List;
712

813
import de.robv.android.xposed.IXposedHookLoadPackage;
914
import de.robv.android.xposed.XC_MethodReplacement;
@@ -12,41 +17,84 @@
1217
import de.robv.android.xposed.callbacks.XC_LoadPackage;
1318

1419
public class Hook implements IXposedHookLoadPackage {
20+
21+
MediaCodecInfo[] getFilteredMediaCodecInfos(MediaCodecInfo[] unfilteredMediaCodecInfos) {
22+
CodecStore codecStore = new CodecStore();
23+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
24+
return Arrays.stream(unfilteredMediaCodecInfos)
25+
.filter(mediaCodecInfo ->
26+
codecStore.getCodecPreference(new MediaCodecInfoWrapper(mediaCodecInfo))
27+
)
28+
.toArray(MediaCodecInfo[]::new);
29+
}
30+
LinkedList<MediaCodecInfo> filteredMediaCodecs = new LinkedList<>();
31+
for (MediaCodecInfo mediaCodecInfo : unfilteredMediaCodecInfos) {
32+
if (codecStore.getCodecPreference(new MediaCodecInfoWrapper(mediaCodecInfo)))
33+
filteredMediaCodecs.add(mediaCodecInfo);
34+
}
35+
return filteredMediaCodecs.toArray(new MediaCodecInfo[0]);
36+
}
37+
38+
// helper function, only to be used on <LOLLIPOP
39+
@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
40+
MediaCodecInfo[] getFilteredMediaCodecInfos() throws InvocationTargetException, IllegalAccessException {
41+
List<MediaCodecInfo> mediaCodecs = new LinkedList<>();
42+
final int codecCount = (int) XposedBridge.invokeOriginalMethod(XposedHelpers.findMethodExact(MediaCodecList.class, "getCodecCount"), null, null);
43+
for (int i = 0; i < codecCount; i++)
44+
mediaCodecs.add((MediaCodecInfo) XposedBridge.invokeOriginalMethod(XposedHelpers.findMethodExact(MediaCodecList.class, "getCodecInfoAt"), null, new Object[]{i}));
45+
return getFilteredMediaCodecInfos(mediaCodecs.toArray(new MediaCodecInfo[0]));
46+
}
47+
1548
@Override
1649
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
1750
if (lpparam.packageName.equals(BuildConfig.APPLICATION_ID)) return;
1851
if (lpparam.packageName.equals("android")) {
1952
// system-wide hooking not implemented
2053
return;
2154
}
22-
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecInfos", new XC_MethodReplacement() {
23-
@Override
24-
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
25-
MediaCodecInfo[] mediaCodecInfos = (MediaCodecInfo[]) XposedBridge.invokeOriginalMethod(param.method, param.thisObject, param.args);
26-
if (mediaCodecInfos.length == 0) return mediaCodecInfos;
27-
CodecStore codecStore = new CodecStore();
28-
MediaCodecInfo[] filteredMediaCodecInfos = Arrays.stream(mediaCodecInfos)
29-
.filter(codecStore::getCodecPreference)
30-
.toArray(MediaCodecInfo[]::new);
31-
return filteredMediaCodecInfos;
32-
}
33-
});
55+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
56+
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecInfos", new XC_MethodReplacement() {
57+
@Override
58+
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
59+
MediaCodecInfo[] mediaCodecInfos = (MediaCodecInfo[]) XposedBridge.invokeOriginalMethod(param.method, param.thisObject, param.args);
60+
if (mediaCodecInfos.length == 0) return mediaCodecInfos;
61+
return getFilteredMediaCodecInfos(mediaCodecInfos);
62+
}
63+
});
64+
65+
// reimplementations of deprecated methods for compatibility
66+
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecCount", new XC_MethodReplacement() {
67+
@Override
68+
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
69+
return (new MediaCodecList(MediaCodecList.REGULAR_CODECS)).getCodecInfos().length;
70+
}
71+
});
72+
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecInfoAt", int.class, new XC_MethodReplacement() {
73+
@Override
74+
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
75+
final int position = (int) param.args[0];
76+
MediaCodecInfo[] mediaCodecInfos = (new MediaCodecList(MediaCodecList.REGULAR_CODECS)).getCodecInfos();
77+
if (position < 0 || position >= mediaCodecInfos.length) throw new IllegalArgumentException();
78+
return mediaCodecInfos[position];
79+
}
80+
});
81+
} else {
82+
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecCount", new XC_MethodReplacement() {
83+
@Override
84+
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
85+
return getFilteredMediaCodecInfos().length;
86+
}
87+
});
88+
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecInfoAt", int.class, new XC_MethodReplacement() {
89+
@Override
90+
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
91+
final int position = (int) param.args[0];
92+
MediaCodecInfo[] mediaCodecInfos = getFilteredMediaCodecInfos();
93+
if (position < 0 || position >= mediaCodecInfos.length) throw new IllegalArgumentException();
94+
return mediaCodecInfos[position];
95+
}
96+
});
97+
}
3498

35-
// reimplementations of deprecated methods for compatibility
36-
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecCount", new XC_MethodReplacement() {
37-
@Override
38-
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
39-
return (new MediaCodecList(MediaCodecList.REGULAR_CODECS)).getCodecInfos().length;
40-
}
41-
});
42-
XposedHelpers.findAndHookMethod(MediaCodecList.class, "getCodecInfoAt", int.class, new XC_MethodReplacement() {
43-
@Override
44-
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
45-
final int position = (int) param.args[0];
46-
MediaCodecInfo[] mediaCodecInfos = (new MediaCodecList(MediaCodecList.REGULAR_CODECS)).getCodecInfos();
47-
if (position < 0 || position >= mediaCodecInfos.length) throw new IllegalArgumentException();
48-
return mediaCodecInfos[position];
49-
}
50-
});
5199
}
52100
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.programminghoch10.CodecMod;
2+
3+
import android.os.Build;
4+
5+
import androidx.annotation.RequiresApi;
6+
7+
/**
8+
* drop in replacement for MediaCodecInfo
9+
* with compatibility checks for older SDKs
10+
*
11+
* @see android.media.MediaCodecInfo
12+
*/
13+
public class MediaCodecInfoWrapper {
14+
private final android.media.MediaCodecInfo mediaCodecInfo;
15+
16+
MediaCodecInfoWrapper(android.media.MediaCodecInfo mediaCodecInfo) {
17+
this.mediaCodecInfo = mediaCodecInfo;
18+
}
19+
20+
public String getCanonicalName() {
21+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
22+
return mediaCodecInfo.getCanonicalName();
23+
}
24+
return mediaCodecInfo.getName();
25+
}
26+
27+
public boolean isAlias() {
28+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
29+
return mediaCodecInfo.isAlias();
30+
}
31+
return false;
32+
}
33+
34+
public String getName() {
35+
return mediaCodecInfo.getName();
36+
}
37+
38+
@RequiresApi(api = Build.VERSION_CODES.Q)
39+
public boolean isHardwareAccelerated() {
40+
return mediaCodecInfo.isHardwareAccelerated();
41+
}
42+
43+
@RequiresApi(api = Build.VERSION_CODES.Q)
44+
public boolean isSoftwareOnly() {
45+
return mediaCodecInfo.isSoftwareOnly();
46+
}
47+
48+
@RequiresApi(api = Build.VERSION_CODES.Q)
49+
public boolean isVendor() {
50+
return mediaCodecInfo.isVendor();
51+
}
52+
53+
public boolean isEncoder() {
54+
return mediaCodecInfo.isEncoder();
55+
}
56+
57+
public boolean isDecoder() {
58+
return !isEncoder();
59+
}
60+
61+
public String[] getSupportedTypes() {
62+
return mediaCodecInfo.getSupportedTypes();
63+
}
64+
}

CodecMod/src/main/java/com/programminghoch10/CodecMod/SettingsActivity.java

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.programminghoch10.CodecMod;
22

33
import android.app.ActionBar;
4-
import android.media.MediaCodecInfo;
54
import android.media.MediaCodecList;
5+
import android.os.Build;
66
import android.os.Bundle;
77

88
import androidx.fragment.app.FragmentActivity;
@@ -11,6 +11,8 @@
1111
import androidx.preference.SwitchPreference;
1212

1313
import java.util.Arrays;
14+
import java.util.LinkedList;
15+
import java.util.List;
1416

1517
public class SettingsActivity extends FragmentActivity {
1618
@Override
@@ -42,8 +44,17 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
4244
CodecStore codecStore = new CodecStore(getContext());
4345
PreferenceCategory decodersPreferenceCategory = findPreference("category_decoders");
4446
PreferenceCategory encodersPreferenceCategory = findPreference("category_encoders");
45-
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
46-
for (MediaCodecInfo mediaCodecInfo : mediaCodecList.getCodecInfos()) {
47+
48+
List<MediaCodecInfoWrapper> mediaCodecs = new LinkedList<>();
49+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
50+
MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
51+
for (android.media.MediaCodecInfo mediaCodecInfo : mediaCodecList.getCodecInfos())
52+
mediaCodecs.add(new MediaCodecInfoWrapper(mediaCodecInfo));
53+
} else {
54+
for (int i = 0; i < MediaCodecList.getCodecCount(); i++)
55+
mediaCodecs.add(new MediaCodecInfoWrapper(MediaCodecList.getCodecInfoAt(i)));
56+
}
57+
for (MediaCodecInfoWrapper mediaCodecInfo : mediaCodecs) {
4758
if (mediaCodecInfo.isAlias() && !SHOW_ALIASES) continue;
4859
SwitchPreference preference = new SwitchPreference(getContext());
4960
preference.setPersistent(false);
@@ -55,17 +66,21 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
5566
});
5667
preference.setTitle(mediaCodecInfo.getName()
5768
+ (mediaCodecInfo.getName().equals(mediaCodecInfo.getCanonicalName()) ? "" : " (" + mediaCodecInfo.getCanonicalName() + ")"));
58-
preference.setSummary(
59-
String.format(getString(R.string.hardware_accelerated), mediaCodecInfo.isHardwareAccelerated())
60-
+ "\n" +
61-
String.format(getString(R.string.software_only), mediaCodecInfo.isSoftwareOnly())
62-
+ "\n" +
63-
String.format(getString(R.string.supported_types), Arrays.toString(mediaCodecInfo.getSupportedTypes())) +
64-
(SHOW_ALIASES ? "\n" +
65-
String.format(getString(R.string.alias), mediaCodecInfo.isAlias()) : "")
66-
+ "\n" +
67-
String.format(getString(R.string.vendor), mediaCodecInfo.isVendor())
68-
);
69+
StringBuilder summaryBuilder = new StringBuilder();
70+
summaryBuilder.append(String.format(getString(R.string.supported_types), Arrays.toString(mediaCodecInfo.getSupportedTypes())));
71+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
72+
summaryBuilder.append("\n");
73+
summaryBuilder.append(String.format(getString(R.string.hardware_accelerated), mediaCodecInfo.isHardwareAccelerated()));
74+
summaryBuilder.append("\n");
75+
summaryBuilder.append(String.format(getString(R.string.software_only), mediaCodecInfo.isSoftwareOnly()));
76+
if (SHOW_ALIASES) {
77+
summaryBuilder.append("\n");
78+
summaryBuilder.append(String.format(getString(R.string.alias), mediaCodecInfo.isAlias()));
79+
}
80+
summaryBuilder.append("\n");
81+
summaryBuilder.append(String.format(getString(R.string.vendor), mediaCodecInfo.isVendor()));
82+
}
83+
preference.setSummary(summaryBuilder);
6984
PreferenceCategory preferenceCategory = mediaCodecInfo.isEncoder() ? encodersPreferenceCategory : decodersPreferenceCategory;
7085
preferenceCategory.addPreference(preference);
7186
preference.setChecked(codecStore.getCodecPreference(mediaCodecInfo));
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
4+
<style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Settings" />
5+
</resources>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
4+
<style name="AppTheme" parent="@android:style/Theme.DeviceDefault" />
5+
</resources>

0 commit comments

Comments
 (0)