GstQueue2 知识文档

GstQueue2 知识文档 GstQueue2 知识文档GStreamer 版本1.0subprojects/gstreamer1. 概述queue2是queue的重量级升级版同属 coreelements 插件。名字里的 “2” 不代表它取代queue——两者定位不同长期共存。相比queuequeue2多出三样核心能力能力说明Buffering 消息按低/高水位线向应用发送GST_MESSAGE_BUFFERING带百分比驱动播放器缓冲中…进度条与自动暂停/恢复本地缓存temp-file / ring-buffer可把数据落到磁盘临时文件或内存环形缓冲缓冲容量远超内存 FIFO支持 download buffering 与 timeshiftPull 模式 / 随机读src pad 支持get_range拉取模式配合磁盘缓存实现下载一次、可回退 seek的按需供给一句话queue 只管线程解耦 小缓冲queue2 在此之上加了「缓冲进度上报」和「大容量磁盘/环形缓存」专为网络流与点播设计。它和queue/multiqueue一样同样是继承自GstElement的元件ELEMENT需要状态催动有一个 sink pad 和一个 src pad靠 src pad 的 task 线程解耦上下游。2. 调试与设计时最该关注的技术点2.1 先确认当前存储模式三种模式的“满”完全不同queue2的核心不是单纯“多一个 buffering 消息”而是存储模式决定了流控语义模式如何启用满队列后的行为最该关注的风险内存 FIFO默认达到max-size-*后阻塞上游形成反压内存上限、下载被反压停止Ring bufferring-buffer-max-size 0当前 range 用满MIN(max-size-bytes, ring-buffer-max-size)后反压可回退窗口有限旧区间会被覆盖/失效Temp filetemp-template ! NULL非 EOS 时gst_queue2_is_filled()始终返回 FALSE不因max-size-*反压磁盘空间、文件生命周期、下载持续到 EOS这是排查“为什么下载仍继续”最重要的判断temp-file 模式下max-size-*只用于 buffering 统计不限制文件增长也不会以此停止下载。2.2use-buffering只发消息不会主动暂停播放use-bufferingTRUE时queue2 计算水位后发GST_MESSAGE_BUFFERINGuse-bufferingFALSE时不发。无论开关状态入队、出队、反压和本地缓存逻辑都仍在执行。queue2 自己不调用gst_element_set_state(PAUSED)。播放器/应用收到消息后才决定收到 BUFFERING 100 - 应用或 playbin 可选择暂停等待 收到 BUFFERING 100 - 应用或 playbin 可选择恢复播放因此排查“进入 buffering 但仍在播”或“显示 100% 仍不恢复”时要分别查看 queue2 发出的 bus message 与上层对消息的处理不能只看 queue2 的buffering_percent。2.3 Buffering 百分比不是占总容量比例buffering_percent的 0~100 是相对 low/high watermark 的状态值达到高水位表示 100%低于低水位则重新进入 buffering。它不等价于“磁盘文件已下载百分之多少”也不必然等价于current-level-bytes / max-size-bytes。调试时必须同时看current-level-buffers/bytes/timemax-size-*low-watermark/high-watermark输入/输出速率与buffering_leftbus 上真正发出的GST_MESSAGE_BUFFERING。2.4 反压位置取决于下游是否真正消费内存 FIFO 与 ring-buffer 模式中sink chain 在gst_queue2_wait_free_space()等待空间src task 由gst_queue2_loop()出队、再gst_pad_push()给下游。下游 gst_pad_push() 阻塞 - queue2 src task 不再出队 - 内存/ring 缓冲逐渐填满 - sink chain 等待 item_del - 上游 gst_pad_push() 不返回 - source 停止继续读取/下载temp-file 模式例外文件模式不以 max-size 判满除非 EOS、flush、错误或外部 source 自身受限否则上游可持续写入磁盘。2.5 状态切换决定缓存资源何时创建和销毁在READY - PAUSEDtemp-file 模式打开临时文件ring-buffer 模式分配ring_buffer_max_size字节内存初始化 range 链表清除本次流的 segment/stream-start 保存状态。在PAUSED - READY关闭临时文件按temp-remove决定是否删除释放 ring buffer清理 range 链表与缓存的 segment/stream-start event。所以缓存文件或环形缓冲的生命周期是播放会话级不是一个 buffer 或一次 seek 的生命周期。反复 set state、异常退回 READY 都会清掉缓存。2.6 Pull/seek 场景必须看 range而不是只看 FIFO 水位queue2 的GstQueue2Range记录 source offset、写入位置、读取位置以及 ring-buffer offset。temp-file/ring-buffer 模式的get_range根据这些 range 回答下游指定 offset 的 pull 请求。排查 seek 失败、回退读不到、timeshift 窗口过短时重点不是GstQueueArray长度而是requested offset 是否落在有效rangewriting_pos是否已下载到目标位置ring buffer 是否已覆盖目标区间seeking是否仍为 TRUE此时 chain 会丢弃到达的数据等待新的 segment。2.7 关键调试点建议重点记录queue2:6、queue_dataflow:6日志并关联 bus 消息。排查时优先确认当前是 FIFO、ring 还是 temp-filesrcresult与sinkresult是否为GST_FLOW_OKis_eos/unexpected/seeking状态waiting_add下游等数据或waiting_del上游等空间当前 range 的writing_pos/reading_pos本次是否真的发出了GST_MESSAGE_BUFFERING以及上层是否消费它。3. 三种工作模式queue2的行为由两个属性决定用哪种底层存储宏QUEUE_IS_USING_*判定#defineQUEUE_IS_USING_TEMP_FILE(queue)((queue)-temp_template!NULL)#defineQUEUE_IS_USING_RING_BUFFER(queue)((queue)-ring_buffer_max_size!0)#defineQUEUE_IS_USING_QUEUE(queue)\(!QUEUE_IS_USING_TEMP_FILE(queue)!QUEUE_IS_USING_RING_BUFFER(queue))模式触发条件存储介质Buffering mode典型用途内存 FIFO默认两个都没设内存GstQueueArrayGST_BUFFERING_STREAM与queue类似纯内存缓冲 buffering 上报Ring buffer环形ring-buffer-max-size ! 0固定大小内存环形缓冲GST_BUFFERING_TIMESHIFT限定内存上限的 timeshift可小幅回退 seekTemp file磁盘设了temp-template磁盘临时文件如/tmp/gstreamer-XXXXXXGST_BUFFERING_DOWNLOAD网络点播 download buffering整条流落盘任意 seek模式与 buffering mode 的映射在get_buffering_stats里if(!QUEUE_IS_USING_QUEUE(queue)){if(QUEUE_IS_USING_RING_BUFFER(queue))*modeGST_BUFFERING_TIMESHIFT;else*modeGST_BUFFERING_DOWNLOAD;}else{*modeGST_BUFFERING_STREAM;}3.1 Temp file 模式要点设temp-template后元件用mkstemp类逻辑分配随机文件名并通过只读属性temp-location回报给应用。落盘容量独立于 max-size-限制*整条流都能写进文件max-size-* 此时只用于 buffering 百分比统计不再限制实际缓冲量。temp-remove默认TRUE回到 READY 状态时删除临时文件。支持随机读下游 seek 时可在文件里回退/前进实现边下边播且可拖动进度条。3.2GstQueue2核心私有状态queue2 在 queue 的 FIFO、阈值和条件变量基础上增加了双向 flow 状态、buffering 统计、下载范围与本地存储资源struct_GstQueue2{GstElement element;GstPad*sinkpad;GstPad*srcpad;GstFlowReturn srcresult;GstFlowReturn sinkresult;gboolean is_eos;gboolean unexpected;GstQueueArray*queue;GstQueue2Size cur_level;GstQueue2Size max_level;// buffers/bytes/time/rate_timegboolean use_buffering;gint low_watermark,high_watermark;gboolean is_buffering;gint buffering_percent;guint64 bytes_in,bytes_out;gdouble byte_in_rate,byte_out_rate;GMutex qlock;GCond item_add,item_del,query_handled;gchar*temp_template;gchar*temp_location;FILE*temp_file;GstQueue2Range*ranges;GstQueue2Range*current;gboolean seeking;};字段作用排查时的意义srcresult/sinkresultsrc task 与 sink chain 的 flow 状态分别保存判断是上游写入失败还是下游推送失败cur_level/max_level当前/最大 buffers、bytes、time 与rate_timerate_time用码率估计时间容量也能参与“满”的判断is_buffering/buffering_percent当前缓冲状态与最近百分比只反映 queue2 判定不代表播放器一定已暂停bytes_in/out、byte_in/out_rate输入输出速率统计用于计算buffering_left、判断下载是否追得上消费temp_file/ranges/current磁盘缓存、有效下载区间、当前访问区间seek/timeshift 问题的核心状态seekingseek 后尚未收到新 segment 的窗口这期间到达的 buffer 会被跳过避免混入旧 segment 数据3.3 入队、出队与 buffering 更新调用链入队sink 流线程gst_pad_push(upstream src pad, buffer) - gst_queue2_chain() - gst_queue2_chain_buffer_or_buffer_list() - 持有 qlock检查 sinkresult / is_eos / unexpected / seeking - gst_queue2_wait_free_space() - FIFO/ring 满时等待 item_del - temp-file 非 EOS 时不因 max-size-* 等待 - gst_queue2_locked_enqueue() - 更新 cur_level 与输入速率 - 写 GstQueueArray或写 temp-file/ring buffer - signal item_add - 释放 qlock - gst_queue2_post_buffering()出队src task 线程gst_queue2_loop() - 持有 qlock - 若 empty等待 item_add - gst_queue2_push_one() - gst_queue2_locked_dequeue() - 更新 cur_level 与输出速率 - 释放 qlock - gst_queue2_post_buffering() - gst_pad_push() / gst_pad_push_list() / gst_pad_push_event() - 重新持有 qlock检查 srcresult 与下游 flow 返回值与 queue 相同出队后推下游时刻意不持qlock。此时数据已脱离共享队列解锁允许上游并行写入重新加锁后通过srcresult处理 flush/EOS/错误竞争。3.4 Pull 模式get_range与缓存范围queue2 的 src pad 同时实现get_range。下游可请求(offset, length)queue2 在 temp-file 或 ring-buffer 的ranges中查找目标区间下游 gst_pad_pull_range(offset, length) - gst_queue2_get_range() - 目标 offset 已在有效 range从文件/ring 读出 - 目标 offset 尚未下载等待 item_add直到 writing_pos 前进或 flow 结束 - 目标 offset 已被 ring 覆盖返回范围不可用/流控结果GstQueue2Range用offset、writing_pos、reading_pos、max_reading_pos追踪 source 位置ring 模式还维护rb_offset/rb_writing_pos把 source 的逻辑 offset 映射到固定大小环形内存。4. 容量与水位属性4.1 最大阈值 max-size-*判满与queue同名默认值不同维度属性默认值宏缓冲个数max-size-buffers100DEFAULT_MAX_SIZE_BUFFERS字节数max-size-bytes2 MBDEFAULT_MAX_SIZE_BYTES时间max-size-time2 秒DEFAULT_MAX_SIZE_TIME任一维度达上限即满0禁用。ring-buffer 模式下实际字节上限取MIN(max-size-bytes, ring-buffer-max-size)宏QUEUE_MAX_BYTES。4.2 Buffering 水位线这是 queue2 区别于 queue 的核心。有两套并存的表达新的 watermark 为主旧的 percent 兼容属性类型默认值含义use-bufferingboolFALSE总开关是否发BUFFERING消息low-watermarkdouble0.01低水位占满容量比例跌破则开始 bufferinghigh-watermarkdouble0.99高水位达到则结束 bufferinglow-percentint10旧式低水位百分比与 low-watermark 等价映射high-percentint99旧式高水位百分比4.3 其它相关属性属性默认值含义ring-buffer-max-size0禁用环形缓冲字节上限非 0 即启用 ring buffer 模式temp-templateNULL临时文件名模板设了即启用 temp-file 模式temp-location—只读回报实际分配的临时文件路径temp-removeTRUEREADY 时是否删临时文件use-rate-estimateTRUE是否用收/发速率估算剩余缓冲时间buffering_leftuse-tags-bitrateFALSE用 tag 里的码率信息辅助时间估算use-bitrate-queryTRUE向上游 query 码率辅助估算avg-in-rate—只读平均输入字节速率5. Buffering 百分比机制——重点5.1 两个概念别混淆源码注释特别澄清了两个易混的量buffering_level缓冲水平队列实际填充程度内部量。buffering_percent缓冲百分比相对于低/高水位线归一化的 0~100 值——buffering_percent 0%⟺ buffering_level 到达低水位buffering_percent 100%⟺ buffering_level 到达高水位。也就是说应用看到的百分比不是占总容量的比例而是在低↔高水位区间里的进度。换算percentbuffering_level*100/high_watermark;/* convert_to_buffering_percent */if(percent100)percent100;5.2 迟滞hysteresis行为low/high 两条水位线构成迟滞区间避免频繁抖动填充上升 ──▶ 达到 high-watermark → 发 100% → 应用开始/恢复播放 播放消耗 ──▶ 跌破 low-watermark → 发 100% → 应用暂停等待再缓冲播放器如 playbin监听GST_MESSAGE_BUFFERING收到 100% 通常暂停管道显示缓冲中收到 100% 恢复 PLAYING。5.3 消息发送时机与去抖update_buffering()计算最新百分比变化时置percent_changed。gst_queue2_get_buffering_message()在持锁下生成消息但有两道保护waiting_del时不改缓冲等级当 sinkpad 正因队列满而阻塞等待空间时不发新的 buffering 消息避免上游单个大 buffer 反复触发 buffering↔100%的抖动。last_posted_buffering_percent去重与上次已发百分比相同则不重发。消息还带buffering_statsmodestream/download/timeshift、平均收发速率、预计剩余缓冲毫秒buffering_left由use-rate-estimate估算。6. 线程模型、事件与 FlushPush 模式常规与queue一样sinkpadchain入队上游线程srcpadloop出队推下游自建 task 线程qlockitem_add/item_del条件变量协调满时反压、空时等待。Pull 模式queue2 独有srcpad 实现get_rangegst_queue2_get_range下游可主动拉取指定 offset/size 的数据。配合 temp-file/ring-buffer下游 seek 到任意位置时 queue2 从磁盘/环形缓冲随机读出对应数据。这是 download buffering 可拖动进度条的底层支撑。src pad 的 activate 模式push/pull由gst_queue2_src_activate_mode决定取决于上游能否提供随机访问及是否配置了缓存。6.1 事件顺序与 EOS序列化 event如STREAM_START、SEGMENT、TAG、EOS入队保证与 buffer 的先后顺序非序列化 event 直接转发。收到 EOS 后置is_eos后续数据与普通 event 被拒收新的STREAM_START或SEGMENT可清除 EOS 状态并恢复数据循环。seekingTRUE表示仍在等待 seek 后的SEGMENT该窗口内抵达的 buffer 会被跳过避免旧位置数据写入新范围。6.2 FlushFLUSH_START - 下传事件push 模式 - srcresult / sinkresult GST_FLOW_FLUSHING - signal item_add / item_del / query_handled - 暂停 src task使等待数据或空间的线程立即退出 FLUSH_STOP - 下传事件push 模式 - 清空缓存状态、range 数据与速率统计 - srcresult / sinkresult GST_FLOW_OK - 清除 EOS / unexpected / seeking - 重启 src task因此 seek 或切流异常时应同时检查两侧 flow result、src task 是否重启以及seeking是否在收到新 segment 后被清除。7. 与 queue / multiqueue 的区别元素缓冲介质Buffering 消息磁盘缓存Pull/随机读多路流典型位置queue内存 FIFO✗只有信号✗✗✗手动插入做线程解耦queue2内存 / 环形 / 磁盘✓✓✓✗网络源后做 download/网络 bufferingmultiqueue内存每流一条✓✗✗✓decodebin/playbin 内部多路流管理选择原则只要线程解耦或小缓冲→queue网络流、要缓冲进度条、要边下边播可 seek→queue2解复用后多条流各自缓冲、防饥饿→multiqueueplaybin 内部网络 source 之后常自动插入queue2做 bufferingbuffer-size/buffer-duration属性即透传到内部 queue2demux 之后用multiqueue分流。8. 常见误区澄清“buffering_percent 是缓冲占总容量的比例”—— 不是。它是相对low/high 水位区间的归一化进度到低水位0%到高水位100%。“queue2 一定用磁盘”—— 不一定。默认是纯内存 FIFO只有设了temp-template才落盘设了ring-buffer-max-size才用环形缓冲。“temp-file 模式受 max-size 限制”—— 不受。整条流都能写进文件max-size-* 此时只用于算 buffering 百分比。“use-buffering 默认开”—— 默认FALSE。不开就不发BUFFERING消息但反压/缓冲仍照常工作。“queue2 会主动暂停播放”—— 不会。它只发消息暂停/恢复是应用如 playbin收到消息后自己做的决策。9. 关键函数 / 宏索引名称作用QUEUE_IS_USING_TEMP_FILE / RING_BUFFER / QUEUE判定当前工作模式gst_queue2_chain/gst_queue2_chain_listpush 模式入队主逻辑gst_queue2_loop/gst_queue2_push_one出队并推下游gst_queue2_get_rangepull 模式随机读取update_buffering重算缓冲百分比、置percent_changedconvert_to_buffering_percentbuffering_level → 相对水位的百分比get_buffering_stats填 mode / 收发速率 / 剩余缓冲估算gst_queue2_get_buffering_message/gst_queue2_post_buffering生成并发送BUFFERING消息含去抖gst_queue2_is_filled/gst_queue2_is_empty满/空判定gst_queue2_change_state状态切换含临时文件、ring buffer 与 range 的创建/清理gst_queue2_handle_sink_event处理序列化事件、EOS 与 flush 状态重置