ffmpeg实现视频切割

1、概述

本程序实现把一个视频切割为2个视频,不涉及编解码,最难理解的地方在于pts和dts的计算,要好好看看,不够完美的地方在于没有按照关键帧切割,所以会在切割点花屏,以后改善。

*注:只处理一个视频流和一个音频流,若流多了,估计会crash。

简单说下流程:

打开输入---->打开输出---->根据输入来创建流---->拷贝流设置---->循环读帧---->判断时间点是否到达切割点,并做设置---->设置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


int _tmain(int argc, _TCHAR* argv[])
{
	if(argc < 3)
	{
		printf("no input file!\n");
		return -1;
	}
	AVFormatContext *ifmt_ctx = NULL, *ofmt1_ctx = NULL, *ofmt2_ctx = NULL;
	AVStream *out1_vstream = NULL, *out1_astream = NULL;
	AVStream *out2_vstream = NULL, *out2_astream = NULL;
	char str_out1_filename[10];
	char str_out2_filename[10];
	sprintf(str_out1_filename, "test1.%s", argv[2]);
	sprintf(str_out2_filename, "test2.%s", argv[2]);

	int inVideo_StreamIndex = -1,inAudio_StreamIndex = -1;
	int ret;

	av_register_all();

	if ((ret = avformat_open_input(&ifmt_ctx, argv[1], NULL, NULL)) < 0)
	{
		printf("can not open the in put file format context!\n");
		return -1;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0)
	{
		printf("can not find the input stream info!\n");
		goto end;
	}
		
	avformat_alloc_output_context2(&ofmt1_ctx, NULL, NULL, str_out1_filename);
	if (!ofmt1_ctx)
	{
		printf( "Could not create output1 context\n");
		ret = AVERROR_UNKNOWN;
		goto end;
	}

	avformat_alloc_output_context2(&ofmt2_ctx, NULL, NULL, str_out2_filename);
	if (!ofmt2_ctx)
	{
		printf( "Could not create output2 context\n");
		ret = AVERROR_UNKNOWN;
		goto end;
	}

	for (int i = 0; i < ifmt_ctx->nb_streams; i++)
	{
		if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			inVideo_StreamIndex = i;
			out1_vstream = avformat_new_stream(ofmt1_ctx, NULL);
			out2_vstream = avformat_new_stream(ofmt2_ctx, NULL);

			//open decoder
			if(0 > avcodec_open2(ifmt_ctx->streams[i]->codec, avcodec_find_decoder(ifmt_ctx->streams[i]->codec->codec_id), NULL))
			{
				printf("can not find or open video decoder!\n");
				goto end;
			}

			if (!out1_vstream)
			{
				printf("Failed allocating output1 video stream\n");
				ret = AVERROR_UNKNOWN;
				goto end;
			}
			else
			{
				//copy the settings of AVCodecContext;
				if (avcodec_copy_context(out1_vstream->codec, ifmt_ctx->streams[i]->codec) < 0)
				{
					printf( "Failed to copy context from input to output stream codec context\n");
					goto end;
				}
				out1_vstream->codec->codec_tag = 0;
				if(ofmt1_ctx->oformat->flags & AVFMT_GLOBALHEADER)
				{
					out1_vstream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
				}
			}

			if (!out2_vstream)
			{
				printf("Failed allocating output1 video stream\n");
				ret = AVERROR_UNKNOWN;
				goto end;
			}
			else
			{
				//copy the settings of AVCodecContext;
				if (avcodec_copy_context(out2_vstream->codec, ifmt_ctx->streams[i]->codec) < 0)
				{
					printf( "Failed to copy context from input to output stream codec context\n");
					goto end;
				}
				out2_vstream->codec->codec_tag = 0;
				if(ofmt2_ctx->oformat->flags & AVFMT_GLOBALHEADER)
				{
					out2_vstream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
				}
			}
		}
		else if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			inAudio_StreamIndex = i;
			out1_astream = avformat_new_stream(ofmt1_ctx, NULL);
			out2_astream = avformat_new_stream(ofmt2_ctx, NULL);

			if (!out1_astream)
			{
				printf("Failed allocating output1 video stream\n");
				ret = AVERROR_UNKNOWN;
				goto end;
			}
			else
			{
				//copy the settings of AVCodecContext;
				if (avcodec_copy_context(out1_astream->codec, ifmt_ctx->streams[i]->codec) < 0)
				{
					printf( "Failed to copy context from input to output stream codec context\n");
					goto end;
				}
				out1_astream->codec->codec_tag = 0;
				if(ofmt1_ctx->oformat->flags & AVFMT_GLOBALHEADER)
				{
					out1_astream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
				}
			}


			if (!out2_astream)
			{
				printf("Failed allocating output1 video stream\n");
				ret = AVERROR_UNKNOWN;
				goto end;
			}
			else
			{
				//copy the settings of AVCodecContext;
				if (avcodec_copy_context(out2_astream->codec, ifmt_ctx->streams[i]->codec) < 0)
				{
					printf( "Failed to copy context from input to output stream codec context\n");
					goto end;
				}
				out2_astream->codec->codec_tag = 0;
				if(ofmt2_ctx->oformat->flags & AVFMT_GLOBALHEADER)
				{
					out2_astream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
				}
			}
		}
	}

	//Dump Format------------------
	printf("\n==============Input Video=============\n");
	av_dump_format(ifmt_ctx, 0, argv[1], 0);
	printf("\n==============Output1============\n");
	av_dump_format(ofmt1_ctx, 0, str_out1_filename, 1);
	printf("\n==============Output2============\n");
	av_dump_format(ofmt2_ctx, 0, str_out1_filename, 1);
	printf("\n======================================\n");
	
	//open output1 file
	if (!(ofmt1_ctx->oformat->flags & AVFMT_NOFILE))
	{
		if (avio_open(&ofmt1_ctx->pb, str_out1_filename, AVIO_FLAG_WRITE) < 0)
		{
			printf( "Could not open output file '%s'", str_out1_filename);
			goto end;
		}
	}

	//open output2 file
	if (!(ofmt2_ctx->oformat->flags & AVFMT_NOFILE))
	{
		if (avio_open(&ofmt2_ctx->pb, str_out2_filename, AVIO_FLAG_WRITE) < 0)
		{
			printf( "Could not open output file '%s'", str_out2_filename);
			goto end;
		}
	}

	//write out 1 file header
	if (avformat_write_header(ofmt1_ctx, NULL) < 0)
	{
		printf( "Error occurred when opening video output file\n");
		goto end;
	}

	//write out 2 file header
	if (avformat_write_header(ofmt2_ctx, NULL) < 0)
	{
		printf( "Error occurred when opening video output file\n");
		goto end;
	}
	int splitPtsV = 0;//the real split video pts
	int splitDtsV = 0;
	int splitPtsA = 0;//the real split audio pts
	int splitDtsA = 0;
	int videoIndex = 0;//the real video index
	
	int splitTime = 30;//the split time (sec)

	AVPacket pkt;
	while(1)
	{
		AVFormatContext *ofmt_ctx;
		AVStream *in_stream, *out_stream;
		if (av_read_frame(ifmt_ctx, &pkt) < 0)
		{
			break;
		}
		in_stream = ifmt_ctx->streams[pkt.stream_index];
		
		if (pkt.stream_index == inVideo_StreamIndex)
		{
			videoIndex++;
			int time = pkt.pts * (((float)in_stream->time_base.num) / ((float)in_stream->time_base.den));
			if (time <= splitTime)
			{
				splitPtsV = pkt.pts;
				splitDtsV = pkt.dts;				
				out_stream = ofmt1_ctx->streams[pkt.stream_index];
				ofmt_ctx = ofmt1_ctx;
			}
			else
			{
				pkt.pts = pkt.pts - splitPtsV;
				pkt.dts = pkt.dts - splitDtsV;
				out_stream = ofmt2_ctx->streams[pkt.stream_index];
				ofmt_ctx = ofmt2_ctx;
			}
		}
		else if (pkt.stream_index == inAudio_StreamIndex)
		{
			int time = pkt.pts * (((float)in_stream->time_base.num) / ((float)in_stream->time_base.den));
			if (time <= splitTime)
			{
				splitPtsA = pkt.pts;
				splitDtsA = pkt.dts;

				out_stream = ofmt1_ctx->streams[pkt.stream_index];
				ofmt_ctx = ofmt1_ctx;
			}
			else
			{	
				pkt.pts = pkt.pts - splitPtsA;
				pkt.dts = pkt.dts - splitDtsA;

				out_stream = ofmt2_ctx->streams[pkt.stream_index];
				ofmt_ctx = ofmt2_ctx;
			}
		}

		pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
		pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
		pkt.pos = -1;

		//write into file
		if (av_interleaved_write_frame(ofmt_ctx, &pkt) < 0)
		{
			printf( "Error muxing packet\n");
			break;
		}

		av_free_packet(&pkt);
	}

	av_write_trailer(ofmt1_ctx);
	av_write_trailer(ofmt2_ctx);

end:
	avformat_close_input(&ifmt_ctx);

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

	avformat_free_context(ofmt1_ctx);

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

	avformat_free_context(ofmt2_ctx);

	return 0;
}


3、下载地址

http://download.csdn.net/detail/dancing_night/8699109

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dancing_night

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

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

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

打赏作者

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

抵扣说明:

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

余额充值