在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(¶m, "preset", "medium", 0); // av_dict_set(¶m, "tune", "zerolatency", 0); // av_dict_set(¶m, "rc", "cbr", 0); } //H.265 if (codec->id == AV_CODEC_ID_H265 || codec->id == AV_CODEC_ID_HEVC){ //av_dict_set(¶m, "x265-params", "qp=20", 0); av_dict_set(¶m, "x265-params", "crf=25", 0); av_dict_set(¶m, "preset", "fast", 0); av_dict_set(¶m, "tune", "zero-latency", 0); } /* open it */ if (avcodec_open2(c, codec, ¶m) < 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)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓文章来源:https://www.toymoban.com/news/detail-537944.html
文章来源地址https://www.toymoban.com/news/detail-537944.html
到了这里,关于ffmpeg使用NVIDIA GPU硬件编解码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!