news 2026/1/16 9:01:01

FFmepg-- 33-ffplay源码-FrameQueue 环形缓冲区涉及以及保持画面

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FFmepg-- 33-ffplay源码-FrameQueue 环形缓冲区涉及以及保持画面

文章目录

      • FrameQueue 使用环形缓冲区的原因
        • 背景需求
        • 环形缓冲区优势
      • FrameQueue vs 普通队列:核心差异
      • keep_last 机制详解
        • 设计目的
        • 核心字段含义
        • 关键函数:frame_queue_peek()
        • 关键函数:frame_queue_next()
      • 运行示例(视频队列,max_size=3, keep_last=1)
      • frame_queue_nb_remaining() 如何计算?
      • keep_last 总结

FrameQueue 使用环形缓冲区的原因

背景需求

视频播放中解码线程持续向队列写入帧(生产者),渲染线程从队列读取帧用于显示(消费者)。需要支持保留当前显示帧、预读下一帧(用于计算显示间隔、插帧等)、暂停时画面不消失、低延迟和高性能。普通动态队列(如链表)频繁 malloc/free 会带来性能开销和内存碎片,不适合实时音视频场景。

环形缓冲区优势

ffplay 的 FrameQueue 是一个固定大小的数组 + 读写指针结构:

typedefstructFrameQueue{AVFrame*queue[MAX_FRAME_QUEUE_SIZE];// 固定数组,提前分配intrindex;// 读指针(逻辑上指向“已读但未释放”的帧)intwindex;// 写指针intsize;// 当前有效帧数intmax_size;// 最大容量(通常 audio=9, video=3, sub=16)intkeep_last;// 是否保留最后一帧intrindex_shown;// 标记当前 rindex 帧是否已被“显示过”SDL_mutex*mutex;SDL_cond*cond;}FrameQueue;

优点总结:

  • 内存一次性分配,无碎片
  • 指针通过 % max_size 循环使用,O(1) 读写
  • 支持“只读不删”(peek)、“延迟删除”(next)

FrameQueue vs 普通队列:核心差异

特性普通队列(如 std::queue)ffplay FrameQueue
出队行为pop() 立即释放内存frame_queue_next() 才真正释放
读操作front() 后必须 pop可多次 peek() 查看当前/下一帧
最后一帧出队即销毁若 keep_last=1,即使出队也保留
用途通用数据传输音视频渲染专用(需保留历史帧)

关键区别:“读” ≠ “消费”。FrameQueue 允许“查看但不移除”,这是实现流畅渲染的基础。

keep_last 机制详解

设计目的
  • 暂停时保持画面:不能因为“已显示”就立刻释放帧
  • 计算帧间隔:需要 lastvp(上一帧)和 vp(当前帧)的 PTS 差值
  • 防止黑屏:在新帧未到达前,继续显示旧帧
核心字段含义
intrindex;// 指向“逻辑上最后一个保留的帧”(通常是 lastvp)intrindex_shown;// =0 表示 rindex 帧尚未作为“当前帧”显示过// =1 表示 rindex 帧已是“上一帧”,当前帧是 (rindex+1)%sizeintkeep_last;// =1 表示启用保留机制(video/subtitle 启用,audio 不启用)
关键函数:frame_queue_peek()
// 获取当前应显示的帧(不移动指针)staticinlineFrame*frame_queue_peek(FrameQueue*q){return&q->queue[(q->rindex+q->rindex_shown)%q->max_size];}

若 rindex_shown=0 → 返回 rindex 帧(即 lastvp,也是当前帧)
若 rindex_shown=1 → 返回 (rindex+1) 帧(当前帧),而 rindex 是 lastvp

关键函数:frame_queue_next()
staticinlinevoidframe_queue_next(FrameQueue*q){if(q->keep_last&&!q->rindex_shown){// 第一次调用 next():仅标记 rindex_shown=1,不移动 rindexq->rindex_shown=1;return;}// 真正释放 rindex 帧,并移动指针av_frame_unref(q->queue[q->rindex]);q->rindex=(q->rindex+1)%q->max_size;q->rindex_shown=0;// 新的 rindex 尚未作为“当前帧”显示q->size--;SDL_CondSignal(q->cond);}

运行示例(视频队列,max_size=3, keep_last=1)

假设解码线程写入了 3 帧:F0, F1, F2

初始状态(刚写完):

queue = [F0, F1, F2] windex = 0(循环回绕) rindex = 0 rindex_shown = 0 size = 3

第一次渲染(显示 F0):

  • frame_queue_peek() → 返回 queue[(0+0)%3] = F0
  • 显示 F0
  • 调用 frame_queue_next():
    • 因为 keep_last=1 && rindex_shown=0 → 仅设 rindex_shown=1
    • rindex 仍为 0,F0 未被释放!
      此时:
rindex = 0 (F0 保留为 lastvp) rindex_shown = 1 → 当前帧是 (0+1)=F1 size = 3(未减少!)

第二次渲染(显示 F1):

  • frame_queue_peek() → 返回 queue[(0+1)%3] = F1
  • 显示 F1
  • 调用 frame_queue_next():
    • 现在 rindex_shown=1 → 执行真实出队:
      • av_frame_unref(F0) → 释放 F0
      • rindex = (0+1)%3 = 1
      • rindex_shown = 0
      • size = 2
        此时:
queue = [__, F1, F2] (F0 已释放) rindex = 1 → 指向 F1(作为新的 lastvp) rindex_shown = 0 → 当前帧仍是 F1(下一次 peek 还是 F1)

注意:F1 被“保留”了两次——第一次作为“当前帧”显示,第二次作为“lastvp”供下次同步参考。

frame_queue_nb_remaining() 如何计算?

staticintframe_queue_nb_remaining(FrameQueue*q){returnq->size-q->rindex_shown;}
  • size:队列中总帧数(包括保留的 lastvp)
  • rindex_shown:若为 1,说明 lastvp 已“转正”为历史帧,当前帧是下一个,因此可显示的帧数 = size - 1
  • 若为 0,说明当前帧就是 rindex,所有 size 帧都可用

示例:

  • size=3, rindex_shown=1 → 可显示帧数 = 2(当前帧 + 下一帧)
  • size=1, rindex_shown=0 → 可显示帧数 = 1(只有当前帧)

这对 video_refresh 判断是否该丢帧或等待至关重要。

keep_last 总结

场景作用
暂停播放保留 lastvp,画面不黑
计算帧率lastvp.pts 与 vp.pts 做差
音视频同步视频时钟基于 lastvp 更新
低内存占用固定 3 帧缓存,避免堆积
流畅渲染支持“显示当前帧 + 预读下一帧”
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/8 19:51:35

AutoGPT提示词工程技巧:提升任务拆解准确性

AutoGPT提示词工程技巧:提升任务拆解准确性 在智能体技术逐渐从“被动响应”迈向“主动执行”的今天,我们正见证一个关键转折点——AI不再只是回答问题的工具,而是能独立完成复杂目标的协作者。像AutoGPT这样的自主代理系统,已经展…

作者头像 李华
网站建设 2026/1/11 2:26:41

Stable Diffusion AIGC 视觉设计实战教程之 07-图生图

图生图生成逻辑 图生图生成逻辑概述 Stable Diffusion 图生图技术的底层逻辑主要基于深度学习,特别是生成对抗网络(GAN)和扩散模型(Diffusion Model)的结合,其核心思想是通过训练大量的数据来让模型学习如何…

作者头像 李华
网站建设 2026/1/4 6:46:33

当毕业论文不再是“一个人的深夜战场”:一位研究生眼中的AI科研协作者如何重塑写作流程

凌晨两点,寝室只剩下电脑屏幕的微光。你盯着文档里那句改了八遍的引言,焦虑感像潮水般涌来——文献综述逻辑松散、方法描述不够严谨、讨论部分缺乏深度……这不是某一个人的困境,而是每年数百万毕业生共同面对的“写作黑洞”。但最近&#xf…

作者头像 李华
网站建设 2026/1/16 5:39:36

统计提交svn代码行数,文件以及文档

本文介绍了如何使用Java开发一个小工具,以统计指定时间段内SVN用户提交的代码行数、文件数量以及文档变化。通过svn log和svn diff命令结合,实现对SVN提交记录的分析,满足对人员工作量可视化的需要。下面简述下自己的开发思想。 一。核心是sv…

作者头像 李华
网站建设 2026/1/9 8:12:21

解锁学术新次元:书匠策AI科研工具为毕业论文注入智慧动能

在学术研究的浩瀚宇宙中,毕业论文宛如一颗璀璨星辰,既承载着学子们对知识的深度探索,又见证着他们从青涩走向成熟的学术蜕变。然而,这条通往星辰的征途并非坦途,选题迷茫、逻辑混乱、内容匮乏、格式繁杂等问题&#xf…

作者头像 李华
网站建设 2026/1/13 1:39:49

GPT-5.2全面解析:AI“打工能力“大提升,程序员职场必备技能

OpenAI最新发布的GPT-5.2模型在30天内快速迭代,重点强化"打工能力"。通过实际测试,GPT-5.2在Excel处理、客服问题解决、超长文档处理等方面表现优异。GDPval测试显示其在74%的任务中达到或超过人类专家水平,且速度比人类快11倍。更…

作者头像 李华