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

FFmpeg硬件编解码-C++

1、FFmpeg支持多种硬件加速类型,用于编解码视频,以提升性能和效率。以下是FFmpeg支持的主要硬件加速类型:

NVIDIA NVENC/NVDEC:利用NVIDIA显卡进行视频编码(NVENC)和解码(NVDEC)。
QSV:利用Intel处理器中的集成图形进行视频加速。
AMD VCE  VDA:利用AMD显卡进行视频编码和解码。
VAAPI:适用于Intel和AMD硬件,通过通用的API接口进行硬件加速。
VDPAU :主要用于NVIDIA显卡的硬件解码加速。
DXVA2 :适用于Windows平台,利用DirectX进行视频加速。
OpenMAX IL :用于移动设备和嵌入式系统的视频加速。
Vulkan:一种跨平台的图形和计算API,也可以用于视频加速

int CH264CodecConvert::OpenH264Encoder()
{
	const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264);
	//codec = avcodec_find_encoder_by_name("h264_nvenc");
	if (!codec)
	{
		fileLogger->error("查找H264编码器失败");
		return -1;
	}

	m_outAvCodecContex = avcodec_alloc_context3(codec);
	if (!m_outAvCodecContex)
	{
		fileLogger->error("H264编码器分配上下文失败");
		return -2;
	}
// 	m_outAvCodecContex->thread_count = 4;
// 	m_outAvCodecContex->thread_type = FF_THREAD_FRAME;
	m_outAvCodecContex->width = 1920;
	m_outAvCodecContex->height = 1080;
	m_outAvCodecContex->time_base = { 1, 25 };
	//多少帧一个I帧
	//m_outAvCodecContex->gop_size = 25;
	//去掉B帧
	//m_outAvCodecContex->max_b_frames = 0;
	//m_outAvCodecContex->pix_fmt = AV_PIX_FMT_YUV420P;//AV_PIX_FMT_CUDA;
	//从GPU出来的帧没法转成yuv420,因为网上也有说法是GPU出来只出NV12
	//解码 > cuda > resize > nv12 > 编码 > yuv420
	//修改编码encctx->pix_fmt = AV_PIX_FMT_NV12,
	//顺便encctx->sw_pix_fmt = AV_PIX_FMT_YUV420P(个人认为这个是转换后的格式)
	av_opt_set(m_outAvCodecContex->priv_data, "preset", "ultrafast", 0);
//av_opt_set(m_outAvCodecContex->priv_data, "tune", "fastdecode", 0);
//av_opt_set(m_outAvCodecContex->priv_data, "profile", "high", 0);
	m_outAvCodecContex->pix_fmt = AV_PIX_FMT_NV12;
	m_outAvCodecContex->sw_pix_fmt = AV_PIX_FMT_YUV420P;
	if (avcodec_open2(m_outAvCodecContex, codec, NULL) < 0)
	{
		fileLogger->error("打开H264编码器失败");
		return -3;
	}
	m_bIsOpenEncoder = true;
	return 0;
}

enum AVPixelFormat CH264CodecConvert::get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) {
	const enum AVPixelFormat *p;

	for (p = pix_fmts; *p != -1; p++) {
		if (*p == ePixFmt_)
			return *p;
	}

	fprintf(stderr, "Failed to get HW surface format.\n");
	return AV_PIX_FMT_NONE;
}

int CH264CodecConvert::hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type) {
	int err = 0;

	if ((err = av_hwdevice_ctx_create(&pDeviceCtx, type,
		nullptr, nullptr, 0)) < 0) {
		fprintf(stderr, "Failed to create specified HW device.\n");
		return err;
	}
	ctx->hw_device_ctx = av_buffer_ref(pDeviceCtx);

	return err;
}

int CH264CodecConvert::InitHardDecode(const std::string& HWType)
{
	pCodec_ = avcodec_find_decoder(AV_CODEC_ID_HEVC);
	if (!pCodec_) {
		std::cout << "avcodec_find_decoder Failed" << std::endl;
		return -1;
	}

	enum AVHWDeviceType type;
	type = av_hwdevice_find_type_by_name(HWType.c_str());
	if (type == AV_HWDEVICE_TYPE_NONE)
	{
		std::cout << "UnKnown HW Device Type" << std::endl;
		while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE)
		{
			std::cout << type << std::endl;
		}
		return -1;
	}

	for (int i = 0; ; i++)
	{
		const AVCodecHWConfig *config = avcodec_get_hw_config(pCodec_, i);
		if (!config)
		{
			std::cout << "avcodec_get_hw_config Failed" << i << std::endl;
			return -1;
		}
		if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&config->device_type == type)
		{
			ePixFmt_ = config->pix_fmt;
			break;
		}
	}
	
	m_inAvCodecContex = avcodec_alloc_context3(pCodec_);
	if (!m_inAvCodecContex) 
	{
		std::cout << "avcodec_alloc_context3 Failed" << std::endl;
		return -1;
	}

	m_inAvCodecContex->thread_count = 4;
	m_inAvCodecContex->thread_type = FF_THREAD_FRAME;
	m_inAvCodecContex->get_format = get_hw_format;
	if (hw_decoder_init(m_inAvCodecContex, type) < 0) 
	{
		return -1;
	}

	if (avcodec_open2(m_inAvCodecContex, pCodec_, nullptr) < 0)
	{
		std::cout << "avcodec_open2 Failed" << std::endl;
		return -1;
	}

	bHWDecode_ = true;
	return 0;
}

void CH264CodecConvert::TransferCodec(const std::uint8_t* pszData, std::int32_t nDataLen)
{
	if (!m_bIsOpenDecoder)
	{
		//cuda dxva2 d3d11va d3d12va
		InitHardDecode("d3d12va");
		m_bIsOpenDecoder = true;
	}

	AVFrame* frame = NULL;
	if (!(frame = av_frame_alloc()))
		return;

	AVPacket inpkt;
	av_init_packet(&inpkt);
	inpkt.data = (std::uint8_t*)pszData;
	inpkt.size = nDataLen;

	// 硬解码
	AVFrame* tmpFrame = nullptr, *swFrame = nullptr;
	int nRet = avcodec_send_packet(m_inAvCodecContex, &inpkt); // 将AVPacket发送至解码器中
	if (!(tmpFrame = av_frame_alloc()) || !(swFrame = av_frame_alloc()))
	{
		av_frame_free(&frame);
		return;
	}

	while (avcodec_receive_frame(m_inAvCodecContex, tmpFrame) >= 0)
	{
		if (!m_bIsOpenEncoder)
			OpenH264Encoder();
		if (!m_bIsOpenEncoder)
			continue;

		// 判断解码帧格式
		if (tmpFrame->format == ePixFmt_)
		{
			/* 将GPU中的数据移交到CPU中 */
			if (av_hwframe_transfer_data(swFrame, tmpFrame, 0) < 0)
			{
				std::cout << "Error transferring the data to system memory" << std::endl;
				av_frame_free(&tmpFrame);
				av_frame_free(&swFrame);
				av_frame_free(&frame);
				return;
			}
			frame = swFrame;
		}
		else
		{
			frame = tmpFrame;
		}

		AVPacket outpkt;
		av_init_packet(&outpkt);
		frame->pts = m_nPTS++;
		avcodec_send_frame(m_outAvCodecContex, frame);

		while (avcodec_receive_packet(m_outAvCodecContex, &outpkt) == 0)
		{
			std::shared_ptr<std::string> packet = std::make_shared<std::string>();
			packet->append((char*)outpkt.data, outpkt.size);
			{
				std::unique_lock<std::mutex> lock(m_mutexQueuePacket);
				m_queuePacket.push(packet);
			}
			av_packet_unref(&outpkt);
		}
	}

	av_packet_unref(&inpkt);
	av_frame_free(&tmpFrame);
	av_frame_free(&swFrame);
}

问题记录:

1.

根据文章https://blog.csdn.net/qq_23282479/article/details/118993650

改用了GPU硬解码,出现了这个问题。软解是没问题的

原本是 解码>cuda>yuv420>编码>yuv420

从GPU出来的帧没法转成yuv420,因为网上也有说法是GPU出来只出NV12

解码>cuda>nv12>编码>yuv420

	m_outAvCodecContex->pix_fmt = AV_PIX_FMT_NV12;
	m_outAvCodecContex->sw_pix_fmt = AV_PIX_FMT_YUV420P;

这样就正常显示了

2.视频出现闪频

m_inAvCodecContex->thread_count = 4;
m_inAvCodecContex->thread_type = FF_THREAD_FRAME;

开启了多线程解码后解决了这个问题

相关文章:

  • cursor使用经验分享(java后端服务开发向)
  • 3D建模--犀牛Rhino for Mac
  • 《Java基础 聊天窗口案例:剖析 GUI、文件 I/O 等关键技术知识》
  • SpringMVC控制器定义:@Controller注解详解
  • FFmpeg入门:最简单的音视频播放器
  • ubuntu挂载固态硬盘
  • 《Canvas修仙传·第四重天元婴境(下集)》 ——虚空幻境与三千法则的终极融合
  • Tampermonkey篡改猴官网,油猴脚本插件电脑版入口(含教程)
  • Fiji图像处理
  • 润开鸿重磅首发基于“RISC-V+OpenHarmony+星闪”的“鸿锐”AI开发平台
  • deepseek使用记录18——文化基因的物质标枪
  • 无人机应用探索:玻纤增强复合材料的疲劳性能研究
  • 自然语言处理:文本分类
  • 后验概率估计
  • FastGPT 源码解析:混合检索与存储方案
  • 冯诺依曼体系结构
  • 学习记录-用例设计编写
  • springbootWeb入门--创建springbootweb项目
  • 低代码平台的后端架构设计与核心技术解析
  • 芯科科技通过全新并发多协议SoC重新定义智能家居连接
  • 专业的网站建设公/泉州seo报价
  • 网站改成响应式/网络营销推广方法十种
  • 网站服务器租用需要注意的点/买链接官网
  • 门户网站建设哪家好/理发美发培训学校
  • 网站建设工作情况汇报/福州短视频seo平台
  • 公安部/宁波seo营销平台