直播流PTS值异常:FFmpeg接收UDP流时DTS/PTS跳变问题求助
问题:UDP流接收中FFmpeg音视频PTS/DTS跳变导致卡顿
使用FFmpeg源码接收UDP网络视频流时,音视频包的DTS和PTS偶尔会同时跳变——从较大值突然降到相差数十倍的极小值,直接影响音视频同步,导致播放卡顿,需要代码层面的解决方案。
收流写入缓冲区的处理代码
//read video package if(pAvPacket->stream_index == pThis->m_iVideoStreamIndex) { std::cout<<"Video_Packet_DTS: "<<pAvPacket->dts<<" Video_Packet_PTS: "<<pAvPacket->pts<<std::endl; //Video frame rate assignment if(pThis->m_pVideoTimeBase->num > 0 && pAvPacket->duration > 0) pThis->m_iVideoFPS = (pThis->m_pVideoTimeBase->den / pThis->m_pVideoTimeBase->num) / pAvPacket->duration; if(pThis->m_iDecodeMode == DECODE_MODE_ONLY_CURRENT_COLLECTION) pThis->m_videoDecodeThreadState = THREAD_NEED_STOP; //stop the thread else pThis->m_videoDecodeThreadState = THREAD_NEED_RUN; //make the thread run if(pThis->m_iMultiMediaType != MULTI_MEDIA_TYPE_FILE && pAvPacket->flags == AV_PKT_FLAG_KEY) { pThis->m_bIsFindIFrame = YTRUE; } VideoFramesCount ++; pThis->m_iReadVideoPacketCount ++; if(pThis->m_iDecodeMode != DECODE_MODE_ONLY_CHECK_NET && pThis->m_iDecodeMode != DECODE_MODE_AUDIO && pThis->m_bIsFindIFrame) YGlobCircleBufferWrite<AVPacket *>(pThis->m_videoPacketBuffer, &pAvPacket, 1); else av_packet_unref(pAvPacket); pAvPacket = YNULL; }
运行时打印的PTS/DTS信息
Video_Packet_DTS: 8590470992 Video_Packet_PTS: 8590470992 Video_Packet_DTS: 8590474592 Video_Packet_PTS: 8590474592 Video_Packet_DTS: 8590478192 Video_Packet_PTS: 8590478192 Video_Packet_DTS: 8590481792 Video_Packet_PTS: 8590481792 Video_Packet_DTS: 550800 Video_Packet_PTS: 550800 Video_Packet_DTS: 554400 Video_Packet_PTS: 554400 Video_Packet_DTS: 558000 Video_Packet_PTS: 558000 Video_Packet_DTS: 561600 Video_Packet_PTS: 561600 Video_Packet_DTS: 565200 Video_Packet_PTS: 565200
错误日志信息
[h264 @ 04fea380] non-existing PPS 0 referenced [h264 @ 04fea380] decode_slice_header error [h264 @ 04fea380] no frame! [swscaler @ 19b04600] Warning: data is not aligned! This can lead to a speed loss [mp3float @ 04feadc0] overread, skip -6 enddists: -2 -2 [mpegts @ 04fcbf40] DTS 550800 < 8590481792 out of order [mp3float @ 04feadc0] overread, skip -7 enddists: -6 -6 [mp3float @ 04feadc0] overread, skip -5 enddists: -2 -2
代码层面解决方案
检测并修正时间戳跳变
维护全局时间戳偏移量timestamp_offset,初始为0。每次收到包时:- 记录上一个有效包的PTS值(仅记录关键帧的PTS,避免非关键帧干扰)
- 计算当前包PTS与上一个有效PTS的差值,若差值为负且绝对值超过10倍单帧时长,判定为跳变
- 更新偏移量:
timestamp_offset = 上一个有效PTS - 当前PTS + timestamp_offset - 对后续所有包的PTS/DTS加上该偏移量,保证时间戳连续
注意:必须在收到关键帧后再应用修正,避免非关键帧依赖旧时间戳导致解码失败。
重置解码上下文
检测到时间戳跳变时,调用avcodec_flush_buffers()清空解码器缓存,丢弃缓冲区中未解码的旧包,等待新关键帧到来后重新启动解码流程,避免旧数据干扰新流解析。增强UDP流容错性
如果是MPEG-TS封装的UDP流,在打开流时设置mpegts_flags=resync,让FFmpeg的TS解析器自动处理流重新同步;如果是RTP封装流,设置reorder_queue_size参数开启包序重组,减少无序包导致的时间戳异常。修正FPS计算逻辑
当前整数除法会导致FPS精度丢失,改为浮点计算更准确:if(pThis->m_pVideoTimeBase->num > 0 && pAvPacket->duration > 0) { double frame_duration = av_q2d(*pThis->m_pVideoTimeBase) * pAvPacket->duration; pThis->m_iVideoFPS = 1.0 / frame_duration; }准确的单帧时长能更精准地判断时间戳跳变。
内容的提问来源于stack exchange,提问作者watermelon_seven
相关产品推荐
相关产品推荐

