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版本适用,编码的函数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
成功截图