[Libav-user] Using H264 Hardware decoding on windows = several problems

Jérôme SALAYET jerome.salayet at hymatom.fr
Thu Sep 6 19:28:34 EEST 2018


Hello, I use FFMPEG 4.0.2 to decode h264/hevc video streams from RTSP cameras on a Windows system.
But, due to the CPU limitation, I want to use the hwdevice to decompress.


Based on hw_decode.c sample file I try to use cuda or d3d11va.

I can use cuda working

My code is :

AVCodecHWConfig*    pHWConfig ;
AVBufferRef*        pHWDeviceCtx;
enum AVPixelFormat  HwPixfmt;
AVHWDeviceType      HwDeviceType;
AVPixelFormat       hwPixelFormat;
AVHWDeviceType      hwDeviceType;
int                 methods;
AVFrame*            pSWFrame;

////////////////////////////////////////////////////////////////////////////////
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts)
////////////////////////////////////////////////////////////////////////////////
{ const enum AVPixelFormat *p;

  for (p = pix_fmts; *p != -1; p++)
   { if (*p == AV_PIX_FMT_RGB0) //hw_pix_fmt for CUDA
      return *p;
   }

  printf("Failed to get HW surface format\r\n");
  return AV_PIX_FMT_NONE;
}

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

  if ((err = av_hwdevice_ctx_create(&pHWDeviceCtx, type, NULL, NULL, 0)) < 0)
   { return err;
   }
  if (av_hwdevice_ctx_init(m_lpHWDeviceCtx) < 0)
   { return -1;
   }
  ctx->hw_device_ctx = lpfnav_buffer_ref(m_lpHWDeviceCtx);
  return err;
}
///////////////////////////////////////////////////////////////
in the decode part (1 per decoding Thread)
////////////////////////////////////////////////////////////////
...
pCodec = avcodec_find_decoder_by_name("h264_cuvid");
if (pCodec != NULL)
  { hwPixelFormat = AV_PIX_FMT_NONE;
    hwDeviceType  = AV_HWDEVICE_TYPE_NONE;
    methods = 0;

    for (int i = 0;; i++)
     { const AVCodecHWConfig* hw_config = lpfnavcodec_get_hw_config(m_lpCodec, i);
       if (!hw_config)
        { pCodec = NULL;
          break;
        }
       if (hw_config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && hw_config->device_type == AV_HWDEVICE_TYPE_CUDA)
        { HwPixfmt = hw_config->pix_fmt;
          pHWConfig = (AVCodecHWConfig*)hw_config;

          if (!(hw_config->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL))
           { // Second Options
             methods      = hw_config->methods;
             HwPixfmt     = hw_config->pix_fmt;
             hwDeviceType = hw_config->device_type;
             break;
           }
          break;
        }
     }
    enum AVHWDeviceType iterateType = AV_HWDEVICE_TYPE_NONE;
    while ((iterateType  = av_hwdevice_iterate_types (iterateType)) != AV_HWDEVICE_TYPE_NONE)
     { if (iterateType == AV_HWDEVICE_TYPE_CUDA)
        { HwDeviceType = iterateType;
        }
     }
  ....

     if ((pCodec != NULL) && (pCodecCtx == NULL))
      { pCodecCtx = avcodec_alloc_context3(pCodec);
        if (pCodecCtx != NULL)
         { if (pHWConfig != NULL)
            { pCodecCtx->get_format = get_hw_format;
                    hw_decoder_init( pCodecCtx, HwDeviceType)
            }

           // Init CodecContext
           if (pCodecCtx->width == 0)
            { pCodecCtx->pix_fmt           = AV_PIX_FMT_YUV420P;
              pCodecCtx->codec_type        = AVMEDIA_TYPE_VIDEO;
              pCodecCtx->codec_id          = AV_CODEC_ID_H264;
              pCodecCtx->coded_width       = 1280
              pCodecCtx->coded_height      = 720
              pCodecCtx->width             = 1280
              pCodecCtx->height            = 720
              pCodecCtx->thread_count      = 1;
              pCodecCtx->thread_type       = FF_THREAD_FRAME|FF_THREAD_SLICE;
              pCodecCtx->err_recognition   = AV_EF_EXPLODE;
            }
         }
      }
     if ((pCodecCtx!=NULL) && (pCodec!=NULL)&&(!avcodec_is_open(pCodecCtx)))
      { if ( pCodec != NULL)
         { ......
           iResult = avcodec_open2( pCodecCtx, pCodec, &optionsDict);

               iRes = avcodec_send_packet( pCodecCtx, &pPacket);
           if (iRes>=0)
            { iRes = avcodec_receive_frame( pCodecCtx, pFrame );
              if (iRes == AVERROR(EAGAIN) || iRes == AVERROR_EOF)
               { av_frame_free(&pFrame);
               }
              else
               if (iRes < 0)
                { printf("receive_frame Error while decoding\r\n");
                }
               else
               if (pFrame->format == HwPixfmt)
                { sw_frame = av_frame_alloc();
                  if ((iRes = av_hwframe_transfer_data( sw_frame, pFrame, 0)) < 0)
                   { tmp_frame = pFrame;
                   }
                  else
                   { tmp_frame = sw_frame;
                   }
                }
            ...

When I set the av_log, I can see when it works :

[h264_cuvid @ 0e389540] Format cuda chosen by get_format().
[h264_cuvid @ 0e389540] Loaded lib: nvcuvid.dll
[h264_cuvid @ 0e389540] Loaded sym: cuvidGetDecoderCaps
[h264_cuvid @ 0e389540] Loaded sym: cuvidCreateDecoder
[h264_cuvid @ 0e389540] Loaded sym: cuvidDestroyDecoder
[h264_cuvid @ 0e389540] Loaded sym: cuvidDecodePicture
[h264_cuvid @ 0e389540] Loaded sym: cuvidMapVideoFrame
[h264_cuvid @ 0e389540] Loaded sym: cuvidUnmapVideoFrame
[h264_cuvid @ 0e389540] Loaded sym: cuvidCtxLockCreate
[h264_cuvid @ 0e389540] Loaded sym: cuvidCtxLockDestroy
[h264_cuvid @ 0e389540] Loaded sym: cuvidCtxLock
[h264_cuvid @ 0e389540] Loaded sym: cuvidCtxUnlock
[h264_cuvid @ 0e389540] Loaded sym: cuvidCreateVideoSource
[h264_cuvid @ 0e389540] Loaded sym: cuvidCreateVideoSourceW
[h264_cuvid @ 0e389540] Loaded sym: cuvidDestroyVideoSource
[h264_cuvid @ 0e389540] Loaded sym: cuvidSetVideoSourceState
[h264_cuvid @ 0e389540] Loaded sym: cuvidGetVideoSourceState
[h264_cuvid @ 0e389540] Loaded sym: cuvidGetSourceVideoFormat
[h264_cuvid @ 0e389540] Loaded sym: cuvidGetSourceAudioFormat
[h264_cuvid @ 0e389540] Loaded sym: cuvidCreateVideoParser
[h264_cuvid @ 0e389540] Loaded sym: cuvidParseVideoData
[h264_cuvid @ 0e389540] Loaded sym: cuvidDestroyVideoParser
[AVHWDeviceContext @ 0e36d800] Loaded lib: nvcuda.dll
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuInit
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuDeviceGetCount
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuDeviceGet
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuDeviceGetName
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuDeviceComputeCapability
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuCtxCreate_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuCtxSetLimit
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuCtxPushCurrent_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuCtxPopCurrent_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuCtxDestroy_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuMemAlloc_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuMemFree_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuMemcpy2D_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuMemcpy2DAsync_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGetErrorName
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGetErrorString
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuStreamCreate
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuStreamQuery
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuStreamSynchronize
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuStreamDestroy_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuStreamAddCallback
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuEventCreate
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuEventDestroy_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuEventSynchronize
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuEventQuery
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuEventRecord
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGLGetDevices_v2
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGraphicsGLRegisterImage
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGraphicsUnregisterResource
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGraphicsMapResources
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGraphicsUnmapResources
[AVHWDeviceContext @ 0e36d800] Loaded sym: cuGraphicsSubResourceGetMappedArray
[AVBSFContext @ 0e3befc0] The input looks like it is Annex B already
[h264_cuvid @ 0e389540] CUVID capabilities for h264_cuvid:
[h264_cuvid @ 0e389540] 8 bit: supported: 1, min_width: 48, max_width: 4096, min_height: 16, max_height: 4096
[h264_cuvid @ 0e389540] 10 bit: supported: 0, min_width: 0, max_width: 0, min_height: 0, max_height: 0
[h264_cuvid @ 0e389540] 12 bit: supported: 0, min_width: 0, max_width: 0, min_height: 0, max_height: 0
[h264_cuvid @ 0e389540] Invalid pkt_timebase, passing timestamps as-is.
[h264_cuvid @ 0e389540] Format cuda chosen by get_format().
[h264_cuvid @ 0e389540] Formats: Original: cuda | HW: cuda | SW: nv12

But when I want to use it for the seventh time, I have an error, is there a limitation ?
[AVHWDeviceContext @ 0e3ca9c0] Error creating a CUDA context


So I want to try d3d11va instead...
Just changing some part in my code

////////////////////////////////////////////////////////////////////////////////
static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts)
////////////////////////////////////////////////////////////////////////////////
{ const enum AVPixelFormat *p;

  for (p = pix_fmts; *p != -1; p++)
   { if (*p == AV_PIX_FMT_RGB0) // is it a hw_pix_fmt for d3d11va?
      return *p;
   }

  printf("Failed to get HW surface format\r\n");
  return AV_PIX_FMT_NONE;
}

And

///////////////////////////////////////////////////////////////
in the decode part (1 per decoding Thread)
////////////////////////////////////////////////////////////////
...
pCodec = avcodec_find_decoder_by_name("h264 ");
if (pCodec != NULL)
  { hwPixelFormat = AV_PIX_FMT_NONE;
    hwDeviceType  = AV_HWDEVICE_TYPE_NONE;
    methods = 0;

    for (int i = 0;; i++)
     { const AVCodecHWConfig* hw_config = lpfnavcodec_get_hw_config(m_lpCodec, i);
       if (!hw_config)
        { pCodec = NULL;
          break;
        }
       if (hw_config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && hw_config->device_type == AV_HWDEVICE_TYPE_D3D11VA)
        { HwPixfmt = hw_config->pix_fmt;
          pHWConfig = (AVCodecHWConfig*)hw_config;

          if (!(hw_config->methods & AV_CODEC_HW_CONFIG_METHOD_INTERNAL))
           { // Second Options
             methods      = hw_config->methods;
             HwPixfmt     = hw_config->pix_fmt;
             hwDeviceType = hw_config->device_type;
             break;
           }
          break;
        }
     }
    enum AVHWDeviceType iterateType = AV_HWDEVICE_TYPE_NONE;
    while ((iterateType  = av_hwdevice_iterate_types (iterateType)) != AV_HWDEVICE_TYPE_NONE)
     { if (iterateType == AV_HWDEVICE_TYPE_DXVA2)
        { HwDeviceType = iterateType;
        }
     }


But in this case, I have ffmpeg error saying...
[h264 @ 0e4d7fc0] Format d3d11va_vld chosen by get_format().
[h264 @ 0e4d7fc0] Format d3d11va_vld requires hwaccel initialisation.

How can I make the hwaccel initialisation ???

[h264 @ 0e4d7fc0] A hardware frames or device context is required for hardware accelerated decoding.
[h264 @ 0e4d7fc0] Failed setup for format d3d11va_vld: hwaccel initialisation returned error.
[h264 @ 0e4d7fc0] Format d3d11va_vld not usable, retrying get_format() without it.


Thanks for your answers...

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://ffmpeg.org/pipermail/libav-user/attachments/20180906/2126ed6c/attachment.html>


More information about the Libav-user mailing list