最简单的视频编码器:基于libx265(编码YUV为H.265)

介绍了一个基于libx265的简单H.265视频编码器实现,可将YUV格式数据编码为H.265码流。提供源代码及运行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

=====================================================

最简单的视频编码器系列文章列表:

最简单的视频编码器:编译

最简单的视频编码器:基于libx264(编码YUV为H.264)

最简单的视频编码器:基于libx265(编码YUV为H.265)

最简单的视频编码器:libvpx(编码YUV为VP8)

=====================================================

本文记录一个最简单的基于libx265的H.265(HEVC)视频编码器。此前记录的编码器是通过FFmpeg调用libx265完成编码的,例如:

《最简单的基于FFmpeg的视频编码器-更新版(YUV编码为HEVC(H.265))》

相比与上文中的编码器,本文记录的编码器属于“轻量级”的编码器。因为它不再包含FFmpeg的代码,直接调用libx265完成编码。因此项目的体积非常小巧。该编码器可以将输入的YUV数据编码为H.265码流文件。

流程图

调用libx265进行视频编码的流程图如下所示。

 

从流程图中可以看出x265的API和x264的API十分相似。它们在用法上只有微小的不同。
流程图中主要的函数如下所示。
x265_param_alloc():为参数集结构体x265_param分配内存。
x265_param_default():设置参数集结构体x265_param的缺省值。
x265_picture_alloc():为图像结构体x265_picture分配内存。
x265_picture_init():设置图像结构体x265_picture的缺省值。
x265_encoder_open():打开编码器。
x265_encoder_encode():编码一帧图像。
x265_encoder_close():关闭编码器。
x265_picture_free():释放x265_picture_alloc()申请的资源。
x265_param_free():释放x265_param_alloc()申请的资源。

存储数据的结构体如下所示。
x265_picture:存储压缩编码前的像素数据。
x265_nal:存储压缩编码后的码流数据。

此外流程图中还包括一个“flush_encoder”模块,该模块使用的函数和编码模块是一样的。唯一的不同在于不再输入视频像素数据。它的作用是输出编码器中剩余的码流数据。

源代码

/**
 * 最简单的基于X265的视频编码器
 * Simplest X265 Encoder
 *
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.youkuaiyun.com/leixiaohua1020
 *
 * 本程序可以YUV格式的像素数据编码为H.265码流,是最简单的
 * 基于libx265的视频编码器
 *
 * This software encode YUV data to H.265 bitstream.
 * It's the simplest encoder example based on libx265.
 */
#include <stdio.h>
#include <stdlib.h>

#if defined ( __cplusplus)
extern "C"
{
#include "x265.h"
};
#else
#include "x265.h"
#endif

int main(int argc, char** argv){
	int i,j;
	FILE *fp_src=NULL;
	FILE *fp_dst=NULL;
	int y_size;
	int buff_size;
	char *buff=NULL;
	int ret;
	x265_nal *pNals=NULL;
	uint32_t iNal=0;

	x265_param* pParam=NULL;
	x265_encoder* pHandle=NULL;
	x265_picture *pPic_in=NULL;

	//Encode 50 frame
	//if set 0, encode all frame
	int frame_num=50;
	int csp=X265_CSP_I420;
	int width=640,height=360;

	fp_src=fopen("../cuc_ieschool_640x360_yuv420p.yuv","rb");
	//fp_src=fopen("../cuc_ieschool_640x360_yuv444p.yuv","rb");

	fp_dst=fopen("cuc_ieschool.h265","wb");
	//Check
	if(fp_src==NULL||fp_dst==NULL){
		return -1;
	}

	pParam=x265_param_alloc();
	x265_param_default(pParam);
	pParam->bRepeatHeaders=1;//write sps,pps before keyframe
	pParam->internalCsp=csp;
	pParam->sourceWidth=width;
	pParam->sourceHeight=height;
	pParam->fpsNum=25;
	pParam->fpsDenom=1;
	//Init
	pHandle=x265_encoder_open(pParam);
	if(pHandle==NULL){
		printf("x265_encoder_open err\n");
		return 0;
	}
	y_size = pParam->sourceWidth * pParam->sourceHeight;

	pPic_in = x265_picture_alloc();
	x265_picture_init(pParam,pPic_in);
	switch(csp){
	case X265_CSP_I444:{
		buff=(char *)malloc(y_size*3);
		pPic_in->planes[0]=buff;
		pPic_in->planes[1]=buff+y_size;
		pPic_in->planes[2]=buff+y_size*2;
		pPic_in->stride[0]=width;
		pPic_in->stride[1]=width;
		pPic_in->stride[2]=width;
		break;
					   }
	case X265_CSP_I420:{
		buff=(char *)malloc(y_size*3/2);
		pPic_in->planes[0]=buff;
		pPic_in->planes[1]=buff+y_size;
		pPic_in->planes[2]=buff+y_size*5/4;
		pPic_in->stride[0]=width;
		pPic_in->stride[1]=width/2;
		pPic_in->stride[2]=width/2;
		break;
					   }
	default:{
		printf("Colorspace Not Support.\n");
		return -1;
			}
	}
	
	//detect frame number
	if(frame_num==0){
		fseek(fp_src,0,SEEK_END);
		switch(csp){
		case X265_CSP_I444:frame_num=ftell(fp_src)/(y_size*3);break;
		case X265_CSP_I420:frame_num=ftell(fp_src)/(y_size*3/2);break;
		default:printf("Colorspace Not Support.\n");return -1;
		}
		fseek(fp_src,0,SEEK_SET);
	}

	//Loop to Encode
	for( i=0;i<frame_num;i++){
		switch(csp){
		case X265_CSP_I444:{
			fread(pPic_in->planes[0],1,y_size,fp_src);		//Y
			fread(pPic_in->planes[1],1,y_size,fp_src);		//U
			fread(pPic_in->planes[2],1,y_size,fp_src);		//V
			break;}
		case X265_CSP_I420:{
			fread(pPic_in->planes[0],1,y_size,fp_src);		//Y
			fread(pPic_in->planes[1],1,y_size/4,fp_src);	//U
			fread(pPic_in->planes[2],1,y_size/4,fp_src);	//V
			break;}
		default:{
			printf("Colorspace Not Support.\n");
			return -1;}
		}

		ret=x265_encoder_encode(pHandle,&pNals,&iNal,pPic_in,NULL);	
		printf("Succeed encode %5d frames\n",i);

		for(j=0;j<iNal;j++){
			fwrite(pNals[j].payload,1,pNals[j].sizeBytes,fp_dst);
		}	
	}
	//Flush Decoder
	while(1){
		ret=x265_encoder_encode(pHandle,&pNals,&iNal,NULL,NULL);
		if(ret==0){
			break;
		}
		printf("Flush 1 frame.\n");

		for(j=0;j<iNal;j++){
			fwrite(pNals[j].payload,1,pNals[j].sizeBytes,fp_dst);
		}
	}
	
	x265_encoder_close(pHandle);
	x265_picture_free(pPic_in);
	x265_param_free(pParam);
	free(buff);
	fclose(fp_src);
	fclose(fp_dst);
	
	return 0;
}

运行结果

程序的输入为一个YUV文件(已经测试过YUV444P和YUV420P两种格式)。


输出为H.265码流文件。


H.265码流文件的信息如下所示。


下载


Simplest Encoder

项目主页

SourceForge:https://sourceforge.net/projects/simplestencoder/

Github:https://github.com/leixiaohua1020/simplest_encoder

开源中国:http://git.oschina.net/leixiaohua1020/simplest_encoder


CDSN下载地址:http://download.youkuaiyun.com/detail/leixiaohua1020/8284105


该解决方案包含了几个常见的编码器的使用示例:
simplest_vpx_encoder:最简单的基于libvpx的视频编码器
simplest_x264_encoder:最简单的基于libx264的视频编码器
simplest_x265_encoder:最简单的基于libx265的视频编码器

<think>好的,我现在需要帮助用户找到如何使用FFmpeg在C++中实现H264编码并通过RTMP推流的解决方案或示例代码。首先,我需要回忆一下之前用户提供的引用内容,看看是否有相关的信息。 用户提到了引用[3]和[4],其中引用[3]提到使用FFmpeg读取USB摄像头的H264帧并进行RTMP推流,但示例代码可能涉及从内存读取数据。引用[4]则提到Qt/C++结合FFmpeg进行视频采集和推流。此外,用户需要的是C++的示例代码,所以可能需要结合这些信息。 接下来,我需要考虑FFmpeg在C++中的基本使用流程。通常,H264编码和RTMP推流的步骤包括初始化FFmpeg库、打开输入设备或文件、设置编码器参数、打开输出流、读取帧数据、编码、封装并推流。每一步都需要正确的API调用和错误处理。 用户可能需要详细的代码示例,所以需要整理一个简单的流程。例如,首先创建AVFormatContext用于输出,设置编码器参数,然后循环读取视频帧,编码后写入输出流。同时,要注意时间戳的处理,确保流媒体服务器能正确接收。 另外,引用[2]中的FFmpeg命令行参数可能对设置编码器参数有帮助,比如使用libx264编码器,调整preset、maxrate等参数。需要将这些参数转换为C++代码中的AVCodecContext设置。 还需要注意内存管理和资源释放,避免内存泄漏。比如,每个AVPacket和AVFrame都需要正确初始化和释放,编码器上下文和输出格式上下文也需要正确关闭。 用户可能遇到的问题包括编码器无法打开、推流连接失败、时间戳错误导致播放问题等。在示例代码中加入错误检查和日志输出会有助于调试。 最后,根据引用[5],用户可能对结合Qt和OpenCV感兴趣,但当前问题集中在FFmpeg的C++实现,所以需要专注于FFmpeg API的使用,而不是其他库的集成。 总结一下,我需要提供一个结构清晰的C++示例代码,涵盖初始化、编码、推流的关键步骤,并解释每个部分的作用。同时,需要引用相关的资料,如雷神的博客,帮助用户进一步学习。</think>以下是使用 FFmpeg C++ 实现 H264 编码及 RTMP 推流的技术方案和示例代码: ### 一、核心实现流程 1. **初始化上下文** 创建输出流格式上下文`AVFormatContext`,设置RTMP服务器地址: ```cpp AVOutputFormat *fmt = av_guess_format("flv", NULL, NULL); AVFormatContext *oc; avformat_alloc_output_context2(&oc, fmt, NULL, "rtmp://example.com/live/stream"); ``` 2. **配置视频编码器** 创建H264编码器并设置参数(引用[2]的转码参数对应): ```cpp AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264); AVCodecContext *cctx = avcodec_alloc_context3(codec); cctx->bit_rate = 400000; cctx->width = 1280; cctx->height = 720; cctx->time_base = (AVRational){1, 25}; cctx->pix_fmt = AV_PIX_FMT_YUV420P; av_opt_set(cctx->priv_data, "preset", "veryfast", 0); // 对应-preset参数 ``` 3. **封装流媒体格式** 创建视频流并关联编码器: ```cpp AVStream *vs = avformat_new_stream(oc, codec); avcodec_parameters_from_context(vs->codecpar, cctx); ``` 4. **帧处理循环** 实现采集/生成YUV帧→编码→封装推流: ```cpp AVFrame *frame = av_frame_alloc(); AVPacket *pkt = av_packet_alloc(); while (1) { // 生成或获取YUV420P格式视频帧 avcodec_send_frame(cctx, frame); while (avcodec_receive_packet(cctx, pkt) >= 0) { av_interleaved_write_frame(oc, pkt); // 推流关键操作 av_packet_unref(pkt); } } ``` ### 二、完整代码示例(关键部分) ```cpp #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> int main() { // 初始化 avformat_network_init(); // 创建输出上下文 AVFormatContext* oc = NULL; avformat_alloc_output_context2(&oc, NULL, "flv", "rtmp://localhost/live/test"); // 配置H264编码器 AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); AVCodecContext* cctx = avcodec_alloc_context3(codec); cctx->bit_rate = 400000; cctx->width = 640; cctx->height = 480; cctx->time_base = {1, 25}; cctx->gop_size = 12; cctx->pix_fmt = AV_PIX_FMT_YUV420P; avcodec_open2(cctx, codec, NULL); // 添加视频流 AVStream* vs = avformat_new_stream(oc, NULL); avcodec_parameters_from_context(vs->codecpar, cctx); // 打开输出 avio_open(&oc->pb, oc->url, AVIO_FLAG_WRITE); avformat_write_header(oc, NULL); // 编码循环 AVFrame* frame = av_frame_alloc(); frame->format = cctx->pix_fmt; frame->width = cctx->width; frame->height = cctx->height; av_frame_get_buffer(frame, 32); AVPacket* pkt = av_packet_alloc(); for(int i=0; ;i++) { // 生成测试图案(实际应从摄像头采集) fill_yuv_frame(frame, i); frame->pts = i; avcodec_send_frame(cctx, frame); while(avcodec_receive_packet(cctx, pkt) >=0) { av_packet_rescale_ts(pkt, cctx->time_base, vs->time_base); pkt->stream_index = vs->index; av_interleaved_write_frame(oc, pkt); av_packet_unref(pkt); } } // 清理资源 av_write_trailer(oc); avcodec_free_context(&cctx); avformat_free_context(oc); return 0; } ``` ### 三、关键技术点说明 1. **硬件加速支持** 可通过`avcodec_find_encoder_by_name("h264_nvenc")`调用NVIDIA硬件编码器 2. **时间戳同步** 必须正确计算`pkt->pts`和`pkt->dts`,推荐使用: $$ pts = frame\_index \times \frac{time\_base.den}{time\_base.num} $$ 3. **错误恢复机制** 建议添加网络中断重连逻辑和编码器重启机制
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值