【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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章