更正av_seek_frame()后,计算音视频流起始pts的方式

#include 
#include 
#include "xdemux.h"
#include "xmux.h"


using namespace std;

extern "C" {
	//引用ffmpeg头文件
#include 
}
//预处理治理导入库
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")

void PrintErr(int err)
{
	char buf[1024] = { 0 };
	av_strerror(err, buf, sizeof(buf));
	cerr << buf << endl;
}

#define CERR(err) if (err)						\
					{							\
						PrintErr(err);			\
						getchar();				\
						return -1;				\
					}

int main(int argc, char* argv[])
{
	
	///输入参数处理
	//使用说明
	string useage = "124_test_format 输入文件 输出文件 开始时间 结束时间(秒)\n";
	useage += "124_test_xformat v1080.mp4 test_out.mp4 10 20";
	cout << useage << endl;

	if (argc < 3)
	{
		return -1;
	}
	string in_file = argv[1];
	string out_file = argv[2];
	cout << "in_file:" << in_file << endl;
	cout << "out_file:" << out_file << endl;

	
	//截取10~20秒之间的音视频  取多不取少
	//假定9、11秒有关键帧,我们取9秒位置
	int begin_sec = 0;	//截取开始时间
	int end_sec = 0;	//截取结束时间
	if (argc > 3)
	{
		begin_sec = atoi(argv[3]);
	}
	if (argc > 4)
	{
		end_sec = atoi(argv[4]);
	}

	


	//打开媒体文件
	//const char* url = "v1080.mp4";


	/
	///解封装
	//解封装输入上下文(我们自定以的ic:i表示input,c表示context,综合意思,输入的上下文)

	XDemux demux;
	AVFormatContext* demux_c = demux.Open(in_file.c_str());
	demux.set_c(demux_c);

	/
	///封装
	//编码器上下文
	//const char* out_url = "out.mp4";

	XMux mux;
	AVFormatContext* mux_c = mux.Open(out_file.c_str());
	mux.set_c(mux_c);
	auto mvs = mux_c->streams[mux.video_index()];		//	视频流信息
	auto mas = mux_c->streams[mux.audio_index()];		//	视频流信息

	//有视频
	if (demux.video_index() >= 0)
	{
		mvs->time_base.num = demux.video_time_base().num;
		mvs->time_base.den = demux.video_time_base().den;
		//复制视频参数
		demux.CopyPara(demux.video_index(), mvs->codecpar);
	}
	//有音频
	if (demux.audio_index() >= 0)
	{
		mas->time_base.num = demux.audio_time_base().num;
		mas->time_base.den = demux.audio_time_base().den;
		//复制音频参数
		demux.CopyPara(demux.audio_index(), mas->codecpar);
	}

	mux.WriteHead();



	
	long long video_begin_pts = 0;
	long long audio_begin_pts = 0;	//音频的开始时间
	long long video_end_pts = 0;

	//
	// //
	注意:由于av_seek_frame()会根据I帧确定seek的位置(seek位置帧的pts值很可能并不是传入的pts值),
	// 按照教程中,根据起始秒数计算的音视频流的起始pts,是不准确的,会导致花屏
	// 以下代码有误,已经被下方重新计算的音视频流起始pts替换
	//开始截断秒数 算出输入视频的pts
	if (begin_sec > 0)
	{
		//计算视频开始 和 结束播放的pts
		if (demux.video_index() >= 0 && demux.video_time_base().num > 0)
		{
			double t = (double)demux.video_time_base().den / (double)demux.video_time_base().num;
			video_begin_pts = t * begin_sec;
			demux.Seek(video_begin_pts, demux.video_index());	//移动到开始帧
			video_end_pts = t * end_sec;
		}
		//计算音频开始播放的pts
		if (demux.audio_index() >= 0 && demux.audio_time_base().num > 0)
		{
			double t = (double)demux.audio_time_base().den / (double)demux.audio_time_base().num;
			audio_begin_pts = begin_sec * t;
		}
	}
	//
	//


	//换算成pts,换算成输入ic的pts,以视频流为准
	//if (vs && vs->time_base.num > 0)
	//{
	//	//公式: pts = sec / timebase
	//	//公式: pts = sec / (num/den) = sec * (den / num)
	//	double t = (double)vs->time_base.den / (double)vs->time_base.num;
	//	begin_pts = begin_sec * t;
	//	end_pts = end_sec * t;
	//}

	//if (as && as->time_base.num > 0)
	//{
	//	begin_audio_pts = begin_sec * ((double)as->time_base.den / (double)as->time_base.num);
	//}

	//seek输入媒体移动到第十秒的关键帧位置
	//if (vs)
	//{
	//	re = av_seek_frame(ic, vs->index, begin_pts,
	//		AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);	//先后关键帧
	//	CERR(re);
	//}

	int audio_count = 0;
	int video_count = 0;
	double total_sec = 0;
	AVPacket pkt;
	bool is_first_video_pkt = true;
	bool is_first_audio_pkt = true;
	for (;;)
	{
		//re = av_read_frame(ic, &pkt);
		CERR(re);	这个宏在读到文件尾的时候会退出函数,此处在读到文件尾后应继续执行
		//if (re)
		//{
		//	PrintErr(re);
		//	break;
		//}
		if (!demux.Read(&pkt))
		{
			break;
		}


		/
		// /
		//注意:由于av_seek_frame()会根据I帧确定seek的位置(seek位置帧的pts值很可能并不是传入的pts值),
		// 根据起始秒数计算的音视频流的起始pts,是不准确的,会导致花屏
		// 
		//根据av_seek_frame()后的第一帧视频数据,获取视频流起始pts,可以获得准确的音视频起始pts,
		//用这个方式获取的pts,不会花屏
		if (is_first_video_pkt == true && pkt.stream_index == demux.video_index())
		{
			video_begin_pts = pkt.pts;
			is_first_video_pkt = false;
		}
		//根据seek后的第一帧音频数据,获取音频流起始pts()
		if (is_first_audio_pkt == true && pkt.stream_index == demux.audio_index())
		{
			audio_begin_pts = pkt.pts;
			is_first_audio_pkt = false;
		}
		/
		// /

		//视频信息
		if (video_end_pts > 0 && 
			pkt.stream_index == demux.video_index() && 
			pkt.pts > video_end_pts)
		{
			av_packet_unref(&pkt);
			break;
		}

		//统计视频帧数,计算持续时间
		if (pkt.stream_index == demux.video_index())
		{
			mux.RescaleTime(&pkt, video_begin_pts, demux.video_time_base());

			video_count++;
			if (demux.video_time_base().den > 0)
			{
				total_sec += pkt.duration * ((double)demux.video_time_base().num / (double)demux.video_time_base().den);
			}
		}
		else if (pkt.stream_index == demux.audio_index())
		{
			mux.RescaleTime(&pkt, audio_begin_pts,demux.audio_time_base());
			audio_count++;
		}



		//写入音视频帧,会清理pkt
		mux.Write(&pkt);
		//av_packet_unref(&pkt);
		//this_thread::sleep_for(1000ms);
	}

	//写入结尾,包含偏移索引
	mux.WriteEnd();


	//avformat_close_input(&ic);
	demux.set_c(nullptr);
	mux.set_c(nullptr);

	cout << "输出文件" << out_file << endl;
	cout << "视频帧:" << video_count << endl;
	cout << "音频帧:" << audio_count << endl;
	cout << "总时长:" << total_sec << endl;
	

	//avio_closep(&ec->pb);
	//avformat_free_context(ec);
	//ec = nullptr;
	return 0;
}

你可能感兴趣的:(音视频)