iOS CVPixelBufferCreate 创建 CVPixelBufferRef 时屏幕拉伸或像素偏移(花屏)

先说结论:

     CVPixelBufferCreate 创建的 CVPixelBufferRef 可能由以下的原因导致的:

1.pixelFormatType 格式错误,换一下格式尝试

2.width和height 非 32 的整数倍

3.视频帧的宽高比非标准比例(4:3,16:9,1:1)

另外说明,我没找到比较有权威的对应文档和教程,上面是我通过测试得出的结论,如果有错误,还请批评指正

一、pixelFormatType 格式错误 

kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 是nv12 的格式 yyyyuvuv

kCVPixelFormatType_420YpCbCr8Planar 是i420的格式 yyyyuuvv

需要注意的是,并不是每一个枚举值在iOS上都是支持的,可能存在不支持的情况

二、width和height 非 32 的整数倍

CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault,1920,1080,kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,   //  NV12(__bridge CFDictionaryRef)(pixelAttributes),&pixelBuffer);

像素的存储格式通常是一行一行存储的,当宽度为了实现32的整数倍做出调整时,从帧数据复制的时候,就要在多出的那部分做对应的填充。缩小也是一样的,要去放弃多余的数据

比如原视频是 宽4 高4的视频,你调成宽8高8的时候

yyyy                        yyyyyyyy (后面四个y是自己填充的)

yyyy                        yyyyyyyy (后面四个y是自己填充的)

uuuu     ->>             uuuuuuuu (后面四个u是自己填充的)

vvvv                        vvvvvvvv (后面四个v是自己填充的)

例子可能不太恰当,但是意思比较清楚的。后面会有代码举例

三、视频帧的宽高比非标准比例(4:3,16:9,1:1)

这个解决办法和上一条是一样的,去调整宽高,来实现比例的要求。

例子:

//例子是 I420 数据转成 NV12 的 CVPixelBufferRef
//width 原视频的宽
//height 原视频的高
//buffer NSData 类型的原视频的帧数据。格式为I420NSDictionary *pixelAttributes = @{(NSString *)kCVPixelBufferIOSurfacePropertiesKey:@{}};//调整后的widthint inner_width = 0;//调整后的heightint inner_height = 0;//视频比例不是 16:9 , 4:3 , 1:1 ,宽高不是32的整数倍时,将宽高统一调整为1920和1080.//因为项目要求,所有视频都不会比1920x1080大,故这里只是放大,无需缩小if (width*1.0 / height*1.0 != 16.0/9 || width*1.0 / height*1.0 != 1.0 || width*1.0 / height*1.0 != 4.0/3 || width%32 != 0 || height%32 != 0 ) {inner_width = 1920;inner_height = 1080;}else{inner_width = width;inner_height = height;}CVPixelBufferRef pixelBuffer = NULL;CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault,inner_width,inner_height,kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,   //  NV12(__bridge CFDictionaryRef)(pixelAttributes),&pixelBuffer);if (result != kCVReturnSuccess) {//CVPixelBufferRef 初始化失败NSLog(@"Unable to create cvpixelbuffer %d", result);}// 复制 buffer.bytes 数据uint8_t* pdata = (uint8_t*)buffer.bytes;CVPixelBufferLockBaseAddress(pixelBuffer, 0);// Y 数据填充unsigned char *yDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);for (int i = 0, k = 0; i < inner_height; i++) {for (int j = 0; j< inner_width; j++) {if (inner_width > width && j >= width) {// 扩大的那部分 width 用该行最后一个像素填充if (i >= height) {// 高度大于视频的实际高度,说明该行没有帧数据,则用实际视频帧的最后一行最后一个像素填充yDestPlane[k++] = pdata[height*width];continue;}yDestPlane[k++] = pdata[i*width + width];continue;}if (inner_height > height && i >= height) {// 高度大于视频的实际高度,说明改行没有帧数据,则用实际视频帧的最后一行最后一个像素填充yDestPlane[k++] = pdata[height * width];continue;}yDestPlane[k++] = pdata[i*width + j];}}// UV 数据填充unsigned char *uvDestPlane = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);for (int i = 0, k = 0; i < inner_height / 2; i++) {for (int j = 0; j< inner_width / 2; j ++) {if (inner_width > width && j >= width/2) {if (i > height/2) {// 高度大于视频的实际高度,说明改行没有帧数据,则用实际视频帧的最后一行最后一个像素填充uvDestPlane[k++] = pdata[height *width/4  + width * height];uvDestPlane[k++] = pdata[height *width/4  + width * height * 5 / 4];}else{// 高度不高于视频的实际高度,说明改行有帧数据,则用实际视频帧的该行最后一个像素填充uvDestPlane[k++] = pdata[i *width/2 + width/2 + width * height];uvDestPlane[k++] = pdata[i *width/2 + width/2 + width * height * 5 / 4];}continue;}if (inner_height > height && i >= height/2) {// 高度大于视频的实际高度,说明改行没有帧数据,则用实际视频帧的最后一行最后一个像素填充uvDestPlane[k++] = pdata[height *width/4 + width * height];uvDestPlane[k++] = pdata[height *width/4 + width * height * 5 / 4];continue;}// 高度不高于视频的实际高度,说明改行有帧数据,则用实际视频帧像素填充uvDestPlane[k++] = pdata[i *width/2 + j + width * height];uvDestPlane[k++] = pdata[i *width/2 + j + width * height * 5 / 4];}}CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
//pixelBuffer 构造完成,用于后续使用

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

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

相关文章

今天面试招了个18K的人,从腾讯出来的果然都有两把刷子···

公司前段时间缺人&#xff0c;也面了不少测试&#xff0c;前面一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资在15-20k&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。看简历很多都是4年工作经验&#xff0c;但面试中&#xff0c;不…

docker save 命令 docker load 命令 快速复制容器

docker save 命令 docker load 命令 1、docker save 命令2、docker load 命令 1、docker save 命令 docker save 命令用于在系统上把正在使用的某个容器镜像 导出成容器镜像文件保存下载&#xff0c;以便在其他系统上导入这个容器镜像文件 以便快速在其他服务器上启动相同的容…

读书笔记:《思考 . 快与慢》- 1 系统1 系统2

《思考 . 快与慢》 [美] 丹尼尔 . 卡尼曼 著 胡晓姣 李爱民 何梦莹 译 这本书会改变你的思考方式 利用闲谈发现和分析别人犯的错误比分析自己的错误更容易&#xff0c;也更有意思 在人生最辉煌的时候&#xff0c;我们很难对自己的信念和需求产生怀疑&#xff0c;越是在最…

【Web】Java反序列化之CC2——commons-collections4的新链之一

目录 关于commons-collections4 一个重要的思维模型 触发Transform的关键类&#xff1a;TransformingComparator 反序列化的入口&#xff1a;PriorityQueue Exp 关于commons-collections4 commons-collections4 是 Apache Commons 组件库中的一个项目&#xff0c;它是对旧…

在Linux上定时执行脚本

在Linux上定时执行脚本通常可以使用 ​cron​任务来实现。​cron​是一个系统服务&#xff0c;用于在预定时间自动执行命令或脚本。下面是如何在Linux上设置定时执行脚本的步骤&#xff1a; 编写脚本&#xff1a;首先&#xff0c;你需要编写需要定时执行的脚本文件&#xff0c;…

找不到msvcp140.dll无法运行程序如何处理?分享5种解决方法

在计算机系统运行过程中&#xff0c;如果无法找到必要的动态链接库文件msvcp140.dll&#xff0c;可能会引发一系列的问题与故障。这个特定的dll文件是Microsoft Visual C Redistributable Package的一部分&#xff0c;对于许多基于此编译环境开发的应用程序至关重要。缺失msvcp…

C++的常用排序(未完待续)

注&#xff1a;本文以升序为例 一、冒泡排序 1.1 操作方法 步骤1比较相邻元素&#xff0c;如果前者比后者大&#xff0c;则交换它们。步骤2对头到尾&#xff0c;对所有元素按序执行一轮这样的操作(这样可以找到第一最大值)步骤3再从第一个元素开始&#xff0c;重复上述比较操…

jax可微分编程的笔记(8)

jax可微分编程的笔记(8) 第八章 循环神经网络 神经网络是可微分编程中最为重要的模型构造形式&#xff0c;也是当代 深度学习的基本组成部分&#xff0c;深度学习中的“深度”一词&#xff0c;便是对 神经网络的层数的形容。 8.1 神经网络的生物学基础 通过层层近似&#x…

智能驾驶规划控制理论学习02-基于搜索的路径规划方法

目录 一、路径搜索问题 二、图论基础 三、图搜索方法 1、广度优先搜索&#xff08;BFS&#xff09; bfs与dfs的区别 bfs的搜索过程 bfs的算法实现 2、迪杰斯特拉算法&#xff08;Dijkstra&#xff09; 核心思想 优先级队列 Dijkstra搜索过程 Dijkstra优缺点…

【.NET Core】深入理解IO - FileSteam流

【.NET Core】深入理解IO - FileSteam流 文章目录 【.NET Core】深入理解IO - FileSteam流一、IO流概述二、文件流FileStream2.1 FileStream概述2.2 FileStream检测流位置更改2.3 FileStream构造函数2.4 FileStream常用属性2.5 FileStream.Read方法2.6 FileStream.Write方法2.7…

插混、油混、增程式、轻混、强混,啥区别

这里写自定义目录标题 随着我国新能源汽车的大力推进&#xff0c;电车可以说是世界未来的主流&#xff0c;只不过现在是处在一个过渡时代 这是个好时代&#xff0c;因为我们见证并体验着历史过渡的细节 这是个不好的时代&#xff0c;因为我们可能只是未来新新人类的试验品 帮他…

MyBatis 学习(三)之 MyBatis 全局配置文件

目录 1 MyBatis 全局配置文件 2 properties 元素 3 setting 设置 4 typeAlianses 别名处理器 5 typeHandler 类型处理器 6 objectFacotry 对象工厂&#xff08;了解&#xff09; 7 plugins 插件&#xff08;了解&#xff09; 8 environments 运行环境 9 databaseIdPro…

今日arXiv最热大模型论文:点击即可播放!港中文发布大模型写歌神器!

一首歌&#xff0c;包含作词作曲两个部分。擅长作词or作曲就已经很牛了。比如方文山是周杰伦的御用作词人&#xff0c;而周杰伦写过很多耳熟能详的曲子。而兼具作词作曲才华的全能创作人却是难得一见。 最近港中文发布了一款歌曲创作大模型SongComposer&#xff0c;作词作曲都…

自测-1 打印沙漏

文章预览&#xff1a; 题目算法代码 题目 算法 以前做过这个&#xff0c;那次是c语言写的&#xff0c;一点一点处理一层一层完成&#xff0c;这次我换了一种语言用了另一种思想使用递归去写&#xff0c;还是我们要先求出应该有多少层这个很容易&#xff0c;中间输出部分我们算…

常见查找算法Java实现

顺序&#xff08;线性&#xff09;查找二分查找/折半查找插值查找斐波那契查找 线性查找 判断数列是否包含要求&#xff0c;如果找到了&#xff0c;就提示找到了&#xff0c;并给出下标值 // 线性查找 public static ArrayList<Integer> seqSearch(int[] arr, int value…

解决 npm install 报错的问题

在使用 npm 安装依赖包时&#xff0c;有时候会遇到各种报错问题&#xff0c;以下是一些常见的报错及解决方法&#xff1a; 1. ENOENT: no such file or directory 如果出现类似 ENOENT: no such file or directory 的报错&#xff0c;可能是因为某些文件或目录缺失或路径错误…

动态规划课堂3-----简单多状态问题(买卖股票最佳时机)

目录 引入&#xff1a; 例题1&#xff1a;按摩师&#xff08;打家劫舍I&#xff09; 例题2&#xff1a;打家劫舍II 例题3&#xff1a;删除并获得点数 例题4&#xff1a;粉刷房子 例题5&#xff1a;买卖股票的最佳时机含冷冻 结语&#xff1a; 引入&#xff1a; 相信看到…

深度学习 精选笔记(8)梯度消失和梯度爆炸

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

带你快速初步了解Python列表

1.列表 列表主要是用来存储多个数据&#xff0c;是有序的集合 2.创建列表 """ 语法&#xff1a;变量名 [数据1,数据2,数据3......] 注意&#xff1a;列表中的数据类型可以是各种不同的数据类型 """ 创建空列表 list1 [] print(list1) …

Gitlab: 私有化部署

目录 1. 说明 2. 资源要求 3. 安装 4. 配置实践 4.1 服务器 4.2 人员与项目 4.2 部署准备 4.2.1 访问变量及用户账号设置 4.2.2 Runner设置 4.2.3 要点 5. 应用项目 CI/CD 6. 参考 1. 说明 gitlab是一个强大且免费的代码管理/部署工具&#xff0c;能统一集成代码仓…