|
@@ -7,7 +7,7 @@
|
|
#include "AppConfig.h"
|
|
#include "AppConfig.h"
|
|
#include "VideoEncoder.h"
|
|
#include "VideoEncoder.h"
|
|
|
|
|
|
-#define ENCODE_H264
|
|
|
|
|
|
+//#define ENCODE_H265
|
|
|
|
|
|
VideoEncoder::VideoEncoder()
|
|
VideoEncoder::VideoEncoder()
|
|
{
|
|
{
|
|
@@ -106,10 +106,14 @@ void VideoEncoder::run()
|
|
|
|
|
|
memcpy(picture_buf, node.buffer, node.size);
|
|
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))
|
|
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_w = mWidth;
|
|
int in_h = mHeight;//宽高
|
|
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_id = codec_id;
|
|
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
|
|
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
|
|
@@ -183,18 +170,18 @@ bool VideoEncoder::openEncoder()
|
|
pCodecCtx->bit_rate = mBitRate; //比特率(调节这个大小可以改变编码后视频的质量)
|
|
pCodecCtx->bit_rate = mBitRate; //比特率(调节这个大小可以改变编码后视频的质量)
|
|
pCodecCtx->gop_size=12;
|
|
pCodecCtx->gop_size=12;
|
|
//H264 还可以设置很多参数 自行研究吧
|
|
//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
|
|
// some formats want stream headers to be separate
|
|
@@ -206,7 +193,7 @@ bool VideoEncoder::openEncoder()
|
|
if(pCodecCtx->codec_id == AV_CODEC_ID_H264)
|
|
if(pCodecCtx->codec_id == AV_CODEC_ID_H264)
|
|
{
|
|
{
|
|
av_dict_set(¶m, "preset", "medium", 0);
|
|
av_dict_set(¶m, "preset", "medium", 0);
|
|
-// av_dict_set(¶m, "preset", "superfast", 0);
|
|
|
|
|
|
+ // av_dict_set(¶m, "preset", "superfast", 0);
|
|
av_dict_set(¶m, "tune", "zerolatency", 0); //实现实时编码
|
|
av_dict_set(¶m, "tune", "zerolatency", 0); //实现实时编码
|
|
av_dict_set(¶m, "profile", "main", 0);
|
|
av_dict_set(¶m, "profile", "main", 0);
|
|
}
|
|
}
|
|
@@ -217,24 +204,218 @@ bool VideoEncoder::openEncoder()
|
|
av_dict_set(¶m, "profile", "main", 0);
|
|
av_dict_set(¶m, "profile", "main", 0);
|
|
}
|
|
}
|
|
|
|
|
|
- if (avcodec_open2(pCodecCtx, pCodec,¶m) < 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, ¶m) < 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, ¶m) < 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, ¶m) < 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()
|
|
bool VideoEncoder::closeEncoder()
|