1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Media Foundation runtime helpers (Windows only).
use OnceLock;
use crateEncodeError;
use ;
use ;
static MF_INIT: = new;
/// `RPC_E_CHANGED_MODE` — COM already initialized with a different apartment.
const RPC_E_CHANGED_MODE: i32 = 0x8001_0106_u32 as i32;
/// Ensure COM + MF are initialized for this process (idempotent).
pub
/// Pack `high:low` the way MF stores frame size / rate in a `UINT64`.
pub const
/// Convert a timestamp in `time_base` units to MF 100-nanosecond units.
///
/// Truncates toward zero. [`from_hns`] rounds to the nearest tick, which is what makes the pair
/// an exact inverse; that function documents why the round trip has to be one.
pub
/// The MF sample duration, in hns, for a frame declared `duration` ticks of the time base long.
///
/// `0` is "unknown" (`VideoFrame::duration` documents it, and a C caller with no duration passes
/// it), and it becomes **one tick**: the nominal frame interval, since this encoder's time base
/// is its frame rate. It used to be `to_hns(0).max(1)`, a sample one hundred *nanoseconds* long.
/// The MFT builds its output timeline from the sample durations it is given, so the H.264 MFT
/// answered 150 frames at `1/30` with presentation times `0, 2, 2, 5, 5, 8, 8, 11, …`. All 150
/// packets came out, the repeated instants were dropped by whatever read the file, and 110
/// frames survived.
///
/// A duration too large for `i64` is treated as unknown too, rather than as zero.
pub
/// Convert MF 100-nanosecond units back to `time_base` units.
///
/// # Why this rounds to the nearest tick rather than truncating
///
/// Every timestamp makes the trip `tick → hns → tick` on its way through an MFT: it goes in on
/// the input sample and comes back on the output one. Unless the two directions are an exact
/// inverse, *distinct* input ticks come back as the *same* tick — and a video track with two
/// frames claiming one instant is malformed, however well it happens to play.
///
/// Truncating both ways does exactly that, because 10 000 000 is not divisible by most timebase
/// denominators. At `1/60`, only every third tick survives:
///
/// | tick | `to_hns` | truncating back | nearest |
/// |---|---|---|---|
/// | 6 | 1 000 000 | 6 | 6 |
/// | 7 | 1 166 666 | **6** | 7 |
/// | 8 | 1 333 333 | **7** | 8 |
///
/// Measured 2026-09-18 on a real recording taken through this path: 279 video packets carried
/// only 215 distinct presentation timestamps. Decoding it, ffmpeg warned *"Application provided
/// invalid, non monotonically increasing dts to muxer"* — the duplicate presentation times,
/// surfacing as duplicate timestamps on its decoded output. The file's own decode timestamps
/// were monotonic throughout. `1/30`, `1/24` and `1001/30000` are all affected the same way,
/// and so is audio — `aac.rs` shares [`to_hns`].
///
/// # Why *nearest*, and not "away from zero"
///
/// Away-from-zero is also an exact inverse of [`to_hns`]: `to_hns` truncates toward zero, so
/// dividing back lands just below the original tick, and the far end recovers it. On every
/// value measured the two rules agree. The HEVC MFT hands back exactly the hns written to it;
/// the H.264 MFT recomputes its own, landing at or a hair *below* each whole tick. No MFT was
/// observed rounding *up*.
///
/// Nearest is chosen for the case that was not observed. An MFT that computed its sample
/// times by rounding up would land a hair above a whole tick, and away-from-zero would push
/// that value into the next tick, while nearest still returns the intended one. For every
/// value actually seen, the two rules cost the same. This is a design argument, not a
/// measurement. An earlier version of this comment claimed a measurement, which was wrong;
/// ADR-0013 § Correction records it.
///
/// Exactness on the round trip holds whenever a tick is worth at least two hns, i.e.
/// `time_base_den <= num * 5 * 10^6`. Past that the timebase is finer than MF's own resolution
/// and the precision is already gone inside [`to_hns`], before this function sees it — no
/// rounding rule here can recover it.
pub