使用FFmpeg调用NVIDIA GPU以将H265转码为H264
背景
最近,该项目遇到了一个问题,即Web前端需要播放视频设备中的H265流,但是目前仅支持H264流,因此如果我想快速解决此问题,我就来了有两种解决方案。
方案1:通过RTMP将H265直接封装到自定义FLV中,然后将其发布到前端播放中。 Web前端必须支持解析H265回放的控制。如果使用这种解决方案,则目前很难找到适用的开源解决方案,而后端和后端的变化基本上可以说是被推翻了,因此这种方案很难实现。一会儿。
方案2:提供转码服务以解码H265,然后将其编码为H264。无需对Web前端播放方案进行任何更改。有两种方案,软件代码转换和硬件代码转换,因为软件代码转换非常消耗CPU资源,因此可以基本消除这种想法,因此仅考虑硬件代码转换方案。下面的文章将介绍硬件代码转换方案。
比较这两种方案,第二种方案比较合理,可以较快地解决H265播放问题。
H265和H264 1、 H265流nalu标头的一些基本知识
nuh_unit_type的值00 00 00 01 40 01为32,其语义是视频参数集VPS
nuh_unit_type的值00 00 00 01 42 01为33,其语义为序列参数集SPS
nuh_unit_type的值00 00 00 01 44 01为34,其语义是图像参数集PPS
nuh_unit_type的值00 00 00 01 4E 01为39,语义为补充增强信息SEI
nuh_unit_type的值00 00 00 01 26 01为19,其语义是可能具有RADL图像的IDR图像的SS编码数据IDR。
nuh_unit_type的值00 00 00 01 02 01为1,语义是引用的帖子图片,以及非TSA,非STSA SS编码的数据
2、 H264流nalu标头
00 00 00 01 06类型的值为06,NALU_TYPE_SEI语义是补充增强信息SEI
00 00 00 01 67 type的值为67,并且NALU_TYPE_SPS的语义是序列参数集SPS
00 00 00 01 68类型值是68,NALU_TYPE_PPS语义是图像参数集PPS
00 00 00 01 65类型值是65,NALU_TYPE_IDR语义是IDR图像中的IDR
3、补充:IDR帧和I帧之间的关系
IDR帧是I帧,但I帧不一定是IDR帧。在完整的视频流单元中,第一图像帧是IDR帧。 IDR帧为强制刷新帧。在解码过程中,当有IDR帧时,有必要更新sps,pps,原因是为了防止以前的I帧错误,导致无法通过参考I帧来校正sps,pps。
另一个流行的概念是GOP。 GOP的全名是“图片组”,它是两个I帧之间的距离。 GOP值越大,则I帧速率之间的P和B帧越多。 ,图像质量越好,如果GOP为120,分辨率为720P,帧频为60,则两个I帧的时间为120/60 = 2s。
转码的一些基础知识1、如何区分软编码和硬编码
软编码:使用CPU进行编码
硬编码:使用非CPU进行编码,例如图形卡GPU,专用DSP,FPGA芯片等。
2、软编码和硬编码的比较
软编码:实现直接,简单,参数调整方便,升级容易,但CPU负担重,性能低于硬编码,质量通常比硬编码低比特率。
硬编码:高性能,在低比特率下质量通常低于软编码器,但是某些产品已经在GPU硬件平台(例如X26 4))上移植了出色的软编码算法,其质量基本上相当于软编码。
3、当前的主流GPU加速平台
NVIDIA、INTEL、AMD等
4、当前主流GPU平台开发框架
CUDA:NVIDIA的封闭式编程框架,通过它可以调用GPU计算资源
AMD APP:AMD为自己的GPU提出了一套通用的并行编程框架。该标准是开放的。通过在CPU和GPU上支持OpenCL框架,它可以集成计算能力。
OpenCL:开放计算语言,一种为异构平台编写程序的框架。异构平台可以包括CPU,GPU和其他计算处理器。目的是使同一计算能够支持不同平台上的硬件加速。
Inel QuickSync:集成在Intel图形卡中的专用视频编模块。
5、流程差异
硬件和软件:读取(ffmpeg)->(NVIDIA cuvid)->编码器(ffmpeg)
软解码和编辑:读取(ffmpeg)->(ffmpeg)->编码器(ffmpeg)
软硬代码:读取(ffmpeg)->(ffmpeg)->编码器(NVIDIA nvenc)
NVIDIA + ffmpeg硬件加速部署1、环境安装和部署:Windows10 + ffmpeg 4. 1. 3 + NVIDIA GeForce GTX 1660Ti
注意:您需要在计算机上安装以下NVIDIA图形卡之一。我本人使用GeForce GTX 1660Ti显卡(在NVIDIA tesla T4显卡上进行了验证,它可以同时对8个以上的通道进行转码)
1. 1 ffmpeg Windows版本下载
下载链接:
支持下载static / dev / shared
我使用的ffmpeg版本是4. 1. 3
1. 2下载NVIDIA驱动程序(GTX 1660Ti)
下载链接:
版本45 1. 77(1 1. 0)地址:
1. 3下载cuda工具包(GTX 1660Ti)
下载链接:
版本45 1. 48(1 1. 0)地址:
不会介绍安装。
1. 4使用ffmpeg.exe -hwaccels显示所有可用的硬件
我下载的FFmpeg已经包含Nvidia的硬件
1. 5使用ffmpeg.exe -codecs查看编支持
DEV.LS h264 H.264 / AVC / MPEG-4 AVC / MPEG-4第10部分(:h264 h264_qsv h264_cuvid)(编码器:libx264 libx264rgb h264_amf h264_nvenc h264_qsv nvenc nvenc_h264)
DEV.L。 hevc H.265 / HEVC(高效视频编码)(:hevc hevc_qsv hevc_cuvid)(编码器:libx265 nvenc_hevc hevc_amf hevc_nvenc hevc_qsv)
h264_cuvid:h264硬件
h264_nvenc:h264硬件编码器
hevc_cuvid:h265硬件
hevc_nvenc:h265硬件编码器
NVENC简介
NVENC是NVIDIA开发的API,允许使用NVIDIA GPU图形卡执行h.264和HEVC(即H.26 5)编码。FFmpeg通过h264_nvenc和hevc_nvenc编码器支持NVENC。
要在FFmpeg中启用它,您需要:
支持硬件编码和解码的NVIDIA GPU
NVIDIA GPU驱动程序
没有配置禁用nvenc的ffmpeg
使用示例:
ffmpeg -i input -c:v h264_nvenc -profile high444p -pixel_format yuv444p -preset default output.mp4
您可以通过ffmpeg -h编码器= h264_nvenc或ffmpeg -h编码器= hevc_nvenc来查看可用的预设,其他选项和编码器信息。
注意:如果在没有NVENC功能的设备中发现错误,请确保您的编码采用受支持的像素格式。请参阅上面显示的编码器信息。
CUDA / CUVID / NvDecode
CUVID现在也被Nvidia称为nvdec,可以在Windows和Linux上进行解码。与nvenc结合使用,可提供完整的硬件转码。
CUVID提供H264,HEVC,MJPEG,mpeg1 / 2/4,vp8 / 9,vc1。编支持因硬件而异。
1、使用CUVID。在此示例中,CUVID将帧复制到系统内存:
ffmpeg -c:v h264_cuvid -i input output.mkv
2、使用CUVID和NVENC来实现完整的硬件转码:
ffmpeg -hwaccel cuvid -c:v h264_cuvid -i input -c:v h264_nvenc -preset slow output.mkv
3、硬件转码的一部分,该帧通过系统内存(这是对10位内容进行转码所必需的):
ffmpeg -c:v h264_cuvid -i input -c:v h264_nvenc -preset slow output.mkv
4、如果在编译ffmpeg时支持libnpp,则可以使用它在链中插入基于GPU的缩放器:
ffmpeg -hwaccel_device 0 -hwaccel cuvid -c:v h264_cuvid -i input -vf scale_npp=-1:720 -c:v h264_nvenc -preset slow output.mkv
hwaccel_device选项可用于指定ffmpeg:中的cuvid hwaccel使用的GPU。
FFmpeg命令行硬件对H265原始流文件进行代码转换
使用NVIDIA GTX1660ti图形卡+ ffmpeg 4. 1. 3
1、 H265软件解码,H264硬件编码
ffmpeg.exe -i h265toh264.h265 -vcodec h264_nvenc -r 30 -y h265toh264.h264
2、完整的硬件转码(H265硬件解码,H264硬件编码)
ffmpeg.exe -hwaccel cuvid -c:v hevc_cuvid -i h265toh264.h265 -c:v h264_nvenc -r 30 -y h265toh264.h264
用于将H265原始流文件转码为H264的FFmpeg API
在VS2017项目下,使用ffmpeg API将H265软件解码为YUV,并使用h264_nvenc(NVIDIA硬件编码器)或libx264(h264软件编码器)将YUV编码实现为H26 4.
直接转到源代码:
1、主要代码
#include "H265Decoder.h"
#include "H264Encoder.h"
#define ISSTORAGEYUV 0
int main()
{
int fpts = 0;
char frame_buf[1024] = { 0 };
int H265Lenth = 0;
char H265Buf[1024 * 512];
H265Decoder h265Decoder;
H264Encoder h264Encoder;
//初始化编时需要把正确的分辨率送进去
/*h265Decoder.Init(1920, 1080);
h264Encoder.Init(1920, 1080);*/
h265Decoder.Init(480, 272);
h264Encoder.Init(480, 272);
//读取文件
FILE * InFile = fopen("h265toh264.h265", "rb");
//输出文件
#if ISSTORAGEYUV
FILE * OutFile = fopen("h265toYUV.YUV", "wb");
#else
FILE * OutFile = fopen("h265toh264.h264", "wb");
#endif
//打印ffmpeg输出日志等级
printf("log level %d \n", av_log_get_level());
//设置ffmpeg输出日志等级
av_log_set_level(AV_LOG_INFO);
while (true)
{
int iReadSize = fread(frame_buf, 1, 512, InFile);
if (iReadSize <> 0)
{
break;
}
memcpy(H265Buf + H265Lenth, frame_buf, iReadSize);
H265Lenth = H265Lenth + iReadSize;
//获取一帧数据
while (true)
{
bool OneFrame = false;
if (H265Lenth <> 8)
{
break;
}
for (int i = 4; i H265Lenth - 4; i++)
{
//解析H265的nalu头
if (H265Buf[i] == 0x00 && H265Buf[i + 1] == 0x00 && H265Buf[i + 2] == 0x00 && H265Buf[i + 3] == 0x01)
{
h265Decoder.AddData(H265Buf, i);
while (true)
{
char * pOutData = NULL;
int OutSize = 0;
#if ISSTORAGEYUV
//获取H265解码后的YUV并存文件
if (!h265Decoder.GetYUVData(pOutData, OutSize))
{
break;
}
fwrite(pOutData, 1, OutSize, OutFile);
#else
//获取H265解码frame并编码H264存文件
AVFrame *frame = NULL;
frame = h265Decoder.GetData();
if (!frame) {
break;
}
frame->pts = fpts++;
h264Encoder.encode(frame, pOutData, OutSize);
fwrite(pOutData, 1, OutSize, OutFile);
#endif
}
H265Lenth = H265Lenth - i;
memcpy(H265Buf, H265Buf + i, H265Lenth);
OneFrame = true;
break;
}
}
if (OneFrame)
{
continue;
}
else
{
break;
}
}
}
if (OutFile) {
fclose(OutFile);
OutFile = NULL;
}
if (InFile) {
fclose(InFile);
InFile = NULL;
}
printf("pasing end\r\n");
return 0;
}
2、过程分析
1、要初始化编,需要发送正确的H265分辨率格式进行初始化
h265Decoder.Init(480, 272);
h264Encoder.Init(480, 272);
2、循环读取H265文件以形成完整的H265帧(根据H265的nalu标头00 00 00 01判断)
if (H265Buf[i] == 0x00 && H265Buf[i + 1] == 0x00 && H265Buf[i + 2] == 0x00 && H265Buf[i + 3] == 0x01)
3、将H265的帧发送到H265以进行软件解码
h265Decoder.AddData(H265Buf, i);
4、要获取由H265解码的YUV,可以选择保存YUV文件或将其发送到H264编码器进行编码并将其另存为H264文件
#if ISSTORAGEYUV
//获取H265解码后的YUV并存文件
if (!h265Decoder.GetYUVData(pOutData, OutSize))
{
break;
}
fwrite(pOutData, 1, OutSize, OutFile);
#else
//获取H265解码frame并编码H264存文件
AVFrame *frame = NULL;
frame = h265Decoder.GetData();
if (!frame) {
break;
}
frame->pts = fpts++;
h264Encoder.encode(frame, pOutData, OutSize);
fwrite(pOutData, 1, OutSize, OutFile);
#endif
3、遇到的问题
在调试过程中,调用h264_nvenc硬件编码器时遇到错误。经调查,发现H265解码后的原始视频格式为YUVJ420P。原因是来自某些视频设备的H265视频流,编码前的数据是YUVJ420P,并非全部都是YUV420P。如果将此原始YUVJ420P直接发送到NVIDIA硬件编码器,则将直接导致程序崩溃。经过调查,发现NVIDIA不支持YUVJ420P的直接编码,如下所示。如图所示,仅YUV420P或YUV444P支持H264编码。
要解决此问题,只能使用ffmpeg的sws_scale将YUVJ420P转换为YUV420P,然后将其发送到h264_nvenc硬件编码器进行编码。
#if ISCHANGEFRAME
if (frame->format == AV_PIX_FMT_YUVJ420P) {
sws_scale(convert_ctx,frame->data, frame->linesize, 0, Height, frameYUV->data, frameYUV->linesize);
frameYUV->format = AV_PIX_FMT_YUV420P;
frameYUV->width = Width;
frameYUV->height = Height;
return frameYUV;
}
#endif
4、补充
1、 libx264可以直接用264编码YUVJ420P,而无需进行格式转换。
2、 YUVJ420P和YUV420P简介
YUVJ420P的字面意思是“使用JPEG颜色范围的YUV420P,并且像素使用的颜色的值范围已更改。
YUV420p的像素颜色范围是[16,235],16表示黑色,235表示白色
YUVJ420P的像素颜色范围是[0,255]。 0表示黑色,255表示白色
FFmpeg的定义和解释:
AV_PIX_FMT_YUV420P, ///< planar="" yuv="" 4:2:0,="" 12bpp,="" (1="" cr="" &="" cb="" sample="" per="" 2x2="" y="" samples)="" av_pix_fmt_yuvj420p,="">< planar="" yuv="" 4:2:0,="" 12bpp,="" full="" scale="" (jpeg),="" deprecated="" in="" favor="" of="" av_pix_fmt_yuv420p="" and="" setting="" color_range="">
下载
1、测试视频
2、 FFmpeg API H265转码H264源代码
参考
1、
2、 ffmpeg nvidia硬件加速解决方案(参考博客)
3、 ffmpeg一些有关硬解码和软解码的参考
4、 YUV420P和YUVJ420P简介
本文来自本站,转载请注明本文网址:
http://www.pc-fly.com/a/shenmilingyu/article-372913-1.html
……