四.NV16到NV12的尝试

出发

为什么是NV16

我选择了节点rkispp_scale0,这个节点支持1到8倍的缩放,并且支持最大分辨率,也就是4K
但是出于某种原因,2k及以上需要使用到NV16,具体可以看上一篇文章,官方文档有提及
具体的实现代码也不复杂

VI_CHN_ATTR_S vi_chn_attr;
memset(&vi_chn_attr, 0, sizeof(vi_chn_attr));
vi_chn_attr.pcVideoNode = pDeviceName;
vi_chn_attr.u32BufCnt = 4;
vi_chn_attr.u32Width = width;
vi_chn_attr.u32Height = height;
vi_chn_attr.enPixFmt = NVmod;
vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;

ret = RK_MPI_VI_SetChnAttr(s32CamId, 0, &vi_chn_attr);
ret |= RK_MPI_VI_EnableChn(s32CamId, 0);
ret |= RK_MPI_VI_StartStream(s32CamId, 0);

if (ret) {
    printf("#####首次启动视频流失败! ret=%d\n", ret);
    return -1;
}

但是实际效果是,通过AMCAP打开UVC摄像头查看2k图像,出现了颜色偏移问题
在这里插入图片描述
通过一番讨论和测试,发现,其实并不是图像本身存在问题,图像就是NV16的正常格式,是因为在UVC处理里,没有针对NV16或者说YUV422SP的处理
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

为什么是NV12

在看UVC源码之前,我曾多次测试了播放器和图像查看器
通过v4l2分别抓取NV12和NV16的图像,并且通过ffmpeg按照对应格式转换为png,其实都是正常的图片,并没有颜色偏移的问题

得出一个结论,或许大部分(或者说我用到的几个)播放器都是不兼容NV16的,多数都是YUV422的形式

这个是VLC播放器的信息

最后抉择

现在搞清楚了为什么不支持,会出现颜色偏移的问题,我有了这么一个思路:
在NV16(2k及以上时)模式下,针对数据buff进行修改,将NV16转换为NV12再传递给UVC的接口,让它将NV12转换为YUYV好适配绝大多数的播放软件

过程

数据结构

基于这个出发点,我稍微分析了一下mb,也就是rk的buff

void *RK_MPI_MB_GetPtr(MEDIA_BUFFER mb) {
	if (!mb)
		return NULL;
	
	MEDIA_BUFFER_IMPLE *mb_impl = (MEDIA_BUFFER_IMPLE *)mb;
	return mb_impl->ptr;
}

顺藤摸瓜,找到强转mb后的结构体

/* SDK/external/rkmedia/src/c_api/rkmedia_buffer_impl.h */
typedef struct _rkMEDIA_BUFFER_S {
	MB_TYPE_E type;
	 void *ptr;         // Virtual address of buffer
	 int fd;            // dma buffer fd
	 int handle;        // dma buffer handle
	 int dev_fd;        // dma device fd
	 size_t size;       // The size of the buffer
	 MOD_ID_E mode_id;  // The module to which the buffer belongs
	 RK_U16 chn_id;     // The channel to which the buffer belongs
	 RK_U64 timestamp;  // buffer timesatmp
	 RK_U32 flag;       // buffer flag
	 RK_U32 tsvc_level; // buffer level
	 std::shared_ptr<easymedia::MediaBuffer> rkmedia_mb;
	 union {
	   MB_IMAGE_INFO_S stImageInfo;
	   MB_AUDIO_INFO_S stAudioInfo;
	 };
} MEDIA_BUFFER_IMPLE;

根据注释,可以了解到指针ptr是指向数据本身的
那么我就可以仿照uvc_encode.cpp的处理来实现NV16到NV12

修改

根据现有代码,对主程序的函数进行了修改

// 获取媒体缓冲区的线程函数
static void *GetMediaBuffer(void *arg) {
    MEDIA_BUFFER mb = NULL;
    (void)arg;

    size_t nv12_size = Wwidth * Hheight * 3 / 2; // 提前计算 NV12 数据大小
    uint8_t *nv12_data = NULL;

    while (!bufq) {
        pthread_mutex_lock(&camera_mutex);
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 0, -1);  // 获取缓冲区
        pthread_mutex_unlock(&camera_mutex);
        if (!mb) {
            printf("RK_MPI_SYS_GetMediaBuffer get null buffer!\n");
            break;
        }
        void *data = RK_MPI_MB_GetPtr(mb);  // buffer 的数据指针
        size_t size = RK_MPI_MB_GetSize(mb);
        if (!data || size == 0) {
            printf("获取到无效缓冲区,跳过\n");
            RK_MPI_MB_ReleaseBuffer(mb);
            continue;
        }
        
        if (NVmod == IMAGE_TYPE_NV16) {
            if (!nv12_data) {
                // 分配 NV12 缓冲区
                nv12_data = (uint8_t *)malloc(nv12_size);
                if (!nv12_data) {
                    printf("分配 NV12 缓冲区失败\n");
                    RK_MPI_MB_ReleaseBuffer(mb);
                    break;
                }
            }
            // NV16 转 NV12
            NV16_to_NV12(Wwidth, Hheight, data, nv12_data);
            // 构建并传递 NV12 数据给 UVC
            MPP_ENC_INFO_DEF info = {0};
            info.fd = RK_MPI_MB_GetFD(mb);
            info.size = nv12_size;
            uvc_read_camera_buffer((void *)nv12_data, &info, NULL, 0);
        } else {
            // 构建并传递原始数据给 UVC
            MPP_ENC_INFO_DEF info = {0};
            info.fd = RK_MPI_MB_GetFD(mb);
            info.size = size;
            uvc_read_camera_buffer(data, &info, NULL, 0);
        }
        RK_MPI_MB_ReleaseBuffer(mb);
    }

    // 释放 NV12 缓冲区
    if (nv12_data) {
        free(nv12_data);
        nv12_data = NULL;
    }
    return NULL;
}

这里只是逻辑上是合理的

  • 判断是否为NV16
  • 是:将NV16处理为NV12,再传递给UVC
  • 否:直接传递给UVC

可惜逻辑上的自洽并不能实现目标

  • 要怎么修改buff数据?

这里只是勉强实现了NV16到NV12的函数


void NV16_to_NV12(int width, int height, void* src, void* dst) {
    uint8_t* src_y = (uint8_t*)src;                  // NV16 的 Y 数据起始位置
    uint8_t* src_uv = src_y + width * height;        // NV16 的 UV 数据起始位置
    uint8_t* dst_y = (uint8_t*)dst;                  // NV12 的 Y 数据目标地址
    uint8_t* dst_uv = dst_y + width * height;        // NV12 的 UV 数据目标地址

    // 拷贝 Y 平面数据
    memcpy(dst_y, src_y, width * height);

    // 转换 UV 平面数据
    for (int j = 0; j < height; j += 2) {            // 每两行处理一次
        uint8_t* src_uv_line = src_uv + j * width;   // NV16 当前行的 UV 数据
        uint8_t* dst_uv_line = dst_uv + (j / 2) * width;

        for (int i = 0; i < width; i += 2) {         // 每两个像素处理一次
            dst_uv_line[i]     = src_uv_line[i];     // 拷贝 U 分量
            dst_uv_line[i + 1] = src_uv_line[i + 1]; // 拷贝 V 分量
        }
    }
}

抵达

还没搞完,别急


ps.如果有大佬遇到过或者处理过类似的问题,还请务必带带菜鸟

<think>好的,我现在要解决用户在Windows命令行中运行test_camera.nv12时遇到的“Permission denied”错误。首先,我需要理解这个错误的原因。通常,“Permission denied”在文件操作中意味着用户没有足够的权限来执行该文件或访问该文件所在的目录。这可能涉及到文件权限设置、文件是否被其他程序占用,或者路径是否正确等问题。 接下来,我应该考虑用户的具体情况。用户是在Windows系统下使用命令行运行一个名为test_camera.nv12的文件,但遇到了权限问题。首先,我需要确认这个文件的扩展名.nv12是什么类型的文件。通常,.nv12文件可能是一种视频或图像格式的原始数据文件,但在这里,用户可能是试图执行一个可执行文件,而文件扩展名可能被错误地命名了。或者,用户可能是在尝试运行一个脚本或程序,但该文件没有执行权限。 首先,需要检查文件的权限。在Windows中,右键点击文件,选择属性,然后查看安全选项卡,确认当前用户是否有执行权限。如果权限不足,用户需要修改权限或获取管理员权限。用户可能需要以管理员身份运行命令行,或者修改文件的安全设置。 另外,用户可能误将数据文件当作可执行文件来运行。比如,test_camera.nv12可能是一个数据文件,而不是.exe或.bat等可执行文件。这时候,用户需要确认自己是否使用了正确的程序来打开该文件。例如,可能需要使用特定的播放器或处理工具来加载.nv12文件,而不是直接执行它。 还有可能,该文件被其他程序占用,导致无法访问。用户可以尝试关闭可能使用该文件的程序,或者重启计算机后再次尝试运行。 此外,路径问题也可能导致权限错误。用户需要确认命令行当前的工作目录是否正确,是否输入了正确的文件路径。如果路径包含空格或特殊字符,可能需要使用引号将路径括起来。 根据用户提供的引用内容,其中提到了在开发板上赋予可执行权限的命令chmod a+x sample_osd,但这属于Linux系统下的操作,而用户的问题是在Windows环境下。因此,可能需要排除用户混淆了不同操作系统的权限管理方式的可能性。在Windows中,文件的可执行权限通常由文件扩展名和用户账户控制(UAC)设置决定,而不是通过chmod命令。 可能的解决方案包括: 1. 以管理员身份运行命令行:右键点击命令提示符或PowerShell,选择“以管理员身份运行”,然后再次尝试执行命令。 2. 检查文件是否可执行:确认test_camera.nv12是否真的是一个可执行文件。如果是,可能需要将其扩展名改为.exe,或者在调用时指定正确的解释器,例如使用python脚本或其他程序来运行它。 3. 修改文件权限:在文件属性中,赋予当前用户完全控制的权限。 4. 检查文件是否被占用:使用资源监视器或Process Explorer查看是否有进程正在使用该文件。 5. 确认文件路径正确:确保命令行中的文件路径正确无误,必要时使用绝对路径。 6. 关闭防病毒软件:某些防病毒软件可能会阻止未知文件的执行,暂时禁用后测试是否有帮助。 需要进一步询问用户的具体情况,例如文件的来源、是否是自己编写的程序、是否有其他错误信息等。但根据现有信息,应该优先考虑权限和文件类型的问题。</think>### 解决方案:Windows命令行运行test_camera.nv12时“Permission denied”错误 #### 1. 以管理员身份运行命令行 Windows系统权限限制可能导致普通用户无法执行某些操作: - 右键点击**命令提示符**或**PowerShell**图标 - 选择**以管理员身份运行** - 重新执行命令(例如:`.\test_camera.nv12`) - 若文件需要特殊权限(如硬件访问),此方法通常有效[^2] #### 2. 验证文件可执行性 `.nv12`扩展名常见于视频原始数据文件,非标准可执行格式: - 检查文件是否为以下类型: - **可执行程序**(应具有`.exe`扩展名) - **脚本文件**(如Python脚本应为`.py`) - 若是可执行文件被错误命名: ```cmd ren test_camera.nv12 test_camera.exe ``` - 若需特定程序打开: ```cmd python test_camera.nv12 # 假设是Python脚本 ``` #### 3. 修改文件安全权限 通过文件属性设置: 1. 右键文件 → **属性** → **安全**选项卡 2. 选择当前用户 → 点击**编辑** 3. 勾选**完全控制**权限 → 确认保存 4. 特别注意父目录的写入权限(临时目录可能需要特殊设置) #### 4. 处理文件占用问题 使用系统工具排查: 1. 打开**任务管理器** → **性能**选项卡 → 底部**资源监视器** 2. 在**CPU**标签页的"关联的句柄"搜索栏输入文件名 3. 结束相关进程后重试 #### 5. 路径与命名规范 ```cmd # 处理特殊字符路径 cd "C:\Program Files\My Project" .\test_camera.nv12 # 使用短文件名格式(适用于长路径) cd C:\PROGRA~1\MYPRO~1 ``` #### 6. 防病毒软件排查 临时禁用安全软件: - Windows Defender:通过安全中心临时关闭实时保护 - 第三方杀毒软件:在系统托盘图标右键暂停保护 - 注意:操作后立即恢复防护 #### 补充诊断方法 ```cmd # 查看文件属性 dir /Q test_camera.nv12 # 查看文件所有者 # 验证文件完整性 certutil -hashfile test_camera.nv12 SHA256 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值