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

NV12数据格式转H265编码格式实现过程

一、需求

在视频处理和传输应用中,将视频数据编码为高效的格式是非常重要的。H.265(也称为HEVC)是一种先进的视频编码标准,具有更好的压缩性能和图像质量,相比于传统的编码标准(如H.264),可以显著减少视频的带宽和存储需求。

NV12是一种常见的视频格式,用于表示YUV图像数据,尤其在实时视频处理中广泛使用。它将亮度(Y)和色度(UV)分量分开存储,其中Y分量占据连续的内存块,而UV分量交错存储在另一个连续的内存块中。

本需求实现将NV12格式的视频数据转换为H.265格式的数据,并将其保存在内存中。这样可以方便地对视频数据进行后续处理,如网络传输、存储或实时流媒体传输等。

为了实现这一需求,使用了C语言和FFmpeg库。FFmpeg是一个强大的开源多媒体处理库,提供了丰富的功能和编解码器,包括H.265编码器。

下面代码实现了如何使用FFmpeg库将NV12格式的视频数据编码为H.265格式的数据,并将其保存在内存中。函数接受NV12数据、宽度和高度作为输入,并返回编码后的H.265数据和数据大小。这样,用户可以方便地将NV12格式的视频数据转换为H.265格式,并在内存中进行进一步处理或传输。同时也提供了文件的方式。

这个功能可以在各种视频处理应用中使用,如视频编辑软件、实时视频流处理系统、视频通信应用等。通过使用H.265编码,可以提高视频传输的效率和质量,减少带宽和存储需求,同时保持良好的视觉体验。

image-20230720162116737

二、NV12和H265格式详细介绍

NV12和H265都是视频编码中经常使用的像素格式,下面分别介绍这两种格式的特点和使用场景。

【1】NV12像素格式

NV12是一种YUV像素格式,常用于视频编码和解码过程中。它是一种planar格式,即Y和UV分量分别存储在不同的平面中。其中,Y分量表示亮度信息,UV分量表示色度信息。在NV12格式中,UV分量的采样率为4:2:0,即每4个Y像素共用一个U和一个V像素。这种采样方式可以有效地减小数据量,同时保持视频质量。

NV12格式的存储顺序为:先存储所有的Y分量,然后存储所有的U和V分量,U和V交错存储。因此,NV12格式的数据大小为(1.5 x 图像宽度 x 图像高度)字节。

NV12格式常用于视频流传输和视频编解码器中,例如在H.264视频编解码器和DirectShow视频开发中都广泛使用。

【2】H265像素格式

H265(又称HEVC)是一种高效的视频编码标准,它可以在相同视频质量的情况下大幅度减小视频文件的大小。H265支持多种像素格式,其中最常用的是YUV 4:2:0和YUV 4:2:2。

YUV 4:2:0格式与NV12类似,也是一种planar格式,其中Y分量存储亮度信息,UV分量采用4:2:0采样方式存储色度信息。YUV 4:2:2格式则采用4:2:2的采样方式存储UV分量,即每2个Y像素共用一个U和一个V像素。

与H264相比,H265的主要改进在于更高的压缩率和更低的比特率,同时保持相同质量的视频输出。因此,H265格式可以在同样的视频质量下使用更低的比特率进行编码,达到更小的文件大小。H265格式常用于网络视频流媒体传输、4K和8K高清视频等领域。

三、代码实现

【1】内存数据处理

要将NV12格式的数据转换为H.265格式的数据并保存在内存中,可以使用FFmpeg库来实现编码操作。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <libavcodec/avcodec.h>// 将NV12数据编码为H.265并保存在内存中
int encodeNV12toH265(uint8_t* nv12Data, int width, int height, uint8_t** h265Data, int* h265Size) {int ret = 0;AVCodec* codec = NULL;AVCodecContext* codecContext = NULL;AVFrame* frame = NULL;AVPacket* packet = NULL;// 注册所有的编解码器avcodec_register_all();// 查找H.265编码器codec = avcodec_find_encoder(AV_CODEC_ID_H265);if (!codec) {fprintf(stderr, "找不到H.265编码器\n");ret = -1;goto cleanup;}// 创建编码器上下文codecContext = avcodec_alloc_context3(codec);if (!codecContext) {fprintf(stderr, "无法分配编码器上下文\n");ret = -1;goto cleanup;}// 配置编码器参数codecContext->width = width;codecContext->height = height;codecContext->pix_fmt = AV_PIX_FMT_NV12;codecContext->codec_id = AV_CODEC_ID_H265;codecContext->codec_type = AVMEDIA_TYPE_VIDEO;codecContext->time_base.num = 1;codecContext->time_base.den = 25; // 假设帧率为25fps// 打开编码器if (avcodec_open2(codecContext, codec, NULL) < 0) {fprintf(stderr, "无法打开编码器\n");ret = -1;goto cleanup;}// 创建输入帧frame = av_frame_alloc();if (!frame) {fprintf(stderr, "无法分配输入帧\n");ret = -1;goto cleanup;}// 设置输入帧的属性frame->format = codecContext->pix_fmt;frame->width = codecContext->width;frame->height = codecContext->height;// 分配输入帧的数据缓冲区ret = av_frame_get_buffer(frame, 32);if (ret < 0) {fprintf(stderr, "无法分配输入帧的数据缓冲区\n");ret = -1;goto cleanup;}// 将NV12数据复制到输入帧的数据缓冲区memcpy(frame->data[0], nv12Data, width * height); // Y分量memcpy(frame->data[1], nv12Data + width * height, width * height / 2); // UV分量// 创建输出数据包packet = av_packet_alloc();if (!packet) {fprintf(stderr, "无法分配输出数据包\n");ret = -1;goto cleanup;}// 发送输入帧给编码器ret = avcodec_send_frame(codecContext, frame);if (ret < 0) {fprintf(stderr, "无法发送输入帧给编码器\n");ret = -1;goto cleanup;}// 循环从编码器接收输出数据包while (ret >= 0) {ret = avcodec_receive_packet(codecContext, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)break;else if (ret < 0) {fprintf(stderr, "从编码器接收输出数据包时发生错误\n");ret = -1;goto cleanup;}// 分配内存并复制输出数据包的数据*h265Data = (uint8_t*)malloc(packet->size);if (!*h265Data) {fprintf(stderr, "无法分配内存\n");ret = -1;goto cleanup;}memcpy(*h265Data, packet->data, packet->size);*h265Size = packet->size;// 释放输出数据包av_packet_unref(packet);}cleanup:// 释放资源if (packet)av_packet_free(&packet);if (frame)av_frame_free(&frame);if (codecContext)avcodec_free_context(&codecContext);return ret;
}

使用以上的封装函数,可以将NV12格式的数据传入函数中,函数会将其编码为H.265格式的数据并保存在内存中。编码后的H.265数据存储在h265Data指针指向的内存中,数据的大小保存在h265Size中。

【2】文件数据处理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>// 将NV12数据编码为H.265格式
int encodeNV12ToH265(const char* nv12FilePath, const char* h265FilePath, int width, int height)
{// 注册所有的编解码器avcodec_register_all();// 打开编码器AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H265);if (!codec) {fprintf(stderr, "Failed to find H.265 encoder\n");return -1;}AVCodecContext* codecContext = avcodec_alloc_context3(codec);if (!codecContext) {fprintf(stderr, "Failed to allocate codec context\n");return -1;}// 配置编码器参数codecContext->width = width;codecContext->height = height;codecContext->time_base = (AVRational){1, 25};  // 帧率为25fpscodecContext->framerate = (AVRational){25, 1};codecContext->pix_fmt = AV_PIX_FMT_NV12;// 打开编码器上下文if (avcodec_open2(codecContext, codec, NULL) < 0) {fprintf(stderr, "Failed to open codec\n");return -1;}// 创建输入文件的AVFrameAVFrame* frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Failed to allocate frame\n");return -1;}frame->format = codecContext->pix_fmt;frame->width = codecContext->width;frame->height = codecContext->height;// 分配输入帧缓冲区int ret = av_frame_get_buffer(frame, 32);if (ret < 0) {fprintf(stderr, "Failed to allocate frame buffer\n");return -1;}// 打开输入文件FILE* nv12File = fopen(nv12FilePath, "rb");if (!nv12File) {fprintf(stderr, "Failed to open NV12 file\n");return -1;}// 打开输出文件FILE* h265File = fopen(h265FilePath, "wb");if (!h265File) {fprintf(stderr, "Failed to open H.265 file\n");return -1;}// 创建编码用的AVPacketAVPacket* packet = av_packet_alloc();if (!packet) {fprintf(stderr, "Failed to allocate packet\n");return -1;}int frameCount = 0;while (1) {// 从输入文件读取NV12数据到AVFrameif (fread(frame->data[0], 1, width * height, nv12File) != width * height) {break;}if (fread(frame->data[1], 1, width * height / 2, nv12File) != width * height / 2) {break;}frame->pts = frameCount++;  // 设置帧的显示时间戳// 发送AVFrame到编码器ret = avcodec_send_frame(codecContext, frame);if (ret < 0) {fprintf(stderr, "Error sending frame to codec: %s\n", av_err2str(ret));return -1;}// 从编码器接收编码后的数据包while (ret >= 0) {ret = avcodec_receive_packet(codecContext, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {fprintf(stderr, "Error receiving packet from codec: %s\n", av_err2str(ret));return -1;}// 将编码后的数据包写入输出文件fwrite(packet->data, 1, packet->size, h265File);av_packet_unref(packet);}}// 刷新编码器ret = avcodec_send_frame(codecContext, NULL);if (ret < 0) {fprintf(stderr, "Error sending flush frame to codec: %s\n", av_err2str(ret));return -1;}while (ret >= 0) {ret = avcodec_receive_packet(codecContext, packet);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {fprintf(stderr, "Error receiving packet from codec: %s\n", av_err2str(ret));return -1;}// 将编码后的数据包写入输出文件fwrite(packet->data, 1, packet->size, h265File);av_packet_unref(packet);}// 释放资源fclose(nv12File);fclose(h265File);av_frame_free(&frame);av_packet_free(&packet);avcodec_free_context(&codecContext);return 0;
}int main()
{const char* nv12FilePath = "input.nv12";const char* h265FilePath = "output.h265";int width = 1920;int height = 1080;int ret = encodeNV12ToH265(nv12FilePath, h265FilePath, width, height);if (ret < 0) {fprintf(stderr, "Failed to encode NV12 to H.265\n");return -1;}printf("Encoding complete\n");return 0;
}

要确保已经正确安装了FFmpeg库,并在编译选项中包含了FFmpeg的头文件和库文件。

需要提供NV12格式的输入文件路径、输出H.265格式文件路径以及图像的宽度和高度。

相关文章:

NV12数据格式转H265编码格式实现过程

一、需求 在视频处理和传输应用中&#xff0c;将视频数据编码为高效的格式是非常重要的。H.265&#xff08;也称为HEVC&#xff09;是一种先进的视频编码标准&#xff0c;具有更好的压缩性能和图像质量&#xff0c;相比于传统的编码标准&#xff08;如H.264&#xff09;&#…...

ubuntu 22.04 深度学习环境配置

第一步 安装驱动 网址&#xff1a;https://www.nvidia.com/download/index.aspx 根据硬件选择&#xff0c;我这里是 ubuntu 服务器,显卡是v100 sudo su root chmod ax NVIDIA //按 TAB 即可 加运行权限 # 禁用原显卡驱动 vim /etc/modprobe.d/blacklist.conf # 在最后一行…...

支付宝小程序集成mqtt兼容IOS和安卓

1. 前言 ​ 去年就想做支付宝小程序接入mqtt协议。但最终多方咨询&#xff0c;问客服问社区得到的答案都是支付宝小程序不能直接支持mqtt协议。偶然间发现徐宏大神的佳作&#xff0c;终于发现了xmqtt.js这个好东西。它实现了支付宝小程序完美接入mqtt协议&#xff0c;设备可以…...

在Qt5中SQLite3的使用

一、SQLite简要介绍 什么是SQLite SQLite是一个进程内的库&#xff0c;实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库&#xff0c;这意味着与其他数据库不一样&#xff0c;您不需要在系统中配置。 就像其他数据库&#xff0c;S…...

使用Docker部署debezium来监控 MySQL 数据库

使用Docker部署debezium来监控 MySQL 数据库 Debezium是一个分布式平台,它将来自现有数据库的信息转换为事件流,使应用程序能够检测并立即响应数据库中的行级更改。 Debezium构建在Apache Kafka之上,并提供了一组Kafka Connect兼容的连接器。每个连接器都与特定的数据库管…...

百度低质量站点怎么办?解决百度低质量站点的方法和工具

百度低质量站点怎么恢复&#xff1f;这是许多网站主和运营人员在SEO优化过程中经常面临的一个问题。百度作为中国最大的搜索引擎&#xff0c;对于网站收录和排名具有至关重要的影响。然而&#xff0c;由于各种原因&#xff0c;有些网站可能面临被百度降权或收录减少的情况。那么…...

MSOS604A是德科技keysight MSOS604A示波器

181/2461/8938Infiniium S系列示波器融合了创新技术&#xff0c;旨在提供卓越的测量。新的10位ADC和低噪声前端技术协同工作&#xff0c;提供高达8 GHz的性能和业界最佳的信号完整性。一个高级框架&#xff0c;配有可快速启动的固态硬盘、可轻松触摸的15英寸电容式显示屏和可快…...

春秋云镜 CVE-2016-0785

春秋云镜 CVE-2016-0785 S2-029 靶标介绍 2.3.28 之前的 Apache Struts 2.x 允许远程攻击者通过标签属性中的“%{}”序列执行任意代码。 启动场景 漏洞利用 工具利用 得到flag flag{a4c7fc9a-8e2d-49b8-9b09-22790fb2bfb6}...

入门ElasticSearch :为什么选择ES作为搜索引擎?

介绍 随着数据量的不断增长&#xff0c;搜索和分析大规模数据集变得越来越重要。传统数据库在面对这种需求时往往表现不佳&#xff0c;这时候就需要一种专门用于搜索和分析的引擎。ElasticSearch &#xff08;简称ES&#xff09;就是这样一款强大的搜索引擎&#xff0c;它具有许…...

汽车安全及标准

汽车安全及标准 我们的测试系统如何处理整个标准&#xff1f; 您是否需要处理汽车行业的一系列标准&#xff1f; 不同的标准侧重于驱动逆变器的安全性和功能性&#xff1a; 功能安全&#xff08;ISO 26262&#xff09;信号和低压车载网络&#xff08;LV 124、LV 148 和 VDA …...

APP备案流程详细解读

背景介绍 2023年8月4日&#xff0c;工信部发布《工业和信息化部关于开展移动互联网应用程序备案工作的通知》。 在中华人民共和国境内从事互联网信息服务的APP主办者&#xff0c;应当依照《中华人民共和国反电信网络诈骗法》《互联网信息服务管理办法》&#xff08;国务院令第…...

ES 集群常用排查命令

说明&#xff1a;集群使用非默认端口9200&#xff0c;使用的是7116端口举例 一、常用命令 #1.集群健康状态 [wlsadminelastic-01~]$ curl -XGET "http://10.219.27.00:7116/_cluster/health?pretty" { cluster name":"cluster" "status"…...

Nougat 深度剖析

Nougat 深度剖析 项目地址&#xff1a;https://github.com/facebookresearch/nougat 论文地址&#xff1a;Nougat: Neural Optical Understanding for Academic Documents 0 背景 近日&#xff0c;MetaAI又放了大招&#xff0c;他们提出了一种全新的端到端的OCR模型&#x…...

ffmpeg的使用

本文章记录ffmpeg 源码下载&#xff0c;编译&#xff0c;及使用。 一、FFMPEG 源码下载解压 源码官网地址&#xff1a;http://ffmpeg.org/download.html#releases 下载最新版本ffmpeg6.0。 使用命令tar xvJf ffmpeg-6.0.tar.xz 解压。 二、了解FFMPEG源码 &#xff08;一&am…...

深度强化学习算法的参数更新时机

深度强化学习算法的参数更新时机 深度强化学习中往往涉及到多个神经网络来拟合策略函数、值函数等&#xff0c;什么时候更新参数因算法而异&#xff0c;与具体算法架构/算法思想紧密相关。 算法参数更新时机架构DQN先收集一定经验&#xff0c;然后每步更新Off Policy Value-B…...

【进阶篇】MySQL的MVCC实现机制详解

文章目录 0.前言1.基础介绍1.1. 什么是MVCC?1.1. 什么是当前读和快照读&#xff1f;1.1. 当前读&#xff0c;快照读和MVCC的关系1.1. MVCC能解决什么问题&#xff0c;好处是&#xff1f;1.1.1. 提高并发性能1.1.2. 避免死锁1.1.3. 解决脏读、不可重复读和幻读等问题1.1.4. 实现…...

Git 命令行查看仓库信息

目录 查看系统config ​编辑查看当前用户&#xff08;global&#xff09;配置 查看当前仓库配置信息 查看系统config git config --system --list 1 查看当前用户&#xff08;global&#xff09;配置 git config --global --list 1 查到的是email , name 等ssl签名信息&a…...

【爬虫】8.1. 深度使用tesseract-OCR技术识别图形验证码

深度使用tesseract-OCR技术识别图形验证码 文章目录 深度使用tesseract-OCR技术识别图形验证码1. OCR技术2. 准备工作3. 简单作用了解3.1. 验证码图片爬取-screenshot_as_png3.2. 识别测试-image_to_string3.2.1. 正确识别3.2.2. 错误识别3.2.3. 灰度调节 3.3. 识别实战-使用im…...

【PythonRS】基于GDAL修改栅格数据的DN值

遥感工作者离不开栅格数据&#xff0c;有时候我们可能需要修改栅格数据的值&#xff0c;但ENVI和ArcGIS中并没有直接修改DN值的工具&#xff0c;只有栅格计算器、Band math这些工具去计算整个波段的值&#xff0c;或者Edit Classification Image工具可以修改ENVI分类后的像元值…...

mysql课堂笔记 mac

目录 启动mac上的mysql 进入mysql mac windows 创建数据库 创建表 修改字段数据类型 修改字段名 增加字段 删除字段 启动mac上的mysql sudo /usr/local/mysql/support-files/mysql.server start 直接输入你的开机密码即可。 编辑 进入mysql mac sudo /usr/local…...

仪表盘管理化技术数据可视化与交互设计

仪表盘管理化技术&#xff1a;数据可视化与交互设计的智慧引擎 在数字化转型浪潮中&#xff0c;仪表盘管理化技术已成为企业决策的核心工具。通过将复杂数据转化为直观图表&#xff0c;并结合交互设计&#xff0c;它帮助用户快速捕捉关键信息&#xff0c;优化业务流程。无论是…...

解决Mac多设备滚动冲突:Scroll Reverser让触控板与鼠标和谐共存

解决Mac多设备滚动冲突&#xff1a;Scroll Reverser让触控板与鼠标和谐共存 【免费下载链接】Scroll-Reverser Per-device scrolling prefs on macOS. 项目地址: https://gitcode.com/gh_mirrors/sc/Scroll-Reverser 你是否在MacBook上使用触控板时习惯"自然滚动&q…...

5分钟搞定网盘直链下载:八大平台一键解析全攻略

5分钟搞定网盘直链下载&#xff1a;八大平台一键解析全攻略 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 /…...

Granite TimeSeries FlowState R1实战:基于SpringBoot的金融时序数据预测微服务

Granite TimeSeries FlowState R1实战&#xff1a;基于SpringBoot的金融时序数据预测微服务 最近和几个做金融科技的朋友聊天&#xff0c;他们都在头疼同一个问题&#xff1a;面对海量的股票价格、交易量这些时序数据&#xff0c;怎么才能快速、准确地预测未来几天的走势&…...

【词汇专栏】 预训练 vs 微调:AI 界最常被混淆的一对概念

预训练 vs 微调&#xff1a;AI 界最常被混淆的一对概念 “我们对模型进行了微调” “这是基于预训练模型的” “我要训练一个专属 AI”……这几句话你一定经常听到&#xff0c;但它们到底有什么区别&#xff1f;谁更厉害&#xff1f;什么时候该用哪个&#xff1f; 一句话定义 …...

基于Simulink的晶闸管直流开环调速系统建模与动态特性分析

1. 晶闸管直流开环调速系统基础认知 第一次接触晶闸管直流调速系统时&#xff0c;我被那一堆专业术语搞得头晕——什么"三相全控整流"、"同步触发器"、"移相控制角"&#xff0c;听着就像天书。但实际拆解后发现&#xff0c;这套系统本质上就是个…...

【SPIE出版、EI检索稳定】2026年智慧油气与可持续发展国际学术会议(SOGSD 2026)

在全球能源转型与科技革命深度融合之际&#xff0c;智慧油气已成为推动行业高质量发展的核心动力。作为首届盛会&#xff0c;2026年智慧油气与可持续发展国际学术会议将于2026年5月29-31日在中国成都举行。SOGSD 2026旨在构建一个高水平的国际合作交流平台&#xff0c;聚焦人工…...

STM32智能循迹小车(1)多路TCRT5000传感器与PWM调速融合实践

1. 多路TCRT5000传感器布局策略 在搭建STM32智能循迹小车时&#xff0c;传感器的布局直接影响循迹效果。我建议采用前三角布局法&#xff1a;将三个TCRT5000模块呈等腰三角形排列&#xff0c;中间传感器位于车头正中&#xff0c;左右两侧传感器对称分布&#xff0c;间距建议控制…...

海思Hi3516DV500/HI3519DV500开发实战:从SDK编译到多媒体例程验证

1. 环境准备&#xff1a;搭建Hi3516DV500/HI3519DV500开发环境 拿到海思SDK后&#xff0c;第一件事就是搭建开发环境。我建议使用Ubuntu 18.04或20.04系统&#xff0c;这是官方推荐的环境。安装完系统后&#xff0c;需要配置一些基础工具链&#xff1a; sudo apt-get update su…...

一个人开发40个需求太慢?我用 Claude Code 搭了套“AI团队“并行干活

上周末&#xff0c;我盯着项目的需求列表发呆——42个用户故事&#xff0c;6大模块&#xff0c;ddl还有两周。用 BMAD 方法论规划得很漂亮&#xff1a;Epic 拆成 Story&#xff0c;Story 有验收标准&#xff0c;一切井井有条。 但问题来了&#xff1a; 每完成一个 Story&#x…...