H264Encoder.cpp 13.7 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * MIT License
xzl committed
3
 *
xiongziliang committed
4
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
xiongziliang committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
xzl committed
25
 */
xiongziliang committed
26

xzl committed
27 28 29 30 31
#ifdef ENABLE_X264
#include "H264Encoder.h"

#include "Util/TimeTicker.h"

xiongziliang committed
32 33 34
using namespace toolkit;

namespace mediakit {
xzl committed
35 36 37 38 39 40 41

H264Encoder::H264Encoder() {

}

H264Encoder::~H264Encoder() {
	//* 清除图像区域
42 43 44
	if (_pPicIn) {
		delete _pPicIn;
		_pPicIn = nullptr;
xzl committed
45
	}
46 47 48
	if (_pPicOut) {
		delete _pPicOut;
		_pPicOut = nullptr;
xzl committed
49 50 51
	}

	//* 关闭编码器句柄
52 53 54
	if (_pX264Handle) {
		x264_encoder_close(_pX264Handle);
		_pX264Handle = nullptr;
xzl committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
	}
}


/*typedef struct x264_param_t
{
   CPU 标志位
  unsigned int cpu;
  int i_threads;  并行编码多帧
  int b_deterministic; 是否允许非确定性时线程优化
  int i_sync_lookahead;  线程超前缓冲

   视频属性
  int i_width;  宽度
  int i_height;  高度
  int i_csp;  编码比特流的CSP,仅支持i420,色彩空间设置
  int i_level_idc;  level值的设置
  int i_frame_total;  编码帧的总数, 默认 0
Vui参数集视频可用性信息视频标准化选项
  struct
  {
   they will be reduced to be 0 < x <= 65535 and prime
  int i_sar_height;
  int i_sar_width;  设置长宽比

80
  int i_overscan;  0=undef, 1=no overscan, 2=overscan 过扫描线,默认"undef"(不设置),可选项:show(观看)/crop(去除)
xzl committed
81 82 83

  见以下的值h264附件E
  Int i_vidformat; 视频格式,默认"undef",component/pal/ntsc/secam/mac/undef
84 85 86
  int b_fullrange; Specify full range samples setting,默认"off",可选项:off/on
  int i_colorprim; 原始色度格式,默认"undef",可选项:undef/bt709/bt470m/bt470bg,smpte170m/smpte240m/film
  int i_transfer; 转换方式,默认"undef",可选项:undef/bt709/bt470m/bt470bg/linear,log100/log316/smpte170m/smpte240m
xzl committed
87 88 89 90 91 92
  int i_colmatrix; 色度矩阵设置,默认"undef",undef/bt709/fcc/bt470bg,smpte170m/smpte240m/GBR/YCgCo
  int i_chroma_loc;  both top & bottom色度样本指定,范围0~5,默认0
  } vui;

  int i_fps_num;
  int i_fps_den;
93
这两个参数是由fps帧率确定的,赋值的过程见下:
xzl committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
{ float fps;
if( sscanf( value, "%d/%d", &p->i_fps_num, &p->i_fps_den ) == 2 )
  ;
  else if( sscanf( value, "%f", &fps ) )
  {
  p->i_fps_num = (int)(fps * 1000 + .5);
  p->i_fps_den = 1000;
  }
  else
  b_error = 1;
  }
Value的值就是fps。

  流参数
  int i_frame_reference;  参考帧最大数目
  int i_keyint_max;  在此间隔设置IDR关键帧
  int i_keyint_min;  场景切换少于次值编码位I, 而不是 IDR.
  int i_scenecut_threshold; 如何积极地插入额外的I帧
  int i_bframe; 两个相关图像间P帧的数目
  int i_bframe_adaptive; 自适应B帧判定
  int i_bframe_bias; 控制插入B帧判定,范围-100~+100,越高越容易插入B帧,默认0
  int b_bframe_pyramid; 允许部分B为参考帧
去块滤波器需要的参数
  int b_deblocking_filter;
  int i_deblocking_filter_alphac0;  [-6, 6] -6 light filter, 6 strong
  int i_deblocking_filter_beta;  [-6, 6] idem
  熵编码
  int b_cabac;
  int i_cabac_init_idc;

  int b_interlaced;  隔行扫描
  量化
  int i_cqm_preset; 自定义量化矩阵(CQM),初始化量化模式为flat
  char *psz_cqm_file;  JM format读取JM格式的外部量化矩阵文件,自动忽略其他—cqm 选项
  uint8_t cqm_4iy[16];  used only if i_cqm_preset == X264_CQM_CUSTOM
  uint8_t cqm_4ic[16];
  uint8_t cqm_4py[16];
  uint8_t cqm_4pc[16];
  uint8_t cqm_8iy[64];
  uint8_t cqm_8py[64];

   日志
  void (*pf_log)( void *, int i_level, const char *psz, va_list );
  void *p_log_private;
  int i_log_level;
  int b_visualize;
  char *psz_dump_yuv;  重建帧的名字

   编码分析参数
  struct
  {
  unsigned int intra;  帧间分区
  unsigned int inter;  帧内分区

  int b_transform_8x8;  帧间分区
  int b_weighted_bipred; 为b帧隐式加权
  int i_direct_mv_pred; 时间空间队运动预测
  int i_chroma_qp_offset; 色度量化步长偏移量

  int i_me_method;  运动估计算法 (X264_ME_*)
  int i_me_range;  整像素运动估计搜索范围 (from predicted mv)
  int i_mv_range;  运动矢量最大长度(in pixels). -1 = auto, based on level
  int i_mv_range_thread;  线程之间的最小空间. -1 = auto, based on number of threads.
  int i_subpel_refine;  亚像素运动估计质量
  int b_chroma_me;  亚像素色度运动估计和P帧的模式选择
  int b_mixed_references; 允许每个宏块的分区在P帧有它自己的参考号
160
  int i_trellis;  Trellis量化,对每个8x8的块寻找合适的量化值,需要CABAC,默认0 0:关闭1:只在最后编码时使用2:一直使用
xzl committed
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
  int b_fast_pskip; 快速P帧跳过检测
  int b_dct_decimate;  在P-frames转换参数域
  int i_noise_reduction; 自适应伪盲区
  float f_psy_rd;  Psy RD strength
  float f_psy_trellis;  Psy trellis strength
  int b_psy;  Toggle all psy optimizations

  ,亮度量化中使用的无效区大小
  int i_luma_deadzone[2];  {帧间, 帧内}

  int b_psnr;  计算和打印PSNR信息
  int b_ssim; 计算和打印SSIM信息
  } analyse;

   码率控制参数
  struct
  {
  int i_rc_method;  X264_RC_*

  int i_qp_constant;  0-51
  int i_qp_min; 允许的最小量化值
  int i_qp_max; 允许的最大量化值
  int i_qp_step; 帧间最大量化步长

  int i_bitrate; 设置平均码率
  float f_rf_constant;  1pass VBR, nominal QP
  float f_rate_tolerance;
  int i_vbv_max_bitrate; 平均码率模式下,最大瞬时码率,默认0(与-B设置相同)
  int i_vbv_buffer_size; 码率控制缓冲区的大小,单位kbit,默认0
  float f_vbv_buffer_init;  <=1: fraction of buffer_size. >1: kbit码率控制缓冲区数据保留的最大数据量与缓冲区大小之比,范围0~1.0,默认0.9
  float f_ip_factor;
  float f_pb_factor;

  int i_aq_mode;  psy adaptive QP. (X264_AQ_*)
  float f_aq_strength;
  int b_mb_tree;  Macroblock-tree ratecontrol.
  int i_lookahead;

   2pass 多次压缩码率控制
  int b_stat_write;  Enable stat writing in psz_stat_out
  char *psz_stat_out;
  int b_stat_read;  Read stat from psz_stat_in and use it
  char *psz_stat_in;

   2pass params (same as ffmpeg ones)
  float f_qcompress;  0.0 => cbr, 1.0 => constant qp
  float f_qblur; 时间上模糊量化
  float f_complexity_blur;  时间上模糊复杂性
  x264_zone_t *zones;  码率控制覆盖
  int i_zones;  number of zone_t's
  char *psz_zones; 指定区的另一种方法
  } rc;

   Muxing parameters
  int b_aud; 生成访问单元分隔符
  int b_repeat_headers;  在每个关键帧前放置SPS/PPS
  int i_sps_id;  SPS 和 PPS id 号

  切片(像条)参数
  int i_slice_max_size;  每片字节的最大数,包括预计的NAL开销.
  int i_slice_max_mbs;  每片宏块的最大数,重写 i_slice_count
  int i_slice_count;  每帧的像条数目: 设置矩形像条.

   Optional callback for freeing this x264_param_t when it is done being used.
  * Only used when the x264_param_t sits in memory for an indefinite period of time,
  * i.e. when an x264_param_t is passed to x264_t in an x264_picture_t or in zones.
  * Not used when x264_encoder_reconfig is called directly.
  void (*param_free)( void* );
} x264_param_t;*/

bool H264Encoder::init(int iWidth, int iHeight, int iFps) {
232
	if (_pX264Handle) {
xzl committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
		return true;
	}
	x264_param_t X264Param, *pX264Param = &X264Param;
	//* 配置参数
	//* 使用默认参数
	x264_param_default_preset(pX264Param, "ultrafast", "zerolatency");

	//* cpuFlags
	pX264Param->i_threads = X264_SYNC_LOOKAHEAD_AUTO;		//* 取空缓冲区继续使用不死锁的保证.
	//* video Properties
	pX264Param->i_width = iWidth; //* 宽度.
	pX264Param->i_height = iHeight; //* 高度
	pX264Param->i_frame_total = 0; //* 编码总帧数.不知道用0.
	pX264Param->i_keyint_max = iFps * 3; //ffmpeg:gop_size 关键帧最大间隔
	pX264Param->i_keyint_min = iFps * 1; //ffmpeg:keyint_min 关键帧最小间隔
	//* Rate control Parameters
	pX264Param->rc.i_bitrate = 5000;		//* 码率(比特率,单位Kbps)
	pX264Param->rc.i_qp_step = 1;	//最大的在帧与帧之间进行切变的量化因子的变化量。ffmpeg:max_qdiff
	pX264Param->rc.i_qp_min = 10;	//ffmpeg:qmin;最小的量化因子。取值范围1-51。建议在10-30之间。
	pX264Param->rc.i_qp_max = 41;	//ffmpeg:qmax;最大的量化因子。取值范围1-51。建议在10-30之间。
	pX264Param->rc.f_qcompress = 0.6;//ffmpeg:qcompress 量化器压缩比率0-1.越小则比特率越区域固定,但是越高越使量化器参数越固定
	pX264Param->analyse.i_me_range = 16;		//ffmpeg:me_range 运动侦测的半径
	pX264Param->i_frame_reference = 3;		//ffmpeg:refsB和P帧向前预测参考的帧数。取值范围1-16。
											//该值不影响解码的速度,但是越大解码
											//所需的内存越大。这个值在一般情况下
											//越大效果越好,但是超过6以后效果就
											//不明显了。

	pX264Param->analyse.i_trellis = 1;							//ffmpeg:trellis
	//pX264Param->analyse.i_me_method=X264_ME_DIA;//ffmpeg:me_method ME_ZERO 运动侦测的方式
	pX264Param->rc.f_qblur = 0.5;		//ffmpeg:qblur

	//* bitstream parameters
	/*open-GOP
	 码流里面包含B帧的时候才会出现open-GOP。
	 一个GOP里面的某一帧在解码时要依赖于前一个GOP的某些帧,
	 这个GOP就称为open-GOP。
	 有些解码器不能完全支持open-GOP码流,
	 例如蓝光解码器,因此在x264里面open-GOP是默认关闭的。
272
	 对于解码端,接收到的码流如果如下:I0 B0 B1 P0 B2 B3...这就是一个open-GOP码流(I帧后面紧跟B帧)。
xzl committed
273
	 因此B0 B1的解码需要用到I0前面一个GOP的数据,B0 B1的dts是小于I0的。
274
	 如果码流如下: I0 P0 B0 B1 P1 B2 B3...这就是一个close-GOP码流,
xzl committed
275 276 277
	 I0后面所有帧的解码不依赖于I0前面的帧,I0后面所有帧的dts都比I0的大。
	 如果码流是IDR0 B0 B1 P0 B2 B3...那个这个GOP是close-GOP,B0,B1虽然dst比IDR0小,
	 但编解码端都刷新了参考缓冲,B0,B1参考不到前向GOP帧。
278
	 对于编码端,如果编码帧类型决定如下: ...P0 B1 B2 P3 B4 B5 I6这就会输出open-Gop码流 (P0 P3 B1 B2 I6 B4 B5...),
xzl committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	 B4 B5的解码依赖P3。
	 如果编码帧类型决定如下...P0 B1 B2 P3 B4 P5 I6这样就不会输出open-GOP码流(P0 P3 B1 B2 P5 B4 I6...)。
	 两者区别在于I6前面的第5帧是设置为B帧还是P帧,
	 如果一个GOP的最后一帧(上例中是第5帧)设置为B帧,
	 这个码流就是open-GOP,设置为P帧就是close-GOP。
	 由于B帧压缩性能好于P帧,因此open-GOP在编码性能上稍微优于close-GOP,
	 但为了兼容性和少一些麻烦,还是把opne-GOP关闭的好。*/
	pX264Param->b_open_gop = 0;
	pX264Param->i_bframe = 0;		//最大B帧数.
	pX264Param->i_bframe_pyramid = 0;
	pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;
	//* Log
	pX264Param->i_log_level = X264_LOG_ERROR;

	//* muxing parameters
	pX264Param->i_fps_den = 1; //* 帧率分母
	pX264Param->i_fps_num = iFps; //* 帧率分子
	pX264Param->i_timebase_den = pX264Param->i_fps_num;
	pX264Param->i_timebase_num = pX264Param->i_fps_den;

	pX264Param->analyse.i_subpel_refine = 1; //这个参数控制在运动估算过程中质量和速度的权衡。Subq=5可以压缩>10%于subq=1。1-7
	pX264Param->analyse.b_fast_pskip = 1; //在P帧内执行早期快速跳跃探测。这个经常在没有任何损失的前提下提高了速度。

	pX264Param->b_annexb = 1; //1前面为0x00000001,0为nal长度
	pX264Param->b_repeat_headers = 1; //关键帧前面是否放sps跟pps帧,0 否 1,放

	//* 设置Profile.使用baseline
	x264_param_apply_profile(pX264Param, "high");

	//* 打开编码器句柄,通过x264_encoder_parameters得到设置给X264
	//* 的参数.通过x264_encoder_reconfig更新X264的参数
310 311
	_pX264Handle = x264_encoder_open(pX264Param);
	if (!_pX264Handle) {
xzl committed
312 313
		return false;
	}
314 315 316 317 318 319
	_pPicIn = new x264_picture_t;
	_pPicOut = new x264_picture_t;
	x264_picture_init(_pPicIn);
	x264_picture_init(_pPicOut);
	_pPicIn->img.i_csp = X264_CSP_I420;
	_pPicIn->img.i_plane = 3;
xzl committed
320 321 322 323 324
	return true;
}

int H264Encoder::inputData(char* apcYuv[3], int aiYuvLen[3], int64_t i64Pts, H264Frame** ppFrame) {
	//TimeTicker1(5);
325 326 327 328 329 330 331
	_pPicIn->img.i_stride[0] = aiYuvLen[0];
	_pPicIn->img.i_stride[1] = aiYuvLen[1];
	_pPicIn->img.i_stride[2] = aiYuvLen[2];
	_pPicIn->img.plane[0] = (uint8_t *) apcYuv[0];
	_pPicIn->img.plane[1] = (uint8_t *) apcYuv[1];
	_pPicIn->img.plane[2] = (uint8_t *) apcYuv[2];
	_pPicIn->i_pts = i64Pts;
xzl committed
332 333 334
	int iNal;
	x264_nal_t* pNals;

335 336
	int iResult = x264_encoder_encode(_pX264Handle, &pNals, &iNal, _pPicIn,
			_pPicOut);
xzl committed
337 338 339 340 341
	if (iResult <= 0) {
		return 0;
	}
	for (int i = 0; i < iNal; i++) {
		x264_nal_t pNal = pNals[i];
342 343 344
		_aFrames[i].iType = pNal.i_type;
		_aFrames[i].iLength = pNal.i_payload;
		_aFrames[i].pucData = pNal.p_payload;
xzl committed
345
	}
346
	*ppFrame = _aFrames;
xzl committed
347 348 349
	return iNal;
}

xiongziliang committed
350
} /* namespace mediakit */
xzl committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

#endif //ENABLE_X264