【FFmpeg 之MP4】mp4視頻切割

一、實現功能

從mp4文件截取局部視頻。

splite_video(std::string in_filename, std::string out_filename, int begin_index, int end_index)
參數解析:
in_filename:輸入文件
out_filename:輸出文件
begin_index:起始幀 //若起始幀不是關鍵幀,則選取前面一幀關鍵幀作爲起始幀
end_index:結束幀
av_seek_frame(ifmt_ctx, video_stream_index, 0, 4);//從頭開始讀取流

二、源程序

#include "SpliteVideo.h"
#ifdef __cplusplus
extern "C" {
#endif __cplusplus

#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>

#ifdef __cplusplus
}
#endif __cplusplus

static void log_packet(const AVFormatContext *fmt_ctx, const AVPacket *pkt, const char *tag)
{
	AVRational *time_base = &fmt_ctx->streams[pkt->stream_index]->time_base;

	printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s duration_time:%s stream_index:%d\n",
		tag,
		av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, time_base),
		av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, time_base),
		av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, time_base),
		pkt->stream_index);
}

int splite_video(std::string in_filename, std::string out_filename, int begin_index, int end_index) {
	if (begin_index < 0 || end_index < 0 || (begin_index > end_index)) {
		fprintf(stderr, "[ERROR] [Invalid parameters]: begin_index and end_index are non-negative, \
and begin_index cannot be greater than end_index\n");
		return -1;
	}
	AVOutputFormat *ofmt = NULL;
	AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
	AVPacket pkt;
	int ret, i;
	int video_stream_index = 0;
	int stream_mapping_size = 0;
	int frame_index = 0;
	bool start_cut_flag = false;
	bool first_traverse_flag = true;
	int key_frame_index = 0;
	int temp_frame_index = 0;

	if ((ret = avformat_open_input(&ifmt_ctx, in_filename.c_str(), 0, 0)) < 0) {
		fprintf(stderr, "Could not open input file '%s'", in_filename);
		goto end;
	}

	if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {
		fprintf(stderr, "Failed to retrieve input stream information");
		goto end;
	}

	av_dump_format(ifmt_ctx, 0, in_filename.c_str(), 0);

	avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename.c_str());
	if (!ofmt_ctx) {
		fprintf(stderr, "Could not create output context\n");
		ret = AVERROR_UNKNOWN;
		goto end;
	}

	stream_mapping_size = ifmt_ctx->nb_streams;

	ofmt = ofmt_ctx->oformat;

	for (i = 0; i < ifmt_ctx->nb_streams; i++) {
		AVStream *out_stream;
		AVStream *in_stream = ifmt_ctx->streams[i];
		AVCodecParameters *in_codecpar = in_stream->codecpar;

		if (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			video_stream_index = i;
			out_stream = avformat_new_stream(ofmt_ctx, NULL);
			if (!out_stream) {
				fprintf(stderr, "Failed allocating output stream\n");
				ret = AVERROR_UNKNOWN;
				goto end;
			}

			ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
			if (ret < 0) {
				fprintf(stderr, "Failed to copy codec parameters\n");
				goto end;
			}
			out_stream->codecpar->codec_tag = 0;
		}
	}
	av_dump_format(ofmt_ctx, 0, out_filename.c_str(), 1);

	if (!(ofmt->flags & AVFMT_NOFILE)) {
		ret = avio_open(&ofmt_ctx->pb, out_filename.c_str(), AVIO_FLAG_WRITE);
		if (ret < 0) {
			fprintf(stderr, "Could not open output file '%s'", out_filename);
			goto end;
		}
	}

	ret = avformat_write_header(ofmt_ctx, NULL);
	if (ret < 0) {
		fprintf(stderr, "Error occurred when opening output file\n");
		goto end;
	}

	while (1) {
		AVStream *in_stream, *out_stream;

		ret = av_read_frame(ifmt_ctx, &pkt);
		if (ret < 0) {
			if (begin_index > frame_index) {
				fprintf(stderr, "[ERROR] [Invalid parameters]: begin_index is greater than video frame counts \n");
				return -1;
			}
			break;
		}

		if (pkt.stream_index != video_stream_index || pkt.stream_index > stream_mapping_size) {
			av_packet_unref(&pkt);
			continue;
		}

		if (first_traverse_flag) { //First traversal to determine the position of the keyframe.
			if (pkt.flags == 1) {
				key_frame_index = temp_frame_index;
				temp_frame_index = frame_index;
				if (frame_index > begin_index) {
					first_traverse_flag = false;
					frame_index = 0;
					av_seek_frame(ifmt_ctx, video_stream_index, 0, 4); 
					av_packet_unref(&pkt);
					continue;
				}				
			}
			frame_index++;
			av_packet_unref(&pkt);
			continue;
		}

		if ((pkt.flags == 1) && (frame_index == key_frame_index)){ //start cutoff
			start_cut_flag = true;
		}

		if (frame_index > end_index) { //stop cutoff
			break;
		}

		if (start_cut_flag) {
			printf("frame index: %d\n", frame_index);
			in_stream = ifmt_ctx->streams[video_stream_index];
			out_stream = ofmt_ctx->streams[video_stream_index];
			ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
			if (ret < 0) {
				fprintf(stderr, "Error muxing packet\n");
				break;
			}
		}
		av_packet_unref(&pkt);
		frame_index++;
	}

	av_write_trailer(ofmt_ctx);
end:

	avformat_close_input(&ifmt_ctx);
	/* close output */
	if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
		avio_closep(&ofmt_ctx->pb);
	avformat_free_context(ofmt_ctx);
	if (ret < 0 && ret != AVERROR_EOF) {
		fprintf(stderr, "Error occurred: %s\n", ret);
		return -1;
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章