NIVIDIA 硬解码学习4

做项目的时候遇到一个问题. 就是起了多个解码器,但是解码器各自拥有其上下文,则获得各自解码器解码得到的GPU数据.无法共同操作.

可以参考下面这个程序解决这个问题.

AppDecMultiInput

  • This sample application demonstrates shows how to decode multiple raw video files andpost-process them with CUDA kernels on different CUDA streams.

程序功能介绍:

  • 读入一个文件.
  • 起了多个解码器(程序中为4个)
  • 对这个文件各自进行解码,然后每个解码器在这个解码后数据上做了一个波纹的特效.
  • 最后把解码的数据Merge到一起.写到一个输出文件

输入:

输出:

代码介绍

  • 主函数
int main(int argc, char *argv[])
{char szInFilePath[256] = "", szOutFilePath[256] = "out.nv12";int iGpu = 0;std::vector<std::exception_ptr> vExceptionPtrs;try{// 从命令行读入文件ParseCommandLine(argc, argv, szInFilePath, szOutFilePath, iGpu);CheckInputFile(szInFilePath);// 初始化GPU环境ck(cuInit(0));int nGpu = 0;ck(cuDeviceGetCount(&nGpu));if (iGpu < 0 || iGpu >= nGpu){std::ostringstream err;err << "GPU ordinal out of range. Should be within [" << 0 << ", " << nGpu - 1 << "]" << std::endl;throw std::invalid_argument(err.str());}CUdevice cuDevice = 0;ck(cuDeviceGet(&cuDevice, iGpu));char szDeviceName[80];ck(cuDeviceGetName(szDeviceName, sizeof(szDeviceName), cuDevice));std::cout << "GPU in use: " << szDeviceName << std::endl;// 创建一个CUDA上下文,后面解码器共享这个上下文!!!CUcontext cuContext = NULL;ck(cuCtxCreate(&cuContext, 0, cuDevice));// 解析,用来初始化解码器使用FFmpegDemuxer demuxer(szInFilePath);int nWidth = demuxer.GetWidth(), nHeight = demuxer.GetHeight(), nByte = nWidth * nHeight * 3 / 2;// Number of decodersconst int n = 4; // 解码器数目// Every decoder has its own round queueuint8_t *aapFrameBuffer[n][8]; // 用来存储最后解码出来的数据的地址// Queue capacityconst int nFrameBuffer = sizeof(aapFrameBuffer[0]) / sizeof(aapFrameBuffer[0][0]);int iEnd = nFrameBuffer;bool abStop[n] = {};int aiHead[n] = {};std::vector <NvThread> vThreads;std::vector <std::unique_ptr<NvDecoder>> vDecoders;// Coordinate of the ripple center for each decoderint axCenter[] = { nWidth / 4, nWidth / 4 * 3, nWidth / 4, nWidth / 4 * 3 }; // 每个波纹的x坐标中心int ayCenter[] = { nHeight / 4, nHeight / 4, nHeight / 4 * 3, nHeight / 4 * 3 }; // 每个波纹的y坐标中心cudaStream_t aStream[n]; vExceptionPtrs.resize(n);for (int i = 0; i < n; i++){ck(cudaStreamCreate(&aStream[i]));// 创建4个流std::unique_ptr<NvDecoder> dec(new NvDecoder(cuContext, demuxer.GetWidth(), demuxer.GetHeight(), true, FFmpeg2NvCodecId(demuxer.GetVideoCodec()))); // 创建解码器vDecoders.push_back(std::move(dec)); // 加入队列vThreads.push_back(NvThread(std::thread(DecProc, vDecoders[i].get(), szInFilePath, nWidth, nHeight, aapFrameBuffer[i],nFrameBuffer, &iEnd, aiHead + i, abStop + i, aStream[i], axCenter[i], ayCenter[i], std::ref(vExceptionPtrs[i]))));// 启动线程进行解码} std::unique_ptr<uint8_t[]> pImage(new uint8_t[nByte]);// 最后存储到CPU内存用于写文件uint8_t* dpImage = nullptr;// 分配GPU内存,最后Merge到一起ck(cudaMalloc(&dpImage, nByte));std::ofstream fpOut(szOutFilePath, std::ios::out | std::ios::binary);if (!fpOut){std::ostringstream err;err << "Unable to open output file: " << szOutFilePath << std::endl;throw std::invalid_argument(err.str());}int nFrame = 0;for (int i = 0;; i++){// For each decoded frame #i// iHead is used for ensuring all decoders have made progressint iHead = INT_MAX;for (int j = 0; j < n; j++){while (!abStop[j] && aiHead[j] <= i){// Decoder #j hasn't decoded frame #istd::this_thread::sleep_for(std::chrono::milliseconds(1));}iHead = (std::min)(iHead, aiHead[j]);}if (iHead <= i){// Some decoder stopsnFrame = i;break;}std::cout << "Merge frames at #" << i << "\r";// 第i帧uint8_t *apNv12[] = { aapFrameBuffer[0][i % nFrameBuffer], aapFrameBuffer[1][i % nFrameBuffer], aapFrameBuffer[2][i % nFrameBuffer], aapFrameBuffer[3][i % nFrameBuffer] };// Merge all frames into dpImageLaunchMerge(0, dpImage, apNv12, n, nWidth, nHeight);ck(cudaMemcpy(pImage.get(), dpImage, nByte, cudaMemcpyDeviceToHost));fpOut.write(reinterpret_cast<char*>(pImage.get()), nByte);for (int j = 0; j < n; j++){vDecoders[j]->UnlockFrame(&aapFrameBuffer[j][i % nFrameBuffer], 1);}iEnd++;}fpOut.close();ck(cudaFree(dpImage));for (int i = 0; i < n; i++){if (vExceptionPtrs[i]){std::rethrow_exception(vExceptionPtrs[i]);}}ck(cudaProfilerStop());if (nFrame){std::cout << "Merged video saved in " << szOutFilePath << ". A total of " << nFrame << " frames were decoded." << std::endl;return 0;}else{std::cout << "Warning: no video frame decoded. Please don't use container formats (such as mp4/avi/webm) as the input, but use raw elementary stream file instead." << std::endl;return 1;}}catch (const std::exception &ex){std::cout << ex.what();exit(1);}return 0;
}
  • 创建解码器
NvDecoder::NvDecoder(CUcontext cuContext, int nWidth, int nHeight, bool bUseDeviceFrame, cudaVideoCodec eCodec, std::mutex *pMutex,bool bLowLatency, bool bDeviceFramePitched, const Rect *pCropRect, const Dim *pResizeDim, int maxWidth, int maxHeight) :m_cuContext(cuContext), m_bUseDeviceFrame(bUseDeviceFrame), m_eCodec(eCodec), m_pMutex(pMutex), m_bDeviceFramePitched(bDeviceFramePitched),m_nMaxWidth (maxWidth), m_nMaxHeight(maxHeight)
{if (pCropRect) m_cropRect = *pCropRect;if (pResizeDim) m_resizeDim = *pResizeDim;NVDEC_API_CALL(cuvidCtxLockCreate(&m_ctxLock, cuContext));// CUVIDPARSERPARAMS:该接口用来创建VideoParser// 主要参数是设置三个回调函数 实现对解析出来的数据的处理CUVIDPARSERPARAMS videoParserParameters = {};videoParserParameters.CodecType = eCodec;videoParserParameters.ulMaxNumDecodeSurfaces = 1;videoParserParameters.ulMaxDisplayDelay = bLowLatency ? 0 : 1;videoParserParameters.pUserData = this;// 三个回调函数videoParserParameters.pfnSequenceCallback = HandleVideoSequenceProc;videoParserParameters.pfnDecodePicture = HandlePictureDecodeProc;videoParserParameters.pfnDisplayPicture = HandlePictureDisplayProc;if (m_pMutex) m_pMutex->lock();NVDEC_API_CALL(cuvidCreateVideoParser(&m_hParser, &videoParserParameters));if (m_pMutex) m_pMutex->unlock();
}
  • 具体每个解码器的解码线程
void DecProc(NvDecoder *pDec, const char *szInFilePath, int nWidth, int nHeight, uint8_t **apFrameBuffer,int nFrameBuffer, int *piEnd, int *piHead, bool *pbStop, cudaStream_t stream, int xCenter, int yCenter, std::exception_ptr &ex)
{try{printf("--> enter DecProc\n");FFmpegDemuxer demuxer(szInFilePath); // 解封装ck(cuCtxSetCurrent(pDec->GetContext())); // 将main中创建的cuda-context设置为当前上下文uint8_t *dpRippleImage;ck(cudaMalloc(&dpRippleImage, nWidth * nHeight));int iTime = 0;// Render a ripple image on dpRippleImage//LaunchRipple(stream, dpRippleImage, nWidth, nHeight, xCenter, yCenter, iTime++);int nVideoBytes = 0/*读取数据大小*/, nFrameReturned /*解码帧数*/ = 0, nFrame = 0;uint8_t *pVideo = NULL, **ppFrame;do{demuxer.Demux(&pVideo, &nVideoBytes); // 解析数据pDec->DecodeLockFrame(pVideo, nVideoBytes, &ppFrame, &nFrameReturned);for (int i = 0; i < nFrameReturned; i++) { // 解码完成的帧数// For each decoded framewhile (*piHead == *piEnd) {// Queue is fullstd::this_thread::sleep_for(std::chrono::milliseconds(1));}// Frame buffer is locked, so no data copy is needed hereapFrameBuffer[*piHead % nFrameBuffer] = ppFrame[i]; // 将指针赋给外部的apFrameBuffer// Overlay dpRippleImage onto the frame buffer 将波纹效果叠加到解码后的数据LaunchOverlayRipple(stream, apFrameBuffer[*piHead % nFrameBuffer], dpRippleImage, nWidth, nHeight);// Make sure CUDA kernel is finished before marking the current position as readyck(cudaStreamSynchronize(stream));// Mark as ready++*piHead;// 更新波纹效果(iTime不同)LaunchRipple(stream, dpRippleImage, nWidth, nHeight, xCenter, yCenter, iTime++);}} while (nVideoBytes);ck(cudaFree(dpRippleImage)); // 全部解码完成释放内存*pbStop = true;}catch (std::exception&){ex = std::current_exception();}
}

NIVIDIA 硬解码学习4相关推荐

  1. 【学习】从零开发的Android音视频开发(13)——MediaCodec到OMX框架过程及其硬解码

    MediaCodec到OMX框架过程 在讲NuPlayer时,NuPlayer解码部分会创建MediaCodec,并且最终到达OMX框架,先看MediaCodec的init函数 从init函数中可以看 ...

  2. AI视频行为分析系统项目复盘——技术篇2:视频流GPU硬解码

    0 项目背景 见<AI视频行为分析系统项目复盘--技术篇1> https://blog.csdn.net/weixin_42118657/article/details/118105545 ...

  3. android硬编码封装mp4,【Android 音视频开发打怪升级:音视频硬解码篇】四、音视频解封和封装:生成一个MP4...

    [声 明] 首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正. 其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了. 最后,写文章过程 ...

  4. 视频GPU硬解码方案对比

    声明:文章仅作知识整理.分享,如有侵权请联系作者删除博文,谢谢! 实时视频处理中,紧靠CPU进行视频解码速度慢/占用CPU资源较多,导致整个系统处理效率不高,解码成为系统的瓶颈.需要进行GPU解码高清 ...

  5. Android音视频【三】硬解码播放H264

    人间观察 穷人家的孩子真的是在社会上瞎混 遥远的2020年马上就过去了,天呐!!! 前两篇介绍了下H264的知识和码流结构,本篇就拿上篇从抖音/快手抽离的h264文件实现在Android中进行解码播放 ...

  6. 【Android 音视频开发-音视频硬解码篇】1.音视频基础知识

    这是一个入门系列,涉及的知识也仅限于够用. 最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享. 本文你可以了解到 作为开篇的文章,我们先来看看音视频由什么构成的,以 ...

  7. ffmpeg + cuda(cuvid) 硬解码+像素格式转换(cpu主导)实战

    注意: VAAPI 是inter gpu 提供的硬编解码接口 VDPAU 是 video decode present api for unix nvdec / ncvid 都是nivida产出的硬解 ...

  8. Android视频滤镜添加硬解码方案

    由于工作的需求,研究过了一段时间的Android 的音视频播放渲染以及编辑方面的知识,这里就自己一些浅薄的了解对所了解做一个简单的介绍和记录,如有不对的地方请指正!同时也会记录下硬件解码的情况下完成滤 ...

  9. 集显也能硬件编码:Intel SDK 各种音视频编解码学习详解

    http://blog.sina.com.cn/s/blog_4155bb1d0100soq9.html INTEL MEDIA SDK是INTEL推出的基于其内建显示核心的编解码技术,我们在播放高清 ...

  10. Qt音视频开发10-ffmpeg内核硬解码

    一.前言 为了极大的降低CPU的占用,实现硬解码(也叫硬件加速)非常有必要,一个视频文件或者一路视频流还好,如果增加到64路视频流呢,如果是4K.8K这种高分辨率的视频呢,必须安装上硬解码才是上上策. ...

最新文章

  1. django2.0集成xadmin0.6报错集锦
  2. HDU 3949 XOR 线性基
  3. JZOJ 5414. 【NOIP2017提高A组集训10.22】幸运值
  4. WPF DataGrid 通过自定义表头模拟首行固定
  5. 第六十期:华为:希望把VR/AR打造成下个智能手机产业
  6. E-triples II_2019牛客暑期多校训练营(第四场)
  7. My new iMac 27
  8. python装饰品 后端_python装饰器
  9. sql还原数据库备份数据库_如何获取SQL数据库还原历史记录
  10. creo外观库_Proe/Creo外观着色与贴图
  11. 华为交换机dhcp获取不到_S7706交换机客户端无法通过DHCP获取地址问题
  12. win10解决,你没有权限打开该文件,请向文件的所有者或管理员申请权限
  13. win7计算机无法连接投影仪,win7系统下投影仪无法输出信号到电脑的解决方法
  14. 异形3×3魔方还原教程_2345异形魔方教程
  15. Neo4j 安装、使用教程
  16. 湖北省计算机二级报名时间2020年12,2020年3月湖北省计算机等级考试报名_时间:12月10日-12月23日...
  17. IIS配置网站访问权限和安全
  18. 关于 The RPC server is unavailable
  19. Python 抽取剔除视频帧工具
  20. 关于在Spark集群中读取本地文件抛出找不到文件异常的问题

热门文章

  1. K-means之亚洲杯
  2. 万恶之源的hello world
  3. 图像处理基本方法-python语言生成纯色BMP文件
  4. 交易系统的高盈亏比怎么实现?
  5. 卡拉赞服务器延迟,卡拉赞开荒详细功略(前门)
  6. YarnAllocator:Container killed by YARN for exceeding memory limits. spark.yarn.executor.memoryOverhe
  7. php 与 html 的混合编程
  8. 代理服务器关闭没过多久又自动开启,Win10自动更新关闭了过几天又自动开启了怎么办?...
  9. 多后端深度学习开发框架TensorlayerX发布
  10. 聊一聊进程、线程和协程以及线程的那些“锁“事