Ver Fonte

新增硬件编码支持

新增硬件编码支持
huihui há 5 anos atrás
pai
commit
c48c8a791e

+ 227 - 46
source/VideoEncode/src/Video/VideoEncoder.cpp

@@ -7,7 +7,7 @@
 #include "AppConfig.h"
 #include "VideoEncoder.h"
 
-#define ENCODE_H264
+//#define ENCODE_H265
 
 VideoEncoder::VideoEncoder()
 {
@@ -106,10 +106,14 @@ void VideoEncoder::run()
 
             memcpy(picture_buf, node.buffer, node.size);
 
-            if (avcodec_send_frame(pCodecCtx, picture) != 0)
+            int ret = avcodec_send_frame(pCodecCtx, picture);
+            if (ret != 0)
             {
-               fprintf(stderr, "Error sending a frame for encoding!\n");
-               continue;
+                char buff[128]={0};
+                av_strerror(ret, buff, 128);
+
+                fprintf(stderr, "Error sending a frame for encoding! (%s)\n", buff);
+                continue;
             }
 
             while (0 == avcodec_receive_packet(pCodecCtx, &pkt))
@@ -151,28 +155,11 @@ void VideoEncoder::run()
 
 }
 
-bool VideoEncoder::openEncoder()
+AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
 {
-    int size;
     int in_w = mWidth;
     int in_h = mHeight;//宽高
 
-#ifdef ENCODE_H265
-    AVCodecID codec_id = AV_CODEC_ID_H265;
-#else
-    AVCodecID codec_id = AV_CODEC_ID_H264;
-#endif
-
-    //查找h264编码器
-    pCodec = avcodec_find_encoder(codec_id);
-    if(!pCodec)
-    {
-      fprintf(stderr, "h264 codec not found\n");
-      exit(1);
-    }
-
-    pCodecCtx = avcodec_alloc_context3(pCodec);
-
     pCodecCtx->codec_id = codec_id;
     pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
     pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
@@ -183,18 +170,18 @@ bool VideoEncoder::openEncoder()
     pCodecCtx->bit_rate = mBitRate; //比特率(调节这个大小可以改变编码后视频的质量)
     pCodecCtx->gop_size=12;
     //H264 还可以设置很多参数 自行研究吧
-////    pCodecCtx->me_range = 16;
-////    pCodecCtx->max_qdiff = 4;
-////    pCodecCtx->qcompress = 0.6;
-////    pCodecCtx->qmin = 10;
-////    pCodecCtx->qmax = 51;
-//    pCodecCtx->me_range = 16;
-//    pCodecCtx->max_qdiff = 1;
-//    pCodecCtx->qcompress = 0.6;
-//    pCodecCtx->qmin = 10;
-//    pCodecCtx->qmax = 51;
-//    //Optional Param
-//    pCodecCtx->max_b_frames=3;
+    ////    pCodecCtx->me_range = 16;
+    ////    pCodecCtx->max_qdiff = 4;
+    ////    pCodecCtx->qcompress = 0.6;
+    ////    pCodecCtx->qmin = 10;
+    ////    pCodecCtx->qmax = 51;
+    //    pCodecCtx->me_range = 16;
+    //    pCodecCtx->max_qdiff = 1;
+    //    pCodecCtx->qcompress = 0.6;
+    //    pCodecCtx->qmin = 10;
+    //    pCodecCtx->qmax = 51;
+    //    //Optional Param
+    //    pCodecCtx->max_b_frames=3;
 
 
     // some formats want stream headers to be separate
@@ -206,7 +193,7 @@ bool VideoEncoder::openEncoder()
     if(pCodecCtx->codec_id == AV_CODEC_ID_H264)
     {
         av_dict_set(&param, "preset", "medium", 0);
-//        av_dict_set(&param, "preset", "superfast", 0);
+    //        av_dict_set(&param, "preset", "superfast", 0);
         av_dict_set(&param, "tune", "zerolatency", 0); //实现实时编码
         av_dict_set(&param, "profile", "main", 0);
     }
@@ -217,24 +204,218 @@ bool VideoEncoder::openEncoder()
         av_dict_set(&param, "profile", "main", 0);
     }
 
-    if (avcodec_open2(pCodecCtx, pCodec,&param) < 0)
+    return param;
+
+}
+
+///打开视频编码器
+bool VideoEncoder::openVideoEncoder(const AVCodecID &codec_id)
+{
+    bool isSucceed = false;
+    bool isHardWareEncoderOpened = false;
+
+//    bool mIsSupportHardEncoder = true;
+//    if (mIsSupportHardEncoder)
     {
-        printf("Failed to open video encoder! 编码器打开失败!\n");
-        return false;
+        ///尝试打开cuvid编码器器
+        isHardWareEncoderOpened = openHardEncoder_Cuvid(codec_id);
+
+        ///cuvid打开失败了 继续尝试 qsv
+        if (!isHardWareEncoderOpened)
+        {
+            isHardWareEncoderOpened = openHardEncoder_Qsv(codec_id);
+        }
     }
 
-    picture = av_frame_alloc();
+    //尝试打开硬件解码器失败了 改用软解码
+    if (!isHardWareEncoderOpened)
+    {
+        isSucceed = openSoftEncoder(codec_id);
+    }
+    else
+    {
+        isSucceed = true;
+    }
 
-    picture->format = pCodecCtx->pix_fmt;
-    picture->width  = pCodecCtx->width;
-    picture->height = pCodecCtx->height;
+    return isSucceed;
+}
 
-    size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); //计算需要用到的数据大小
-    picture_buf = (uint8_t *)av_malloc(size); //分配空间
-    avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
+///打开硬件编码器(英伟达)
+bool VideoEncoder::openHardEncoder_Cuvid(const AVCodecID &codec_id)
+{
+    bool isSucceed = false;
 
-    return true;
+    fprintf(stderr,"open hardware encoder cuvid...\n");
+
+    ///查找硬件解码器
+    char hardWareDecoderName[32] = {0};
+
+    if (AV_CODEC_ID_H264 == codec_id)
+    {
+        sprintf(hardWareDecoderName, "h264_nvenc");
+    }
+    else if (AV_CODEC_ID_HEVC == codec_id)
+    {
+        sprintf(hardWareDecoderName, "hevc_nvenc");
+    }
+
+    if (strlen(hardWareDecoderName) > 0)
+    {
+        pCodec = avcodec_find_encoder_by_name(hardWareDecoderName);
+
+        if (pCodec != NULL)
+        {
+            pCodecCtx = avcodec_alloc_context3(pCodec);
+
+            AVDictionary *param = setEncoderParam(codec_id);
+
+            ///打开解码器
+            if (avcodec_open2(pCodecCtx, pCodec, &param) < 0)
+            {
+                avcodec_close(pCodecCtx);
+                avcodec_free_context(&pCodecCtx);
+                pCodecCtx = nullptr;
+                isSucceed = false;
+
+                fprintf(stderr,"Could not open codec %s\n",hardWareDecoderName);
+            }
+            else
+            {
+                isSucceed = true;
+                fprintf(stderr,"open codec %s succeed! %d %d\n",hardWareDecoderName,pCodec->id,pCodecCtx->codec_id);
+            }
+        }
+        else
+        {
+            fprintf(stderr,"Codec %s not found.\n",hardWareDecoderName);
+        }
+    }
+
+    return isSucceed;
+}
+
+///打开硬件编码器(intel)
+bool VideoEncoder::openHardEncoder_Qsv(const AVCodecID &codec_id)
+{
+    bool isSucceed = false;
+
+    fprintf(stderr,"open hardware encoder cuvid...\n");
+
+    ///查找硬件解码器
+    char hardWareDecoderName[32] = {0};
+
+    if (AV_CODEC_ID_H264 == codec_id)
+    {
+        sprintf(hardWareDecoderName, "h264_qsv");
+    }
+    else if (AV_CODEC_ID_HEVC == codec_id)
+    {
+        sprintf(hardWareDecoderName, "hevc_qsv");
+    }
+
+    if (strlen(hardWareDecoderName) > 0)
+    {
+        pCodec = avcodec_find_encoder_by_name(hardWareDecoderName);
+
+        if (pCodec != NULL)
+        {
+            pCodecCtx = avcodec_alloc_context3(pCodec);
+
+            AVDictionary *param = setEncoderParam(codec_id);
+
+            ///打开解码器
+            if (avcodec_open2(pCodecCtx, pCodec, &param) < 0)
+            {
+                avcodec_close(pCodecCtx);
+                avcodec_free_context(&pCodecCtx);
+                pCodecCtx = nullptr;
+                isSucceed = false;
+
+                fprintf(stderr,"Could not open codec %s\n",hardWareDecoderName);
+            }
+            else
+            {
+                isSucceed = true;
+                fprintf(stderr,"open codec %s succeed! %d %d\n",hardWareDecoderName,pCodec->id,pCodecCtx->codec_id);
+            }
+        }
+        else
+        {
+            fprintf(stderr,"Codec %s not found.\n",hardWareDecoderName);
+        }
+    }
+
+    return isSucceed;
+}
+
+///打开软编码器
+bool VideoEncoder::openSoftEncoder(const AVCodecID &codec_id)
+{
+    bool isSucceed = false;
+
+    fprintf(stderr,"open software encoder... \n");
+
+    pCodec = avcodec_find_encoder(codec_id);
+
+    if (pCodec == NULL)
+    {
+        fprintf(stderr, "Codec not found.\n");
+        isSucceed = false;
+    }
+    else
+    {
+        pCodecCtx = avcodec_alloc_context3(pCodec);
+        pCodecCtx->thread_count = 8;
+
+        AVDictionary *param = setEncoderParam(codec_id);
+
+        ///打开解码器
+        if (avcodec_open2(pCodecCtx, pCodec, &param) < 0)
+        {
+            avcodec_close(pCodecCtx);
+            avcodec_free_context(&pCodecCtx);
+            pCodecCtx = nullptr;
+            isSucceed = false;
+
+            fprintf(stderr,"Could not open codec.\n");
+        }
+        else
+        {
+            isSucceed = true;
+        }
+    }
+
+    return isSucceed;
+}
+
+bool VideoEncoder::openEncoder()
+{
+
+#ifdef ENCODE_H265
+    AVCodecID codec_id = AV_CODEC_ID_H265;
+#else
+    AVCodecID codec_id = AV_CODEC_ID_H264;
+#endif
+
+    bool isSucceed = openVideoEncoder(codec_id);
+
+    if (isSucceed)
+    {
+        picture = av_frame_alloc();
+
+        picture->format = pCodecCtx->pix_fmt;
+        picture->width  = pCodecCtx->width;
+        picture->height = pCodecCtx->height;
+
+        int size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height); //计算需要用到的数据大小
+        picture_buf = (uint8_t *)av_malloc(size); //分配空间
+        avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
+
+        fprintf(stderr, " 编码器打开成功!pCodecCtx->pix_fmt=%d %d %d %d %d %d\n",
+                pCodecCtx->pix_fmt, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12, pCodecCtx->width, pCodecCtx->height, size);
+    }
 
+    return isSucceed;
 }
 
 bool VideoEncoder::closeEncoder()

+ 7 - 0
source/VideoEncode/src/Video/VideoEncoder.h

@@ -73,6 +73,13 @@ private:
 
     bool mIsStop;
 
+    AVDictionary *setEncoderParam(const AVCodecID &codec_id); //设置编码器参数
+
+    bool openVideoEncoder(const AVCodecID &codec_id); //打开视频编码器
+    bool openHardEncoder_Cuvid(const AVCodecID &codec_id); //打开硬件编码器(英伟达)
+    bool openHardEncoder_Qsv(const AVCodecID &codec_id);   //打开硬件编码器(intel)
+    bool openSoftEncoder(const AVCodecID &codec_id);//打开软编码器
+
     bool openEncoder(); //打开编码器
     bool closeEncoder(); //关闭编码器