Video FFMpeg使用指南 (v1.4.1)

更新时间:
复制 MD 格式

1. Overview

The PPU SDK is compatible with the Nvidia Video Codec SDK, enabling you to use FFmpeg plugins like cuvid, nvenc, and libnpp for hardware acceleration without code modifications.

FFmpeg 7.0.1

Requires nv-codec-headers 12.2.72.0.

Requires Video Codec SDK 12.2 or later.

This latest official release significantly optimizes parallelization performance for transcoding.

FFmpeg 6.1.2

Depends on nv-codec-headers 12.1.14.0.

Requires Video Codec SDK 12.1 or later.

This is currently the most widely used version and is a dependency for frameworks like PyAV.

2. Build from source

FFmpeg: https://github.com/FFmpeg/FFmpeg

nv-codec-headers: https://github.com/FFmpeg/nv-codec-headers

Important

Ensure that the FFmpeg version matches the corresponding nv-codec-headers version.

The following sections provide build instructions for FFmpeg 7.0 and FFmpeg 6.1. Before building, ensure you have installed the PPU SDK and configured its runtime environment by running envsetup.sh.

2.1. Prepare the FFmpeg code

## Clone nv-codec-headers version 12.2.72.0 to match FFmpeg 7.0
git clone --branch n12.2.72.0 --depth 1 https://github.com/FFmpeg/nv-codec-headers
cp nv-codec-headers/ffnvcodec.pc.in nv-codec-headers/ffnvcodec.pc
    
## Download FFmpeg
wget https://ffmpeg.org/releases/ffmpeg-7.0.1.tar.gz --no-check-certificate
tar -xzvf ffmpeg-7.0.1.tar.gz

Replace the dynlink_loader.h file in the nv-codec-headers/include/ffnvcodec/ directory with the provided version.

Note: The provided dynlink_loader.h file is modified to comment out certain unsupported or unnecessary interface functions:

    // tcuGLGetDevices_v2 *cuGLGetDevices;
    // tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage;
    // tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource;
    // tcuGraphicsMapResources *cuGraphicsMapResources;
    // tcuGraphicsUnmapResources *cuGraphicsUnmapResources;
    // tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray;
    // tcuGraphicsResourceGetMappedPointer *cuGraphicsResourceGetMappedPointer;

    ... ...

    // tcuArrayCreate *cuArrayCreate;
    // tcuArray3DCreate *cuArray3DCreate;
    // tcuArrayDestroy *cuArrayDestroy;

    // tcuEGLStreamProducerConnect *cuEGLStreamProducerConnect;
    // tcuEGLStreamProducerDisconnect *cuEGLStreamProducerDisconnect;
    // tcuEGLStreamConsumerDisconnect *cuEGLStreamConsumerDisconnect;
    // tcuEGLStreamProducerPresentFrame *cuEGLStreamProducerPresentFrame;
    // tcuEGLStreamProducerReturnFrame *cuEGLStreamProducerReturnFrame;
... ...
    // LOAD_SYMBOL(cuDevicePrimaryCtxRelease, tcuDevicePrimaryCtxRelease, "cuDevicePrimaryCtxRelease");
    LOAD_SYMBOL(cuDevicePrimaryCtxRelease, tcuDevicePrimaryCtxRelease, "cuDevicePrimaryCtxRelease_v2");
    // LOAD_SYMBOL(cuDevicePrimaryCtxSetFlags, tcuDevicePrimaryCtxSetFlags, "cuDevicePrimaryCtxSetFlags");
    LOAD_SYMBOL(cuDevicePrimaryCtxSetFlags, tcuDevicePrimaryCtxSetFlags, "cuDevicePrimaryCtxSetFlags_v2");
    
    ... ...
    // LOAD_SYMBOL(cuDevicePrimaryCtxReset, tcuDevicePrimaryCtxReset, "cuDevicePrimaryCtxReset");
    LOAD_SYMBOL(cuDevicePrimaryCtxReset, tcuDevicePrimaryCtxReset, "cuDevicePrimaryCtxReset_v2");

    ... ... 

    // LOAD_SYMBOL(cuLinkCreate, tcuLinkCreate, "cuLinkCreate");
    LOAD_SYMBOL(cuLinkCreate, tcuLinkCreate, "cuLinkCreate_v2");
    // LOAD_SYMBOL(cuLinkAddData, tcuLinkAddData, "cuLinkAddData");
    LOAD_SYMBOL(cuLinkAddData, tcuLinkAddData, "cuLinkAddData_v2");

    ... ... 
        
    // LOAD_SYMBOL(cuModuleGetGlobal, tcuModuleGetGlobal, "cuModuleGetGlobal");
    LOAD_SYMBOL(cuModuleGetGlobal, tcuModuleGetGlobal, "cuModuleGetGlobal_v2");
    
    ... ...

    // LOAD_SYMBOL(cuGLGetDevices, tcuGLGetDevices_v2, "cuGLGetDevices_v2");
    // LOAD_SYMBOL(cuGraphicsGLRegisterImage, tcuGraphicsGLRegisterImage, "cuGraphicsGLRegisterImage");
    // LOAD_SYMBOL(cuGraphicsUnregisterResource, tcuGraphicsUnregisterResource, "cuGraphicsUnregisterResource");
    // LOAD_SYMBOL(cuGraphicsMapResources, tcuGraphicsMapResources, "cuGraphicsMapResources");
    // LOAD_SYMBOL(cuGraphicsUnmapResources, tcuGraphicsUnmapResources, "cuGraphicsUnmapResources");
    // LOAD_SYMBOL(cuGraphicsSubResourceGetMappedArray, tcuGraphicsSubResourceGetMappedArray, "cuGraphicsSubResourceGetMappedArray");
    // LOAD_SYMBOL(cuGraphicsResourceGetMappedPointer, tcuGraphicsResourceGetMappedPointer, "cuGraphicsResourceGetMappedPointer_v2");

    ... ...
    // LOAD_SYMBOL(cuArrayCreate, tcuArrayCreate, "cuArrayCreate_v2");
    // LOAD_SYMBOL(cuArray3DCreate, tcuArray3DCreate, "cuArray3DCreate_v2");
    // LOAD_SYMBOL(cuArrayDestroy, tcuArrayDestroy, "cuArrayDestroy");

    // LOAD_SYMBOL_OPT(cuEGLStreamProducerConnect, tcuEGLStreamProducerConnect, "cuEGLStreamProducerConnect");
    // LOAD_SYMBOL_OPT(cuEGLStreamProducerDisconnect, tcuEGLStreamProducerDisconnect, "cuEGLStreamProducerDisconnect");
    // LOAD_SYMBOL_OPT(cuEGLStreamConsumerDisconnect, tcuEGLStreamConsumerDisconnect, "cuEGLStreamConsumerDisconnect");
    // LOAD_SYMBOL_OPT(cuEGLStreamProducerPresentFrame, tcuEGLStreamProducerPresentFrame, "cuEGLStreamProducerPresentFrame");
    // LOAD_SYMBOL_OPT(cuEGLStreamProducerReturnFrame, tcuEGLStreamProducerReturnFrame, "cuEGLStreamProducerReturnFrame");

2.2. Build FFmpeg

mkdir output
cd ffmpeg-7.0.1
export PKG_CONFIG_PATH=$(pwd)/../nv-codec-headers:$PKG_CONFIG_PATH
./configure --enable-shared \
    --enable-nonfree --enable-gpl \
    --enable-cuvid --enable-nvenc --enable-libnpp \
    --extra-cflags="-I$(pwd)/../nv-codec-headers/include -I$CUDA_HOME/include" \
    --extra-ldflags="-L$CUDA_HOME/lib64" \
    --prefix=$(pwd)/../output
    
make -j16 && make install
Note

The --prefix flag specifies the installation directory. If this flag is not set, FFmpeg installs to the /usr/local directory by default.

After a successful build, the installation directory will contain bin, include, lib, and share directories. The lib directory contains a pkgconfig directory. To check the version, run pkg-config --modversion libavcodec.

3. Run examples

cd ../output
export LD_LIBRARY_PATH=lib:$LD_LIBRARY_PATH

# Test hardware decoding with cuvid
bin/ffmpeg -vcodec h264_cuvid -i 640x360_y8c8.h264 -frames 10 -y out-dec-h264.yuv

# Test transcoding with cuvid and nvenc
bin/ffmpeg -hwaccel_output_format cuda -vcodec h264_cuvid -i 640x360_y8c8.h264 -vcodec hevc_nvenc -r 30.0 -b:v 15000000 -preset p4 output.hevc

# Test transcoding with cuvid and nvenc, and resizing with libnpp support
bin/ffmpeg -hwaccel_output_format cuda -vcodec h264_cuvid -i input.h264 -vcodec hevc_nvenc -frames 300 -preset p7 -acodec copy -y output.h265

4. FAQ

Q: What hardware acceleration capabilities does FFmpeg support on the PPU?

A: The PPU supports hardware decoding for h264_cuvid, hevc_cuvid, av1_cuvid, and vp9_cuvid, and hardware encoding for h264_nvenc, hevc_nvenc, and av1_nvenc. It also supports libnpp-accelerated operations such as transpose, color conversion, and scaling. A patch for AVS2 decoding support is available upon request.

5. Appendix A: Building FFmpeg 6.1

FFmpeg 7.0 introduces significant architectural changes that optimize parallelization for transcoding and multistreaming, leading to noticeable performance improvements. However, if you need to use FFmpeg 6.1 for compatibility reasons, follow the instructions below to build the older version.

## Clone nv-codec-headers version 12.1.14.0 to match FFmpeg 6.1
git clone --branch n12.1.14.0 --depth 1 https://github.com/FFmpeg/nv-codec-headers
cp nv-codec-headers/ffnvcodec.pc.in nv-codec-headers/ffnvcodec.pc
    
## Download FFmpeg
wget https://ffmpeg.org/releases/ffmpeg-6.1.2.tar.gz --no-check-certificate
tar -xzvf ffmpeg-6.1.2.tar.gz

As with FFmpeg 7.0, you must modify the nv-codec-headers/include/ffnvcodec/dynlink_loader.h file to comment out unsupported or unnecessary interface functions. The build and run commands are identical to those for FFmpeg 7.0.

mkdir output
cd ffmpeg-6.1.2
export PKG_CONFIG_PATH=$(pwd)/../nv-codec-headers:$PKG_CONFIG_PATH
./configure --enable-shared \
    --enable-nonfree --enable-gpl \
    --enable-cuvid --enable-nvenc --enable-libnpp \
    --extra-cflags="-I$(pwd)/../nv-codec-headers/include -I$CUDA_HOME/include" \
    --extra-ldflags="-L$CUDA_HOME/lib64" \
    --prefix=$(pwd)/../output
    
make -j16 && make install

6. Appendix B: The FFmpeg ecosystem

6.1. ffmpeg-python

Official repository: https://github.com/kkroening/ffmpeg-python

ffmpeg-python essentially assembles the parameters you provide and then calls the ffmpeg program for execution. Therefore, your runtime environment must have ffmpeg installed, or you must set the paths to the bin and lib directories of your ffmpeg installation. Because the PPU Video Codec supports only cuvid decoding, configuring only hwaccel='cuda' is insufficient. You must also configure the cuvid parameter, as shown in the following example.

Example:

import ffmpeg
import sys

probe = ffmpeg.probe('input.mp4')
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)

if video_stream is None or video_stream['codec_name'] is None:
    sys.exit()
    
if video_stream['codec_name'] in ['h264', 'hevc', 'av1', 'vp9']:
    out, _ = (
        ffmpeg.input('input.mp4', vcodec = video_stream['codec_name']+'_cuvid')
        .output('output.rgb', format='rawvideo', pix_fmt='rgb24')
        .run()
    )
else:
    out, _ = (
        ffmpeg.input('input.mp4')
        .output('output.rgb', format='rawvideo', pix_fmt='rgb24')
        .run()

Since ffmpeg-python is essentially a wrapper for FFmpeg commands, it can also process data from pipe: inputs and outputs (stdin and stdout). However, this approach is not recommended because it requires a data copy between the CPU and GPU, which introduces performance overhead.

6.2. PyAV

Torchvision supports three backends: PyAV, video_reader, and cuda. The default backend is PyAV. The video_reader backend performs pure software decoding on the CPU, while the cuda backend uses a GPU decoder. PyAV relies on FFmpeg for codec operations. Therefore, PyAV's hardware acceleration capabilities depend on the underlying FFmpeg installation. You must build and install an FFmpeg version with hardware acceleration enabled.

Note

As of this writing, PyAV does not support FFmpeg 7.0. You must build the FFmpeg 6.1 version as described in Appendix A.

Official repository: https://github.com/PyAV-Org/PyAV

Call path 1: PyTorch->Torchvision->PyAV->FFmpeg

Hardware decoding support (requires modifying Torchvision code in torchvision/io/video_reader.py):

--- a/torchvision/io/video_reader.py
+++ b/torchvision/io/video_reader.py
@@ -280,6 +280,13 @@ class VideoReader:
         if self.backend == "pyav":
             stream_type = stream.split(":")[0]
             stream_id = 0 if len(stream.split(":")) == 1 else int(stream.split(":")[1])
+            video_stream = self.container.streams.video[stream_id]
+            # Setting up the codec with cuvid
+            if video_stream.codec.name in ('h264', 'hevc', 'av1', 'vp9'):
+                codec_name = f'{video_stream.codec.name}_cuvid'
+            else:
+                codec_name = video_stream.codec.name  # Fallback to software decoding
+            video_stream.codec_context = av.codec.CodecContext.create(codec_name, 'r')
             self.pyav_stream = {stream_type: stream_id}
             self._c = self.container.decode(**self.pyav_stream)

By default, PyAV outputs data in TCHW format.

Hardware encoding support (does not require Torchvision code modification, but you must specify the nvenc codec in your call):

from torchvision.io import write_video
write_video(save_path, frames, fps=fps, video_codec="h264_nvenc")  # Specify the encoder here. We currently support h264_nvenc, av1_nvenc, and hevc_nvenc.

Call path 2: PyTorch->Torchaudio->PyAV->FFmpeg

Torchaudio also supports PyAV as a backend for hardware decoding. No code modifications are needed, but you must explicitly specify the cuvid codec:

import torch
import torchaudio

from torchaudio.io import StreamReader
from torchaudio.utils import ffmpeg_utils

s = StreamReader(src)
s.add_video_stream(int(s.get_src_stream_info(0).frame_rate), decoder="h264_cuvid") # You must specify the cuvid codec here. We currently support h264_cuvid, av1_cuvid, hevc_cuvid, and vp9_cuvid.
s.fill_buffer()
(video,) = s.pop_chunks()

Note: You can use torchvision.get_video_backend() to obtain the current backend and torchvision.set_video_backend("pyav") to set the default backend for torchvision. However, the "cuda" backend is not supported in the default installation of torchvision. You must manually compile and install torchvision to enable it.

6.3. torchcodec

Official documentation: https://ai-gerrit.eng.t-head.cn/c/acvid/+/67622

Project repository: https://github.com/pytorch/torchcodec

Call path: PyTorch->torchcodec->FFmpeg

torchcodec v0.1 has been released and now includes support for hardware video decoding acceleration from SAIL.

Installation:

  1. Install the dependency: FFmpeg (both FFmpeg 6.1 and 7.0 are supported).

  2. Build from source.

git clone https://github.com/pytorch/torchcodec.git
cd torchcodec/
git apply torchcodec.diff
git checkout -b 0.1.0 origin/release/0.1.0
ENABLE_CUDA=1 I_CONFIRM_THIS_IS_NOT_A_LICENSE_VIOLATION=1 pip install . --no-build-isolation

The torchcodec source code requires two modifications to run on the PPU:

(1) Modify the decode function to use cuvid for decoding, as the default method is not supported.

(2) Change sw_scale to scale_npp to enable libnpp acceleration.

diff --git a/src/torchcodec/decoders/_core/CudaDevice.cpp b/src/torchcodec/decoders/_core/CudaDevice.cpp
index 5d48d26..026c0e3 100644
--- a/src/torchcodec/decoders/_core/CudaDevice.cpp
+++ b/src/torchcodec/decoders/_core/CudaDevice.cpp
@@ -181,6 +181,7 @@ void initializeContextOnCuda(
   torch::Tensor dummyTensorForCudaInitialization = torch::empty(
       {1}, torch::TensorOptions().dtype(torch::kUInt8).device(device));
   codecContext->hw_device_ctx = getCudaContext(device);
+  codecContext->hw_frames_ctx = av_buffer_ref(codecContext->hw_device_ctx);
   return;
 }

diff --git a/src/torchcodec/decoders/_core/VideoDecoder.cpp b/src/torchcodec/decoders/_core/VideoDecoder.cpp
index df8aaae..131ef1a 100644
--- a/src/torchcodec/decoders/_core/VideoDecoder.cpp
+++ b/src/torchcodec/decoders/_core/VideoDecoder.cpp
@@ -312,8 +312,9 @@ void VideoDecoder::initializeFilterGraphForStream(
   }
   const AVFilter* buffersrc = avfilter_get_by_name("buffer");
   const AVFilter* buffersink = avfilter_get_by_name("buffersink");
-  enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB24, AV_PIX_FMT_NONE};
+  enum AVPixelFormat pix_fmts[] = {AV_PIX_FMT_RGB24, AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE};
   const StreamInfo& activeStream = streams_[streamIndex];
+  AVBufferSrcParameters *par_in = av_buffersrc_parameters_alloc();

   AVCodecContext* codecContext = activeStream.codecContext.get();
   char args[512];
@@ -341,6 +342,9 @@ void VideoDecoder::initializeFilterGraphForStream(
         std::string("Failed to create filter graph: ") + args + ": " +
         getFFMPEGErrorStringFromErrorCode(ffmpegStatus));
   }
+  par_in->hw_frames_ctx = codecContext->hw_frames_ctx;
+  av_buffersrc_parameters_set(filterState.sourceContext, par_in);
+  av_freep(&par_in);
   ffmpegStatus = avfilter_graph_create_filter(
       &filterState.sinkContext,
       buffersink,
@@ -364,6 +368,8 @@ void VideoDecoder::initializeFilterGraphForStream(
         "Failed to set output pixel formats: " +
         getFFMPEGErrorStringFromErrorCode(ffmpegStatus));
   }
   UniqueAVFilterInOut outputs(avfilter_inout_alloc());
   UniqueAVFilterInOut inputs(avfilter_inout_alloc());
   outputs->name = av_strdup("in");
@@ -383,7 +389,7 @@ void VideoDecoder::initializeFilterGraphForStream(
   std::snprintf(
       description,
       sizeof(description),
-      "scale=%d:%d:sws_flags=bilinear",
+      "scale_npp=%d:%d",
       width,
       height);
   AVFilterInOut* outputsTmp = outputs.release();
@@ -436,6 +442,13 @@ void VideoDecoder::addVideoStreamDecoder(
   if (streamNumber < 0) {
     throw std::invalid_argument("No valid stream found in input file.");
   }
+  const char* cuvid_suffix = "_cuvid";
+  size_t cuvid_length = std::strlen(codec->name) + std::strlen(cuvid_suffix) + 1;
+  char* cuvid_name = new char[cuvid_length];
+  std::strcpy(cuvid_name, codec->name);
+  std::strcat(cuvid_name, cuvid_suffix);
+  codec = avcodec_find_decoder_by_name(cuvid_name);
+  delete[] cuvid_name;
   TORCH_CHECK(codec != nullptr);
   StreamInfo& streamInfo = streams_[streamNumber];
   streamInfo.streamIndex = streamNumber;
@@ -449,9 +462,6 @@ void VideoDecoder::addVideoStreamDecoder(
   AVCodecContext* codecContext = avcodec_alloc_context3(codec);
   codecContext->thread_count = options.ffmpegThreadCount.value_or(0);
   TORCH_CHECK(codecContext != nullptr);
-  streamInfo.codecContext.reset(codecContext);
-  int retVal = avcodec_parameters_to_context(
-      streamInfo.codecContext.get(), streamInfo.stream->codecpar);
   if (options.device.type() == torch::kCPU) {
     // No more initialization needed for CPU.
   } else if (options.device.type() == torch::kCUDA) {
@@ -459,11 +469,18 @@ void VideoDecoder::addVideoStreamDecoder(
   } else {
     TORCH_CHECK(false, "Invalid device type: " + options.device.str());
   }
+  streamInfo.codecContext.reset(codecContext);
+  int retVal = avcodec_parameters_to_context(
+      streamInfo.codecContext.get(), streamInfo.stream->codecpar);
   TORCH_CHECK_EQ(retVal, AVSUCCESS);
   retVal = avcodec_open2(streamInfo.codecContext.get(), codec, nullptr);
   if (retVal < AVSUCCESS) {
     throw std::invalid_argument(getFFMPEGErrorStringFromErrorCode(retVal));
   }
+  codecContext->hw_frames_ctx = av_buffer_ref(codecContext->hw_device_ctx);
+  AVHWFramesContext* hwframe_ctx = (AVHWFramesContext*)codecContext->hw_frames_ctx->data;
+  // in avcodec_open2, cuvid will not set sw_format until sequence callback in avcodec_send_packet()
+  hwframe_ctx->sw_format = codecContext->sw_pix_fmt;
   codecContext->time_base = streamInfo.stream->time_base;
   activeStreamIndices_.insert(streamNumber);
   updateMetadataWithCodecContext(streamInfo.streamIndex, codecContext);

Usage example:

LD_PRELOAD=/usr/local/PPU_SDK/CUDA_SDK/lib64/libnppc.so python sample_torchcodec.py
import torch

print(f"{torch.__version__=}")
print(f"{torch.cuda.is_available()=}")
print(f"{torch.cuda.get_device_properties(0)=}")

import torchcodec
from torchcodec.decoders import VideoDecoder

decoder = VideoDecoder("sample.mp4", device="cuda")
frame = decoder[0]

6.4. imageio-ffmpeg

imageio-ffmpeg is an imageio plugin that wraps FFmpeg. Like ffmpeg-python, it constructs and executes FFmpeg commands from your input. Therefore, your runtime environment must have FFmpeg installed, or the FFmpeg bin and lib directories must be in your system's path. Not recommended. The official documentation states: You should probably use PyAV instead; it is faster and offers more features.

imageio-ffmpeg: https://github.com/imageio/imageio-ffmpeg

Usage example:

import imageio

video_reader = imageio.get_reader('input.mp4', 'ffmpeg', ffmpeg_params=['-c:v', 'h264_cuvid'])
frame_list = []
for i, frame in enumerate(video_reader):
    frame_list.append(frame)
imageio.mimsave('out.mp4', frame_list)

6.5. Decord

The default Decord installation is CPU-only and lacks hardware acceleration. To enable this feature, you must build Decord from source.

Decord depends on FFmpeg, but only uses its demux capabilities. You do not need to build a custom version of FFmpeg with hardware acceleration.

An issue in older FFmpeg versions can cause a deadlock in Decord. This was resolved in FFmpeg 7.0. Therefore, you must build and install FFmpeg 7.0 or a later version.

Download Decord:

git clone -b v0.6.0 --depth 1 --recursive https://github.com/dmlc/decord
cd decord
git apply decord_ffmpeg7.patch

The patch mainly updates Decord's FFmpeg dependency from version 4.2 to 7.0 or later:

decord_ffmpeg7.patch

+++ b/src/video/nvcodec/cuda_threaded_decoder.cc
@@ -17,7 +17,7 @@ namespace decord {
 namespace cuda {
 using namespace runtime;

-CUThreadedDecoder::CUThreadedDecoder(int device_id, AVCodecParameters *codecpar, AVInputFormat *iformat)
+CUThreadedDecoder::CUThreadedDecoder(int device_id, AVCodecParameters *codecpar, const AVInputFormat *iformat)
     : device_id_(device_id), stream_({device_id, false}), device_{}, ctx_{}, parser_{}, decoder_{},
     pkt_queue_{}, frame_queue_{},
     run_(false), frame_count_(0), draining_(false),
@@ -70,7 +70,7 @@ CUThreadedDecoder::CUThreadedDecoder(int device_id, AVCodecParameters *codecpar,
     }
 }

-void CUThreadedDecoder::InitBitStreamFilter(AVCodecParameters *codecpar, AVInputFormat *iformat) {
+void CUThreadedDecoder::InitBitStreamFilter(AVCodecParameters *codecpar, const AVInputFormat *iformat) {
     const char* bsf_name = nullptr;
     if (AV_CODEC_ID_H264 == codecpar->codec_id) {
         // H.264
diff --git a/src/video/nvcodec/cuda_threaded_decoder.h b/src/video/nvcodec/cuda_threaded_decoder.h
index d7e6fcd..61958a1 100644
--- a/src/video/nvcodec/cuda_threaded_decoder.h
+++ b/src/video/nvcodec/cuda_threaded_decoder.h
@@ -46,7 +46,7 @@ class CUThreadedDecoder final : public ThreadedDecoderInterface {
     using FrameOrderQueuePtr = std::unique_ptr<FrameOrderQueue>;

     public:
-        CUThreadedDecoder(int device_id, AVCodecParameters *codecpar, AVInputFormat *iformat);
+        CUThreadedDecoder(int device_id, AVCodecParameters *codecpar, const AVInputFormat *iformat);
         void SetCodecContext(AVCodecContext *dec_ctx, int width = -1, int height = -1, int rotation = 0);
         bool Initialized() const;
         void Start();
@@ -70,7 +70,7 @@ class CUThreadedDecoder final : public ThreadedDecoderInterface {
         void LaunchThreadImpl();
         void RecordInternalError(std::string message);
         void CheckErrorStatus();
-        void InitBitStreamFilter(AVCodecParameters *codecpar, AVInputFormat *iformat);
+        void InitBitStreamFilter(AVCodecParameters *codecpar, const AVInputFormat *iformat);

         int device_id_;
         CUStream stream_;
diff --git a/src/video/video_reader.cc b/src/video/video_reader.cc
index af4858d..99c9635 100644
--- a/src/video/video_reader.cc
+++ b/src/video/video_reader.cc
@@ -145,7 +145,7 @@ VideoReader::~VideoReader(){

 void VideoReader::SetVideoStream(int stream_nb) {
     if (!fmt_ctx_) return;
-    AVCodec *dec;
+    const AVCodec *dec;
     int st_nb = av_find_best_stream(fmt_ctx_.get(), AVMEDIA_TYPE_VIDEO, stream_nb, -1, &dec, 0);
     // LOG(INFO) << "find best stream: " << st_nb;
     CHECK_GE(st_nb, 0) << "ERROR cannot find video stream with wanted index: " << stream_nb;

Build Decord:

mkdir build && cd build
cmake .. -DUSE_CUDA=ON -DCMAKE_BUILD_TYPE=Release
make

Install Decord:

cd ../python
python3 setup.py install --user

Usage example:

import decord
import torch
from decord import gpu, cpu

video_path="input.mp4"
vr = decord.VideoReader(video_path, ctx=gpu(0))
nframes = 40
total_frames = 500
idx = torch.linspace(0, total_frames - 1, nframes).round().long().tolist()
video = vr.get_batch(idx).asnumpy()