RK3568 android11 移植 v4l2loopback 虚拟摄像头

这篇具有很好参考价值的文章主要介绍了RK3568 android11 移植 v4l2loopback 虚拟摄像头。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一,v4l2loopback 简介

v4l2loopback是一个Linux内核模块,它允许用户创建虚拟视频设备。这种虚拟视频设备可以用于各种用途,例如将实际摄像头的视频流复制到虚拟设备上,或者用于视频流的处理和分析等。v4l2loopback的主要作用是创建一个虚拟的Video4Linux2设备,它可以接收来自其他应用程序的视频数据,并将这些数据提供给其他应用程序
一旦加载了v4l2loopback模块,就可以在/dev目录下找到虚拟设备文件,通常命名为/dev/videoX(X是一个数字)。

二,驱动文件配置

1. v4l2loopback 内核模块驱动文件

RK3568 android11 移植 v4l2loopback 虚拟摄像头,RockChip平台 调试记录,android,rk3568,音视频,虚拟摄像头,v4l2loopback

1> v4l2loopback.c: v4l2loopback 内核模块的 C 语言源代码文件。它包含了实现 v4l2loopback 模块功能的代码。
2> v4l2loopback.h: v4l2loopback 内核模块的头文件,通常包含一些宏定义、结构体定义、函数声明等。
3> v4l2loopback_formats.h:这个文件包含了有关视频格式的定义和处理,用于支持 v4l2loopback 模块对不同视频格式的处理和转换。

2. 移植 v4l2loopback驱动

a. 将驱动(v4l2loopback)拷贝到下面的文件夹:

./kernel/drivers/v4l2loopback

b. 在 Makefile 中添加 v4l2loopback设备

kernel/drivers/Makefile中添加:
+obj-y                           +=v4l2loopback/

c. 编译kernel后会在目录下生成对应的.o文件

~/RK3568_Android11/kernel/drivers/v4l2loopback$ ls
built-in.a  Makefile  modules.builtin  modules.order  v4l2loopback.c  v4l2loopback_formats.h  v4l2loopback.h  v4l2loopback.o

d. 验证 v4l2loopback.ko 模块是否加载成功
RK3568 android11 移植 v4l2loopback 虚拟摄像头,RockChip平台 调试记录,android,rk3568,音视频,虚拟摄像头,v4l2loopback
设备 video9 就是 v4l2loopback.ko 模块驱动的设备,确认v4l2loopback.ko 模块移植成功。


三,hardware下整合v4l2loopback 虚拟摄像头设备

源码目录:hardware/interfaces/camera/
修改补丁如下:

diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index ec3264894..c2a1f4156 100755
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -22,6 +22,8 @@
 #include <array>
 #include <regex>
 #include <linux/videodev2.h>
+#include <linux/v4l2-subdev.h>
+#include <linux/videodev2.h>
 #include "android-base/macros.h"
 #include "CameraMetadata.h"
 #include "../../3.2/default/include/convert.h"
@@ -373,6 +395,7 @@ status_t ExternalCameraDevice::initDefaultCharsKeys(
     UPDATE(ANDROID_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION,
            &opticalStabilizationMode, 1);
 
+    ALOGD("=========mCameraId.c_str():%s ANDROID_LENS_FACING_EXTERNAL:%d========", mCameraId.c_str(), ANDROID_LENS_FACING_EXTERNAL);
     const uint8_t facing = ANDROID_LENS_FACING_EXTERNAL;
     UPDATE(ANDROID_LENS_FACING, &facing, 1);
 
@@ -831,6 +854,16 @@ void ExternalCameraDevice::getFrameRateList(
         }
     }
 
+    struct v4l2_capability capability_v4l2;
+    int ret_query_v4l2 = ioctl(fd, VIDIOC_QUERYCAP, &capability_v4l2);
+    if (ret_query_v4l2 < 0) {
+        ALOGE("%s v4l2 QUERYCAP %s failed: %s", __FUNCTION__, strerror(errno));
+    }
+    if(strstr((const char*)capability_v4l2.driver,"v4l2")){
+        LOGD("======%s: capability_v4l2.driver:%s ========", __func__, capability_v4l2.driver);
+        SupportedV4L2Format::FrameRate fr = {1,30}; //特定帧率(1帧每秒到30帧每秒)添加到 format->frameRates 向量中
+        format->frameRates.push_back(fr);
+    }
     if (format->frameRates.empty()) {
         ALOGE("%s: failed to get supported frame rates for format:%c%c%c%c w %d h %d",
                 __FUNCTION__,
@@ -917,6 +950,12 @@ std::vector<SupportedV4L2Format> ExternalCameraDevice::getCandidateSupportedForm
     int ret = 0;
     while (ret == 0) {
         ret = TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc));
+        if(ret < 0 && strstr((const char*)capability.driver, "v4l2")) {
+            ALOGE("driver.find :%s",capability.driver);
+            fmtdesc.pixelformat = V4L2_PIX_FMT_NV12; 
+			ret = 0;
+        }
         ALOGV("index:%d,ret:%d, format:%c%c%c%c", fmtdesc.index, ret,
                 fmtdesc.pixelformat & 0xFF,
                 (fmtdesc.pixelformat >> 8) & 0xFF,
@@ -963,6 +1002,39 @@ std::vector<SupportedV4L2Format> ExternalCameraDevice::getCandidateSupportedForm
                     }
                 }
             }
+            if(strstr((const char*)capability.driver, "v4l2")) {
+					ALOGD("driver.find :%s",capability.driver);
+					SupportedV4L2Format format_1920x1080 {
+                            .width = 1920,
+                            .height = 1080,
+                            .fourcc = V4L2_PIX_FMT_NV12
+                        };
+					updateFpsBounds(fd, cropType, fpsLimits, format_1920x1080, outFmts);//名为 updateFpsBounds 的函数,向其传递了一些参数,包括文件描述符 fd、crop 类型、帧率限制、format_1920x1080 结构和 outFmts
+					ret = -1;
+                }
         }
         fmtdesc.index++;
     }
diff --git a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
index e2ab4fa2f..664d7d262 100644
--- a/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.4/default/ExternalCameraDeviceSession.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 #define LOG_TAG "ExtCamDevSsn@3.4"
-//#define LOG_NDEBUG 0
+#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_CAMERA
 #include <log/log.h>
 
@@ -3104,7 +3104,18 @@ int ExternalCameraDeviceSession::configureV4l2StreamLocked(
             ALOGE("%s: QUERYBUF %d failed: %s", __FUNCTION__, i,  strerror(errno));
             return -errno;
         }
+//fy
+        ALOGD("==========mV4L2Buffer[%d] = (char*)mmap()==========", i);
+        if (buffer.memory == V4L2_MEMORY_MMAP) {
+            mV4L2Buffer[i] = (char*)mmap(0 /* start anywhere */ ,
+                        buffer.length, PROT_READ, MAP_SHARED, mV4l2Fd.get(),
+                        buffer.m.offset);
+            if (mV4L2Buffer[i] == MAP_FAILED) {
+                LOGE("%s(%d): Unable to map buffer(length:0x%x offset:0x%x) %s(err:%d)\n",__FUNCTION__,__LINE__, buffer.length,buffer.m.offset,strerror(errno),errno);
+            }
 
+        }
+        V4l2BufferLen = buffer.length;
         if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_QBUF, &buffer)) < 0) {
             ALOGE("%s: QBUF %d failed: %s", __FUNCTION__, i,  strerror(errno));
             return -errno;
@@ -3401,8 +3412,10 @@ Status ExternalCameraDeviceSession::configureStreams(
         }
     }
     // Find the smallest format that matches the desired aspect ratio and is wide/high enough
-    SupportedV4L2Format v4l2Fmt {.width = 0, .height = 0};
-    SupportedV4L2Format v4l2Fmt_tmp {.width = 0, .height = 0};
+//    SupportedV4L2Format v4l2Fmt {.width = 0, .height = 0};
+//    SupportedV4L2Format v4l2Fmt_tmp {.width = 0, .height = 0};
+//初始化宽度为 1920,高度为 1088,fourcc 值为 V4L2_PIX_FMT_NV12;
+    SupportedV4L2Format v4l2Fmt {.width = 1920, .height = 1088, .fourcc = V4L2_PIX_FMT_NV12};
+    SupportedV4L2Format v4l2Fmt_tmp {.width = 1920, .height = 1088, .fourcc = V4L2_PIX_FMT_NV12};
     for (const auto& fmt : mSupportedFormats) {
         uint32_t dim = (mCroppingType == VERTICAL) ? fmt.width : fmt.height;
         if (dim >= maxDim) {
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession_3.4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession_3.4.h
index 209c5e91e..027a27ae7 100755
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession_3.4.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession_3.4.h
@@ -383,6 +383,9 @@ protected:
     SupportedV4L2Format mV4l2StreamingFmt;
     double mV4l2StreamingFps = 0.0;
     size_t mV4L2BufferCount = 0;
+    #define V4L2_BUFFER_MAX             32
+    char *mV4L2Buffer[V4L2_BUFFER_MAX];
+    unsigned int V4l2BufferLen = 0;
     struct v4l2_plane planes[1];
     struct v4l2_capability mCapability;
 
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
index 6e0e4ebab..e085682d4 100644
diff --git a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
index 65447421c..a5a74e380 100755
--- a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
@@ -257,6 +257,8 @@ void ExternalCameraProviderImpl_2_4::addExternalCamera(const char* devName) {
 }
 
 void ExternalCameraProviderImpl_2_4::deviceAdded(const char* devName) {
+    ALOGD("=============%s===========", __func__);
+    struct v4l2_capability capability;
     if (std::atoi(devName + kDevicePrefixLen) >= 30)
     {
         sp<device::V3_4::implementation::ExternalFakeCameraDevice> deviceImpl =
@@ -267,14 +269,15 @@ void ExternalCameraProviderImpl_2_4::deviceAdded(const char* devName) {
         }
         deviceImpl.clear();
     } else {
-    {
+        {
+        ALOGD("======fd(::open(devName, O_RDWR)) devName:%s ==========", devName);
         base::unique_fd fd(::open(devName, O_RDWR));
         if (fd.get() < 0) {
             ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno));
             return;
         }
 
-        struct v4l2_capability capability;
+//        struct v4l2_capability capability;
         int ret = ioctl(fd.get(), VIDIOC_QUERYCAP, &capability);
         if (ret < 0) {
             ALOGE("%s v4l2 QUERYCAP %s failed", __FUNCTION__, devName);
@@ -289,6 +292,7 @@ void ExternalCameraProviderImpl_2_4::deviceAdded(const char* devName) {
     // See if we can initialize ExternalCameraDevice correctly
     sp<device::V3_4::implementation::ExternalCameraDevice> deviceImpl =
             new device::V3_4::implementation::ExternalCameraDevice(devName, mCfg);
+    ALOGD("=========ExternalCameraDevice(devName:%s======", devName);
     if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
         ALOGW("%s: Attempt to init camera device %s failed!", __FUNCTION__, devName);
         return;
@@ -296,7 +300,15 @@ void ExternalCameraProviderImpl_2_4::deviceAdded(const char* devName) {
     deviceImpl.clear();
     }
 
-    addExternalCamera(devName);
+    //fy
+    if(capability.device_caps & V4L2_CAP_VIDEO_CAPTURE) {
+        ALOGD("===========dzp_test: devName:%s========", devName);
+        addExternalCamera(devName);
+    }
+    else addExternalCamera(devName);
     return;
 }
 
@@ -331,6 +349,7 @@ ExternalCameraProviderImpl_2_4::HotplugThread::HotplugThread(
 ExternalCameraProviderImpl_2_4::HotplugThread::~HotplugThread() {}
 
 bool ExternalCameraProviderImpl_2_4::HotplugThread::threadLoop() {
+    ALOGD("============%s kDevicePath:%s========", __func__, kDevicePath);
     // Find existing /dev/video* devices
     DIR* devdir = opendir(kDevicePath);
     if(devdir == 0) {
@@ -351,6 +370,7 @@ bool ExternalCameraProviderImpl_2_4::HotplugThread::threadLoop() {
                 snprintf(v4l2DevicePath, kMaxDevicePathLen,
                         "%s%s", kDevicePath, de->d_name);
                 mParent->deviceAdded(v4l2DevicePath);
+                ALOGD("=============v4l2DevicePath:%s ==============", v4l2DevicePath);
             }
         }
     }

  1. 添加虚拟摄像头设备接口:在 “hardware/interfaces/camera/” 目录下可能会添加或修改相机设备的接口,以便 Android 系统可以与 v4l2loopback 虚拟摄像头进行交互。

  2. 实现虚拟摄像头设备功能:可能会在该目录下实现虚拟摄像头设备的功能,包括与 Android 相机框架的集成、数据流处理等。

  3. 支持虚拟摄像头的配置:可能会对 Android 相机服务的配置进行修改,以支持虚拟摄像头设备的添加和管理。

改动的目的是将 v4l2loopback 虚拟摄像头设备整合到 Android 系统中,以便应用程序可以与虚拟摄像头进行交互,并利用其提供的视频流数据


四,赋予虚拟摄像头注册节点权限

device/rockchip/common/ueventd.rockchip.rc中修改:

/dev/video9          0666   media      camera

虚拟摄像头注册节点设为666权限是为了确保所有用户都能够访问和使用该节点。权限数字666表示所有用户都有读写权限,这意味着任何用户都可以读取和写入该节点,从而能够对虚拟摄像头进行访问和控制
例如,如果虚拟摄像头用于视频会议应用程序,那么所有参与会议的用户都需要能够访问虚拟摄像头节点。
需要注意的是,赋予666权限也可能存在一定的安全风险,因为这样做会允许任何用户都能够对该节点进行读写操作。因此,在实际应用中,需要仔细考虑安全性和访问控制的需求,以确定是否真的需要将虚拟摄像头节点权限设置为666。文章来源地址https://www.toymoban.com/news/detail-805121.html

到了这里,关于RK3568 android11 移植 v4l2loopback 虚拟摄像头的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux之V4L2驱动框架

    目录 一、V4L2简介 二、V4L2操作流程  1.打开摄像头 2.查询设备的属性/能力/功能 3.获取摄像头支持的格式 4.设置摄像头的采集通道 5.设置/获取摄像头的采集格式和参数 6.申请帧缓冲、内存映射、入队 (1)申请帧缓冲 (2)内存映射 (3)入队 7.开启视频采集 8.读取数据、对数

    2024年02月08日
    浏览(39)
  • 内存不足V4L2 申请DMC缓存报错问题

    当内存不足时,V4L2可能存在申请DMA缓存报错,如下日志:

    2024年02月12日
    浏览(39)
  • 项目之利用 V4L2应用程序框架 进行视频录制

    目录 知识储备: 视频采集方式: 处理采集数据: 相关结构体: 对于设备的操作步骤:         V4L2较V4L有较大的改动,并已成为 2.6 的标准接口,函盖 videodvbFM... ,多数驱动都在向 V4l2 迁移。更好地了解 V4L2 先从应用入手,然后再深入到内核中结合物理设备/接口的规范实现

    2023年04月09日
    浏览(43)
  • 汽车IVI中控开发入门及进阶(十二):V4L2视频

    前言     IVI,In-Vehicle Infotainment,智能座舱信息娱乐系统,或称车载信息娱乐系统, 汽车中控也被称为车机、车载多媒体、车载娱乐等, 它是智能座舱的重要组成部分。IVI 采用车载专用中央处理器,基于车身总线系统和联网服务提供车载综合信息处理功能,包括音频播放、

    2024年02月04日
    浏览(48)
  • 【C++】开源:Linux端V4L2视频设备库

    😏 ★,° :.☆( ̄▽ ̄)/$: .°★ 😏 这篇文章主要介绍Linux端V4L2视频设备库。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下次更新不迷路🥞 Video4Linux2( V4L2 )是一个用于Linux操作系统的视频设备驱动框

    2024年02月11日
    浏览(34)
  • linux v4l2架构分析之异步注册v4l2_async_subdev_notifier_register、v4l2_async_register_subdev、v4l2_async_notifie

            在camera驱动注册中,v4l2_async_subdev_notifier_register、v4l2_async_register_subdev、v4l2_async_notifier_register这几个函数都会被使用到,三者在异步注册的实现中是紧密关联的,所以本文将三者放在一起进行分析。本文主要介绍异步注册的功能的整体实现框架,为了更好把握整体思

    2024年02月14日
    浏览(51)
  • Linux摄像头(v4l2应用)——获取摄像头一帧图像

    一.V4L2简介 V4L2(Video for Linux 2):Linux内核中视频设备中的驱动框架,对于应用层它提供了一系列的API接口,同时对于硬件层,它适配大部分的视频设备,因此通过调用V4L2的接口函数可以适配大部分的视频设备。 二、操作流程 1.打开设备 当把摄像头插入到电脑后,执行ls /

    2024年02月20日
    浏览(52)
  • c 摄像头利用v4l2直接生成avi视频(不利用ffmpeg)

    自定义avi结构头文件。现在不能实时显示摄像头画面,准备参照fim(终端中显示图片),直接对显示framebuffer操作,显示视频。不用qt等。 生成的视频根据机子的性能不同,诂计要手动调一下生成视频的帧率。 播放: $ aplay  musicdemo.wmv 录音: $ arecord -c 2 -r 44100 -f S16_LE musicd

    2024年02月08日
    浏览(42)
  • rk3568 安卓11 修改android id

    frameworksbasepackagesSettingsProvidersrccomandroidproviderssettingsSettingsProvider.java 系统应用获取的android id 直接写死 上层普通应用 android id跟着sn变化 android id跟着IMEI变化

    2024年04月28日
    浏览(49)
  • Linux + 香橙派 + V4L2 + http 实现远程监控摄像头在网页端显示

    项目需求,需要做一个基于边缘端的人脸识别远程监控摄像头并在网页前端展示 ,这里采用国产香橙派作为边缘计算终端,安装ubuntu系统,系统中采用v4l2接口对摄像头进行获取,当客户端通过网页进行请求时,服务器通过http服务的形式将一帧帧图像发送给客户端,只要一秒

    2024年02月15日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包