基于FFmpeg的Dxva2硬解码及Direct3D显示(四)
初始化硬解码上下文
创建解码数据缓冲区
这一步为了得到 LPDIRECT3DSURFACE9* 实例 m_pSurface,就是之前说过的那个数组。
// m_surfaceNums 为希望创建的缓冲区个数,单路视频一个就够了,太多可能显存不够用
m_pSurface = (LPDIRECT3DSURFACE9*)av_mallocz(m_surfaceNums * sizeof(LPDIRECT3DSURFACE9));
if (!m_pSurface)
{
return FALSE;
}
// 字节对齐
int surfaceAlignment = 0;
if (pCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
{
surfaceAlignment = 32;
}
else if (pCodecCtx->codec_id == AV_CODEC_ID_HEVC)
{
surfaceAlignment = 128;
}
else
{
surfaceAlignment = 16;
}
// 创建缓冲区
HRESULT hr = m_pDecoderService->CreateSurface(
FFALIGN(pCodecCtx->coded_width, surfaceAlignment), // 缓冲区宽
FFALIGN(pCodecCtx->coded_height, surfaceAlignment), // 缓冲区高
m_surfaceNums, // 缓冲区个数,这里可以设置为0,CreateVideoDecoder里面会重新设置个数
m_renderFormat, // 缓冲区格式
D3DPOOL_DEFAULT, // 缓冲区位置,D3DPOOL_DEFAULT--显存
0, // 资源如何被使用
DXVA2_VideoDecoderRenderTarget, // 缓冲区为视频解码器渲染目标
m_pSurface, // 缓冲区数组指针
NULL); // 保留字
if (FAILED(hr))
{
return FALSE;
}
创建IDirectXVideoDecoder视频解码器
- 获取当前GPU支持的解码能力等级和渲染格式
BOOL GetDxva2FormatAndGuid(AVCodecContext *pCodecCtx, GUID & guid, D3DFORMAT & fmt)
{
// 获取当前设备支持的解码标准等级标识列表
GUID *guidList = NULL;
unsigned guidCount = 0;
HRESULT hr = m_pDecoderService->GetDecoderDeviceGuids(&guidCount, &guidList);
if (FAILED(hr))
{
VX_LOG_ERROR("Get hardware acclerate device guids failed!");
return FALSE;
}
for (int i = 0; ; i++)
{
if (NULL == guid2AVCodecID[i].guidID)
{
// 查到最后一个了直接退出循环
break;
}
const Guid2CodecID *mode = &guid2AVCodecID[i];
if (mode->codecID == pCodecCtx->codec_id)
{
for (uint32_t j = 0; j < guidCount; j++)
{
if (IsEqualGUID(*mode->guidID, guidList[j]))
{
// 获取当前解码标准下渲染器目标格式数组
D3DFORMAT *targetList = NULL;
UINT targetCount = 0;
hr = m_pDecoderService->GetDecoderRenderTargets(*mode->guidID, &targetCount, &targetList);
if (FAILED(hr))
{
VX_LOG_ERROR("Get support render format failed!");
return FALSE;
}
for (uint32_t j = 0; j < targetCount; j++)
{
if (targetList[j] == MKTAG('N', 'V', '1', '2'))
{
fmt = targetList[j];
guid = *mode->guidID;
break;
}
}
// 释放内存资源
CoTaskMemFree(targetList);
}
}
}
}
CoTaskMemFree(guidList);
if (D3DFMT_UNKNOWN == fmt || GUID_NULL == guid)
{
return FALSE;
}
else
{
return TRUE;
}
}
- 获取当前解码等级下的配置信息
void CDxva2Decode::GetDecoderCfg(AVCodecContext *pCodecCtx, const GUID *pGuid,
const DXVA2_VideoDesc *pDesc, DXVA2_ConfigPictureDecode *pCfg)
{
unsigned cfgCount = 0, bestScore = 0;
DXVA2_ConfigPictureDecode *cfgList = NULL;
HRESULT hr = m_pDecoderService->GetDecoderConfigurations(*pGuid, pDesc, NULL, &cfgCount, &cfgList);
for (uint32_t i = 0; i < cfgCount; i++)
{
DXVA2_ConfigPictureDecode cfg = cfgList[i];
unsigned score;
if (cfg.ConfigBitstreamRaw == 1)
{
score = 1;
}
else if (pCodecCtx->codec_id == AV_CODEC_ID_H264 && cfg.ConfigBitstreamRaw == 2)
{
score = 2;
}
else
{
continue;
}
if (IsEqualGUID(cfg.guidConfigBitstreamEncryption, DXVA2_NoEncrypt))
{
score += 16;
}
if (score > bestScore)
{
bestScore = score;
*pCfg = cfg;
}
}
CoTaskMemFree(cfgList);
}
- 这一步为了得到 IDirectXVideoDecoder* 实例 m_pDxva2Decoder,亦即硬件解码器。
if (!GetDxva2FormatAndGuid(pCodecCtx, m_decoderGuid, m_renderFormat))
{
// 不支持DXVA2加速
VX_LOG_ERROR("Do not support Dxva2!");
return FALSE;
}
// 设置解码后的格式
DXVA2_VideoDesc desc = { 0 };
desc.SampleWidth = pCodecCtx->coded_width;
desc.SampleHeight = pCodecCtx->coded_height;
desc.Format = m_renderFormat;
// 获取支持的配置
GetDecoderCfg(pCodecCtx, &m_decoderGuid, &desc, &m_config);
// 创建解码器设备
HRESULT hr = m_pDecoderService->CreateVideoDecoder(m_decoderGuid, // 设备标识符
&desc, // 视频内容描述
&m_config, // 解码器配置
m_pSurface, // 渲染目标数组指针(解码后的数据写到这里)
m_surfaceNums, // 渲染目标数,必须大于0,
&m_pDxva2Decoder); // 解码器
if (FAILED(hr))
{
return FALSE;
}
设置硬解码上下文
// 这一步为了将解码缓冲区数组传给GetBufferCallBack回调函数
pCodecCtx->opaque = m_pSurface;
// 设置回调
pCodecCtx->get_buffer2 = GetBufferCallBack;
pCodecCtx->get_format = GetHwFormat;
// 单路视频启动多线程解码,理解是启用多个线程将待解码数据送往GPU,因为数据从内存到显存比较慢
pCodecCtx->thread_safe_callbacks = TRUE;
pCodecCtx->thread_count = 2;
// 为解码器上下文申请硬件加速内存
pCodecCtx->hwaccel_context = av_mallocz(sizeof(struct dxva_context));
if (!pCodecCtx->hwaccel_context)
{
return FALSE;
}
// 设置硬件加速上下文
struct dxva_context *dxva2Ctx = (dxva_context *)pCodecCtx->hwaccel_context;
dxva2Ctx->cfg = &m_config;
dxva2Ctx->decoder = m_pDecoder;
dxva2Ctx->surface = m_pSurface;
dxva2Ctx->surface_count = m_surfaceNums;
// 对老的intel GPU 的支持
if (IsEqualGUID(m_decoderGuid, DXVADDI_Intel_ModeH264_E))
{
dxva2Ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
}
解码回调函数
- 解码输出格式回调
static AVPixelFormat GetHwFormat(AVCodecContext * pCodecCtx, const AVPixelFormat * pPixFmt)
{
// 因为采用的是DXVA2,所以这里直接写死了
return AV_PIX_FMT_DXVA2_VLD;
}
解码数据回调
此时解码后的数据放在解码缓冲数组里面,这里数组大小为1。单路视频时 pFrame 地址为两个固定地址切换,这些应该都是FFmpeg内部实现的。这里理解的不清楚,希望大神可以指点。
// 个人理解就是将LPDIRECT3DSURFACE9转为(uint8_t *),同时得保证内存不会立即被释放
static int GetBufferCallBack(AVCodecContext * pCodecCtx, AVFrame * pFrame, int flags)
{
if (pFrame->format != AV_PIX_FMT_DXVA2_VLD)
{
return -1;
}
// 获取解码后的数据,出于安全性不可直接访问,这一步没有内存拷贝
LPDIRECT3DSURFACE9 surface = ((LPDIRECT3DSURFACE9*)(pCodecCtx->opaque))[0];
// 将LPDIRECT3DSURFACE9转为AVBuffer,内存地址不变,并返回AVBufferRef,并返回AVBufferRef供FFmpeg内部使用,这一步应该发生了内存拷贝
// 类似于智能指针,增加对surface的引用计数,当计数为0时FFmpeg会认为该帧数据丢弃掉。默认使用av_buffer_default_free释放,
pFrame->buf[0] = av_buffer_create((uint8_t*)surface, 0, nullptr, nullptr, AV_BUFFER_FLAG_READONLY);
if (!pFrame->buf[0])
{
return AVERROR(ENOMEM);
}
// 这一步拿到最终可以显示的数据,必须是data[3],此时surface应该是AVBuffer
pFrame->data[3] = (uint8_t *)surface;
return 0;
}
[参考链接]:(http://www.cnblogs.com/betterwgo/p/6125507.html)
基于FFmpeg的Dxva2硬解码及Direct3D显示(四)的更多相关文章
- 基于FFmpeg的Dxva2硬解码及Direct3D显示(一)
目录 前言 名词解释 代码实现逻辑 前言 关于视频软解码的资料网上比较多了,但是关于硬解可供参考的资料非常之有限,虽然总得来说软解和硬解的基本逻辑一样,但是实现细节上的差别还是比较多的.虽然目前功能已 ...
- 基于FFmpeg的Dxva2硬解码及Direct3D显示(五)
解码及显示 目录 解码及显示 解码 显示 资源清理 解码 循环读取视频帧 AVPacket packet = { 0 }; while (av_read_frame(m_pFmtCtx, &p ...
- 基于FFmpeg的Dxva2硬解码及Direct3D显示(三)
初始化Direct3D 目录 初始化Direct3D 创建Direct3D物理设备对象实例 创建Direct3D渲染设备实例 创建Direct3D视频解码服务 Direct3D渲染可以通过Surfac ...
- 基于FFmpeg的Dxva2硬解码及Direct3D显示(二)
解析视频源 目录 解析视频源 获取视频流 解析视频流 说明:这篇博文分为"获取视频流"和"解析视频流"两个部分,使用的是FFmpeg4.1的版本,与网上流传的低 ...
- 使用C#+FFmpeg+DirectX+dxva2硬件解码播放h264流
本文门槛较高,因此行文看起来会乱一些,如果你看到某处能会心一笑请马上联系我开始摆龙门阵 如果你跟随这篇文章实现了播放器,那你会得到一个高效率,低cpu占用(单路720p视频解码播放占用1%左右cpu) ...
- 【视频开发】 ffmpeg支持的硬解码接口
To enable DXVA2, use the --enable-dxva2 ffmpeg configure switch. To test decoding, use the following ...
- [原]ffmpeg编译android 硬解码支持库 libstagefright
最近花了一天时间将ffmpeg/tools/build_stagefright执行成功,主要是交叉编译所需要的各种动态库的支持没链接上,导致各种报错,基本上网络上问到的问题我都碰到了,特此记录下来. ...
- 【并行计算与CUDA开发】基于NVIDIA显卡的硬编解码的一点心得 (完结)
原文:基于NVIDIA显卡的硬编解码的一点心得 (完结) 1.硬解码软编码方法:大体流程,先用ffmpeg来读取视频文件的包,接着开启两个线程,一个用于硬解码,一个用于软编码,然后将读取的包传给解码器 ...
- 最简单的基于FFMPEG的封装格式转换器(无编解码)
本文介绍一个基于FFMPEG的封装格式转换器.所谓的封装格式转换,就是在AVI,FLV,MKV,MP4这些格式之间转换(相应.avi,.flv,.mkv,.mp4文件).须要注意的是,本程序并不进行视 ...
随机推荐
- python -re库
正则表达式的语法 正则表达式语法由字符和操作符构成 正则表达式的常用操作符: print("--正则表达式常用操作符--") mata="11356352135 abcd ...
- 如何修改或新增visual studio 的模板
在 visual studio 中添加模板,我这里是新增mvc.net的模板 vs2017在文件夹=>(举例说明,请替换为相应的安装目录) D:\Program Files (x86)\Micr ...
- rs232转rs485
rs232转rs485 rs232转rs485 ZLAN9223E是上海卓岚科技开发的一款先进的无源RS232转RS485转换器.具有如下优点: 支持最高达230400bps的波特率.高波特率下供电能 ...
- day22 Pyhton学习 re模块和正则表达式
正则表达式本身也和python没有什么关系,就是匹配字符串内容的一种规则. 官方定义:正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个" ...
- [论文阅读]阿里DIN深度兴趣网络之总体解读
[论文阅读]阿里DIN深度兴趣网络之总体解读 目录 [论文阅读]阿里DIN深度兴趣网络之总体解读 0x00 摘要 0x01 论文概要 1.1 概括 1.2 文章信息 1.3 核心观点 1.4 名词解释 ...
- CentOS7克隆多个虚拟机
VMware+centos7克隆虚拟机 步骤一:打开虚拟机,右键选中已经配置好的虚拟机,选择manage下面的clone选项.这里有一个需要注意的地方,就是虚拟机在启动或者挂起的状态下是不能clone ...
- js实现自定义弹窗
众所周知,浏览器自带的原生弹窗很不美观,而且功能比较单一,绝大部分时候我们都会按照设计图自定义弹窗或者直接使用注入layer的弹窗等等.前段时间在慕课网上看到了一个自定义弹窗的实现,自己顺便就学习尝试 ...
- 浏览器缓存引起的bug总结
缓存原理 浏览器缓存分为强缓存和协商缓存 先检查是否过期,没有过期直接使用本地缓存.如果过期,查看是否使用协商缓存 协商缓存流程: 后端返回headers: ETag: W/"1e3-175 ...
- Vue实例中封装api接口的思路 在页面中用async,await调用方法请求
一般我们写小型的项目是用不到封装axios实例 但是当我们写大型项目时 接口有时候多到有上百个接口,那我们在请求一次调用一次接口,接口上好多都是重复的,这个时候我们就可以封装axios实例,既节省了 ...
- RocketMQ主从搭建
RocketMQ可分为以下几种模式: 单点模式 主从模式 双从模式 双主双从模式,多主多从模式 搭建主从模式 tar -zxvf rocketmq-4.6.0.tar.gz -C /usr/local ...