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

news2024/12/25 9:31:19

目录

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、最后

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1190244.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑msvcp110.dll丢失怎么办,msvcp110.dll缺失的详细修复步骤

在现代科技发展的时代,电脑已经成为我们生活和工作中不可或缺的工具。然而,由于各种原因,电脑可能会出现一些问题,其中之一就是msvcp110.dll文件丢失。这个问题可能会导致一些应用程序无法正常运行,给我们的生活和工作…

[直播自学]-[汇川easy320]搞起来(3)看文档安装软件 查找设备

2023.11.09 20:04 按照文档 解压压缩包得到: 打开 里面有一条值得注意: 想把软件安装到C盘,但是C盘没什么空间了,把C盘清理清理。 20:35 安装很快完成,然后阅读 由于PLC是新的&#xff0c…

【MATLAB源码-第70期】基于matlab的萤火虫算法(FA)的栅格路径规划,输出最短路径和适应度曲线。

操作环境: MATLAB 2022a 1、算法描述 萤火虫算法(Firefly Algorithm,FA)是由剑桥大学的Xin-She Yang在2008年提出的一种元启发式优化算法。该算法的灵感来源于萤火虫闪烁的行为特征,主要用于解决连续的优化问题。萤…

【123. 买卖股票的最佳时机 III】

目录 一、题目描述二、算法原理三、代码实现 一、题目描述 二、算法原理 三、代码实现 class Solution { public:const int Init-0x3f3f3f3f;int maxProfit(vector<int>& prices) {int nprices.size();vector<vector<int>> f(n,vector<int>(3,Ini…

使用TS进行Vue-Router的Meta类型扩展

文章目录 1、前言2、解决 1、前言 使用Vue-Router时&#xff0c;会将一些字段信息附加到路由的Meta对象里面&#xff0c;比如图标icon&#xff0c;标题&#xff0c;权限等&#xff0c;如下&#xff1a; {path: /billboard/board/:boardId,name: billboardBoard,props: true,c…

超全总结!大模型算法面试指南(含答案)

大家好&#xff0c;从 2019 年的谷歌 T5 到 OpenAI GPT 系列&#xff0c;参数量爆炸的大模型不断涌现。可以说&#xff0c;LLMs 的研究在学界和业界都得到了很大的推进&#xff0c;尤其去年 11 月底对话大模型 ChatGPT 的出现更是引起了社会各界的广泛关注。 近些年&#xff0…

java项目之服装定制系统(ssm框架)

项目简介 服装定制系统实现了以下功能&#xff1a; 管理员&#xff1a;管理员使用本系统涉到的功能主要有首页、个人中心、用户管理、服装类型管理、服装信息管理、服装定制管理、留言反馈、系统管理等功能。用户&#xff1a;用户进入系统可以对首页、个人中心、服装定制管理…

Java类和对象(1)

&#x1f435;本篇文章将会开始对类和对象的第一部分讲解 一、简单描述类和对象 对象可以理解为一个实体&#xff0c;在现实生活中&#xff0c;比如在创建一个建筑之前&#xff0c;要先有一个蓝图&#xff0c;这个蓝图用来描述这个建筑的各种属性&#xff1b;此时蓝图就是类&a…

C++day6作业

1.思维导图 2.编程题&#xff1a; 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&am…

后台管理系统解决方案-中大型-Vben Admin

后台管理系统解决方案-中大型-Vben Admin 官网 Vben Admin 在线演示 Vben Admin 为什么选择它 github现有20K星&#xff0c;并且它有个可视化生成表单&#xff0c;我很喜欢 快速开始 # 拉取代码 git clone https://github.com/vbenjs/vue-vben-admin-doc# 安装依赖 yarn#…

天津WEB前端培训哪家好?Web机构推荐!

05年以后&#xff0c;互联网已经进入了web2.0时代&#xff0c;同时也标志着网站的前端由此发生了翻天覆地的变化&#xff0c;现在市场上对WEB前端开发工程师岗位有着很大的需求&#xff0c;学习web前端开发的方式有很多种&#xff0c;对于初学者来说&#xff0c;选择自学还是培…

使用篇(一):Ai绘图-Stable Diffusion WebUI

1.介绍 1.1 概述 Stable Diffusion Web UI是一个基于Stable diffusion AI模型的AI绘画软件。它是一个多功能的AI绘画软件&#xff0c;支持以下几个功能&#xff1a; 用户可以输入一堆关键词或一句话来生成图片。 它使用了图像加噪去噪过程中的生成模型—— Duffusion&#xff…

与set和map相关的OJ题练习

一、两个数组的交集 题目链接&#xff1a; 349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给两个数组&#xff0c;求在数组里面共同出现的部分&#xff0c;就是求两个数组的交集&#xff0c;返回顺序不做要求 解题思路&#xff1a; …

视频剪辑:制作视频画中画效果,背景图片的添加方法

随着社交媒体的兴起&#xff0c;视频制作越来越受到人们的关注。在视频制作过程中&#xff0c;除了主要的画面&#xff0c;背景图片往往能够增加视频的层次感和视觉效果。今天&#xff0c;我们就来探讨一下如何使用云炫AI智剪制作视频画中画效果&#xff0c;并添加背景图片。 在…

Java自学第6课:电商项目(2)

1 创建工具类并连接数据库 在工程src右键单击new&#xff0c;新建util包 再创建DBUtil类 数据库交互需要有数据库支持的包&#xff0c;这是官方给出的类库。 先声明1个代码块 // 静态代码块 只加载1次static{try {Class.forName("com.mysql.jdbc.Driver");} catch (…

基于springboot实现高校党务平台管理系统【项目源码】

基于springboot实现高校党务平台管理系统演示 Java技术 Java是由Sun公司推出的一门跨平台的面向对象的程序设计语言。因为Java 技术具有卓越的通用性、高效性、健壮的安全性和平台移植性的特点&#xff0c;而且Java是开源的&#xff0c;拥有全世界最大的开发者专业社群&#x…

可持久化01Trie

例题&#xff1a; 解释&#xff1a; 首先这里要求连续异或&#xff0c;所以存储前缀异或和数组。首先的话&#xff0c;我们只考虑前r个版本的Trie&#xff0c;所以以root[r]为根节点的Trie就是1到r位置数。但是&#xff0c;还有一个l左端点&#xff0c;所以我们对于每一个节点…

python开发过程中注意编码规范~

文章目录 一、 代码编排二、 文档编排三、 空格的使用四、 注释五、 文档描述六、 命名规范总体原则&#xff0c;新编代码必须按下面命名风格进行&#xff0c;现有库的编码尽量保持风格。七 编码建议关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、…

解析虚拟文件系统的调用

Linux 可以支持多达数十种不同的文件系统。它们的实现各不相同&#xff0c;因此 Linux 内核向用户空间提供了虚拟文件系统这个统一的接口&#xff0c;来对文件系统进行操作。它提供了常见的文件系统对象模型&#xff0c;例如 inode、directory entry、mount 等&#xff0c;以及…

【Git】如何安装git,项目中使用git上传到远程仓库,使用git中对多人使用出现的版本问题的解决

前言&#xff1a; 一&#xff0c;Git的介绍&#xff0c;安装&#xff0c;与SVN的对比 1.1Git的介绍 Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目。 Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控…