ffmpeg实现视频合并

1、概述

简单说下流程:

打开输入---->打开输出---->根据输入来创建流---->拷贝流设置---->循环读帧---->判断第一个读完,改输入为第二个---->设置pts和dts---->写入---->善后


2、代码

/*
*最简单的视频合并
*缪国凯 Mickel
*821486004@qq.com
*本程序实现把2个视频合并为一个视频,不涉及编解码,但是对视频源有要求,必须是相同的参数
*着重理解第二个视频开始的时候的pts和dts计算
*注:只处理一个视频流和一个音频流,若流多了,估计会crash
*2015-5-14
*/

#include "stdafx.h"

#ifdef __cplusplus
extern"C"
{
#endif
#include <libavformat/avformat.h>
#include "libavcodec/avcodec.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/avutil.h"
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "libswresample\swresample.h"
#include "libavutil\fifo.h"
#include "libavutil/audio_fifo.h"


#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
	//#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avfilter.lib")
	//#pragma comment(lib, "postproc.lib")
#pragma comment(lib, "swresample.lib")
	//#pragma comment(lib, "swscale.lib")
#ifdef __cplusplus
};
#endif

AVFormatContext *in1_fmtctx = NULL, *in2_fmtctx = NULL, *out_fmtctx = NULL;
AVStream *out_video_stream = NULL, *out_audio_stream = NULL;
int video_stream_index = -1, audio_stream_index = -1;


int open_input(const char * in1_name, const char * in2_name)
{
	int ret = -1;
	if ((ret = avformat_open_input(&in1_fmtctx, in1_name, NULL, NULL)) < 0)
	{
		printf("can not open the first input context!\n");
		return ret;
	}
	if ((ret = avformat_find_stream_info(in1_fmtctx, NULL)) < 0)
	{
		printf("can not find the first input stream info!\n");
		return ret;
	}

	if ((ret = avformat_open_input(&in2_fmtctx, in2_name, NULL, NULL)) < 0)
	{
		printf("can not open the first input context!\n");
		return ret;
	}
	if ((ret = avformat_find_stream_info(in2_fmtctx, NULL)) < 0)
	{
		printf("can not find the second input stream info!\n");
		return ret;
	}
}

int open_output(const char * out_name)
{
	int ret = -1;
	if ((ret = avformat_alloc_output_context2(&out_fmtctx, NULL, NULL, out_name)) < 0)
	{
		printf("can not alloc context for output!\n");
		return ret;
	}

	//new stream for out put
	for (int i = 0; i < in1_fmtctx->nb_streams; i++)
	{
		if (in1_fmtctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			video_stream_index = i;
			out_video_stream = avformat_new_stream(out_fmtctx, NULL);
			if (!out_video_stream)
			{
				printf("Failed allocating output1 video stream\n");
				ret = AVERROR_UNKNOWN;
				return ret;
			}
			if ((ret = avcodec_copy_context(out_video_stream->codec, in1_fmtctx->streams[i]->codec)) < 0)
			{
				printf("can not copy the video codec context!\n");
				return ret;
			}
			out_video_stream->codec->codec_tag = 0;
			if(out_fmtctx->oformat->flags & AVFMT_GLOBALHEADER)
			{
				out_video_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
			}
		}
		else if (in1_fmtctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audio_stream_index = i;
			out_audio_stream = avformat_new_stream(out_fmtctx, NULL);

			if (!out_audio_stream)
			{
				printf("Failed allocating output1 video stream\n");
				ret = AVERROR_UNKNOWN;
				return ret;
			}
			if ((ret = avcodec_copy_context(out_audio_stream->codec, in1_fmtctx->streams[i]->codec)) < 0)
			{
				printf("can not copy the video codec context!\n");
				return ret;
			}
			out_audio_stream->codec->codec_tag = 0;
			if(out_fmtctx->oformat->flags & AVFMT_GLOBALHEADER)
			{
				out_audio_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
			}
		}
	}

	//open output file
	if (!(out_fmtctx->oformat->flags & AVFMT_NOFILE))
	{
		if ((ret = avio_open(&out_fmtctx->pb, out_name, AVIO_FLAG_WRITE)) < 0)
		{
			printf("can not open the out put file handle!\n");
			return ret;
		}
	}

	//write out  file header
	if ((ret = avformat_write_header(out_fmtctx, NULL)) < 0)
	{
		printf( "Error occurred when opening video output file\n");
		return ret;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	if(argc < 4)
	{
		printf("no input file!\n");
		return -1;
	}
	char out_name[20];
	sprintf(out_name, "combine.%s", argv[3]);
	av_register_all();	
	if (0 > open_input(argv[1], argv[2]))
	{
		goto end;
	}
	
	if(0 > open_output(out_name))
	{
		goto end;
	}

	AVFormatContext *input_ctx = in1_fmtctx;
	AVPacket pkt;
	int pts_v, pts_a, dts_v, dts_a;
	while(1)
	{
		if(0 > av_read_frame(input_ctx, &pkt))
		{
			if (input_ctx == in1_fmtctx)
			{
				float vedioDuraTime, audioDuraTime;

				//calc the first media dura time
				vedioDuraTime = ((float)input_ctx->streams[video_stream_index]->time_base.num / 
					(float)input_ctx->streams[video_stream_index]->time_base.den) * ((float)pts_v);
				audioDuraTime = ((float)input_ctx->streams[audio_stream_index]->time_base.num / 
					(float)input_ctx->streams[audio_stream_index]->time_base.den) * ((float)pts_a);

				//calc the pts and dts end of the first media
				if (audioDuraTime > vedioDuraTime)
				{
					dts_v = pts_v = audioDuraTime / ((float)input_ctx->streams[video_stream_index]->time_base.num / 
						(float)input_ctx->streams[video_stream_index]->time_base.den);
					dts_a++;
					pts_a++;
				}
				else
				{
					dts_a = pts_a = vedioDuraTime / ((float)input_ctx->streams[audio_stream_index]->time_base.num / 
						(float)input_ctx->streams[audio_stream_index]->time_base.den);
					dts_v++;
					pts_v++;
				}
				input_ctx = in2_fmtctx;
				continue;
			}
			break;
		}

		if (pkt.stream_index == video_stream_index)
		{
			if (input_ctx == in2_fmtctx)
			{
				pkt.pts += pts_v;
				pkt.dts += dts_v;
			}
			else
			{
				pts_v = pkt.pts;
				dts_v = pkt.dts;
			}
		}
		else if (pkt.stream_index == audio_stream_index)
		{
			if (input_ctx == in2_fmtctx)
			{
				pkt.pts += pts_a;
				pkt.dts += dts_a;
			}
			else
			{
				pts_a = pkt.pts;
				dts_a = pkt.dts;
			}
		}

		pkt.pts = av_rescale_q_rnd(pkt.pts, input_ctx->streams[pkt.stream_index]->time_base, 
			out_fmtctx->streams[pkt.stream_index]->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.dts = av_rescale_q_rnd(pkt.dts, input_ctx->streams[pkt.stream_index]->time_base, 
			out_fmtctx->streams[pkt.stream_index]->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.pos = -1;

		if (av_interleaved_write_frame(out_fmtctx, &pkt) < 0) 
		{
			printf( "Error muxing packet\n");
			//break;
		}
		av_free_packet(&pkt);		
	}

	av_write_trailer(out_fmtctx);
	
end:
	avformat_close_input(&in1_fmtctx);
	avformat_close_input(&in2_fmtctx);

	/* close output */
	if (out_fmtctx && !(out_fmtctx->oformat->flags & AVFMT_NOFILE))
		avio_close(out_fmtctx->pb);

	avformat_free_context(out_fmtctx);

	return 0;
}


3、下载地址

http://download.youkuaiyun.com/detail/dancing_night/8700439
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dancing_night

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值