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

字符编码转换时发生内存越界引发的摄像头切换失败问题的排查

目录

1、问题说明

2、初步分析

3、字符串字符编码说明

4、进一步分析

5、为啥在日常测试时没有遇到切换摄像头失败的问题呢?

6、华为MateBook笔记本使用高通的CPU

7、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       最近在项目中遇到了一个因字符串编码转换时发生内存越界引发摄像头切换失败的问题,今天就来分享这个问题的排查过程,以供借鉴或参考。

1、问题说明

       最近在某个项目中,将软件部署到客户的环境中后,有台华为MateBook笔记本(后经证实这是华为新出的Pad平板电脑)在使用我们客户端音视频软件时发现切换笔记本前置摄像头时有问题。打开软件时,默认使用的是后置摄像头,客户在设置中将摄像头由后置摄像头切换到前置摄像头时:

发现没有反应,显示的还是后置摄像头图像,应该是摄像头切换失败了。

2、初步分析

       系统当前的摄像头列表的获取以及摄像头的切换,最终都是程序底层的音视频模块去执行的,所以优先从音视频模块查起。

这里的摄像头包括机器内置的摄像头以及USB摄像头(通过USB接口接到系统中的摄像头)。

1)台式机电脑一般使用的都是外接的USB摄像头;

2)笔记本电脑使用的是内置摄像头,也可以使用外界的USB摄像头(更清晰一点,角度可所以调整);

3)Pad平板一般会像手机一样,内置两个摄像头,一个前置摄像头,一个后置摄像头。

       经过客户允许,我们使用远程桌面软件远程连接到出问题的客户笔记本上。首先,查看音视频模块的打印日志,发现调用音视频模块切换摄像头时,上层传下来的摄像头id是空的:(日志打印是排查问题最重要、最常用的手段之一!

正常的摄像头id是类似于这样的字符串:\\?\usb#vid_046d&pid_0825&mi_00#6&259ee562&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\{bbefb6c7-2fc4-4139-bb8b-a58bba724083}

       音视频模块是通过上层传下来的设备id去匹配对应的摄像头设备,找到目标摄像头后,将摄像头源切换过去。根据打印,上层在调用音视频模块切换摄像头的接口时传入的是摄像头id为空,肯定匹配不到摄像头设备,所以没法切换到目标摄像头,摄像头切换失败了。

       客户端UI层模块,也会保存摄像头列表数据(包含摄像头名和id),UI层调用切换摄像头的接口时是从UI层的内存中去找对应的设备id,然后将要切换到的目标摄像头id传递给底层。难道UI层内存中保存的摄像头列表中的id有问题?id为空?于是去查看UI层的打印日志,通过日志得知,UI层保存的摄像头列表数据是正常的,id也不为空,是有效的!

3、字符串字符编码说明

       本案例中的问题与字符编码转换有关,所以此处给大家大概地介绍一下字符编码及相互转换的相关内容。

很多人在处理字符串编码及转换时可能会有不解或疑惑,甚至有多年工作经验的朋友也搞不清楚,所以这个地方很有必要给大家介绍一下字符编码相关的内容!

       有字符串的地方,就会涉及到字符编码,是对字符串的编码。常用的字符编码有ANSI窄字节编码UNICODE宽字节编码UTF8可变长度编码,其中UNICODE编码在Windows系统中普遍的使用。

        ANSI编码是本地语言编码,不是全球统一编码,不同国家语言文字的ANSI编码可能是相同的。中文字符的ANSI编码也被成为GBK编码(是对GB2312编码的扩展)。

       UNICODE编码则是全球统一的双字节编码(1个字符使用两个字节编码来表示),不同国家文字字符的UNICODE编码是全球唯一的,不重复的。在Windows平台上开发的软件,要支持多国语言,则软件中的字符要使用UNICODE编码。用Visual Studio开发的软件,如果要使用UNICODE编码,一般需要工程属性中需要字符集设置为“使用 Unicode 字符集”:(VS会在工程中自动添加UNICODE宏)

       涉及到字符串参数的系统API函数,一般都有两个版本,一个ANSI窄字节版本,一个UNICODE宽字节版本,比如获取窗口文字的API函数GetWindowText的声明如下:

int
WINAPI
GetWindowTextA(_In_ HWND hWnd,_Out_writes_(nMaxCount) LPSTR lpString,_In_ int nMaxCount);
_Ret_range_(0, nMaxCount)WINUSERAPI
int
WINAPI
GetWindowTextW(_In_ HWND hWnd,_Out_writes_(nMaxCount) LPWSTR lpString,_In_ int nMaxCount);#ifdef UNICODE
#define GetWindowText  GetWindowTextW
#else
#define GetWindowText  GetWindowTextA
#endif // !UNICODE

如果有定义UNICODE,则指向宽字节版本。如果在Visual Studio的字符集选项中选择“使用多字节字符集”,则对应ANSI窄字节编码。

       UTF8编码,是可变长字符编码,也是一种全球统一编码,之所以称之为可变长编码,因为不同类型的字符的编码长度是不同的,比如一个数字或英文字母的编码只占1个字节,一个中文文字的编码则占3个字节。

UTF8编码一般用于客户端和服务器之间交互的字符数据的统一编码格式,比如服务器中的Linux系统支持直接操作UTF8编码的字符数据,对于Windows客户端程序,在UI界面展现字符串数据时,需要先将服务器给过来的UTF8编码的字符转换成ANSI或UNICODE编码的字符,然后再将字符串拿到UI界面上显示。

       软件中可能会涉及到不同字符编码的转换,关于常用字符编码的详细说明以及编码之间转换的源码实现,可以参见我之前写的文章:

一文带你弄懂C++中的ANSI、Unicode和UTF8三种字符编码及相互转换icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/121070073VC++中ANSI、UNICODE与UTF-8字符编码之间的转换(附源码)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/123589349

4、进一步分析

       这就奇怪了,UI层的摄像头列表数据没问题,音视频模块中的摄像头列表数据也没问题,为啥上层调用音视频模块的切换摄像头接口时传入的目标摄像有id为空呢?其实,在UI层与音视频模块之间还有个业务组件模块,难道是中间的业务组件模块的代码有问题?

       音视频模块库是业务组件层封装起来的,即音视频模块的接口是业务组件层调用的,于是到业务组件层中查看调用音视频模块的切换摄像头接口的函数,如下所示:

因为上层与组件层之间交互涉及到字符串的,都统一使用UTF8字符编码,而音视频模块切换摄像头的接口声明如下:

// 选择视频源
unsigned short SelectVideoSource(const TVideoCapParam  *ptVideoCapParam, const wchar_t *pszDevName = NULL, const wchar_t *pszDevGUID = NULL);

该函数的第二个参数pszDevName(摄像头名称)和第三个参数pszDevGUID(摄像头id)都是wchar_t类型宽字节字符,在Windows平台上就是上面讲的UNICODE。而上层传过来的摄像头名称和id字符串都是UTF8编码的,所以在调用上述SelectVideoSource之前,需要将UTF8编码的字符串转换成UNICODE编码的字符串,代码中确实调用了UTF8转换到UNICODE的接口Utf8Tolnicode

       看到这里突然想到,是不是摄像头id比较长,在进行字符编码转换时存放转换后的字符串的buffer长度不足,导致内存越界了?(我们经常与字符编码打交道,对这方面的问题比较敏感)所以导致底层音视频模块的切换摄像头接口中收到的id为空的问题?于是将之前在日志中看到的目标摄像头的id拷贝到notepad++中查看到字符串的长度:

全选拷贝进来的摄像头id字符串,然后再notepad++状态栏显示的字符个数,得知摄像头id字符串的字符个数为130个,这些字符是键盘上的数字、字母等字符的组合,在UTF8编码中保存的是ASCII码,只占用1个字节。

       如果将上述字符串转成UNICODE编码的字符串,在UNICODE编码中一个字符占两个字节,对于数字和字母,其编码值就是ACSII码,只需要一个字节就能存放了,但还是要固定的占两个字节(高位填充0)。所以,上述130个字符的字符串,转成UNICODE编码后需要130*2 = 260字节,如果要包含字符串\0结尾符,那就需要262字节了!而编码转换的代码中却使用了256字节的buffer去接收转换出来的UNICODE编码

所以,在调用Utf8Tolnicode接口转换时肯定发生内存越界了!所以引发了音视频模块的切换摄像头的接口内部收到的id为空的问题。

按讲此处即使有越界,按讲传入的id不应该为空,因为在打印字符串时会直到找到字符串\0结尾符才会结束,此处就没再深究了,反正是存放转换出的UNICODE字符串的buffer长度不够导致的问题,修改后就正常了。

       解决办法很简单,直接将接收UNICODE字符串的buffer长度由256改成512就可以了。

       此外,在Windows平台上主要是调用API函数MultiByteToWideChar实现的,其实我们在调用MultiByteToWideChar接口时第五个参数(接收转码后的字符串buffer地址)传入NULL,MultiByteToWideChar返回的就是转码后字符串的长度,然后再调用一次MultiByteToWideChar进行转换,相关代码如下所示:

int nTmpLen = MultiByteToWideChar( CP_UTF8, 0, pchSrc, -1, NULL, 0 );
WCHAR* pWTemp = new WCHAR[nTmpLen+1];
memset( pWTemp, 0, (nTmpLen+1)*sizeof(WCHAR) );
MultiByteToWideChar( CP_UTF8, 0, pchSrc, -1, pWTemp, nTmpLen+1 );

5、为啥在日常测试时没有遇到切换摄像头失败的问题呢?

       遇到问题时,我们要多思考,要搞清楚问题的来龙去脉,甚至可以和正常的场景做比较。本案例中的问题以前从来没遇到过,这个到底有什么不一样的地方呢?我们的音视频软件需要使用到摄像头,不管是内置的摄像头,还是外插的USB摄像头。我们的软件已经有很多年了,为啥到现在才暴露出这个问题呢?

       于是到使用USB摄像头的测试电脑上,看看USB摄像头id和客户的摄像头id有什么不同。通过打印日志,拿到USB摄像头id的打印:(当前用的是外接的USB摄像头,id中包含usb字样)

\\?\usb#vid_046d&pid_0825&mi_00#6&259ee562&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\{bbefb6c7-2fc4-4139-bb8b-a58bba724083}

把这个id串拷贝到notepad++中:

选中所有字符,看到一共有127个字符,那么将这个字符串转成UNICODE编码,需要127*2 = 254字节(如果要包含\0结尾符,则需要256个字节),所以上述代码中给出256字节是够用的,没有问题。

       但在本案例中的客户笔记本电脑上,出问题的摄像头id为:(笔记本自带摄像头,是内置的,id中包含了display字样)

\\?\display#qcom_avstream_8180#3&1716c7e7&2&uid32768#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\{4faeafd4-041b-4e46-85fd-400473891182}

比较两个摄像头id字符串得知,id字符串的前半部分是不同的。

6、华为MateBook笔记本使用高通的CPU

       在我们的印象中,高通主要是做手机CPU芯片的,没想到华为的MateBook笔记本也使用高通的Snapdragon骁龙CPU,相关截图如下:

高通CPU是基于ARM架构的,Windows系统也是支持ARM架构的CPU的。

       笔记本电脑的CPU一般使用Intel或AMD的CPU,第一次见到用高通CPU的,很是好奇!于是以Snapdragon 8cx Gen 2型号到网上搜索这款CPU的说明,在CPUBenchmark测评工具的官网上看到了该CPU的说明:

这款CPU 8核8线程,支持平台有移动端、嵌入式和笔记本电脑(Laptop)。

       这款笔记本型号是MateBook E Go,后来到华为商城上搜了一下,这个是类似微软Surface的平板电脑,屏幕和键盘是可以分开的,本质上还属于Pad平板电脑,和一般意义上的笔记本还是有些不一样的。

7、最后

       这个问题其实相对比较简单,但其中涉及到的字符编码及编码转换的部分内容,正好趁此机会给大家普及一下,希望能给大家提供一定的借鉴或参考。

相关文章:

字符编码转换时发生内存越界引发的摄像头切换失败问题的排查

目录 1、问题说明 2、初步分析 3、字符串字符编码说明 4、进一步分析 5、为啥在日常测试时没有遇到切换摄像头失败的问题呢? 6、华为MateBook笔记本使用高通的CPU 7、最后 VC常用功能开发汇总(专栏文章列表,欢迎订阅,持续更…...

git修改之前的commit提交的作者信息和邮箱信息

更改之前提交的作者信息和邮箱信息需要进行两步操作。首先,使用 git filter-branch 命令进行历史重写,然后使用 git push --force 将更改推送到远程仓库。 步骤 1: 使用 git filter-branch 进行历史重写 在终端或命令行中执行以下命令: gi…...

《视觉SLAM十四讲》-- 相机与图像

04 相机与图像 4.1 相机模型 4.1.1 针孔相机模型 针孔模型描述了一束光线通过针孔后,在针孔背面投影成像的关系(类似小孔成像原理)。 根据相似三角关系 Z f − X X ′ − Y Y ′ (3-1) \frac{Z}{f}-\frac{X}{X^{\prime}}-\frac{Y}{Y^{\p…...

欧科云链:成本与规模之辨——合规科技如何赋能香港Web3生态?

作为国际金融中心,香港近两年来在虚拟资产及Web3领域频频发力。秉持着“稳步创新”的基本逻辑,香港在虚拟资产与Web3领域已建立一定优势,但近期各类风险事件的发生则让业界的关注焦点再次转向“安全”与“合规”。 在香港FinTech Week前夕&a…...

【文献分享】NASA JPL团队CoSTAR一大力作:直接激光雷达里程计:利用密集点云快速定位

论文题目:Direct LiDAR Odometry: Fast Localization With Dense Point Clouds 中文题目:直接激光雷达里程计:利用密集点云快速定位 作者:Kenny Chen, Brett T.Lopez, Ali-akbar Agha-mohammadi 论文链接:https://arxiv.org/pd…...

SPASS-探索性分析

探索性分析的意义 探索性分析更加强大,它是一种在对资料的性质、分布特点等完全不清楚的情况下,对变量进行更深入研究的描述性统计方法。在进行统计分析前,通常需要寻求和确定适合所研究的问题的统计方法, SPSS提供的探索性分析是解决此类问题的有效办法 探索性分析提供了很…...

电子印章怎么弄?三步教你电子印章在线生成免费教程!

在这个数字化快速发展的时代,电子印章已经成为日常商务活动中不可或缺的一部分。相对于传统的实体印章,电子印章具有更高的便捷性和安全性,更是无纸化办公中必不可少的一环。那么,电子印章怎么弄呢?跟着下面这三步来操…...

以技术创新引领行业发展,飞凌嵌入式获双项省级荣誉

近日,飞凌嵌入式荣获「2023年河北省专精特新示范企业」以及「第五批省级制造业单项冠军企业」两项殊荣。这两项荣誉的获得,是对飞凌嵌入式在专业技术领域与创新能力的高度认可,荣誉的背后,凝聚着飞凌嵌入式无数次的研发探索与对创…...

在Google Kubernetes集群创建分布式Jenkins(二)

上一篇博客在Google Kubernetes集群创建分布式Jenkins(一)-CSDN博客我介绍了如何在GCP的K8S集群上部署一个分布式的Jenkins,并实现了一个简单的Pipeline的运行。 在实际的开发中,我们通常都会按照以下的CICD流程来设置Pipeline 在我司的实际实践中&…...

GEE:GEE中调用 Math.js 教程

作者:CSDN @ _养乐多_ Math.js 是一个强大的 JavaScript 数学库,它提供了大量用于数学计算的函数和工具。这个库可用于解决各种数学问题,从基本的算术运算到高级数学和线性代数等领域。本文将介绍在 Google Earth Engine(GEE)云平台中调用 Math.js 第三方库做一些事情的方…...

迅为龙芯3A5000主板,支持PCIE 3.0、USB 3.0和 SATA 3.0显示接口2 路、HDMI 和1路 VGA,可直连显示器

性能强 采用全国产龙芯3A5000处理器,基于龙芯自主指令系统 (LoongArch)的LA464微结构,并进一步提升频率,降低功耗,优化性能。 桥片 桥片采用龙芯 7A2000,支持PCIE 3.0、USB 3.0和 SATA 3.0显示接口2 路、HDMI 和1路 …...

Opencv for unity 下载

GitHub - EnoxSoftware/VideoPlayerWithOpenCVForUnityExample: This example shows how to convert VideoPlayer texture to OpenCV Mat using AsyncGPUReadback. OpenCV for Unity | Integration | Unity Asset Store...

独立IP主机怎么样?对网站有什么影响

对于现在企业来说,搭建网站是必不可少的,而大部分企业网站都会选择使用虚拟主机搭建,且使用的也是共享IP的这样会 有许多的弊端,所以部分站长会选择独立IP搭建。那么到底独立IP主机怎么样呢?使用独立IP主机搭建对网站有…...

Gerrit lfs安装及配置

Gerrit版本:3.1.4 lfs下载:Zuul Gerrit CI界面已经没有3.1.4对应版本的lfs.jar了,需要从上面的页面下载。 一、安装配置lfs 将上面下载的lfs.jar放到$GERRIT_SITE/plugins目录。 修改配置文件:$GERRIT_SITE/etc/gerrit.config …...

基于表面电势的AlGaN/GaN MODFET紧凑模型

标题:A Surface-Potential-Based Compact Model for AlGaN/GaN MODFETs 来源:IEEE TRANSACTIONS ON ELECTRON DEVICES(11年) 摘要 - 本文首次构建了基于表面势(SP)的AlGaN/GaN调制掺杂场效应晶体管&#…...

c语言中的fread

#include <stdio.h> //函数的定义&#xff1a; //size_t fread(void *ptr,size_t nmemb,FILE *stream); //函数的说明&#xff1a; //fread函数从stream所标示的文件中读取数据&#xff0c;一块是size个字节&#xff0c;共nmemb块&#xff0c;存放到ptr指向的内存里 …...

Unity游戏开发基础组件

Unity2D 相机调整&#xff1a;Projection设置为Orthographic。也就是正交模式&#xff0c;忽视距离。 资源&#xff1a; Sprite&#xff1a;一种游戏资源&#xff0c;在2D游戏中表示角色场景的图片资源 SpriteSheet&#xff1a;切割一张图片为多个Sprite 在Sprite Editor中可以…...

股票四倍杠杆什么意思?

股票四倍杠杆是指投资者通过借款或使用金融衍生品&#xff0c;以增加其投资股票的能力&#xff0c;达到放大投资回报的目的。具体来说&#xff0c;投资者可以通过向券商或银行等金融机构借入资金&#xff0c;或者使用融资融券等金融衍生品&#xff0c;以增加其购买股票的资本&a…...

【hcie-cloud】【2】华为云Stack解决方案介绍、缩略语整理 【下】

文章目录 华为文档获取方式、云计算发展背景、坚实基座华为云Stack&#xff0c;政企只能升级首选智能数据湖仓一体&#xff0c;让业务洞见更准&#xff0c;价值兑现更快MRS&#xff1a;一个架构可构建三种数据湖&#xff0c;业务场景更丰富离线数据湖&#xff1a;提供云原生、湖…...

多无人机在线路径规划的新算法

南京航空航天大学自动化学院使用NOKOV度量动作捕捉系统获取多架无人机的精确位置信息&#xff0c;实现多架无人机协同实时路径规划。 研究背景 近年来&#xff0c;无人机越来越多地应用于执行战场侦察、目标识别、跟踪打击等任务。 由多架无人机协同执行任务&#xff0c;通过…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

《Offer来了:Java面试核心知识点精讲》大纲

文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...

嵌入式面试常问问题

以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

python打卡day49@浙大疏锦行

知识点回顾&#xff1a; 通道注意力模块复习空间注意力模块CBAM的定义 作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 一、通道注意力模块复习 & CBAM实现 import torch import torch.nn as nnclass CBAM(nn.Module):def __init__…...

Java设计模式:责任链模式

一、什么是责任链模式&#xff1f; 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09; 是一种 行为型设计模式&#xff0c;它通过将请求沿着一条处理链传递&#xff0c;直到某个对象处理它为止。这种模式的核心思想是 解耦请求的发送者和接收者&#xff0c;…...

java+webstock

maven依赖 <dependency><groupId>org.java-websocket</groupId><artifactId>Java-WebSocket</artifactId><version>1.3.5</version></dependency><dependency><groupId>org.apache.tomcat.websocket</groupId&…...