ffmpe3.4.1 YUV数据保存成Mp4 文件

简单封装了一下,只有三个文件 writemp4.h writemp4.cpp main.cpp
采用ffmpeg3.4.1
wirtemp4.h

#pragma once
#ifndef WRITEMP4_H
#define WRITEMP4_H

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/avutil.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"

#include "libavutil/avassert.h"
#include "libavutil/channel_layout.h"
#include "libavutil/opt.h"
#include "libavutil/mathematics.h"
#include "libavutil/timestamp.h"
};
#include 
#include 

typedef struct AVWriteMp4_
{
	AVFormatContext *pFormatCtx = nullptr;
	AVOutputFormat *fmt = nullptr;
	AVStream *video_st = nullptr;
	AVCodecContext *pCodecCtx = nullptr;
	AVCodec *pCodec = nullptr;

	uint8_t *picture_buf = nullptr;
	AVFrame *picture = nullptr;
	int size;
	AVPacket *enpkt;
	int y_size;

}AVWriteMp4;


int initWriteMp4(std::string &output, int width, int height, AVWriteMp4 *mAVWriteMp4, int indexValue, int bit_rate);

void uinitWriteMp4(AVWriteMp4 * mAVWriteMp4);

void encode(AVWriteMp4 *pAVWriteMp4, AVFrame *frame, AVPacket *pkt);

#endif // WRITEMP4_H

writemp4.cpp

#pragma once
#include "writemp4.h"
void encode(AVWriteMp4 *pAVWriteMp4, AVFrame *frame, AVPacket *pkt)
{
	int ret;


	/* send the frame to the encoder */
	if (frame)
		printf("Send frame %d \n", frame->pts);

	ret = avcodec_send_frame(pAVWriteMp4->pCodecCtx, frame);
	if (ret < 0) {
		fprintf(stderr, "Error sending a frame for encoding\n");
		exit(1);
	}

	while (ret >= 0) {
		ret = avcodec_receive_packet(pAVWriteMp4->pCodecCtx, pkt);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return;
		else if (ret < 0) {
			fprintf(stderr, "Error during encoding\n");
			exit(1);
		}

		//printf("Write packet %d" (size=%5d)\n", pkt->pts, pkt->size);
		//fwrite(pkt->data, 1, pkt->size, outfile);

		//printf("Write packet %d\n", pkt->pts, pkt->size);
		//fwrite(pkt->data, 1, pkt->size, outfile);

		pAVWriteMp4->enpkt->stream_index = pAVWriteMp4->video_st->index;
		av_packet_rescale_ts(pAVWriteMp4->enpkt, pAVWriteMp4->pCodecCtx->time_base, pAVWriteMp4->video_st->time_base);
		pAVWriteMp4->enpkt->pos = -1;
		ret = av_interleaved_write_frame(pAVWriteMp4->pFormatCtx, pAVWriteMp4->enpkt);

		//av_free_packet(&mAVWriteMp4.enpkt);

		av_packet_unref(pAVWriteMp4->enpkt);


	}
}


int initWriteMp4(std::string &output, int width, int height, AVWriteMp4 *mAVWriteMp4, int indexValue, int bit_rate)
{


	mAVWriteMp4->y_size = width * height;

	//[2] --初始化AVFormatContext结构体,根据文件名获取到合适的封装格式
	avformat_alloc_output_context2(&(mAVWriteMp4->pFormatCtx), NULL, NULL, output.c_str());

	//avcodec_copy_context(mAVWriteMp4->pCodecCtx, inCodexc);

	mAVWriteMp4->fmt = mAVWriteMp4->pFormatCtx->oformat;

	//[3] --打开文件
	if (avio_open(&(mAVWriteMp4->pFormatCtx->pb), output.c_str(), AVIO_FLAG_READ_WRITE))
	{
		printf("output file open fail!");
		return -1;
	}

	//[4] --初始化视频码流
	mAVWriteMp4->video_st = avformat_new_stream(mAVWriteMp4->pFormatCtx, NULL);
	if (mAVWriteMp4->video_st == NULL)
	{
		printf("failed allocating output stram\n");
		return -2;
	}
	// avcodec_copy_context(mAVWriteMp4->pCodecCtx, inCodexc);
	mAVWriteMp4->video_st->time_base.num = 1;
	mAVWriteMp4->video_st->time_base.den = 25;


	//[5] --编码器Context设置参数
	mAVWriteMp4->pCodecCtx = mAVWriteMp4->video_st->codec;
	mAVWriteMp4->pCodecCtx->codec_id = mAVWriteMp4->fmt->video_codec;
	mAVWriteMp4->pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
	mAVWriteMp4->pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
	mAVWriteMp4->pCodecCtx->width = width;
	mAVWriteMp4->pCodecCtx->height = height;
	mAVWriteMp4->pCodecCtx->time_base.num = 1;
	mAVWriteMp4->pCodecCtx->time_base.den = 25;
	mAVWriteMp4->pCodecCtx->bit_rate = bit_rate; // 400000
	mAVWriteMp4->pCodecCtx->gop_size = 12;

	mAVWriteMp4->pCodecCtx->thread_count = 4;

	if (mAVWriteMp4->pCodecCtx->codec_id == AV_CODEC_ID_H264)
	{
		printf("mAVWriteMp4->pCodecCtx->codec_id == AV_CODEC_ID_H264! \n");
		mAVWriteMp4->pCodecCtx->qmin = 10;
		mAVWriteMp4->pCodecCtx->qmax = indexValue; // 越大编码质量越差51
		mAVWriteMp4->pCodecCtx->qcompress = 0.6;
	}
	if (mAVWriteMp4->pCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO)
	{
		printf("mAVWriteMp4->pCodecCtx->codec_id == AV_CODEC_ID_MPEG2VIDEO! \n");
		mAVWriteMp4->pCodecCtx->max_b_frames = 2;
	}
	if (mAVWriteMp4->pCodecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO)
	{
		printf("mAVWriteMp4->pCodecCtx->codec_id == AV_CODEC_ID_MPEG1VIDEO! \n");
		mAVWriteMp4->pCodecCtx->mb_decision = 2;
	}
	//[5]

	//[6] --寻找编码器并打开编码器
	mAVWriteMp4->pCodec = avcodec_find_encoder(mAVWriteMp4->pCodecCtx->codec_id);
	if (!mAVWriteMp4->pCodec)
	{

		printf("no right encoder! \n");
		return -3;
	}
	if (avcodec_open2(mAVWriteMp4->pCodecCtx, mAVWriteMp4->pCodec, NULL) < 0)
	{

		printf("open encoder fail! \n");
		return -4;
	}

	//输出格式信息
	av_dump_format(mAVWriteMp4->pFormatCtx, 0, output.c_str(), 1);

	//初始化帧
	mAVWriteMp4->picture = av_frame_alloc();
	mAVWriteMp4->picture->width = mAVWriteMp4->pCodecCtx->width;
	mAVWriteMp4->picture->height = mAVWriteMp4->pCodecCtx->height;
	mAVWriteMp4->picture->format = mAVWriteMp4->pCodecCtx->pix_fmt;
	mAVWriteMp4->size = avpicture_get_size(mAVWriteMp4->pCodecCtx->pix_fmt, mAVWriteMp4->pCodecCtx->width, mAVWriteMp4->pCodecCtx->height);
	mAVWriteMp4->picture_buf = (uint8_t*)av_malloc(mAVWriteMp4->size);
	avpicture_fill((AVPicture*)mAVWriteMp4->picture, mAVWriteMp4->picture_buf, mAVWriteMp4->pCodecCtx->pix_fmt, mAVWriteMp4->pCodecCtx->width, mAVWriteMp4->pCodecCtx->height);

	//[7] --写头文件
	avformat_write_header(mAVWriteMp4->pFormatCtx, NULL);
	//[7]

	//AVPacket enpkt; //创建已编码帧
	int y_size = mAVWriteMp4->pCodecCtx->width*mAVWriteMp4->pCodecCtx->height;

	//av_new_packet(&mAVWriteMp4->enpkt, mAVWriteMp4->size * 3);

	//mAVWriteMp4->pCodecCtx->codec_tag = 828601953;
	printf("out_taget = %x  %x \n", mAVWriteMp4->pCodecCtx->codec_tag);

	mAVWriteMp4->enpkt = av_packet_alloc();
	if (!mAVWriteMp4->enpkt)
	{
		return -1;
	}
	return 0;
}


void uinitWriteMp4(AVWriteMp4 * mAVWriteMp4)
{

	if (mAVWriteMp4->video_st)
	{
		avcodec_close(mAVWriteMp4->video_st->codec);
		av_free(mAVWriteMp4->picture);
		av_free(mAVWriteMp4->picture_buf);
	}
	if (mAVWriteMp4->pFormatCtx)
	{
		avio_close(mAVWriteMp4->pFormatCtx->pb);
		avformat_free_context(mAVWriteMp4->pFormatCtx);
	}

	av_packet_free(&mAVWriteMp4->enpkt);

}

main.cpp

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

#include 
using namespace std;


// ffmpeg 库采用3.4.1
// 需要配置一下库的路径 
// x64 Debug
#include "writemp4.h"

int main(int argc, char *argv[])
{

	int size;

	//打开视频
	FILE *in_file = fopen("./src01_480x272.yuv", "rb");
	if (!in_file)
	{
		cout << "can not open file!" << endl;
		return -1;
	}

	int in_w = 480, in_h = 272;
	int framenum = 50;
	const char* out_file = "src01.mp4";

	int y_size = in_w * in_h;

	AVWriteMp4 mAVWriteMp4;

	//[1] --注册所有ffmpeg组件
	avcodec_register_all();
	av_register_all();
// 说明, 30 和图像质量有关, 10-51 都行的,越大质量越差
// 400000 比特率自行理解,
	int ret = initWriteMp4(std::string(out_file), in_w, in_h, &mAVWriteMp4, 30, 400000);

	
	//[8] --循环编码每一帧
	for (int i = 0; i < framenum; i++)
	{
		//读入YUV
		if (fread(mAVWriteMp4.picture_buf, 1, y_size * 3 / 2, in_file) < 0)
		{
			cout << "read file fail!" << endl;
			goto end;
		}
		else if (feof(in_file))
			break;

		mAVWriteMp4.picture->data[0] = mAVWriteMp4.picture_buf; //亮度Y
		mAVWriteMp4.picture->data[1] = mAVWriteMp4.picture_buf + y_size; //U
		mAVWriteMp4.picture->data[2] = mAVWriteMp4.picture_buf + y_size * 5 / 4; //V
													 //AVFrame PTS


		mAVWriteMp4.picture->data[0] = mAVWriteMp4.picture_buf; //亮度Y
		mAVWriteMp4.picture->data[1] = mAVWriteMp4.picture_buf + mAVWriteMp4.y_size; //U
		mAVWriteMp4.picture->data[2] = mAVWriteMp4.picture_buf + mAVWriteMp4.y_size * 5 / 4; //V
																							 //AVFrame PTS
		mAVWriteMp4.picture->pts = i;

		encode(&mAVWriteMp4, mAVWriteMp4.picture, mAVWriteMp4.enpkt);

	}

	encode(&mAVWriteMp4, NULL, mAVWriteMp4.enpkt);

	//[8]
end:
	av_write_trailer(mAVWriteMp4.pFormatCtx);
	//[10]
	uinitWriteMp4(&mAVWriteMp4);

	fclose(in_file);

	return 0;
}

可以在项目中直接调用
项目下载地址

你可能感兴趣的:(ffmpeg,c++)