forked from Foxyz/sync_replay_project
Vendor media_kit_video 2.0.1 with the X11/EGL fix (hardware rendering on X11 with Flutter >= 3.38)
Flutter >= 3.38 keeps its EGL contexts on the raster thread, so media_kit's video output found none on the platform thread and fell back to software rendering (no shaders, ~5 fps) on X11. The patched output opens Flutter's EGL display itself, steps GDK's GLX context aside on X11 and releases its own context. Patch from a friend's agent, kept in third_party/media_kit_video/starsync/. Wayland/NVIDIA still renders on the GPU (checked); the X11 path was verified on the friend's machine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
25b19f2d6a
commit
a87ee98faa
131 files changed
+15532
-6
No files matched your search
@@ -0,0 +1,38 @@
|
||||
group 'com.alexmercerind.media_kit_video'
|
||||
version '1.0'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.13.0'
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
namespace 'com.alexmercerind.media_kit_video'
|
||||
|
||||
compileSdkVersion 36
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
-keepclassmembers class io.flutter.embedding.engine.FlutterEngine {
|
||||
private io.flutter.embedding.engine.FlutterJNI flutterJNI;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
rootProject.name = 'media_kit_video'
|
||||
@@ -0,0 +1 @@
|
||||
<manifest package="com.alexmercerind.media_kit_video"></manifest>
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* This file is a part of media_kit (https://github.com/media-kit/media-kit).
|
||||
* <p>
|
||||
* Copyright © 2021 & onwards, Hitesh Kumar Saini <saini123hitesh@gmail.com>.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by MIT license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.alexmercerind.media_kit_video;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
|
||||
/**
|
||||
* MediaKitVideoPlugin
|
||||
*/
|
||||
public class MediaKitVideoPlugin implements FlutterPlugin, MethodCallHandler {
|
||||
private MethodChannel channel;
|
||||
private VideoOutputManager videoOutputManager;
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "com.alexmercerind/media_kit_video");
|
||||
channel.setMethodCallHandler(this);
|
||||
|
||||
videoOutputManager = new VideoOutputManager(flutterPluginBinding.getTextureRegistry());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||
switch (call.method) {
|
||||
case "VideoOutputManager.Create": {
|
||||
final long handle = Long.parseLong(call.argument("handle"));
|
||||
videoOutputManager.create(handle, (id, wid, width, height) -> channel.invokeMethod("VideoOutput.Resize", new HashMap<String, Object>() {{
|
||||
put("handle", handle);
|
||||
put("id", id);
|
||||
put("wid", wid);
|
||||
put("rect", new HashMap<String, Object>() {{
|
||||
put("left", 0);
|
||||
put("top", 0);
|
||||
put("width", width);
|
||||
put("height", height);
|
||||
}});
|
||||
}}));
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
case "VideoOutputManager.SetSurfaceSize": {
|
||||
final long handle = Long.parseLong(call.argument("handle"));
|
||||
final int width = Integer.parseInt(call.argument("width"));
|
||||
final int height = Integer.parseInt(call.argument("height"));
|
||||
videoOutputManager.setSurfaceSize(handle, width, height);
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
case "VideoOutputManager.Dispose": {
|
||||
final long handle = Long.parseLong(call.argument("handle"));
|
||||
videoOutputManager.dispose(handle);
|
||||
result.success(null);
|
||||
break;
|
||||
}
|
||||
case "Utils.IsEmulator": {
|
||||
result.success(Utils.isEmulator());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||
channel.setMethodCallHandler(null);
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.alexmercerind.media_kit_video;
|
||||
|
||||
public interface TextureUpdateCallback {
|
||||
void onTextureUpdate(long id, long wid, int width, int height);
|
||||
}
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* This file is a part of media_kit (https://github.com/media-kit/media-kit).
|
||||
* <p>
|
||||
* Copyright © 2021 & onwards, Hitesh Kumar Saini <saini123hitesh@gmail.com>.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by MIT license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.alexmercerind.media_kit_video;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import io.flutter.Log;
|
||||
|
||||
|
||||
public abstract class Utils {
|
||||
private static final String TAG = "Utils";
|
||||
|
||||
public static boolean isEmulator() {
|
||||
try {
|
||||
// https://github.com/fluttercommunity/plus_plugins/blob/ff54dc49230ee5f8b772a3326d4ff3758618df80/packages/device_info_plus/device_info_plus/android/src/main/kotlin/dev/fluttercommunity/plus/device_info/MethodCallHandlerImpl.kt#L110-L125
|
||||
return Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
|
||||
|| Build.FINGERPRINT.startsWith("generic")
|
||||
|| Build.FINGERPRINT.startsWith("unknown")
|
||||
|| Build.HARDWARE.contains("goldfish")
|
||||
|| Build.HARDWARE.contains("ranchu")
|
||||
|| Build.MODEL.contains("google_sdk")
|
||||
|| Build.MODEL.contains("Emulator")
|
||||
|| Build.MODEL.contains("Android SDK built for x86")
|
||||
|| Build.MANUFACTURER.contains("Genymotion")
|
||||
|| Build.PRODUCT.contains("sdk_google")
|
||||
|| Build.PRODUCT.contains("google_sdk")
|
||||
|| Build.PRODUCT.contains("sdk")
|
||||
|| Build.PRODUCT.contains("sdk_x86")
|
||||
|| Build.PRODUCT.contains("vbox86p")
|
||||
|| Build.PRODUCT.contains("emulator")
|
||||
|| Build.PRODUCT.contains("simulator");
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "isEmulator", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
third_party/media_kit_video/android/src/main/java/com/alexmercerind/media_kit_video/VideoOutput.java
Vendored
+141
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* This file is a part of media_kit (https://github.com/media-kit/media-kit).
|
||||
* <p>
|
||||
* Copyright © 2021 & onwards, Hitesh Kumar Saini <saini123hitesh@gmail.com>.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by MIT license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.alexmercerind.media_kit_video;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.flutter.view.TextureRegistry;
|
||||
|
||||
public class VideoOutput implements TextureRegistry.SurfaceProducer.Callback {
|
||||
private static final String TAG = "VideoOutput";
|
||||
private static final Method newGlobalObjectRef;
|
||||
private static final Method deleteGlobalObjectRef;
|
||||
private static final HashSet<Long> deletedGlobalObjectRefs = new HashSet<>();
|
||||
private static final Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
static {
|
||||
try {
|
||||
// com.alexmercerind.mediakitandroidhelper.MediaKitAndroidHelper is part of package:media_kit_libs_android_video & package:media_kit_libs_android_audio packages.
|
||||
// Use reflection to invoke methods of com.alexmercerind.mediakitandroidhelper.MediaKitAndroidHelper.
|
||||
Class<?> mediaKitAndroidHelperClass = Class.forName("com.alexmercerind.mediakitandroidhelper.MediaKitAndroidHelper");
|
||||
newGlobalObjectRef = mediaKitAndroidHelperClass.getDeclaredMethod("newGlobalObjectRef", Object.class);
|
||||
deleteGlobalObjectRef = mediaKitAndroidHelperClass.getDeclaredMethod("deleteGlobalObjectRef", long.class);
|
||||
newGlobalObjectRef.setAccessible(true);
|
||||
deleteGlobalObjectRef.setAccessible(true);
|
||||
} catch (Throwable e) {
|
||||
Log.i("media_kit", "package:media_kit_libs_android_video missing. Make sure you have added it to pubspec.yaml.");
|
||||
throw new RuntimeException("Failed to initialize com.alexmercerind.media_kit_video.VideoOutput.");
|
||||
}
|
||||
}
|
||||
|
||||
private long id = 0;
|
||||
private long wid = 0;
|
||||
|
||||
private final TextureUpdateCallback textureUpdateCallback;
|
||||
|
||||
private final TextureRegistry.SurfaceProducer surfaceProducer;
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
VideoOutput(TextureRegistry textureRegistryReference, TextureUpdateCallback textureUpdateCallback) {
|
||||
this.textureUpdateCallback = textureUpdateCallback;
|
||||
|
||||
surfaceProducer = textureRegistryReference.createSurfaceProducer();
|
||||
surfaceProducer.setCallback(this);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
surfaceProducer.getSurface().release();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dispose", e);
|
||||
}
|
||||
try {
|
||||
surfaceProducer.release();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "dispose", e);
|
||||
}
|
||||
onSurfaceCleanup();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSurfaceSize(int width, int height) {
|
||||
setSurfaceSize(width, height, false);
|
||||
}
|
||||
|
||||
private void setSurfaceSize(int width, int height, boolean force) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
if (!force && surfaceProducer.getWidth() == width && surfaceProducer.getHeight() == height) {
|
||||
return;
|
||||
}
|
||||
surfaceProducer.setSize(width, height);
|
||||
onSurfaceAvailable();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "setSurfaceSize", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceAvailable() {
|
||||
synchronized (lock) {
|
||||
Log.i(TAG, "onSurfaceAvailable");
|
||||
id = surfaceProducer.id();
|
||||
wid = newGlobalObjectRef(surfaceProducer.getSurface());
|
||||
textureUpdateCallback.onTextureUpdate(id, wid, surfaceProducer.getWidth(), surfaceProducer.getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCleanup() {
|
||||
synchronized (lock) {
|
||||
Log.i(TAG, "onSurfaceCleanup");
|
||||
textureUpdateCallback.onTextureUpdate(id, 0, surfaceProducer.getWidth(), surfaceProducer.getHeight());
|
||||
if (wid != 0) {
|
||||
final long widReference = wid;
|
||||
handler.postDelayed(() -> deleteGlobalObjectRef(widReference), 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long newGlobalObjectRef(Object object) {
|
||||
Log.i(TAG, String.format(Locale.ENGLISH, "newGlobalRef: object = %s", object));
|
||||
try {
|
||||
return (long) Objects.requireNonNull(newGlobalObjectRef.invoke(null, object));
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "newGlobalRef", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void deleteGlobalObjectRef(long ref) {
|
||||
if (deletedGlobalObjectRefs.contains(ref)) {
|
||||
Log.i(TAG, String.format(Locale.ENGLISH, "deleteGlobalObjectRef: ref = %d ALREADY DELETED", ref));
|
||||
return;
|
||||
}
|
||||
if (deletedGlobalObjectRefs.size() > 100) {
|
||||
deletedGlobalObjectRefs.clear();
|
||||
}
|
||||
deletedGlobalObjectRefs.add(ref);
|
||||
Log.i(TAG, String.format(Locale.ENGLISH, "deleteGlobalObjectRef: ref = %d", ref));
|
||||
try {
|
||||
deleteGlobalObjectRef.invoke(null, ref);
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "deleteGlobalObjectRef", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* This file is a part of media_kit (https://github.com/media-kit/media-kit).
|
||||
* <p>
|
||||
* Copyright © 2021 & onwards, Hitesh Kumar Saini <saini123hitesh@gmail.com>.
|
||||
* All rights reserved.
|
||||
* Use of this source code is governed by MIT license that can be found in the LICENSE file.
|
||||
*/
|
||||
package com.alexmercerind.media_kit_video;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.flutter.view.TextureRegistry;
|
||||
|
||||
public class VideoOutputManager {
|
||||
private static final String TAG = "VideoOutputManager";
|
||||
|
||||
private final HashMap<Long, VideoOutput> videoOutputs = new HashMap<>();
|
||||
private final TextureRegistry textureRegistryReference;
|
||||
private final Object lock = new Object();
|
||||
|
||||
VideoOutputManager(TextureRegistry textureRegistryReference) {
|
||||
this.textureRegistryReference = textureRegistryReference;
|
||||
}
|
||||
|
||||
public void create(long handle, TextureUpdateCallback textureUpdateCallback) {
|
||||
synchronized (lock) {
|
||||
Log.i(TAG, String.format(Locale.ENGLISH, "com.alexmercerind.media_kit_video.VideoOutputManager.create: %d", handle));
|
||||
if (!videoOutputs.containsKey(handle)) {
|
||||
final VideoOutput videoOutput = new VideoOutput(textureRegistryReference, textureUpdateCallback);
|
||||
videoOutputs.put(handle, videoOutput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose(long handle) {
|
||||
synchronized (lock) {
|
||||
Log.i(TAG, String.format(Locale.ENGLISH, "com.alexmercerind.media_kit_video.VideoOutputManager.dispose: %d", handle));
|
||||
if (videoOutputs.containsKey(handle)) {
|
||||
Objects.requireNonNull(videoOutputs.get(handle)).dispose();
|
||||
videoOutputs.remove(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setSurfaceSize(long handle, int width, int height) {
|
||||
synchronized (lock) {
|
||||
Log.i(TAG, String.format(Locale.ENGLISH, "com.alexmercerind.media_kit_video.VideoOutputManager.setSurfaceSize: %d %d %d", handle, width, height));
|
||||
if (videoOutputs.containsKey(handle)) {
|
||||
Objects.requireNonNull(videoOutputs.get(handle)).setSurfaceSize(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user