【视频开发】【CUDA开发】英伟达CUVID硬解,并通过FFmpeg读取文件
虽然FFmpeg本身有cuvid硬解,但是找不到什么好的资料,英伟达的SDK比较容易懂,参考FFmpeg源码,将NVIDIA VIDEO CODEC SDK的数据获取改为FFmpeg获取,弥补原生SDK不能以流作为数据源的不足。所用SDK版本为Video_Codec_SDK_7.1.9,英伟达官网可下载。
1.修改数据源
首先是FFmpeg的一些常规的初始化

bool VideoSource::init(const std::string sFileName, FrameQueue *pFrameQueue)
{
assert(0 != pFrameQueue);
oSourceData_.hVideoParser = 0;
oSourceData_.pFrameQueue = pFrameQueue; int i;
AVCodec *pCodec; av_register_all();
avformat_network_init();
pFormatCtx = avformat_alloc_context(); if (avformat_open_input(&pFormatCtx, sFileName.c_str(), NULL, NULL) != 0){
printf("Couldn't open input stream.\n");
return false;
}
if (avformat_find_stream_info(pFormatCtx, NULL)<0){
printf("Couldn't find stream information.\n");
return false;
}
videoindex = -1;
for (i = 0; i<pFormatCtx->nb_streams; i++)
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
videoindex = i;
break;
} if (videoindex == -1){
printf("Didn't find a video stream.\n");
return false;
} pCodecCtx = pFormatCtx->streams[videoindex]->codec; pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (pCodec == NULL){
printf("Codec not found.\n");
return false;
} //Output Info-----------------------------
printf("--------------- File Information ----------------\n");
av_dump_format(pFormatCtx, 0, sFileName.c_str(), 0);
printf("-------------------------------------------------\n"); memset(&g_stFormat, 0, sizeof(CUVIDEOFORMAT)); switch (pCodecCtx->codec_id) {
case AV_CODEC_ID_H263:
g_stFormat.codec = cudaVideoCodec_MPEG4;
break; case AV_CODEC_ID_H264:
g_stFormat.codec = cudaVideoCodec_H264;
break; case AV_CODEC_ID_HEVC:
g_stFormat.codec = cudaVideoCodec_HEVC;
break; case AV_CODEC_ID_MJPEG:
g_stFormat.codec = cudaVideoCodec_JPEG;
break; case AV_CODEC_ID_MPEG1VIDEO:
g_stFormat.codec = cudaVideoCodec_MPEG1;
break; case AV_CODEC_ID_MPEG2VIDEO:
g_stFormat.codec = cudaVideoCodec_MPEG2;
break; case AV_CODEC_ID_MPEG4:
g_stFormat.codec = cudaVideoCodec_MPEG4;
break; case AV_CODEC_ID_VP8:
g_stFormat.codec = cudaVideoCodec_VP8;
break; case AV_CODEC_ID_VP9:
g_stFormat.codec = cudaVideoCodec_VP9;
break; case AV_CODEC_ID_VC1:
g_stFormat.codec = cudaVideoCodec_VC1;
break;
default:
return false;
} //这个地方的FFmoeg与cuvid的对应关系不是很确定,不过用这个参数似乎最靠谱
switch (pCodecCtx->sw_pix_fmt)
{
case AV_PIX_FMT_YUV420P:
g_stFormat.chroma_format = cudaVideoChromaFormat_420;
break;
case AV_PIX_FMT_YUV422P:
g_stFormat.chroma_format = cudaVideoChromaFormat_422;
break;
case AV_PIX_FMT_YUV444P:
g_stFormat.chroma_format = cudaVideoChromaFormat_444;
break;
default:
g_stFormat.chroma_format = cudaVideoChromaFormat_420;
break;
} //找了好久,总算是找到了FFmpeg中标识场格式和帧格式的标识位
//场格式是隔行扫描的,需要做去隔行处理
switch (pCodecCtx->field_order)
{
case AV_FIELD_PROGRESSIVE:
case AV_FIELD_UNKNOWN:
g_stFormat.progressive_sequence = true;
break;
default:
g_stFormat.progressive_sequence = false;
break;
} pCodecCtx->thread_safe_callbacks = 1; g_stFormat.coded_width = pCodecCtx->coded_width;
g_stFormat.coded_height = pCodecCtx->coded_height; g_stFormat.display_area.right = pCodecCtx->width;
g_stFormat.display_area.left = 0;
g_stFormat.display_area.bottom = pCodecCtx->height;
g_stFormat.display_area.top = 0; if (pCodecCtx->codec_id == AV_CODEC_ID_H264 || pCodecCtx->codec_id == AV_CODEC_ID_HEVC) {
if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
else
h264bsfc = av_bitstream_filter_init("hevc_mp4toannexb");
} return true;
}

这里面非常重要的一段代码是
if (pCodecCtx->codec_id == AV_CODEC_ID_H264 || pCodecCtx->codec_id == AV_CODEC_ID_HEVC) {
if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
h264bsfc = av_bitstream_filter_init("h264_mp4toannexb");
else
h264bsfc = av_bitstream_filter_init("hevc_mp4toannexb");
}
网上有许多代码和伪代码都说实现了把数据源修改为FFmpeg,但我在尝试的时候发现cuvidCreateVideoParser创建的Parser的回调函数都没有调用。经过一番折腾,综合英伟达网站、stackoverflow和FFmpeg源码,才发现对H264数据要做一个处理才能把AVPacket有效的转为CUVIDSOURCEDATAPACKET。其中h264bsfc的定义为AVBitStreamFilterContext* h264bsfc = NULL;
2.AVPacket转CUVIDSOURCEDATAPACKET,并交给cuvidParseVideoData

void VideoSource::play_thread(LPVOID lpParam)
{
AVPacket *avpkt;
avpkt = (AVPacket *)av_malloc(sizeof(AVPacket));
CUVIDSOURCEDATAPACKET cupkt;
int iPkt = 0;
CUresult oResult;
while (av_read_frame(pFormatCtx, avpkt) >= 0){
if (bThreadExit){
break;
}
bStarted = true;
if (avpkt->stream_index == videoindex){ cuCtxPushCurrent(g_oContext); if (avpkt && avpkt->size) {
if (h264bsfc)
{
av_bitstream_filter_filter(h264bsfc, pFormatCtx->streams[videoindex]->codec, NULL, &avpkt->data, &avpkt->size, avpkt->data, avpkt->size, 0);
} cupkt.payload_size = (unsigned long)avpkt->size;
cupkt.payload = (const unsigned char*)avpkt->data; if (avpkt->pts != AV_NOPTS_VALUE) {
cupkt.flags = CUVID_PKT_TIMESTAMP;
if (pCodecCtx->pkt_timebase.num && pCodecCtx->pkt_timebase.den){
AVRational tb;
tb.num = 1;
tb.den = AV_TIME_BASE;
cupkt.timestamp = av_rescale_q(avpkt->pts, pCodecCtx->pkt_timebase, tb);
}
else
cupkt.timestamp = avpkt->pts;
}
}
else {
cupkt.flags = CUVID_PKT_ENDOFSTREAM;
} oResult = cuvidParseVideoData(oSourceData_.hVideoParser, &cupkt);
if ((cupkt.flags & CUVID_PKT_ENDOFSTREAM) || (oResult != CUDA_SUCCESS)){
break;
}
iPkt++;
//printf("Succeed to read avpkt %d !\n", iPkt);
checkCudaErrors(cuCtxPopCurrent(NULL));
}
av_free_packet(avpkt);
} oSourceData_.pFrameQueue->endDecode();
bStarted = false;
}

这里FFmpeg读取数据包后,对H264和HEVC格式,有一个重要的处理,就是前面提到的,
if (h264bsfc)
{
av_bitstream_filter_filter(h264bsfc, pFormatCtx->streams[videoindex]->codec, NULL, &avpkt->data, &avpkt->size, avpkt->data, avpkt->size, 0);
}
这个处理的含义见雷霄华的博客http://blog.csdn.net/leixiaohua1020/article/details/39767055。
这样,通过FFmpeg,CUVID就可以对流进行处理了。个人尝试过读取本地文件和rtsp流。FFmpeg读取rtsp流的方式竟然只需要把文件改为rtsp流的地址就可以,以前没做过流式的,我还以为会很复杂的。
3.一点数据

这是在GTX 1080上把解码进程(没做显示)开了20路解码得到的数据。20路1920X1080解码还能到平局37fps,这显卡也是6得不行。
【视频开发】【CUDA开发】英伟达CUVID硬解,并通过FFmpeg读取文件的更多相关文章
- 英伟达CUVID硬解,并通过FFmpeg读取文件
虽然FFmpeg本身有cuvid硬解,但是找不到什么好的资料,英伟达的SDK比较容易懂,参考FFmpeg源码,将NVIDIA VIDEO CODEC SDK的数据获取改为FFmpeg获取,弥补原生SD ...
- CUDA学习笔记4:CUDA(英伟达显卡统一计算架构)代码运行时间测试
CUDA内核运行时间的测量函数 cudaEvent_t start1; cudaEventCreate(&start1); cudaEvent_t stop1; cudaEventCreate ...
- CUDA学习笔记2:CUDA(英伟达显卡统一计算架构)与已有的VS项目结合
一.步骤 1.先新建一个简单的控制台应用程序,项目名称为Mytest,如下图所示: 2.在项目中添加一个名为Test.cu文件,如下图所示: 3.在解决方案资源管理器中选择该项目并点击右键,在弹出的菜 ...
- 【并行计算与CUDA开发】英伟达硬件加速编解码
硬件加速 并行计算 OpenCL OpenCL API VS SDK 英伟达硬件编解码方案 基于 OpenCL 的 API 自己写一个编解码器 使用 SDK 中的编解码接口 使用编码器对于 OpenC ...
- 【并行计算-CUDA开发】英伟达硬件解码器分析
这篇文章主要分析 NVCUVID 提供的解码器,里面提到的所有的源文件都可以在英伟达的 nvenc_sdk 中找到. 解码器的代码分析 SDK 中的 sample 文件夹下的 NvTranscoder ...
- 【并行计算-CUDA开发】从熟悉到精通 英伟达显卡选购指南
举报 说到显卡,就不免令人想到英伟达和AMD两家面向个人消费级和企业级最大的显示芯片生产企业,英伟达和AMD,今天小编为大家简单的介绍一下英伟达的显卡选购方面的攻略,为一些想要购买显卡的用户提供一些参 ...
- 英伟达GPU 嵌入式开发平台
英伟达GPU 嵌入式开发平台 1. JETSON TX1 开发者组件 JETSON TX1 开发者组件是视觉计算的全功能 开发平台,旨在让您能够快速地安装和运行. 该组件带有 Lin ...
- 第一篇:CUDA 6.0 安装及配置( WIN7 64位 / 英伟达G卡 / VS2010 )
前言 本文讲解如何在VS 2010开发平台中搭建CUDA开发环境. 当前配置: 系统:WIN7 64位 开发平台:VS 2010 显卡:英伟达G卡 CUDA版本:6.0 若配置不同,请谨慎参考本文. ...
- 玩深度学习选哪块英伟达 GPU?有性价比排名还不够!
本文來源地址:https://www.leiphone.com/news/201705/uo3MgYrFxgdyTRGR.html 与“传统” AI 算法相比,深度学习(DL)的计算性能要求,可以说完 ...
随机推荐
- 浏览器 Web Storage - sessionStorage & localStorage
storage事件 当储存的数据发生变化时,会触发storage事件.我们可以指定这个事件的回调函数. window.addEventListener("storage",onSt ...
- 决策树——C4.5
-- coding: utf-8 -- """ Created on Thu Aug 2 17:09:34 2018 决策树ID3,C4.5的实现 @author: we ...
- L1141(bfs思想)
一,看 1,整个方格图其实可以看做是一些不连通的图. 当然图内部必然是联通的. 2,遍历的技巧没什么. 方格图入队的技巧..额,是这样的 int gtid(int x,int y) { return ...
- 简易配置中心Confd入手
改成动态更新配置文件,如下每五秒重新生成配置文件 confd与etcd的使用 Add keys This guide assumes you have a working etcd, or consu ...
- 题目一:编写一个类Computer,类中含有一个求n的阶乘的方法
作业:编写一个类Computer,类中含有一个求n的阶乘的方法.将该类打包,并在另一包中的Java文件App.java中引入包,在主类中定义Computer类的对象,调用求n的阶乘的方法(n值由参数决 ...
- git的搭建和使用
目录: 1.git与github介绍2.下载安装Git-20-64-bit.exe3.Git常用命令 git与github介绍 Git是什么 Git是一个开源的[分布式][版本控制系统],用于敏捷高效 ...
- WinDbg命令窗口的使用
调试器命令窗口是windbg中的主要调试信息窗口.可以在此窗口中输入调试程序命令并查看命令输出.Windbg的命令窗口是我们进行调试时,主要打交道的窗口.界面如下 对于windbg,“调试器命令窗口” ...
- 权限管理(chown、chgrp、umask)
对于文件或目录的权限的修改,只能管理员和文件的所有者拥有此权限,但是对于文件或目录的的所有者的更改,只有管理员拥有此权限(虽然普通用户创建的文件或目录,用户也不能修改文件或目录的所有者). 1.cho ...
- Omnibus-ctl: What is it and what can it do for you?
转自:https://blog.chef.io/2015/05/26/omnibus-ctl-what-is-it-and-what-can-it-do-for-you/ Are you buildi ...
- 干货 | column generation求解VRPTW问题 java代码分享
00 前言 经过小编不断的努力,关于column generation求解VRPTW的代码终于新鲜出炉啦. 01 运行说明 关于这部分的代码,这里提供两个版本. 第一个版本GitHub一个叫Semin ...