ffmpeg之mp4文件解封装截取一段视频并重封装

#include 
#include 
#include 

extern "C"{
#include 
}
//预处理指令导入库
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avcodec.lib")

#define CERR(err) do{if(err!=0)\
				{printErr(err); return -1;}\
					}while(0)

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

int main(int argc, char* argv[])
{
/**********解封装初始化************/
//打开媒体文件
	const char* url = "v1080.mp4";
//1, 生成解封装输入上下文, ic需要释放空间
	AVFormatContext* ic = nullptr;
	int re = avformat_open_input(&ic, 
		url, 
		NULL, //解封装格式,自动探测,以后缀名,或者文件头
		NULL);//参数设置, rtsp需要设置
	CERR(re);

//2, 获取媒体信息 无头部格式的
	re = avformat_find_stream_info(ic, NULL);
	CERR(re);
	//打印封装信息
	av_dump_format(ic, 0, url, 0);

	//提取音视频流, 后面直接使用as, vs
	AVStream* as = nullptr;
	AVStream* vs = nullptr;

	for (int i = 0; i < ic->nb_streams; ++i) {
		if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
			as = ic->streams[i];
			cout << "===音频===" << endl;
			cout << "sample_rate:" << as->codecpar->sample_rate << endl;
		}
		else if (ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			vs = ic->streams[i];
			cout << "===视频===" << endl;
			cout << "width:" << vs->codecpar->width << endl;
			cout << "height" << vs->codecpar->height << endl;
		}
	}

/***************************/


/**********封装初始化************/

//1, 封装上下文  ec需要释放空间
	AVFormatContext* ec = nullptr;
	re = avformat_alloc_output_context2(&ec, NULL, NULL, out_url);
	CERR(re);
//2, 添加视频流, 音频流
	AVStream* mvs = avformat_new_stream(ec, NULL); //在后面通过上下文设置编码器指针
	AVStream* mas = avformat_new_stream(ec, NULL);

//3, 打开输出IO 需要关闭
	re = avio_open(&ec->pb, out_url, AVIO_FLAG_WRITE);
	CERR(re);

//如果需要重编码, 下面4,5步骤的这些参数的设置就要重新计算

//4, 设置编码音视频流参数
	mvs->time_base = vs->time_base; //时间基数与原视频一致
	mas->time_base = as->time_base;

	mvs->codecpar->codec_tag = 0;
	mas->codecpar->codec_tag = 0;

//5, 从解封装复制参数
	avcodec_parameters_copy(mvs->codecpar, vs->codecpar);
	avcodec_parameters_copy(mas->codecpar, as->codecpar);

//6, 写入文件头
	re = avformat_write_header(ec, NULL);
	CERR(re);
	//打印输出上下文
	av_dump_format(ec, 0, out_url, 1);	
	
/***************************/

/**********截取视频seek 10-20s************/
	//取多不取少  假定9  11秒有关键帧我们取第9秒
	double begin_sec = 10.0;   //截取开始时间
	double end_sec = 20.0;		//截取结束时间
	long long begin_pts = 0;
	long long begin_audio_pts = 0;
	long long end_pts = 0;

	//时间点换算成pts,  以视频流为准, 到第20s的pts就结束封装
	//换算公式 播放时间time = pts * timebase
	//令 vs->pts * vs->timebase = time
	if (vs && vs->time_base.num > 0) { //除以分数就要判断分数的分子不为零
		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输入媒体文件 移动到第十秒的关键帧位置
	re = av_seek_frame(ic, vs->index, begin_pts,
		AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);//向后关键帧
	CERR(re);

/****************************************/


	AVPacket pkt;
    while (1){
    	re = av_read_frame(ic, &pkt); //从封装中读一包,有可能是视频包也有可能是音频包
    	if (re != 0) CERR(re);
    	
    	AVStream* out_stream = nullptr;
    	long long offset_pts = 0;
    	if (vs && pkt.stream_index == vs->index){//取到的是视频包
    		cout << "视频";
			//以视频为准 , 超过第20秒退出, 只存10~20秒 , 当然也可以以音频为准
			if (pkt.pts > end_pts) {
				av_packet_unref(&pkt);
				break;
			}
			out_stream = ec->streams[0];
			offset_pts = begin_pts;
    	}
		if (as && pkt.stream_index == as->index) {//读到的是音频包
			cout << "音频";
			out_stream = ec->streams[1];
			offset_pts = begin_audio_pts;
		}
		AVStream* in_stream = nullptr;
		if(pkt.stream_index <= 1){
			in_stream = ic->streams[pkt.stream_index];//可能是视频,也可能是音频
			//重新计算pts dts duration
			//a * bq (输入basetime)/ cq(输出basetime)   basetime存在AVStream中
			//因为进行了seek操作, 所以要输入的pts减去前面跳过的pts
			pkt.pts = av_rescale_q_rnd(pkt.pts - offset_pts, 
			in_stream->time_base, out_stream->time_base,
				(AVRounding)(AV_ROUND_INF|AV_ROUND_PASS_MINMAX));

			pkt.dts = av_rescale_q_rnd(pkt.dts - offset_pts, 
			in_stream->time_base, out_stream->time_base,
				(AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
			
			pkt.duration = av_rescale_q(pkt.duration, 
			in_stream->time_base, out_stream->time_base);

			pkt.pos = -1;
		}
		//写入音视频帧
		av_interleaved_write_frame(ec, &pkt);

		av_packet_unref(&pkt);
    }


	//写入结尾
	re = av_write_trailer(ec);
	CERR(re);


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

你可能感兴趣的:(C++,音视频开发,音视频)