【C++】ffmpeg将png/jpg图片序列转成mp4/avi

 20190715更新

该方法没有对视频帧进行编码压缩,生成的视频很大,而且不能在windows自带的播放器上播放。

后来在网上找到了一段代码,修改之后满足了自己的需求,封装成了一个类,可以将png\jpg格式的图片合成为mp4或者avi格式的视频,其中因为读取硬盘图片用的是自己的一个库(主要用到的功能是:读取图片数据区,对图片进行格式转换),这一部分可以根据大家自己手上已有的代码资源进行替代,这里实验用到的png和jpg都是24bit的,32位也可以,最终都转化为32bit了。另,因为我这个图片读取库只能读2^n的图片,所以我用的是2048*1024的图片,如果你的库可以读任意分辨率的图片应该也是可以的。

调用类的代码如下:

#if 1
#include "VideoCapture2.h"

#define DATASIZE 2048*2048

int main()
{
	VideoCapture2* video = new VideoCapture2();

	video->Init(1332, 620, 25, 21000, "D:\\9\\outputVideo\\xxzx\\mp4\\temp_record_png_21000.mp4");//[bitrate]mp4:21000; avi:210

	int frame_index = 0;
	char buf[250] = { 0 };
	unsigned char *mydata = new unsigned char[DATASIZE];
	std::string dir = "D:\\9\\inputImg\\png2048_1024\\";
	while (frame_index < 50)//将图像压入视频中2250
	{
		_snprintf(buf, 255, "1_0%04d.png", frame_index);//1_00000
		std::string filePath = dir + buf;

		beCore::Ptr<beResource::Image> tempImg = beResource::Image::CreateFromFile(filePath);
		if (!tempImg)
		{
			break;
		}
		video->AddFrame(tempImg);

		std::cout << frame_index << std::endl;
		frame_index++;
	}

	video->Finish();
	return 0;
}

 

 

//****************************************************************************************************************************

#include <stdio.h>

extern "C"//包含C文件头
{
#include "libavformat/avformat.h"
};
#define DATASIZE 2048*2048

AVStream *add_vidio_stream(AVFormatContext *oc, enum AVCodecID codec_id)//用以初始化一个用于输出的AVFormatContext结构体
{
	AVStream *st;
	AVCodec *codec;

	st = avformat_new_stream(oc, NULL);
	if (!st)
	{
		printf("Could not alloc stream\n");
		exit(1);
	}
	codec = avcodec_find_encoder(codec_id);//查找mjpeg解码器
	if (!codec)
	{
		printf("codec not found\n");
		exit(1);
	}
	avcodec_get_context_defaults3(st->codec, codec);//申请AVStream->codec(AVCodecContext对象)空间并设置默认值(由avcodec_get_context_defaults3()设置

	st->codec->bit_rate = 400000;//设置采样参数,即比特率  
	st->codec->width = 1332;//设置视频宽高,这里跟图片的宽高保存一致即可
	st->codec->height = 620;
	st->codec->time_base.den = 25;//设置帧率
	st->codec->time_base.num = 1;

	st->codec->pix_fmt = AV_PIX_FMT_YUV420P;//设置像素格式  
	st->codec->codec_tag = 0;
	if (oc->oformat->flags & AVFMT_GLOBALHEADER)//一些格式需要视频流数据头分开
		st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
	return st;
}

void main()
{
	AVFormatContext *ofmt_ctx = NULL;//其包含码流参数较多,是一个贯穿始终的数据结构,很多函数都要用到它作为参数
	const char *out_filename = "d:\\out.mp4";//输出文件路径,在这里也可以将mkv改成别的ffmpeg支持的格式,如mp4,flv,avi之类的
	int ret;//返回标志

	av_register_all();//初始化解码器和复用器
	avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);//初始化一个用于输出的AVFormatContext结构体,视频帧率和宽高在此函数里面设置
	if (!ofmt_ctx)
	{
		printf("Could not create output context\n");
		return;
	}

	//AVStream *out_stream = add_vidio_stream(ofmt_ctx, AV_CODEC_ID_MJPEG);//创造输出视频流
	AVStream *out_stream = add_vidio_stream(ofmt_ctx, AV_CODEC_ID_PNG);//创造输出视频流(第三个参数指向图片的格式)
	av_dump_format(ofmt_ctx, 0, out_filename, 1);//该函数会打印出视频流的信息,如果看着不开心可以不要

	if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))//打开输出视频文件
	{
		ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
		if (ret < 0) {
			printf("Could not open output file '%s'", out_filename);
			return;
		}
	}

	if (avformat_write_header(ofmt_ctx, NULL) < 0)//写文件头(Write file header)
	{
		printf("Error occurred when opening output file\n");
		return;
	}

	int frame_index = 0;//放入视频的图像计数
	unsigned char *mydata = new unsigned char[DATASIZE];
	AVPacket pkt;
	av_init_packet(&pkt);
	pkt.flags |= AV_PKT_FLAG_KEY;
	pkt.stream_index = out_stream->index;//获取视频信息,为压入帧图像做准备

	char buf[250] = { 0 };
	while (frame_index < 626)//将图像压入视频中
	{
		_snprintf(buf, 255, "1_00%03d.png", frame_index);//1_00000

		std::string dir = "F:\\帧转视频\\data5\\";
		std::string filePath = dir + buf;
		FILE *file;//打开一张jpeg图像并读取其数据,在这里图像最大为1M,如果超过1M,则需要修改1024*1024这里
		fopen_s(&file, filePath.c_str(), "rb");
		pkt.size = fread(mydata, 1, DATASIZE, file);
		pkt.data = mydata;

		fclose(file);
		if (av_interleaved_write_frame(ofmt_ctx, &pkt) < 0) //写入图像到视频
		{
			printf("Error muxing packet\n");
			break;
		}
		printf("Write %8d frames to output file\n", frame_index);//打印出当前压入的帧数
		frame_index++;
	}


	av_free_packet(&pkt);//释放掉帧数据包对象
	av_write_trailer(ofmt_ctx);//写文件尾(Write file trailer)
	delete[]mydata;//释放数据对象
	if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
		avio_close(ofmt_ctx->pb);//关闭视频文件
	avformat_free_context(ofmt_ctx);//释放输出视频相关数据结构
	return;
}

 

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值