FFmpeg —— 13.示例程序(七):视频编码器(YUV编码为H265)

此程序和上一篇YUN编码为H264类似,仅仅是修改几个参数

程序源码

/*
 *
 * 本程序实现了YUV像素数据编码为视频码流(H264)
 *
 */

#include <stdio.h>

#define __STDC_CONSTANT_MACROS

extern "C"
{
#include "libavdevice/avdevice.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
};


int flush_encoder(AVFormatContext *fmtCtx,unsigned int streamIndex){
	int ret;
	int got_frame;
	AVPacket enc_pkt;

	if (!(fmtCtx->streams[streamIndex]->codec->codec->capabilities & AV_CODEC_CAP_DELAY))
	{
		return 0;
	}

	while (1)
	{
		enc_pkt.data = NULL;
		enc_pkt.size = 0;
		av_init_packet(&enc_pkt);
		ret = avcodec_encode_video2 (fmtCtx->streams[streamIndex]->codec, &enc_pkt,
			NULL, &got_frame);
		av_frame_free(NULL);
		if (ret < 0)
			break;
		if (!got_frame){
			ret=0;
			break;
		}
		printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);
		/* mux encoded frame */
		ret = av_write_frame(fmtCtx, &enc_pkt);
		if (ret < 0)
			break;
	}

	return ret;
}

int main(int argc, char *argv[])
{
	AVFormatContext *pFormatCtx = NULL;
	AVCodecContext *pCodecCtx = NULL;
	AVCodec *pCodec = NULL;
	AVOutputFormat *pOutFmt = NULL;

	const char *inFilename = "input.yuv";
//	const char *outFilename = "output.h264";
	const char *outFilename = "output.hevc";


	//1.注册组件:编解码器等
	avdevice_register_all();

	//2.初始化封装格式上下文
	//方法一:
//	pFormatCtx = avformat_alloc_context();
//	pOutFmt = av_guess_format(NULL, outFilename, NULL);
//	pFormatCtx->oformat = pOutFmt;

	//方法二:
	avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, outFilename);
	pOutFmt = pFormatCtx->oformat;

	//3.打开输出文件
	if (avio_open(&pFormatCtx->pb, outFilename, AVIO_FLAG_WRITE) < 0)
	{
		printf("can't open output file\n");
		return -1;
	}

	//4.创建输出码流
	AVStream *pOutStream = avformat_new_stream(pFormatCtx, NULL);
	if (!pOutStream)
	{
		printf("can't allocate new stream\n");
		return -1;
	}

	//5.查找视频编码器
	//获取编码器上下文
//	avcodec_parameters_to_context(pCodecCtx, pOutStream->codecpar);
	pCodecCtx = pOutStream->codec;

	//设置编码器上下文参数
	pCodecCtx->codec_id = pOutFmt->video_codec;
	pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
	pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
	//视频的宽度和高度:确保等于输入文件的宽度和高度
	pCodecCtx->width = 960;
	pCodecCtx->height = 544;
	//设置帧率25fps
	pCodecCtx->time_base.num = 1;
	pCodecCtx->time_base.den = 25;
	//设置码率
	pCodecCtx->bit_rate = 400000;
	//设置GOP
	pCodecCtx->gop_size = 250;
	//设置量化参数
	pCodecCtx->qmin = 10;
	pCodecCtx->qmax = 51;
//	pCodecCtx->max_b_frames = 0;


	//6.查找编码器
	pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
	if (!pCodec)
	{
		printf("can't find encoder\n");
		return -1;
	}

	printf("pCodec.name = %s\n", pCodec->name);

	//若是H264编码器,要设置一些参数
	AVDictionary *param = NULL;
	if (pCodecCtx->codec_id == AV_CODEC_ID_H264)
	{
		av_dict_set(&param, "preset", "slow", 0);
		av_dict_set(&param, "tune", "zerolatency", 0);
	}

	//H.265
	if(pCodecCtx->codec_id == AV_CODEC_ID_H265){
//		av_dict_set(&param, "x265-params", "qp=20", 0);
		av_dict_set(&param, "preset", "ultrafast", 0);
		av_dict_set(&param, "tune", "zero-latency", 0);
	}

	//7.打开编码器
	if (avcodec_open2(pCodecCtx, pCodec, &param) < 0)
	{
		printf("can't open encoder\n");
		return -1;
	}

	//8.写入头文件信息
	avformat_write_header(pFormatCtx, NULL);

	//9.循环编码YUV文件为H264
	//(1)开辟缓冲区
	int bufSize = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, 1);
	int ySize = pCodecCtx->width * pCodecCtx->height;
	uint8_t *outBuffer = (uint8_t *)av_malloc(bufSize);

	FILE *inFile = fopen(inFilename, "rb");
	if (!inFile)
	{
		printf("can't find input file\n");
		return -1;
	}

	//(2) 内容空间填充
	AVFrame *pFrame = av_frame_alloc();
	//设置真的格式、宽度和高度,否则会出现
	//1.AVFrame.format is not set
	//AVFrame.width or height is not set
	pFrame->format = pCodecCtx->pix_fmt;
	pFrame->width = pCodecCtx->width;
	pFrame->height = pCodecCtx->height;

	av_image_fill_arrays(pFrame->data,
			pFrame->linesize,
			outBuffer,
			pCodecCtx->pix_fmt,
			pCodecCtx->width,
			pCodecCtx->height,
			1);

	//(3)开辟packet
	AVPacket *pPacket = (AVPacket *)av_malloc(bufSize);

	int i = 0, frameIndex = 0;

	//(4)循环编码
	while(1)
	{
		//从YUV文件里面读取缓冲区
		//读取大小:ySize * 3 / 2
		if (fread(outBuffer, 1, ySize * 3 / 2, inFile) <= 0)
		{
			printf("finished to read data\n");
			break;
		}
		else if (feof(inFile))
		{
			break;
		}

		//将缓冲区数据转换成AVFrame类型
		pFrame->data[0] = outBuffer;			//Y值
		pFrame->data[1] = outBuffer + ySize;	//U值
		pFrame->data[2] = outBuffer + ySize * 5 / 4;	//V值

		pFrame->pts = i * (pOutStream->time_base.den) / (pOutStream->time_base.num * 25);


		//10.视频编码处理
		//(1)发送一帧视频像素数据
		if (avcodec_send_frame(pCodecCtx, pFrame) < 0)
		{
			printf("failed to encoder\n");
			return -1;
		}
		//(2)接收一帧视频压缩数据格式(像素数据编码而来)
		if (avcodec_receive_packet(pCodecCtx, pPacket) >= 0)
		{
			//编码成功
			//11.将视频写入到输出文件
			pPacket->stream_index = pOutStream->index;
			if (av_write_frame(pFormatCtx, pPacket) < 0)
			{
				printf("failed to write frame\n");
				return -1;
			}
			printf("succeed to write frame: %d\tsize:%d\n", frameIndex++, pPacket->size);
		}

		av_packet_unref(pPacket);

	}

    //写入剩余帧数据->可能没有
    flush_encoder(pFormatCtx, 0);

    //写入文件尾部信息
    av_write_trailer(pFormatCtx);

    //释放内存
    avcodec_close(pCodecCtx);
    av_free(pFrame);
    av_free(outBuffer);
    av_packet_free(&pPacket);
    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);
    fclose(inFile);

    return 0;
}

问题总结

出错问题1:Lookahead depth must be greater than the max consecutive bframe count

 解决办法1:关闭B帧

//    pCodecCtx->max_b_frames = 0;

解决办法2:不设置zero-latency

//        av_dict_set(&param, "tune", "zero-latency", 0);

以上2种方法二选一即可

发布了61 篇原创文章 · 获赞 124 · 访问量 70万+

猜你喜欢

转载自blog.csdn.net/guoyunfei123/article/details/105611539