首页 > 其他分享 >毕设系列之Libx264实时视频流(YUV 420P转H264视频编码篇)

毕设系列之Libx264实时视频流(YUV 420P转H264视频编码篇)

时间:2023-02-11 16:58:47浏览次数:53  
标签:毕设 H264 pPic Libx264 pX264Param int x264 mvl id

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=035)
  本文发布于 2017-08-07 16:30:54,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=035)

环境说明

  Ubuntu 16.04 LTS

前言


  无





Libx264实时视频流


  本文的技术实现部分参考雷博士的这篇文章。http://blog.csdn.net/leixiaohua1020/article/details/42078645

  1. 现在网上关于H264的文章有很多,但是我个人认为最好的就是雷霄骅博士的x264部分的文章最详细。所以许多的细节部分,我推荐大家去雷博士的blog去看。本文只提及我们使用Libx264时候,我们要注意的问题。
  2. 使用Libx264时候,我们需要关注的东西(下面用我的代码来说明假如我们要使用Libx264,那么我们需要注意的几个事情)。
//encoder
	x264_t * pX264Handle;//结构体是一个编码器实例句柄,要使用这个编码库,我们必须有一个这种变量,没有为啥。
//param
	x264_param_t * pX264Param;//这个结构体就比较重要了,他是我们设置编码器参数的载体,我们必须具体的了解各种参数的意义。具体参数在下一节进行分析。
//input,output pic
	x264_picture_t *pPic_In;//这就是YUV输入图像和输出图像的载体,这里面有一个pts参数需要注意,下面小节进行说明。
	x264_picture_t *pPic_Out;
//output h264 stream
	x264_nal_t * pNals;//这个也是比较重要的一个东西,他的作用是用来保存编码后,网络抽象层所保存的数据(NAL HEADER,NAL BODY),想具体了解,可以去看H264编码原理。
//user config callback
	//UESER_CONF_CALLBACK yX264_UserConfig;
	int (*yX264_UserConfig)(struct ymx264 * mvl);//私有,忽略
//pi_nal is the number of NAL units 
	int pi_nal;//网络抽象单元个数
  1. 编码器参数分析
	//* cpuFlags  
    mvl->pX264Param->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;//* 取空缓冲区继续使用不死锁的保证. 
	//* 视频选项  
    mvl->pX264Param->i_width   = FRAME_WIDTH; //* 要编码的图像宽度.  
    mvl->pX264Param->i_height  = FRAME_HEIGHT; //* 要编码的图像高度  
	mvl->pX264Param->i_frame_total = 0; //* 编码总帧数.不知道用0.  
	/* Force an IDR keyframe at this interval */
    mvl->pX264Param->i_keyint_max = 10; //这个参数很重要,控制i帧的频率
  
	mvl->pX264Param->b_repeat_headers = 1;  // 重复SPS/PPS 放到关键帧前面//做实时流播放,此参数必须ENABLE
	//* 流参数  
	//* how many b-frame between 2 references pictures */
    mvl->pX264Param->i_bframe  = 5;  
	//
    mvl->pX264Param->b_open_gop  = 0;  
	//* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */
    mvl->pX264Param->i_bframe_pyramid = 0;  
	//
    mvl->pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;  
	 
	 
	//* Log参数,不需要打印编码信息时直接注释掉就行  
    //mvl->pX264Param->i_log_level  = X264_LOG_DEBUG; 
 
	//* 速率控制参数  
    //pX264Param->rc.i_bitrate = 1024 * 10;//* 码率(比特率,单位Kbps) ,重要

	//* muxing parameters  帧率控制,重要。
    mvl->pX264Param->i_fps_den  = 1; //* 帧率分母
    mvl->pX264Param->i_fps_num  = Y_STREAM_FPS;//* 帧率分子  

    mvl->pX264Param->i_timebase_den = mvl->pX264Param->i_fps_num;  
    mvl->pX264Param->i_timebase_num = mvl->pX264Param->i_fps_den;  

  最后,我们需要注意一点:关于我们设置的帧率的问题,不一定是设置多少,播放的时候就是多少,只是一个参考值,编码器会尽量的把视频编码为这个帧率。

  1. x264_picture_t * pPic_In->i_pts += 1; 此参数非常重要。如果不进行设置,视频流将不会正常播放。
 /*
PTS:Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS:Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。
*/
  1. 关于颜色空间的问题,大家可以去百度YUV 420 ,YUV 422,YUV 444等这些原始图像的存储问题。具体来说,他们分为两类,一种是分组存储(例如:YYYUUUVVV*),一种是交叉存储(例如:YUYV)
  2. 此模块我的源代码
    ym_x264.h
/*
	FileName:ym_x264.h
	Version:1.0
	Description:
	Created On: 2017-3-19
	Modified date:
	Author:Sky
*/

#ifndef _YM_X264_H
#define _YM_X264_H

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */

#include <stdint.h>
#include <stdio.h>

#include <ym_x264_config.h>

#include <x264.h>

#include <stdlib.h>




#define CLEAR_MEM(x) memset(&(x),0,sizeof(x))

enum yX264Cmd{

	DO_DEFAULT_PRESET = 0,
	DO_DEFAULT_USERCONF = 1,
	DO_PARAM_APPLY_PROFILE = 2,
	OPEN_ENCODER = 3,
	ENCODER_ENCODE = 4,
	
};
enum yX264ColorSpace{
	
	Y_CSP_I444 = 0,
	Y_CSP_I422 = 1,
	Y_CSP_I420 = 2,
	Y_CSP_YUYV = 3,
};

//typedef struct ymx264 yMX264;



typedef struct ymx264{
		
//encoder
	x264_t * pX264Handle;
//param
	x264_param_t * pX264Param;
//input,output pic
	x264_picture_t *pPic_In;
	x264_picture_t *pPic_Out;
//output h264 stream
	x264_nal_t * pNals;
//user config callback
	//UESER_CONF_CALLBACK yX264_UserConfig;
	int (*yX264_UserConfig)(struct ymx264 * mvl);
//pi_nal is the number of NAL units 
	int pi_nal;
	
	long cur_pts;
	
}yMX264; 

typedef int (*UESER_CONF_CALLBACK)(yMX264 * mvl);

int yInitMX264(yMX264 * mvl);
int yDestroyMX264(yMX264 * mvl);
int yIoctlX264(enum yX264Cmd cmd,...);
//CSC = ColorSpaceCovert,FIP = Fill In_Pic
int yDoCSC_And_FIP(yMX264 * mvl,enum yX264ColorSpace t_csp, int cache_id);

int yDo_Default_UserConf(yMX264 * mvl);


#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */ 


#endif

ym_x264.c

/*
	FileName:ym_x264.c
	Version:1.0
	Description:
	Created On: 2017-3-19
	Modified date:
	Author:Sky
*/

/*
x264_param_default():设置参数集结构体x264_param_t的缺省值。
x264_picture_alloc():为图像结构体x264_picture_t分配内存。
x264_encoder_open():打开编码器。
x264_encoder_encode():编码一帧图像。
x264_encoder_close():关闭编码器。
x264_picture_clean():释放x264_picture_alloc()申请的资源。
 
存储数据的结构体如下所示。
x264_picture_t:存储压缩编码前的像素数据。
x264_nal_t:存储压缩编码后的码流数据。
*/
#include <ym_x264.h>

uint8_t ImgCache[ImageCacheNum][FRAME_SIZE];
int yInitMX264(yMX264 * mvl){


	mvl->pX264Param = (x264_param_t *)malloc(sizeof(x264_param_t));
	
//	for (int i = 0; i < ImageCacheNum; i++){

		mvl->pPic_In = (x264_picture_t *)malloc(sizeof(x264_picture_t));
		mvl->pPic_Out = (x264_picture_t *)malloc(sizeof(x264_picture_t));
		
		mvl->pNals = NULL;

		mvl->pi_nal = 0;

		x264_picture_init(mvl->pPic_Out);  
    	x264_picture_alloc(mvl->pPic_In, FRAME_COLORSPACE, FRAME_WIDTH, FRAME_HEIGHT);

		// PTS FROM 0,AND AUTO INCRESE 1
		mvl->pPic_In->i_pts = 0;
//	}

	mvl->cur_pts = 0;




	return 0;
}
int yDestroyMX264(yMX264 * mvl){

	// 清除图像区域  
	//for (int i = 0; i < ImageCacheNum; i++)
	x264_picture_clean(mvl->pPic_In);  
    x264_encoder_close(mvl->pX264Handle);  


	free(mvl->pX264Param);
	//for (int i = 0; i < ImageCacheNum; i++){
	
		free(mvl->pPic_In);
		free(mvl->pPic_Out);
	//}


	return 0;
}

int yIoctlX264(enum yX264Cmd cmd,...){

	va_list arg;
    va_start(arg,cmd);
    yMX264 *mx264;
    mx264 = va_arg(arg,yMX264 *);
    va_end(arg);

	switch(cmd){

		case DO_DEFAULT_PRESET:
		{
			/* Get default params for preset/tuning */
			//x264_param_default(pParam);  //this do default set for x264,but can not config some info
    		if( x264_param_default_preset( mx264->pX264Param, "veryfast", "zerolatency" ) < 0 ){
		
				printf("x264_param_default_preset failed!\n");
				return -1;
			}
			break;
		}
		case DO_DEFAULT_USERCONF:
		{
			//va_list arg;
    		//va_start(arg,cmd);
    		//mx264->yX264_UserConfig = va_arg(arg,UESER_CONF_CALLBACK);
			//mx264->yX264_UserConfig = NULL;
			
    		//va_end(arg);

			if ( (*mx264->yX264_UserConfig)(mx264) < 0){
				
				printf("Do user conf callback failed.\n");
				return -1;
			}

			break;
		}
		case DO_PARAM_APPLY_PROFILE:
		{
			//x264_profile_names[0] = baseline , to set stream-quality
			if (x264_param_apply_profile(mx264->pX264Param, x264_profile_names[0]) < 0 ){

				printf("x264_param_apply_profile failed.\n");
				return -1;
			} 
			break;
		}
		case OPEN_ENCODER:
		{
			//open encoder
			if( (mx264->pX264Handle = x264_encoder_open(mx264->pX264Param)) == NULL){
				
				printf("x264_encoder_open failed.\n");
				return -1;				
			}
			break;
		}
		case ENCODER_ENCODE:
		{
/*
	x264_encoder_encode:
 *      encode one picture.
 *      *pi_nal is the number of NAL units outputted in pp_nal.
 *      returns the number of bytes in the returned NALs.
 *      returns negative on error and zero if no NAL units returned.
 *      the payloads of all output NALs are guaranteed to be sequential in memory. 
	int     x264_encoder_encode( x264_t *, x264_nal_t **pp_nal, int *pi_nal, x264_picture_t *pic_in, x264_picture_t *pic_out );
*/



			if ( x264_encoder_encode(mx264->pX264Handle, &mx264->pNals, &mx264->pi_nal, mx264->pPic_In, mx264->pPic_Out) < 0){
				
				printf("x264_encoder_encode failed.\n");
				return -1;					
				
			}
/*
PTS:Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS:Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。
*/
			//Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
			//MUST DO THIS ,IT DECIDE ,主要用于度量解码后的视频帧什么时候被显示出来
			//mx264->pPic_In->i_pts += 1;
			mx264->pPic_In->i_pts += 1; 
			printf("pts = %d\n",mx264->pPic_In->i_pts);
			break; 
		}
		default :
		{
			printf("No this cmd to analyse\n");
			return -1;
			break;
		}
	}

	return 0;
}

int yDoCSC_And_FIP(yMX264 * mvl,enum yX264ColorSpace t_csp, int cache_id){

	switch(t_csp){  
        case Y_CSP_I444:
		{  
/*
            read(fd_in,pPic_In->img.plane[0],FRAME_SIZE);         //Y  
            read(fd_in,pPic_In->img.plane[1],FRAME_SIZE);         //U  
            read(fd_in,pPic_In->img.plane[2],FRAME_SIZE);         //V  
*/
            break;
		}  
        case Y_CSP_I420:
		{  
/*
			#ifndef ENABLE_YUYVTOI420 
            read(fd_in,pPic_In->img.plane[0],FRAME_SIZE);         //Y  
            read(fd_in,pPic_In->img.plane[1],FRAME_SIZE/4);     //U  
            read(fd_in,pPic_In->img.plane[2],FRAME_SIZE/4);     //V  
			#else
			//YUYV to I420
			read(fd_in,Cache,FRAME_SIZE*2);         //read one frame to cache 
			//must set to 0
			int id_u = 0,  id_v = 0 , id_y = 0;
			for (int i = 0; i < FRAME_SIZE*2 ;i+=4){ 
				
				pPic_In->img.plane[0][id_y] = Cache[i];//get Y
				id_y++;
				pPic_In->img.plane[0][id_y] = Cache[i+2];//get Y
				id_y++;
				if ( ((int)((i)/1280)%2) == 0 ){
					pPic_In->img.plane[1][id_u] = Cache[i+1];//get U
					pPic_In->img.plane[2][id_v] = Cache[i+3];//get V
					id_u++;
					id_v++;
				}
			}
	
			#endif
*/
            break;
		} 
		case Y_CSP_YUYV:{
			// firstly,Do YUYV to I420 ,then, Fill in_pic
			int id_u = 0,  id_v = 0 , id_y = 0;
			for (int i = 0; i < FRAME_SIZE ;i+=4){ 
				
				mvl->pPic_In->img.plane[0][id_y] = ImgCache[cache_id][i];//get Y
				id_y++;
				mvl->pPic_In->img.plane[0][id_y] = ImgCache[cache_id][i+2];//get Y
				id_y++;
				if ( ((int)((i)/1280)%2) == 0 ){

					mvl->pPic_In->img.plane[1][id_u] = ImgCache[cache_id][i+1];//get U
					mvl->pPic_In->img.plane[2][id_v] = ImgCache[cache_id][i+3];//get V
					id_u++;
					id_v++;
				}
			}
			break;
		}
		case Y_CSP_I422:
		{  
/*
			
            read(fd_in,pPic_In->img.plane[0],FRAME_SIZE);         //Y  
            read(fd_in,pPic_In->img.plane[1],FRAME_SIZE/2);     //U  
            read(fd_in,pPic_In->img.plane[2],FRAME_SIZE/2);     //V 
*/
            break;
		} 

        default:
		{  
            printf("Colorspace Not Support.\n");  
            return -1;
        }  
	}

}

int yDo_Default_UserConf(yMX264 * mvl){


	//* cpuFlags  
    mvl->pX264Param->i_threads  = X264_SYNC_LOOKAHEAD_AUTO;//* 取空缓冲区继续使用不死锁的保证. 
	//* 视频选项  
    mvl->pX264Param->i_width   = FRAME_WIDTH; //* 要编码的图像宽度.  
    mvl->pX264Param->i_height  = FRAME_HEIGHT; //* 要编码的图像高度  
	mvl->pX264Param->i_frame_total = 0; //* 编码总帧数.不知道用0.  
	/* Force an IDR keyframe at this interval */
    mvl->pX264Param->i_keyint_max = 10; 
  
	mvl->pX264Param->b_repeat_headers = 1;  // 重复SPS/PPS 放到关键帧前面
	//* 流参数  
	//* how many b-frame between 2 references pictures */
    mvl->pX264Param->i_bframe  = 5;  
	//
    mvl->pX264Param->b_open_gop  = 0;  
	//* Keep some B-frames as references: 0=off, 1=strict hierarchical, 2=normal */
    mvl->pX264Param->i_bframe_pyramid = 0;  
	//
    mvl->pX264Param->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;  
	 
	 
	//* Log参数,不需要打印编码信息时直接注释掉就行  
    //mvl->pX264Param->i_log_level  = X264_LOG_DEBUG; 
 
	//* 速率控制参数  
    //pX264Param->rc.i_bitrate = 1024 * 10;//* 码率(比特率,单位Kbps) 

	//* muxing parameters  
    mvl->pX264Param->i_fps_den  = 1; //* 帧率分母
    mvl->pX264Param->i_fps_num  = Y_STREAM_FPS;//* 帧率分子  

    mvl->pX264Param->i_timebase_den = mvl->pX264Param->i_fps_num;  
    mvl->pX264Param->i_timebase_num = mvl->pX264Param->i_fps_den;  

	return 0;
}




后记


  无

参考文献




打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

标签:毕设,H264,pPic,Libx264,pX264Param,int,x264,mvl,id
From: https://www.cnblogs.com/Iflyinsky/p/17111986.html

相关文章