毕设系列之Libx264实时视频流(YUV 420P转H264视频编码篇)
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
- 现在网上关于H264的文章有很多,但是我个人认为最好的就是雷霄骅博士的x264部分的文章最详细。所以许多的细节部分,我推荐大家去雷博士的blog去看。本文只提及我们使用Libx264时候,我们要注意的问题。
- 使用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;//网络抽象单元个数
- 编码器参数分析
//* 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;
最后,我们需要注意一点:关于我们设置的帧率的问题,不一定是设置多少,播放的时候就是多少,只是一个参考值,编码器会尽量的把视频编码为这个帧率。
- x264_picture_t * pPic_In->i_pts += 1; 此参数非常重要。如果不进行设置,视频流将不会正常播放。
/*
PTS:Presentation Time Stamp。PTS主要用于度量解码后的视频帧什么时候被显示出来
DTS:Decode Time Stamp。DTS主要是标识读入内存中的bit流在什么时候开始送入解码器中进行解码。
*/
- 关于颜色空间的问题,大家可以去百度YUV 420 ,YUV 422,YUV 444等这些原始图像的存储问题。具体来说,他们分为两类,一种是分组存储(例如:YYYUUUVVV*),一种是交叉存储(例如:YUYV)
- 此模块我的源代码
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;
}
后记
无
参考文献
打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
PS: 请尊重原创,不喜勿喷。
PS: 要转载请注明出处,本人版权所有。
PS: 有问题请留言,看到后我会第一时间回复。
毕设系列之Libx264实时视频流(YUV 420P转H264视频编码篇)的更多相关文章
- 人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我”
人脸检测及识别python实现系列(6)——终篇:从实时视频流识别出“我” 终于到了最后一步,激动时刻就要来临了,先平复一下心情,把剩下的代码加上,首先是为Model类增加一个预测函数: #识别人脸 ...
- 人脸检测及识别python实现系列(1)——配置、获取实时视频流
人脸检测及识别python实现系列(1)——配置.获取实时视频流 1. 前言 今天用多半天的时间把QQ空间里的几篇年前的旧文搬到了这里,算是完成了博客搬家.QQ空间里还剩下一些记录自己数学学习路线的学 ...
- opencv获取IP摄像头(IP-camera)实时视频流
之前这篇文章讲了如何通过网络摄像头(web camera)获取实时视频流,但是这种方法的缺陷就是摄像头和主机必须连在一起,那这种在室外部署的时候就会非常麻烦并且不安全,所以后来找了下用海康威视或者大华 ...
- 纠删码在实时视频流中的应用丨Dev for Dev 专栏
本文为「Dev for Dev 专栏」系列内容,作者为声网网络体验团队王瑞. 01 背景 在实时音视频通话中,音视频质量受网络丢包影响较大,特别是对于视频. 为什么视频对丢包更敏感呢?通常来说,音频的 ...
- 利用flask将opencv实时视频流输出到浏览器
opencv通过webcam可以获取本地实时视频流,但是如果需要将视频流共享给其他机器调用,就可以将利用flask框架构建一个实时视频流服务器,然后其他机器可以通过向这个服务器发送请求来获取这台机器上 ...
- 在WEB显示实时视频流
转载自:https://www.jianshu.com/p/7ef5490fbef7 安装摄像头 这里使用的是树莓派的官方摄像头,使用普通的 USB 摄像头也可以,但前提是你能够搞的定它的驱动. 大概 ...
- 视频系列:RTX实时射线追踪(下)
视频系列:RTX实时射线追踪(下) Key things from part 4 光线有效载荷是从一个着色器传递到另一个着色器的结构. 这一切都发生在RTX的引擎下. 更小的有效载荷要好得多! 新的D ...
- 视频系列:RTX实时射线追踪(上)
视频系列:RTX实时射线追踪(上) Video Series: Practical Real-Time Ray Tracing With RTX RTX在游戏和应用程序中引入了一个令人兴奋的和根本性的 ...
- [常用工具] OpenCV获取网络摄像头实时视频流
所需要硬件及软件环境: python 3/OpenCV3.4 or C++11/OpenCV3.4 1 RTSP协议 RTSP (Real Time Streaming Protocol),是一种语法 ...
- JS组件系列——表格组件神器:bootstrap table(三:终结篇,最后的干货福利)
前言:前面介绍了两篇关于bootstrap table的基础用法,这章我们继续来看看它比较常用的一些功能,来个终结篇吧,毛爷爷告诉我们做事要有始有终~~bootstrap table这东西要想所有功能 ...
随机推荐
- 主机--Host
概念:主机是用于构建应用程序和服务.封装应用资源的对象,负责程序的启动和生命周期的管理,简单来说主机即应用程序. 主机运行:当主机运行的时候,他会将托管在服务容器集合里面注册的IHostService ...
- Linux 列出文件的绝对路径
1.使用ls中列出文件的绝对路径ls | sed "s:^:`pwd`/:" 2.列出当前目录下的所有文件(包括隐藏文件)的绝对路径, 对目录不做递归find $PWD -maxd ...
- [JVM]GC日志解读解析
GC日志解读解析 示例代码 package jvm.test1; import java.util.Random; import java.util.concurrent.TimeUnit; impo ...
- JS 保姆级贴心,从零教你手写实现一个防抖debounce方法
壹 ❀ 引 防抖在前端开发中算一个基础但很实用的开发技巧,在对于一些高频操作例如监听输入框值变化触发更新之类,会有奇效.除了实际开发,在面试中我们也可能偶遇手写防抖节流的问题,鉴于不同公司考核要求不一 ...
- cached地址和uncached地址的区别
cached地址和uncached地址的区别是 对cached地址的访问是委托给CPU进行的,也就是说你的操作到底是提交给真正的外设或内存,还是转到CPU缓存,是由CPU决定的.CPU有一套缓存策略来 ...
- Vue+SpringBoot+ElementUI实战学生管理系统-4.后端API编写
1.章节介绍 前一篇介绍了项目的表结构设计,这一篇编写后端API,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.项目截图 登录页 列表操作 动态图 ...
- Java集合框架学习(六) LinkedList详解
LinkedList介绍 ArrayList与LinkedList都是List接口的实现类,因此都实现了List的所有未实现的方法,只是实现的方式有所不同. LinkedList是采用链表的方式来实现 ...
- Redis 缓存过期删除/淘汰策略分析
Redis 缓存过期删除/淘汰策略分析 Redis 缓存删除 Redis 键过期删除,定期删除(主动)和惰性删除(被动) Redis 内存不足时,缓存淘汰策略 key 键过期删除 我们用 redis ...
- 【C# .Net】List循环add,出现数据相同现象? 引发对引用类型和值类型的底层逻辑的思考。
赶项目时发现了一个问题,定义一个引用对象,如果在循环外定义对象,在循环内list.add(object).最后的结果却是所有的对象值都是一样的,即每add一次,都会把之前的数据覆盖. 解决方法:把对象 ...
- toml格式配置文件介绍
toml官方wik toml官方文档 此次文档是以v1.0.0为例,进行说明的.如果使用到的版本不同,直接去官方文档中找对应的版本即可. 谈到配置文件,大家都能说出来好几种,比如常见的ini.xml. ...