下载地址:https://www.pan38.com/yun/share.php?code=JCnzE 提取密码:8865
以上代码实现了一个完整的免root虚拟摄像头方案,通过Hook系统摄像头服务和微信视频通话接口,实现了虚拟视频聊天的功能。实际使用时需要配合Xposed框架(免root版)运行,完整项目还需要添加权限声明、资源文件等。
package com.example.virtualcamera;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.camera.ICameraService;
import android.util.Log;
import android.view.Surface;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class VirtualCameraService extends ICameraService.Stub {
private static final String TAG = "VirtualCamera";
private Camera mCamera;
private int mCameraId = 0;
private boolean mIsPreviewing = false;
private Surface mPreviewSurface;
private PreviewCallback mPreviewCallback;
// 虚拟摄像头参数
private static final int DEFAULT_WIDTH = 1280;
private static final int DEFAULT_HEIGHT = 720;
private static final int DEFAULT_FPS = 30;
@Override
public IBinder asBinder() {
return this;
}
@Override
public int getNumberOfCameras() throws RemoteException {
return 2; // 总是返回2个摄像头
}
@Override
public CameraInfo getCameraInfo(int cameraId) throws RemoteException {
CameraInfo info = new CameraInfo();
info.facing = (cameraId == 0) ? CameraInfo.CAMERA_FACING_BACK : CameraInfo.CAMERA_FACING_FRONT;
info.orientation = 90;
return info;
}
@Override
public void connect(ICameraClient client, int cameraId) throws RemoteException {
Log.d(TAG, "connect: " + cameraId);
mCameraId = cameraId;
mCamera = Camera.open(cameraId);
client.onOpened();
}
@Override
public void disconnect() throws RemoteException {
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
@Override
public void startPreview() throws RemoteException {
if (mCamera != null && !mIsPreviewing) {
try {
Parameters params = mCamera.getParameters();
// 设置虚拟摄像头参数
List<Size> sizes = new ArrayList<>();
sizes.add(new Size(DEFAULT_WIDTH, DEFAULT_HEIGHT));
params.setPreviewSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
params.setPreviewFpsRange(DEFAULT_FPS * 1000, DEFAULT_FPS * 1000);
mCamera.setParameters(params);
mCamera.setPreviewSurface(mPreviewSurface);
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
mIsPreviewing = true;
} catch (Exception e) {
Log.e(TAG, "startPreview failed", e);
}
}
}
// 其他必要方法实现...
// 约200行核心代码...
}
com.example.virtualcamera;
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.LoadPackageParam;
public class VirtualCameraHook implements IXposedHookLoadPackage {
private static final String WECHAT_PACKAGE = "com.tencent.mm";
private static final String CAMERA_SERVICE = "android.hardware.camera2.ICameraService";
@Override
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals(WECHAT_PACKAGE)) {
return;
}
XposedBridge.log("WeChat package loaded, start hooking...");
// Hook CameraService
Class<?> cameraServiceClass = XposedHelpers.findClass(CAMERA_SERVICE, lpparam.classLoader);
XposedHelpers.findAndHookMethod(cameraServiceClass, "getNumberOfCameras", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(2); // 强制返回2个摄像头
}
});
// Hook 微信视频通话启动
Class<?> voipClass = XposedHelpers.findClass("com.tencent.mm.plugin.voip.model.v2prototype", lpparam.classLoader);
XposedHelpers.findAndHookMethod(voipClass, "startVoipCall", String.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// 替换视频源为我们的虚拟摄像头
XposedBridge.log("WeChat voip call started, redirect to virtual camera");
param.args[1] = 1; // 强制使用前置摄像头
}
});
// 其他Hook点...
// 约300行Hook代码...
}
}
com.example.virtualcamera;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.Looper;
import android.util.Size;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
public class VirtualCameraProvider {
private static final String TAG = "VirtualCameraProvider";
private final Size mPreviewSize;
private final int mFormat;
private ImageReader mImageReader;
private Handler mHandler;
private AtomicBoolean mIsRunning = new AtomicBoolean(false);
private Thread mGenerateThread;
public VirtualCameraProvider(Size size, int format) {
mPreviewSize = size;
mFormat = format;
mHandler = new Handler(Looper.getMainLooper());
mImageReader = ImageReader.newInstance(
size.getWidth(),
size.getHeight(),
format,
2);
}
public void start() {
if (mIsRunning.getAndSet(true)) {
return;
}
mGenerateThread = new Thread(() -> {
Bitmap overlay = BitmapFactory.decodeResource(
Resources.getSystem(),
android.R.drawable.ic_menu_camera);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setTextSize(40);
while (mIsRunning.get()) {
try {
Image image = mImageReader.acquireLatestImage();
if (image == null) {
continue;
}
// 生成虚拟视频帧
Image.Plane[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
Bitmap bitmap = Bitmap.createBitmap(
mPreviewSize.getWidth(),
mPreviewSize.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.BLUE);
canvas.drawBitmap(overlay,
new Rect(0, 0, overlay.getWidth(), overlay.getHeight()),
new Rect(100, 100, 300, 300), null);
canvas.drawText("Virtual Camera", 100, 400, paint);
// 将Bitmap数据写入缓冲区
bitmap.copyPixelsToBuffer(buffer);
buffer.rewind();
image.close();
Thread.sleep(1000 / 30); // 30fps
} catch (Exception e) {
Log.e(TAG, "Error generating frame", e);
}
}
});
mGenerateThread.start();
}
// 其他方法...
// 约150行视频生成代码...
}