diff --git a/README.md b/README.md
index 896a0a6..75923ac 100644
--- a/README.md
+++ b/README.md
@@ -1,46 +1,56 @@
# android_virtual_cam
-xposed安卓虚拟摄像头
-## 感谢https://github.com/wangwei1237/CameraHook 提供的HOOK思路!!
-## 求有无极的大佬,希望帮忙测试一下此模块虚拟框架下是否可用,测试后希望在issue中反馈一下,谢谢!!!
-
-## 具体的使用方法(English version is below):
-1、安装xposed框架(传统xposed,edxp,lsposed等均可,不确定虚拟框架能否使用,已经确定VMOS可用,应用转生不可用)
-2、安装模块,启用模块,lsposed等包含定义域的框架需要选勾目标app,但无需选勾系统框架。
-3、将需要替换的视频命名为`virtual.mp4`,放在`/sdcard/DCIM/Camera1/`目录下。(前置摄像头需要水平翻转后右旋90°保存,如果打开相机时看见“宽:...高:...”,则需要匹配分辨率)
-4、若需要拦截拍照事件,请在`/sdcard/DCIM/Camera1/`目录下放置 `1000.bmp` 用于替换,(前置摄像头需要水平翻转后右旋90°保存,需要匹配分辨率)
-> 注意:不是所有应用的拍照都调用`1000.bmp`,有很多app只是从`virtual.mp4`里截屏,只有在拍照时弹出“发现拍照”才是真正的调用`1000.bmp`
-
-5、**授予目标应用读取本地文件的权限,至少是允许读取媒体文件。**
-6、强制结束目标应用/重启手机。
-
-## 如何获得分辨率??(仅看见“宽:...高:...”需要)
-在目标应用中打开摄像头,可在弹出的toast消息里看见。
-
-## Camera2接口有问题??
-是的,目前Camera2接口的HOOK不是所有应用程序都能生效,部分app报错打开相机失败,如果想停用Camera2接口的HOOK,可在`/sdcard/DCIM/Camera1/`下创建`disable.jpg`,以停用此项HOOK
-
-## 我不需要静音??
-在`/sdcard/DCIM/Camera1/`下创建`no-silent.jpg`,就不会静音了。
-
-## Detailed usage :
-1. Install this moudle. enable it in Xposed. Framework which has a scope list need to choose target app, but needn't to choose system framework.
-2. Create `virtual.mp4` and put it under `/sdcard/DCIM/Camera1/` ,(if use front camera ,image should be Flip horizontal and right rotation 90 degrees, if you see "宽:...高:..." when opan camera, the resolution should be matched)
-3. If you wants to hook image capture event, you should create `1000.bmp` under `/sdcard/DCIM/Camera1/` for replace. (if use front camera ,image should be Flip horizontal and right rotation 90 degrees, the resolution should be matched)
-> ATTENTION: NOT all apps invoke `1000.bmp` when capture image, some apps directly capture a photo from `virtual.mp4`, when you see “发现拍照” when take photos, the app invoke `1000.bmp` .
-4. authorize the target app to access local storage in system.
-5. Reboot your phone or shutdown target app.
-
-## bugs with camera2 api, need to disable it?
-create `disable.jpg` under `/sdcard/DCIM/Camera1/` to disable this method hook.
-## how to get resolution ??(only needed if you see "宽:...高:...")?
-open camera in target app, and you can find resolution in toast message.
-## Needn't mute?
-Create `no-silent.jpg` under `/sdcard/DCIM/Camera1/`, and it will play sounds.
-
-## release无法下载/gitee下载(gitee与github作者同id,同仓库名)??
-在/app/release/app-release.apk,下载前请注意分支。
-GitHub: https://github.com/w2016561536/android_virtual_cam/blob/master/app/release/app-release.apk
-gitee(中国大陆建议此点): https://gitee.com/w2016561536/android_virtual_cam/blob/master/app/release/app-release.apk
-
-# 请勿用于非法用途,所有后果自负。
-# DO NOT USE FOR ANY ILLEAGLE INTENTION!!YOU NEED TO TAKE ALL RESPONSIBILITY AND CONSEQUENCE!!"
+
+[简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md)
+
+基于Xposed的虚拟摄像头
+
+# 请勿用于任何非法用途,所有后果自负。
+
+## 支持平台:
+
+- 安卓5.0+
+
+## 使用方法
+
+1. 安装此模块,并在Xposed中启用此模块,Lsposed等包含作用域的框架需要选择目标app,无需选择系统框架。
+
+2. 在系统设置中,授予目标应用读取本地存储的权限,并强制结束目标应用程序。若应用程序未申请此权限,请见步骤3。
+
+3. 打开目标应用,若应用未能获得读取存储的权限,则会以气泡消息提示,`Camera1`目录被重定向至应用程序私有目录`/[内部存储]/Android/data/[应用包名]/files/Camera1/`。若未提示,则默认`Camera1`目录为`/[内部存储]/DCIM/Camera1/`。若目录不存在,请手动创建。
+
+> 注意:私有目录下的`Camera1`仅对该应用单独生效。
+
+4. 在目标应用中打开相机预览,会以气泡消息提示“宽:……高:……”,需要根据此分辨率数据制作替换视频,放置于`Camera1`目录下,并命名为`virtual.mp4`,若打开相机并无提示消息,则无需调整视频分辨率。
+
+5. 若在目标应用中拍照却显示真实图片,且出现气泡消息`发现拍照`和分辨率,则需根据此分辨率数据准备一张照片,命名为`1000.bmp`,放入`Camera1`目录下(支持其它格式改后缀为bmp)。如果拍照时无气泡消息提示,则`1000.bmp`无效。
+
+6. 如果需要播放视频的声音,需在`Camera1`目录下创建`no-silent.jpg`文件。
+
+7. 如果需要临时停用视频替换,需在`Camera1`目录下创建`disable.jpg`。
+
+
+## 常见问题
+
+A1. 前置摄像头方向问题?
+Q1. 大多数情况下,替换前置摄像头的视频需要水平翻转并右旋90度,并且视频***处理后***的分辨率应与气泡消息内分辨率相同。但有时这并不需要,具体请根据实际情况判断。
+
+
+Q2. 画面黑屏,相机启动失败?
+A2. 目前有些应用并不能成功替换(特别是系统相机),但剩下的大多是由于视频**编码格式**不正确(并非封装格式),目前仅支持`H.264`编码格式。或者时因为视频路径不对。
+
+
+Q3. 画面花屏?
+A3. 视频分辨率不对。
+
+## 反馈问题
+
+请直接在issues中反馈,如果为BUG反馈,请附带日志信息。
+
+
+## 致谢:
+
+提供HOOK思路: https://github.com/wangwei1237/CameraHook
+
+H264硬解码: https://github.com/zhantong/Android-VideoToImages
+
+JPEG转YUV: https://blog.csdn.net/jacke121/article/details/73888732
diff --git a/README_en.md b/README_en.md
new file mode 100644
index 0000000..4a85cf2
--- /dev/null
+++ b/README_en.md
@@ -0,0 +1,52 @@
+# android_virtual_cam
+
+[简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md)
+
+A virtual camera based on Xposed
+
+## DO NOT USE FOR ANY ILLEGAL PURPOSE, YOU NEED TO TAKE ALL RESPONSIBILITY AND CONSEQUENCE!
+
+## Supported platform
+
+- Android 5.0+
+
+## Usage
+
+1. Install this module , enable it in Xposed . Lsposed and other framework which have a scope list, you need to choose target app instead of System Framework.
+
+2. In system Setting, authorize target to access local storage, and force stop the app. If the app does not request this permission, see step3.
+
+3. open target app, if the app does not have the permission to access local storage. There will be a toast message showing that `Camera1` directory has been redirect to app's private directory `/[INTERNEL_STORAGE]/Android/data/[package_name]/files/Camera1/`. If there isn't the message, the default `Camera1` directory is `/[INTERNEL_STORAGE]/DCIM/Camera1/`. If the directory doesn't exist. Please create it by yourself.
+
+> Attention: `Camera1` in the private directory only works for single app.
+
+4. Open the camera in target app. There will be a toast message showing the resolution (宽width: , 高height:) . And you need to adjust the replacing video's resolution to make them same. Name it as `virtual.mp4`, put it under `Camera1` directory.
+
+5. If there is a toast message when you take photoes in app ("发现拍照"),it shows the photo's resolution. You need to prepare a photo which has the same resolution. Name it as `1000.bmp` . Put it under `Camera1` directory. (it support other image format renamed to bmp ). If there isn't a toast message , `1000.bmp` will have nothing to do with replacing capture.
+
+6. If you need to play video's sound, create `no-silent.jpg` under `Camera1` directory.
+
+7. If you nedd to turn off the module temporarily, create `disable.jpg` under `Camera1` directory.
+
+## FAQ
+
+Q1. The problems of front camera?
+A1. In most cases , the video for replacing front camera need to be flipped horizontally and rotated right 90 degrees. The video's resolution **after being processed** need to same with that in toast message. But in some came, it dosen't need to make adjustment, so you need to judge it according to situation.
+
+Q2. Black screen ? Open camera fail ?
+A2. Till now ,there are a few apps that can't be hooked, especially the system camera. But most of it caused by wrong encoding format (only support `H.264`). Or it caused by wrong `Camera1` directory.
+
+Q3. Blurred screen?
+A3. The resolution of video is wrong.
+
+## Question report:
+
+raise it in issues directly. If it is a bug, please attach with Xposed log.
+
+## Credit
+
+Provide hook method: https://github.com/wangwei1237/CameraHook
+
+H.264 hardware decode: https://github.com/zhantong/Android-VideoToImages
+
+JPEG-YUV convert: https://blog.csdn.net/jacke121/article/details/73888732
diff --git a/README_tc.md b/README_tc.md
new file mode 100644
index 0000000..5eabecf
--- /dev/null
+++ b/README_tc.md
@@ -0,0 +1,53 @@
+# android_virtual_cam
+
+[简体中文](./README.md) | [繁體中文](./README_tc.md) | [English](./README_en.md)
+
+基於Xposed的虛擬攝影機
+
+# 請勿用於任何非法用途,所有後果自負。
+
+## 支持平臺:
+
+- 安卓5.0+
+
+## 使用方法
+
+1. 安裝此模組,並在Xposed中啟用此模組,Lsposed等包含作用域的框架需要選擇目標app,無需選擇系統框架。
+
+2. 在系統設定中,授予目標應用讀取本地存儲的許可權,並強制結束目標應用程序。 若應用程序未申請此許可權,請見步驟3。
+
+3. 打開目標應用,若應用未能獲得讀取存儲的許可權,則會以氣泡消息提示,`Camera1`目錄被重定向至應用程序私有目錄`/[內部存儲]/Android/data/[應用包名]/files/Camera1/`。 若未提示,則默認`Camera1`目錄為`/[內部存儲]/DCIM/Camera1/`。 若目錄不存在,請手動創建。
+
+> 注意:私有目錄下的`Camera1`僅對該應用單獨生效。
+
+4. 在目標應用中打開相機預覽,會以氣泡消息提示“寬:……高:……”,需要根據此解析度數據製作替換影片,放置於`Camera1`目錄下,並命名為`virtual.mp4`,若打開相機並無提示消息,則無需調整影片解析度。
+
+5. 若在目標應用中拍照卻顯示真實圖片,且出現氣泡消息`發現拍照`和解析度,則需根據此解析度數據準備一張照片,命名為`1000.bmp`,放入`Camera1`目錄下(支持其它格式改尾碼為bmp)。 如果拍照時無氣泡消息提示,則`1000.bmp`無效。
+
+6. 如果需要播放影片的聲音,需在`Camera1`目錄下創建`no-silent.jpg`檔案。
+
+7. 如果需要臨時停用影片替換,需在`Camera1`目錄下創建`disable.jpg`。
+
+## 常見問題
+
+A1. 前置攝影機方向問題?
+Q1.大多數情况下,替換前置攝影機的影片需要水准翻轉並右旋90度,並且影片***處理後***的解析度應與氣泡消息內解析度相同。 但有時這並不需要,具體請根據實際情況判斷。
+
+Q2. 畫面黑屏,相機啟動失敗?
+A2. 現時有些應用並不能成功替換(特別是系統相機),但剩下的大多是由於影片**編碼格式**不正確(並非封裝格式),現時僅支持`H.264`編碼格式。 或者時因為影片路徑不對。
+
+Q3. 畫面花屏?
+A3. 影片解析度不對。
+
+## 迴響問題
+
+請直接在issues中迴響,如果為BUG迴響,請附帶日誌資訊。
+
+
+##致謝:
+
+提供HOOK思路: https://github.com/wangwei1237/CameraHook
+
+H264硬解碼: https://github.com/zhantong/Android-VideoToImages
+
+JPEG轉YUV: https://blog.csdn.net/jacke121/article/details/73888732
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 22a0f0b..b0dce34 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,10 +9,10 @@ android {
applicationId "com.example.vcam"
minSdk 21
targetSdk 28
- versionCode 17
- versionName "3.4"
+ versionCode 18
+ versionName "3.5"
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -28,13 +28,7 @@ android {
}
dependencies {
-
- implementation 'androidx.appcompat:appcompat:1.2.0'
- implementation 'com.google.android.material:material:1.3.0'
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
compileOnly 'de.robv.android.xposed:api:82'
compileOnly 'de.robv.android.xposed:api:82:sources'
- testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.2'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+
}
\ No newline at end of file
diff --git a/app/release/app-release.apk b/app/release/app-release.apk
index d58736d..e9da87d 100644
Binary files a/app/release/app-release.apk and b/app/release/app-release.apk differ
diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json
index 9177647..1a47272 100644
--- a/app/release/output-metadata.json
+++ b/app/release/output-metadata.json
@@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
- "versionCode": 17,
- "versionName": "3.4",
+ "versionCode": 18,
+ "versionName": "3.5",
"outputFile": "app-release.apk"
}
],
diff --git a/app/src/main/java/com/example/vcam/HookMain.java b/app/src/main/java/com/example/vcam/HookMain.java
index d60a6c2..78c8641 100644
--- a/app/src/main/java/com/example/vcam/HookMain.java
+++ b/app/src/main/java/com/example/vcam/HookMain.java
@@ -1,9 +1,11 @@
package com.example.vcam;
+import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
@@ -30,8 +32,10 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -73,6 +77,8 @@ public class HookMain implements IXposedHookLoadPackage {
public static int onemwidth;
public static Class camera_callback_calss;
+ public static String video_path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/";
+
public static Surface c2_preview_Surfcae;
public static Surface c2_preview_Surfcae_1;
public static Surface c2_reader_Surfcae;
@@ -84,6 +90,8 @@ public class HookMain implements IXposedHookLoadPackage {
public static SurfaceTexture c2_virtual_surfaceTexture;
public boolean need_recreate;
+ public static String last_package_name;
+
public int c2_ori_width = 1280;
public int c2_ori_height = 720;
@@ -91,12 +99,16 @@ public class HookMain implements IXposedHookLoadPackage {
public Context toast_content;
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Exception {
- File file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ File file = new File(video_path + "virtual.mp4");
if (file.exists()) {
Class cameraclass = XposedHelpers.findClass("android.hardware.Camera", lpparam.classLoader);
XposedHelpers.findAndHookMethod(cameraclass, "setPreviewTexture", SurfaceTexture.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
+ File control_file = new File(video_path + "disable.jpg");
+ if (control_file.exists()) {
+ return;
+ }
if (is_hooked) {
is_hooked = false;
return;
@@ -108,22 +120,22 @@ protected void beforeHookedMethod(MethodHookParam param) {
return;
}
if (reallycamera != null && reallycamera.equals(param.thisObject)) {
- param.args[0] = HookMain.virtual_st;
- XposedBridge.log("发现重复" + reallycamera.toString());
+ param.args[0] = virtual_st;
+ XposedBridge.log("【VCAM】发现重复" + reallycamera.toString());
return;
} else {
- XposedBridge.log("创建预览");
+ XposedBridge.log("【VCAM】创建预览");
}
reallycamera = (Camera) param.thisObject;
- HookMain.msurftext = (SurfaceTexture) param.args[0];
- if (HookMain.virtual_st == null) {
- HookMain.virtual_st = new SurfaceTexture(10);
+ msurftext = (SurfaceTexture) param.args[0];
+ if (virtual_st == null) {
+ virtual_st = new SurfaceTexture(10);
} else {
- HookMain.virtual_st.release();
- HookMain.virtual_st = new SurfaceTexture(10);
+ virtual_st.release();
+ virtual_st = new SurfaceTexture(10);
}
- param.args[0] = HookMain.virtual_st;
+ param.args[0] = virtual_st;
}
});
} else {
@@ -136,18 +148,18 @@ protected void beforeHookedMethod(MethodHookParam param) {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
c2_state_callback = param.args[1].getClass();
- File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/disable.jpg");
+ File control_file = new File(video_path + "disable.jpg");
if (control_file.exists()) {
return;
}
- File file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ File file = new File(video_path + "virtual.mp4");
if (!file.exists()) {
if (toast_content != null) {
Toast.makeText(toast_content, "不存在替换视频", Toast.LENGTH_SHORT).show();
return;
}
}
- XposedBridge.log("1位参数初始化相机,类:" + c2_state_callback.toString());
+ XposedBridge.log("【VCAM】1位参数初始化相机,类:" + c2_state_callback.toString());
is_first_hook_build = true;
process_camera2_init(c2_state_callback);
}
@@ -158,11 +170,11 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
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 {
- File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/disable.jpg");
+ File control_file = new File(video_path + "disable.jpg");
if (control_file.exists()) {
return;
}
- File file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ File file = new File(video_path + "virtual.mp4");
if (!file.exists()) {
if (toast_content != null) {
Toast.makeText(toast_content, "不存在替换视频", Toast.LENGTH_SHORT).show();
@@ -170,7 +182,7 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
}
}
c2_state_callback = param.args[2].getClass();
- XposedBridge.log("2位参数初始化相机,类:" + c2_state_callback.toString());
+ XposedBridge.log("【VCAM】2位参数初始化相机,类:" + c2_state_callback.toString());
is_first_hook_build = true;
process_camera2_init(c2_state_callback);
}
@@ -217,7 +229,7 @@ protected void beforeHookedMethod(MethodHookParam param) {
XposedHelpers.findAndHookMethod("android.hardware.Camera", lpparam.classLoader, "takePicture", Camera.ShutterCallback.class, Camera.PictureCallback.class, Camera.PictureCallback.class, Camera.PictureCallback.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
- XposedBridge.log("4参数拍照");
+ XposedBridge.log("【VCAM】4参数拍照");
if (param.args[1] != null) {
process_a_shot_YUV(param);
}
@@ -257,7 +269,49 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
if (param.args[0] instanceof Application) {
- toast_content = ((Application) param.args[0]).getApplicationContext();
+ try {
+ toast_content = ((Application) param.args[0]).getApplicationContext();
+ }catch (Exception ee){
+ XposedBridge.log("【VCAM】"+ee.toString());
+ }
+ if (toast_content != null){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ int auth_statue = PackageManager.PERMISSION_DENIED;
+ try {
+ auth_statue = toast_content.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
+ }catch (Exception eee){
+
+ }
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ auth_statue = toast_content.checkSelfPermission(Manifest.permission.MANAGE_EXTERNAL_STORAGE);
+ }
+ }catch (Exception eee){
+
+ }
+ if ( auth_statue != PackageManager.PERMISSION_GRANTED){
+ File shown_file = new File(Environment.getExternalStorageDirectory()+"/Android/data/"+lpparam.packageName+"/files/Camera1/" + "has_shown.jpg");
+ if (!(lpparam.packageName.equals(BuildConfig.APPLICATION_ID) || shown_file.exists())) {
+ Toast.makeText(toast_content, "未授予读取本地目录权限,请检查权限\nCamera1目前重定向为 " + Environment.getExternalStorageDirectory() + "/Android/data/" + lpparam.packageName + "/files/Camera1/", Toast.LENGTH_LONG).show();
+ String path = Environment.getExternalStorageDirectory()+"/Android/data/"+lpparam.packageName+"/files/Camera1/" ;
+ try {
+ FileOutputStream fos = new FileOutputStream(path+ "has_shown.jpg");
+ String info = "shown";
+ fos.write(info.getBytes());
+ fos.flush();
+ fos.close();
+ } catch (Exception e) {
+ XposedBridge.log("【VCAM】"+ e.toString());
+ }
+ }
+ video_path = Environment.getExternalStorageDirectory()+"/Android/data/"+lpparam.packageName+"/files/Camera1/";
+ }else {
+ video_path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/";
+ }
+ }else {
+ video_path = Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/";
+ }
+ }
}
}
});
@@ -265,15 +319,19 @@ protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedHelpers.findAndHookMethod("android.hardware.Camera", lpparam.classLoader, "startPreview", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- File file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ File file = new File(video_path + "virtual.mp4");
if (!file.exists()) {
if (toast_content != null) {
Toast.makeText(toast_content, "不存在替换视频", Toast.LENGTH_SHORT).show();
return;
}
}
+ File control_file = new File(video_path + "disable.jpg");
+ if (control_file.exists()) {
+ return;
+ }
is_someone_playing = false;
- XposedBridge.log("开始预览");
+ XposedBridge.log("【VCAM】开始预览");
start_preview_camera = (Camera) param.thisObject;
if (ori_holder != null) {
@@ -284,73 +342,73 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
mplayer1 = null;
mplayer1 = new MediaPlayer();
}
- if (!HookMain.ori_holder.getSurface().isValid() || HookMain.ori_holder == null) {
+ if (!ori_holder.getSurface().isValid() || ori_holder == null) {
return;
}
- HookMain.mplayer1.setSurface(HookMain.ori_holder.getSurface());
- File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/no-silent.jpg");
+ mplayer1.setSurface(ori_holder.getSurface());
+ File sfile = new File(video_path + "no-silent.jpg");
if (!(sfile.exists() && (!is_someone_playing))) {
- HookMain.mplayer1.setVolume(0, 0);
+ mplayer1.setVolume(0, 0);
is_someone_playing = false;
} else {
is_someone_playing = true;
}
- HookMain.mplayer1.setLooping(true);
+ mplayer1.setLooping(true);
- HookMain.mplayer1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ mplayer1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
- HookMain.mplayer1.start();
+ mplayer1.start();
}
});
try {
- HookMain.mplayer1.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
- HookMain.mplayer1.prepare();
+ mplayer1.setDataSource(video_path + "virtual.mp4");
+ mplayer1.prepare();
} catch (IOException e) {
- XposedBridge.log(e.toString());
+ XposedBridge.log("【VCAM】"+e.toString());
}
}
if (msurftext != null) {
- if (HookMain.msurf == null) {
- HookMain.msurf = new Surface(HookMain.msurftext);
+ if (msurf == null) {
+ msurf = new Surface(msurftext);
} else {
- HookMain.msurf.release();
- HookMain.msurf = new Surface(HookMain.msurftext);
+ msurf.release();
+ msurf = new Surface(msurftext);
}
- if (HookMain.mMedia == null) {
- HookMain.mMedia = new MediaPlayer();
+ if (mMedia == null) {
+ mMedia = new MediaPlayer();
} else {
- HookMain.mMedia.release();
- HookMain.mMedia = new MediaPlayer();
+ mMedia.release();
+ mMedia = new MediaPlayer();
}
- HookMain.mMedia.setSurface(HookMain.msurf);
+ mMedia.setSurface(msurf);
- File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/no-silent.jpg");
+ File sfile = new File(video_path + "no-silent.jpg");
if (!(sfile.exists() && (!is_someone_playing))) {
- HookMain.mMedia.setVolume(0, 0);
+ mMedia.setVolume(0, 0);
is_someone_playing = false;
} else {
is_someone_playing = true;
}
- HookMain.mMedia.setLooping(true);
+ mMedia.setLooping(true);
- HookMain.mMedia.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ mMedia.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
- HookMain.mMedia.start();
+ mMedia.start();
}
});
try {
- HookMain.mMedia.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
- HookMain.mMedia.prepare();
+ mMedia.setDataSource(video_path + "virtual.mp4");
+ mMedia.prepare();
} catch (IOException e) {
- XposedBridge.log(e.toString());
+ XposedBridge.log("【VCAM】" + e.toString());
}
}
}
@@ -359,14 +417,18 @@ public void onPrepared(MediaPlayer mp) {
XposedHelpers.findAndHookMethod("android.hardware.Camera", lpparam.classLoader, "setPreviewDisplay", SurfaceHolder.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- XposedBridge.log("添加Surfaceview预览");
- File file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ XposedBridge.log("【VCAM】添加Surfaceview预览");
+ File file = new File(video_path + "virtual.mp4");
if (!file.exists()) {
if (toast_content != null) {
Toast.makeText(toast_content, "不存在替换视频", Toast.LENGTH_SHORT).show();
return;
}
}
+ File control_file = new File(video_path + "disable.jpg");
+ if (control_file.exists()) {
+ return;
+ }
mcamera1 = (Camera) param.thisObject;
ori_holder = (SurfaceHolder) param.args[0];
if (c1_fake_texture == null) {
@@ -397,7 +459,7 @@ protected void beforeHookedMethod(MethodHookParam param) {
if (param.args[0] == null) {
return;
}
- File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/disable.jpg");
+ File control_file = new File(video_path + "disable.jpg");
if (control_file.exists()) {
return;
}
@@ -419,7 +481,7 @@ protected void beforeHookedMethod(MethodHookParam param) {
}
}
}
- XposedBridge.log("添加目标:" + param.args[0].toString());
+ XposedBridge.log("【VCAM】添加目标:" + param.args[0].toString());
param.args[0] = c2_virtual_surface;
}
@@ -428,11 +490,11 @@ protected void beforeHookedMethod(MethodHookParam param) {
XposedHelpers.findAndHookMethod("android.hardware.camera2.CaptureRequest.Builder", lpparam.classLoader, "build", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- File control_file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/disable.jpg");
+ File control_file = new File(video_path + "disable.jpg");
if (control_file.exists()) {
return;
}
- XposedBridge.log("开始build请求");
+ XposedBridge.log("【VCAM】开始build请求");
process_camera2_play();
}
});
@@ -462,12 +524,12 @@ protected void beforeHookedMethod(MethodHookParam param) {
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("应用创建了渲染器:宽:" + param.args[0] + " 高:" + param.args[1] + "格式" + param.args[2]);
+ 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];
Imagereader_format = (int) param.args[2];
if (toast_content != null) {
- Toast.makeText(toast_content, "应用创建了渲染器:\n宽:" + param.args[0] + "\n高:" + param.args[1] + "\n一般只需要匹配宽高比", Toast.LENGTH_LONG).show();
+ Toast.makeText(toast_content, "应用创建了渲染器:\n宽:" + param.args[0] + "\n高:" + param.args[1] + "\n一般只需要宽高比与视频相同", Toast.LENGTH_LONG).show();
}
}
});
@@ -477,7 +539,7 @@ protected void beforeHookedMethod(MethodHookParam param) {
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
- XposedBridge.log("onCaptureFailed" + "原因:" + ((CaptureFailure) param.args[2]).getReason());
+ XposedBridge.log("【VCAM】onCaptureFailed" + "原因:" + ((CaptureFailure) param.args[2]).getReason());
}
});
@@ -485,58 +547,58 @@ protected void beforeHookedMethod(MethodHookParam param) {
public void process_camera2_play() {
if (c2_preview_Surfcae != null) {
- if (HookMain.c2_player == null) {
- HookMain.c2_player = new MediaPlayer();
+ if (c2_player == null) {
+ c2_player = new MediaPlayer();
} else {
- HookMain.c2_player.release();
- HookMain.c2_player = new MediaPlayer();
+ c2_player.release();
+ c2_player = new MediaPlayer();
}
- HookMain.c2_player.setSurface(HookMain.c2_preview_Surfcae);
- File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/no-silent.jpg");
+ c2_player.setSurface(c2_preview_Surfcae);
+ File sfile = new File(video_path + "no-silent.jpg");
if (!sfile.exists()) {
- HookMain.c2_player.setVolume(0, 0);
+ c2_player.setVolume(0, 0);
}
- HookMain.c2_player.setLooping(true);
+ c2_player.setLooping(true);
- HookMain.c2_player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ c2_player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
- HookMain.c2_player.start();
+ c2_player.start();
}
});
try {
- HookMain.c2_player.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
- HookMain.c2_player.prepare();
+ c2_player.setDataSource(video_path + "virtual.mp4");
+ c2_player.prepare();
} catch (IOException e) {
- XposedBridge.log(e.toString());
+ XposedBridge.log("【VCAM】"+e.toString());
}
}
if (c2_preview_Surfcae_1 != null) {
- if (HookMain.c2_player_1 == null) {
- HookMain.c2_player_1 = new MediaPlayer();
+ if (c2_player_1 == null) {
+ c2_player_1 = new MediaPlayer();
} else {
- HookMain.c2_player_1.release();
- HookMain.c2_player_1 = new MediaPlayer();
+ c2_player_1.release();
+ c2_player_1 = new MediaPlayer();
}
- HookMain.c2_player_1.setSurface(HookMain.c2_preview_Surfcae_1);
- File sfile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/no-silent.jpg");
+ c2_player_1.setSurface(c2_preview_Surfcae_1);
+ File sfile = new File(video_path + "no-silent.jpg");
if (!sfile.exists()) {
- HookMain.c2_player_1.setVolume(0, 0);
+ c2_player_1.setVolume(0, 0);
}
- HookMain.c2_player_1.setLooping(true);
+ c2_player_1.setLooping(true);
- HookMain.c2_player_1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ c2_player_1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
- HookMain.c2_player_1.start();
+ c2_player_1.start();
}
});
try {
- HookMain.c2_player_1.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
- HookMain.c2_player_1.prepare();
+ c2_player_1.setDataSource(video_path + "virtual.mp4");
+ c2_player_1.prepare();
} catch (IOException e) {
- XposedBridge.log(e.toString());
+ XposedBridge.log("【VCAM】"+e.toString());
}
}
@@ -554,10 +616,10 @@ public void onPrepared(MediaPlayer mp) {
} else {
c2_hw_decode_obj.setSaveFrames(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera2/", OutputImageFormat.NV21);
}
- c2_hw_decode_obj.set_surfcae(HookMain.c2_reader_Surfcae);
- c2_hw_decode_obj.decode(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ c2_hw_decode_obj.set_surfcae(c2_reader_Surfcae);
+ c2_hw_decode_obj.decode(video_path + "virtual.mp4");
} catch (Throwable throwable) {
- XposedBridge.log(throwable.toString());
+ XposedBridge.log("【VCAM】"+throwable.toString());
}
}
@@ -574,17 +636,17 @@ public void onPrepared(MediaPlayer mp) {
} else {
c2_hw_decode_obj_1.setSaveFrames(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera2/", OutputImageFormat.NV21);
}
- c2_hw_decode_obj_1.set_surfcae(HookMain.c2_reader_Surfcae_1);
- c2_hw_decode_obj_1.decode(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ 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(throwable.toString());
+ XposedBridge.log("【VCAM】"+throwable.toString());
}
}
}
public Surface create_virtual_surface() {
if (need_recreate) {
- XposedBridge.log("重建垃圾场");
+ XposedBridge.log("【VCAM】重建垃圾场");
if (c2_virtual_surfaceTexture != null) {
c2_virtual_surfaceTexture.release();
c2_virtual_surfaceTexture = null;
@@ -637,17 +699,17 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
c2_reader_Surfcae = null;
c2_preview_Surfcae = null;
is_first_hook_build = true;
- XposedBridge.log("打开相机C2");
+ XposedBridge.log("【VCAM】打开相机C2");
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 {
create_virtual_surface();
- XposedBridge.log("创捷捕获,原始:" + paramd.args[0].toString() + "虚拟:" + HookMain.c2_virtual_surface.toString());
- paramd.args[0] = Arrays.asList(HookMain.c2_virtual_surface);
+ XposedBridge.log("【VCAM】创捷捕获,原始:" + paramd.args[0].toString() + "虚拟:" + c2_virtual_surface.toString());
+ paramd.args[0] = Arrays.asList(c2_virtual_surface);
XposedHelpers.findAndHookMethod(paramd.args[1].getClass(), "onConfigureFailed", CameraCaptureSession.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- XposedBridge.log("onConfigureFailed :" + param.args[0].toString());
+ XposedBridge.log("【VCAM】onConfigureFailed :" + param.args[0].toString());
}
});
@@ -655,7 +717,7 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedHelpers.findAndHookMethod(paramd.args[1].getClass(), "onConfigured", CameraCaptureSession.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- XposedBridge.log("onConfigured :" + param.args[0].toString());
+ XposedBridge.log("【VCAM】onConfigured :" + param.args[0].toString());
}
});
@@ -716,7 +778,7 @@ protected void afterHookedMethod(MethodHookParam paramd) throws Throwable {
XposedHelpers.findAndHookMethod(hooked_class, "onError", CameraDevice.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- XposedBridge.log("相机错误onerror:" + (int) param.args[1]);
+ XposedBridge.log("【VCAM】相机错误onerror:" + (int) param.args[1]);
}
});
@@ -725,7 +787,7 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedHelpers.findAndHookMethod(hooked_class, "onDisconnected", CameraDevice.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
- XposedBridge.log("相机断开onDisconnected :");
+ XposedBridge.log("【VCAM】相机断开onDisconnected :");
}
});
@@ -735,9 +797,9 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
public void process_a_shot_jpeg(XC_MethodHook.MethodHookParam param, int index) {
try {
- XposedBridge.log("第二个jpeg:" + param.args[index].toString());
+ XposedBridge.log("【VCAM】第二个jpeg:" + param.args[index].toString());
} catch (Exception eee) {
- XposedBridge.log(eee.toString());
+ XposedBridge.log("【VCAM】"+eee.toString());
}
Class callback = param.args[index].getClass();
@@ -749,17 +811,21 @@ protected void beforeHookedMethod(MethodHookParam paramd) throws Throwable {
Camera loaclcam = (Camera) paramd.args[1];
onemwidth = loaclcam.getParameters().getPreviewSize().width;
onemhight = loaclcam.getParameters().getPreviewSize().height;
- XposedBridge.log("JPEG拍照回调初始化:宽:" + onemwidth + "高:" + onemhight + "对应的类:" + loaclcam.toString());
+ XposedBridge.log("【VCAM】JPEG拍照回调初始化:宽:" + onemwidth + "高:" + onemhight + "对应的类:" + loaclcam.toString());
if (toast_content != null) {
Toast.makeText(toast_content, "发现拍照\n宽:" + onemwidth + "\n高:" + onemhight + "\n格式:JPEG", Toast.LENGTH_LONG).show();
}
- Bitmap pict = getBMP(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/1000.bmp");
+ File control_file = new File(video_path + "disable.jpg");
+ if (control_file.exists()) {
+ return;
+ }
+ Bitmap pict = getBMP(video_path + "1000.bmp");
ByteArrayOutputStream temp_array = new ByteArrayOutputStream();
pict.compress(Bitmap.CompressFormat.JPEG, 100, temp_array);
byte[] jpeg_data = temp_array.toByteArray();
paramd.args[0] = jpeg_data;
} catch (Exception ee) {
- XposedBridge.log(ee.toString());
+ XposedBridge.log("【VCAM】"+ee.toString());
}
}
});
@@ -767,9 +833,9 @@ protected void beforeHookedMethod(MethodHookParam paramd) throws Throwable {
public void process_a_shot_YUV(XC_MethodHook.MethodHookParam param) {
try {
- XposedBridge.log("发现拍照YUV:" + param.args[1].toString());
+ XposedBridge.log("【VCAM】发现拍照YUV:" + param.args[1].toString());
} catch (Exception eee) {
- XposedBridge.log(eee.toString());
+ XposedBridge.log("【VCAM】"+eee.toString());
}
Class callback = param.args[1].getClass();
XposedHelpers.findAndHookMethod(callback, "onPictureTaken", byte[].class, android.hardware.Camera.class, new XC_MethodHook() {
@@ -779,59 +845,73 @@ protected void beforeHookedMethod(MethodHookParam paramd) throws Throwable {
Camera loaclcam = (Camera) paramd.args[1];
onemwidth = loaclcam.getParameters().getPreviewSize().width;
onemhight = loaclcam.getParameters().getPreviewSize().height;
- XposedBridge.log("YUV拍照回调初始化:宽:" + onemwidth + "高:" + onemhight + "对应的类:" + loaclcam.toString());
+ XposedBridge.log("【VCAM】YUV拍照回调初始化:宽:" + onemwidth + "高:" + onemhight + "对应的类:" + loaclcam.toString());
if (toast_content != null) {
Toast.makeText(toast_content, "发现拍照\n宽:" + onemwidth + "\n高:" + onemhight + "\n格式:YUV_420_888" , Toast.LENGTH_LONG).show();
}
- input = getYUVByBitmap(getBMP(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/1000.bmp"));
+ File control_file = new File(video_path + "disable.jpg");
+ if (control_file.exists()) {
+ return;
+ }
+ input = getYUVByBitmap(getBMP(video_path + "1000.bmp"));
paramd.args[0] = input;
} catch (Exception ee) {
- XposedBridge.log(ee.toString());
+ XposedBridge.log("【VCAM】"+ee.toString());
}
}
});
}
public void process_callback(XC_MethodHook.MethodHookParam param) {
- Class nmb = param.args[0].getClass();
- XposedHelpers.findAndHookMethod(nmb, "onPreviewFrame", byte[].class, android.hardware.Camera.class, new XC_MethodHook() {
+ Class preview_cb_class = param.args[0].getClass();
+ int need_stop = 0;
+ File control_file = new File(video_path + "disable.jpg");
+ if (control_file.exists()) {
+ need_stop = 1;
+ }
+ File file = new File(video_path + "virtual.mp4");
+ if (!file.exists()) {
+ if (toast_content != null) {
+ Toast.makeText(toast_content, "不存在替换视频", Toast.LENGTH_SHORT).show();
+ need_stop = 1;
+ }
+ }
+ int finalNeed_stop = need_stop;
+ XposedHelpers.findAndHookMethod(preview_cb_class, "onPreviewFrame", byte[].class, android.hardware.Camera.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam paramd) throws Throwable {
Camera localcam = (android.hardware.Camera) paramd.args[1];
if (localcam.equals(data_camera)) {
while (data_buffer == null) {
}
- System.arraycopy(HookMain.data_buffer, 0, paramd.args[0], 0, Math.min(HookMain.data_buffer.length, ((byte[]) paramd.args[0]).length));
+ System.arraycopy(data_buffer, 0, paramd.args[0], 0, Math.min(data_buffer.length, ((byte[]) paramd.args[0]).length));
} else {
- camera_callback_calss = nmb;
- HookMain.data_camera = (android.hardware.Camera) paramd.args[1];
+ camera_callback_calss = preview_cb_class;
+ data_camera = (android.hardware.Camera) paramd.args[1];
mwidth = data_camera.getParameters().getPreviewSize().width;
mhight = data_camera.getParameters().getPreviewSize().height;
int frame_Rate = data_camera.getParameters().getPreviewFrameRate();
- XposedBridge.log("帧预览回调初始化:宽:" + mwidth + " 高:" + mhight + " 帧率:" + frame_Rate);
+ XposedBridge.log("【VCAM】帧预览回调初始化:宽:" + mwidth + " 高:" + mhight + " 帧率:" + frame_Rate);
if (toast_content != null) {
- Toast.makeText(toast_content, "发现预览\n宽:" + mwidth + "\n高:" + mhight + "\n" + "需要完全匹配分辨率", Toast.LENGTH_LONG).show();
+ Toast.makeText(toast_content, "发现预览\n宽:" + mwidth + "\n高:" + mhight + "\n" + "需要视频分辨率与其完全相同", Toast.LENGTH_LONG).show();
}
- File file = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
- if (!file.exists()) {
- if (toast_content != null) {
- Toast.makeText(toast_content, "不存在替换视频", Toast.LENGTH_SHORT).show();
- return;
- }
+ if (finalNeed_stop == 1){
+ return;
}
if (hw_decode_obj != null) {
hw_decode_obj.stopDecode();
}
hw_decode_obj = new VideoToFrames();
hw_decode_obj.setSaveFrames("", OutputImageFormat.NV21);
- hw_decode_obj.decode(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera1/virtual.mp4");
+ hw_decode_obj.decode(video_path + "virtual.mp4");
while (data_buffer == null) {
}
- System.arraycopy(HookMain.data_buffer, 0, paramd.args[0], 0, Math.min(HookMain.data_buffer.length, ((byte[]) paramd.args[0]).length));
+ System.arraycopy(data_buffer, 0, paramd.args[0], 0, Math.min(data_buffer.length, ((byte[]) paramd.args[0]).length));
}
}
});
+
}
//以下代码来源:https://blog.csdn.net/jacke121/article/details/73888732
@@ -953,7 +1033,7 @@ public void run() {
@SuppressLint("WrongConstant")
public void videoDecode(String videoFilePath) throws IOException {
- XposedBridge.log("开始解码");
+ XposedBridge.log("【VCAM】开始解码");
MediaExtractor extractor = null;
MediaCodec decoder = null;
try {
@@ -1063,7 +1143,7 @@ private void decodeFramesToImage(MediaCodec decoder, MediaExtractor extractor, M
try {
mQueue.put(arr);
} catch (InterruptedException e) {
- XposedBridge.log(e.toString());
+ XposedBridge.log("【VCAM】"+e.toString());
}
}
if (outputImageFormat != null) {
@@ -1076,8 +1156,8 @@ private void decodeFramesToImage(MediaCodec decoder, MediaExtractor extractor, M
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
- XposedBridge.log(e.toString());
- XposedBridge.log("线程延迟出错");
+ XposedBridge.log("【VCAM】"+e.toString());
+ XposedBridge.log("【VCAM】线程延迟出错");
}
}
decoder.releaseOutputBuffer(outputBufferId, true);
diff --git a/app/src/main/java/com/example/vcam/MainActivity.java b/app/src/main/java/com/example/vcam/MainActivity.java
index b97dbf9..7a3f088 100644
--- a/app/src/main/java/com/example/vcam/MainActivity.java
+++ b/app/src/main/java/com/example/vcam/MainActivity.java
@@ -1,24 +1,15 @@
package com.example.vcam;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.UriMatcher;
-import android.database.Cursor;
+import android.app.Activity;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
-import android.widget.EditText;
-import android.widget.Toast;
-public class MainActivity extends AppCompatActivity {
+public class MainActivity extends Activity {
@SuppressLint("WorldReadableFiles")
public void onCreate(Bundle savedInstanceState) {
@@ -26,5 +17,20 @@ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
+ Button repo_button = findViewById(R.id.button);
+
+ repo_button.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+
+ public void onClick(View v) {
+
+ Uri uri = Uri.parse("https://github.com/w2016561536/android_virtual_cam");
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(intent);
+ }
+ });
}
}
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 9aa52c2..72e49a8 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,24 +1,32 @@
-
+ xmlns:android="http://schemas.android.com/apk/res/android">
-
+
+
+
+
-
-
\ No newline at end of file
+ android:text="@string/click_to_go_to_repo" />
+
+
diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml
index 19f2000..0c07a53 100644
--- a/app/src/main/res/values-en/strings.xml
+++ b/app/src/main/res/values-en/strings.xml
@@ -1,20 +1,7 @@
VCAM
- "
-##Detailed usage :
-1. Install this moudle. enable it in Xposed. Framework which has a scope list need to choose target app, but needn't to choose system framework.\n
-2. Create virtual.mp4 and put it under /sdcard/DCIM/Camera1/ ,(if use front camera ,image should be Flip horizontal and right rotation 90 degrees, if you want to hook onPreviewFrame ,the resolution should be matched)\n
-3. If you wants to hook image capture event, you should create 1000.bmp under /sdcard/DCIM/Camera1/ for replace. (if use front camera ,image should be Flip horizontal and right rotation 90 degrees, the resolution should be matched)\n
-4. authorize the target app to access local storage in system.\n
-5. Reboot your phone or shutdown target app.\n
-##bugs with camera2 api, need to disable it?
-create disable.jpg under /sdcard/DCIM/Camera1/ to disable this method hook.\n
-##how to get resolution ??(only hook onPreviewFrame and image capture need it)?
-open camera in target app, and you can find resolution in toast message.\n
-##Needn't mute?
-Create no-silent.jpg under /sdcard/DCIM/Camera1/, and it will play sounds.\n
-open source repo link : https://github.com/w2016561536/android_virtual_cam \n
-DO NOT USE FOR ANY ILLEAGLE INTENTION!!YOU NEED TO TAKE ALL RESPONSIBILITY AND CONSEQUENCE!!"
-
+ click to go to repository
+ Please go to online repository to check for latest update, latest usgae and report bugs
+ DO NOT USE FOR ANY ILLEGAL PURPOSE !!YOU NEED TO TAKE ALL RESPONSIBILITY AND CONSEQUENCE!!
\ No newline at end of file
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
deleted file mode 100644
index a5620ec..0000000
--- a/app/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 8b43ac7..d252964 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -1,21 +1,6 @@
VCAM
-
-## 具体的使用方法: \n\n
-1、安装xposed框架(xposed,edxposed,lsposed等均可,不确定虚拟框架能否使用,已经确定VMOS可用,应用转生不可用) \n\n
-2、安装模块,启用模块,lsposed等包含作用域的框架需要选勾目标app,但无需选勾系统框架。 \n\n
-3、将需要替换的视频命名为virtual.mp4,放在/sdcard/DCIM/Camera1/目录下。(前置摄像头需要水平翻转后右旋90°保存,拦截onPreviewFrame需要匹配分辨率) \n\n
-4、若需要拦截拍照事件,请在/sdcard/DCIM/Camera1/目录下放置 1000.bmp 用于替换,(前置摄像头需要水平翻转后右旋90°保存,需要匹配分辨率) \n\n
-5、在系统设置中授予目标应用访问存储的权限。\n\n
-6、强制结束目标应用/重启手机。
-\n\n
-## 如何获得分辨率??(仅拦截onPreviewFrame和拍照需要,其它系统自动处理) \n
-在目标应用中打开摄像头,可在弹出的toast消息里看见。 \n\n
-## Camera2接口有问题?? \n
-是的,目前Camera2接口的HOOK不是所有应用程序都能生效,部分app报错打开相机失败,如果想停用Camera2接口的HOOK,可在/sdcard/DCIM/Camera1/下创建disable.jpg,以停用此项HOOK \n\n
-## 我不需要静音??\n
-在/sdcard/DCIM/Camera1/下创建no-silent.jpg,就不会静音了。\n\n
-开源地址:https://github.com/w2016561536/android_virtual_cam \n\n
-请勿用于任何违法用途,所有后果自负!!!
-
+ 前往仓库
+ 请前往在线仓库来查看使用方法,应用更新和反馈缺陷
+ 请勿用于非法用途,所有后果自负!
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml
index 8b43ac7..e6d9914 100644
--- a/app/src/main/res/values-zh-rHK/strings.xml
+++ b/app/src/main/res/values-zh-rHK/strings.xml
@@ -1,21 +1,6 @@
VCAM
-
-## 具体的使用方法: \n\n
-1、安装xposed框架(xposed,edxposed,lsposed等均可,不确定虚拟框架能否使用,已经确定VMOS可用,应用转生不可用) \n\n
-2、安装模块,启用模块,lsposed等包含作用域的框架需要选勾目标app,但无需选勾系统框架。 \n\n
-3、将需要替换的视频命名为virtual.mp4,放在/sdcard/DCIM/Camera1/目录下。(前置摄像头需要水平翻转后右旋90°保存,拦截onPreviewFrame需要匹配分辨率) \n\n
-4、若需要拦截拍照事件,请在/sdcard/DCIM/Camera1/目录下放置 1000.bmp 用于替换,(前置摄像头需要水平翻转后右旋90°保存,需要匹配分辨率) \n\n
-5、在系统设置中授予目标应用访问存储的权限。\n\n
-6、强制结束目标应用/重启手机。
-\n\n
-## 如何获得分辨率??(仅拦截onPreviewFrame和拍照需要,其它系统自动处理) \n
-在目标应用中打开摄像头,可在弹出的toast消息里看见。 \n\n
-## Camera2接口有问题?? \n
-是的,目前Camera2接口的HOOK不是所有应用程序都能生效,部分app报错打开相机失败,如果想停用Camera2接口的HOOK,可在/sdcard/DCIM/Camera1/下创建disable.jpg,以停用此项HOOK \n\n
-## 我不需要静音??\n
-在/sdcard/DCIM/Camera1/下创建no-silent.jpg,就不会静音了。\n\n
-开源地址:https://github.com/w2016561536/android_virtual_cam \n\n
-请勿用于任何违法用途,所有后果自负!!!
-
+ 前往倉庫
+ 請前往線上倉庫來查看使用方法,應用更新和迴響缺陷
+ 請勿用於非法用途,所有後果自負!
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rMO/strings.xml b/app/src/main/res/values-zh-rMO/strings.xml
index 8b43ac7..e6d9914 100644
--- a/app/src/main/res/values-zh-rMO/strings.xml
+++ b/app/src/main/res/values-zh-rMO/strings.xml
@@ -1,21 +1,6 @@
VCAM
-
-## 具体的使用方法: \n\n
-1、安装xposed框架(xposed,edxposed,lsposed等均可,不确定虚拟框架能否使用,已经确定VMOS可用,应用转生不可用) \n\n
-2、安装模块,启用模块,lsposed等包含作用域的框架需要选勾目标app,但无需选勾系统框架。 \n\n
-3、将需要替换的视频命名为virtual.mp4,放在/sdcard/DCIM/Camera1/目录下。(前置摄像头需要水平翻转后右旋90°保存,拦截onPreviewFrame需要匹配分辨率) \n\n
-4、若需要拦截拍照事件,请在/sdcard/DCIM/Camera1/目录下放置 1000.bmp 用于替换,(前置摄像头需要水平翻转后右旋90°保存,需要匹配分辨率) \n\n
-5、在系统设置中授予目标应用访问存储的权限。\n\n
-6、强制结束目标应用/重启手机。
-\n\n
-## 如何获得分辨率??(仅拦截onPreviewFrame和拍照需要,其它系统自动处理) \n
-在目标应用中打开摄像头,可在弹出的toast消息里看见。 \n\n
-## Camera2接口有问题?? \n
-是的,目前Camera2接口的HOOK不是所有应用程序都能生效,部分app报错打开相机失败,如果想停用Camera2接口的HOOK,可在/sdcard/DCIM/Camera1/下创建disable.jpg,以停用此项HOOK \n\n
-## 我不需要静音??\n
-在/sdcard/DCIM/Camera1/下创建no-silent.jpg,就不会静音了。\n\n
-开源地址:https://github.com/w2016561536/android_virtual_cam \n\n
-请勿用于任何违法用途,所有后果自负!!!
-
+ 前往倉庫
+ 請前往線上倉庫來查看使用方法,應用更新和迴響缺陷
+ 請勿用於非法用途,所有後果自負!
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rSG/strings.xml b/app/src/main/res/values-zh-rSG/strings.xml
index 8b43ac7..e6d9914 100644
--- a/app/src/main/res/values-zh-rSG/strings.xml
+++ b/app/src/main/res/values-zh-rSG/strings.xml
@@ -1,21 +1,6 @@
VCAM
-
-## 具体的使用方法: \n\n
-1、安装xposed框架(xposed,edxposed,lsposed等均可,不确定虚拟框架能否使用,已经确定VMOS可用,应用转生不可用) \n\n
-2、安装模块,启用模块,lsposed等包含作用域的框架需要选勾目标app,但无需选勾系统框架。 \n\n
-3、将需要替换的视频命名为virtual.mp4,放在/sdcard/DCIM/Camera1/目录下。(前置摄像头需要水平翻转后右旋90°保存,拦截onPreviewFrame需要匹配分辨率) \n\n
-4、若需要拦截拍照事件,请在/sdcard/DCIM/Camera1/目录下放置 1000.bmp 用于替换,(前置摄像头需要水平翻转后右旋90°保存,需要匹配分辨率) \n\n
-5、在系统设置中授予目标应用访问存储的权限。\n\n
-6、强制结束目标应用/重启手机。
-\n\n
-## 如何获得分辨率??(仅拦截onPreviewFrame和拍照需要,其它系统自动处理) \n
-在目标应用中打开摄像头,可在弹出的toast消息里看见。 \n\n
-## Camera2接口有问题?? \n
-是的,目前Camera2接口的HOOK不是所有应用程序都能生效,部分app报错打开相机失败,如果想停用Camera2接口的HOOK,可在/sdcard/DCIM/Camera1/下创建disable.jpg,以停用此项HOOK \n\n
-## 我不需要静音??\n
-在/sdcard/DCIM/Camera1/下创建no-silent.jpg,就不会静音了。\n\n
-开源地址:https://github.com/w2016561536/android_virtual_cam \n\n
-请勿用于任何违法用途,所有后果自负!!!
-
+ 前往倉庫
+ 請前往線上倉庫來查看使用方法,應用更新和迴響缺陷
+ 請勿用於非法用途,所有後果自負!
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 35684d2..2639870 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -1,21 +1,6 @@
VCAM
-
-## 具體的使用方法: \n\n
-1、安裝xposed框架(xposed,edxposed,lsposed等均可,不確定虛擬框架能否使用,已經確定VMOS可用,應用轉生不可用) \n\n
-2、安裝模組,啟用模組,lsposed等包含作用域的框架需要選勾目標app,但無需選勾系統框架。 \n\n
-3、將需要取代的影片命名為virtual.mp4,放在/sdcard/DCIM/Camera1/目錄下。(前置攝影機需要水平翻轉後右旋90°儲存,攔截onPreviewFrame需要匹配解析度) \n\n
-4、若需要攔截拍照事件,請在/sdcard/DCIM/Camera1/目錄下放置 1000.bmp 用於取代,(前置攝影機需要水平翻轉後右旋90°儲存,需要匹配解析度) \n\n
-5、在系統設定中授予目標應用訪問儲存的權限。\n\n
-6、強制結束目標應用/重啟手機。
-\n\n
-## 如何獲得解析度??(僅攔截onPreviewFrame和拍照需要,其它系統自動處理) \n
-在目標應用中打開攝影機,可在彈出的toast消息裡看見。 \n\n
-## Camera2介面有問題?? \n
-是的,目前Camera2介面的HOOK不是所有應用程式都能生效,部分app報錯打開相機失敗,如果想停用Camera2介面的HOOK,可在/sdcard/DCIM/Camera1/下建立disable.jpg,以停用此項HOOK \n\n
-## 我不需要靜音??\n
-在/sdcard/DCIM/Camera1/下建立no-silent.jpg,就不會靜音了。\n\n
-開源地址:https://github.com/w2016561536/android_virtual_cam \n\n
-請勿用於任何違法用途,所有後果自負!!!
-
+ 前往倉庫
+ 請前往線上倉庫來查看使用方法,應用更新和迴響缺陷
+ 請勿用於非法用途,所有後果自負!
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index 8b43ac7..d252964 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -1,21 +1,6 @@
VCAM
-
-## 具体的使用方法: \n\n
-1、安装xposed框架(xposed,edxposed,lsposed等均可,不确定虚拟框架能否使用,已经确定VMOS可用,应用转生不可用) \n\n
-2、安装模块,启用模块,lsposed等包含作用域的框架需要选勾目标app,但无需选勾系统框架。 \n\n
-3、将需要替换的视频命名为virtual.mp4,放在/sdcard/DCIM/Camera1/目录下。(前置摄像头需要水平翻转后右旋90°保存,拦截onPreviewFrame需要匹配分辨率) \n\n
-4、若需要拦截拍照事件,请在/sdcard/DCIM/Camera1/目录下放置 1000.bmp 用于替换,(前置摄像头需要水平翻转后右旋90°保存,需要匹配分辨率) \n\n
-5、在系统设置中授予目标应用访问存储的权限。\n\n
-6、强制结束目标应用/重启手机。
-\n\n
-## 如何获得分辨率??(仅拦截onPreviewFrame和拍照需要,其它系统自动处理) \n
-在目标应用中打开摄像头,可在弹出的toast消息里看见。 \n\n
-## Camera2接口有问题?? \n
-是的,目前Camera2接口的HOOK不是所有应用程序都能生效,部分app报错打开相机失败,如果想停用Camera2接口的HOOK,可在/sdcard/DCIM/Camera1/下创建disable.jpg,以停用此项HOOK \n\n
-## 我不需要静音??\n
-在/sdcard/DCIM/Camera1/下创建no-silent.jpg,就不会静音了。\n\n
-开源地址:https://github.com/w2016561536/android_virtual_cam \n\n
-请勿用于任何违法用途,所有后果自负!!!
-
+ 前往仓库
+ 请前往在线仓库来查看使用方法,应用更新和反馈缺陷
+ 请勿用于非法用途,所有后果自负!
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 19f2000..0c07a53 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,20 +1,7 @@
VCAM
- "
-##Detailed usage :
-1. Install this moudle. enable it in Xposed. Framework which has a scope list need to choose target app, but needn't to choose system framework.\n
-2. Create virtual.mp4 and put it under /sdcard/DCIM/Camera1/ ,(if use front camera ,image should be Flip horizontal and right rotation 90 degrees, if you want to hook onPreviewFrame ,the resolution should be matched)\n
-3. If you wants to hook image capture event, you should create 1000.bmp under /sdcard/DCIM/Camera1/ for replace. (if use front camera ,image should be Flip horizontal and right rotation 90 degrees, the resolution should be matched)\n
-4. authorize the target app to access local storage in system.\n
-5. Reboot your phone or shutdown target app.\n
-##bugs with camera2 api, need to disable it?
-create disable.jpg under /sdcard/DCIM/Camera1/ to disable this method hook.\n
-##how to get resolution ??(only hook onPreviewFrame and image capture need it)?
-open camera in target app, and you can find resolution in toast message.\n
-##Needn't mute?
-Create no-silent.jpg under /sdcard/DCIM/Camera1/, and it will play sounds.\n
-open source repo link : https://github.com/w2016561536/android_virtual_cam \n
-DO NOT USE FOR ANY ILLEAGLE INTENTION!!YOU NEED TO TAKE ALL RESPONSIBILITY AND CONSEQUENCE!!"
-
+ click to go to repository
+ Please go to online repository to check for latest update, latest usgae and report bugs
+ DO NOT USE FOR ANY ILLEGAL PURPOSE !!YOU NEED TO TAKE ALL RESPONSIBILITY AND CONSEQUENCE!!
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 670a68b..763beae 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,16 +1,6 @@
-
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index d778d14..b08335a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,10 +3,10 @@ buildscript {
repositories {
google()
mavenCentral()
- maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
- maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
- maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
- maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' }
+ maven { url 'https://maven.aliyun.com/repository/central' }
+ maven { url 'https://maven.aliyun.com/repository/public' }
+ maven { url 'https://maven.aliyun.com/repository/google' }
+ maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.0"
diff --git a/gradle.properties b/gradle.properties
index 52f5917..19a5e9d 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -14,6 +14,3 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
-android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
\ No newline at end of file