ffmpeg 解码文件时的时间戳问题

本文讨论了实时流与普通文件在编码和解码时对pts和dts的处理,强调了在处理带b帧的文件时使用dts的重要性,并给出了正确获取时间基和播放延时的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实时流和普通文件

1 实时流
实时流编码时,我们一般不进行b帧编码,但是文件存储时为了减小大小,会增加b帧,实时流只带了I,P帧,那就会好很多
2 普通文件
很多文件带了b帧,所以要使用解码时间去同步,如果使用pts,很多人一定会被其时间搞混。

我们可以正确使用AVFrame 的pts 和pkt-dts 去保存正常的时间,如果不给其正确赋值,那么值就如下所示,是novalue。
在这里插入图片描述
那么获取pts和dts 去赋值给avframe就行了,注意时间基。如果不进行时间延时,那么文件会被cpu 快速读完,这就看cpu有多强了,当然,为了仅仅是测试,当然可以直接使用帧率,间隔时间延时就行,如果做播放器就不能这样了,我们要严格掐时间,按照秒表去对。

int main()
{
	c_test test;
	std::thread t([&test]() {
		// 在这里编写你的匿名函数的代码
		std::cout << "Hello from the new thread!" << std::endl;
		test.func_init("G:/record/A1_.mp4");
		test.Start();
	});
	while (1)
	{
		AVFrame* f = test.GetData();
		if (f != NULL)
		{

			int h = f->height;
			int w = f->width;
			
			cv::Mat mat(h, w, CV_8UC3,f->data[0]);
			cv::Mat matBGR;
			cv::cvtColor(mat, matBGR,cv::COLOR_RGB2BGR/* cv::COLOR_BGR2YUV_I420*/);
			cv::imshow("show", matBGR);
			std::cout << "the pts is :" << f->pts<<std::endl;
			av_freep(&f->data[0]);
			av_frame_free(&f);
		}
		if (cv::waitKey(30) == 'q')
			break;
	}
	test.Stop();
	std::cout << "end of this thread" << std::endl;
	//test.func_seek(10);
	t.join();
}

以上代码的RGB到BGR的转化其实没有必要使用, 实际播放的时候,我们直接渲染RGB24,RGBA,甚至YUV,NV12 都可以,这里为了方便,把这个转化成了BGR24,不过是想让opencv 直接渲染,由于opencv 中 bgr的rgb的颜色交换,播放时看起来不舒服,所以转化一下更符合实际,不转化也没什么。如果不想转化,甚至直接使用nv12,yuv420,rgb24,为了方便演示可以使用sdl去播放,如果对opengl熟悉,自己就写一段代码去播放。文章可以到我其他的文章里面找,我应该都写过。

正确的播放延时

获取时间基

	AVRational time_base = input_ctx->streams[video_stream]->time_base;
	AVRational time_base_q = { 1,AV_TIME_BASE }; // AV_TIME_BASE_Q;

要正确播放,一定要使用dts,也就是解码时间,而非pts,对于带b帧的视频来说一定是如此。

if (v_isrealtime == false && video_stream == packet.stream_index)
{
	//这里需要更加精确的计算
	if (v_starttime == -1)
		v_starttime = av_gettime();
	//av_usleep(30 * 1000);
	int64_t pts_time = av_rescale_q(packet.dts, time_base, time_base_q);
	if (v_startptstime == -1)
		v_startptstime = pts_time;
	int64_t N = av_gettime() - v_starttime;
	int64_t S = pts_time - v_startptstime;
	if (S > N)
	{
		av_usleep(S-N);
		std::cout << "sleep:" << S - N << std::endl;
	}
}

播放测试的时候可以使用pc上有秒表卡住时间,没有就使用手机也可以,两个相差一直是同样的描述,持续时间长一点

在这里插入图片描述

正确的同步

同步时一定要使用系统时间和播放时间相对应,如果文件有跳帧拖拉进度条和快放的需求,更加要注意,其实以上代码已经包含了跳帧和快放的基本需求,研究一下就知道了。

### 关于 FFmpeg 多线程解码时间戳错位的问题 FFmpeg 是一个多用途的多媒体处理工具,在多线程环境下进行视频解码可能会遇到帧的时间戳(timestamp)错位问题。这种现象通常是由以下几个原因引起的: #### 1. **多线程解码机制** FFmpeg 支持通过 `-threads` 参数设置多个解码线程来加速解码过程。然而,当启用多线程解码,不同线程之间的数据流可能存在不一致的情况,这可能导致某些帧的时间戳被错误分配[^1]。 #### 2. **时间戳计算逻辑** 在多线程模式下,FFmpeg 可能会因为并行处理而导致部分帧的时间戳未按顺序生成。具体来说,如果某个线程完成了解码但未能及更新全局时间戳计数器,则后续帧的时间戳可能基于旧的状态而发生偏移[^3]。 #### 解决方案 以下是几种常见的解决方案用于解决 FFmpeg 多线程解码中的时间戳错位问题: #### 方法一:禁用多线程解码 最简单的方法之一是完全关闭多线程支持,强制使用单线程解码。可以通过命令行参数 `threads=1` 实现这一点: ```bash ffmpeg -i input.mp4 -c:v copy -threads 1 output.mp4 ``` 这种方法虽然可以有效避免时间戳错位,但由于只利用了一个 CPU 核心资源,因此性能较低。 #### 方法二:调整线程类型 除了控制总的线程数量外,还可以指定具体的线程工作模式。例如,尝试切换到切片级或多帧级平行化策略: ```bash ffmpeg -init_thread_copy true -thread_type slice -i input.mp4 -vf "setpts=N/FRAME_RATE/TB" output.mp4 ``` 这里的关键选项包括: - `-init_thread_copy`: 确保每个子线程拥有独立的数据副本。 - `-thread_type`: 定义如何分割任务给各个线程执行 (slice 或 frame)[^2]. #### 方法三:重新校准 PTS/DTS 即使存在轻微偏差,也可以通过对输出流应用滤镜重设每帧呈现时间戳(PTS) 来修正异常情况。下面是一个例子: ```bash ffmpeg -i input.mp4 -vf "setpts=N/(FRAME_RATE*TB)" corrected_output.mp4 ``` 此方法不会改变实际媒体内容本身,只是修改关联元数据以便播放更加流畅。 #### 方法四:升级至最新版本 开发者持续改进软件功能的同也在修复已知缺陷;所以建议始终采用官方发布的最新稳定版 FFmpeg ,因为它很可能已经解决了早期版本中存在的此类 bug . --- ### 示例代码片段展示如何手动调整 PTS 值 如果你希望通过编程手段干预这一流程,那么可以用 Python 结合 PyAV 库来进行更精细的操作: ```python import av container = av.open('input.mp4') stream = container.streams.video[0] for packet in container.demux(stream): for frame in packet.decode(): # 手动设定新的 pts 值 frame.pts = int(frame.time * stream.time_base.denominator / stream.time_base.numerator) # 编码回原容器或其他目标位置... ``` 上述脚本遍历所有视频包及其内部帧,并逐一为其赋予经过标准化后的 presentation timestamp 数值[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qianbo_insist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值