提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
《libx264编码摄像头数据为H264代码实现》链接:
摄像头数据使用x264编码H264代码实现
随着互联网的发展,短视频、视频直播、视频会议、视频监控在人们的生活中越来越普遍,也越来越重要。当前视频制作使用最多的编码方案之一就是h264。
目前最受欢迎的编码h264的免费开源软件当数x264。x264不仅提供了一个命令行工具进行编码,还提供了一组丰富的接口(API)可以被调用来编码h264视频。x264开源软件下载路径为:http://download.videolan.org/pub/videolan/x264/snapshots/。
该函数的主要功能是入参进行初始化,也是对x264编码器设置默认参数。关于x264_param_t 结构体的定义在x264.h中,x264_param_t 的参数有很多,很多参数适合专业的编码其开发人员去设置,专业的编码器开发人员根据视频场景,通过设置参数可以编码出更好画质和更高的编码效率,如果对x264或者h264不够精通的人员调整太复杂的参数可能导致编码出来的画质差,码率高等问题;所以一般情形下使用x264默认编码参数即可。通常情况下我们需要配置的编码参数有:
typedef struct x264_param_t
{
/* Video Properties */
int i_width;
int i_height;
int i_frame_total;
int i_keyint_max; /* Force an IDR keyframe at this interval */
int i_keyint_min; /* Scenecuts closer together than this are coded as I, not IDR. */
int b_open_gop;
int i_bframe; /* how many b-frame between 2 references pictures */
int i_bframe_pyramid; /* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */
int i_bframe_adaptive;
int i_log_level;
uint32_t i_fps_num;
uint32_t i_fps_den;
uint32_t i_timebase_num; /* Timebase numerator */
uint32_t i_timebase_den; /* Timebase denominator */
/* Rate control parameters */
struct
{
int i_bitrate;
} rc;
} x264_param_t;
i_width、i_height:是编码视频的分辨率,如1280x720;
i_frame_total:指定编码器编码的yuv帧数,通常可以不设置或者设置0,编码yuv帧数由上层调用者来决定编码帧个数。
i_keyint_max、i_keyint_min:表示关键帧(IDR)帧的最大间隔,最小间隔。关键帧的间隔对码率,编码效率,以及解码都有影响(间隔越小码率大,编码复杂度低、速度快,丢帧/丢包/帧错误解码恢复越快等)。
b_open_gop:设置开放GOP/闭合GOP,当该值设置为1表示是开放GOP,参考帧可以跨越IDR,即当前GOP的P帧/B帧可以参考前面的GOP。当该值设置为0表示是闭合GOP,当前GOP内的P帧/B帧不可以参考前面的GOP,只能参考当前GOP里面的参考帧。
i_bframe:b帧的个数。b帧是双向参考帧,其压缩率比I帧/P帧都要高,也就是说B帧可以很小,但是同时B帧的压缩算法复杂度很高,会带来编码性能上的损失,导致编码速度降低,同时B帧的存在会导致视频存在一定延时,不合适实时性要求很高的场景。
i_bframe_pyramid:该参数是否允许B帧做参考帧。
i_bframe_adaptive:B帧决策算法。取值有X264_B_ADAPT_NONE表示不做B帧类型决策固定i_bframe的数目;X264_B_ADAPT_FAST表示采用快速B帧类型决策算法;X264_B_ADAPT_TRELLIS采用更加准确的B帧类型决策算法。配置X264_B_ADAPT_FAST和X264_B_ADAPT_TRELLIS,编码器会根据视频场景计算当前帧是否合适作为B帧,前者算法速度块,但是B帧决策不够准确,后者算法速度慢,对B帧类型的决策比较准确。
i_log_level:x264库在运行时的日志输出等级。可以编码异常的时候开启更多打印来分析问题。
i_fps_num、i_fps_den:用户设置视频编码帧率,如编码25帧,可以设置i_fps_den=1,i_fps_num=25;
i_timebase_num、i_timebase_den:设置编码器的时间基准,其值和帧率正相关。
该函数用于不同场景下的参数“一键”配置;该函数根据preset,tune 来调整默认编码参数。
*x264_param_t 参数 :是编码器默认参数的地址;
preset:用于预设编码器,其值定义如下,在x264_preset_names[]里面前往后编码速度会越来越慢,但编码质量、码率等会越来越好。即该参数是编码器速度和编码器效率的单项调整。在实时场景往往会选择ultrafast,编码速度最快,在高清蓝光等可以选择veryslow或者placebo,画质码率最佳,但是速度很慢。
static const char * const x264_preset_names[] = { "ultrafast", "superfast", "veryfast", "faster", "fast", "medium", "slow", "slower", "veryslow", "placebo", 0 };
tune :用于优化不同场景下的编码参数,其值定义如下,x264_tune_names[]里面定义了不同的场景,如zerolatency(编码零延时),animation(编码动漫视频),根据不同的场景可以设置不同的参数从而达到编码质量压缩率最高的目的。
static const char * const x264_tune_names[] = { "film", "animation", "grain", "stillimage", "psnr", "ssim", "fastdecode", "zerolatency", 0 };
该函数用于设置h264编码的的profile级别。x264的profile 有
“baseline”, “main”, “high”, “high10”, “high422”, “high444”;
不同的profile级别对编码器或者说对视频参数存在不同限制。h264编码器profile级别对编码参数的限制如下图:
该函数用于打开一个编码器;根据 x264_param_t *参数创建x264编码器并返回x264编码器句柄指针。
在开始编码yuv之前可以通过该函数用于获取该编码器中输出码流的nal单元部信息数据,包含SPS、PPS、SEI等信息。
第一个参数:x264_encoder_open返回的句柄。
pp_nal:存放SPS、PPS等nal单元信息的地址;x264_nal_t 结构体的定义如下:
typedef struct x264_nal_t
{
int i_ref_idc; /* nal_priority_e */
int i_type; /* nal_unit_type_e */
int b_long_startcode;
int i_first_mb; /* If this NAL is a slice, the index of the first MB in the slice. */
int i_last_mb; /* If this NAL is a slice, the index of the last MB in the slice. */
/* Size of payload (including any padding) in bytes. */
int i_payload;
/* If param->b_annexb is set, Annex-B bytestream with startcode.
* Otherwise, startcode is replaced with a 4-byte size.
* This size is the size used in mp4/similar muxing; it is equal to i_payload-4 */
uint8_t *p_payload;
/* Size of padding in bytes. */
int i_padding;
} x264_nal_t;
iNal:nal单元头的个数,比如nal只有SPS、PPS则该值为2,如果还包含了SEI该值为3.
该函数是在编码流程中可选的,根据需要进行调用,如果需要在编码yuv之前需要知道码流的信息和先调用该函数。
该函数用于初始化x264_picture_t 结构体,即用于设置pic 参数。
x264_picture_t结构体的定义如下:
typedef struct x264_picture_t
{
int i_type;
/* In: force quantizer for != X264_QP_AUTO */
int i_qpplus1;
int i_pic_struct;
int b_keyframe;
/* In: user pts, Out: pts of encoded picture (user)*/
int64_t i_pts;
int64_t i_dts;
x264_param_t *param;
x264_image_t img;
/* In: optional information to modify encoder decisions for this frame
* Out: information about the encoded frame */
x264_image_properties_t prop;
/* Out: HRD timing information. Output only when i_nal_hrd is set. */
x264_hrd_t hrd_timing;
/* In: arbitrary user SEI (e.g subtitles, AFDs) */
x264_sei_t extra_sei;
/* private user data. copied from input to output frames. */
void *opaque;
} x264_picture_t;
该函数用于申请编码yuv数据缓存。根据yuv类型i_csp、编码分辨率来申请合适大小的缓存。
pic:输入图像参数变量。
i_csp:x264编码采用的yuv格式,目前是X264_CSP_I420(yuv 4:2:0 planar);
i_width、i_height:编码分辨率。
该函数用于编码一帧yuv数据,并返回当前帧的nal单元数据,以及编码过程的重建yuv。
第一个参:编码器句柄。
pp_nal:存放SPS、PPS等nal单元信息的地址
iNal:nal单元头的个数,比如nal只有SPS、PPS则该值为2,如果还包含了SEI该值为3.
pic_in:输入yuv帧数据,当该值为空的时候会刷新编码器缓冲区强制输出最后一帧编码数据。通常在编码结束关闭编码器前调用x264_encoder_encode函数并将pic_in设置NULL,来强制输出最后一帧确保编码器缓存为空。
pic_out :通常是编码过程中该帧编码后重建的yuv。
该函数用于获取当前编码器缓存延延迟输出的编码帧个数。通常编码码流存在B帧的时候存在延迟输出的帧。一般在需要编码B帧的时候该函数和x264_encoder_encode()来强制输出编码缓存的帧,来确保编码器之后能彻底关闭。示例如下:
while(x264_encoder_delayed_frames(x264Handle)) {
x264_encoder_encode(x264Handle, &pNals, &iNal, null, pPicOut);
}
该函数用于释放x264_picture_alloc函数申请的资源(内存);
该函数用于关闭编码并释放资源。
入参为:x264_encoder_open函数返回的句柄。
h264_enc_init函数申请编码参数资源并根据编码分辨率帧率设置编码参数,该套参数通常用于低延时场景。
x264_param_t *h264_enc_init(int width,int height,int fps)
{
x264_param_t* pX264Param = new x264_param_t;
x264_param_default(pX264Param);
x264_param_default_preset(pX264Param, "veryfast", "zerolatency");
pX264Param->i_width = width; //* width
pX264Param->i_height = height; //* height
pX264Param->i_keyint_max = 2 * fps;
pX264Param->rc.i_bitrate = 1024 * 512; //设置编码码率512kbps
pX264Param->i_fps_den = 1; //编码帧率
pX264Param->i_fps_num = fps;
x264_param_apply_profile(pX264Param, x264_profile_names[0]); //baseline
return pX264Param;
}
x264_t* h264_enc_open(x264_param_t *pH264Param)
{
return x264_encoder_open(pH264Param);
}
void h264_enc_close(x264_t *encHandle)
{
x264_encoder_close(encHandle);
}
x264_picture_t* h264_enc_pic_alloc(x264_param_t *pH264Param)
{
x264_picture_t* pPic = new x264_picture_t;
x264_picture_alloc(pPic, X264_CSP_I420, pH264Param->i_width, pH264Param->i_height);
pPic->img.i_csp = X264_CSP_I420; //设置编码数据yuv420p
pPic->img.i_plane = 3; //编码数据有y、u、v三个通道
return pPic;
}
void h264_enc_pic_free(x264_picture_t *pPic)
{
x264_picture_clean(pPic);
}
int h264_encoder(x264_t* pX264Handle, x264_picture_t* pPicIn,x264_nal_t* pNals)
{
x264_picture_t pic_out;
int pi_nal,ret;
if(pPicIn) //编码一帧yuv
{
ret = x264_encoder_encode(pX264Handle, &pNals, &pi_nal, pPicIn, &pic_out);
if(ret < 0)
{
std::cout << " x264_encoder_encode error"<< std::endl;
}
}
else //刷新编码缓存-通常是关闭编码器前的操作
{
int maxNum = 16; //编码器帧缓存是小于16的
while(x264_encoder_delayed_frames(pX264Handle) && maxNum) //确保编码缓冲的码流数据全部被清空,通常编码B帧的情况需要这样处理
{
ret = x264_encoder_encode(pX264Handle, &pNals, &pi_nal, NULL, &pic_out);
if(ret < 0)
{
std::cout << " x264_encoder_encode error"<< std::endl;
}
maxNum--;
}
}
}
void h264_enc_deinit(x264_param_t *pX264Param)
{
delete pX264Param;
}
先初始化编码参数、申请编码图像资源、打开编码器、编码yuv、刷新编码器资源、关闭编码器、释放编码图像资源、释放编码参数。