音视频(关于视频的封装,由mp4->mov)

(20条消息) FFmpeg + Visual studio 开发环境搭建_HW140701的博客-CSDN博客

1.封装格式:AVI,MP4,ASF

AVI:压缩标准可以任意选

FLV,ts:直播等使用的流媒体

mp4:既是封装又是压缩

#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")

int main(int argc, char *argv[])
{
	char infile[] = "test.mp4";//要转的文件
	char outfile[] = "test.mov";//目标文件
	av_register_all();//注册了很多东西
	/*AVFormatContext **ps整个封装格式上下文
		{
			    AVIOContext *pb;IO上下文,通过他可以进行写入
				AVStream **streams;视频音频字幕流,存放了所有的流的信息,一般这个数组的第一个是视频,第二个是音频

		}
	const char * url音视频文件地址
	AVInputFormat *fmt音视频格式,此格式可以传NULL,由url决定
	AVDictionary **options音视频格式的参数
	*/
	AVFormatContext* ic = NULL;
	/**
 * 打开输入流并读取标头。编解码器未打开。
 * 流必须使用 avformat_close_input() 关闭。
 *
 * @param ps 指向用户提供的 AVFormatContext 的指针(由 avformat_alloc_context 分配)。
 * 可能是指向 NULL 的指针,在这种情况下,AVFormatContext 由此分配
 * 函数并写入 PS。
 * 请注意,用户提供的 AVFormatContext 将在失败时释放。
 * @param要打开的流的网址网址。
 * @param fmt 如果非 NULL,则此参数强制使用特定的输入格式。
 * 否则会自动检测格式。
 * @param选项 一个充满AVFormatContext和demuxer-private选项的字典。
 * 返回时,此参数将被销毁并替换为包含
 * 未找到的选项。可能为空。
 *
 * 成功时为 @return 0,失败时为 AVERROR 为负值。
 *
 * @note 如果要使用自定义 IO,请预分配格式上下文并设置其 pb 字段。
 */
	avformat_open_input(&ic, infile, 0, 0);//后两个参数可根据音频文件的后缀自动检测
	if (!ic)
	{
		cout << "avformat_open_input failed!" << endl;
		getchar();
	}

	///2 create output context
	AVFormatContext* oc = NULL;
	/**
 * 为输出格式分配 AVFormatContext。
 * avformat_free_context() 可用于释放上下文和
 * 其中框架分配的所有内容。
 *
 * @param *ctx 设置为创建的格式上下文,或在
 * 故障案例
 * @param用于分配上下文的 oformat 格式,如果为 NULL
 * 改用format_name和文件名
 * @param format_name用于分配
 * 上下文,如果使用 NULL 文件名代替
 * @param文件名 用于分配
 * 上下文,可能是空的
 * @return >= 0 如果成功,则为负 AVERROR 代码
 *失败
 */
	avformat_alloc_output_context2(&oc, NULL, NULL, outfile);
	if (!oc)
	{
		cerr << "avformat_alloc_output_context2 " << outfile << " failed!" << endl;
		getchar();
		return -1;
	}

	///3 add the stream
	AVStream* videoStream = avformat_new_stream(oc, NULL);//一般数组的第一个都是视频
	AVStream* audioStream = avformat_new_stream(oc, NULL);

	///4 copy para
	avcodec_parameters_copy(videoStream->codecpar, ic->streams[0]->codecpar);
	avcodec_parameters_copy(audioStream->codecpar, ic->streams[1]->codecpar);

	videoStream->codecpar->codec_tag = 0;//有关编解码器的其他信息,我们此处不进行使用
	audioStream->codecpar->codec_tag = 0;

	av_dump_format(ic, 0, infile, 0);//打印输入文件的信息
	cout << "================================================" << endl;
	av_dump_format(oc, 0, outfile, 1);//打印输出文件的信息


	///5 open out file io,write head
	int ret = avio_open(&oc->pb, outfile, AVIO_FLAG_WRITE);
	if (ret < 0)
	{
		cerr << "avio open failed!" << endl;
		getchar();
		return -1;
	}
	/**
 * 分配流私有数据并将流标头写入
 * 输出媒体文件。
 *
 * @param 的媒体文件句柄,必须使用 avformat_alloc_context() 分配。
 *其oformat字段必须设置为所需的输出格式;
 * 其 pb 字段必须设置为已打开的 AVIOContext。
 * @param选项 一个充满 AVFormatContext 和 muxer-private 选项的 AVDictionary。
 * 返回时,此参数将被销毁并替换为包含
 * 未找到的选项。可能为空。
 *
 * 如果编解码器尚未在avformat_init中完全初始化,则@return AVSTREAM_INIT_IN_WRITE_HEADER成功,
 * 如果编解码器已在 avformat_init 中完全初始化,则AVSTREAM_INIT_IN_INIT_OUTPUT成功,
 * 失败时的负面 AVERROR。
 *
 * @see av_opt_find、av_dict_set、avio_open、av_oformat_next、avformat_init_output。
 */
	ret = avformat_write_header(oc, NULL);
	if (ret < 0)
	{
		cerr << "avformat_write_header failed!" << endl;
		getchar();
	}
	AVPacket pkt;
	for (;;)
	{
		int re = av_read_frame(ic, &pkt);//此处注意,因为帧长不一定,所以每次循环需要把之前的数据清理掉
		if (re < 0)
			break;

/**
	 * 以 AVStream->time_base 为单位的演示时间戳;时间
	 * 解压缩的数据包将呈现给用户。
	 * 如果未存储在文件中,则可以AV_NOPTS_VALUE。
	 * pts 必须大于或等于 dts,因为演示之前不能发生
	 *解压缩,除非有人想查看十六进制转储。某些格式滥用
	 * 术语 DTS 和 PTS/CTS 的含义不同。这样的时间戳
	 * 在存储在 AVPacket 中之前,必须转换为真正的 pts/dts。
	AVPacket:
		pts: (int64_t)显示时间,结合AVStream->time_base转换成时间戳
		dts : (int64_t)解码时间,结合AVStream->time_base转换成时间戳
		size : (int)data的大小
		stream_index : (int)packet在stream的index位置

		flags : (int)标示,结合AV_PKT_FLAG使用,其中最低为1表示该数据是一个关键帧。
		#define AV_PKT_FLAG_KEY    0x0001 //关键帧
		#define AV_PKT_FLAG_CORRUPT 0x0002 //损坏的数据
		#define AV_PKT_FLAG_DISCARD  0x0004 /丢弃的数据

		side_data_elems : (int)边缘数据元数个数
		duration : (int64_t)数据的时长,以所属媒体流的时间基准为单位,未知则值为默认值0
		pos : (int64_t )数据在流媒体中的位置,未知则值为默认值 - 1
*/
		pkt.pts = av_rescale_q_rnd(pkt.pts,
			ic->streams[pkt.stream_index]->time_base,
			oc->streams[pkt.stream_index]->time_base,
			(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
		);
		pkt.dts = av_rescale_q_rnd(pkt.dts,
			ic->streams[pkt.stream_index]->time_base,
			oc->streams[pkt.stream_index]->time_base,
			(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
		);
		pkt.pos = -1;
		pkt.duration = av_rescale_q_rnd(pkt.duration,
			ic->streams[pkt.stream_index]->time_base,
			oc->streams[pkt.stream_index]->time_base,
			(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
		);

		av_write_frame(oc, &pkt);
		av_packet_unref(&pkt);
		cout << ".";
	}

	av_write_trailer(oc);//加一个尾字段,类似于记录前面每一帧的索引和总的时长。
	avio_close(oc->pb);
	cout << "================end================" << endl;




	getchar();
	return 0;
}

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