使用FFMpeg实现视频剪切功能

首先致敬雷神提供的资源,使用雷神的代码实现视频剪切功能

雷神实现的ffmpeg代码文章

说明一下,这里转载首先是记录一下实现方法,其次就是解决代码无法正常运行问题(avformat_write_header返回-22)

本文介绍一个基于FFMPEG的封装格式转换器。所谓的封装格式转换,就是在AVI,FLV,MKV,MP4这些格式之间转换(对应.avi,.flv,.mkv,.mp4文件)。需要注意的是,本程序并不进行视音频的编码和解码工作。而是直接将视音频压缩码流从一种封装格式文件中获取出来然后打包成另外一种封装格式的文件。传统的转码程序工作原理如下图所示:
在这里插入图片描述
上图例举了一个举例:FLV(视频:H.264,音频:AAC)转码为AVI(视频:MPEG2,音频MP3)的例子。可见视频转码的过程通俗地讲相当于把视频和音频重新“录”了一遍。

本程序的工作原理如下图所示:
在这里插入图片描述
由图可见,本程序并不进行视频和音频的编解码工作,因此本程序和普通的转码软件相比,有以下两个特点:
处理速度极快。视音频编解码算法十分复杂,占据了转码的绝大部分时间。因为不需要进行视音频的编码和解码,所以节约了大量的时间。
视音频质量无损。因为不需要进行视音频的编码和解码,所以不会有视音频的压缩损伤。


#include <stdio.h>
extern "C"
{
#include <FFmpeg\libavcodec\avcodec.h>
#include <FFmpeg\libavformat\avformat.h>
}
#pragma comment (lib, "avcodec.lib")
#pragma comment (lib, "avformat.lib")
#pragma comment (lib, "avutil.lib")

int main(int argc, char* argv[])
{
	int startTime = 1;//起始时间  如果为0就不启用该变量
	int endTime = 4;//结束时间   如果为0就不启用该变量
	AVCodecContext* pCodecCtx = NULL;
	AVCodecContext *pAVCodecContext = NULL;
	AVOutputFormat *ofmt = NULL;
	AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
	AVPacket pkt;
	const char *in_filename, *out_filename;
	int ret, i;

	in_filename = "e://Test.mp4";
	out_filename = "e://Temp.mp4";
	av_register_all();
	//输入(Input)
	if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {
		printf("Could not open input file.");
		goto end;
	}
	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
		printf("Failed to retrieve input stream information");
		goto end;
	}
	av_dump_format(ifmt_ctx, 0, in_filename, 0);
	//输出(Output)
	avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
	if (!ofmt_ctx) {
		printf("Could not create output context\n");
		ret = AVERROR_UNKNOWN;
		goto end;
	}
	ofmt = ofmt_ctx->oformat;
	for (i = 0; i < ifmt_ctx->nb_streams; i++) {
		//根据输入流创建输出流(Create output AVStream according to input AVStream)
		AVStream *in_stream = ifmt_ctx->streams[i];
		AVStream *out_stream = avformat_new_stream(ofmt_ctx, in_stream->codec->codec);
		if (!out_stream) {
			printf("Failed allocating output stream\n");
			ret = AVERROR_UNKNOWN;
			goto end;
		}
		ret = avcodec_copy_context(out_stream->codec, in_stream->codec);
		if (ret < 0) 
		{
			printf("Failed to copy context from input to output stream codec context\n");
			goto end;
		}
		out_stream->codec->codec_tag = 0;
		if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
			out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
		//Param that must set
		pCodecCtx = out_stream->codec;
		
		/*
			//特殊处理 
			如果使用雷神的代码这里不增加特殊的处理,会在avformat_write_header函数执行的时候
			返回-22
			i=0: codec_id = AV_CODEC_ID_HEV //H265
			i=1:codec_id = AV_CODEC_ID_AAC
			i=2:codec_id = AV_CODEC_ID_NONE
			i=3:codec_id = AV_CODEC_ID_NONE
			i=4:codec_id = AV_CODEC_ID_NONE
		*/
		pAVCodecContext = ifmt_ctx->streams[i]->codec;
		if (i < 2)
			pCodecCtx->codec_id = ifmt_ctx->streams[i]->codec->codec_id;
		else
			pCodecCtx->codec_id = ifmt_ctx->streams[i%2]->codec->codec_id;
		
#if 0
		//其他参数配置
		pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
		pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
		pCodecCtx->width = 1920;
		pCodecCtx->height = 1080;
		pCodecCtx->bit_rate = pAVCodecContext->bit_rate;
		pCodecCtx->gop_size = 250;
		pCodecCtx->time_base.num = 1;
		pCodecCtx->time_base.den = 25;
		pCodecCtx->qmin = 10;
		pCodecCtx->qmax = 51;
		//Optional Param
		pCodecCtx->max_b_frames = 3;
#endif
	}
	pAVCodecContext = ifmt_ctx->streams[0]->codec;
	// 显示视频相关的参数信息(编码上下文)
	printf("比特率: = %lld\n", pAVCodecContext->bit_rate);
	printf("宽高: = %d\n" , pAVCodecContext->width );
	printf("格式: = %d\n" , pAVCodecContext->pix_fmt);  // AV_PIX_FMT_YUV420P 0
	printf("帧率: = %d\n",pAVCodecContext->time_base.den );
	printf("总时长: = %f s\n", (ifmt_ctx->duration) / AV_TIME_BASE );
	printf("总帧数: = %d\n", ifmt_ctx->streams[0]->nb_frames );
	int fps = ifmt_ctx->streams[0]->avg_frame_rate.num * 1.0f / ifmt_ctx->streams[0]->avg_frame_rate.den;
	int interval = 1 * 1000 / fps;
	printf("平均帧率: = %d\n", fps );
	printf("帧间隔: = %d ms\n", interval);

	//输出一下格式------------------
	av_dump_format(ofmt_ctx, 0, out_filename, 1);
	//打开输出文件(Open output file)
	if (!(ofmt->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);
			goto end;
		}
	}
	//写文件头(Write file header)
	ret = avformat_write_header(ofmt_ctx, NULL);
	if (ret < 0) {
		printf("Error occurred when opening output file\n");
		goto end;
	}

	//跳转到多少秒
	if(startTime <= 0)
	{
		ret = av_seek_frame(ifmt_ctx, -1, startTime * AV_TIME_BASE, AVSEEK_FLAG_ANY);
		if (ret<0) {
			fprintf(stderr, "Error seek\n");
		}
	}
	

	int frame_index = 0;
	while (1) {
		AVStream *in_stream, *out_stream;

		//获取一个AVPacket(Get an AVPacket)
		ret = av_read_frame(ifmt_ctx, &pkt);
		if (ret < 0)
			break;
		in_stream = ifmt_ctx->streams[pkt.stream_index];
		out_stream = ofmt_ctx->streams[pkt.stream_index];


		//裁剪区域
		if (endTime > startTime)
		{
			if (av_q2d(in_stream->time_base) * pkt.pts > (startTime + endTime))
			{
				av_packet_unref(&pkt);
				break;
			}
		}
		/* copy packet */
		//转换PTS/DTS(Convert PTS/DTS)
		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)
		ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
		if (ret < 0) {
			printf("Error muxing packet\n");
			break;
		}
		printf("Write %8d frames to output file\n", frame_index);
		av_free_packet(&pkt);
		frame_index++;
	}
	//写文件尾(Write file trailer)
	av_write_trailer(ofmt_ctx);
end:
	avformat_close_input(&ifmt_ctx);
	/* close output */
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
		avio_close(ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF) {
		printf("Error occurred.\n");
		return -1;
	}
	return 0;
}

转:文章资源:https://blog.csdn.net/leixiaohua1020/article/details/84597944

完整工程下载地址(代码是一样的,就是包含了ffmpeg运行所需要的环境,能直接编译通过):
https://download.csdn.net/download/qq_36351159/85748703
工程项目VS2015 x64 Debug下载 (包括ffmpeg库,配置好的所需包含目录、库,可直接编译)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值