|
@@ -7,6 +7,9 @@
|
|
#include "AppConfig.h"
|
|
#include "AppConfig.h"
|
|
#include "VideoEncoder.h"
|
|
#include "VideoEncoder.h"
|
|
|
|
|
|
|
|
+//是否启用 ROI (region of interest) encoding 感兴趣区域视频编码技术
|
|
|
|
+#define TEST_ROI_ENCODING
|
|
|
|
+
|
|
//#define ENCODE_H265
|
|
//#define ENCODE_H265
|
|
|
|
|
|
VideoEncoder::VideoEncoder()
|
|
VideoEncoder::VideoEncoder()
|
|
@@ -106,6 +109,38 @@ void VideoEncoder::run()
|
|
|
|
|
|
memcpy(picture_buf, node.buffer, node.size);
|
|
memcpy(picture_buf, node.buffer, node.size);
|
|
|
|
|
|
|
|
+#ifdef TEST_ROI_ENCODING
|
|
|
|
+ {
|
|
|
|
+ // 申请side_data空间,假设有2个区域。
|
|
|
|
+ // 不需要显式释放申请到的空间,
|
|
|
|
+ // 在AVFrame的清理函数中会自动处理。
|
|
|
|
+ AVFrameSideData *sd = av_frame_new_side_data(picture,
|
|
|
|
+ AV_FRAME_DATA_REGIONS_OF_INTEREST,
|
|
|
|
+ 1*sizeof(AVRegionOfInterest));
|
|
|
|
+
|
|
|
|
+ // 获取刚申请到的内存地址
|
|
|
|
+ AVRegionOfInterest* roi = (AVRegionOfInterest*)sd->data;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // 设置第一个区域
|
|
|
|
+ roi[0].self_size = sizeof(*roi);
|
|
|
|
+ roi[0].top = 0;
|
|
|
|
+ roi[0].bottom = 600;
|
|
|
|
+ roi[0].left = 600;
|
|
|
|
+ roi[0].right = 1000;
|
|
|
|
+ roi[0].qoffset = AVRational{-1,1};
|
|
|
|
+
|
|
|
|
+// // 设置第二个区域
|
|
|
|
+// roi[1].self_size = sizeof(*roi);
|
|
|
|
+// roi[1].top = ...;
|
|
|
|
+// roi[1].bottom = ...;
|
|
|
|
+// roi[1].left = ...;
|
|
|
|
+// roi[1].right = ...;
|
|
|
|
+// roi[1].qoffset = ...;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+
|
|
int ret = avcodec_send_frame(pCodecCtx, picture);
|
|
int ret = avcodec_send_frame(pCodecCtx, picture);
|
|
if (ret != 0)
|
|
if (ret != 0)
|
|
{
|
|
{
|
|
@@ -123,7 +158,9 @@ void VideoEncoder::run()
|
|
|
|
|
|
fprintf(stderr, "%s encoded size=%d isKeyFrame=%d\n", __FUNCTION__, pkt.size, isKeyFrame);
|
|
fprintf(stderr, "%s encoded size=%d isKeyFrame=%d\n", __FUNCTION__, pkt.size, isKeyFrame);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+#ifdef TEST_ROI_ENCODING
|
|
|
|
+ av_frame_remove_side_data(picture, AV_FRAME_DATA_REGIONS_OF_INTEREST);
|
|
|
|
+#endif
|
|
av_packet_unref(&pkt);
|
|
av_packet_unref(&pkt);
|
|
av_free(node.buffer);
|
|
av_free(node.buffer);
|
|
}
|
|
}
|
|
@@ -155,6 +192,156 @@ void VideoEncoder::run()
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef TEST_ROI_ENCODING
|
|
|
|
+AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
|
|
|
|
+{
|
|
|
|
+ int in_w = mWidth;
|
|
|
|
+ int in_h = mHeight;//宽高
|
|
|
|
+
|
|
|
|
+ int mFrameRate = 15;
|
|
|
|
+ int mQuality = 0;
|
|
|
|
+
|
|
|
|
+ pCodecCtx->codec_id = codec_id;
|
|
|
|
+ pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
|
|
|
|
+ pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
|
|
|
|
+ pCodecCtx->width = in_w;
|
|
|
|
+ pCodecCtx->height = in_h;
|
|
|
|
+ pCodecCtx->time_base.num = 1;
|
|
|
|
+ pCodecCtx->time_base.den = mFrameRate;//帧率(既一秒钟多少张图片)
|
|
|
|
+ pCodecCtx->bit_rate = 100000; //比特率(调节这个大小可以改变编码后视频的质量)
|
|
|
|
+ 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;
|
|
|
|
+
|
|
|
|
+ // 视频编码器常用的码率控制方式包括abr(平均码率),crf(恒定码率),cqp(恒定质量),
|
|
|
|
+ // ffmpeg中AVCodecContext显示提供了码率大小的控制参数,但是并没有提供其他的控制方式。
|
|
|
|
+ // ffmpeg中码率控制方式分为以下几种情况:
|
|
|
|
+ // 1.如果设置了AVCodecContext中bit_rate的大小,则采用abr的控制方式;
|
|
|
|
+ // 2.如果没有设置AVCodecContext中的bit_rate,则默认按照crf方式编码,crf默认大小为23(此值类似于qp值,同样表示视频质量);
|
|
|
|
+ // 3.如果用户想自己设置,则需要借助av_opt_set函数设置AVCodecContext的priv_data参数。下面给出三种控制方式的实现代码:
|
|
|
|
+
|
|
|
|
+#if 0
|
|
|
|
+ ///平均码率
|
|
|
|
+ //目标的码率,即采样的码率;显然,采样码率越大,视频大小越大
|
|
|
|
+ pCodecCtx->bit_rate = mBitRate;
|
|
|
|
+
|
|
|
|
+#elif 1
|
|
|
|
+ ///恒定码率
|
|
|
|
+// 量化比例的范围为0~51,其中0为无损模式,23为缺省值,51可能是最差的。该数字越小,图像质量越好。从主观上讲,18~28是一个合理的范围。18往往被认为从视觉上看是无损的,它的输出视频质量和输入视频一模一样或者说相差无几。但从技术的角度来讲,它依然是有损压缩。
|
|
|
|
+// 若Crf值加6,输出码率大概减少一半;若Crf值减6,输出码率翻倍。通常是在保证可接受视频质量的前提下选择一个最大的Crf值,如果输出视频质量很好,那就尝试一个更大的值,如果看起来很糟,那就尝试一个小一点值。
|
|
|
|
+// av_opt_set(pCodecCtx->priv_data, "crf", "51.000", AV_OPT_SEARCH_CHILDREN);
|
|
|
|
+
|
|
|
|
+#else
|
|
|
|
+ ///qp的值和crf一样
|
|
|
|
+// av_opt_set(pCodecCtx->priv_data, "qp", "31.000",AV_OPT_SEARCH_CHILDREN);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+#if TEST_OPT
|
|
|
|
+
|
|
|
|
+ av_opt_set(pCodecCtx,"b","400000",0); //bitrate
|
|
|
|
+ //Another method
|
|
|
|
+ //av_opt_set_int(pCodecCtx,"b",400000,0); //bitrate
|
|
|
|
+
|
|
|
|
+ av_opt_set(pCodecCtx,"time_base","1/25",0); //time_base
|
|
|
|
+ av_opt_set(pCodecCtx,"bf","5",0); //max b frame
|
|
|
|
+ av_opt_set(pCodecCtx,"g","25",0); //gop
|
|
|
|
+ av_opt_set(pCodecCtx,"qmin","10",0); //qmin/qmax
|
|
|
|
+ av_opt_set(pCodecCtx,"qmax","51",0);
|
|
|
|
+
|
|
|
|
+#else
|
|
|
|
+
|
|
|
|
+// pCodecCtx->time_base.num = 1;
|
|
|
|
+// pCodecCtx->time_base.den = 25;
|
|
|
|
+// pCodecCtx->max_b_frames=5;
|
|
|
|
+// pCodecCtx->bit_rate = 400000;
|
|
|
|
+// pCodecCtx->gop_size=25;
|
|
|
|
+// pCodecCtx->qmin = 10;
|
|
|
|
+// pCodecCtx->qmax = 51;
|
|
|
|
+
|
|
|
|
+ /* time base: this is the fundamental unit of time (in seconds) in terms
|
|
|
|
+ of which frame timestamps are represented. for fixed-fps content,
|
|
|
|
+ timebase should be 1/framerate and timestamp increments should be
|
|
|
|
+ identically 1. */
|
|
|
|
+// ost->st->time_base = { 1, m_videoFrameRate };
|
|
|
|
+// pCodecCtx->time_base = ost->st->time_base;
|
|
|
|
+// c->gop_size = 12; /* emit one intra frame every twelve frames at most */
|
|
|
|
+// pCodecCtx->gop_size = m_videoFrameRate * 2; ///I帧间隔
|
|
|
|
+
|
|
|
|
+// //固定允许的码率误差,数值越大,视频越小
|
|
|
|
+// c->bit_rate_tolerance = mBitRate;
|
|
|
|
+
|
|
|
|
+// //H264 还可以设置很多参数 自行研究吧
|
|
|
|
+//// pCodecCtx->me_range = 16;
|
|
|
|
+//// pCodecCtx->max_qdiff = 1;
|
|
|
|
+// c->qcompress = 0.85;
|
|
|
|
+// c->qmin = 18;
|
|
|
|
+
|
|
|
|
+ pCodecCtx->qmin = 0;
|
|
|
|
+ pCodecCtx->qmax = 51;
|
|
|
|
+
|
|
|
|
+// pCodecCtx->qmin = 16+(10-mQuality)*2;
|
|
|
|
+// pCodecCtx->qmax = 31+(10-mQuality)*2;
|
|
|
|
+
|
|
|
|
+//// //采用(qmin/qmax的比值来控制码率,1表示局部采用此方法,0表示全局)
|
|
|
|
+//// c->rc_qsquish = 0;
|
|
|
|
+
|
|
|
|
+//// //因为我们的量化系数q是在qmin和qmax之间浮动的,
|
|
|
|
+//// //qblur表示这种浮动变化的变化程度,取值范围0.0~1.0,取0表示不削减
|
|
|
|
+//// c->qblur = 1.0;
|
|
|
|
+
|
|
|
|
+//std::cout<<"mBitRate"<<mBitRate<<m_videoFrameRate<<std::endl;
|
|
|
|
+
|
|
|
|
+//// ///b_frame_strategy
|
|
|
|
+//// ///如果为true,则自动决定什么时候需要插入B帧,最高达到设置的最大B帧数。
|
|
|
|
+//// ///如果设置为false,那么最大的B帧数被使用。
|
|
|
|
+//// c->b_frame_strategy = 1;
|
|
|
|
+//// c->max_b_frames = 5;
|
|
|
|
+
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ // some formats want stream headers to be separate
|
|
|
|
+ if (pCodecCtx->flags & AVFMT_GLOBALHEADER)
|
|
|
|
+ pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
|
|
|
+
|
|
|
|
+ //编码器预设
|
|
|
|
+ AVDictionary *param = 0;
|
|
|
|
+ if(pCodecCtx->codec_id == AV_CODEC_ID_H264)
|
|
|
|
+ {
|
|
|
|
+// //H.264
|
|
|
|
+// //av_dict_set(¶m, "preset", "slow", 0);
|
|
|
|
+// av_dict_set(¶m, "preset", "superfast", 0);
|
|
|
|
+// av_dict_set(¶m, "tune", "zerolatency", 0); //实现实时编码
|
|
|
|
+
|
|
|
|
+ ///下面的可以启用硬件编码
|
|
|
|
+ av_dict_set(¶m, "preset", "medium", 0);
|
|
|
|
+ // av_dict_set(¶m, "preset", "superfast", 0);
|
|
|
|
+ av_dict_set(¶m, "tune", "zerolatency", 0); //实现实时编码
|
|
|
|
+ av_dict_set(¶m, "profile", "main", 0);
|
|
|
|
+ }
|
|
|
|
+ else if(pCodecCtx->codec_id == AV_CODEC_ID_H265)
|
|
|
|
+ {
|
|
|
|
+ av_dict_set(¶m, "preset", "ultrafast", 0);
|
|
|
|
+ av_dict_set(¶m, "tune", "zerolatency", 0);
|
|
|
|
+ av_dict_set(¶m, "profile", "main", 0);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return param;
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#else
|
|
|
|
+
|
|
AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
|
|
AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
|
|
{
|
|
{
|
|
int in_w = mWidth;
|
|
int in_w = mWidth;
|
|
@@ -208,6 +395,8 @@ AVDictionary *VideoEncoder::setEncoderParam(const AVCodecID &codec_id)
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#endif
|
|
|
|
+
|
|
///打开视频编码器
|
|
///打开视频编码器
|
|
bool VideoEncoder::openVideoEncoder(const AVCodecID &codec_id)
|
|
bool VideoEncoder::openVideoEncoder(const AVCodecID &codec_id)
|
|
{
|
|
{
|
|
@@ -216,16 +405,16 @@ bool VideoEncoder::openVideoEncoder(const AVCodecID &codec_id)
|
|
|
|
|
|
// bool mIsSupportHardEncoder = true;
|
|
// bool mIsSupportHardEncoder = true;
|
|
// if (mIsSupportHardEncoder)
|
|
// if (mIsSupportHardEncoder)
|
|
- {
|
|
|
|
- ///尝试打开cuvid编码器器
|
|
|
|
- isHardWareEncoderOpened = openHardEncoder_Cuvid(codec_id);
|
|
|
|
-
|
|
|
|
- ///cuvid打开失败了 继续尝试 qsv
|
|
|
|
- if (!isHardWareEncoderOpened)
|
|
|
|
- {
|
|
|
|
- isHardWareEncoderOpened = openHardEncoder_Qsv(codec_id);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+// {
|
|
|
|
+// ///尝试打开cuvid编码器器
|
|
|
|
+// isHardWareEncoderOpened = openHardEncoder_Cuvid(codec_id);
|
|
|
|
+
|
|
|
|
+// ///cuvid打开失败了 继续尝试 qsv
|
|
|
|
+// if (!isHardWareEncoderOpened)
|
|
|
|
+// {
|
|
|
|
+// isHardWareEncoderOpened = openHardEncoder_Qsv(codec_id);
|
|
|
|
+// }
|
|
|
|
+// }
|
|
|
|
|
|
//尝试打开硬件解码器失败了 改用软解码
|
|
//尝试打开硬件解码器失败了 改用软解码
|
|
if (!isHardWareEncoderOpened)
|
|
if (!isHardWareEncoderOpened)
|