m0_61770108 2024-06-19 22:24 采纳率: 13.5%
浏览 14
已结题

av_seek_frame将视频和音频跳转到指定时间戳,视频和音频不同步

av_seek_frame将视频和音频跳转到指定时间戳,视频和音频不同步
环境:Qt+ffmpeg将本地MP4文件封装为rtsp流,使用av_seek_frame将视频和音频跳转到指定时间戳,
问题:音频视频不同步,且视频图画一直卡着不动
源码:


while(m_statue){//循环读取每一帧直到读完

        if(OperationType.size()>0){
            if(OperationType.dequeue() == 1){
                int progress = ProgressTime.dequeue();
                if(av_seek_frame(inAFmtCtx, inAStreamIndex, progress*AV_TIME_BASE, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) == 0&&
                    av_seek_frame(inAFmtCtx, inVStreamIndex, progress*AV_TIME_BASE, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) == 0){
                    qDebug()<<"跳转成功";

                }
            }
        }

        if(av_read_frame(inAFmtCtx, m_pkt)>=0){
            if (m_pkt->stream_index == inVStreamIndex || m_pkt->stream_index == inAStreamIndex) {

                if(m_pkt->stream_index == inVStreamIndex) {
                    m_pkt->stream_index = m_outVideoIndex;
                    m_pkt->dts *= 1;
                }
                if(m_pkt->stream_index == inAStreamIndex){
                    m_pkt->stream_index = m_outAudioIndex;
                    m_pkt->dts *= 1;
                }

                in_stream  = inAFmtCtx->streams[m_pkt->stream_index];
                //FIX me如果pts异常设置为0, 更为稳妥的做法是在前一帧上叠加
                if(m_pkt->pts == AV_NOPTS_VALUE) {
                    m_pkt->pts *= 1;
                }
                if(m_pkt->dts == AV_NOPTS_VALUE) {
                    m_pkt->dts *= 1;
                }
                if(m_pkt->duration == AV_NOPTS_VALUE) {
                    m_pkt->duration = 0;
                }
                out_stream = outFmtCtx->streams[m_pkt->stream_index];

                m_pkt->pts = av_rescale_q_rnd(m_pkt->pts, in_stream->time_base, out_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                m_pkt->dts = av_rescale_q_rnd(m_pkt->dts, in_stream->time_base, out_stream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                m_pkt->duration = av_rescale_q(m_pkt->duration, in_stream->time_base, out_stream->time_base);
                m_pkt->pos = -1;

                // 控制读取速率
                if (m_pkt->stream_index == inVStreamIndex) {
                    int64_t duration_us = av_rescale_q(m_pkt->duration, in_stream->time_base, AV_TIME_BASE_Q);
                    av_usleep(duration_us);
                }


                if(av_interleaved_write_frame(outFmtCtx, m_pkt)<0){
                    qDebug()<<"Error muxing packet";
                    break;
                }

                //输出帧数
                // frameIndex++;
                // qDebug()<<"frameIndex:"<<frameIndex;


                av_packet_unref(m_pkt);

            }
        }
    }

  • 写回答

2条回答 默认 最新

  • 鱼弦 全栈领域优质创作者 2024-07-13 11:37
    关注

    在使用 FFmpeg 的 av_seek_frame 函数进行跳转时,音视频不同步的问题可能是由以下几个原因引起的:

    1. 关键帧问题:在跳转到指定时间戳时,如果没有跳转到关键帧,视频会卡住或显示不正常。音频和视频的解码器需要从关键帧开始解码。
    2. 缓冲区清理:在进行跳转后,需要清理解码器的缓冲区,以确保不会处理旧的数据。
    3. PTS 和 DTS 同步:在跳转后,需要确保音频和视频的 PTS(Presentation Timestamp)和 DTS(Decoding Timestamp)正确调整。

    解决方案建议

    1. 跳转到最近的关键帧
      使用 AVSEEK_FLAG_BACKWARD 跳转到最近的关键帧,这样可以确保从关键帧开始解码。

    2. 清理解码器缓冲区
      在跳转后,调用 avcodec_flush_buffers 清理解码器缓冲区,以确保不会处理旧的数据。

    3. 同步 PTS 和 DTS
      确保在跳转后,正确设置音频和视频的 PTS 和 DTS。

    下面是修改后的代码示例:

    while (m_statue) { // 循环读取每一帧直到读完
    
        if (OperationType.size() > 0) {
            if (OperationType.dequeue() == 1) {
                int progress = ProgressTime.dequeue();
                int ret1 = av_seek_frame(inAFmtCtx, inAStreamIndex, progress * AV_TIME_BASE, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY);
                int ret2 = av_seek_frame(inAFmtCtx, inVStreamIndex, progress * AV_TIME_BASE, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY);
    
                if (ret1 >= 0 && ret2 >= 0) {
                    qDebug() << "跳转成功";
    
                    // 清空解码器缓冲区
                    avcodec_flush_buffers(audio_codec_context);
                    avcodec_flush_buffers(video_codec_context);
                } else {
                    qDebug() << "跳转失败";
                    continue;
                }
            }
        }
    
        if (av_read_frame(inAFmtCtx, m_pkt) >= 0) {
            if (m_pkt->stream_index == inVStreamIndex || m_pkt->stream_index == inAStreamIndex) {
    
                if (m_pkt->stream_index == inVStreamIndex) {
                    m_pkt->stream_index = m_outVideoIndex;
                    m_pkt->dts *= 1;
                }
                if (m_pkt->stream_index == inAStreamIndex) {
                    m_pkt->stream_index = m_outAudioIndex;
                    m_pkt->dts *= 1;
                }
    
                in_stream = inAFmtCtx->streams[m_pkt->stream_index];
                if (m_pkt->pts == AV_NOPTS_VALUE) {
                    m_pkt->pts = 0; // 更为稳妥的做法是在前一帧上叠加
                }
                if (m_pkt->dts == AV_NOPTS_VALUE) {
                    m_pkt->dts = 0; // 更为稳妥的做法是在前一帧上叠加
                }
                if (m_pkt->duration == AV_NOPTS_VALUE) {
                    m_pkt->duration = 0;
                }
                out_stream = outFmtCtx->streams[m_pkt->stream_index];
    
                m_pkt->pts = av_rescale_q_rnd(m_pkt->pts, in_stream->time_base, out_stream->time_base,
                                              (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                m_pkt->dts = av_rescale_q_rnd(m_pkt->dts, in_stream->time_base, out_stream->time_base,
                                              (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                m_pkt->duration = av_rescale_q(m_pkt->duration, in_stream->time_base, out_stream->time_base);
                m_pkt->pos = -1;
    
                // 控制读取速率
                if (m_pkt->stream_index == inVStreamIndex) {
                    int64_t duration_us = av_rescale_q(m_pkt->duration, in_stream->time_base, AV_TIME_BASE_Q);
                    av_usleep(duration_us);
                }
    
                if (av_interleaved_write_frame(outFmtCtx, m_pkt) < 0) {
                    qDebug() << "Error muxing packet";
                    break;
                }
    
                av_packet_unref(m_pkt);
            }
        }
    }
    

    注意事项

    1. 清理解码器缓冲区
      在调用 avcodec_flush_buffers() 时,需要传递正确的解码器上下文(audio_codec_contextvideo_codec_context)。

    2. 检查返回值
      一定要检查 av_seek_frame 和其他函数的返回值,以确保操作成功。

    通过这些调整,可以更好地控制音视频的同步,并确保在跳转后画面和声音能够正常播放。如果问题仍然存在,请考虑进一步调试具体的音视频流数据,确保它们的时间戳和解码顺序正确。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 8月6日
  • 创建了问题 6月19日

悬赏问题

  • ¥15 Opencv(C++)异常
  • ¥15 VScode上配置C语言环境
  • ¥15 汇编语言没有主程序吗?
  • ¥15 这个函数为什么会爆内存
  • ¥15 无法装系统,grub成了顽固拦路虎
  • ¥15 springboot aop 应用启动异常
  • ¥15 matlab有关债券凸性久期的代码
  • ¥15 lvgl v8.2定时器提前到来
  • ¥15 qtcp 发送数据时偶尔会遇到发送数据失败?用的MSVC编译器(标签-qt|关键词-tcp)
  • ¥15 cam_lidar_calibration报错