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

2024-06-16 07:08

本文主要是介绍毕设系列之Libx264实时视频流(YUV 420P转H264视频编码篇),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

#PS:这个只是 《 我自己 》理解,如果和你的

#原则相冲突,请谅解,勿喷

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

1、现在网上关于H264的文章有很多,但是我个人认为最好的就是雷霄骅博士的x264部分的文章最详细。所以许多的细节部分,我推荐大家去雷博士的blog去看。本文只提及我们使用Libx264时候,我们要注意的问题。
2、 使用Libx264时候,我们需要关注的东西(下面用我的代码来说明假如我们要使用Libx264,那么我们需要注意的几个事情)。

//encoderx264_t * pX264Handle;//结构体是一个编码器实例句柄,要使用这个编码库,我们必须有一个这种变量,没有为啥。
//paramx264_param_t * pX264Param;//这个结构体就比较重要了,他是我们设置编码器参数的载体,我们必须具体的了解各种参数的意义。具体参数在下一节进行分析。
//input,output picx264_picture_t *pPic_In;//这就是YUV输入图像和输出图像的载体,这里面有一个pts参数需要注意,下面小节进行说明。x264_picture_t *pPic_Out;
//output h264 streamx264_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;//网络抽象单元个数

3、 编码器参数分析

    //* 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;  

最后,我们需要注意一点:关于我们设置的帧率的问题,不一定是设置多少,播放的时候就是多少,只是一个参考值,编码器会尽量的把视频编码为这个帧率。
4、x264_picture_t * pPic_In->i_pts += 1; 此参数非常重要。如果不进行设置,视频流将不会正常播放。
/*
PTS:Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS:Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。
*/
5、关于颜色空间的问题,大家可以去百度YUV 420 ,YUV 422,YUV 444等这些原始图像的存储问题。具体来说,他们分为两类,一种是分组存储(例如:YYY*UUU*VVV*),一种是交叉存储(例如:YUYV)
6、此模块我的源代码
ym_x264.h

/*FileName:ym_x264.hVersion:1.0Description:Created On: 2017-3-19Modified 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{//encoderx264_t * pX264Handle;
//paramx264_param_t * pX264Param;
//input,output picx264_picture_t *pPic_In;x264_picture_t *pPic_Out;
//output h264 streamx264_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.cVersion:1.0Description:Created On: 2017-3-19Modified 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 1mvl->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 infoif( 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-qualityif (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 encoderif( (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;                  }
/*
PTSPresentation Time StampPTS主要用于度量解码后的视频帧什么时候被显示出来
DTSDecode Time StampDTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。
*///Presentation Time StampPTS主要用于度量解码后的视频帧什么时候被显示出来//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 I420read(fd_in,Cache,FRAME_SIZE*2);         //read one frame to cache //must set to 0int 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 Yid_y++;pPic_In->img.plane[0][id_y] = Cache[i+2];//get Yid_y++;if ( ((int)((i)/1280)%2) == 0 ){pPic_In->img.plane[1][id_u] = Cache[i+1];//get UpPic_In->img.plane[2][id_v] = Cache[i+3];//get Vid_u++;id_v++;}}#endif
*/break;} case Y_CSP_YUYV:{// firstly,Do YUYV to I420 ,then, Fill in_picint 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 Yid_y++;mvl->pPic_In->img.plane[0][id_y] = ImgCache[cache_id][i+2];//get Yid_y++;if ( ((int)((i)/1280)%2) == 0 ){mvl->pPic_In->img.plane[1][id_u] = ImgCache[cache_id][i+1];//get Umvl->pPic_In->img.plane[2][id_v] = ImgCache[cache_id][i+3];//get Vid_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;
}

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

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

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

这篇关于毕设系列之Libx264实时视频流(YUV 420P转H264视频编码篇)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1065775

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试