ffmpeg使用NVIDIA GPU硬件编解码

在Ubuntu14.04版本上编译安装ffmpeg3.4.8,开启NVIDIA硬件加速功能。

1、安装依赖库

sudo apt-get install libtool automake autoconf nasm yasm  //nasm yasm注意版本
sudo apt-get install libx264-dev
sudo apt-get install libx265-dev
sudo apt-get install libmp3lame-dev
sudo apt-get install libvpx-dev
sudo apt-get install libfaac-dev

2、安装ffnvcodec

git clone https://git.videolan.org/git/ffmpeg/nv-codec-headers.git
cd nv-codec-headers
make
sudo make install

3、安装NVIDIA驱动

直接使用apt安装方便,在官方网站下载驱动未安装成功

1.卸载系统里的Nvidia低版本显卡驱动

sudo apt-get purge nvidia*

2.把显卡驱动加入PPA

sudo add-apt-repository ppa:graphics-drivers
sudo apt-get update

3.查找显卡驱动最新的版本号

查找并安装最新驱动

sudo apt-cache search nvidia
sudo apt-get install nvidia-430

4、安装CUDA

CUDA是Nvidia出的一个GPU计算库,让程序员可以驱动Nvidia显卡的GPU进行各种工作,其中就包含了视频的编解码。

1.下载https://developer.download.nvidia.cn/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_8.0.44-1_amd64.deb

sudo dpkg -i cuda-repo-ubuntu1604_8.0.44-1_amd64.deb
sudo apt-get update
sudo apt-get install cuda
2.检查驱动和CUDA安装是否成功
nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 430.64       Driver Version: 430.64       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:01:00.0  On |                  N/A |
| 42%   25C    P8     6W / 120W |     62MiB /  6077MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1311      G   /usr/lib/xorg/Xorg                            59MiB |
+-----------------------------------------------------------------------------+

5、编译ffmpeg

./configure --enable-gpl --enable-version3 --enable-nonfree --enable-shared --enable-ffmpeg --enable-ffplay --enable-ffprobe --enable-ffserver --enable-libx264 --enable-nvenc --enable-cuda --enable-cuvid --enable-libnpp --extra-cflags=-I/usr/local/cuda/include --extra-ldflags=-L/usr/local/cuda/lib64
make
sudo make install 

检查:

ffmpeg -codecs | grep nv
 DEV.LS h264                 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (decoders: h264 h264_cuvid ) (encoders: libx264 libx264rgb h264_nvenc nvenc nvenc_h264 )
 DEV.L. hevc                 H.265 / HEVC (High Efficiency Video Coding) (decoders: hevc hevc_cuvid ) (encoders: nvenc_hevc hevc_nvenc )
测试:
ffmpeg -i aidedaijia.mkv -c:v h264_nvenc -c:a aac output.mp4
ffmpeg -hwaccel cuvid -i output.mp4 output.yuv

6、修改部分

1.突破NVIDIA显卡NVENC并发Session数目限制

具体查看Video Encode and Decode GPU Support Matrix | NVIDIA Developer

我使用的是gtx1060显卡,最大只能并发2路编码,最后看到老雷blog突破NVIDIA NVENC并发Session数目限制,发现是驱动里面进行了限制。但老雷是windows下进行了修改,Linux下修改方法在githu中有(找了很久),而且可以针对很多驱动程序版本都做了匹配(牛!)。地址在:GitHub - keylase/nvidia-patch: This patch removes restriction on maximum number of simultaneous NVENC video encoding sessions imposed by Nvidia to consumer-grade GPUs.

git clone https://github.com/keylase/nvidia-patch.git
sudo bash ./patch.sh    (修改驱动)
sudo bash ./patch.sh -r (恢复原驱动)

2.关于nvenc编码输出帧的问题

使用nvenc和h264_nvenc编码的时候,每帧的前面都添加了一个SEI帧,在使用RTP传输的时候,对方接收到的编码出错,于是直接在ffmpeg源码进行了修改。

在ffmpeg-3.4.8/libavcodec/nvenc.c文件中修改:

if (IS_CBR(cc->rcParams.rateControlMode)) {
    h264->outputBufferingPeriodSEI = 1;//不知道干啥用的,修改了没有发现编码有变化
}
​
h264->outputPictureTimingSEI = 1;//重要是修改这里,将1修改为0,每个帧前就不再输出SEI帧了。

7、测试编码程序

#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <stdio.h>
​
/*
* Video encoding example
*/
​
​
#define H264_START_CODE 0x000001
uint32_t h264_find_next_start_code (uint8_t *pBuf,
                    uint32_t bufLen)
{
  uint32_t val;
  uint32_t offset;
 
  offset = 0;
  if (pBuf[0] == 0 && pBuf[1] == 0 && pBuf[2] == 0 && pBuf[3] == 1) {
    pBuf += 4;
    offset = 4;
  } else if (pBuf[0] == 0 && pBuf[1] == 0 && pBuf[2] == 1) {
    pBuf += 3;
    offset = 3;
  }
  val = 0xffffffff;
  while (offset < bufLen - 3) {
    val <<= 8;
    val |= *pBuf++;
    offset++;
    if (val == H264_START_CODE) {
      return offset - 4;
    }
    if ((val & 0x00ffffff) == H264_START_CODE) {
      return offset - 3;
    }
  }
  return 0;
}
​
static void video_encode_example(const char *filename)
{
    AVCodec *codec;
    AVCodecContext *c = NULL;
    int i, ret, x, y, got_output,k;
    AVFrame *frame;
    AVPacket pkt;
    uint8_t endcode[] = { 0, 0, 1, 0xb7 };
​
//    av_log_set_level(64);
​
​
    printf("Encode video file %s\n", filename);
​
    /* find the video encoder */
//    codec = avcodec_find_encoder_by_name("libx264");
    codec = avcodec_find_encoder_by_name("nvenc");//nvenc nvenc_h264 h264_nvenc
    //codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        exit(1);
    }
​
    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        exit(1);
    }
​
    c->bit_rate = 4000*1024;
    c->width = 1920;
    c->height = 1080;
    c->time_base.num = 1;
    c->time_base.den = 60;
    c->gop_size = 10;
    c->pix_fmt = AV_PIX_FMT_YUV420P;//AV_PIX_FMT_CUDA;
    c->max_b_frames = 0;
    c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
​
    AVDictionary *param = 0;
    //H.264
    if (codec->id == AV_CODEC_ID_H264) {
//        av_dict_set(&param, "preset", "medium", 0);
//        av_dict_set(&param, "tune", "zerolatency", 0);
//  av_dict_set(&param, "rc", "cbr", 0);
    }
    //H.265
    if (codec->id == AV_CODEC_ID_H265 || codec->id == AV_CODEC_ID_HEVC){
        //av_dict_set(&param, "x265-params", "qp=20", 0);
        av_dict_set(&param, "x265-params", "crf=25", 0);
        av_dict_set(&param, "preset", "fast", 0);
        av_dict_set(&param, "tune", "zero-latency", 0);
    }
​
    /* open it */
    if (avcodec_open2(c, codec, &param) < 0) {
        fprintf(stderr, "Could not open codec\n");
        system("pause");
        exit(1);
    }
​
    FILE *f;
    f = fopen(filename, "wb");
    if (!f) {
        fprintf(stderr, "Could not open %s\n", filename);
        exit(1);
    }
​
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    frame->format = c->pix_fmt;
    frame->width = c->width;
    frame->height = c->height;
​
    /* the image can be allocated by any means and av_image_alloc() is
    * just the most convenient way if av_malloc() is to be used */
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height,
        c->pix_fmt, 32);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate raw picture buffer\n");
        exit(1);
    }
​
    /* encode (i=500/25) second of video */
    for (i = 0; i < 60*10; i++) {
        av_init_packet(&pkt);
        pkt.data = NULL;    // packet data will be allocated by the encoder
        pkt.size = 0;
​
        fflush(stdout);
        /* prepare a dummy image */
        /* Y */
        for (y = 0; y < c->height; y++) {
            for (x = 0; x < c->width; x++) {
                frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
            }
        }
​
        /* Cb and Cr */
        for (y = 0; y < c->height / 2; y++) {
            for (x = 0; x < c->width / 2; x++) {
                frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
            }
        }
​
        frame->pts = i;
​
        // encode the image 
        ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }
        if(pkt.flags & AV_PKT_FLAG_KEY){
        printf("@-\n");//输出的是关键帧
       for (k=0;k< c->extradata_size;k++){
        printf("%02x ",c->extradata[k]);
       }
       printf("\n");
int offset=0;
offset=h264_find_next_start_code(c->extradata,c->extradata_size);
printf("offset=%d\n",offset);
int spslen= offset;
int ppslen= c->extradata_size -offset;
​
           for (k=0;k<spslen;k++){
                printf("%02x ",c->extradata[k]);
           }
           printf("\n");
       for (k=0;k<ppslen;k++){       
                printf("%02x ",c->extradata[k+spslen]);
           }
           printf("\n");
​
    }
printf("[%d] ",i);
        if (got_output) {
        for (k=0;k<46;k++){
        printf("%02x ",pkt.data[k]);
        }
        printf(" size=%d\n",pkt.size);
            //printf("\n1:Write frame %3d (size=%5d)\n", i, pkt.size);
//            fwrite(pkt.data, 1, pkt.size, f);
            av_packet_unref(&pkt);
        }
​
    }
​
    /* get the delayed frames */
    for (got_output = 1; got_output; i++) {
        fflush(stdout);
​
        ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            exit(1);
        }
​
        if (got_output) {
            printf("2:Write frame %3d (size=%5d)\n", i, pkt.size);
            fwrite(pkt.data, 1, pkt.size, f);
            av_packet_unref(&pkt);
        }
    }
​
    /* add sequence end code to have a real MPEG file */
    fwrite(endcode, 1, sizeof(endcode), f);
    fclose(f);
​
    avcodec_close(c);
    av_free(c);
    av_freep(&frame->data[0]);
    av_frame_free(&frame);
}
​
int main(int argc, char **argv)
{
    /* register all the codecs */
    avcodec_register_all();
    video_encode_example("test.h264");
    return 0;
}

编译

gcc ./encode.c -o encode -lavcodec -lavutil

原文链接:ffmpeg使用NVIDIA GPU硬件编解码

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值