当前位置: 首页 > news >正文

采用v4l2loopback来实现 虚拟Camera

“Android Camera一站式 学习平台”:https://www.camerahub.cn

Android上面实现虚拟摄像头的思路有很多,本文提供的一个思路是,采用v4l2loopback 来实现。

主要思路是:生成一个video节点,往这个video节点里面写入自定义的内容,应用端则是访问该video节点,进行内容的获取以及展示。

一、v4l2loopback源码下载、编译
1、官网下载源码

https://github.com/gjasny/v4l-utils

2、v4l2loopback源码放置在路径 kernel-4.19/drivers/virtual_camera
***/kernel-4.19/drivers/virtual_camera$ ls -ls 总计 252 84 -rwxr--r-- 1 123456 123456 85987 5月 24 17:35 v4l2loopback.c 12 -rwxr--r-- 1 123456 123456 8899 5月 24 17:35 v4l2loopback_formats.h 4 -rwxr--r-- 1 123456 123456 3053 5月 24 17:35 v4l2loopback.h 4 drwxr-xr-x 2 123456 123456 4096 5月 24 17:35 vagrant
3、kernel-4.19/drivers/virtual_camera/Makefile修改:

在原Makefile的基础上,主要添加交叉编译链工具路径

ifneq ($(wildcard .gitversion),) # building a snapshot version V4L2LOOPBACK_SNAPSHOT_VERSION=$(patsubst v%,%,$(shell git describe --always --dirty 2>/dev/null || shell git describe --always 2>/dev/null || echo snapshot)) override KCPPFLAGS += -DSNAPSHOT_VERSION='"$(V4L2LOOPBACK_SNAPSHOT_VERSION)"' endif include Kbuild ifeq ($(KBUILD_MODULES),) #KERNELRELEASE ?= `uname -r` #KERNEL_DIR ?= /lib/modules/$(KERNELRELEASE)/build #+Add start,添加kernel路径,和交叉编译工具路径 ARCH=arm64 KERNEL_DIR=***/kernel-4.19 CROSS_COMPILE=***/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android- #-Add end,添加kernel路径,和交叉编译工具路径
4、kernel-4.19/drivers/Makefile文件修改:

添加编译virtual_camera模块内容。

obj-y += virtual_camera/
5、make编译生成ko文件

make bootimage 进行编译, out目录下可以看到下面相关文件生成。

***/out/target/product/***_64/obj/KERNEL_OBJ/drivers/virtual_camera$ ls built-in.a modules.builtin modules.order v4l2loopback.ko v4l2loopback.mod.c v4l2loopback.mod.o v4l2loopback.o
6、验证ko文件是否生效

adb push v4l2loopback.ko 到vendor/lib64下,

adb push v4l2loopback.ko /vendor/lib64 adb shell chmod 0777 /vendor/lib64/v4l2loopback.ko /vendor/lib64 # insmod v4l2loopback.ko

/dev下查看,可以看到多了一个video4的节点。

****:/dev # ls -ls |grep video 0 crw-rw---- 1 media system 81, 0 2024-08-01 11:00 video0 0 crw-rw---- 1 media system 81, 1 2024-08-01 11:00 video1 0 crw-rw---- 1 camera system 81, 2 2024-08-01 11:00 video2 0 crw-rw---- 1 camera system 81, 3 2024-08-01 11:00 video3 0 crwxrwxrwx 1 media system 81, 4 2024-08-01 11:04 video4

/sys/class/video4linux 下ls查看,可以看到video4是一个虚拟摄像头。

**** :/sys/class/video4linux # ls -ls total 0 0 lrwxrwxrwx 1 root root 0 2024-08-01 11:15 video0 -> ../../devices/platform/1b100000.dvp/video4linux/video0 0 lrwxrwxrwx 1 root root 0 2024-08-01 11:15 video1 -> ../../devices/platform/1602f000.vdec/video4linux/video1 0 lrwxrwxrwx 1 root root 0 2024-08-01 11:15 video2 -> ../../devices/platform/17020000.venc/video4linux/video2 0 lrwxrwxrwx 1 root root 0 2024-08-01 11:15 video3 -> ../../devices/platform/17030000.jpgenc/video4linux/video3 0 lrwxrwxrwx 1 root root 0 2024-08-01 11:15 video4 -> ../../devices/virtual/video4linux/video4
二、v4l2-ctl 源码下载编译

1、源码下载、编译

原始路径:

https://github.com/compilelife/v4l-utils

(针对Android版本修改过的,我用的是这个)

https://github.com/youhandcn/v4l2-ctl-android

v4l2-ctl 的代码放到Android源码路径下,然后进行mm编译。

编译成功后,会在out目录下生成v4l2-ctl文件。将v4l2-ctl文件 adb push到system/bin目录下。则在Android设备上就能使用v4l2-ctl命令。

***/v4l2-ctl-android-master/v4l2-ctl-android-master$ ls -ls 总计 16 4 -rwxr--r-- 1 123456 123456 368 8月 2 00:59 Android.bp 4 drwxr-xr-x 3 123456 123456 4096 8月 2 00:58 include 4 -rwxr--r-- 1 123456 123456 107 1月 26 2021 README.md 4 drwxr-xr-x 4 123456 123456 4096 8月 2 00:58 v4l2
***/v4l2-ctl-android-master/v4l2-ctl-android-master$ mm -j8
三、yuv420_infiniteloop 测试文件编译和使用

下面命令的意思是,往/dev/video0节点中写入 /data/akiyo_qcif_176_144.yuv 路径下的yuv数据,分辨率是176* 144,30fps。

yuv420_infiniteloop /dev/video4 /data/akiyo_qcif_176_144.yuv 176 144 30

XXX/vendor/examples$ ls -ls 总计 56 4 -rwxr--r-- 1 z00001 z00001 173 8月 2 19:28 Android.mk 4 -rwxr--r-- 1 z00001 z00001 199 8月 2 19:28 Makefile.backup 20 -rwxr--r-- 1 z00001 z00001 17248 8月 2 19:28 ondemandcam 4 -rwxr--r-- 1 z00001 z00001 3022 8月 2 19:28 ondemandcam.c 4 -rwxr--r-- 1 z00001 z00001 2169 8月 2 19:28 README 4 -rwxr--r-- 1 z00001 z00001 971 8月 2 19:28 restarting-writer.sh 8 -rwxr--r-- 1 z00001 z00001 4862 8月 2 19:28 test.c 4 -rwxr--r-- 1 z00001 z00001 2592 8月 2 22:52 yuv420_infiniteloop.c 4 -rwxr--r-- 1 z00001 z00001 3057 8月 2 19:28 yuv4mpeg_to_v4l2.c

编写Android.mk文件,编译 yuv420_infiniteloop 。

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := yuv420_infiniteloop LOCAL_SRC_FILES := yuv420_infiniteloop.c include $(BUILD_EXECUTABLE)
四、其它
1、Android环境下,insmode 安装模块命令
insmode v4l2loopback.ko video_nr=3,4,7 card_label="device number 3","the number four","the last one" max_width=1920 max_height=1080
2、v4l2-ctl命令的使用

将物理摄像头的视频流输出到虚拟摄像头设备

v4l2-ctl --stream-mmap --stream-count=1 --stream-to=/dev/video6 --stream-from=/dev/video0

v4l2-ctl --list-formats-ext # 列出设备支持的所有格式v4l2-ctl --set-fmt-video=width=1920,height=1080,pixelformat=H264 # 设置视频格式v4l2-ctl --stream-mmap --stream-count=100 --stream-to=output.raw # 捕获视频流

3、ffmpeg命令的使用

ffmpeg -f video4linux2 -s 640x480 -r 30 -i /dev/video0 -vcodec copy -f v4l2 /dev/video10 -vcodec copy -f v4l2 /dev/video11

#启动ffmpeg开始向/dev/video0写入数据

ffmpeg -f x11grab -r 15 -s 1280x720 -i :0.0+0,0 -vcodec rawvideo -pix_fmt yuv420p -threads 0 -f v4l2 /dev/video0

4、

yuv测试资源:

http://trace.eas.asu.edu/yuv/

=================================

将YUV数据写到video节点

可以参考yuv420_infiniteloop.c 的实现来。下面是代码示例:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/videodev2.h> int mDevFd; bool mHasSaveYuv = false; int frameCount = 0; int openVideoDevice(int frameWidth,int frameHeight,int frameBytes) { if (mDevFd > 0) { return mDevFd; } struct v4l2_format v; int dev_fd = open("/dev/video4", O_RDWR, 0); if (dev_fd < 0) { ALOGE("Error opening device"); return -1; } v.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; if (ioctl(dev_fd, VIDIOC_G_FMT, &v) == -1) { ALOGE("cannot setup video device"); } v.fmt.pix.width = frameWidth; v.fmt.pix.height = frameHeight; v.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; v.fmt.pix.sizeimage = frameBytes; v.fmt.pix.field = V4L2_FIELD_NONE; if (ioctl(dev_fd, VIDIOC_S_FMT, &v) == -1) { ALOGE("cannot setup video device"); } return dev_fd; } static int save_yuv(char* data,int width,int height) { int ret = 0; FILE *fp = fopen("data/test.yuv", "wb"); if(fp != NULL) { fwrite(data,1,width * height * 3/2,fp); fclose(fp); MY_LOGE("save_yuv success!!!,size:%d * %d",width,height); } ret = 1; return ret; } void writeFrameToVideoNode(IImageBuffer *buffer) { char *ptr; int plane, stride, bufsize,width,height,frmaeSize; if( buffer ) { stride = buffer->getBufStridesInBytes(0); bufsize = buffer->getBufSizeInBytes(0); ptr = (char*)buffer->getBufVA(0); width = buffer->getImgSize().w; height = buffer->getImgSize().h; frmaeSize = stride*height*3/2; mDevFd = openVideoDevice(stride,height,bufsize); if (ptr && mDevFd > 0) { if (!mHasSaveYuv && frameCount == 100) { save_yuv(ptr,width,height); mHasSaveYuv = true; } frameCount++; int ret = write(mDevFd, ptr, frmaeSize); MY_LOGD("writeFrameToVideoNode plane:%d,stride:%d,bufsize:%d,mDevFd:%d,width:%d,height:%d,ret:%d",plane,stride,bufsize,mDevFd,width,height,ret); } } }

============ 三方App适配情况

1、美颜相机(com.meitu.meiyancamera)

有个异常,请求的640 * 480 分辨率没有。

Requested preview size 640 x 480 is not supported

------ 测试环境采用的yuv数据是176 * 144的。如果采用640 * 480的资源,应该也是没有问题。因为app已经正常执行了open动作。

08-30 10:54:53.204 I/CameraService( 778): CameraService::connect call (PID -1 "com.meitu.meiyancamera", camera ID 104) and Camera API version 1 08-30 10:54:53.205 I/Camera2ClientBase( 778): Camera 104: Opened. Client: com.meitu.meiyancamera (PID 16365, UID 10099) 08-30 10:54:53.207 D/ExtCamDev@3.4(10061): =========mCameraId.c_str():104 ANDROID_LENS_FACING_EXTERNAL:2======== 08-30 10:54:53.209 I/Camera2-Parameters( 778): initialize: allowZslMode: 0 slowJpegMode 0 08-30 10:54:53.214 E/Camera2-Parameters( 778): set: Requested preview size 640 x 480 is not supported

2、一甜相机

-----正常

3、抖音

----- 扫一扫正常,直播待测

下面是所有相关调试代码和相关资料,仅针对 “星球成员” 免费提供,需要的同学可以咨询。

《小驰行动派的知识星球》

————————————————


推荐阅读:

关于博主

《小驰Camera私房菜》小册目录

Camera基础及一些基本概念

Android Camera 学习路线 | 个人推荐

Android Camera开发系列(干货满满)

Camera Hal|如何学习一个新平台

一篇文章带你了解Android 最新Camera框架

学习完Camera入门课程视频,可以去找工作了?


http://www.jsqmd.com/news/1390002/

相关文章:

  • 微信防撤回工具 RevokeMsgPatcher 实测:3 步让你的重要消息不再被撤回
  • 建站公司都在推网站建设五合一,小白到底该怎么选?揭秘行业真相与避坑指南
  • 数学建模竞赛三年实战:从技术堆砌到问题本质的思维蜕变
  • 从数据驱动到机理建模:2025数学建模国赛B题解题策略与实战指南
  • 分布式 Geo 优化源码搭建思路与多节点部署数据同步技术方案
  • 【CVPR2026】给医学图像“相面”:视觉大模型驱动下的数据偶然不确定性深度解析
  • 数学建模竞赛B题深度攻略:从问题拆解到模型求解的实战解析
  • Windows系统错误0x80004005排查指南:COM组件、注册表与权限修复
  • Windows CMD下Curl命令全解析:从安装配置到高级应用实战
  • 为何选 Common Lisp 进行代码生成?这 8 大优势给出答案!
  • 洛雪音乐音源怎么配置?从免费无损音质到音源失效解决的一站式指南
  • es search包
  • 复杂自演化网络的测地线控制论:基于 4D 协变时空流形与自适应阻抗矩阵的内生安全范式
  • MathorCup D题解析:多目标装箱优化算法与建模实战
  • 数学建模竞赛实战指南:从问题解析到论文呈现的完整思维框架
  • gti与Git的完美结合:Bash/Zsh自动补全配置指南
  • 前端岗位结构性调整:从技术演进到AI冲击下的生存指南
  • 5分钟上手国产开源OpenFOAM流体仿真软件APPFlow:从几何建模到后处理全流程实战指南
  • 鹰手营子矿网站建设:打造本地企业数字化名片与网络营销实战指南
  • Formality:时序变换(三)(相位反转)
  • Stork Oracle Auto Bot安全使用指南:规避风险与合规操作建议
  • Windows系统文件UiaManager.dll丢失找不到问题解决
  • They Live Adblocker高级技巧:自定义标语、调整样式和优化性能
  • WIN10系统下delphi7编译大点项目经常出错或不通过
  • Weakpass规则库详解:nsa64/top_1500等密码生成规则的应用场景
  • 【Linux系统】OS、进程PCB、状态、进程的切换和调度,虚拟地址空间
  • macOS文件隔离机制解析与xattr命令实战应用
  • linux下C++生成并使用静态库和静态库
  • LangGraph状态管理核心:Reducer原理、类型与并发实战
  • 如何建设个人网站从0到1:普通人也能做出的高颜值独立门户