硬编码支持情况(一)

                                                              硬编码支持情况(一)

图片信息原文链接:http://trac.ffmpeg.org/wiki/HWAccelIntro

截个图:

注:

(一):Intel 平台

1:Intel 平台的Quick Sync Video(qsv)是对于音视频编解码的框架具体对外接口则为上面图中的LIBMFX,可以理解为Quick Sync Video(qsv)是对外宣称的技术ffmpeg对外设备TYPE选择也是这个,但如果不用ffmpeg直接用intel自身api则操作的是LIBMFX库提供的对外API接口。

2:SDK下载地址:https://github.com/Intel-Media-SDK/MediaSDK,需要安装。

3:samples下载地址:https://github.com/Intel-Media-SDK/samples/tree/4293f965b42310c043bc49d23c6f9f5d3d587dd8,里面有编码和解码的samples。

4:【Intel(R)_Media_SDK】官方文档翻译摘要:https://blog.csdn.net/zhuweigangzwg/article/details/83861644

5:如果开发程序需要添加的lib库如下:

//用intel的mfx的media库command工程中引用如下才能编译成功生成lib库。
//本项目再引用此lib库。
/*
..\include;
$(INTELMEDIASDKROOT);               //intelmediasdk
..\include\vm;
*/
/*
本工程添加的头文件目录
..\INTEL_MFX\include;
$(INTELMEDIASDKROOT)\include;       //intelmediasdk
添加的lib库目录:..\Debug; ..\Release; 即上面command生成的lib库。
添加lib库目录:$(INTELMEDIASDKROOT)\lib\$(Platform)    //intelmediasdk
*/

//添加command库
#pragma comment(lib,"libmfx_vs2015.lib")  
#pragma comment(lib,"dxva2.lib") 
#pragma comment(lib,"d3d9.lib") 
#pragma comment(lib,"d3d11.lib") 
#pragma comment(lib,"dwmapi.lib") 
#pragma comment(lib,"comsuppw.lib") 
#pragma comment(lib,"dxgi.lib") 

//目录C:\Program Files (x86)\IntelSWTools\Intel(R) Media SDK 2018 R2\Software Development Kit\lib

#pragma comment(lib,"libmfx.lib")           

//目录C:\Program Files (x86)\IntelSWTools\Intel(R) Media SDK 2018 R2\Software Development Kit\lib             
#pragma comment(lib,"libmfx_vs2015.lib")  
#pragma comment(lib,"command.lib")  

6:CBuffering::m_pSurfaces内存管理的类,由于此类中的一些函数或变量是protected的,
只有用c++继承方式,但这里是c语言,需要修改源码protected为public,文件是mfx_buffering.h,函数和变量都修改为public。

7:mfx解码时候的5个surface,输入两个在(1)中说明,输出两个在(2)中说明,(3)中是个临时变量为解决解码参数问题。

如果输出的surface不用直接将(3)的解码后的surface用于输出也可以。

(1)CurrentFreeSurface是空闲的surface,传入解码器,用于解码,如果在解码途中则需要转给m_UsedSurfacesPool队列。
即这段数据正在解码被锁住则传入正在使用的surface队列中
m_FreeSurfacesPool队列中取出的m_pCurrentFreeInSurface和m_UsedSurfacesPool都是用于存放对H264处理的surface.都是输入surface。current字段是防止和系统的变量名称冲突。

(2)CurrentFreeOutputSurface是空闲的输出surface,传入解码器,用于接收解码之后的yuv数据,接收之后传给m_OutputSurfacesPool队列。GetFreeOutputSurface中取出的m_pCurrentFreeOutputSurface和m_OutputSurfacesPool都是用于存放解码后的yuv数据surface,都是输出队列。

(3)引入m_pOutSurface这个变量只是为了适应DecodeFrameAsync这个函数传入**指针的操作,如果可以用m_pCurrentFreeOutputSurface也可以。

8:这里一定要注意有个大坑:

mfxBitstream * pBitstream 这个变量中就是存放一帧要解码数据的结构体其中,MaxLength:申请的内存大小,DataLength:解码前是传入的数据大小,解码后是剩余的数据大小。DataOffset:解码前为0,解码后实际解码了多少数据。

解码后的状态码MFX_ERR_MORE_DATA == sts时pBitstream->DataLength一定等于0这时候再读取一帧数据没问题。
如果MFX_ERR_NONE == sts时分为两种情况,1:pBitstream->DataLength == 0,这时再去读取一帧数据。
如果MFX_ERR_NONE == sts时分为两种情况,2:pBitstream->DataLength != 0,这时一定不能读取新的数据,要继续用剩下的数据去解码.否则会有帧减少产生图像花屏绿屏情况。

也就是说解码传进去一帧数据有可能返回的时候剩余一些字段,需要将这些字段再传给解码器做解码,而不是再读取一帧用于解码这个值就是pBitstream->DataLength。

9:mfxEncodeCtrl encCtrl;   //编码的帧类型和一些扩展信息存储结构体。只是扩展用填写为NULL也可以如果没有扩展。

 //编码时这个值必须清零否则EncodeFrameAsync会失败返回-15.
 MSDK_ZERO_MEMORY(encCtrl);

编码后需要做异步同步操作。//同步操作,输出数据到m_mfxBS中
sts = m_mfxSession.SyncOperation(UserSyncp, MSDK_WAIT_INTERVAL);

10:mfx 填写qp时,unsigned short UserRateControlMethod = MFX_RATECONTROL_CQP;  //CBR,ABR,CQP

m_mfxEncParams.mfx.RateControlMethod = UserRateControlMethod;  //CBR,ABR,CQP  

 if (UserRateControlMethod == MFX_RATECONTROL_CQP)
    {
        m_mfxEncParams.mfx.QPI = 24;
        m_mfxEncParams.mfx.QPP = 34;
        m_mfxEncParams.mfx.QPB = 34;
    }

其他值RateControlMethod 则一旦填写QP初始化会失败。

11:    //解决实时编码问题(传入空数据)。
    do 
    {
        sts = m_pmfxENC->EncodeFrameAsync(&encCtrl, pSurf, &m_mfxBS, &UserSyncp);
        pSurf = NULL;
        Sleep(1);
    } while (sts == MFX_ERR_MORE_DATA);

12://解决实时解码问题(传入空数据)。

    do
    {
        sts = m_pmfxDEC->DecodeFrameAsync(NULL, &(m_pCurrentFreeSurface->frame), &m_pOutSurface, &usersyncp);

        if (sts == MFX_ERR_MORE_DATA)
        {
            Sleep(1);
        }

    } while (MFX_ERR_MORE_DATA== sts);

13:  //解决实时解码问题(传入空数据)。

    //错误处理
    if (sts == MFX_ERR_DEVICE_LOST ||
        sts == MFX_ERR_INCOMPATIBLE_VIDEO_PARAM ||
        sts == MFX_ERR_INVALID_VIDEO_PARAM ||
        sts == MFX_ERR_UNDEFINED_BEHAVIOR ||
        sts == MFX_ERR_DEVICE_FAILED)
    {
        DISPATCHER_LOG_INFO((("CMfxDecoder::DecodeFrameAsync = %d."), sts));
        return -1;
    }

这些返回的错误或者可以根据测试情况添加其他错误,即解码失败而不能循环处理解决实时解码的问题。

14:如果想在解码途中修改解码参数需要。

       //m_pmfxDEC->Reset接口不好用,用下面方式替代。

        if (m_pmfxDEC != NULL)
        {
            m_pmfxDEC->Close();
            MSDK_SAFE_DELETE(m_pmfxDEC); 
            m_pmfxDEC = NULL;
        }

        FreeBuffers();

        m_pCurrentFreeSurface = NULL;
        MSDK_SAFE_FREE(m_pCurrentFreeOutputSurface);

        // delete frames
        if (m_pGeneralAllocator)
        {
            m_pGeneralAllocator->Free(m_pGeneralAllocator->pthis, &m_mfxResponse);
        }
        MSDK_SAFE_DELETE(m_pGeneralAllocator);
        MSDK_SAFE_DELETE(m_pmfxAllocatorParams);

        //重新初始化
        mfxStatus sts = Init();

15:控制码率情况:

    if (UserRateControlMethod == MFX_RATECONTROL_CQP)
    {
        //只填写这三个值起作用但控制不住码率。相同分辨率下,图像剧烈时码率会有较大变化。
        m_mfxEncParams.mfx.QPI = 24;
        m_mfxEncParams.mfx.QPP = 24;
        m_mfxEncParams.mfx.QPB = 24;
        //通过下列方式填写MaxQPI,MinQPI等六个参数在MFX_RATECONTROL_CQP模式下不起作用。
        // for look ahead BRC configuration
        //m_CodingOption2.MaxQPI = m_mfxEncParams.mfx.QPI + 5;
        //m_CodingOption2.MinQPI = m_mfxEncParams.mfx.QPI - 5;
        //m_CodingOption2.MaxQPP = m_mfxEncParams.mfx.QPP + 5;
        //m_CodingOption2.MinQPP = m_mfxEncParams.mfx.QPP - 5;
        //m_CodingOption2.MaxQPB = m_mfxEncParams.mfx.QPB + 5;
        //m_CodingOption2.MinQPB = m_mfxEncParams.mfx.QPB - 5;
        m_EncExtParams.push_back((mfxExtBuffer *)&m_CodingOption2);
        if (!m_EncExtParams.empty())
        {
            m_mfxEncParams.ExtParam = &m_EncExtParams[0]; // vector is stored linearly in memory
            m_mfxEncParams.NumExtParam = (mfxU16)m_EncExtParams.size();
        }
    }
    else if (UserRateControlMethod == MFX_RATECONTROL_VBR)
    {
        //采用MFX_RATECONTROL_VBR模式用填写码率控制。
        m_bitRateIn = 800;
        m_mfxEncParams.mfx.InitialDelayInKB = 0;
        m_mfxEncParams.mfx.TargetKbps = (unsigned short)m_bitRateIn; //码率
        m_mfxEncParams.mfx.MaxKbps = m_bitRateIn;
    }

16:

 

 

(二)AMD平台

1:AMD平台的AMF对外API接口开源地址:https://gpuopen.com/gaming-product/advanced-media-framework/ 。  

https://github.com/GPUOpen-LibrariesAndSDKs/AMF。里面有编码和解码的samples。

2:AMF的库Version 1.4.9: AMD Radeon™ Software Adrenalin 18.8.1 (18.30.01.01) or later。需要显卡类型是这个,否则在安装驱动时里面不会包含amfrt32.dll和amfrt64.dll,这两个运行时库用在

//初始化AMF解码所需的全局库 res = g_AMFFactory.Init();

如果找不到会失败驱动版本应该是17.几以后,像老的ATI的显卡驱动里面没有,但是把这两个库拷贝到运行程序同目录可能有未知问题。

3:从GPU拷贝数据到CPU,解码后的数据如果是1080P的耗时约7-10毫秒一帧,这已经是AMF优化过的,如果不优化耗时更多。

        long long llbegin = __rdtsc();
        // convert to system memory 转换为系统内存AMF_MEMORY_HOST就是系统内存CPU中。
        dataDecoder->Convert(amf::AMF_MEMORY_HOST);
        long long llend = __rdtsc();
        float ftime = (llend - llbegin) / (float)3400000000;
        printf("************************%f\n", ftime);

这里需要注意耗时情况,以便做相应的处理。

4:关于amfrt32.dll,win32程序运行时首先查找可执行程序同目录下是否有amfrt32.dll,如果没有则去C:\Windows\SysWOW64这个目录查找是否有,AMD的这个动态库有个bug,先看下下面版本对应信息。

    //产品版本:1.4.0.0的amfrt32.dll  :AMFRuntimeVersion : 281492156579840 (decoder->Init成功)
    //产品版本:1.4.1.0的amfrt32.dll  :AMFRuntimeVersion : 281492156645376 (decoder->Init成功)
    //产品版本:1.4.2.0的amfrt32.dll  :AMFRuntimeVersion : 281492156710912 (decoder->Init成功)
    //产品版本:1.4.4.0的amfrt32.dll  :AMFRuntimeVersion : 281492156841984 (decoder->Init失败)
    //产品版本:1.4.6.0的amfrt32.dll  :AMFRuntimeVersion : 281492156973056 (decoder->Init失败)
    //产品版本:1.4.8.0的amfrt32.dll  :AMFRuntimeVersion : 281492157104128 (decoder->Init失败)

即1.4.4.0以前的版本amfrt32.dll ,decoder->Init时无论是否正常安装了AMD的显卡驱动并且显卡驱动是否支持编解码都能初始化init成功,如在intel机器中将此库放到可执行程序同目录下也可以初始化成功。

AMD显卡启动程序将amfrt32.dll_1.4.4.0(不包含这个版本只以下1.4.0.0或1.4.1.0或1.4.2.0)远程桌面才能支持amd显卡解码,并且将amfrt32.dll_1.4.0.0和可执行的exe程序放入同一个目录,则init为非常慢。甚至达到1秒左右。

amfrt32.dll的各个版本库可以在这个链接下。

https://www.pconlife.com/viewfileinfo/amfrt64-dll/

显卡支持amf的情况可在官网查看:

Version 1.4.9: AMD Radeon™ Software Adrenalin 18.8.1 (18.30.01.01) or later
Version 1.4.7: AMD Radeon™ Software Crimson 18.3.4 (17.50.33) or later
Version 1.4.6: AMD Radeon™ Software Crimson 17.12.1 (17.50.02) or later
Version 1.4.4: AMD Radeon™ Software Crimson 17.7.2 (17.30.1041) or later
Version 1.4.0: AMD Radeon™ Software Crimson 17.1.1 (16.50.2611) or later
Version 1.3.0: AMD Radeon™ Software Crimson 16.7.3 (16.30.2311) or later
Microsoft® Windows® 7, Microsoft® Windows® 8.1 or Microsoft® Windows® 10
AMF 1.4.4 or later requires OCL_SDK_LIGHT.  Previous versions require  AMD APP SDK (Version 3.0 or later),  Microsoft® Windows® 10 SDK (Version 10586 as required by the build solution) and some samples require the Microsoft® Foundation Class Library.
Examples are built with Visual Studio® 2013, Visual Studio® 2015 and Visual Studio® 2017

链接地址:https://gpuopen.com/gaming-product/advanced-media-framework/

5:amf::AMFBufferPtr,amf::AMFDataPtr ,amf::AMFSurfacePtr这些变量都是智能指针,生命周期结束自动释放,但在Detach()之后即 amf::AMFDataPtr = amf::AMFBufferPtr.Detach();之后amf::AMFDataPtr有时候需要手动释放,有时候不需要释放释放会崩溃,这个问题百思不得其解。需要手动释放时如果不手动释放会有内存泄漏。

6:AMD编码在多进程情况下支持多个测试程序输出并输出正常。但在多线程情况下只支持一路输出。其他路不能正常编码。

初步分析:查找amd官方文档查找对应参数如下,AMF_VIDEO_ENCODER_MAX_INSTANCES 1 or 2 Hardware-dependent, only some hardware supports 2 instances,

AMF_VIDEO_ENCODER_MULTI_INSTANCE_MODE True or False Enables or disables,multi-instance mode,default – disabled

测试发现只支持一个实例编码输出。

7:  编码之后取出编码后的数据有可能返回AMF_REPEAT,编码后的数据为空,可以用下列方法解决取出数据为空的问题。

//解决实时编码问题。
    do 
    {
        res = m_pEncoder->QueryOutput(&dataEncoder);
        amf_sleep(1);
    } while (res == AMF_REPEAT);

8:如果想在解码途中修改解码参数需要。

        //m_pDecoder->ReInit接口不好用,用下面方式替代。
        // drain decoder queue 
        m_pDecoder->Drain();
        m_pDecoder->Terminate();

        res = m_pDecoder->Init(m_formatOut, VideoWidth,VideoHeight);

9:SubmitInput第一次由于需要解析sps,pps所以耗时较高约3-5毫秒,其他没有sps,pps解析的帧数据传入SubmitInput耗时只需要0.3-0.5毫秒。

10:码率控制情况

            //码率控制模式,CBR,VBR,CQP;
            if (amf_video_encode_rate_modle == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP)
            {
                m_bitRateIn *= 1024;
                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE, m_bitRateIn);
                //设置最大码率
                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_PEAK_BITRATE, m_bitRateIn);
                //设置qp_min
                m_qp_min = 20;
                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_MIN_QP, m_qp_min);
                //设置qp_max
                m_qp_max = 30;
                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_MAX_QP, m_qp_max);
            }
            else if (amf_video_encode_rate_modle == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR ||
                amf_video_encode_rate_modle == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR)
            {
                //设置码率

                m_bitRateIn = 800;
                m_bitRateIn *= 1024;  //传入的是Kbps不是bps,所以要*1024.        

                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_TARGET_BITRATE, m_bitRateIn);
                //设置最大码率
                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_PEAK_BITRATE, m_bitRateIn);
                //buf缓存:VBV Buffer Size in bits;填写这个很重要,否则码率不会按照真实码率控制。
                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_VBV_BUFFER_SIZE, (m_bitRateIn * 1 / m_frameRateIn) * 5);
                //buf缓存:Initial VBV Buffer Fullness 0=0% 64=100%;填写这个很重要,否则码率不会按照真实码率控制。
                res = m_pEncoder->SetProperty(AMF_VIDEO_ENCODER_INITIAL_VBV_BUFFER_FULLNESS, 64);
            }
        }

11:

 

(三)NVDIA平台

1:开发者官网链接:https://developer.nvidia.com

SDK链接:https://developer.nvidia.com/nvidia-video-codec-sdk。需要注册账号才能下载,下载:找到界面链接,Download Video Codec SDK 8.2,或其他版本的SDK用于开发。

CUDA工具包:https://developer.nvidia.com/cuda-toolkit

2:在安装CUDA的时候,会安装三个大的组件,分别是NVIDIA驱动、toolkit和samples。驱动用来控制gpu硬件,
toolkit里面包括nvcc编译器、Nsight调试工具(支持Eclipse和VS,linux用cuda-gdb)、分析和调试工具和函数库。
samples或者说SDK,里面包括很多样例程序包括查询设备、带宽测试等等。

3:toolkit安装:在这个目录https://developer.nvidia.com/cuda-downloads?target_os=Windows&target_arch=x86_64&target_version=7&target_type=exelocal。
下载click cuda_10.0.130_411.31_windows.exe或其他版本,安装到目录C:\Users\用户\AppData\Local\Temp\CUDA下。
在这个目录下查找nvcc文件夹(如果只是取出这个库一定要在安装的途中解压进度条运行完成后到安装时将这个库取出,否则取消安装或安装失败会删除这个安装文件夹),如果安装成功则会有这个文件夹。

4://附加包含目录

//..\NVDIA_NVDEC_NVENC
//..\NVDIA_NVDEC_NVENC\Common
//..\NVDIA_NVDEC_NVENC\External\FFmpeg\include     //编码不需要,解码为了分帧
//..\NVDIA_NVDEC_NVENC\NvCodec
//..\NVDIA_NVDEC_NVENC\Utils
//..\NVDIA_NVDEC_NVENC\nvcc\include

//附加库目录
//..\NVDIA_NVDEC_NVENC\External\FFmpeg\lib\Win32   //编码不需要,解码为了分帧
//..\NVDIA_NVDEC_NVENC\NvCodec\Lib\Win32
//..\NVDIA_NVDEC_NVENC\nvcc\lib\Win32
//$(CudaToolkitLibDir)

//添加新建筛选器,将NvCodec/NvDecoder加入,以编译cpp文件

//添加新建筛选器,将NvCodec/NvEncoder加入,以编译cpp文件

//忽略警告:在 项目->属性->C / C++->预处理器->预处理器定义中添加_CRT_SECURE_NO_WARN。

//运行时需要dll动态库
//nvcuvid.dll  nvcuda.dll
//运行时有可能缺少nvfatbinaryloader.dll,动态加载dll方式还未验证。

5://如果是静态加载则需要加载这些lib,nvcuvid.lib在官网下载的实例中,cuda.lib在工具包nvcc中,并将所需要的dll放在和exe同目录下。动态加载用LoadLibrary加载dll。
//..\NVDIA_NVDEC_NVENC\nvcc\lib\Win32中存在这几个库cuda.lib cudadevrt.lib  cudart.lib cudart_static.lib OpenCL.lib。根据自己需求添加
//#pragma comment(lib,"nvcuvid.lib") 
//#pragma comment(lib,"cuda.lib")  

6://如果是动态加载需要修改源码添加函数指针(解码)
//解码:
//(1):修改NvDecoder类的构造函数和析构函数,修改为:NvDecoderInit和NvDecoderUninit,初始化和反初始化,代码从原构造函数和析构函数拷贝得来。
//修改源码,设置nvcuvid.dll库的指针和nvcuda.dll库的指针传入void SetNvDecoderDllHandle。
//修改源码,设置nvcuvid.dll库的指针和nvcuda.dll库的指针句柄
//static void * m_pcuNvcuvidLib = NULL;       //nvcuvid.dll库的指针
//static void * m_pcuNvcudaLib = NULL;       //nvcuda.dll库的指针

//(2):修改NvDecoder/NvDecoder.h中的包含//#include "nvcuvid.h"
//新建#include "dylink_nvcuvid.h"文件,将nvcuvid.h中所有CUDAAPI的对外导出函数全部注释掉。
//在dylink_nvcuvid.h中添加#include "nvcuvid.h"。
//例如:在nvcuvid.h中注释掉//CUresult CUDAAPI cuvidCreateVideoSource(CUvideosource *pObj, const char *pszFileName, CUVIDSOURCEPARAMS *pParams);
//在dylink_nvcuvid.h中添加typedef CUresult (CUDAAPI *tcuvidCreateVideoSource)(CUvideosource *pObj, const char *pszFileName, CUVIDSOURCEPARAMS *pParams);
//static tcuvidCreateVideoSource cuvidCreateVideoSource = NULL; 其他函数同理。

//(3):修改nvcuvid.h中的包含//#include "cuviddec.h"
//新建#include "dylink_cuviddec.h"文件,将cuviddec.h中所有CUDAAPI的对外导出函数全部注释掉。
//在dylink_cuviddec.h中添加#include "cuviddec.h"。
//例如:在cuviddec.h中注释掉//extern CUresult CUDAAPI cuvidGetDecoderCaps(CUVIDDECODECAPS *pdc);
//在dylink_cuviddec.h中添加typedef CUresult (CUDAAPI * tcuvidGetDecoderCaps)(CUVIDDECODECAPS *pdc);
//static tcuvidGetDecoderCaps cuvidGetDecoderCaps = NULL; 其他函数同理。

//(4):修改cuviddec.h中的包含//#include <cuda.h>
//新建#include "dylink_cuda.h"文件,将cuda.h中所有CUDAAPI的对外导出函数全部注释掉。
//在dylink_cuda.h中添加#include "cuda.h"。
//例如:在cuda.h中注释掉//CUresult CUDAAPI cuGetErrorString(CUresult error, const char **pStr);
//在dylink_cuda.h中添加typedef CUresult (CUDAAPI *tcuGetErrorString)(CUresult error, const char **pStr);
//static tcuGetErrorString cuGetErrorString; 其他函数同理(约400多个)。

//(5):在main.cpp中添加loadCudaLibrary引用nvcuvid.dll  nvcuda.dll两个dll.
//根据函数需求getProcAddress获取对外函数地址并使用。
//例如:if ((cuInit = (tcuInit)getProcAddress(cuNvcudaLib, "cuInit")) == NULL)
//if ((cuInit(0)) == CUDA_SUCCESS) 根据函数指针名称获取并使用。

//(6):修改NvDecoder/NvDecoder.cpp中所有用到dylink_nvcuvid.h,dylink_cuviddec.h以及dylink_cuda.h修改后的函数指针应用,改为动态加载方式。
//例如:    //修改源码,改为动态加载方式
//if ((cuvidCtxLockCreate = (tcuvidCtxLockCreate)GetProcAddress((HMODULE)m_pcuNvcuvidLib, "cuvidCtxLockCreate")) == NULL)
//{
//    return false;
//}
//所有例如cuCtxPushCurrent_v2包含_v2的对外函数名称在GetProcAddress时必须用_v2的名称。
//例如:修改源码,改为动态加载方式
//if ((cuCtxPushCurrent = (tcuCtxPushCurrent)GetProcAddress((HMODULE)m_pcuNvcudaLib, "cuCtxPushCurrent_v2")) == NULL)
//{
//    return nDecodeSurface;
//}
//用cuCtxPushCurrent_v2这个函数名称获取,如果用cuCtxPushCurrent会出现崩溃的情况。
//例如NVDEC_API_CALL(cuvidCtxLockCreate(&m_ctxLock, cuContext));
//或者写个宏
//有些扩展的对外函数例如 #define cuCtxCreate  cuCtxCreate_v2,
//需要GetProcAddress用cuCtxCreate_v2这个名称而不是cuCtxCreate这个名称。
//自动检查是否有_v2,如果有用_v2函数名称,如果没有用原名称。
//#define USERTOT(x) # x          //#作用将x变成字符串。
//#define USERMA(x) USERTOT(x)    //作用是将x展开,即如果x为fuc,因为有(#define fuc fuc_v2),所以x将变为fuc_v2.
//应用例如:if ((cuInit = (tcuInit)getProcAddress(cuNvcudaLib, USERMA(cuInit))) == NULL)。

//(7):需要将所有的导出函数指针dylink_文件中添加extern "C"。

//(8):

7://如果是动态加载需要修改源码添加函数指针(编码)

//编码:

//(1):修改NvEncoderCuda类的构造函数和析构函数,修改为:NvEncoderCudaInit和NvEncoderCudaUninit,初始化和反初始化,代码从原构造函数和析构函数拷贝得来。
//修改源码,设置nvcuvid.dll库的指针和nvcuda.dll库的指针传入void SetNvEncoderCudaDllHandle。
//修改源码,设置nvcuvid.dll库的指针和nvcuda.dll库的指针句柄
//static void * m_pcuNvcuvidLib = NULL;       //nvcuvid.dll库的指针
//static void * m_pcuNvcudaLib = NULL;       //nvcuda.dll库的指针

//(2):修改NvEncoder/NvEncoderCuda.h中的包含//#include "cuda.h"
//新建#include "dylink_cuda.h"文件,将cuda.h中所有CUDAAPI的对外导出函数全部注释掉。
//在dylink_cuda.h中添加#include "cuda.h"。
//例如:在cuda.h中注释掉//CUresult CUDAAPI cuGetErrorString(CUresult error, const char **pStr);
//在dylink_cuda.h中添加typedef CUresult (CUDAAPI *tcuGetErrorString)(CUresult error, const char **pStr);
//static tcuGetErrorString cuGetErrorString; 其他函数同理(约400多个)。
//(这里注意可以用和解码decoder操作同一个文件dylink_cuda.h,即解码和编码重复用和包含cuda.h,和dylink_cuda.h两个一样的文件)。

//(3):由于NvEncoder.h和NvEncoder.cpp这两个文件中只用到了nvEncodeAPI.h中的内容未用到cuda.h和nvcuvid.h中的内容所以不需要修改。
//4:由于nvEncodeAPI.h这个文件里面的导出函数在NvEncoder.cpp中是动态加载的LoadLibrary(TEXT("nvEncodeAPI.dll"));
//所以nvEncodeAPI.h这个文件的导出函数不需要处理。

//(4):在main.cpp中添加loadCudaLibrary引用nvcuvid.dll  nvcuda.dll两个dll.
//根据函数需求getProcAddress获取对外函数地址并使用。
//例如:if ((cuInit = (tcuInit)getProcAddress(cuNvcudaLib, "cuInit")) == NULL)
//if ((cuInit(0)) == CUDA_SUCCESS) 根据函数指针名称获取并使用。

//(5):只用到cuda.h,未用到nvcuvid.h所以没有类似dylink_nvcuvid.h这样的文件包含nvcuvid.h的函数指针。
//解码需要cuda.h和nvcuvid.h,编码只需要cuda.h。

//(6):修改NvEncoder/NvEncoderCuda.cpp中所有用到dylink_cuda.h修改后的函数指针应用,改为动态加载方式。
//修改源码,改为动态加载方式
//if ((cuCtxPushCurrent = (tcuCtxPushCurrent)GetProcAddress((HMODULE)m_pcuNvcudaLib, USERMA(cuCtxPushCurrent))) == NULL)
//{
//    break;
//}
//所有例如cuCtxPushCurrent_v2包含_v2的对外函数名称在GetProcAddress时必须用_v2的名称。
//例如:修改源码,改为动态加载方式
//if ((cuCtxPushCurrent = (tcuCtxPushCurrent)GetProcAddress((HMODULE)m_pcuNvcudaLib, "cuCtxPushCurrent_v2")) == NULL)
//{
//    return;
//}
//用cuCtxPushCurrent_v2这个函数名称获取,如果用cuCtxPushCurrent会出现崩溃的情况。
//例如CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext));
//或者写个宏
//有些扩展的对外函数例如 #define cuCtxCreate  cuCtxCreate_v2,
//需要GetProcAddress用cuCtxCreate_v2这个名称而不是cuCtxCreate这个名称。
//自动检查是否有_v2,如果有用_v2函数名称,如果没有用原名称。
//#define USERTOT(x) # x          //#作用将x变成字符串。
//#define USERMA(x) USERTOT(x)    //作用是将x展开,即如果x为fuc,因为有(#define fuc fuc_v2),所以x将变为fuc_v2.
//应用例如:if ((cuInit = (tcuInit)getProcAddress(cuNvcudaLib, USERMA(cuInit))) == NULL)。

//(7):需要将所有的导出函数指针dylink_文件中添加extern "C"。

//(8):

8:https://developer.nvidia.com/video-encode-decode-gpu-support-matrix;这个链接里面有各个型号的显卡,编码解码情况的列表,值得提的一点Max # of concurrent sessions,这是并行编码支持的最大路数。有些型号得显卡只支持最多2路。测试发现如果这种显卡启动第三路编码:在调用NVENC_API_CALL(m_nvenc.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder));时会报NV_ENC_ERR_OUT_OF_MEMORY 并崩溃,需要做处理限制路数。

9:如果想调整参数或者将编码的谋一帧强制为idr帧用如下方法。

//重新填写编码的参数信息,测试//if (numframe % 3 == 0)
 if ((numframe % m_gopsize)== m_gopsize)
{
    NV_ENC_RECONFIGURE_PARAMS reconfigureParams = { NV_ENC_RECONFIGURE_PARAMS_VER };
    memcpy(&reconfigureParams.reInitEncodeParams, &initializeParams, sizeof(initializeParams));
    NV_ENC_CONFIG reInitCodecConfig = { NV_ENC_CONFIG_VER };
    memcpy(&reInitCodecConfig, &encodeConfig, sizeof(reInitCodecConfig));
    reconfigureParams.reInitEncodeParams.encodeConfig = &reInitCodecConfig;
    //指定某一帧为IDR帧
    reconfigureParams.forceIDR = 1;
    //重新设置编码参数
    pencoder->Reconfigure(&reconfigureParams);
}

10:如何设置编码为H264还是H265(HEVC)

    if (m_encodeH264orHEVC == 0)
    {
        const char *m_encodeCLIOptionsParam = "-codec  h264";                //如果想编码成hevc用"-codec  hevc";如果编码成H264用"-codec h264";如果是空字符串则是编码为h264。
        m_encodeCLIOptions = NvEncoderInitParam(m_encodeCLIOptionsParam, nullptr, true);
    }
    else if (m_encodeH264orHEVC == 1)
    {
        const char *m_encodeCLIOptionsParam = "-codec  hevc";                //如果想编码成hevc用"-codec  hevc";如果编码成H264用"-codec h264";如果是空字符串则是编码为h264。
        m_encodeCLIOptions = NvEncoderInitParam(m_encodeCLIOptionsParam, nullptr, true);
    }
    else
    {
        m_encodeCLIOptions = NvEncoderInitParam("", nullptr, true);
    }

11:如果想在解码途中修改解码参数需要。

//第一次初始化init.

        m_oDecoder = new NvDecoder();

        //初始化解码器(m_decodemaxWidth, m_decodemaxHeight必须填写为0,否则有些显卡解码出来的视频为绿色空数据)
        // Here set bLowLatency=true in the constructor.
        //Please don't use this flag except for low latency, it is harder to get 100% utilization of
        //hardware decoder with this flag set.
        //bLowLatency=true 低延迟模式,无缓存在解码器中
        lbReslut = m_oDecoder->NvDecoderInit(m_cuContext, m_pNvidiaCodec->m_sVideoWidth, m_pNvidiaCodec-            >m_sVideoHeight, false,
            cudaVideoCodec_H264, NULL, true, false, &m_cropRect, &m_resizeDim, m_decodemaxWidth, m_decodemaxHeight);

//第二次初始化

        //This function allow app to set decoder reconfig params
        //此功能允许app设置解码器重新配置参数(有些显卡不起作用)
        m_resizeDim.w = m_pNvidiaCodec->m_sVideoWidth;
        //m_resizeDim.h = m_pNvidiaCodec->m_sVideoHeight;
        //int ret = m_oDecoder.setReconfigParams(&m_cropRect,&m_resizeDim);

        //析构decoder
        if (m_oDecoder != NULL)
        {
            m_oDecoder->NvDecoderUninit();
            delete m_oDecoder;
            m_oDecoder = NULL;
        }

        m_oDecoder = new NvDecoder();

        //初始化解码器(m_decodemaxWidth, m_decodemaxHeight必须填写为0,否则有些显卡解码出来的视频为绿色空数据)
        // Here set bLowLatency=true in the constructor.
        //Please don't use this flag except for low latency, it is harder to get 100% utilization of
        //hardware decoder with this flag set.
        //bLowLatency=true 低延迟模式,无缓存在解码器中
        lbReslut = m_oDecoder->NvDecoderInit(m_cuContext, m_pNvidiaCodec->m_sVideoWidth, m_pNvidiaCodec->m_sVideoHeight, false,
            cudaVideoCodec_H264, NULL, true, false, &m_cropRect, &m_resizeDim, m_decodemaxWidth, m_decodemaxHeight);

12:码率控制情况

    if (m_encodeConfig.rcParams.rateControlMode == NV_ENC_PARAMS_RC_CONSTQP)
    {
        m_bitRateIn = 0;                            //CQP模式码率填写为0
        m_encodeConfig.rcParams.averageBitRate = m_bitRateIn; //指定用于编码的平均比特率(以位/秒为单位,例如 = 800000;  //输入编码的码率800k。
        m_encodeConfig.rcParams.vbvBufferSize = (m_encodeConfig.rcParams.averageBitRate * m_initializeParams.frameRateDen / m_initializeParams.frameRateNum) * 5; //buffer size. in bits. Set 0 to use the default VBV  buffer size.
        m_encodeConfig.rcParams.vbvInitialDelay = m_encodeConfig.rcParams.vbvBufferSize; //buffer size. in bits. Set 0 to use the default VBV  buffer size.
        m_encodeConfig.rcParams.maxBitRate = m_encodeConfig.rcParams.averageBitRate; //指定用于编码的最大比特率(以位/秒为单位,例如 = 800000;  //输入编码的码率800k。
        m_encodeConfig.rcParams.enableMinQP = 1;            //Set this to 1 if minimum QP used for rate control.
        m_encodeConfig.rcParams.enableMaxQP = 1;            //Set this to 1 if maximum QP used for rate control.
        m_qp_min = 15;
        m_encodeConfig.rcParams.minQP.qpIntra = m_qp_min; //Specifies the minimum QP used for rate control. Client must set NV_ENC_CONFIG::enableMinQP to 1.
        m_encodeConfig.rcParams.minQP.qpInterP = m_encodeConfig.rcParams.minQP.qpIntra;
        m_encodeConfig.rcParams.minQP.qpInterB = m_encodeConfig.rcParams.minQP.qpIntra;
        m_qp_max = 25;
        m_encodeConfig.rcParams.maxQP.qpIntra = m_qp_max; //Specifies the maximum QP used for rate control.Client must set NV_ENC_CONFIG::enableMaxQP to 1.
        m_encodeConfig.rcParams.maxQP.qpInterP = m_encodeConfig.rcParams.maxQP.qpIntra;
        m_encodeConfig.rcParams.maxQP.qpInterB = m_encodeConfig.rcParams.maxQP.qpIntra;
    }
    else if (m_encodeConfig.rcParams.rateControlMode == NV_ENC_PARAMS_RC_VBR_HQ)
    {
        //采用NV_ENC_PARAMS_RC_VBR_HQ模式用填写码率控制。
        m_bitRateIn = 800;
        m_bitRateIn *= 1024;  //传入的是Kbps不是bps,所以要*1024.                         
        m_encodeConfig.rcParams.averageBitRate = m_bitRateIn; //指定用于编码的平均比特率(以位/秒为单位,例如 = 800000;  //输入编码的码率800k。
        m_encodeConfig.rcParams.vbvBufferSize = (m_encodeConfig.rcParams.averageBitRate * m_initializeParams.frameRateDen / m_initializeParams.frameRateNum) * 5; //buffer size. in bits. Set 0 to use the default VBV  buffer size.;填写这个很重要,否则码率不会按照真实码率控制。
        m_encodeConfig.rcParams.vbvInitialDelay = m_encodeConfig.rcParams.vbvBufferSize; //buffer size. in bits. Set 0 to use the default VBV  buffer size.;填写这个很重要,否则码率不会按照真实码率控制。
        m_encodeConfig.rcParams.maxBitRate = m_encodeConfig.rcParams.averageBitRate; //指定用于编码的最大比特率(以位/秒为单位,例如 = 800000;  //输入编码的码率800k。
    }

13:

 

 

 

 

用于记录。

 

如有错误请指正:

交流请加QQ群:62054820
QQ:379969650.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/10141.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

宏景eHr 手机App应用二次开发(图文)

应用宏景eHr软件&#xff0c;其中将hr与通达OA进行了组织结构和人员信息的数据同步开发。 在后续的使用过程中&#xff0c;需要使用到手机App&#xff0c;但是宏景的app是完全按照应用的用户数来计费&#xff0c;这样算下来如果使用员工自助的话会是一笔不小的费用。 通过对系…

技巧|你的微信|QQ授权了多少网站与APP?查完吓一跳啊!

编辑&#xff5c;排版&#xff5c; 宅哥技术转载请联系商务合作给你开白名单来源&#xff1a;宅哥技术&#xff08;zg_jishu&#xff09; ---------♥--------- 前言 相信很多朋友都有在用QQ或者微信快捷登录某些应用或网站&#xff0c;每次的授权我们都没在意&#xff0c;但是…

[EF]事务管理+批量删除

using (var transaction context.Database.BeginTransaction()){try{ //删除合同主表信息FAContract fa GetFAContract();context.Entry(fa).State EntityState.Deleted;//删除合同子表var query (from fad in context.FAContractDetail where fad.FACID fa.ID …

python将图片base流保存为图片文件

python将图片base流保存为图片文件。 #!/usr/bin/python # -*- coding: UTF-8 -*- """ author: Roc-xb desc: 将图片base流保存为图片文件 """ import base64if __name__ __main__:source_img "data:image/jpg;base64,/9j/4AAQSkZJRgABAQ…

Flutter AppBar 简述

AppBar AppBar 显示在app的顶部&#xff0c;或者说 顶端栏&#xff0c;对应着 Android 的 Toolbar。如下图&#xff1a; 一个AppBar 的基本组成 &#xff11; 只有标题 无其他按钮 Widget buildDefaultBar(String title) {return appBar AppBar(//标题居中显示centerTitle: …

什么是JIT

1、动态编译(dynamic compilation)指的是“在运行时进行编译”;与之相对的是事前编译(ahead-of-time compilation,简称AOT),也叫静态编译(static compilation)。 2、JIT编译(just-in-time compilation)狭义来说是当某段代码即将第一次被执行时进行编译,因而叫“即…

搜索功能全流程解析

在产品中一般会分布着大大小小的搜索&#xff0c;以便提升用户的信息获取效率和信息消费的能力。本文作者全流程角度&#xff0c;对搜索功能进行了讲解&#xff0c;并从搜索流程中寻找提升体验的触点&#xff0c;一起来看一下吧。 在产品中因多功能诉求和业务复杂性等因素&…

Java面试题复习(1)

目录 1.mysql使用innodb引擎&#xff0c;请简述mysql索引的最左前缀&#xff0c;如何优化order by语句 2.在JVM内存模型中&#xff0c;为什么要区分新生去和老年代&#xff0c;对于新生代为什么要区分eden区和survial区&#xff1f; 3.常见的远程调用有几种 4.对于外部衔接的…

微信闪退Bug罪魁祸首竟是二维码引擎,附源代码分析

建议别尝试&#xff1a;转发这个二维码到群里&#xff0c;3秒后你会回来骂我&#xff08;抖m求骂&#xff09; 近日&#xff0c;网传微信识别上方二维码就会出现闪退BUG&#xff0c;小编也忍不住尝试了一下&#xff0c;果然&#xff0c;一识别该二维码微信立马就出现了闪退的现…

浅谈大模型时代的后台技术发展

1、前言 随着互联网的快速发展&#xff0c;大数据、人工智能、大模型等技术的兴起&#xff0c;大模型时代已经到来&#xff0c;也让后台工程面临着新的挑战和机遇&#xff1a; 大模型时代下&#xff0c;AI 对后台的计算能力和存储能力提出了更强要求&#xff0c;以满足更高的性…

陆奇最新演讲全文实录、完整PPT和视频:大模型带来的新范式

本文约34000字&#xff0c;建议阅读20分钟 陆奇博士以“新范式”为核心&#xff0c;分享了他对当前技术变革的观点。 面临一场快速到来的技术变革&#xff0c;比较有效的方法是去剖析&#xff1a;事物的内在结构&#xff0c;它的发展体系&#xff1b;什么是稳定不变的&#xff…

GPT上车只为人车交互?车企还在憋大招

ChatGPT改造汽车的序幕已经拉开&#xff0c;奔驰率先出手。 不久前&#xff0c;梅赛德斯-奔驰将ChatGPT集成到了车机上&#xff0c;开启了为期三个月的测试&#xff0c;结果显示&#xff0c;它的语音助手不仅可以完成简单地指令&#xff0c;还能进行连续多轮对话&#xff0c;理…

【Bard vs. GPT-4】谷歌一雪前耻!全新PaLM 2反超GPT-4,办公全家桶炸裂升级,Bard史诗进化

在人工智能领域,自然语言处理一直是一个备受关注的领域。近日,谷歌发布了全新的PaLM 2,这一新技术一经问世,就引起了广泛的关注。 【更多阅读】 Google I/O 2023 全球开发者大会 Reveal Event_禅与计算机程序设计艺术的博客-CSDN博客 【人工智能】谷歌的巴德聊天机器人向公…

亚马逊Q4利润下滑98%/ 盖茨:ChatGPT意义不亚于互联网诞生/ ICLR 2023放榜… 今日更多新鲜事在此...

日报君 发自 凹非寺量子位 | 公众号 QbitAI 大家好&#xff0c;今天是2月3日&#xff0c;星期五。 喜欢下班的打工人们&#xff0c;今天可以喜提本周第七次&#xff08;也是最后一次&#xff09;下班了&#xff01; 来跟日报君看看科技圈的今日大事吧。 ICLR 2023放榜&#xff…

抖音究竟对线下生意有没有价值?

&#x1f446;戳上方增长黑盒Growthbox点亮星标⭐深度研究不错过&#xff01; 主笔&#xff1a;邹小困 研究员&#xff1a;彬超、Emma 出品&#xff1a;增长黑盒研究组 01 防火防盗防抖音 互联网世界的后起之秀字节跳动&#xff0c;如同进击的巨人&#xff0c;在短短几年内就已…

网传微软苏州被一锅端/ ChatGPT要出App/ Meta元宇宙年亏900亿…今日更多新鲜事在此...

日报君 发自 凹非寺量子位 | 公众号 QbitAI 大家好&#xff0c;今天是2月2日星期四&#xff0c;7天的超长工作日就要过去啦&#xff5e; 快和日报君看看&#xff0c;今天科技圈有哪些新鲜事吧。 网传微软苏州一锅端&#xff0c;赔偿N12&#xff0c;后有人辟谣 近日&#xff0c;…

最实用的网上赚钱方法:这个方法真的很实用哦!

​  线上兼职 疫情居家期间有哪些比较好的线上兼职可以做&#xff1f; 线上批改作业兼职app有哪些&#xff1f; 1、爱作业 免费帮助小学一至六年级学生家长和老师批改数学作业。爱作业现支持应用题、选择题、判断题、填空题、解方程、竖式计算等小学阶段各类题型的识别和批改…

【ChatGPT】程序员的半个老师,今天简单讲讲使用心得

【ChatGPT】程序员的半个老师&#xff0c;简单讲讲今天的使用心得 程序员有个玩笑叫做复制黏贴&#xff0c;原来的百度谷歌只能是碎片层面&#xff0c;有了chatGPT&#xff0c;可以实现更深层次的复制黏贴&#xff0c;直接帮你写好整个方法和实现步骤&#xff0c;不再是简单的…

问了ChatGPT几个硬件问题

第一次试用ChatGPT&#xff0c;问了如下几个问题&#xff1a; 天线增益这种说法不准确&#xff0c;天线是无源器件&#xff0c;不可能放大&#xff1f; 请问如何检测锂电池的损耗&#xff0c;只量电压够不够&#xff1f; 画电路板lavout注意事项&#xff1f; 变压器EQ2514和EQ2…