1use crate::*;
2
3ext! {
7 name: Prft,
8 versions: [0, 1],
9 flags: {
10 output_time = 0,
11 fragment_finalised = 1,
12 fragment_written = 2,
13 consistent_offset = 3,
14 real_time = 4,
15 }
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Default)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub enum ReferenceTime {
21 #[default]
25 Input,
26
27 Output,
31
32 Finalised,
37
38 Written,
43
44 Consistent,
47
48 RealTime,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Default)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55pub struct Prft {
56 pub reference_track_id: u32,
57 pub ntp_timestamp: u64,
58 pub media_time: u64,
59 pub utc_time_semantics: ReferenceTime,
60}
61
62impl AtomExt for Prft {
63 type Ext = PrftExt;
64
65 const KIND_EXT: FourCC = FourCC::new(b"prft");
66
67 fn decode_body_ext<B: Buf>(buf: &mut B, ext: PrftExt) -> Result<Self> {
68 let reference_track_id = u32::decode(buf)?;
69 let ntp_timestamp = u64::decode(buf)?;
70 let utc_time_semantics = if ext.real_time && ext.consistent_offset {
71 ReferenceTime::RealTime
72 } else if ext.consistent_offset {
73 ReferenceTime::Consistent
74 } else if ext.fragment_written {
75 ReferenceTime::Written
76 } else if ext.fragment_finalised {
77 ReferenceTime::Finalised
78 } else if ext.output_time {
79 ReferenceTime::Output
80 } else {
81 ReferenceTime::Input
83 };
84 if ext.version == PrftVersion::V0 {
85 Ok(Prft {
86 reference_track_id,
87 ntp_timestamp,
88 media_time: u32::decode(buf)?.into(),
89 utc_time_semantics,
90 })
91 } else {
92 Ok(Prft {
93 reference_track_id,
94 ntp_timestamp,
95 media_time: u64::decode(buf)?,
96 utc_time_semantics,
97 })
98 }
99 }
100
101 fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<PrftExt> {
102 self.reference_track_id.encode(buf)?;
103 self.ntp_timestamp.encode(buf)?;
104 let (output_time, fragment_finalised, fragment_written, consistent_offset, real_time) =
105 match self.utc_time_semantics {
106 ReferenceTime::Input => (false, false, false, false, false),
107 ReferenceTime::Output => (true, false, false, false, false),
108 ReferenceTime::Finalised => (false, true, false, false, false),
109 ReferenceTime::Written => (false, false, true, false, false),
110 ReferenceTime::Consistent => (false, false, false, true, false),
111 ReferenceTime::RealTime => (false, false, false, true, true),
112 };
113 if self.media_time <= u32::MAX.into() {
114 (self.media_time as u32).encode(buf)?;
115 Ok(PrftExt {
116 version: PrftVersion::V0,
117 output_time,
118 fragment_finalised,
119 fragment_written,
120 consistent_offset,
121 real_time,
122 })
123 } else {
124 self.media_time.encode(buf)?;
125 Ok(PrftExt {
126 version: PrftVersion::V1,
127 output_time,
128 fragment_finalised,
129 fragment_written,
130 consistent_offset,
131 real_time,
132 })
133 }
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 const ENCODED_PRFT: &[u8] = &[
143 0x00, 0x00, 0x00, 0x20, 0x70, 0x72, 0x66, 0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
144 0x01, 0xda, 0x74, 0xca, 0x46, 0x6b, 0xc6, 0xa7, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
145 0xff, 0xf8,
146 ];
147
148 const DECODED_PRFT: Prft = Prft {
150 reference_track_id: 1,
151 ntp_timestamp: 15741429001371428847,
152 media_time: 18446744073709551608,
153 utc_time_semantics: ReferenceTime::Input,
154 };
155
156 #[test]
157 fn test_prft_v1_decode() {
158 let buf: &mut std::io::Cursor<&&[u8]> = &mut std::io::Cursor::new(&ENCODED_PRFT);
159 let prft = Prft::decode(buf).expect("failed to decode prft");
160 assert_eq!(prft, DECODED_PRFT);
161 }
162
163 #[test]
164 fn test_prft_v1_encode() {
165 let mut buf = Vec::new();
166 DECODED_PRFT.encode(&mut buf).unwrap();
167
168 assert_eq!(buf.as_slice(), ENCODED_PRFT);
169 }
170
171 #[test]
172 fn test_prft_v0_round_trip() {
173 let mut buf = Vec::new();
174 let prft = Prft {
175 reference_track_id: 7,
176 ntp_timestamp: 15741429001371428847,
177 media_time: u32::MAX.into(),
178 utc_time_semantics: ReferenceTime::Written,
179 };
180 prft.encode(&mut buf).unwrap();
181 assert_eq!(
182 buf.as_slice(),
183 &[
184 0x00, 0x00, 0x00, 0x1C, 0x70, 0x72, 0x66, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
185 0x00, 0x07, 0xda, 0x74, 0xca, 0x46, 0x6b, 0xc6, 0xa7, 0xef, 0xff, 0xff, 0xff, 0xff
186 ]
187 );
188
189 let decoded = Prft::decode(&mut buf.as_ref()).unwrap();
190 assert_eq!(decoded, prft);
191 }
192
193 #[test]
194 fn test_prft_realtime_roundtrip() {
195 let mut buf = Vec::new();
196 let prft = Prft {
197 reference_track_id: 1,
198 ntp_timestamp: 16571585696146385000,
199 media_time: 41234604048,
200 utc_time_semantics: ReferenceTime::RealTime,
201 };
202 prft.encode(&mut buf).unwrap();
203 assert_eq!(
204 buf.as_slice(),
205 &[
206 0x00, 0x00, 0x00, 0x20, 0x70, 0x72, 0x66, 0x74, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00,
207 0x00, 0x01, 0xe5, 0xfa, 0x19, 0x63, 0xff, 0xbf, 0xe8, 0x68, 0x00, 0x00, 0x00, 0x09,
208 0x99, 0xc6, 0x20, 0x10
209 ]
210 );
211
212 let decoded = Prft::decode(&mut buf.as_ref()).unwrap();
213 assert_eq!(decoded, prft);
214 }
215}