package com.example.vcam;
import android.Manifest;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.SurfaceTexture;
//import android.hardware.Camera;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.params.SessionConfiguration;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.Toast;
import com.example.vcam.event.EventProxyHandler;
import com.example.vcam.event.SomeEvent;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
public class HookMain implements IXposedHookLoadPackage {
public static Surface mSurface;
public static SurfaceTexture mSurfacetexture;
public static MediaPlayer mMediaPlayer;
public static SurfaceTexture fake_SurfaceTexture;
//public static Camera origin_preview_camera;
//public static Camera camera_onPreviewFrame;
//public static Camera start_preview_camera;
public static volatile byte[] data_buffer = {0};
public static byte[] input;
public static int mhight;
public static int mwidth;
public static boolean is_someone_playing;
public static boolean is_hooked;
public static VideoToFrames hw_decode_obj;
public static VideoToFrames c2_hw_decode_obj;
public static VideoToFrames c2_hw_decode_obj_1;
public static SurfaceTexture c1_fake_texture;
public static Surface c1_fake_surface;
public static SurfaceHolder ori_holder;
public static MediaPlayer mplayer1;
//public static Camera mcamera1;
public int imageReaderFormat = 0;
public static boolean is_first_hook_build = true;
public static int onemhight;
public static int onemwidth;
public static Class camera_callback_calss;
public static String video_path = "/storage/emulated/0/DCIM/Camera1/";
public static Surface c2_preview_Surfcae;
public static Surface c2_preview_Surfcae_1;
public static Surface c2_reader_Surfcae;
public static Surface c2_reader_Surfcae_1;
public static MediaPlayer c2_player;
public static MediaPlayer c2_player_1;
public static Surface c2_virtual_surface;
public static SurfaceTexture c2_virtual_surfaceTexture;
public boolean need_recreate;
public static CameraDevice.StateCallback c2_state_cb;
public static CaptureRequest.Builder c2_builder;
public static SessionConfiguration fake_sessionConfiguration;
public static SessionConfiguration sessionConfiguration;
public static OutputConfiguration outputConfiguration;
public boolean need_to_show_toast = true;
private static Context appContext;
public int c2_ori_width = 1280;
public int c2_ori_height = 720;
public static Class c2_state_callback;
public Context toast_content;
private static ClassLoader hostClassLoader;
// 视频源管理器
//public static VideoSourceManager videoSourceManager = new VideoSourceManager();
VideoSourceManager manager = VideoSourceManager.getInstance();
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Exception {
// 初始化视频源管理器
if (manager == null) {
manager = new VideoSourceManager();
}
// 添加默认视频源
String defaultVideo = video_path + "virtual.mp4";
manager.addVideoSource("default", defaultVideo);
manager.setCurrentSource("default");
if (lpparam.packageName.equals("com.example.vcam")){
XposedBridge.log("【VCAM】event2 " + lpparam.packageName);
hostClassLoader = lpparam.classLoader;
Class<?> mainActivityClass = hostClassLoader.loadClass(
"com.example.vcam.MainActivity"
);
Class<?> iEventInterface = hostClassLoader.loadClass(
"com.example.vcam.IEvent"
);
XposedHelpers.findAndHookMethod(mainActivityClass,"onEvent", iEventInterface,new XC_MethodHook(){
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
//XposedBridge.log("【VCAM】event2 onEvent" );
Object event = param.args[0];
// EventProxyHandler.handleEvent(
// event,
// event.getClass() // 使用实际运行时类
// );
Method getMessage = iEventInterface.getMethod("getMessage");
String MessageStr = (String) getMessage.invoke(event);
Method getBeiCan1 = iEventInterface.getMethod("getBeiCan1");
String BeiCan1Str = (String) getBeiCan1.invoke(event);
XposedBridge.log("【VCAM】event " + MessageStr);
XposedBridge.log("【VCAM】eventpath " + BeiCan1Str);
if (MessageStr == "qiehuan1") {
String sourceName = "source_virtual21";
// 添加新视频源(如果不存在)
if (!manager.videoSources.containsKey(sourceName)) {
manager.addVideoSource(sourceName, BeiCan1Str);
}
// 切换到新视频源
manager.switchToSource(sourceName);
// 通知相机重启
manager.onCameraRestart();
// 强制重启相机
forceRestartCamera(lpparam);
}
}
});
}
XposedHelpers.findAndHookMethod("android.hardware.camera2.CameraManager", lpparam.classLoader, "openCamera", String.class, CameraDevice.StateCallback.class, Handler.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (param.args[1] == null) {
return;
}
if (param.args[1].equals(c2_state_cb)) {
return;
}
c2_state_cb = (CameraDevice.StateCallback) param.args[1];
c2_state_callback = param.args[1].getClass();
File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg");
if (control_file.exists()) {
return;
}
File file = new File(video_path + "virtual.mp4");
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (!file.exists()) {
if (toast_content != null && need_to_show_toast) {
try {
Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径1:" + video_path, Toast.LENGTH_SHORT).show();
} catch (Exception ee) {
XposedBridge.log("【VCAM】[toast]" + ee.toString());
}
}
return;
}
XposedBridge.log("【VCAM】1位参数初始化相机,类:" + c2_state_callback.getName().toString());
is_first_hook_build = true;
process_camera2_init(c2_state_callback);
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
XposedHelpers.findAndHookMethod("android.hardware.camera2.CameraManager", lpparam.classLoader, "openCamera", String.class, Executor.class, CameraDevice.StateCallback.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("【VCAM】2位参数初始化相机,类:" + c2_state_callback.toString());
if (param.args[2] == null) {
return;
}
if (param.args[2].equals(c2_state_cb)) {
return;
}
c2_state_cb = (CameraDevice.StateCallback) param.args[2];
File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg");
if (control_file.exists()) {
return;
}
File file = new File(video_path + "virtual.mp4");
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (!file.exists()) {
if (toast_content != null && need_to_show_toast) {
try {
Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径2:" + video_path, Toast.LENGTH_SHORT).show();
} catch (Exception ee) {
XposedBridge.log("【VCAM】[toast]" + ee.toString());
}
}
return;
}
c2_state_callback = param.args[2].getClass();
XposedBridge.log("【VCAM】2位参数初始化相机,类:" + c2_state_callback.toString());
is_first_hook_build = true;
process_camera2_init(c2_state_callback);
}
});
}
// XposedHelpers.findAndHookMethod("android.media.MediaRecorder", lpparam.classLoader, "setCamera", Camera.class, new XC_MethodHook() {
// @Override
// protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// super.beforeHookedMethod(param);
// File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
// need_to_show_toast = !toast_control.exists();
// XposedBridge.log("【VCAM】[record]" + lpparam.packageName);
// if (toast_content != null && need_to_show_toast) {
// try {
// Toast.makeText(toast_content, "应用:" + lpparam.appInfo.name + "(" + lpparam.packageName + ")" + "触发了录像,但目前无法拦截", Toast.LENGTH_SHORT).show();
// }catch (Exception ee){
// XposedBridge.log("【VCAM】[toast]" + Arrays.toString(ee.getStackTrace()));
// }
// }
// }
// });
XposedHelpers.findAndHookMethod("android.app.Instrumentation", lpparam.classLoader, "callApplicationOnCreate", Application.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
if (param.args[0] instanceof Application) {
try {
toast_content = ((Application) param.args[0]).getApplicationContext();
} catch (Exception ee) {
XposedBridge.log("【VCAM】" + ee.toString());
}
File force_private = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera1/private_dir.jpg");
if (toast_content != null) {//后半段用于强制私有目录
int auth_statue = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
try {
auth_statue += (toast_content.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) + 1);
} catch (Exception ee) {
XposedBridge.log("【VCAM】[permission-check]" + ee.toString());
}
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
auth_statue += (toast_content.checkSelfPermission(Manifest.permission.MANAGE_EXTERNAL_STORAGE) + 1);
}
} catch (Exception ee) {
XposedBridge.log("【VCAM】[permission-check]" + ee.toString());
}
}else {
if (toast_content.checkCallingPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED ){
auth_statue = 2;
}
}
//权限判断完毕
if (auth_statue < 1 || force_private.exists()) {
File shown_file = new File(toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/");
if ((!shown_file.isDirectory()) && shown_file.exists()) {
shown_file.delete();
}
if (!shown_file.exists()) {
shown_file.mkdir();
}
shown_file = new File(toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/" + "has_shown");
File toast_force_file = new File(Environment.getExternalStorageDirectory().getPath()+ "/DCIM/Camera1/force_show.jpg");
if ((!lpparam.packageName.equals(BuildConfig.APPLICATION_ID)) && ((!shown_file.exists()) || toast_force_file.exists())) {
try {
Toast.makeText(toast_content, lpparam.packageName+"未授予读取本地目录权限,请检查权限\nCamera1目前重定向为 " + toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/", Toast.LENGTH_SHORT).show();
FileOutputStream fos = new FileOutputStream(toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/" + "has_shown");
String info = "shown";
fos.write(info.getBytes());
fos.flush();
fos.close();
} catch (Exception e) {
XposedBridge.log("【VCAM】[switch-dir]" + e.toString());
}
}
video_path = toast_content.getExternalFilesDir(null).getAbsolutePath() + "/Camera1/";
}else {
video_path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/";
}
} else {
video_path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/";
File uni_DCIM_path = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/");
if (uni_DCIM_path.canWrite()) {
File uni_Camera1_path = new File(video_path);
if (!uni_Camera1_path.exists()) {
uni_Camera1_path.mkdir();
}
}
}
}
}
});
XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "addTarget", Surface.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
if (param.args[0] == null) {
return;
}
if (param.thisObject == null) {
return;
}
File file = new File(video_path + "virtual.mp4");
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (!file.exists()) {
if (toast_content != null && need_to_show_toast) {
try {
Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径3:" + video_path, Toast.LENGTH_SHORT).show();
} catch (Exception ee) {
XposedBridge.log("【VCAM】[toast]" + ee.toString());
}
}
return;
}
if (param.args[0].equals(c2_virtual_surface)) {
return;
}
File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg");
if (control_file.exists()) {
return;
}
String surfaceInfo = param.args[0].toString();
if (surfaceInfo.contains("Surface(name=null)")) {
if (c2_reader_Surfcae == null) {
c2_reader_Surfcae = (Surface) param.args[0];
} else {
if ((!c2_reader_Surfcae.equals(param.args[0])) && c2_reader_Surfcae_1 == null) {
c2_reader_Surfcae_1 = (Surface) param.args[0];
}
}
} else {
if (c2_preview_Surfcae == null) {
c2_preview_Surfcae = (Surface) param.args[0];
} else {
if ((!c2_preview_Surfcae.equals(param.args[0])) && c2_preview_Surfcae_1 == null) {
c2_preview_Surfcae_1 = (Surface) param.args[0];
}
}
}
XposedBridge.log("【VCAM】添加目标:" + param.args[0].toString());
param.args[0] = c2_virtual_surface;
}
});
XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder",
lpparam.classLoader, "addTarget", Surface.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Surface surface = (Surface) param.args[0];
VideoSourceManager.getInstance().prepareForSurface(surface);
}
});
XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "removeTarget", Surface.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
if (param.args[0] == null) {
return;
}
if (param.thisObject == null) {
return;
}
File file = new File(video_path + "virtual.mp4");
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (!file.exists()) {
if (toast_content != null && need_to_show_toast) {
try {
Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径4:" + video_path, Toast.LENGTH_SHORT).show();
} catch (Exception ee) {
XposedBridge.log("【VCAM】[toast]" + ee.toString());
}
}
return;
}
File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg");
if (control_file.exists()) {
return;
}
Surface rm_surf = (Surface) param.args[0];
if (rm_surf.equals(c2_preview_Surfcae)) {
c2_preview_Surfcae = null;
}
if (rm_surf.equals(c2_preview_Surfcae_1)) {
c2_preview_Surfcae_1 = null;
}
if (rm_surf.equals(c2_reader_Surfcae_1)) {
c2_reader_Surfcae_1 = null;
}
if (rm_surf.equals(c2_reader_Surfcae)) {
c2_reader_Surfcae = null;
}
XposedBridge.log("【VCAM】移除目标:" + param.args[0].toString());
}
});
XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "build", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
if (param.thisObject == null) {
return;
}
if (param.thisObject.equals(c2_builder)) {
return;
}
c2_builder = (CaptureRequest.Builder) param.thisObject;
File file = new File(video_path + "virtual.mp4");
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (!file.exists() && need_to_show_toast) {
if (toast_content != null) {
try {
Toast.makeText(toast_content, "不存在替换视频\n" + lpparam.packageName + "当前路径5:" + video_path, Toast.LENGTH_SHORT).show();
} catch (Exception ee) {
XposedBridge.log("【VCAM】[toast]" + ee.toString());
}
}
return;
}
File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg");
if (control_file.exists()) {
return;
}
XposedBridge.log("【VCAM】开始build请求");
process_camera2_play();
}
});
XposedHelpers.findAndHookMethod("android.media.ImageReader", lpparam.classLoader, "newInstance", int.class, int.class, int.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("【VCAM】应用创建了渲染器:宽:" + param.args[0] + " 高:" + param.args[1] + "格式" + param.args[2]);
c2_ori_width = (int) param.args[0];
c2_ori_height = (int) param.args[1];
imageReaderFormat = (int) param.args[2];
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (toast_content != null && need_to_show_toast) {
try {
Toast.makeText(toast_content, "应用创建了渲染器:\n宽:" + param.args[0] + "\n高:" + param.args[1] + "\n一般只需要宽高比与视频相同", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
XposedBridge.log("【VCAM】[toast]" + e.toString());
}
}
}
});
XposedHelpers.findAndHookMethod("android.hardware.camera2.CameraCaptureSession.CaptureCallback", lpparam.classLoader, "onCaptureFailed", CameraCaptureSession.class, CaptureRequest.class, CaptureFailure.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("【VCAM】onCaptureFailed" + "原因:" + ((CaptureFailure) param.args[2]).getReason());
}
});
}
private void process_camera2_play() {
if (c2_reader_Surfcae != null) {
XposedBridge.log("【VCAM c2_reader_Surfcae 非空】" );
if (c2_hw_decode_obj != null) {
c2_hw_decode_obj.stopDecode();
c2_hw_decode_obj = null;
}
c2_hw_decode_obj = new VideoToFrames();
try {
if (imageReaderFormat == 256) {
c2_hw_decode_obj.setSaveFrames("null", OutputImageFormat.JPEG);
} else {
c2_hw_decode_obj.setSaveFrames("null", OutputImageFormat.NV21);
}
c2_hw_decode_obj.set_surfcae(c2_reader_Surfcae);
c2_hw_decode_obj.decode(video_path + "virtual.mp4");
} catch (Throwable throwable) {
XposedBridge.log("【VCAM】" + throwable);
}
}
if (c2_reader_Surfcae_1 != null) {
XposedBridge.log("【VCAM c2_reader_Surfcae_1 非空】" );
if (c2_hw_decode_obj_1 != null) {
c2_hw_decode_obj_1.stopDecode();
c2_hw_decode_obj_1 = null;
}
c2_hw_decode_obj_1 = new VideoToFrames();
try {
if (imageReaderFormat == 256) {
c2_hw_decode_obj_1.setSaveFrames("null", OutputImageFormat.JPEG);
} else {
c2_hw_decode_obj_1.setSaveFrames("null", OutputImageFormat.NV21);
}
c2_hw_decode_obj_1.set_surfcae(c2_reader_Surfcae_1);
c2_hw_decode_obj_1.decode(video_path + "virtual.mp4");
} catch (Throwable throwable) {
XposedBridge.log("【VCAM】" + throwable);
}
}
if (c2_preview_Surfcae != null) {
XposedBridge.log("【VCAM c2_preview_Surfcae 非空】" );
if (c2_player == null) {
c2_player = new MediaPlayer();
} else {
c2_player.release();
c2_player = new MediaPlayer();
}
c2_player.setSurface(c2_preview_Surfcae);
File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no-silent.jpg");
if (!sfile.exists()) {
c2_player.setVolume(0, 0);
}
c2_player.setLooping(true);
try {
c2_player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
c2_player.start();
}
});
c2_player.setDataSource(video_path + "virtual.mp4");
c2_player.prepare();
} catch (Exception e) {
XposedBridge.log("【VCAM】[c2player][" + c2_preview_Surfcae.toString() + "]" + e);
}
}
if (c2_preview_Surfcae_1 != null) {
XposedBridge.log("【VCAM c2_preview_Surfcae_1 非空】" );
if (c2_player_1 == null) {
c2_player_1 = new MediaPlayer();
} else {
c2_player_1.release();
c2_player_1 = new MediaPlayer();
}
c2_player_1.setSurface(c2_preview_Surfcae_1);
File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no-silent.jpg");
if (!sfile.exists()) {
c2_player_1.setVolume(0, 0);
}
c2_player_1.setLooping(true);
try {
c2_player_1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
c2_player_1.start();
}
});
c2_player_1.setDataSource(video_path + "virtual.mp4");
c2_player_1.prepare();
} catch (Exception e) {
XposedBridge.log("【VCAM】[c2player1]" + "[ " + c2_preview_Surfcae_1.toString() + "]" + e);
}
}
XposedBridge.log("【VCAM】Camera2处理过程完全执行");
}
private Surface create_virtual_surface() {
if (need_recreate) {
if (c2_virtual_surfaceTexture != null) {
c2_virtual_surfaceTexture.release();
c2_virtual_surfaceTexture = null;
}
if (c2_virtual_surface != null) {
c2_virtual_surface.release();
c2_virtual_surface = null;
}
c2_virtual_surfaceTexture = new SurfaceTexture(15);
c2_virtual_surface = new Surface(c2_virtual_surfaceTexture);
need_recreate = false;
} else {
if (c2_virtual_surface == null) {
need_recreate = true;
c2_virtual_surface = create_virtual_surface();
}
}
XposedBridge.log("【VCAM】【重建垃圾场】" + c2_virtual_surface.toString());
return c2_virtual_surface;
}
// 在HookMain类中添加
private void recreateVirtualSurface() {
if (c2_virtual_surface != null) {
XposedBridge.log("【VCAM】重建虚拟Surface: 1");
c2_virtual_surface.release();
c2_virtual_surface = null;
}
if (c2_virtual_surfaceTexture != null) {
XposedBridge.log("【VCAM】重建虚拟Surface: 2");
c2_virtual_surfaceTexture.release();
c2_virtual_surfaceTexture = null;
}
c2_virtual_surfaceTexture = new SurfaceTexture(15);
c2_virtual_surface = new Surface(c2_virtual_surfaceTexture);
need_recreate = false;
XposedBridge.log("【VCAM】重建虚拟Surface: " + c2_virtual_surface.toString());
}
// 释放Camera2相关资源
private void releaseCamera2Resources() {
if (c2_player != null) {
c2_player.stop();
c2_player.release();
c2_player = null;
}
if (c2_player_1 != null) {
c2_player_1.stop();
c2_player_1.release();
c2_player_1 = null;
}
if (c2_hw_decode_obj != null) {
c2_hw_decode_obj.stopDecode();
c2_hw_decode_obj = null;
}
if (c2_hw_decode_obj_1 != null) {
c2_hw_decode_obj_1.stopDecode();
c2_hw_decode_obj_1 = null;
}
c2_preview_Surfcae = null;
c2_preview_Surfcae_1 = null;
c2_reader_Surfcae = null;
c2_reader_Surfcae_1 = null;
}
private void process_camera2_init(Class hooked_class) {
XposedBridge.log("【VCAM】重启跟踪1" );
if(hooked_class==null){
XposedBridge.log("【VCAM】hooked_class 为null" );
}
XposedHelpers.findAndHookMethod(hooked_class, "onOpened", CameraDevice.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("【VCAM】重启跟踪2");
// 重建虚拟Surface
recreateVirtualSurface();
XposedBridge.log("【VCAM】重启跟踪3");
// 释放旧资源
releaseCamera2Resources();
XposedBridge.log("【VCAM】重启跟踪4");
XposedBridge.log("【VCAM】相机设备已打开");
XposedBridge.log("【VCAM】打开相机C2");
final CameraDevice cameraDevice = (CameraDevice) param.args[0];
File file = new File(video_path + "virtual.mp4");
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (!file.exists()) {
if (toast_content != null && need_to_show_toast) {
try {
Toast.makeText(toast_content, "不存在替换视频\n" + toast_content.getPackageName() + "当前路径6:" + video_path, Toast.LENGTH_SHORT).show();
} catch (Exception ee) {
XposedBridge.log("【VCAM】[toast]" + ee.toString());
}
}
return;
}
XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createCaptureSession", List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam paramd) throws Throwable {
// 替换为虚拟Surface
paramd.args[0] = Arrays.asList(c2_virtual_surface);
XposedBridge.log("【VCAM】配置CaptureSession使用虚拟Surface");
// if (paramd.args[0] != null) {
// XposedBridge.log("【VCAM】createCaptureSession创捷捕获,原始:" + paramd.args[0].toString() + "虚拟:" + c2_virtual_surface.toString());
// paramd.args[0] = Arrays.asList(c2_virtual_surface);
// if (paramd.args[1] != null) {
// process_camera2Session_callback((CameraCaptureSession.StateCallback) paramd.args[1]);
// }
// }
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createCaptureSessionByOutputConfigurations", List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
if (param.args[0] != null) {
outputConfiguration = new OutputConfiguration(c2_virtual_surface);
param.args[0] = Arrays.asList(outputConfiguration);
XposedBridge.log("【VCAM】执行了createCaptureSessionByOutputConfigurations-144777");
if (param.args[1] != null) {
process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[1]);
}
}
}
});
}
XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createConstrainedHighSpeedCaptureSession", List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
if (param.args[0] != null) {
param.args[0] = Arrays.asList(c2_virtual_surface);
XposedBridge.log("【VCAM】执行了 createConstrainedHighSpeedCaptureSession -5484987");
if (param.args[1] != null) {
process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[1]);
}
}
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createReprocessableCaptureSession", InputConfiguration.class, List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
if (param.args[1] != null) {
param.args[1] = Arrays.asList(c2_virtual_surface);
XposedBridge.log("【VCAM】执行了 createReprocessableCaptureSession ");
if (param.args[2] != null) {
process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[2]);
}
}
}
});
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createReprocessableCaptureSessionByConfigurations", InputConfiguration.class, List.class, CameraCaptureSession.StateCallback.class, Handler.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
if (param.args[1] != null) {
outputConfiguration = new OutputConfiguration(c2_virtual_surface);
param.args[0] = Arrays.asList(outputConfiguration);
XposedBridge.log("【VCAM】执行了 createReprocessableCaptureSessionByConfigurations");
if (param.args[2] != null) {
process_camera2Session_callback((CameraCaptureSession.StateCallback) param.args[2]);
}
}
}
});
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
XposedHelpers.findAndHookMethod(param.args[0].getClass(), "createCaptureSession", SessionConfiguration.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
if (param.args[0] != null) {
XposedBridge.log("【VCAM】执行了 createCaptureSession -5484987");
sessionConfiguration = (SessionConfiguration) param.args[0];
outputConfiguration = new OutputConfiguration(c2_virtual_surface);
fake_sessionConfiguration = new SessionConfiguration(sessionConfiguration.getSessionType(),
Arrays.asList(outputConfiguration),
sessionConfiguration.getExecutor(),
sessionConfiguration.getStateCallback());
param.args[0] = fake_sessionConfiguration;
process_camera2Session_callback(sessionConfiguration.getStateCallback());
}
}
});
}
}
});
XposedHelpers.findAndHookMethod(hooked_class, "onError", CameraDevice.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("【VCAM】相机错误onerror:" + (int) param.args[1]);
}
});
XposedHelpers.findAndHookMethod(hooked_class, "onDisconnected", CameraDevice.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("【VCAM】相机断开onDisconnected :");
}
});
}
private void process_a_shot_jpeg(XC_MethodHook.MethodHookParam param, int index) {
try {
XposedBridge.log("【VCAM】第二个jpeg:" + param.args[index].toString());
} catch (Exception eee) {
XposedBridge.log("【VCAM】" + eee);
}
Class callback = param.args[index].getClass();
}
private void process_a_shot_YUV(XC_MethodHook.MethodHookParam param) {
try {
XposedBridge.log("【VCAM】发现拍照YUV:" + param.args[1].toString());
} catch (Exception eee) {
XposedBridge.log("【VCAM】" + eee);
}
Class callback = param.args[1].getClass();
}
private void process_callback(XC_MethodHook.MethodHookParam param) {
Class preview_cb_class = param.args[0].getClass();
int need_stop = 0;
File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "disable.jpg");
if (control_file.exists()) {
need_stop = 1;
}
File file = new File(video_path + "virtual.mp4");
File toast_control = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/" + "no_toast.jpg");
need_to_show_toast = !toast_control.exists();
if (!file.exists()) {
if (toast_content != null && need_to_show_toast) {
try {
Toast.makeText(toast_content, "不存在替换视频\n" + toast_content.getPackageName() + "当前路径7:" + video_path, Toast.LENGTH_SHORT).show();
} catch (Exception ee) {
XposedBridge.log("【VCAM】[toast]" + ee);
}
}
need_stop = 1;
}
int finalNeed_stop = need_stop;
}
private void process_camera2Session_callback(CameraCaptureSession.StateCallback callback_calss){
if (callback_calss == null){
return;
}
XposedBridge.log("【VCAM】process_camera2Session_callback :" + callback_calss.getClass().getName());
XposedHelpers.findAndHookMethod(callback_calss.getClass(), "onConfigureFailed", CameraCaptureSession.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("【VCAM】onConfigureFailed :" + param.args[0].toString());
}
});
XposedHelpers.findAndHookMethod(callback_calss.getClass(), "onConfigured", CameraCaptureSession.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("【VCAM】onConfigured :" + param.args[0].toString());
}
});
XposedHelpers.findAndHookMethod( callback_calss.getClass(), "onClosed", CameraCaptureSession.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("【VCAM】onClosed :"+ param.args[0].toString());
}
});
}
// 强制重启相机方法
private void forceRestartCamera(XC_LoadPackage.LoadPackageParam lpparam) {
try {
// 1. 关闭当前相机会话
if (c2_state_cb != null) {
XposedBridge.log("【VCAM】断开相机链接 onDisconnected");
Method onDisconnected = c2_state_callback.getMethod("onDisconnected", CameraDevice.class);
onDisconnected.invoke(c2_state_cb, (Object)null);
}
// 2. 重建虚拟Surface
recreateVirtualSurface();
// 3. 模拟重新打开相机
XposedBridge.log("【VCAM】准备重启相机 openCamera");
XposedBridge.log("【VCAM】c2_state_callback " + c2_state_callback.getClass().getName().toString());
process_camera2_init(c2_state_callback);
process_camera2_play();
XposedBridge.log("【VCAM】强制重启相机成功");
} catch (Exception e) {
XposedBridge.log("【VCAM】强制重启相机失败: " + e.toString());
}
}
//以下代码来源:https://blog.csdn.net/jacke121/article/details/73888732
private Bitmap getBMP(String file) throws Throwable {
return BitmapFactory.decodeFile(file);
}
private static byte[] rgb2YCbCr420(int[] pixels, int width, int height) {
int len = width * height;
// yuv格式数组大小,y亮度占len长度,u,v各占len/4长度。
byte[] yuv = new byte[len * 3 / 2];
int y, u, v;
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int rgb = (pixels[i * width + j]) & 0x00FFFFFF;
int r = rgb & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = (rgb >> 16) & 0xFF;
// 套用公式
y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
y = y < 16 ? 16 : (Math.min(y, 255));
u = u < 0 ? 0 : (Math.min(u, 255));
v = v < 0 ? 0 : (Math.min(v, 255));
// 赋值
yuv[i * width + j] = (byte) y;
yuv[len + (i >> 1) * width + (j & ~1)] = (byte) u;
yuv[len + +(i >> 1) * width + (j & ~1) + 1] = (byte) v;
}
}
return yuv;
}
private static byte[] getYUVByBitmap(Bitmap bitmap) {
if (bitmap == null) {
return null;
}
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int size = width * height;
int[] pixels = new int[size];
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
return rgb2YCbCr420(pixels, width, height);
}
}
forceRestartCamera 调用时发现 c2_state_callback为null
最新发布