DTS和PTS

一、先给最终结论(精确定义版)

送给 decoder 的数据:

  • 输入顺序 = 编码顺序(decode order)

  • 每个 access unit 携带的是 PTS(显示时间)

decoder 内部:

  • 根据 bitstream(slice / reference / DPB)做 frame reorder

decoder 输出:

  • 输出顺序 = 显示顺序(presentation order)

  • 输出的 timestamp = 原来就带着的那个 PTS

不存在“把 DTS 转成 PTS”的过程
PTS 从来没有在 decoder 内部被计算或修改


二、你这句话哪里“对”,哪里“需要校正”

你说的是:

送给 decoder 的时候是按编码顺序给的(DTS)

✔ 对的部分

  • 输入顺序确实是 decode order

  • 对于含 B 帧的 H.264/H.265:

    • demuxer 会按 decode order 喂给 decoder

  • 这是 decoder 能正常工作的前提


需要校正的部分

(DTS)

Android 并没有把 DTS 显式传给 decoder。

更准确的说法应该是:

送给 decoder 的是“按 DTS 排列的数据”,但携带的是 PTS

这是一个非常容易混淆、但必须区分的点


三、用一个具体例子拆开(H.264 有 B 帧)

假设编码结构

显示顺序(PTS):

I0   B1   B2   P3

解码顺序(DTS):

I0   P3   B1   B2

Demux → Decoder 输入

输入顺序AU 内容携带时间戳
1I0PTS=0
2P3PTS=3
3B1PTS=1
4B2PTS=2

注意:

  • 顺序 = decode order

  • 时间戳 = display time


Decoder 内部

  • 解码 I0、P3 后,reference frame ready

  • B1 / B2 decode 完成

  • 内部 DPB 决定哪些 frame 可以输出


Decoder 输出

输出顺序FramePTS
1I00
2B11
3B22
4P33

PTS 从头到尾没有被修改过

四、为什么你会自然地说“按 DTS 给 decoder”?

因为在概念模型里

  • decode order ≈ DTS order

  • display order ≈ PTS order

但在 Android Framework 的实现里

  • DTS 只是“排序依据”

  • PTS 才是“携带的数据”

DTS 是“隐含信息”,PTS 是“显式字段”


五、对应到 Android / Codec2 的真实字段

Codec2

C2Work {
    input.buffers[]
    input.ordinal.timestamp  // PTS (us)
}
  • 没有 DTS 字段

  • reorder 完全由 component 内部完成

OMX(老)

OMX_BUFFERHEADERTYPE {
    nTimeStamp  // PTS
}

同样:

  • 没有 DTS

  • 只有 PTS

### 正确配置视频编码器中的DTSPTS 在处理视频流时,正确设置解码时间戳(DTS)显示时间戳(PTS)对于确保媒体文件能够被准确无误地回放至关重要。H264编码标准定义了两种不同类型的时间戳:一种用于指示何时应该对特定帧进行解码操作;另一种则决定了该帧应在屏幕上呈现的具体时刻。 #### DTSPTS的关系 当不存在B帧(双向预测帧)时,由于每桢图像仅依赖于前一帧的数据来进行压缩,因此其解码顺序同展示顺序一致,此时DTS等于PTS[^3]。然而,在引入了B帧之后情况变得复杂起来——因为这些帧不仅基于前面已经看到的画面也考虑到了未来即将出现的内容来做优化,这就导致了解码次序不同于实际观看过程中各帧之间应有的先后关系。为了适应这种变化: - **I帧** (关键帧): 总是具有最早的DTS值,并且它的PTS通常紧随其后的某个位置; - **P帧** (前向预测帧): 它们的DTS位于所参考的关键帧之后但在任何后续的B帧之前; - **B帧**: 需要在两个相邻的关键帧或P帧间完成解码工作,尽管它们可能较早出现在比特流中,但直到所有必要的参照数据都可用以后才会真正展现给观众。 #### 设置方法 针对不同的开发环境技术栈,具体实现方式会有所差异。下面提供了一个通用指导原则以及一段Python伪代码示例来说明如何手动调整这两者之间的关联性。 ##### Python伪代码实例 ```python from datetime import timedelta class Frame: def __init__(self, type_, duration_ms=0): self.type_ = type_ self.duration_ms = duration_ms def set_timestamps(frames_list): current_time = 0 for i, frame in enumerate(frames_list): if frame.type_.upper() == 'I': # I frames have equal DTS and PTS. dts = pts = current_time elif frame.type_.upper() == 'P': # P frames' DTS is after the previous reference frame's PTS, # but before its own PTS which follows immediately afterwards. prev_frame_pts = get_previous_reference_frame(i).pts dts = max(prev_frame_pts + 1, current_time) pts = dts else: # Assuming it’s a B-frame here. next_ref_frames = find_next_two_reference_frames(i) earliest_possible_dts = min([f.dts for f in next_ref_frames]) latest_allowed_pts = max([f.pts for f in next_ref_frames]) - 1 dts = earliest_possible_dts pts = latest_allowed_pts yield {'index': i, 'type': frame.type_, 'dts': dts, 'pts': pts} # Update time based on this frame's duration. current_time += frame.duration_ms # Helper functions to simulate finding references. def get_previous_reference_frame(index): pass # Implement logic to retrieve last key or predicted frame. def find_next_two_reference_frames(index): pass # Implement logic to locate upcoming two refs. ``` 此段代码展示了怎样遍历一系列由不同类型的帧组成的列表,并为其分配合理的DTS/PTS组合。需要注意的是这只是一个简化版本的实际应用场景可能会更加复杂尤其是涉及到多线程处理或是硬件加速等情况下的同步机制设计等问题上。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值