ffmpeg 编码yuv420p编码成h264格式

win10环境配置见(32条消息) win10+VS2019配置ffmpeg4.3.1环境+H264编码_hollq的博客-CSDN博客

下面这段代码是用新的ffmpeg版本适用,如ffmpeg4.4.1,主要变化就是编码的函数avcodec_encode_video2不能使用了,变成了avcodec_send_frame,avcodec_receive_packet这两个函数来替代。

#include 
#include 
#include 
extern "C"
{
#include 
#include 
#include 
#include 
}
static int encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* pkt, FILE* outfile)
{
    int ret;
    //发送一帧进行编码
    ret = avcodec_send_frame(enc_ctx, frame);
    if (ret < 0)
    {
        fprintf(stderr, "avcodec_send_frame() failed!\n");
        return -1;
    }

    while (ret >= 0)
    {
        //获取编码后的数据
        ret = avcodec_receive_packet(enc_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            return 0;
        }
        else if (ret < 0)
        {
            fprintf(stderr, "avcodec_receive_packet() failed!\n");
            return -1;
        }


        //写入文件
        fwrite(pkt->data, 1, pkt->size, outfile);
    }

}


int main()
{
    printf("Hello video encoder!\n");

    const char* in_yuv_file = "C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.yuv";
    const char* out_h264_file = "C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.h264";
    FILE* infile = NULL;
    FILE* outfile = NULL;

    const char* codec_name = "h264_nvenc";
    const AVCodec* codec = NULL;
    AVCodecContext* codec_ctx = NULL;
    AVFrame* frame = NULL;
    AVPacket* pkt = NULL;

    int ret = 0;


    //查找指定的编码器
    codec = avcodec_find_encoder_by_name(codec_name);
    if (!codec)
    {
        fprintf(stderr, "avcodec_find_encoder_by_name() failed!\n");
        return 0;
    }

    //分配编码器上下文
    codec_ctx = avcodec_alloc_context3(codec);
    if (!codec_ctx)
    {
        fprintf(stderr, "avcodec_alloc_context3() failed!\n");
        return 0;
    }

    //设置分辨率
    codec_ctx->width = 832;
    codec_ctx->height = 480;
    //设置time_base
    AVRational time_base = { 1, 25 };
    AVRational framerate = { 25, 1 };
    codec_ctx->time_base = time_base;
    codec_ctx->framerate = framerate;

    //设置I帧间隔(GOP size)
    codec_ctx->gop_size = 25;

    //planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
    //YYYY....UU....VV....
    codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;


    //设置一些参数
    //这些参数可能会相互影响的,preset设置就有可能会影响到profile
    if (codec->id == AV_CODEC_ID_H264)
    {
        //h264的参数
        // baseline profile:基本画质。支持I/P 帧,只支持无交错(Progressive)和CAVLC;
        //extended profile:进阶画质。支持I/P/B/SP/SI 帧,只支持无交错(Progressive)和CAVLC;
        //main profile:主流画质。提供I/P/B 帧,支持无交错(Progressive)和交错(Interlaced),也支持
        //CAVLC 和CABAC 的支持;
        //high profile:高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、 无损视频编码
        //和更多的YUV 格式;
        ret = av_opt_set(codec_ctx->priv_data, "profile", "main", 0);
        if (ret != 0)
        {
            fprintf(stderr, "av_opt_set() profile = main failed!\n");
        }


        //x264编码器下的参数
        //编码速度和压缩率之间做出1个权衡
        //ultrafast
        //superfast
        //veryfast
        //faster
        //fast
        //medium – default preset
        //slow
        //slower
        //veryslow
        //placebo
        ret = av_opt_set(codec_ctx->priv_data, "preset", "medium", 0);
        if (ret != 0)
        {
            fprintf(stderr, "av_opt_set() preset = medium failed!\n");
        }


        //x264编码器下的参数
        //film:电影类型,对视频的质量非常严格时使用该选项
        //animation:动画片,压缩的视频是动画片时使用该选项
        //grain:颗粒物很重,该选项适用于颗粒感很重的视频
        //stillimage:静态图像,该选项主要用于静止画面比较多的视频
        //psnr:提高psnr,该选项编码出来的视频psnr比较高
        //ssim:提高ssim,该选项编码出来的视频ssim比较高
        //fastdecode:快速解码,该选项有利于快速解码
        //zerolatency:零延迟,该选项主要用于视频直播
        /*ret = av_opt_set(codec_ctx->priv_data, "tune", "zerolatency", 0);
        if (ret != 0)
        {
            fprintf(stderr, "av_opt_set() tune = zerolatency failed!\n");
        }*/


    }


    //码率
    codec_ctx->bit_rate = 3000000;

    //将codec_ctx 和codec关联
    ret = avcodec_open2(codec_ctx, codec, NULL);
    if (ret < 0)
    {
        fprintf(stderr, "avcodec_open2() failed!\n");
        return 0;
    }

    //打开输入文件 和 输出文件
    infile = fopen(in_yuv_file, "rb");
    if (!infile)
    {
        fprintf(stderr, "fopen() in_yuv_file failed!\n");
        return 0;
    }
    outfile = fopen(out_h264_file, "wb");
    if (!outfile)
    {
        fprintf(stderr, "fopen() out_h264_file failed!\n");
        return 0;
    }


    //分配AVPacket
    pkt = av_packet_alloc();
    if (!pkt)
    {
        fprintf(stderr, "av_packet_alloc() failed!\n");
        return 0;
    }

    //分配AVFrame
    frame = av_frame_alloc();
    if (!frame)
    {
        fprintf(stderr, "av_frame_alloc() failed!\n");
        return 0;
    }

    frame->width = codec_ctx->width;
    frame->height = codec_ctx->height;
    frame->format = codec_ctx->pix_fmt;

    //计算出一帧数据的大小 像素格式 * 宽 * 高
    int frame_bytes = av_image_get_buffer_size(frame->format, frame->width,
        frame->height, 1);
    //int frame_bytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, frame->width,
    //   frame->height, 1);
    uint8_t* yuv_buf = (uint8_t*)malloc(frame_bytes);
    if (!yuv_buf)
    {
        printf("yuv_buf malloc() failed!\n");
        return 0;
    }


    int64_t pts = 0;

    while (1)
    {
        //从文件读一帧数据
        memset(yuv_buf, 0, frame_bytes);
        size_t read_bytes = fread(yuv_buf, 1, frame_bytes, infile);
        if (read_bytes <= 0)
        {
            fprintf(stderr, "fread end!\n");
            break;
        }


        //根据设置的参数将yuv数据填充到frame->data , frame->linesize
        int fill_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf,frame->format, frame->width, frame->height, 1);
        //int fill_size = av_image_fill_arrays(frame->data, frame->linesize, yuv_buf, AV_PIX_FMT_YUV420P, frame->width, frame->height, 1);
        if (fill_size != frame_bytes)
        {
            fprintf(stderr, "av_image_fill_arrays failed!\n");
            break;
        }


        pts += 40;
        //设置pts
        frame->pts = pts;
        ret = encode(codec_ctx, frame, pkt, outfile);
        if (ret < 0)
        {
            fprintf(stderr, "encode failed!\n");
            break;
        }
    }

    //冲刷编码器
    encode(codec_ctx, NULL, pkt, outfile);
    fclose(infile);
    fclose(outfile);

    if (yuv_buf)
    {
        free(yuv_buf);
    }

    av_frame_free(&frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_ctx);

    printf("video encoder end!\n");

    return 0;
}

使用时

1、参考此篇配置(29条消息) ffmpeg+h264_nvenc+vs2019配置编译_hollq的博客-CSDN博客

2、将下面这两行改成自己的文件目录

    const char* in_yuv_file = "C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.yuv";
    const char* out_h264_file = "C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.h264";

3、更改编码尺寸

    //设置分辨率
    codec_ctx->width = 832;
    codec_ctx->height = 480;

4、如果是用h264软件编码,把h264_nvenc改成libx264即可,如果用h264_nvenc,则需要配置相应的硬件环境

const char* codec_name = "h264_nvenc";

成功截图

ffmpeg 编码yuv420p编码成h264格式_第1张图片

二、下面这段代码是用旧的ffmpeg版本适用,编码的函数avcodec_encode_video2

#include 
#include   
#include 
#include 
#include 
#include 
#include "MBS.hpp"
#include   
#include   
#include  
#include "opencv2/imgproc/imgproc_c.h"

using namespace std;

//test different codec  
#define TEST_H264  1  
#define TEST_HEVC  0  

#define __STDC_CONSTANT_MACROS

#pragma comment(lib, "avcodec.lib")  
#pragma comment(lib, "avdevice.lib")  
#pragma comment(lib, "avfilter.lib")  
#pragma comment(lib, "avformat.lib")  
#pragma comment(lib, "swscale.lib")  
#pragma comment(lib, "avutil.lib")  
#pragma comment(lib, "postproc.lib")  
#pragma comment(lib, "swresample.lib")  

#ifdef _WIN32
//Windows
extern "C"
{
#include "libavutil/opt.h"
#include "libavcodec/avcodec.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
#include "libavformat/avformat.h"

};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include 
#include 
#include 
#include 
#include 
#ifdef __cplusplus
}
#endif
#endif

int main(int argc, char* argv[])
{
	avcodec_register_all();
	av_register_all();
	avformat_network_init();
	av_log_set_level(64);
	AVFormatContext* pFormatCtx;
	AVOutputFormat* fmt;
	AVStream* video_st;
	AVCodecContext* pCodecCtx;
	AVCodec* pCodec;
	AVPacket pkt;
	uint8_t* picture_buf;
	AVFrame* frame;
	int picture_size;
	int y_size;
	int framecnt = 0;
	int fps = 50;
	FILE* in_file = fopen("C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.yuv", "rb");   //Input raw YUV data
	int in_w = 832, in_h = 480;                              //Input data's width and height
	int framenum = 501;                                   //Frames to encode
	const char* out_file = "C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.h264";//Output Filepath

	
	av_register_all();
	//add by hollq
	pCodec = avcodec_find_encoder_by_name("h264_nvenc"); //libx264
	if (!pCodec) {
		printf("Codec not found\n");
		return -1;
	}
	pCodecCtx = avcodec_alloc_context3(pCodec);

	//end add
	//Method1.
	pFormatCtx = avformat_alloc_context();
	//Guess Format
	fmt = av_guess_format(NULL, out_file, NULL);
	pFormatCtx->oformat = fmt;

	//Open output URL
	if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) {
		printf("Failed to open output file! \n");
		return -1;
	}

	video_st = avformat_new_stream(pFormatCtx, 0);
	//video_st->time_base.num = 1; 
	//video_st->time_base.den = fps;  

	if (video_st == NULL) {
		return -1;
	}
	//Param that must set
	pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
	pCodecCtx->width = in_w;
	pCodecCtx->height = in_h;
	pCodecCtx->bit_rate = 400000;
	pCodecCtx->gop_size = 250;
	pCodecCtx->time_base.num = 1;
	pCodecCtx->time_base.den = 25;
	pCodecCtx->qmin = 10;
	pCodecCtx->qmax = 51;
	pCodecCtx->max_b_frames = 0;
	
	

	AVDictionary* param = 0;
	//H.264
	//*add by hollq

	if (pCodec->id == AV_CODEC_ID_H264)
	{
		av_opt_set(pCodecCtx->priv_data, "aq-mode", "1", 0);
		av_dict_set(¶m, "preset", "fast", 0);
		av_dict_set(¶m, "tune", "zerolatency", 0);
	}
	//Show some Information
	av_dump_format(pFormatCtx, 0, out_file, 1);

	if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) {
		printf("Failed to open encoder! \n");
		return -1;
	}

	frame = av_frame_alloc();
	frame->format = pCodecCtx->pix_fmt;
	frame->width = pCodecCtx->width;
	frame->height = pCodecCtx->height;

	picture_size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
	picture_buf = (uint8_t*)av_malloc(picture_size);
	avpicture_fill((AVPicture*)frame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);

	//Write File Header
	avformat_write_header(pFormatCtx, NULL);

	av_new_packet(&pkt, picture_size);

	y_size = pCodecCtx->width * pCodecCtx->height;
	
	for (int i = 0; i < framenum; i++) {
		//Read raw YUV data
		if (fread(picture_buf, 1, y_size * 3 / 2, in_file) <= 0) {
			printf("Failed to read raw data! \n");
			return -1;
		}
		else if (feof(in_file)) {
			break;
		}
		frame->data[0] = picture_buf;              // Y
		frame->data[1] = picture_buf + y_size;      // U 
		frame->data[2] = picture_buf + y_size * 5 / 4;  // V
		//PTS
		
		//pFrame->pts=i;
		frame->pts = i * (video_st->time_base.den) / ((video_st->time_base.num) * 25);
		int got_picture = 0;
			//Encode
			int ret = avcodec_encode_video2(pCodecCtx, &pkt, frame, &got_picture);
			if (ret < 0) {
				printf("Failed to encode! \n");
				return -1;
			}
			if (got_picture == 1) {
				
				printf("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);
				framecnt++;
				pkt.stream_index = video_st->index;
				ret = av_write_frame(pFormatCtx, &pkt);
				av_free_packet(&pkt);
				
			}
			
	}
		//Flush Encoder
		/* get the delayed frames */
		for (int got_picture = 1; got_picture; framecnt++) {
			fflush(stdout);

				// 然后调用视频编码器进行编码
				//encode(frame);
				printf("Succeed to use Froi: %5d\n", framecnt);
				
			}
			int ret = avcodec_encode_video2(pCodecCtx, &pkt, NULL, &got_picture);
			if (ret < 0) {
				fprintf(stderr, "Error encoding frame\n");
				exit(1);
			}

			if (got_picture == 1) {
				
				printf("Succeed to encode frame: %5d\tsize:%5d\n", framecnt, pkt.size);
				pkt.stream_index = video_st->index;
				ret = av_write_frame(pFormatCtx, &pkt);
				av_free_packet(&pkt);
			}
			
		}
	

	//Write file trailer
	av_write_trailer(pFormatCtx);

	//Clean
	if (video_st) {
		avcodec_close(video_st->codec);
		av_free(frame);
		av_free(picture_buf);
	}
	avio_close(pFormatCtx->pb);
	avformat_free_context(pFormatCtx);

	fclose(in_file);

	return 0;
}

使用时

1、参考此篇配置(29条消息) ffmpeg+h264_nvenc+vs2019配置编译_hollq的博客-CSDN博客

2、将下面这两行改成自己的文件目录

FILE* in_file = fopen("C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.yuv", "rb");   //Input raw YUV data
                                //Frames to encode
	const char* out_file = "C:/msys64/home/loken/ffmpeg/ROIEncodingTest/ffmLibTest/yuv_seq/PartyScene_832x480_50.h264";//Output Filepath

3、更改编码尺寸和帧数

	int in_w = 832, in_h = 480;                              //Input data's width and height
	int framenum = 501;                                   //Frames to encode

4、如果是用h264软件编码,把h264_nvenc改成libx264即可,如果用h264_nvenc,则需要配置相应的硬件环境

pCodec = avcodec_find_encoder_by_name("h264_nvenc"); //libx264

成功截图

ffmpeg 编码yuv420p编码成h264格式_第2张图片

ffmpeg 编码yuv420p编码成h264格式_第3张图片

你可能感兴趣的:(视频编解码)