安卓虚拟摄像头怎么实现用Xposed Hook在3个关键位置偷换视频流【免费下载链接】android_virtual_camxposed安卓虚拟摄像头 android virtual camera on xposed hook项目地址: https://gitcode.com/gh_mirrors/an/android_virtual_cam安卓虚拟摄像头听起来很玄乎其实目标就一句话让别的应用把一段视频当成摄像头画面。本文围绕Xposed Hook机制从拦截点的选择、视频数据的输送再到部署和排坑把虚拟摄像头的实现思路完整讲透。就算你之前没写过任何 Hook 代码读完后也能对这套偷梁换柱的玩法心中有数。先还原一个让人抓狂的测试现场假设你在做直播类 App 的测试需要反复验证不同分辨率、不同光线下美颜滤镜的表现可工位上光线糟糕现场又没有合适的拍摄对象。又或者你想知道某个 App 是不是在后台偷偷调用摄像头、有没有把画面传出去——这类问题靠人工模拟非常痛苦。这时候你真正需要的不是更好的摄像头而是一个能替真摄像头上班的替身App 照常请求打开相机、照常收到画面数据只不过这些数据不是来自镜头而是来自你指定的一段视频或一张图片。这就是安卓虚拟摄像头存在的意义。它不是一个真实硬件而是运行在系统层的一套障眼法。换个角度理解它是摄像头调用链路上的替身演员你可以这样理解整个机制应用要画面会走一条固定的链路——向系统相机接口发出请求系统把镜头捕捉到的数据回传。整个过程像剧组拍戏导演应用喊开拍镜头真实相机就开始工作。Xposed Hook 干的事情就是在这条链路上安插一个替身演员相机请求依然被正常处理接口签名一个没变应用完全感觉不到异常——但真正递到它手里的已经被调包成了视频帧或图片帧。观众应用看戏看得津津有味完全不知道主角换了人。为什么能做到关键在于 Xposed 框架会在应用进程启动的极早期Zygote 阶段注入我们的代码从而在目标方法真正执行前插入拦截逻辑。方法还没跑参数已经被我们改过了——这就是偷换的时机窗口。所有虚拟摄像头的花样本质上都是在这个时机窗口里做文章。替身演员的 3 个登场位置Hook 点到底选在哪整个方案的核心难点不是解码视频这反而是最简单的而是找对拦截的位置。源码里密密麻麻的 Hook 点归纳起来其实就三大类。位置一预览入口——把纹理和显示目标掉包Camera1 老接口这边拦截的是setPreviewTexture、setPreviewDisplay、startPreview这几个方法。思路很直接应用原本要把画面渲染到某个SurfaceTexture或SurfaceHolder上我们在方法执行前把这个参数偷偷换成自己的假纹理再让一个MediaPlayer把视频画面播到这个假纹理上。画面从哪儿来视频来。应用看到的是什么还是那个熟悉的预览窗口。位置二帧回调——把每一帧数据掉包有些应用不走渲染通道而是通过setPreviewCallback、setPreviewCallbackWithBuffer、setOneShotPreviewCallback直接拿 YUV 字节数组做分析比如人脸识别、扫码。对付这类应用就要在回调方法执行前把参数里的byte[]整体替换成我们解码好的视频帧数据。这里有个细节addCallbackBuffer也一并被 Hook 了为的是让应用反复填充的缓冲区也走我们的数据源避免出现半真半假的穿帮画面。位置三拍照与 Camera2 新接口拍照拦截发生在takePicture之后应用拿到照片回调时我们把里面的 JPEG 或 YUV 数据换成预设图片的编码结果。Camera2 新接口则复杂不少——需要 HookCameraManager.openCamera注意它有两个重载签名Android 9 前后的参数不同、CaptureRequest.Builder.addTarget把应用要绑定的 Surface 换成虚拟 Surface、build甚至要动态 HookCameraDevice上那一大串createCaptureSession系列方法覆盖不同系统版本的调用路径。这也是为什么 Camera2 的支持代码比 Camera1 多出一大截。下面是一段拍照替换的简化示意帮你感受一下偷换的写法XposedHelpers.findAndHookMethod(callback, onPictureTaken, byte[].class, android.hardware.Camera.class, new XC_MethodHook() { Override protected void beforeHookedMethod(MethodHookParam param) { // 把预设图片读出来压缩成 JPEG 字节流 Bitmap pict BitmapFactory.decodeFile(video_path 1000.bmp); ByteArrayOutputStream out new ByteArrayOutputStream(); pict.compress(Bitmap.CompressFormat.JPEG, 100, out); // 关键一步替换回调参数里的真实照片数据 param.args[0] out.toByteArray(); } });视频数据从哪儿来两条截然不同的输送路线搞定了拦截点下一步是拿什么来填。项目里其实准备了两套输送方案按需选用。路线 A预览画面直接用 MediaPlayer 播放。这是最省事的做法——MediaPlayer天然支持setSurface()直接渲染设置成循环播放后视频画面就源源不断地喂给那个被调包的目标。缺点也很明显它只能填预览通道喂不了那些要逐帧分析的应用。路线 B帧数据走 MediaCodec 硬解码。当应用注册了预览帧回调、需要真实字节数据时就轮到VideoToFrames上场了。它的工作流可以概括成四步用MediaExtractor打开视频文件自动识别出视频轨道根据轨道信息创建MediaCodec解码器先查一下设备支持哪些颜色格式选一个兼容的循环dequeueInputBuffer/dequeueOutputBuffer把帧解出来从解码输出的Image中提取数据转换成 NV21 格式写进一个全局缓冲区应用回调触发时再把这块数据System.arraycopy进回调参数。打个比方MediaPlayer 像是整盘端上桌MediaCodec 则是一盘一盘精准分菜——前者省事后者灵活虚拟摄像头真正复杂的部分其实集中在后者。解码循环里还做了按时间戳休眠的节奏控制sleepTime让帧的吐出速度尽量贴近视频本身的帧率否则应用拿到的就是一坨快进的数据。十分钟上手从安装到画面出现光看原理不过瘾下面是一份可以直接照做的实操清单安装并启用模块装好应用后在 Xposed 框架中勾选启用LSPosed 这类带作用域设置的框架需要把目标 App 加进作用域不需要勾选系统框架。给权限、杀进程在系统设置里授予目标应用读取本地存储的权限然后强制结束目标应用让 Hook 干净地重新加载。确认目录打开目标应用正常情况会弹气泡提示Camera1目录位置。默认是/[内部存储]/DCIM/Camera1/如果应用没有存储权限目录会被自动重定向到它的私有目录/[内部存储]/Android/data/[应用包名]/files/Camera1/。准备视频在目标应用里打开相机预览气泡消息会显示宽xxx 高xxx。按这个分辨率制作视频命名为virtual.mp4放进Camera1目录。没弹提示就说明无需纠结分辨率。准备照片如果拍照时气泡提示了分辨率就准备一张对应尺寸的图片命名为1000.bmp放进去其他格式改后缀为 bmp 也能用。重启应用验证再次打开目标应用的相机画面应该已经变成你准备的视频了。关键提示virtual.mp4的分辨率与预览提示的分辨率不一致是绝大多数黑屏花屏的根源。视频和照片都只认Camera1目录下的固定文件名放错位置等于白放。六个文件开关用文件名当遥控器这个项目有个特别朴实的思路把配置文件做成文件存在与否。在Camera1目录下创建或删除某个文件就等于按下对应的遥控器按钮而且全局实时生效不需要重启文件名作用disable.jpg临时停用视频替换回到真实摄像头no_toast.jpg关闭气泡提示no-silent.jpg允许播放视频原声默认静音private_dir.jpg强制所有应用走私有目录方便给每个 App 分配专属视频force_show.jpg让被只提示一次的目录重定向提示重新出现这些开关在应用的主界面里也有对应的开关按钮本质就是帮你创建/删除这些文件手动操作完全等价。踩坑实录黑屏、花屏与方向实战中翻车最多的几个问题逐个对号入座画面全黑、相机打不开先查两件事一是视频路径对不对——很多人会误建出两级目录比如DCIM/Camera1/Camera1/virtual.mp4但正确只需要一级二是目标应用本身可能无法替换特别是系统自带相机这类特殊应用Hook 上去往往没反应。画面花屏十有八九是视频分辨率与预览提示的分辨率对不上。重新用剪辑软件把视频调到提示的尺寸即可。画面扭曲变形分辨率匹配但比例不对或者前置摄像头存在镜像问题。大多数情况下前置摄像头的替换视频需要水平翻转并右旋 90 度处理后的分辨率再对齐提示值。当然个别应用的表现会不一样以实际效果为准。创建disable.jpg没效果注意版本差异老版本4.0 及以下对无存储权限的应用开关文件需要放到它的私有目录下才生效新版本4.1 及以上统一在DCIM/Camera1下创建即可。录像被触发目前MediaRecorder.setCamera只做了发现并提示的处理录像本身还无法拦截——这是项目已知的能力边界使用时要心里有数。除了测试它还能用在哪儿虚拟摄像头的价值远不止测 App。实际使用中你会发现它的场景相当宽开发与兼容性测试模拟各种分辨率、帧率下的相机行为验证 App 的适配逻辑比反复换真机高效得多隐私与安全验证把摄像头架空后观察应用是否仍在后台尝试取景借此判断它有没有越权采集画面的嫌疑演示与教学给团队演示应用如何拿到相机数据或者做摄像头工作原理的教学虚拟画面比真实镜头更可控通话与直播场景的个性化在视频通话中投放定制背景或创意内容省去搭真实场景的麻烦。它还能变得更好优化方向与展望从源码也能看出一些可以打磨的空间解码线程和MediaPlayer的创建销毁比较频繁如果能做资源池复用会减少卡顿stopDecode和对象释放逻辑散布在多处集中管理能降低内存泄漏风险。方向上看这个项目未来可以拓展多视频源轮播、实时滤镜、多摄像头同时虚拟化体验上图形化的配置界面、预设视频库管理也能把上手门槛再降一档。对于想深入研究的开发者来说源码里 Camera2 那一大串防御式Hook 写法本身就是一部生动的系统版本兼容性教材。说到底安卓虚拟摄像头教给我们的不是骗过相机的技巧而是一条理解 Android 系统扩展性的捷径只要理解了 Hook 的时机窗口很多看似不可能的系统级能力其实都有实现的路径。技术本身没有倾向把它用在测试、研究和教学上才是它最值得的样子。【免费下载链接】android_virtual_camxposed安卓虚拟摄像头 android virtual camera on xposed hook项目地址: https://gitcode.com/gh_mirrors/an/android_virtual_cam创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考