oxedyne/fe2o3/fe2o3_graphics/src/mp4.rs
158 KiB, 366 runs
created by r1870400018:20054, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | //! An MP4 writer: the ISO base media file format boxes that wrap an already-encoded video track. |
| 2 | //! |
| 3 | //! This module writes a container around a stream it can neither encode nor decode. That is |
| 4 | //! unusual for a graphics crate and it is deliberate. A video codec is months of rate control, |
| 5 | //! motion estimation and entropy coding, at a quality that would be visibly worse than the encoder |
| 6 | //! already sitting in every browser and in most machines' silicon; a container is a few hundred |
| 7 | //! lines of length-prefixed boxes with no compression in it at all, and it is the part that |
| 8 | //! describes *our* frames and *our* timing. So the caller encodes -- through `VideoEncoder` in a |
| 9 | //! browser, or through whatever hardware path it has -- and hands the encoded samples and the |
| 10 | //! decoder configuration here, and gets back a file. |
| 11 | //! |
| 12 | //! # What is written |
| 13 | //! |
| 14 | //! `ftyp`, then `moov` carrying one video track's whole sample table, then `mdat` carrying the |
| 15 | //! samples. Not fragmented: the caller holds every sample's size and duration before the first byte |
| 16 | //! is written, so the sample table can be exact and the file needs no `moof` machinery, no |
| 17 | //! duration-unknown placeholder and no rewrite at the end. |
| 18 | //! |
| 19 | //! `moov` is written *before* `mdat`, which is what a progressive download wants: a reader has the |
| 20 | //! whole index in hand after the first few kilobytes and can start playing without seeking to the |
| 21 | //! end. It costs a second pass over the box tree, because the chunk offsets in `stco` are absolute |
| 22 | //! file offsets and cannot be known until the size of the index that precedes them is. |
| 23 | //! |
| 24 | //! [`Fragments`] writes the other shape, for the film a writer cannot hold: `ftyp` and a `moov` |
| 25 | //! that names the streams and states no duration, then one `moof` and `mdat` for each run of |
| 26 | //! samples handed over. Every sample's timing is stated in the fragment that carries it, so nothing |
| 27 | //! is kept back and nothing is rewritten at the end, and a film of unknown length -- several hours |
| 28 | //! of it, or several streams of it -- can be written a fragment at a time. |
| 29 | //! |
| 30 | //! # What is refused |
| 31 | //! |
| 32 | //! A track with no samples; a timescale of zero; a sample of zero duration; a sample whose bytes |
| 33 | //! are not exactly tiled by the length-prefixed NAL units the decoder configuration says they are; |
| 34 | //! a decoder configuration record that is malformed or truncated; frame dimensions that disagree |
| 35 | //! with the ones coded in the sequence parameter set; a first sample that is not a sync sample, |
| 36 | //! since a track no reader can begin decoding is not a track; and a total duration too large for |
| 37 | //! the 32-bit fields the version-0 header boxes carry. |
| 38 | //! |
| 39 | //! # References |
| 40 | //! |
| 41 | //! `ftyp`, `moov` and everything under it, and `mdat`, are ISO/IEC 14496-12 (the ISO base media |
| 42 | //! file format). The `avc1` sample entry and the `avcC` configuration box it carries are ISO/IEC |
| 43 | //! 14496-15 (the AVC file format), and the `hvc1` entry and its `hvcC` box are ISO/IEC 14496-15 |
| 44 | //! §8.3 and §8.4. The sequence parameter set whose geometry is checked against the caller's |
| 45 | //! declared dimensions is ITU-T H.264 §7.3.2.1.1 for AVC and ITU-T H.265 §7.3.2.2 for HEVC, the |
| 46 | //! latter read by [`crate::hevc`]. Each non-obvious constant below names the clause it comes from. |
| 47 | //! |
| 48 | //! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\ |
| 49 | //! Anthropic Claude |
| 50 | |
| 51 | use crate::hevc; |
| 52 | |
| 53 | use oxedyne_fe2o3_core::prelude::*; |
| 54 | |
| 55 | use std::{ |
| 56 | fs::File, |
| 57 | io::{ |
| 58 | Read, |
| 59 | Seek, |
| 60 | SeekFrom, |
| 61 | }, |
| 62 | }; |
| 63 | |
| 64 | // The most samples one track may hold, a ceiling against a length that is a mistake. A million |
| 65 | // frames is about eleven and a half hours at twenty-four a second, which is longer than anything |
| 66 | // a single non-fragmented file is the right shape for. |
| 67 | pub const MAX_SAMPLES: usize = 1_000_000; |
| 68 | |
| 69 | // The timescale of the movie header, in ticks a second. The movie has a timescale of its own, |
| 70 | // separate from each track's, and every duration in mvhd and tkhd is expressed in it while every |
| 71 | // duration in mdhd and stts is expressed in the track's. A thousand -- milliseconds -- is the |
| 72 | // conventional choice and is what makes the movie header readable when a second track arrives on |
| 73 | // a different timescale from the first. |
| 74 | pub const MOVIE_TIMESCALE: u32 = 1000; |
| 75 | |
| 76 | // The unity transformation a tkhd and an mvhd carry: nine values in the order a, b, u, c, d, v, |
| 77 | // x, y, w (ISO/IEC 14496-12 §8.2.2.3 for mvhd and §8.3.2.3 for tkhd). The six that scale and |
| 78 | // rotate are 16.16 fixed point, so one is 0x00010000; the three of the projection column are 2.30, |
| 79 | // so one is 0x40000000. Written as unity because a track that is played the way it was drawn needs |
| 80 | // no transformation, and a non-unity matrix here is how a video ends up rotated in one player and |
| 81 | // not another. |
| 82 | const UNITY: [u32; 9] = [ |
| 83 | 0x0001_0000, 0, 0, |
| 84 | 0, 0x0001_0000, 0, |
| 85 | 0, 0, 0x4000_0000, |
| 86 | ]; |
| 87 | |
| 88 | // The language of the media, packed as three five-bit letters offset from 0x60, per ISO/IEC |
| 89 | // 14496-12 §8.4.2.3: und, undetermined, which is what a video track without speech in it is. |
| 90 | // u is 21, n is 14 and d is 4, so the packed value is (21 << 10) | (14 << 5) | 4. |
| 91 | const LANG_UND: u16 = 0x55C4; |
| 92 | |
| 93 | // The horizontal and vertical resolution of a visual sample entry, in 16.16 fixed point dots an |
| 94 | // inch: 72, which ISO/IEC 14496-12 §8.5.2.3 gives as the value to write. |
| 95 | const RESOLUTION_72: u32 = 0x0048_0000; |
| 96 | |
| 97 | /// A sample of an encoded track: the bytes of one access unit, how long it is shown, and whether a |
| 98 | /// reader may begin decoding at it. |
| 99 | /// |
| 100 | /// A sample is one coded picture. Its duration is in the track's timescale rather than in seconds, |
| 101 | /// because the rates that matter divide badly -- a 24000/1001 frame rate is exact on a timescale of |
| 102 | /// 24000 and is nothing at all in milliseconds -- and because that is the unit the sample table |
| 103 | /// stores. |
| 104 | #[derive(Clone, Debug, PartialEq, Eq)] |
| 105 | pub struct Sample { |
| 106 | pub data: Vec<u8>, // the coded bytes, as a chain of length-prefixed NAL units |
| 107 | pub dur: u32, // how long it is shown, in the track's timescale |
| 108 | pub sync: bool, // may decoding begin here? an IDR for AVC, an IRAP for HEVC |
| 109 | // How far after its decoding time this sample is shown, in the track's timescale. Nought for |
| 110 | // a stream whose pictures are shown in the order they are decoded, which is what a screen |
| 111 | // recording or a poster is. A film is not such a stream. Where B-pictures are used a picture |
| 112 | // is decoded before the ones it is shown between, so the two orders differ and the container |
| 113 | // has to state both: stts gives the decoding times and this gives the difference. Writing a |
| 114 | // reordered stream with this left at nought produces a file that opens, reports the right |
| 115 | // number of frames, and plays them in the wrong order. |
| 116 | pub off: i32, |
| 117 | } |
| 118 | |
| 119 | impl Sample { |
| 120 | |
| 121 | /// A sync sample: one a reader may begin decoding at. |
| 122 | pub fn key(data: Vec<u8>, dur: u32) -> Self { |
| 123 | Self { data, dur, sync: true, off: 0 } |
| 124 | } |
| 125 | |
| 126 | /// A sample that depends on those before it. |
| 127 | pub fn delta(data: Vec<u8>, dur: u32) -> Self { |
| 128 | Self { data, dur, sync: false, off: 0 } |
| 129 | } |
| 130 | |
| 131 | /// The same sample, shown the given distance after it is decoded. |
| 132 | /// |
| 133 | /// See [`composition_offsets`], which works the offsets out from the presentation times a |
| 134 | /// container states, since that is the form a film arrives in. |
| 135 | pub fn shown_after(mut self, off: i32) -> Self { |
| 136 | self.off = off; |
| 137 | self |
| 138 | } |
| 139 | |
| 140 | pub fn len(&self) -> usize { |
| 141 | self.data.len() |
| 142 | } |
| 143 | |
| 144 | /// Does the sample carry no bytes? No legal coded picture does. |
| 145 | pub fn is_empty(&self) -> bool { |
| 146 | self.data.is_empty() |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | /// What a stream carries. |
| 151 | /// |
| 152 | /// The distinction decides which media header a track is given, which handler declares it, and the |
| 153 | /// shape of its sample entry -- three boxes that must agree, because a reader told two different |
| 154 | /// things about one track believes whichever it reads last. |
| 155 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 156 | pub enum Media { |
| 157 | Picture { // moving pictures of the given size |
| 158 | w: u16, |
| 159 | h: u16, |
| 160 | }, |
| 161 | Sound { // sound of the given channel count and rate |
| 162 | channels: u16, |
| 163 | // Samples a second. A track's timescale is usually this same number, so that a |
| 164 | // sample's duration is a count of sound samples and no rounding enters. |
| 165 | rate: u32, |
| 166 | }, |
| 167 | } |
| 168 | |
| 169 | /// One stream of a film, as its header describes it. |
| 170 | #[derive(Clone, Debug, PartialEq, Eq)] |
| 171 | pub struct Stream { |
| 172 | pub media: Media, // what it carries |
| 173 | // Ticks a second the stream's own durations are counted in. Not required to be the sampling |
| 174 | // rate of a sound stream, though it often is. A repackaging keeps the source's unit so that |
| 175 | // no time is rescaled between the two containers, and a millisecond timescale against a |
| 176 | // 44,100 Hz stream is therefore ordinary rather than wrong. |
| 177 | pub timescale: u32, |
| 178 | pub codec: Codec, // how the samples are coded, and what a decoder needs |
| 179 | // The decode time the stream's first sample lands at, in this stream's own timescale. Nearly |
| 180 | // always nought, and it exists for the case that is not: the streams of a film do not begin |
| 181 | // together. A film's first sound frame is rarely on the same instant as its first picture, |
| 182 | // and a picture track shifted so that none of its composition offsets is negative has moved |
| 183 | // relative to sound that was not shifted with it. Without this the two are nailed to a common |
| 184 | // zero and the film carries an offset between picture and sound that no caller can remove -- |
| 185 | // which is the characteristic fault of a bad repackaging and the one nobody notices until the |
| 186 | // film is being watched. |
| 187 | pub start: u64, |
| 188 | } |
| 189 | |
| 190 | /// How a track's samples are coded, and the decoder configuration that goes with them. |
| 191 | /// |
| 192 | /// An enum rather than a trait object, so that adding a codec is a variant and a match arm rather |
| 193 | /// than a second dispatch mechanism, and so that a caller can see from the type what the writer |
| 194 | /// will accept. There is deliberately no catch-all arm anywhere it is matched on: the next codec |
| 195 | /// added must be considered at every site rather than take whatever the last one does. |
| 196 | #[derive(Clone, Debug, PartialEq, Eq)] |
| 197 | pub enum Codec { |
| 198 | // H.264, carrying the AVCDecoderConfigurationRecord of ISO/IEC 14496-15 §5.3.3.1 verbatim: |
| 199 | // the bytes a VideoEncoder hands back as its output's description, or the avcC box body |
| 200 | // lifted out of another file. |
| 201 | Avc(Vec<u8>), |
| 202 | // HEVC, carrying the HEVCDecoderConfigurationRecord of ISO/IEC 14496-15 §8.3.3.1 verbatim -- |
| 203 | // the hvcC box body lifted out of another file. |
| 204 | Hevc(Vec<u8>), |
| 205 | // AAC, carrying the AudioSpecificConfig of ISO/IEC 14496-3 §1.6.2.1 verbatim: two bytes for |
| 206 | // the common profiles, naming the object type, the sampling frequency and the channel |
| 207 | // configuration. It is what a Matroska track entry's CodecPrivate holds for A_AAC, so a |
| 208 | // repackaging copies it across exactly as the picture's record is copied. |
| 209 | // |
| 210 | // The sample bytes are raw AAC frames, not ADTS: an ADTS header states again, once a frame, |
| 211 | // what this record states once for the track, and a decoder handed both refuses. |
| 212 | Aac(Vec<u8>), |
| 213 | } |
| 214 | |
| 215 | impl Codec { |
| 216 | |
| 217 | /// Is the stream a picture? That decides the boxes its track is described by. |
| 218 | pub fn is_picture(&self) -> bool { |
| 219 | match self { |
| 220 | Self::Avc(_) => true, |
| 221 | Self::Hevc(_) => true, |
| 222 | Self::Aac(_) => false, |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | /// The four-character code of the sample entry this codec is described by: ISO/IEC 14496-15 |
| 227 | /// §5.4.2.1 for AVC and §8.4.1 for HEVC. |
| 228 | /// |
| 229 | /// `hvc1` rather than `hev1`. The two differ in one promise: `hvc1` states that every parameter |
| 230 | /// set is in the sample entry and none arrives in the samples, while `hev1` allows them in |
| 231 | /// either place. A repackaging from a container that keeps the sets in a `CodecPrivate` -- which |
| 232 | /// is what Matroska does, and what this writer is handed -- produces exactly the first, so that |
| 233 | /// is what is claimed. `hev1` would be true as well but weaker, and it makes a reader look for |
| 234 | /// sets in the samples that are not there. |
| 235 | fn entry(&self) -> &'static [u8; 4] { |
| 236 | match self { |
| 237 | Self::Avc(_) => b"avc1", |
| 238 | Self::Hevc(_) => b"hvc1", |
| 239 | Self::Aac(_) => b"mp4a", |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | /// The four-character code of the configuration box carried inside that sample entry. |
| 244 | fn config(&self) -> &'static [u8; 4] { |
| 245 | match self { |
| 246 | Self::Avc(_) => b"avcC", |
| 247 | Self::Hevc(_) => b"hvcC", |
| 248 | Self::Aac(_) => b"esds", |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | /// The configuration record's bytes, written into the configuration box unchanged. |
| 253 | fn record(&self) -> &[u8] { |
| 254 | match self { |
| 255 | Self::Avc(rec) => rec, |
| 256 | Self::Hevc(rec) => rec, |
| 257 | Self::Aac(rec) => rec, |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | /// The name a visual sample entry's compressor field displays. |
| 262 | /// |
| 263 | /// Descriptive only -- nothing decodes from it -- but it is a field some viewers show, so it |
| 264 | /// says what the stream is rather than what the first codec this writer supported was. An empty |
| 265 | /// name is written as a count of nought, which is what a file with nothing to say there should |
| 266 | /// carry, and is what sound gets because sound has no visual sample entry to name. |
| 267 | fn picture_name(&self) -> &'static str { |
| 268 | match self { |
| 269 | Self::Avc(_) => "AVC Coding", |
| 270 | Self::Hevc(_) => "HEVC Coding", |
| 271 | Self::Aac(_) => "", |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | /// How many bytes prefix each NAL unit in a sample, which the configuration record states and |
| 276 | /// the sample data must obey. |
| 277 | fn nal_len(&self) -> Outcome<usize> { |
| 278 | match self { |
| 279 | Self::Avc(rec) => { |
| 280 | if rec.len() < 7 { |
| 281 | return Err(err!( |
| 282 | "An AVC decoder configuration record is at least 7 bytes and this one is \ |
| 283 | {}.", rec.len(); |
| 284 | Invalid, Input, Size)); |
| 285 | } |
| 286 | // `lengthSizeMinusOne` occupies the low two bits of byte 4; a value of 2, naming a |
| 287 | // three-byte length, is forbidden by ISO/IEC 14496-15 §5.3.3.1. |
| 288 | let n = (rec[4] & 0x03) as usize + 1; |
| 289 | if n == 3 { |
| 290 | return Err(err!( |
| 291 | "The AVC decoder configuration record names a NAL length of 3 bytes, which \ |
| 292 | ISO/IEC 14496-15 does not allow; it must be 1, 2 or 4."; |
| 293 | Invalid, Input)); |
| 294 | } |
| 295 | Ok(n) |
| 296 | }, |
| 297 | // The whole record is walked to reach a field that sits at a fixed offset in it, |
| 298 | // because [`crate::hevc::config`] is the reader this crate already holds to the |
| 299 | // specification and a second hand-rolled one would be a second thing to be wrong. It |
| 300 | // also means a malformed record is refused here rather than trusted as far as byte 21. |
| 301 | Self::Hevc(rec) => Ok(res!(hevc::config(rec)).length_size), |
| 302 | Self::Aac(_) => Err(err!( |
| 303 | "A sound sample is a coded frame and is not tiled by NAL length prefixes, so \ |
| 304 | asking for its prefix width is a question about the wrong kind of stream."; |
| 305 | Invalid, Input)), |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | /// Checks the configuration record is well formed, and gives the frame geometry it codes. |
| 310 | /// |
| 311 | /// The record is walked rather than trusted, because every field of the sample table below is |
| 312 | /// derived from it and a truncated record produces a file that is well formed and unplayable. |
| 313 | fn geometry(&self) -> Outcome<(u16, u16)> { |
| 314 | match self { |
| 315 | Self::Aac(_) => Err(err!( |
| 316 | "A sound stream codes no frame geometry, so a track built from one cannot be \ |
| 317 | checked against a declared picture size."; |
| 318 | Invalid, Input)), |
| 319 | Self::Avc(rec) => { |
| 320 | let _ = res!(self.nal_len()); |
| 321 | if rec[0] != 1 { |
| 322 | return Err(err!( |
| 323 | "The AVC decoder configuration record's version is {}, and 1 is the only \ |
| 324 | one ISO/IEC 14496-15 defines.", rec[0]; |
| 325 | Invalid, Input)); |
| 326 | } |
| 327 | // Byte 5's top three bits are reserved and set, and its low five hold the count of |
| 328 | // sequence parameter sets. |
| 329 | let n_sps = (rec[5] & 0x1F) as usize; |
| 330 | if n_sps == 0 { |
| 331 | return Err(err!( |
| 332 | "The AVC decoder configuration record carries no sequence parameter set, \ |
| 333 | so the frame geometry it should describe is absent."; |
| 334 | Invalid, Input, Missing)); |
| 335 | } |
| 336 | let mut at = 6usize; |
| 337 | let mut first: Option<&[u8]> = None; |
| 338 | for i in 0..n_sps { |
| 339 | if at + 2 > rec.len() { |
| 340 | return Err(err!( |
| 341 | "The AVC decoder configuration record ends {} bytes into a run of {} \ |
| 342 | sequence parameter sets, before the length of set {}.", |
| 343 | at, n_sps, i; |
| 344 | Invalid, Input, Size)); |
| 345 | } |
| 346 | let len = ((rec[at] as usize) << 8) | rec[at + 1] as usize; |
| 347 | at += 2; |
| 348 | if at + len > rec.len() { |
| 349 | return Err(err!( |
| 350 | "Sequence parameter set {} of the AVC decoder configuration record \ |
| 351 | claims {} bytes but only {} remain.", i, len, rec.len() - at; |
| 352 | Invalid, Input, Size)); |
| 353 | } |
| 354 | if first.is_none() { |
| 355 | first = Some(&rec[at..at + len]); |
| 356 | } |
| 357 | at += len; |
| 358 | } |
| 359 | if at >= rec.len() { |
| 360 | return Err(err!( |
| 361 | "The AVC decoder configuration record ends after its sequence parameter \ |
| 362 | sets, before the count of picture parameter sets."; |
| 363 | Invalid, Input, Size)); |
| 364 | } |
| 365 | let n_pps = rec[at] as usize; |
| 366 | at += 1; |
| 367 | if n_pps == 0 { |
| 368 | return Err(err!( |
| 369 | "The AVC decoder configuration record carries no picture parameter set."; |
| 370 | Invalid, Input, Missing)); |
| 371 | } |
| 372 | for i in 0..n_pps { |
| 373 | if at + 2 > rec.len() { |
| 374 | return Err(err!( |
| 375 | "The AVC decoder configuration record ends before the length of \ |
| 376 | picture parameter set {}.", i; |
| 377 | Invalid, Input, Size)); |
| 378 | } |
| 379 | let len = ((rec[at] as usize) << 8) | rec[at + 1] as usize; |
| 380 | at += 2; |
| 381 | if at + len > rec.len() { |
| 382 | return Err(err!( |
| 383 | "Picture parameter set {} of the AVC decoder configuration record \ |
| 384 | claims {} bytes but only {} remain.", i, len, rec.len() - at; |
| 385 | Invalid, Input, Size)); |
| 386 | } |
| 387 | at += len; |
| 388 | } |
| 389 | let sps = match first { |
| 390 | Some(s) => s, |
| 391 | None => return Err(err!( |
| 392 | "The first sequence parameter set was not taken."; Bug, Unreachable)), |
| 393 | }; |
| 394 | sps_geometry(sps) |
| 395 | }, |
| 396 | Self::Hevc(rec) => { |
| 397 | let cfg = res!(hevc::config(rec)); |
| 398 | // The first sequence parameter set the record carries. A film's record often carries |
| 399 | // several, and a slice names which of them it was coded against, but every set in |
| 400 | // one record describes the same pictures at the same size -- a change of geometry |
| 401 | // mid-film is a new sample entry, not a new set in this one. |
| 402 | // |
| 403 | // `Unit::body` is the payload with the emulation prevention **already** undone: |
| 404 | // `hevc::unit` builds it through `hevc::rbsp` and keeps the escaped form beside it |
| 405 | // as `Unit::raw`. So nothing is unescaped again here. Doing it twice would take a |
| 406 | // genuine `00 00 03` out of the syntax and give a size that is wrong and plausible. |
| 407 | let mut sps = None; |
| 408 | let mut found: Vec<String> = Vec::with_capacity(cfg.sets.len()); |
| 409 | for unit in &cfg.sets { |
| 410 | if unit.kind == hevc::nal::SPS && sps.is_none() { |
| 411 | sps = Some(res!(hevc::sps(&unit.body))); |
| 412 | } |
| 413 | found.push(unit.kind.to_string()); |
| 414 | } |
| 415 | let sps = match sps { |
| 416 | Some(s) => s, |
| 417 | None => { |
| 418 | let carries = if found.is_empty() { |
| 419 | fmt!("no parameter sets at all") |
| 420 | } else { |
| 421 | fmt!("NAL unit types {}", found.join(", ")) |
| 422 | }; |
| 423 | return Err(err!( |
| 424 | "The HEVC decoder configuration record carries no sequence parameter \ |
| 425 | set, which is NAL unit type {}, so the frame geometry it should \ |
| 426 | describe is absent. It carries {}.", hevc::nal::SPS, carries; |
| 427 | Invalid, Input, Missing)); |
| 428 | }, |
| 429 | }; |
| 430 | // `hevc::sps` refuses a picture wider or taller than 16,384, so this cannot fire |
| 431 | // today. It is written because the cast below is silent where that ceiling is not, |
| 432 | // and the two are in different crates' worth of code from each other. |
| 433 | if sps.width > u16::MAX as u32 || sps.height > u16::MAX as u32 { |
| 434 | return Err(err!( |
| 435 | "The sequence parameter set codes a frame of {} by {} pixels, beyond what a \ |
| 436 | visual sample entry can state.", sps.width, sps.height; |
| 437 | Invalid, Input, Excessive)); |
| 438 | } |
| 439 | Ok((sps.width as u16, sps.height as u16)) |
| 440 | }, |
| 441 | } |
| 442 | } |
| 443 | |
| 444 | /// Checks that a sample's bytes are exactly tiled by length-prefixed NAL units. |
| 445 | /// |
| 446 | /// The tiling is the test, and a start code at the front is only a diagnosis of why it failed. |
| 447 | /// It cannot be the test: a four-byte length between `0x00000100` and `0x000001FF` -- any NAL |
| 448 | /// between 256 and 511 bytes, which is a great many of them -- has the same first three bytes |
| 449 | /// as a three-byte start code, so a sample refused on that alone would be a perfectly good one. |
| 450 | /// A sample that genuinely does not tile is nearly always Annex B, an elementary stream |
| 451 | /// separated by start codes rather than lengths, handed straight through; that produces a file |
| 452 | /// every demuxer accepts and no decoder plays, so it is worth naming. |
| 453 | fn check_sample(&self, i: usize, data: &[u8]) -> Outcome<()> { |
| 454 | match self { |
| 455 | // A coded sound frame has no internal framing to check against: its length is |
| 456 | // the whole of what says where it ends. Refusing an empty one is done by the |
| 457 | // caller, and there is nothing else here that can be told from the bytes. |
| 458 | Self::Aac(_) => return Ok(()), |
| 459 | // Both picture codecs are checked by the same walk, because both are tiled by |
| 460 | // length prefixes and the framing is the whole of what is being tested. The NAL |
| 461 | // unit types inside differ and nothing here reads one. Written as a match rather |
| 462 | // than a test for sound, so that a codec added later has to say which it is. |
| 463 | Self::Avc(_) | Self::Hevc(_) => {}, |
| 464 | } |
| 465 | let n = res!(self.nal_len()); |
| 466 | let mut at = 0usize; |
| 467 | let mut nals = 0usize; |
| 468 | let mut why: Option<String> = None; |
| 469 | while at < data.len() { |
| 470 | if at + n > data.len() { |
| 471 | why = Some(fmt!( |
| 472 | "it ends {} bytes into a {}-byte length field, after {} whole NAL units", |
| 473 | data.len() - at, n, nals)); |
| 474 | break; |
| 475 | } |
| 476 | let mut len = 0usize; |
| 477 | for k in 0..n { |
| 478 | len = (len << 8) | data[at + k] as usize; |
| 479 | } |
| 480 | at += n; |
| 481 | if len == 0 { |
| 482 | why = Some(fmt!("NAL unit {} declares a length of zero", nals)); |
| 483 | break; |
| 484 | } |
| 485 | if at + len > data.len() { |
| 486 | why = Some(fmt!( |
| 487 | "NAL unit {} declares {} bytes and only {} remain", |
| 488 | nals, len, data.len() - at)); |
| 489 | break; |
| 490 | } |
| 491 | at += len; |
| 492 | nals += 1; |
| 493 | } |
| 494 | if why.is_none() && nals == 0 { |
| 495 | why = Some(fmt!("it carries no NAL units at all")); |
| 496 | } |
| 497 | match why { |
| 498 | None => Ok(()), |
| 499 | Some(why) => { |
| 500 | let start = data.len() >= 4 && data[0] == 0 && data[1] == 0 |
| 501 | && (data[2] == 1 || (data[2] == 0 && data[3] == 1)); |
| 502 | if start { |
| 503 | Err(err!( |
| 504 | "Sample {} is not tiled by {}-byte NAL length prefixes -- {} -- and it \ |
| 505 | begins with an Annex B start code, so it is most likely an elementary \ |
| 506 | stream handed over without conversion.", i, n, why; |
| 507 | Invalid, Input, Mismatch)) |
| 508 | } else { |
| 509 | Err(err!( |
| 510 | "Sample {} is not tiled by {}-byte NAL length prefixes: {}.", i, n, why; |
| 511 | Invalid, Input, Size)) |
| 512 | } |
| 513 | }, |
| 514 | } |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | /// A video track, written as a whole MP4: samples pushed one at a time, and the file's bytes taken |
| 519 | /// at the end. |
| 520 | /// |
| 521 | /// One track is the whole of what this writes today, so the track is the file. A second track -- |
| 522 | /// narration, which wants its own timescale and its own sample table beside this one -- is a |
| 523 | /// `Movie` taking two of these, and is not built until there is audio to put in it. |
| 524 | /// |
| 525 | /// # Why the samples are held |
| 526 | /// |
| 527 | /// The sample table states every sample's size, its duration and the file offset of the chunk it |
| 528 | /// sits in, and the offsets are absolute, so none of them is known until the size of the table |
| 529 | /// itself is. Holding the samples is what buys an exact table and a `moov` that precedes the media, |
| 530 | /// which is the layout a reader can start playing before the download finishes. |
| 531 | pub struct Track { |
| 532 | w: u16, // frame width in pixels |
| 533 | h: u16, // frame height in pixels |
| 534 | timescale: u32, // ticks a second, in which every sample duration is expressed |
| 535 | codec: Codec, // the codec and its decoder configuration |
| 536 | samples: Vec<Sample>, // decode order, which without B-pictures is display order |
| 537 | bytes: u64, // the samples' total size, kept as they arrive |
| 538 | ticks: u64, // and their total duration, in the track's timescale |
| 539 | } |
| 540 | |
| 541 | impl Track { |
| 542 | |
| 543 | /// Begins a video track of the given size and timescale, coded as the given codec says. |
| 544 | /// |
| 545 | /// The dimensions are checked against the geometry coded in the decoder configuration's |
| 546 | /// sequence parameter set, and a disagreement is refused: the two describe the same pictures, |
| 547 | /// and where they differ it is the caller's bookkeeping that is wrong, not the stream's. |
| 548 | pub fn new(w: u16, h: u16, timescale: u32, codec: Codec) -> Outcome<Self> { |
| 549 | if w == 0 || h == 0 { |
| 550 | return Err(err!( |
| 551 | "A track of {} by {} pixels has no picture in it.", w, h; |
| 552 | Invalid, Input, Range)); |
| 553 | } |
| 554 | if timescale == 0 { |
| 555 | return Err(err!( |
| 556 | "A timescale of zero ticks a second names no rate, so no sample duration written \ |
| 557 | against it would mean anything."; |
| 558 | Invalid, Input, Range)); |
| 559 | } |
| 560 | let (cw, ch) = res!(codec.geometry()); |
| 561 | if cw != w || ch != h { |
| 562 | return Err(err!( |
| 563 | "The track is declared {} by {} pixels, but the sequence parameter set in the \ |
| 564 | decoder configuration codes {} by {}.", w, h, cw, ch; |
| 565 | Invalid, Input, Mismatch)); |
| 566 | } |
| 567 | Ok(Self { |
| 568 | w, |
| 569 | h, |
| 570 | timescale, |
| 571 | codec, |
| 572 | samples: Vec::new(), |
| 573 | bytes: 0, |
| 574 | ticks: 0, |
| 575 | }) |
| 576 | } |
| 577 | |
| 578 | pub fn push(&mut self, s: Sample) -> Outcome<()> { |
| 579 | let i = self.samples.len(); |
| 580 | if i >= MAX_SAMPLES { |
| 581 | return Err(err!( |
| 582 | "A track may hold {} samples, and this is sample {}.", MAX_SAMPLES, i + 1; |
| 583 | Invalid, Input, Excessive)); |
| 584 | } |
| 585 | if s.dur == 0 { |
| 586 | return Err(err!( |
| 587 | "Sample {} is given a duration of zero ticks, so it is shown for no time at all.", |
| 588 | i; |
| 589 | Invalid, Input, Range)); |
| 590 | } |
| 591 | if s.data.is_empty() { |
| 592 | return Err(err!("Sample {} carries no bytes.", i; Invalid, Input, Missing)); |
| 593 | } |
| 594 | res!(self.codec.check_sample(i, &s.data)); |
| 595 | self.bytes += s.data.len() as u64; |
| 596 | self.ticks += s.dur as u64; |
| 597 | self.samples.push(s); |
| 598 | Ok(()) |
| 599 | } |
| 600 | |
| 601 | pub fn samples(&self) -> usize { |
| 602 | self.samples.len() |
| 603 | } |
| 604 | |
| 605 | pub fn is_empty(&self) -> bool { |
| 606 | self.samples.is_empty() |
| 607 | } |
| 608 | |
| 609 | /// The total duration so far, in the track's own timescale. |
| 610 | pub fn duration(&self) -> u64 { |
| 611 | self.ticks |
| 612 | } |
| 613 | |
| 614 | pub fn media_bytes(&self) -> u64 { |
| 615 | self.bytes |
| 616 | } |
| 617 | |
| 618 | pub fn finish(self) -> Outcome<Vec<u8>> { |
| 619 | if self.samples.is_empty() { |
| 620 | return Err(err!( |
| 621 | "A track must hold at least one sample, and none were pushed."; |
| 622 | Invalid, Input, Missing)); |
| 623 | } |
| 624 | if !self.samples[0].sync { |
| 625 | return Err(err!( |
| 626 | "Sample 0 is not a sync sample, so there is nowhere in the track a reader may \ |
| 627 | begin decoding."; |
| 628 | Invalid, Input)); |
| 629 | } |
| 630 | if self.ticks > u32::MAX as u64 { |
| 631 | return Err(err!( |
| 632 | "The track runs {} ticks at {} a second, which will not fit the 32-bit duration a \ |
| 633 | version-0 media header carries.", self.ticks, self.timescale; |
| 634 | Invalid, Input, Excessive)); |
| 635 | } |
| 636 | let movie_ticks = res!(rescale(self.ticks, self.timescale, MOVIE_TIMESCALE)); |
| 637 | if movie_ticks > u32::MAX as u64 { |
| 638 | return Err(err!( |
| 639 | "The track runs {} milliseconds, which will not fit the 32-bit duration a \ |
| 640 | version-0 movie header carries.", movie_ticks; |
| 641 | Invalid, Input, Excessive)); |
| 642 | } |
| 643 | |
| 644 | let ftyp = res!(ftyp(&self.codec)); |
| 645 | // The `mdat` header is eight bytes, unless the media will not fit a 32-bit size, in which |
| 646 | // case ISO/IEC 14496-12 §4.2 puts a 64-bit `largesize` after the type and writes 1 in the |
| 647 | // size field. |
| 648 | let mdat_hdr = if self.bytes + 8 > u32::MAX as u64 { 16u64 } else { 8u64 }; |
| 649 | |
| 650 | // Two passes. The first sizes the index with a 32-bit offset table and zeroed offsets; if |
| 651 | // the last byte of media then falls beyond what a 32-bit offset can name, the second uses |
| 652 | // the 64-bit table instead. Widening the table only pushes the media further out, so this |
| 653 | // settles after one look. |
| 654 | let probe = res!(self.moov(0, false)); |
| 655 | let head = ftyp.len() as u64 + probe.len() as u64 + mdat_hdr; |
| 656 | let wide = head + self.bytes > u32::MAX as u64; |
| 657 | let index = if wide { |
| 658 | let probe64 = res!(self.moov(0, true)); |
| 659 | let head64 = ftyp.len() as u64 + probe64.len() as u64 + mdat_hdr; |
| 660 | res!(self.moov(head64, true)) |
| 661 | } else { |
| 662 | res!(self.moov(head, false)) |
| 663 | }; |
| 664 | |
| 665 | // The rebuilt index must be the size the offsets were computed against, or every one of |
| 666 | // them is wrong by the difference. The table's entries are a fixed width, so this holds by |
| 667 | // construction; it is asserted rather than assumed because nothing downstream would catch |
| 668 | // it. |
| 669 | let probe_len = if wide { res!(self.moov(0, true)).len() } else { probe.len() }; |
| 670 | if index.len() != probe_len { |
| 671 | return Err(err!( |
| 672 | "The movie index came to {} bytes when sized and {} bytes when written, so the \ |
| 673 | chunk offsets in it are wrong by {}.", |
| 674 | probe_len, index.len(), index.len() as i64 - probe_len as i64; |
| 675 | Bug, Unreachable)); |
| 676 | } |
| 677 | |
| 678 | let total = ftyp.len() as u64 + index.len() as u64 + mdat_hdr + self.bytes; |
| 679 | let mut out = Vec::with_capacity(total as usize); |
| 680 | out.extend_from_slice(&ftyp); |
| 681 | out.extend_from_slice(&index); |
| 682 | if mdat_hdr == 16 { |
| 683 | out.extend_from_slice(&1u32.to_be_bytes()); |
| 684 | out.extend_from_slice(b"mdat"); |
| 685 | out.extend_from_slice(&(self.bytes + 16).to_be_bytes()); |
| 686 | } else { |
| 687 | out.extend_from_slice(&((self.bytes + 8) as u32).to_be_bytes()); |
| 688 | out.extend_from_slice(b"mdat"); |
| 689 | } |
| 690 | for s in &self.samples { |
| 691 | out.extend_from_slice(&s.data); |
| 692 | } |
| 693 | Ok(out) |
| 694 | } |
| 695 | |
| 696 | /// The `moov` box, with the chunk offsets placed against a media that begins at `base`. |
| 697 | fn moov(&self, base: u64, wide: bool) -> Outcome<Vec<u8>> { |
| 698 | let movie_ticks = res!(rescale(self.ticks, self.timescale, MOVIE_TIMESCALE)) as u32; |
| 699 | let mut body = Vec::new(); |
| 700 | body.extend_from_slice(&res!(self.mvhd(movie_ticks))); |
| 701 | body.extend_from_slice(&res!(self.trak(movie_ticks, base, wide))); |
| 702 | bx(b"moov", &body) |
| 703 | } |
| 704 | |
| 705 | /// The movie header, ISO/IEC 14496-12 §8.2.2, in its version-0 form with 32-bit times. |
| 706 | fn mvhd(&self, movie_ticks: u32) -> Outcome<Vec<u8>> { |
| 707 | let mut b = Vec::with_capacity(100); |
| 708 | b.extend_from_slice(&full(0, 0)); |
| 709 | b.extend_from_slice(&0u32.to_be_bytes()); // Creation time, unset. |
| 710 | b.extend_from_slice(&0u32.to_be_bytes()); // Modification time, unset. |
| 711 | b.extend_from_slice(&MOVIE_TIMESCALE.to_be_bytes()); |
| 712 | b.extend_from_slice(&movie_ticks.to_be_bytes()); |
| 713 | b.extend_from_slice(&0x0001_0000u32.to_be_bytes()); // Rate: 1.0 in 16.16. |
| 714 | b.extend_from_slice(&0x0100u16.to_be_bytes()); // Volume: 1.0 in 8.8. |
| 715 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 716 | b.extend_from_slice(&[0u8; 8]); // Reserved. |
| 717 | for v in UNITY { |
| 718 | b.extend_from_slice(&v.to_be_bytes()); |
| 719 | } |
| 720 | b.extend_from_slice(&[0u8; 24]); // Pre-defined. |
| 721 | b.extend_from_slice(&2u32.to_be_bytes()); // Next track ID: one past the only track. |
| 722 | bx(b"mvhd", &b) |
| 723 | } |
| 724 | |
| 725 | /// The track box: its header and its media. |
| 726 | fn trak(&self, movie_ticks: u32, base: u64, wide: bool) -> Outcome<Vec<u8>> { |
| 727 | let mut body = Vec::new(); |
| 728 | body.extend_from_slice(&res!(self.tkhd(movie_ticks))); |
| 729 | body.extend_from_slice(&res!(self.mdia(base, wide))); |
| 730 | bx(b"trak", &body) |
| 731 | } |
| 732 | |
| 733 | /// The track header, ISO/IEC 14496-12 §8.3.2, version 0. |
| 734 | /// |
| 735 | /// The flags are `0x000007`: enabled, in the movie, and in the preview. A track written without |
| 736 | /// `track_enabled` is present in the file and played by nothing. |
| 737 | fn tkhd(&self, movie_ticks: u32) -> Outcome<Vec<u8>> { |
| 738 | let mut b = Vec::with_capacity(84); |
| 739 | b.extend_from_slice(&full(0, 0x0000_0007)); |
| 740 | b.extend_from_slice(&0u32.to_be_bytes()); // Creation time, unset. |
| 741 | b.extend_from_slice(&0u32.to_be_bytes()); // Modification time, unset. |
| 742 | b.extend_from_slice(&1u32.to_be_bytes()); // Track ID; zero is not allowed. |
| 743 | b.extend_from_slice(&0u32.to_be_bytes()); // Reserved. |
| 744 | b.extend_from_slice(&movie_ticks.to_be_bytes()); |
| 745 | b.extend_from_slice(&[0u8; 8]); // Reserved. |
| 746 | b.extend_from_slice(&0u16.to_be_bytes()); // Layer: the front. |
| 747 | b.extend_from_slice(&0u16.to_be_bytes()); // Alternate group: no alternatives. |
| 748 | b.extend_from_slice(&0u16.to_be_bytes()); // Volume, which is zero for a visual track. |
| 749 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 750 | for v in UNITY { |
| 751 | b.extend_from_slice(&v.to_be_bytes()); |
| 752 | } |
| 753 | // The presentation size, in 16.16 fixed point. It is the size the track is drawn at, which |
| 754 | // need not be the coded size the sample entry carries -- an anamorphic stream differs in |
| 755 | // exactly this field -- but for square pixels the two agree. |
| 756 | b.extend_from_slice(&((self.w as u32) << 16).to_be_bytes()); |
| 757 | b.extend_from_slice(&((self.h as u32) << 16).to_be_bytes()); |
| 758 | bx(b"tkhd", &b) |
| 759 | } |
| 760 | |
| 761 | /// The media box: the media header, the handler, and the media information. |
| 762 | fn mdia(&self, base: u64, wide: bool) -> Outcome<Vec<u8>> { |
| 763 | let mut body = Vec::new(); |
| 764 | body.extend_from_slice(&res!(self.mdhd())); |
| 765 | body.extend_from_slice(&res!(hdlr())); |
| 766 | body.extend_from_slice(&res!(self.minf(base, wide))); |
| 767 | bx(b"mdia", &body) |
| 768 | } |
| 769 | |
| 770 | /// The media header, ISO/IEC 14496-12 §8.4.2, version 0, carrying the track's own timescale. |
| 771 | fn mdhd(&self) -> Outcome<Vec<u8>> { |
| 772 | let mut b = Vec::with_capacity(24); |
| 773 | b.extend_from_slice(&full(0, 0)); |
| 774 | b.extend_from_slice(&0u32.to_be_bytes()); // Creation time, unset. |
| 775 | b.extend_from_slice(&0u32.to_be_bytes()); // Modification time, unset. |
| 776 | b.extend_from_slice(&self.timescale.to_be_bytes()); |
| 777 | b.extend_from_slice(&(self.ticks as u32).to_be_bytes()); |
| 778 | b.extend_from_slice(&LANG_UND.to_be_bytes()); |
| 779 | b.extend_from_slice(&0u16.to_be_bytes()); // Pre-defined. |
| 780 | bx(b"mdhd", &b) |
| 781 | } |
| 782 | |
| 783 | /// The media information box: the video media header, where the media lives, and the sample |
| 784 | /// table. |
| 785 | fn minf(&self, base: u64, wide: bool) -> Outcome<Vec<u8>> { |
| 786 | let mut body = Vec::new(); |
| 787 | body.extend_from_slice(&res!(vmhd())); |
| 788 | body.extend_from_slice(&res!(dinf())); |
| 789 | body.extend_from_slice(&res!(self.stbl(base, wide))); |
| 790 | bx(b"minf", &body) |
| 791 | } |
| 792 | |
| 793 | /// The sample table: what the samples are, how long each lasts, how big it is, which chunk it |
| 794 | /// is in, where the chunks are, and which samples a reader may begin at. |
| 795 | fn stbl(&self, base: u64, wide: bool) -> Outcome<Vec<u8>> { |
| 796 | let mut body = Vec::new(); |
| 797 | body.extend_from_slice(&res!(self.stsd())); |
| 798 | body.extend_from_slice(&res!(self.stts())); |
| 799 | if let Some(ctts) = res!(self.ctts()) { |
| 800 | body.extend_from_slice(&ctts); |
| 801 | } |
| 802 | body.extend_from_slice(&res!(self.stsz())); |
| 803 | body.extend_from_slice(&res!(stsc())); |
| 804 | body.extend_from_slice(&res!(self.offsets(base, wide))); |
| 805 | if let Some(stss) = res!(self.stss()) { |
| 806 | body.extend_from_slice(&stss); |
| 807 | } |
| 808 | bx(b"stbl", &body) |
| 809 | } |
| 810 | |
| 811 | /// The sample description: one entry, describing every sample in the track. |
| 812 | fn stsd(&self) -> Outcome<Vec<u8>> { |
| 813 | let mut b = Vec::new(); |
| 814 | b.extend_from_slice(&full(0, 0)); |
| 815 | b.extend_from_slice(&1u32.to_be_bytes()); // Entry count. |
| 816 | b.extend_from_slice(&res!(self.entry())); |
| 817 | bx(b"stsd", &b) |
| 818 | } |
| 819 | |
| 820 | /// The visual sample entry, ISO/IEC 14496-12 §8.5.2, carrying the codec's configuration box. |
| 821 | fn entry(&self) -> Outcome<Vec<u8>> { |
| 822 | let mut b = Vec::with_capacity(86); |
| 823 | b.extend_from_slice(&[0u8; 6]); // Reserved. |
| 824 | b.extend_from_slice(&1u16.to_be_bytes()); // Data reference index: the first `dref` entry. |
| 825 | b.extend_from_slice(&0u16.to_be_bytes()); // Pre-defined. |
| 826 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 827 | b.extend_from_slice(&[0u8; 12]); // Pre-defined. |
| 828 | b.extend_from_slice(&self.w.to_be_bytes()); |
| 829 | b.extend_from_slice(&self.h.to_be_bytes()); |
| 830 | b.extend_from_slice(&RESOLUTION_72.to_be_bytes()); |
| 831 | b.extend_from_slice(&RESOLUTION_72.to_be_bytes()); |
| 832 | b.extend_from_slice(&0u32.to_be_bytes()); // Reserved. |
| 833 | b.extend_from_slice(&1u16.to_be_bytes()); // Frames a sample: one coded picture each. |
| 834 | // A fixed 32-byte field holding a counted string: a length byte, then that many bytes of |
| 835 | // name, then padding. Not a null-terminated string, and writing one there is a common way |
| 836 | // to put rubbish in front of a viewer that displays the field. The name comes from the |
| 837 | // codec, because a fixed one would go on saying "AVC Coding" over an HEVC track. |
| 838 | let name = self.codec.picture_name().as_bytes(); |
| 839 | let mut cname = [0u8; 32]; |
| 840 | cname[0] = name.len() as u8; |
| 841 | cname[1..1 + name.len()].copy_from_slice(name); |
| 842 | b.extend_from_slice(&cname); |
| 843 | b.extend_from_slice(&0x0018u16.to_be_bytes()); // Depth: colour with no alpha. |
| 844 | b.extend_from_slice(&0xFFFFu16.to_be_bytes()); // Pre-defined: -1. |
| 845 | b.extend_from_slice(&res!(bx(self.codec.config(), self.codec.record()))); |
| 846 | bx(self.codec.entry(), &b) |
| 847 | } |
| 848 | |
| 849 | /// The decoding time to sample table, ISO/IEC 14496-12 §8.6.1.2, run-length coded. |
| 850 | /// |
| 851 | /// Consecutive samples of equal duration share one entry, so a track at a constant frame rate |
| 852 | /// has exactly one entry however many frames it holds. |
| 853 | fn stts(&self) -> Outcome<Vec<u8>> { |
| 854 | let mut runs: Vec<(u32, u32)> = Vec::new(); |
| 855 | for s in &self.samples { |
| 856 | match runs.last_mut() { |
| 857 | Some((n, d)) if *d == s.dur => *n += 1, |
| 858 | _ => runs.push((1, s.dur)), |
| 859 | } |
| 860 | } |
| 861 | let mut b = Vec::with_capacity(8 + runs.len() * 8); |
| 862 | b.extend_from_slice(&full(0, 0)); |
| 863 | b.extend_from_slice(&(runs.len() as u32).to_be_bytes()); |
| 864 | for (n, d) in runs { |
| 865 | b.extend_from_slice(&n.to_be_bytes()); |
| 866 | b.extend_from_slice(&d.to_be_bytes()); |
| 867 | } |
| 868 | bx(b"stts", &b) |
| 869 | } |
| 870 | |
| 871 | /// The composition time to sample table, ISO/IEC 14496-12 §8.6.1.3, run-length coded. |
| 872 | /// |
| 873 | /// `None` where every sample is shown in the order it is decoded, because a track that never |
| 874 | /// reorders must not carry the box at all -- an absent `ctts` is the statement that the two |
| 875 | /// orders are the same, and writing a table of zeroes says the same thing at the cost of four |
| 876 | /// bytes a sample. |
| 877 | /// |
| 878 | /// Written at version 1, whose offsets are **signed**. Version 0's are unsigned, which forces |
| 879 | /// every decoding time to sit at or before the earliest presentation time and makes the first |
| 880 | /// pictures of a reordered stream inexpressible without shifting the whole track. |
| 881 | /// [`composition_offsets`] shifts anyway, so version 0 would serve -- but a signed table states |
| 882 | /// what is true rather than what has been arranged to be true, and a caller that works its own |
| 883 | /// offsets out is not forced into the same arrangement. |
| 884 | fn ctts(&self) -> Outcome<Option<Vec<u8>>> { |
| 885 | if self.samples.iter().all(|s| s.off == 0) { |
| 886 | return Ok(None); |
| 887 | } |
| 888 | let mut runs: Vec<(u32, i32)> = Vec::new(); |
| 889 | for s in &self.samples { |
| 890 | match runs.last_mut() { |
| 891 | Some((n, o)) if *o == s.off => *n += 1, |
| 892 | _ => runs.push((1, s.off)), |
| 893 | } |
| 894 | } |
| 895 | let mut b = Vec::with_capacity(8 + runs.len() * 8); |
| 896 | b.extend_from_slice(&full(1, 0)); |
| 897 | b.extend_from_slice(&(runs.len() as u32).to_be_bytes()); |
| 898 | for (n, o) in runs { |
| 899 | b.extend_from_slice(&n.to_be_bytes()); |
| 900 | b.extend_from_slice(&o.to_be_bytes()); |
| 901 | } |
| 902 | Ok(Some(res!(bx(b"ctts", &b)))) |
| 903 | } |
| 904 | |
| 905 | /// The sample size table, ISO/IEC 14496-12 §8.7.3.2. |
| 906 | /// |
| 907 | /// The common size field is written as zero, meaning the sizes vary and are listed one by one. |
| 908 | /// A coded picture stream where every sample is the same length would be a remarkable |
| 909 | /// coincidence, so the branch that would save the table is not taken. |
| 910 | fn stsz(&self) -> Outcome<Vec<u8>> { |
| 911 | let mut b = Vec::with_capacity(12 + self.samples.len() * 4); |
| 912 | b.extend_from_slice(&full(0, 0)); |
| 913 | b.extend_from_slice(&0u32.to_be_bytes()); // Sizes vary. |
| 914 | b.extend_from_slice(&(self.samples.len() as u32).to_be_bytes()); |
| 915 | for s in &self.samples { |
| 916 | if s.data.len() > u32::MAX as usize { |
| 917 | return Err(err!( |
| 918 | "A sample of {} bytes will not fit the 32-bit size a sample size table holds.", |
| 919 | s.data.len(); |
| 920 | Invalid, Input, Excessive)); |
| 921 | } |
| 922 | b.extend_from_slice(&(s.data.len() as u32).to_be_bytes()); |
| 923 | } |
| 924 | bx(b"stsz", &b) |
| 925 | } |
| 926 | |
| 927 | /// The chunk offset table, as either the 32-bit `stco` or the 64-bit `co64` of ISO/IEC |
| 928 | /// 14496-12 §8.7.5. |
| 929 | /// |
| 930 | /// One sample a chunk. That costs four bytes a sample over packing the whole track into one |
| 931 | /// chunk, and it buys a table a second track can be interleaved into without the first being |
| 932 | /// rewritten -- which is what adding narration will want. |
| 933 | fn offsets(&self, base: u64, wide: bool) -> Outcome<Vec<u8>> { |
| 934 | let n = self.samples.len(); |
| 935 | let mut b = Vec::with_capacity(8 + n * if wide { 8 } else { 4 }); |
| 936 | b.extend_from_slice(&full(0, 0)); |
| 937 | b.extend_from_slice(&(n as u32).to_be_bytes()); |
| 938 | let mut at = base; |
| 939 | for s in &self.samples { |
| 940 | if wide { |
| 941 | b.extend_from_slice(&at.to_be_bytes()); |
| 942 | } else { |
| 943 | if at > u32::MAX as u64 { |
| 944 | return Err(err!( |
| 945 | "A chunk begins at byte {}, beyond what the 32-bit offset table can name.", |
| 946 | at; |
| 947 | Invalid, Input, Excessive)); |
| 948 | } |
| 949 | b.extend_from_slice(&(at as u32).to_be_bytes()); |
| 950 | } |
| 951 | at += s.data.len() as u64; |
| 952 | } |
| 953 | bx(if wide { b"co64" } else { b"stco" }, &b) |
| 954 | } |
| 955 | |
| 956 | /// The sync sample table, ISO/IEC 14496-12 §8.6.2, or `None` where every sample is a sync |
| 957 | /// sample. |
| 958 | /// |
| 959 | /// Its absence is not an omission: the specification says that where there is no sync sample |
| 960 | /// box, every sample is a sync sample. Writing one that lists all of them says the same thing |
| 961 | /// at four bytes a frame. |
| 962 | fn stss(&self) -> Outcome<Option<Vec<u8>>> { |
| 963 | if self.samples.iter().all(|s| s.sync) { |
| 964 | return Ok(None); |
| 965 | } |
| 966 | let keys: Vec<u32> = self.samples.iter() |
| 967 | .enumerate() |
| 968 | .filter(|(_, s)| s.sync) |
| 969 | .map(|(i, _)| i as u32 + 1) // Sample numbers are one-based. |
| 970 | .collect(); |
| 971 | let mut b = Vec::with_capacity(8 + keys.len() * 4); |
| 972 | b.extend_from_slice(&full(0, 0)); |
| 973 | b.extend_from_slice(&(keys.len() as u32).to_be_bytes()); |
| 974 | for k in keys { |
| 975 | b.extend_from_slice(&k.to_be_bytes()); |
| 976 | } |
| 977 | Ok(Some(res!(bx(b"stss", &b)))) |
| 978 | } |
| 979 | } |
| 980 | |
| 981 | // ------------------------------------------------------------------------- a fragmented film |
| 982 | |
| 983 | // The flags a sync sample carries in a track run: sample_depends_on = 2, meaning it refers to no |
| 984 | // other picture, and sample_is_non_sync_sample = 0. ISO/IEC 14496-12 §8.8.3.1. |
| 985 | const SAMPLE_SYNC: u32 = 0x0200_0000; |
| 986 | |
| 987 | // The flags a sample that is not a sync sample carries: sample_depends_on = 1, meaning it refers |
| 988 | // to other pictures, and sample_is_non_sync_sample = 1. Both halves are stated: a reader deciding |
| 989 | // where it may begin reads one or the other, and not always the same one, so a sample that says it |
| 990 | // depends on nothing while also saying it is not a sync sample is a contradiction each reader |
| 991 | // settles its own way. |
| 992 | const SAMPLE_DELTA: u32 = 0x0101_0000; |
| 993 | |
| 994 | /// A film written as a header followed by fragments. |
| 995 | /// |
| 996 | /// The counterpart of [`Track`], for the film whose samples are not all in hand. The header states |
| 997 | /// what the streams are and states no duration at all, and each fragment after it carries its own |
| 998 | /// timing for the samples it holds, so nothing is held back and nothing is rewritten at the end: |
| 999 | /// the bytes can go out as they are produced, to a file being appended to or to a reader already |
| 1000 | /// playing the fragments before them. |
| 1001 | /// |
| 1002 | /// What that costs against [`Track`] is the index. A fragmented film has no whole-film sample |
| 1003 | /// table, so a reader seeking into one walks the fragments to find where it is going. What it buys |
| 1004 | /// is that the writer never holds the film, and that a film of unknown length can be written. |
| 1005 | pub struct Fragments { |
| 1006 | streams: Vec<Stream>, // in the order given; track ids are positions plus one |
| 1007 | // Each stream's next decode time, in that stream's own timescale. Kept here because a |
| 1008 | // fragment states the decode time of its first sample outright, and nothing in the fragments |
| 1009 | // before it says where that time has got to: a reader handed only fragment fifty must be |
| 1010 | // able to place it, which is the whole point of the field. |
| 1011 | times: Vec<u64>, |
| 1012 | seq: u32, // the sequence number the next fragment carries, from one |
| 1013 | } |
| 1014 | |
| 1015 | impl Fragments { |
| 1016 | |
| 1017 | /// Begins a film of the given streams. Track ids are 1..=n in the order given. |
| 1018 | /// |
| 1019 | /// Everything about a stream that can be checked is checked here rather than at the first |
| 1020 | /// fragment, because the header describing it has by then been handed to a reader and cannot be |
| 1021 | /// taken back. |
| 1022 | pub fn new(streams: Vec<Stream>) -> Outcome<Self> { |
| 1023 | if streams.is_empty() { |
| 1024 | return Err(err!( |
| 1025 | "A film is made of streams and none were given."; |
| 1026 | Invalid, Input, Missing)); |
| 1027 | } |
| 1028 | for (i, s) in streams.iter().enumerate() { |
| 1029 | if s.timescale == 0 { |
| 1030 | return Err(err!( |
| 1031 | "Stream {} is given a timescale of zero ticks a second, so no sample duration \ |
| 1032 | written against it would mean anything.", i; |
| 1033 | Invalid, Input, Range)); |
| 1034 | } |
| 1035 | match s.media { |
| 1036 | Media::Picture { w, h } => { |
| 1037 | if !s.codec.is_picture() { |
| 1038 | return Err(err!( |
| 1039 | "Stream {} is declared as pictures and coded by a sound codec, so the \ |
| 1040 | handler, the media header and the sample entry its track carries cannot \ |
| 1041 | all be right.", i; |
| 1042 | Invalid, Input, Mismatch)); |
| 1043 | } |
| 1044 | let (cw, ch) = match s.codec.geometry() { |
| 1045 | Ok(g) => g, |
| 1046 | Err(e) => return Err(err!(e, |
| 1047 | "Stream {}'s decoder configuration could not be read.", i; |
| 1048 | Invalid, Input)), |
| 1049 | }; |
| 1050 | if cw != w || ch != h { |
| 1051 | return Err(err!( |
| 1052 | "Stream {} is declared {} by {} pixels, but the sequence parameter set \ |
| 1053 | in its decoder configuration codes {} by {}.", i, w, h, cw, ch; |
| 1054 | Invalid, Input, Mismatch)); |
| 1055 | } |
| 1056 | }, |
| 1057 | Media::Sound { rate, .. } => { |
| 1058 | if s.codec.is_picture() { |
| 1059 | return Err(err!( |
| 1060 | "Stream {} is declared as sound and coded by a picture codec, so the \ |
| 1061 | handler, the media header and the sample entry its track carries cannot \ |
| 1062 | all be right.", i; |
| 1063 | Invalid, Input, Mismatch)); |
| 1064 | } |
| 1065 | // The sample entry states the rate in 16.16 fixed point, whose whole part is |
| 1066 | // sixteen bits. There is a version 1 entry that carries a wider one, and it is |
| 1067 | // not written here, so a rate that will not fit is refused rather than truncated |
| 1068 | // into a file that plays at the wrong speed. |
| 1069 | if rate >= 1 << 16 { |
| 1070 | return Err(err!( |
| 1071 | "Stream {} is sampled at {} Hz, and the 16.16 fixed point field a sound \ |
| 1072 | sample entry states its rate in stops one short of 65536.", i, rate; |
| 1073 | Invalid, Input, Excessive)); |
| 1074 | } |
| 1075 | }, |
| 1076 | } |
| 1077 | } |
| 1078 | let times = streams.iter().map(|s| s.start).collect(); |
| 1079 | Ok(Self { |
| 1080 | streams, |
| 1081 | times, |
| 1082 | seq: 1, |
| 1083 | }) |
| 1084 | } |
| 1085 | |
| 1086 | /// `ftyp` + `moov`: the initialisation segment, carrying no samples. |
| 1087 | /// |
| 1088 | /// Every duration in it is nought, and in a fragmented film that is a statement rather than a |
| 1089 | /// gap left to be filled: the length is not known, and a reader is told to take the timing from |
| 1090 | /// the fragments. The sample tables under `stbl` are written empty for the same reason, and they |
| 1091 | /// are written rather than left out because ISO/IEC 14496-12 §8.5.1 requires them present. |
| 1092 | pub fn head(&self) -> Outcome<Vec<u8>> { |
| 1093 | let ftyp = res!(ftyp_frag()); |
| 1094 | let moov = res!(self.moov()); |
| 1095 | let mut out = Vec::with_capacity(ftyp.len() + moov.len()); |
| 1096 | out.extend_from_slice(&ftyp); |
| 1097 | out.extend_from_slice(&moov); |
| 1098 | Ok(out) |
| 1099 | } |
| 1100 | |
| 1101 | /// One `moof` + `mdat` pair carrying the given samples. |
| 1102 | /// |
| 1103 | /// Each entry is (index into the streams given to [`Fragments::new`], that stream's samples in |
| 1104 | /// decode order). The samples are taken by value because they are moved into the media box and |
| 1105 | /// not copied: a `Vec` behind a shared reference cannot be moved out of, so borrowing here would |
| 1106 | /// clone every sample's bytes and carry the whole fragment twice. |
| 1107 | /// |
| 1108 | /// An empty `Vec` of samples for a listed stream is legal and writes a track fragment whose |
| 1109 | /// `sample_count` is nought. A stream with nothing in this fragment is ordinary -- sound and |
| 1110 | /// pictures do not divide at the same instants -- and the empty run still says the stream is |
| 1111 | /// there and where its decode time has got to. |
| 1112 | /// |
| 1113 | /// Each fragment's decode times carry on from the fragments before it, so the same samples |
| 1114 | /// handed over in two calls and in one produce the same timing. |
| 1115 | pub fn next(&mut self, runs: Vec<(usize, Vec<Sample>)>) -> Outcome<Vec<u8>> { |
| 1116 | let frag = self.seq; |
| 1117 | let mut seen = vec![false; self.streams.len()]; |
| 1118 | let mut total = 0u64; |
| 1119 | for (i, samples) in &runs { |
| 1120 | let i = *i; |
| 1121 | if i >= self.streams.len() { |
| 1122 | return Err(err!( |
| 1123 | "Fragment {} names stream {}, and the film has {}.", |
| 1124 | frag, i, self.streams.len(); |
| 1125 | Invalid, Input, Index)); |
| 1126 | } |
| 1127 | if seen[i] { |
| 1128 | return Err(err!( |
| 1129 | "Fragment {} names stream {} twice, and a stream has at most one track fragment \ |
| 1130 | in a movie fragment.", frag, i; |
| 1131 | Invalid, Input, Duplicate)); |
| 1132 | } |
| 1133 | seen[i] = true; |
| 1134 | let codec = &self.streams[i].codec; |
| 1135 | for (k, sam) in samples.iter().enumerate() { |
| 1136 | if sam.data.is_empty() { |
| 1137 | return Err(err!( |
| 1138 | "Sample {} of stream {} in fragment {} carries no bytes.", k, i, frag; |
| 1139 | Invalid, Input, Missing)); |
| 1140 | } |
| 1141 | if sam.dur == 0 { |
| 1142 | return Err(err!( |
| 1143 | "Sample {} of stream {} in fragment {} is given a duration of zero ticks, \ |
| 1144 | so it is shown for no time at all.", k, i, frag; |
| 1145 | Invalid, Input, Range)); |
| 1146 | } |
| 1147 | if sam.data.len() > u32::MAX as usize { |
| 1148 | return Err(err!( |
| 1149 | "Sample {} of stream {} in fragment {} is {} bytes, which will not fit the \ |
| 1150 | 32-bit size a track run states.", k, i, frag, sam.data.len(); |
| 1151 | Invalid, Input, Excessive)); |
| 1152 | } |
| 1153 | if let Err(e) = codec.check_sample(k, &sam.data) { |
| 1154 | return Err(err!(e, |
| 1155 | "Sample {} of stream {} in fragment {} is not coded the way the stream's \ |
| 1156 | decoder configuration says it is.", k, i, frag; |
| 1157 | Invalid, Input)); |
| 1158 | } |
| 1159 | total += sam.data.len() as u64; |
| 1160 | } |
| 1161 | } |
| 1162 | |
| 1163 | // The media box's header is eight bytes, unless its payload will not fit a 32-bit size, in |
| 1164 | // which case ISO/IEC 14496-12 §4.2 writes 1 in the size field and a 64-bit `largesize` after |
| 1165 | // the type. The width is settled here and not at the writing, because every data offset |
| 1166 | // below is measured across it. |
| 1167 | let mdat_hdr = if total + 8 > u32::MAX as u64 { 16u64 } else { 8u64 }; |
| 1168 | |
| 1169 | // Where each stream stands before this fragment adds to it. Taken now because the movie |
| 1170 | // fragment box is built twice and both passes must state the same times. |
| 1171 | let bases: Vec<u64> = runs.iter().map(|(i, _)| self.times[*i]).collect(); |
| 1172 | |
| 1173 | // Two passes. A track run's data offset is measured from the first byte of the movie |
| 1174 | // fragment box that holds it, so it cannot be known until that box's size is -- and the size |
| 1175 | // depends on the offsets only through fields of a fixed width, so sizing the box against |
| 1176 | // placeholders and writing it again with the real values settles at once. |
| 1177 | let blank = vec![0i32; runs.len()]; |
| 1178 | let probe = res!(self.moof(frag, &runs, &bases, &blank)); |
| 1179 | let mut offs = Vec::with_capacity(runs.len()); |
| 1180 | let mut at = probe.len() as u64 + mdat_hdr; |
| 1181 | for (_, samples) in &runs { |
| 1182 | if at > i32::MAX as u64 { |
| 1183 | return Err(err!( |
| 1184 | "Fragment {} puts a track run's data {} bytes past the movie fragment it is \ |
| 1185 | measured from, and that offset is a signed 32-bit field.", frag, at; |
| 1186 | Invalid, Input, Excessive)); |
| 1187 | } |
| 1188 | offs.push(at as i32); |
| 1189 | for s in samples { |
| 1190 | at += s.data.len() as u64; |
| 1191 | } |
| 1192 | } |
| 1193 | let moof = res!(self.moof(frag, &runs, &bases, &offs)); |
| 1194 | |
| 1195 | // The rebuilt box must be the size the offsets were measured against, or every one of them |
| 1196 | // is wrong by the difference. It holds by construction, and it is asserted because a file |
| 1197 | // whose offsets are all out by a few bytes is well formed, opens, and plays rubbish. |
| 1198 | if moof.len() != probe.len() { |
| 1199 | return Err(err!( |
| 1200 | "Fragment {}'s movie fragment box came to {} bytes when sized and {} bytes when \ |
| 1201 | written, so every data offset in it is out by {}.", |
| 1202 | frag, probe.len(), moof.len(), moof.len() as i64 - probe.len() as i64; |
| 1203 | Bug, Unreachable)); |
| 1204 | } |
| 1205 | // And the walk that laid the offsets out must have covered exactly the samples that are |
| 1206 | // about to be written, or a later track run points past the end of the media box. |
| 1207 | if at != moof.len() as u64 + mdat_hdr + total { |
| 1208 | return Err(err!( |
| 1209 | "Fragment {} laid its data offsets out to byte {}, and the fragment ends at {}.", |
| 1210 | frag, at, moof.len() as u64 + mdat_hdr + total; |
| 1211 | Bug, Unreachable)); |
| 1212 | } |
| 1213 | |
| 1214 | let mut out = Vec::with_capacity(moof.len() + mdat_hdr as usize + total as usize); |
| 1215 | out.extend_from_slice(&moof); |
| 1216 | if mdat_hdr == 16 { |
| 1217 | out.extend_from_slice(&1u32.to_be_bytes()); |
| 1218 | out.extend_from_slice(b"mdat"); |
| 1219 | out.extend_from_slice(&(total + 16).to_be_bytes()); |
| 1220 | } else { |
| 1221 | out.extend_from_slice(&((total + 8) as u32).to_be_bytes()); |
| 1222 | out.extend_from_slice(b"mdat"); |
| 1223 | } |
| 1224 | // Track fragment order, then sample order: all of the first stream's bytes, then all of the |
| 1225 | // second's. That is the order the offsets above were counted in. |
| 1226 | for (_, samples) in &runs { |
| 1227 | for s in samples { |
| 1228 | out.extend_from_slice(&s.data); |
| 1229 | } |
| 1230 | } |
| 1231 | |
| 1232 | // Only now is anything of the film's state moved on, so a refused fragment leaves the writer |
| 1233 | // where it was and the caller may hand over a corrected one. |
| 1234 | for (i, samples) in &runs { |
| 1235 | let mut ticks = 0u64; |
| 1236 | for s in samples { |
| 1237 | ticks += s.dur as u64; |
| 1238 | } |
| 1239 | self.times[*i] += ticks; |
| 1240 | } |
| 1241 | self.seq += 1; |
| 1242 | Ok(out) |
| 1243 | } |
| 1244 | |
| 1245 | /// The movie box: the movie header, one track for each stream, and the extends box that says |
| 1246 | /// the sample descriptions are completed by fragments rather than by the tables above them. |
| 1247 | fn moov(&self) -> Outcome<Vec<u8>> { |
| 1248 | let mut body = Vec::new(); |
| 1249 | body.extend_from_slice(&res!(self.mvhd())); |
| 1250 | for (i, s) in self.streams.iter().enumerate() { |
| 1251 | body.extend_from_slice(&res!(self.trak(i, s))); |
| 1252 | } |
| 1253 | body.extend_from_slice(&res!(self.mvex())); |
| 1254 | bx(b"moov", &body) |
| 1255 | } |
| 1256 | |
| 1257 | /// The movie header, ISO/IEC 14496-12 §8.2.2, version 0, stating no duration. |
| 1258 | /// |
| 1259 | /// A duration of nought is the fragmented film's way of saying the length is not known yet; a |
| 1260 | /// reader that wants it adds the fragments up, or reads an `mfra` at the end if one was written. |
| 1261 | fn mvhd(&self) -> Outcome<Vec<u8>> { |
| 1262 | let mut b = Vec::with_capacity(100); |
| 1263 | b.extend_from_slice(&full(0, 0)); |
| 1264 | b.extend_from_slice(&0u32.to_be_bytes()); // Creation time, unset. |
| 1265 | b.extend_from_slice(&0u32.to_be_bytes()); // Modification time, unset. |
| 1266 | b.extend_from_slice(&MOVIE_TIMESCALE.to_be_bytes()); |
| 1267 | b.extend_from_slice(&0u32.to_be_bytes()); // Duration, not yet known. |
| 1268 | b.extend_from_slice(&0x0001_0000u32.to_be_bytes()); // Rate: 1.0 in 16.16. |
| 1269 | b.extend_from_slice(&0x0100u16.to_be_bytes()); // Volume: 1.0 in 8.8. |
| 1270 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 1271 | b.extend_from_slice(&[0u8; 8]); // Reserved. |
| 1272 | for v in UNITY { |
| 1273 | b.extend_from_slice(&v.to_be_bytes()); |
| 1274 | } |
| 1275 | b.extend_from_slice(&[0u8; 24]); // Pre-defined. |
| 1276 | // One past the highest track id in use, which is what the field is defined as. ffmpeg |
| 1277 | // writes the track count itself -- 2 for two tracks, whose ids are 1 and 2 -- and that is a |
| 1278 | // violation: a tool adding a track would take an id already taken. Written correctly here. |
| 1279 | b.extend_from_slice(&(self.streams.len() as u32 + 1).to_be_bytes()); |
| 1280 | bx(b"mvhd", &b) |
| 1281 | } |
| 1282 | |
| 1283 | /// One track: its header and its media. |
| 1284 | fn trak(&self, i: usize, s: &Stream) -> Outcome<Vec<u8>> { |
| 1285 | let mut body = Vec::new(); |
| 1286 | body.extend_from_slice(&res!(self.tkhd(i, s))); |
| 1287 | body.extend_from_slice(&res!(self.mdia(i, s))); |
| 1288 | bx(b"trak", &body) |
| 1289 | } |
| 1290 | |
| 1291 | /// The track header, ISO/IEC 14496-12 §8.3.2, version 0, stating no duration. |
| 1292 | /// |
| 1293 | /// The flags are `0x000003`: enabled, and in the movie. The whole-file writer above sets |
| 1294 | /// `track_in_preview` as well; nothing here writes a preview, so the bit is left clear, and |
| 1295 | /// what matters is `track_enabled` -- a track without it is in the file and played by nothing. |
| 1296 | fn tkhd(&self, i: usize, s: &Stream) -> Outcome<Vec<u8>> { |
| 1297 | let mut b = Vec::with_capacity(84); |
| 1298 | b.extend_from_slice(&full(0, 0x0000_0003)); |
| 1299 | b.extend_from_slice(&0u32.to_be_bytes()); // Creation time, unset. |
| 1300 | b.extend_from_slice(&0u32.to_be_bytes()); // Modification time, unset. |
| 1301 | b.extend_from_slice(&(i as u32 + 1).to_be_bytes()); // Track id; zero is not allowed. |
| 1302 | b.extend_from_slice(&0u32.to_be_bytes()); // Reserved. |
| 1303 | b.extend_from_slice(&0u32.to_be_bytes()); // Duration, not yet known. |
| 1304 | b.extend_from_slice(&[0u8; 8]); // Reserved. |
| 1305 | b.extend_from_slice(&0u16.to_be_bytes()); // Layer: the front. |
| 1306 | match s.media { |
| 1307 | // A sound track is put in alternate group 1 and a picture track in none. The group says |
| 1308 | // "play one of these, not both", which is right for the sound tracks of a film in |
| 1309 | // several languages and wrong for a picture track, whose group must not name any |
| 1310 | // alternative to it. |
| 1311 | Media::Picture { .. } => b.extend_from_slice(&0u16.to_be_bytes()), |
| 1312 | Media::Sound { .. } => b.extend_from_slice(&1u16.to_be_bytes()), |
| 1313 | } |
| 1314 | match s.media { |
| 1315 | Media::Picture { .. } => b.extend_from_slice(&0u16.to_be_bytes()), |
| 1316 | Media::Sound { .. } => b.extend_from_slice(&0x0100u16.to_be_bytes()), // 1.0 in 8.8. |
| 1317 | } |
| 1318 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 1319 | for v in UNITY { |
| 1320 | b.extend_from_slice(&v.to_be_bytes()); |
| 1321 | } |
| 1322 | // The presentation size in 16.16 fixed point, and nought by nought for sound, which is |
| 1323 | // drawn nowhere. A non-zero size on a sound track makes some players lay out a blank |
| 1324 | // rectangle for it. |
| 1325 | match s.media { |
| 1326 | Media::Picture { w, h } => { |
| 1327 | b.extend_from_slice(&((w as u32) << 16).to_be_bytes()); |
| 1328 | b.extend_from_slice(&((h as u32) << 16).to_be_bytes()); |
| 1329 | }, |
| 1330 | Media::Sound { .. } => { |
| 1331 | b.extend_from_slice(&0u32.to_be_bytes()); |
| 1332 | b.extend_from_slice(&0u32.to_be_bytes()); |
| 1333 | }, |
| 1334 | } |
| 1335 | bx(b"tkhd", &b) |
| 1336 | } |
| 1337 | |
| 1338 | /// The media box: the media header, the handler that declares what the track is, and the media |
| 1339 | /// information. |
| 1340 | fn mdia(&self, i: usize, s: &Stream) -> Outcome<Vec<u8>> { |
| 1341 | let mut body = Vec::new(); |
| 1342 | body.extend_from_slice(&res!(self.mdhd(s))); |
| 1343 | body.extend_from_slice(&res!(match s.media { |
| 1344 | Media::Picture { .. } => handler(b"vide", "VideoHandler"), |
| 1345 | Media::Sound { .. } => handler(b"soun", "SoundHandler"), |
| 1346 | })); |
| 1347 | body.extend_from_slice(&res!(self.minf(i, s))); |
| 1348 | bx(b"mdia", &body) |
| 1349 | } |
| 1350 | |
| 1351 | /// The media header, ISO/IEC 14496-12 §8.4.2, version 0, carrying the stream's own timescale. |
| 1352 | /// |
| 1353 | /// The timescale is the stream's and not the movie's, and it is the unit every duration in |
| 1354 | /// every fragment of this track is counted in, so it is the one number here a fragment depends |
| 1355 | /// on being right. |
| 1356 | fn mdhd(&self, s: &Stream) -> Outcome<Vec<u8>> { |
| 1357 | let mut b = Vec::with_capacity(24); |
| 1358 | b.extend_from_slice(&full(0, 0)); |
| 1359 | b.extend_from_slice(&0u32.to_be_bytes()); // Creation time, unset. |
| 1360 | b.extend_from_slice(&0u32.to_be_bytes()); // Modification time, unset. |
| 1361 | b.extend_from_slice(&s.timescale.to_be_bytes()); |
| 1362 | b.extend_from_slice(&0u32.to_be_bytes()); // Duration, not yet known. |
| 1363 | b.extend_from_slice(&LANG_UND.to_be_bytes()); |
| 1364 | b.extend_from_slice(&0u16.to_be_bytes()); // Pre-defined. |
| 1365 | bx(b"mdhd", &b) |
| 1366 | } |
| 1367 | |
| 1368 | /// The media information box: the media header its kind requires, where the media lives, and |
| 1369 | /// the sample table. |
| 1370 | fn minf(&self, i: usize, s: &Stream) -> Outcome<Vec<u8>> { |
| 1371 | let mut body = Vec::new(); |
| 1372 | body.extend_from_slice(&res!(match s.media { |
| 1373 | Media::Picture { .. } => vmhd(), |
| 1374 | Media::Sound { .. } => smhd(), |
| 1375 | })); |
| 1376 | body.extend_from_slice(&res!(dinf())); |
| 1377 | body.extend_from_slice(&res!(self.stbl(i, s))); |
| 1378 | bx(b"minf", &body) |
| 1379 | } |
| 1380 | |
| 1381 | /// The sample table: what the samples are, and four empty tables where a whole-file writer |
| 1382 | /// would put the timing, the sizes and the offsets. |
| 1383 | /// |
| 1384 | /// No `ctts` and no `stss`. A fragmented film states composition offsets and sync flags once a |
| 1385 | /// sample in its track runs, so a table here would be a second, empty, statement of the same |
| 1386 | /// thing -- and an empty `stss` in particular reads as "no sample is a sync sample", which would |
| 1387 | /// leave a reader with nowhere to begin. |
| 1388 | fn stbl(&self, i: usize, s: &Stream) -> Outcome<Vec<u8>> { |
| 1389 | let mut body = Vec::new(); |
| 1390 | body.extend_from_slice(&res!(self.stsd(i, s))); |
| 1391 | body.extend_from_slice(&res!(empty_table(b"stts"))); |
| 1392 | body.extend_from_slice(&res!(empty_table(b"stsc"))); |
| 1393 | body.extend_from_slice(&res!(empty_stsz())); |
| 1394 | body.extend_from_slice(&res!(empty_table(b"stco"))); |
| 1395 | bx(b"stbl", &body) |
| 1396 | } |
| 1397 | |
| 1398 | /// The sample description: one entry, describing every sample the fragments will carry for this |
| 1399 | /// stream. |
| 1400 | fn stsd(&self, i: usize, s: &Stream) -> Outcome<Vec<u8>> { |
| 1401 | let entry = match s.media { |
| 1402 | Media::Picture { w, h } => res!(picture_entry(&s.codec, w, h)), |
| 1403 | Media::Sound { channels, rate } => res!(sound_entry( |
| 1404 | &s.codec, i as u32 + 1, channels, rate)), |
| 1405 | }; |
| 1406 | let mut b = Vec::with_capacity(8 + entry.len()); |
| 1407 | b.extend_from_slice(&full(0, 0)); |
| 1408 | b.extend_from_slice(&1u32.to_be_bytes()); // Entry count. |
| 1409 | b.extend_from_slice(&entry); |
| 1410 | bx(b"stsd", &b) |
| 1411 | } |
| 1412 | |
| 1413 | /// The movie extends box: one `trex` a track, and no `mehd`. |
| 1414 | /// |
| 1415 | /// Its presence is what tells a reader that the empty sample tables above are not an empty film |
| 1416 | /// but a film continued in fragments. `mehd` would state the whole duration, which is the one |
| 1417 | /// thing a writer that has not seen the end cannot say. |
| 1418 | fn mvex(&self) -> Outcome<Vec<u8>> { |
| 1419 | let mut body = Vec::new(); |
| 1420 | for i in 0..self.streams.len() { |
| 1421 | body.extend_from_slice(&res!(trex(i as u32 + 1))); |
| 1422 | } |
| 1423 | bx(b"mvex", &body) |
| 1424 | } |
| 1425 | |
| 1426 | /// The movie fragment box: its header, then one track fragment for each entry of `runs`, in the |
| 1427 | /// order given. |
| 1428 | /// |
| 1429 | /// `bases` and `offs` are read positionally against `runs`: the decode time each track fragment |
| 1430 | /// begins at, and the offset of its samples from the first byte of this box. |
| 1431 | fn moof( |
| 1432 | &self, |
| 1433 | seq: u32, |
| 1434 | runs: &[(usize, Vec<Sample>)], |
| 1435 | bases: &[u64], |
| 1436 | offs: &[i32], |
| 1437 | ) |
| 1438 | -> Outcome<Vec<u8>> |
| 1439 | { |
| 1440 | let mut body = Vec::new(); |
| 1441 | body.extend_from_slice(&res!(mfhd(seq))); |
| 1442 | for (n, (i, samples)) in runs.iter().enumerate() { |
| 1443 | let base = match bases.get(n) { |
| 1444 | Some(t) => *t, |
| 1445 | None => return Err(err!( |
| 1446 | "Fragment {} has {} track runs and {} decode times to place them at.", |
| 1447 | seq, runs.len(), bases.len(); |
| 1448 | Bug, Unreachable)), |
| 1449 | }; |
| 1450 | let off = match offs.get(n) { |
| 1451 | Some(o) => *o, |
| 1452 | None => return Err(err!( |
| 1453 | "Fragment {} has {} track runs and {} data offsets for them.", |
| 1454 | seq, runs.len(), offs.len(); |
| 1455 | Bug, Unreachable)), |
| 1456 | }; |
| 1457 | body.extend_from_slice(&res!(traf(*i as u32 + 1, base, off, samples))); |
| 1458 | } |
| 1459 | bx(b"moof", &body) |
| 1460 | } |
| 1461 | } |
| 1462 | |
| 1463 | /// The file type box of a fragmented film. |
| 1464 | /// |
| 1465 | /// Written separately from [`ftyp`] rather than by a flag on it, because the two make different |
| 1466 | /// promises and the whole-file writer's must not move: `avc1` in that list promises a track whose |
| 1467 | /// sample table is in `moov`, which is exactly what this file does not have. `iso5` with a minor |
| 1468 | /// version of 512, and `iso5`, `iso6` and `mp41` behind it, is what ffmpeg writes for a fragmented |
| 1469 | /// file and so is the list the readers of one have been tried against. |
| 1470 | /// |
| 1471 | /// **No `hvc1` for an HEVC film**, for three reasons. The brand is a conformance claim about the |
| 1472 | /// whole file (ISO/IEC 14496-15 §8.4.1) and a [`Fragments`] may carry several streams, only one of |
| 1473 | /// which is the picture; nothing here is handed the codec anyway, which is the same reason `avc1` |
| 1474 | /// is absent; and ffmpeg writes these same three brands and no fourth for a fragmented HEVC file, |
| 1475 | /// so adding one would be a list no reader of such a file has been tried against. A reader finds |
| 1476 | /// the codec in the sample entry, which is where the answer belongs. |
| 1477 | fn ftyp_frag() -> Outcome<Vec<u8>> { |
| 1478 | let mut b = Vec::with_capacity(20); |
| 1479 | b.extend_from_slice(b"iso5"); |
| 1480 | b.extend_from_slice(&512u32.to_be_bytes()); |
| 1481 | b.extend_from_slice(b"iso5"); |
| 1482 | b.extend_from_slice(b"iso6"); |
| 1483 | b.extend_from_slice(b"mp41"); |
| 1484 | bx(b"ftyp", &b) |
| 1485 | } |
| 1486 | |
| 1487 | /// A sample table box with no entries: a full box and a count of nought. |
| 1488 | /// |
| 1489 | /// `stts`, `stsc` and `stco` all take that shape, and a fragmented film writes all three empty. |
| 1490 | /// They are written rather than left out because ISO/IEC 14496-12 §8.5.1 requires them in every |
| 1491 | /// sample table, and readers that check do refuse a table missing one. |
| 1492 | fn empty_table(kind: &[u8; 4]) -> Outcome<Vec<u8>> { |
| 1493 | let mut b = Vec::with_capacity(8); |
| 1494 | b.extend_from_slice(&full(0, 0)); |
| 1495 | b.extend_from_slice(&0u32.to_be_bytes()); // Entry count. |
| 1496 | bx(kind, &b) |
| 1497 | } |
| 1498 | |
| 1499 | /// The sample size table with nothing in it. |
| 1500 | /// |
| 1501 | /// Not the same shape as the other three: it carries a common size before its count, and both are |
| 1502 | /// nought here -- no common size, and no samples to give one to. |
| 1503 | fn empty_stsz() -> Outcome<Vec<u8>> { |
| 1504 | let mut b = Vec::with_capacity(12); |
| 1505 | b.extend_from_slice(&full(0, 0)); |
| 1506 | b.extend_from_slice(&0u32.to_be_bytes()); // Sizes vary, so each is listed. |
| 1507 | b.extend_from_slice(&0u32.to_be_bytes()); // Sample count. |
| 1508 | bx(b"stsz", &b) |
| 1509 | } |
| 1510 | |
| 1511 | /// The visual sample entry of a fragmented film, ISO/IEC 14496-12 §8.5.2, carrying the codec's |
| 1512 | /// configuration box. |
| 1513 | /// |
| 1514 | /// The same 78-byte lead-in as the whole-file writer's, with one difference: the compressor name is |
| 1515 | /// left as 32 zero bytes. The field is a counted string, so a leading nought is a name of no |
| 1516 | /// characters, which is what a file that has nothing to say there should write -- and it is what |
| 1517 | /// ffmpeg writes for a fragmented file. |
| 1518 | fn picture_entry(codec: &Codec, w: u16, h: u16) -> Outcome<Vec<u8>> { |
| 1519 | if !codec.is_picture() { |
| 1520 | return Err(err!( |
| 1521 | "A visual sample entry was asked for around a sound codec."; Bug, Invalid)); |
| 1522 | } |
| 1523 | let mut b = Vec::with_capacity(86); |
| 1524 | b.extend_from_slice(&[0u8; 6]); // Reserved. |
| 1525 | b.extend_from_slice(&1u16.to_be_bytes()); // Data reference index: the first `dref` entry. |
| 1526 | b.extend_from_slice(&0u16.to_be_bytes()); // Pre-defined. |
| 1527 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 1528 | b.extend_from_slice(&[0u8; 12]); // Pre-defined. |
| 1529 | b.extend_from_slice(&w.to_be_bytes()); |
| 1530 | b.extend_from_slice(&h.to_be_bytes()); |
| 1531 | b.extend_from_slice(&RESOLUTION_72.to_be_bytes()); |
| 1532 | b.extend_from_slice(&RESOLUTION_72.to_be_bytes()); |
| 1533 | b.extend_from_slice(&0u32.to_be_bytes()); // Reserved. |
| 1534 | b.extend_from_slice(&1u16.to_be_bytes()); // Frames a sample: one coded picture each. |
| 1535 | b.extend_from_slice(&[0u8; 32]); // Compressor name: none given. |
| 1536 | b.extend_from_slice(&0x0018u16.to_be_bytes()); // Depth: colour with no alpha. |
| 1537 | b.extend_from_slice(&0xFFFFu16.to_be_bytes()); // Pre-defined: -1. |
| 1538 | b.extend_from_slice(&res!(bx(codec.config(), codec.record()))); |
| 1539 | bx(codec.entry(), &b) |
| 1540 | } |
| 1541 | |
| 1542 | /// The sound sample entry, ISO/IEC 14496-12 §8.5.2, carrying the elementary stream descriptor. |
| 1543 | /// |
| 1544 | /// The channel count and the rate are stated here as well as inside the `AudioSpecificConfig` the |
| 1545 | /// descriptor carries, and the two must agree: a reader shows what this says and a decoder produces |
| 1546 | /// what the configuration says, so a disagreement is a file that reports stereo and plays mono. |
| 1547 | /// They are not checked against each other here because parsing the configuration is a decoder's |
| 1548 | /// job, and the caller that copied both out of one source container has them consistent already. |
| 1549 | fn sound_entry(codec: &Codec, track_id: u32, channels: u16, rate: u32) -> Outcome<Vec<u8>> { |
| 1550 | if codec.is_picture() { |
| 1551 | return Err(err!( |
| 1552 | "A sound sample entry was asked for around a picture codec."; Bug, Invalid)); |
| 1553 | } |
| 1554 | if rate >= 1 << 16 { |
| 1555 | return Err(err!( |
| 1556 | "Track {} is sampled at {} Hz, which will not fit the whole part of the 16.16 fixed \ |
| 1557 | point field a sound sample entry states its rate in.", track_id, rate; |
| 1558 | Invalid, Input, Excessive)); |
| 1559 | } |
| 1560 | let mut b = Vec::with_capacity(64); |
| 1561 | b.extend_from_slice(&[0u8; 6]); // Reserved. |
| 1562 | b.extend_from_slice(&1u16.to_be_bytes()); // Data reference index: the first `dref` entry. |
| 1563 | b.extend_from_slice(&[0u8; 8]); // Reserved. |
| 1564 | b.extend_from_slice(&channels.to_be_bytes()); |
| 1565 | b.extend_from_slice(&16u16.to_be_bytes()); // Sample size in bits, which this field fixes at 16. |
| 1566 | b.extend_from_slice(&0u16.to_be_bytes()); // Pre-defined. |
| 1567 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 1568 | b.extend_from_slice(&(rate << 16).to_be_bytes()); |
| 1569 | b.extend_from_slice(&res!(esds(track_id, codec.record()))); |
| 1570 | bx(codec.entry(), &b) |
| 1571 | } |
| 1572 | |
| 1573 | /// A descriptor's length, in the four-byte form ISO/IEC 14496-1 §8.3.3 allows. |
| 1574 | /// |
| 1575 | /// A length is a run of bytes carrying seven bits each, the top bit saying another follows, so |
| 1576 | /// anything under 128 could be written in one byte. Four are written whatever the length, padded |
| 1577 | /// with `0x80` bytes that contribute nothing: that is what ffmpeg writes, and a reader that assumed |
| 1578 | /// the fixed width and stepped four bytes on regardless has been met often enough that the padded |
| 1579 | /// form is the safe one to write. |
| 1580 | fn desc_len(n: usize) -> Outcome<[u8; 4]> { |
| 1581 | if n >= 1 << 28 { |
| 1582 | return Err(err!( |
| 1583 | "A descriptor of {} bytes will not fit the four seven-bit length bytes a descriptor \ |
| 1584 | header carries.", n; |
| 1585 | Invalid, Input, Excessive)); |
| 1586 | } |
| 1587 | Ok([ |
| 1588 | 0x80 | ((n >> 21) & 0x7F) as u8, |
| 1589 | 0x80 | ((n >> 14) & 0x7F) as u8, |
| 1590 | 0x80 | ((n >> 7) & 0x7F) as u8, |
| 1591 | (n & 0x7F) as u8, |
| 1592 | ]) |
| 1593 | } |
| 1594 | |
| 1595 | /// The elementary stream descriptor box of a sound sample entry, ISO/IEC 14496-1 §7.2.6. |
| 1596 | /// |
| 1597 | /// Its contents are a tree of descriptors rather than boxes, so nothing inside is length-prefixed |
| 1598 | /// the way the rest of the file is, and each length has to be added up from the ones below it. They |
| 1599 | /// are computed here rather than written down: 34 and 20 are right for the two-byte configuration |
| 1600 | /// of common AAC-LC and wrong for every longer one, and a wrong length there produces a file whose |
| 1601 | /// box tree is perfectly well formed and whose audio decoder will not start. |
| 1602 | fn esds(track_id: u32, config: &[u8]) -> Outcome<Vec<u8>> { |
| 1603 | if config.is_empty() { |
| 1604 | return Err(err!( |
| 1605 | "Track {} carries no AudioSpecificConfig, and a decoder cannot be started without one.", |
| 1606 | track_id; |
| 1607 | Invalid, Input, Missing)); |
| 1608 | } |
| 1609 | // Each descriptor is one tag byte, four length bytes, and its payload, so a descriptor adds |
| 1610 | // five bytes to whatever it holds. |
| 1611 | let dsi = config.len(); |
| 1612 | let dcd = 13 + 5 + dsi; |
| 1613 | let es = 3 + 5 + dcd + 5 + 1; |
| 1614 | let mut b = Vec::with_capacity(4 + 5 + es); |
| 1615 | b.extend_from_slice(&full(0, 0)); |
| 1616 | |
| 1617 | b.push(0x03); // ES_Descriptor. |
| 1618 | b.extend_from_slice(&res!(desc_len(es))); |
| 1619 | // The elementary stream's id, which is the track's: two numbering schemes for one thing, and |
| 1620 | // they are kept equal so that nothing has to map between them. |
| 1621 | b.extend_from_slice(&(track_id as u16).to_be_bytes()); |
| 1622 | b.push(0x00); // No stream dependence, no URL, no OCR stream, priority 0. |
| 1623 | |
| 1624 | b.push(0x04); // DecoderConfigDescriptor. |
| 1625 | b.extend_from_slice(&res!(desc_len(dcd))); |
| 1626 | b.push(0x40); // Object type: MPEG-4 audio. |
| 1627 | // Stream type 5, AudioStream, in the top six bits; upstream 0; and the reserved bit, which the |
| 1628 | // specification requires set. A zero there is what a hand-written `0x14` gives, and some |
| 1629 | // decoders take the whole descriptor as malformed. |
| 1630 | b.push(0x15); |
| 1631 | b.extend_from_slice(&[0u8; 3]); // Decoding buffer size, unstated. |
| 1632 | b.extend_from_slice(&0u32.to_be_bytes()); // Maximum bitrate, unstated. |
| 1633 | b.extend_from_slice(&0u32.to_be_bytes()); // Average bitrate, unstated. |
| 1634 | |
| 1635 | b.push(0x05); // DecoderSpecificInfo. |
| 1636 | b.extend_from_slice(&res!(desc_len(dsi))); |
| 1637 | b.extend_from_slice(config); |
| 1638 | |
| 1639 | b.push(0x06); // SLConfigDescriptor. |
| 1640 | b.extend_from_slice(&res!(desc_len(1))); |
| 1641 | b.push(0x02); // Predefined: the MP4 file setting. |
| 1642 | |
| 1643 | bx(b"esds", &b) |
| 1644 | } |
| 1645 | |
| 1646 | /// A track extends box, ISO/IEC 14496-12 §8.8.3: the defaults a track fragment falls back on. |
| 1647 | /// |
| 1648 | /// Every default but the sample description index is nought, and nothing falls back on them, |
| 1649 | /// because each track run below states every value for every sample. Defaults are how a fragmented |
| 1650 | /// file is usually made smaller; they are also how a fragment ends up timed by a value set in a |
| 1651 | /// header written hours earlier, and the saving is four bytes a sample. |
| 1652 | fn trex(track_id: u32) -> Outcome<Vec<u8>> { |
| 1653 | let mut b = Vec::with_capacity(24); |
| 1654 | b.extend_from_slice(&full(0, 0)); |
| 1655 | b.extend_from_slice(&track_id.to_be_bytes()); |
| 1656 | b.extend_from_slice(&1u32.to_be_bytes()); // Sample description index; one-based. |
| 1657 | b.extend_from_slice(&0u32.to_be_bytes()); // Default sample duration. |
| 1658 | b.extend_from_slice(&0u32.to_be_bytes()); // Default sample size. |
| 1659 | b.extend_from_slice(&0u32.to_be_bytes()); // Default sample flags. |
| 1660 | bx(b"trex", &b) |
| 1661 | } |
| 1662 | |
| 1663 | /// The movie fragment header, ISO/IEC 14496-12 §8.8.5, carrying the fragment's sequence number. |
| 1664 | /// |
| 1665 | /// The numbers count from one and rise by one, which is what lets a reader that has been handed |
| 1666 | /// fragments out of order, or has missed one, say so. |
| 1667 | fn mfhd(seq: u32) -> Outcome<Vec<u8>> { |
| 1668 | let mut b = Vec::with_capacity(8); |
| 1669 | b.extend_from_slice(&full(0, 0)); |
| 1670 | b.extend_from_slice(&seq.to_be_bytes()); |
| 1671 | bx(b"mfhd", &b) |
| 1672 | } |
| 1673 | |
| 1674 | /// One track's part of a fragment: which track it is, where its decode time has got to, and the |
| 1675 | /// run of samples itself. |
| 1676 | fn traf(track_id: u32, base: u64, off: i32, samples: &[Sample]) -> Outcome<Vec<u8>> { |
| 1677 | let mut body = Vec::new(); |
| 1678 | body.extend_from_slice(&res!(tfhd(track_id))); |
| 1679 | body.extend_from_slice(&res!(tfdt(base))); |
| 1680 | body.extend_from_slice(&res!(trun(off, samples))); |
| 1681 | bx(b"traf", &body) |
| 1682 | } |
| 1683 | |
| 1684 | /// The track fragment header, ISO/IEC 14496-12 §8.8.7. |
| 1685 | /// |
| 1686 | /// The flags are `0x020000`, `default-base-is-moof`, and nothing else. That fixes the origin every |
| 1687 | /// data offset below is measured from at the first byte of the enclosing `moof`, which is a |
| 1688 | /// position the fragment knows about itself -- the alternative bases are file offsets, and a |
| 1689 | /// fragment that has been cut out and served on its own no longer knows where in a file it was. |
| 1690 | /// |
| 1691 | /// Deliberately simpler than ffmpeg, which also sets the default duration, size and flags. Every |
| 1692 | /// per-sample value is written in the track run instead: four bytes a sample against a class of |
| 1693 | /// fault where a sample takes a default nobody meant it to have. |
| 1694 | fn tfhd(track_id: u32) -> Outcome<Vec<u8>> { |
| 1695 | let mut b = Vec::with_capacity(8); |
| 1696 | b.extend_from_slice(&full(0, 0x0002_0000)); |
| 1697 | b.extend_from_slice(&track_id.to_be_bytes()); |
| 1698 | bx(b"tfhd", &b) |
| 1699 | } |
| 1700 | |
| 1701 | /// The track fragment decode time, ISO/IEC 14496-12 §8.8.12, version 1. |
| 1702 | /// |
| 1703 | /// The absolute decode time of the fragment's first sample, in the track's own timescale. Version 1 |
| 1704 | /// carries it in 64 bits: a 32-bit field overflows after thirteen hours at 90 kHz, which is a |
| 1705 | /// running time a recording reaches, and the box exists precisely so that a reader handed one |
| 1706 | /// fragment can place it without the fragments before it. |
| 1707 | fn tfdt(base: u64) -> Outcome<Vec<u8>> { |
| 1708 | let mut b = Vec::with_capacity(12); |
| 1709 | b.extend_from_slice(&full(1, 0)); |
| 1710 | b.extend_from_slice(&base.to_be_bytes()); |
| 1711 | bx(b"tfdt", &b) |
| 1712 | } |
| 1713 | |
| 1714 | /// The track run, ISO/IEC 14496-12 §8.8.8: the samples of this fragment, one row each. |
| 1715 | /// |
| 1716 | /// Version 1, so that the composition offsets are **signed**. That is the point of the version: a |
| 1717 | /// version-0 run states them unsigned, so a picture shown before the one decoded before it cannot |
| 1718 | /// be expressed without shifting the whole track, and [`Sample::off`] is an `i32` exactly because |
| 1719 | /// the shift is the caller's business and not the container's. |
| 1720 | /// |
| 1721 | /// The flags are `0x000F01`: the data offset, then a duration, a size, flags and a composition |
| 1722 | /// offset for every sample. Nothing is defaulted, so nothing depends on a header written earlier. |
| 1723 | fn trun(off: i32, samples: &[Sample]) -> Outcome<Vec<u8>> { |
| 1724 | let mut b = Vec::with_capacity(16 + samples.len() * 16); |
| 1725 | b.extend_from_slice(&full(1, 0x0000_0F01)); |
| 1726 | b.extend_from_slice(&(samples.len() as u32).to_be_bytes()); |
| 1727 | b.extend_from_slice(&off.to_be_bytes()); |
| 1728 | for s in samples { |
| 1729 | if s.data.len() > u32::MAX as usize { |
| 1730 | return Err(err!( |
| 1731 | "A sample of {} bytes will not fit the 32-bit size a track run states.", |
| 1732 | s.data.len(); |
| 1733 | Invalid, Input, Excessive)); |
| 1734 | } |
| 1735 | b.extend_from_slice(&s.dur.to_be_bytes()); |
| 1736 | b.extend_from_slice(&(s.data.len() as u32).to_be_bytes()); |
| 1737 | b.extend_from_slice(&(if s.sync { SAMPLE_SYNC } else { SAMPLE_DELTA }).to_be_bytes()); |
| 1738 | b.extend_from_slice(&s.off.to_be_bytes()); |
| 1739 | } |
| 1740 | bx(b"trun", &b) |
| 1741 | } |
| 1742 | |
| 1743 | /// The file type box, ISO/IEC 14496-12 §4.3. |
| 1744 | /// |
| 1745 | /// `isom` as the major brand with a minor version of 512 is what the reference muxers write, and |
| 1746 | /// the compatible brands list `isom`, `iso2`, `avc1` and `mp41`: a reader that knows only the AVC |
| 1747 | /// file format, and one that knows only version 1 of MP4, can both see a brand they recognise. |
| 1748 | /// The third brand names the coding, and naming the wrong one is a false claim. |
| 1749 | /// |
| 1750 | /// A brand in this list is a statement that the file conforms to that specification, so `avc1` on a |
| 1751 | /// film coded in HEVC says something untrue about it -- harmlessly to most readers, and the sort of |
| 1752 | /// untruth a strict one is entitled to refuse. ffmpeg agrees it matters: for a whole-file HEVC film |
| 1753 | /// it writes `isom iso2 mp41` and drops `avc1`, which it does include for H.264. |
| 1754 | fn ftyp(codec: &Codec) -> Outcome<Vec<u8>> { |
| 1755 | let mut b = Vec::with_capacity(24); |
| 1756 | b.extend_from_slice(b"isom"); |
| 1757 | b.extend_from_slice(&512u32.to_be_bytes()); |
| 1758 | b.extend_from_slice(b"isom"); |
| 1759 | b.extend_from_slice(b"iso2"); |
| 1760 | match codec { |
| 1761 | Codec::Avc(_) => b.extend_from_slice(b"avc1"), |
| 1762 | Codec::Hevc(_) => b.extend_from_slice(b"hvc1"), |
| 1763 | // Sound alone claims no picture coding, and the brand count changes with |
| 1764 | // it rather than a placeholder being written. |
| 1765 | Codec::Aac(_) => {}, |
| 1766 | } |
| 1767 | b.extend_from_slice(b"mp41"); |
| 1768 | bx(b"ftyp", &b) |
| 1769 | } |
| 1770 | |
| 1771 | /// The handler reference, ISO/IEC 14496-12 §8.4.3, declaring a visual track. |
| 1772 | /// |
| 1773 | /// The trailing name is a null-terminated UTF-8 string. A counted string is written there by some |
| 1774 | /// tools, following an older convention, and a reader that takes the specification at its word then |
| 1775 | /// shows the count byte as the first character of the name. |
| 1776 | fn hdlr() -> Outcome<Vec<u8>> { |
| 1777 | handler(b"vide", "VideoHandler") |
| 1778 | } |
| 1779 | |
| 1780 | /// The handler reference for a track of the given kind. |
| 1781 | /// |
| 1782 | /// `vide` for a picture and `soun` for sound, which is the field a reader uses to decide which |
| 1783 | /// media header to expect and how to present the track at all. |
| 1784 | fn handler(kind: &[u8; 4], name: &str) -> Outcome<Vec<u8>> { |
| 1785 | let mut b = Vec::with_capacity(32 + name.len()); |
| 1786 | b.extend_from_slice(&full(0, 0)); |
| 1787 | b.extend_from_slice(&0u32.to_be_bytes()); // Pre-defined. |
| 1788 | b.extend_from_slice(kind); |
| 1789 | b.extend_from_slice(&[0u8; 12]); // Reserved. |
| 1790 | b.extend_from_slice(name.as_bytes()); |
| 1791 | b.push(0); |
| 1792 | bx(b"hdlr", &b) |
| 1793 | } |
| 1794 | |
| 1795 | /// The sound media header, ISO/IEC 14496-12 §8.4.5.3. |
| 1796 | /// |
| 1797 | /// The counterpart of [`vmhd`], and a track carries exactly one of the two: which one is what |
| 1798 | /// `hdlr` has just declared, and a reader meeting the wrong one has been told two different things |
| 1799 | /// about the same track. |
| 1800 | fn smhd() -> Outcome<Vec<u8>> { |
| 1801 | let mut b = Vec::with_capacity(8); |
| 1802 | b.extend_from_slice(&full(0, 0)); |
| 1803 | b.extend_from_slice(&0u16.to_be_bytes()); // Balance: centre. |
| 1804 | b.extend_from_slice(&0u16.to_be_bytes()); // Reserved. |
| 1805 | bx(b"smhd", &b) |
| 1806 | } |
| 1807 | |
| 1808 | /// The video media header, ISO/IEC 14496-12 §8.4.5.2. |
| 1809 | /// |
| 1810 | /// Its flags must be 1, which the specification states outright and gives no meaning for; a zero |
| 1811 | /// there is rejected by some readers. |
| 1812 | fn vmhd() -> Outcome<Vec<u8>> { |
| 1813 | let mut b = Vec::with_capacity(12); |
| 1814 | b.extend_from_slice(&full(0, 1)); |
| 1815 | b.extend_from_slice(&0u16.to_be_bytes()); // Graphics mode: copy. |
| 1816 | b.extend_from_slice(&[0u8; 6]); // Operation colour, unused by copy. |
| 1817 | bx(b"vmhd", &b) |
| 1818 | } |
| 1819 | |
| 1820 | /// The data information box: where the media of this track is to be found. |
| 1821 | /// |
| 1822 | /// One `url ` entry with the self-contained flag set, meaning the media is in this file. The |
| 1823 | /// four-character code has a trailing space, which is not a typographic accident. |
| 1824 | fn dinf() -> Outcome<Vec<u8>> { |
| 1825 | let url = res!(bx(b"url ", &full(0, 1))); |
| 1826 | let mut dref = Vec::with_capacity(8 + url.len()); |
| 1827 | dref.extend_from_slice(&full(0, 0)); |
| 1828 | dref.extend_from_slice(&1u32.to_be_bytes()); // Entry count. |
| 1829 | dref.extend_from_slice(&url); |
| 1830 | let dref = res!(bx(b"dref", &dref)); |
| 1831 | bx(b"dinf", &dref) |
| 1832 | } |
| 1833 | |
| 1834 | /// The sample to chunk table, ISO/IEC 14496-12 §8.7.4, for one sample a chunk. |
| 1835 | /// |
| 1836 | /// A single entry: from chunk 1 onward, one sample a chunk, described by sample description 1. |
| 1837 | /// Entries are only written where the run changes, so one entry covers every chunk in the track. |
| 1838 | /// Works out each sample's composition offset from the times a source container states. |
| 1839 | /// |
| 1840 | /// # Why a caller needs this at all |
| 1841 | /// |
| 1842 | /// Matroska, and the other containers a film arrives in, state **only when a picture is shown**. |
| 1843 | /// MP4 states when it is decoded and how long after that it is shown. The decoding times are |
| 1844 | /// already fixed by the durations the caller is writing -- sample `i` is decoded after the sum of |
| 1845 | /// the durations before it -- so the offset is the difference, and this computes it. |
| 1846 | /// |
| 1847 | /// `times` are presentation times **in decode order**, which is the order the frames come out of a |
| 1848 | /// container and the order they must be written in, in whatever unit the durations are given in. |
| 1849 | /// |
| 1850 | /// # They must be relative to the track's start, and this does not rebase them |
| 1851 | /// |
| 1852 | /// A composition offset is the distance between a picture's decoding and its showing, and it is |
| 1853 | /// meant to be a handful of frames. Decoding here begins at nought, so handing this the *absolute* |
| 1854 | /// times of a film that starts an hour in gives every sample an offset of an hour: representable, |
| 1855 | /// wrong in meaning, and the sort of thing that plays correctly on the machine that wrote it and |
| 1856 | /// puzzles everything else. Subtract the first sample's time before calling, and put where the |
| 1857 | /// track actually begins in [`Stream::start`], which is what that field is for. |
| 1858 | /// |
| 1859 | /// This deliberately does **not** rebase, because it cannot tell the two cases apart: the smallest |
| 1860 | /// raw difference is a real measurement when a track begins at nought, and rebasing on it would |
| 1861 | /// quietly discard a genuine reordering delay. |
| 1862 | /// |
| 1863 | /// # The shift, and why the whole track moves |
| 1864 | /// |
| 1865 | /// A picture may be shown *before* a later-decoded picture that precedes it in decode order, so the |
| 1866 | /// raw difference is negative for the pictures at the head of a reordered run: they are decoded |
| 1867 | /// early precisely so the ones they are shown between can refer to them. A negative offset says a |
| 1868 | /// picture is shown before it is decoded, which is not something a decoder can do. |
| 1869 | /// |
| 1870 | /// So every offset is raised by one constant -- the largest shortfall -- which delays the whole |
| 1871 | /// track by that much and leaves the intervals between pictures exactly as they were. Nothing about |
| 1872 | /// the film changes but the instant it starts, by a few frames. |
| 1873 | pub fn composition_offsets(times: &[i64], durs: &[u32]) -> Outcome<Vec<i32>> { |
| 1874 | if times.len() != durs.len() { |
| 1875 | return Err(err!( |
| 1876 | "There are {} presentation times and {} durations, and each sample needs one of each.", |
| 1877 | times.len(), durs.len(); |
| 1878 | Invalid, Input, Mismatch)); |
| 1879 | } |
| 1880 | let mut raw = Vec::with_capacity(times.len()); |
| 1881 | let mut dts = 0i64; |
| 1882 | let mut least = 0i64; |
| 1883 | for (i, t) in times.iter().enumerate() { |
| 1884 | let d = t - dts; |
| 1885 | if d < least { |
| 1886 | least = d; |
| 1887 | } |
| 1888 | raw.push(d); |
| 1889 | dts += durs[i] as i64; |
| 1890 | } |
| 1891 | let mut out = Vec::with_capacity(raw.len()); |
| 1892 | for d in raw { |
| 1893 | let v = d - least; |
| 1894 | if v > i32::MAX as i64 { |
| 1895 | return Err(err!( |
| 1896 | "A composition offset of {} ticks will not fit the 32 bits the table holds, so the \ |
| 1897 | presentation times given are not those of one film.", v; |
| 1898 | Invalid, Input, Excessive)); |
| 1899 | } |
| 1900 | out.push(v as i32); |
| 1901 | } |
| 1902 | Ok(out) |
| 1903 | } |
| 1904 | |
| 1905 | fn stsc() -> Outcome<Vec<u8>> { |
| 1906 | let mut b = Vec::with_capacity(20); |
| 1907 | b.extend_from_slice(&full(0, 0)); |
| 1908 | b.extend_from_slice(&1u32.to_be_bytes()); // Entry count. |
| 1909 | b.extend_from_slice(&1u32.to_be_bytes()); // First chunk; one-based. |
| 1910 | b.extend_from_slice(&1u32.to_be_bytes()); // Samples a chunk. |
| 1911 | b.extend_from_slice(&1u32.to_be_bytes()); // Sample description index; one-based. |
| 1912 | bx(b"stsc", &b) |
| 1913 | } |
| 1914 | |
| 1915 | /// Wraps a payload in a box header: a 32-bit size covering the whole box, then its four-character |
| 1916 | /// type. ISO/IEC 14496-12 §4.2. |
| 1917 | fn bx(kind: &[u8; 4], body: &[u8]) -> Outcome<Vec<u8>> { |
| 1918 | let size = body.len() + 8; |
| 1919 | if size > u32::MAX as usize { |
| 1920 | return Err(err!( |
| 1921 | "The '{}' box comes to {} bytes, which will not fit the 32-bit size a box header \ |
| 1922 | carries; only 'mdat' is written in the 64-bit form.", |
| 1923 | String::from_utf8_lossy(kind), size; |
| 1924 | Invalid, Input, Excessive)); |
| 1925 | } |
| 1926 | let mut out = Vec::with_capacity(size); |
| 1927 | out.extend_from_slice(&(size as u32).to_be_bytes()); |
| 1928 | out.extend_from_slice(kind); |
| 1929 | out.extend_from_slice(body); |
| 1930 | Ok(out) |
| 1931 | } |
| 1932 | |
| 1933 | /// The version and flags a full box begins with: one byte of version, then three of flags. ISO/IEC |
| 1934 | /// 14496-12 §4.2. |
| 1935 | fn full(ver: u8, flags: u32) -> [u8; 4] { |
| 1936 | let f = flags.to_be_bytes(); |
| 1937 | [ver, f[1], f[2], f[3]] |
| 1938 | } |
| 1939 | |
| 1940 | /// A duration counted in one timescale, expressed in another, rounded to nearest. |
| 1941 | fn rescale(ticks: u64, from: u32, to: u32) -> Outcome<u64> { |
| 1942 | if from == 0 { |
| 1943 | return Err(err!("A duration cannot be rescaled from a timescale of zero."; Invalid, Input)); |
| 1944 | } |
| 1945 | let n = (ticks as u128) * (to as u128) + (from as u128) / 2; |
| 1946 | Ok((n / from as u128) as u64) |
| 1947 | } |
| 1948 | |
| 1949 | /// The raw byte sequence payload of a NAL unit: its body with the emulation prevention bytes taken |
| 1950 | /// out, so that a `00 00 03` written to keep a start code out of the stream reads back as the |
| 1951 | /// `00 00` it stands for. ITU-T H.264 §7.4.1. |
| 1952 | fn rbsp(nal: &[u8]) -> Vec<u8> { |
| 1953 | let mut out = Vec::with_capacity(nal.len()); |
| 1954 | let mut zeros = 0usize; |
| 1955 | for &b in nal { |
| 1956 | if zeros >= 2 && b == 0x03 { |
| 1957 | zeros = 0; |
| 1958 | continue; |
| 1959 | } |
| 1960 | out.push(b); |
| 1961 | zeros = if b == 0 { zeros + 1 } else { 0 }; |
| 1962 | } |
| 1963 | out |
| 1964 | } |
| 1965 | |
| 1966 | /// A reader of the bits of an RBSP, most significant first, which is how H.264 codes its syntax |
| 1967 | /// elements. |
| 1968 | struct Bits<'a> { |
| 1969 | buf: &'a [u8], |
| 1970 | pos: usize, // the next bit, counted from the first bit of the first byte |
| 1971 | } |
| 1972 | |
| 1973 | impl<'a> Bits<'a> { |
| 1974 | |
| 1975 | fn new(buf: &'a [u8]) -> Self { |
| 1976 | Self { buf, pos: 0 } |
| 1977 | } |
| 1978 | |
| 1979 | /// The next `n` bits as an unsigned integer, most significant first. |
| 1980 | fn u(&mut self, n: usize) -> Outcome<u32> { |
| 1981 | if n > 32 { |
| 1982 | return Err(err!("A field of {} bits was asked for, and 32 is the widest.", n; Bug)); |
| 1983 | } |
| 1984 | let mut v = 0u32; |
| 1985 | for _ in 0..n { |
| 1986 | let byte = self.pos >> 3; |
| 1987 | if byte >= self.buf.len() { |
| 1988 | return Err(err!( |
| 1989 | "The sequence parameter set ends after {} bits, before its frame geometry was \ |
| 1990 | read.", self.buf.len() * 8; |
| 1991 | Invalid, Input, Decode)); |
| 1992 | } |
| 1993 | let bit = (self.buf[byte] >> (7 - (self.pos & 7))) & 1; |
| 1994 | v = (v << 1) | bit as u32; |
| 1995 | self.pos += 1; |
| 1996 | } |
| 1997 | Ok(v) |
| 1998 | } |
| 1999 | |
| 2000 | fn flag(&mut self) -> Outcome<bool> { |
| 2001 | Ok(res!(self.u(1)) == 1) |
| 2002 | } |
| 2003 | |
| 2004 | /// An unsigned Exp-Golomb code, ITU-T H.264 §9.1. |
| 2005 | fn ue(&mut self) -> Outcome<u32> { |
| 2006 | let mut zeros = 0usize; |
| 2007 | while res!(self.u(1)) == 0 { |
| 2008 | zeros += 1; |
| 2009 | if zeros > 31 { |
| 2010 | return Err(err!( |
| 2011 | "An Exp-Golomb code in the sequence parameter set is prefixed by more than 31 \ |
| 2012 | zeroes, which no legal value is."; |
| 2013 | Invalid, Input, Decode)); |
| 2014 | } |
| 2015 | } |
| 2016 | if zeros == 0 { |
| 2017 | return Ok(0); |
| 2018 | } |
| 2019 | let rest = res!(self.u(zeros)) as u64; |
| 2020 | let v = (1u64 << zeros) - 1 + rest; |
| 2021 | if v > u32::MAX as u64 { |
| 2022 | return Err(err!( |
| 2023 | "An Exp-Golomb code in the sequence parameter set decodes to {}, beyond what any \ |
| 2024 | of its fields may hold.", v; |
| 2025 | Invalid, Input, Decode)); |
| 2026 | } |
| 2027 | Ok(v as u32) |
| 2028 | } |
| 2029 | |
| 2030 | /// A signed Exp-Golomb code, ITU-T H.264 §9.1.1. |
| 2031 | fn se(&mut self) -> Outcome<i32> { |
| 2032 | let k = res!(self.ue()); |
| 2033 | let m = ((k as i64 + 1) / 2) as i32; |
| 2034 | Ok(if k % 2 == 1 { m } else { -m }) |
| 2035 | } |
| 2036 | } |
| 2037 | |
| 2038 | /// Steps over a scaling list without keeping it, ITU-T H.264 §7.3.2.1.1.1. |
| 2039 | /// |
| 2040 | /// The list has to be walked rather than skipped by a byte count, because it is coded as a run of |
| 2041 | /// variable-length differences and its end is only found by decoding all of them. |
| 2042 | fn skip_scaling_list(b: &mut Bits, size: usize) -> Outcome<()> { |
| 2043 | let mut last = 8i32; |
| 2044 | let mut next = 8i32; |
| 2045 | for _ in 0..size { |
| 2046 | if next != 0 { |
| 2047 | let delta = res!(b.se()); |
| 2048 | next = (last + delta + 256).rem_euclid(256); |
| 2049 | } |
| 2050 | last = if next == 0 { last } else { next }; |
| 2051 | } |
| 2052 | Ok(()) |
| 2053 | } |
| 2054 | |
| 2055 | /// The frame width and height coded in a sequence parameter set, in pixels. |
| 2056 | /// |
| 2057 | /// This reads the geometry and nothing else: the macroblock counts, whether the stream is coded in |
| 2058 | /// frames or in fields, and the cropping window. It is not a decoder and does not become one -- the |
| 2059 | /// point of it is that the caller's declared dimensions can be checked against the stream's own, |
| 2060 | /// rather than written into the container on trust. |
| 2061 | /// |
| 2062 | /// The frame is `(pic_width_in_mbs_minus1 + 1) * 16` wide before cropping, and |
| 2063 | /// `(2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16` high, and the crop |
| 2064 | /// offsets are then subtracted in units of the chroma sampling, per ITU-T H.264 §7.4.2.1.1. |
| 2065 | fn sps_geometry(sps: &[u8]) -> Outcome<(u16, u16)> { |
| 2066 | if sps.is_empty() { |
| 2067 | return Err(err!("The sequence parameter set is empty."; Invalid, Input, Missing)); |
| 2068 | } |
| 2069 | let kind = sps[0] & 0x1F; |
| 2070 | if kind != 7 { |
| 2071 | return Err(err!( |
| 2072 | "The first parameter set in the decoder configuration is NAL unit type {}, and a \ |
| 2073 | sequence parameter set is type 7.", kind; |
| 2074 | Invalid, Input, Mismatch)); |
| 2075 | } |
| 2076 | let body = rbsp(&sps[1..]); |
| 2077 | let mut b = Bits::new(&body); |
| 2078 | |
| 2079 | let profile = res!(b.u(8)); |
| 2080 | let _constraints = res!(b.u(8)); |
| 2081 | let _level = res!(b.u(8)); |
| 2082 | let _sps_id = res!(b.ue()); |
| 2083 | |
| 2084 | // The profiles that carry a chroma format and scaling lists in the parameter set, ITU-T H.264 |
| 2085 | // §7.3.2.1.1. Every other profile is 4:2:0 with no lists. |
| 2086 | let mut chroma = 1u32; |
| 2087 | let mut separate_planes = false; |
| 2088 | if matches!(profile, 100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135) { |
| 2089 | chroma = res!(b.ue()); |
| 2090 | if chroma == 3 { |
| 2091 | separate_planes = res!(b.flag()); |
| 2092 | } |
| 2093 | let _bit_depth_luma = res!(b.ue()); |
| 2094 | let _bit_depth_chroma = res!(b.ue()); |
| 2095 | let _qpprime_bypass = res!(b.flag()); |
| 2096 | if res!(b.flag()) { |
| 2097 | let lists = if chroma != 3 { 8 } else { 12 }; |
| 2098 | for i in 0..lists { |
| 2099 | if res!(b.flag()) { |
| 2100 | res!(skip_scaling_list(&mut b, if i < 6 { 16 } else { 64 })); |
| 2101 | } |
| 2102 | } |
| 2103 | } |
| 2104 | } |
| 2105 | |
| 2106 | let _log2_max_frame_num = res!(b.ue()); |
| 2107 | let poc_type = res!(b.ue()); |
| 2108 | match poc_type { |
| 2109 | 0 => { |
| 2110 | let _log2_max_poc_lsb = res!(b.ue()); |
| 2111 | }, |
| 2112 | 1 => { |
| 2113 | let _delta_always_zero = res!(b.flag()); |
| 2114 | let _offset_non_ref = res!(b.se()); |
| 2115 | let _offset_top_bottom = res!(b.se()); |
| 2116 | let cycle = res!(b.ue()); |
| 2117 | if cycle > 255 { |
| 2118 | return Err(err!( |
| 2119 | "The sequence parameter set names {} entries in its picture order count cycle, \ |
| 2120 | and 255 is the most allowed.", cycle; |
| 2121 | Invalid, Input, Decode)); |
| 2122 | } |
| 2123 | for _ in 0..cycle { |
| 2124 | let _offset = res!(b.se()); |
| 2125 | } |
| 2126 | }, |
| 2127 | 2 => {}, |
| 2128 | other => return Err(err!( |
| 2129 | "The sequence parameter set names picture order count type {}, and 0, 1 and 2 are the \ |
| 2130 | only ones defined.", other; |
| 2131 | Invalid, Input, Decode)), |
| 2132 | } |
| 2133 | |
| 2134 | let _max_ref_frames = res!(b.ue()); |
| 2135 | let _gaps_allowed = res!(b.flag()); |
| 2136 | let mbs_wide = res!(b.ue()) as u64 + 1; |
| 2137 | let map_units_high = res!(b.ue()) as u64 + 1; |
| 2138 | let frame_mbs_only = res!(b.flag()); |
| 2139 | if !frame_mbs_only { |
| 2140 | let _mb_adaptive = res!(b.flag()); |
| 2141 | } |
| 2142 | let _direct_8x8 = res!(b.flag()); |
| 2143 | |
| 2144 | let (mut left, mut right, mut top, mut bottom) = (0u64, 0u64, 0u64, 0u64); |
| 2145 | if res!(b.flag()) { |
| 2146 | left = res!(b.ue()) as u64; |
| 2147 | right = res!(b.ue()) as u64; |
| 2148 | top = res!(b.ue()) as u64; |
| 2149 | bottom = res!(b.ue()) as u64; |
| 2150 | } |
| 2151 | |
| 2152 | // The crop offsets are counted in chroma samples, so they scale by the chroma subsampling. |
| 2153 | // Monochrome, and a stream whose colour planes are coded separately, crop in luma samples. |
| 2154 | let (sub_w, sub_h) = match chroma { |
| 2155 | 0 => (1u64, 1u64), |
| 2156 | 1 => (2, 2), |
| 2157 | 2 => (2, 1), |
| 2158 | 3 => (1, 1), |
| 2159 | other => return Err(err!( |
| 2160 | "The sequence parameter set names chroma format {}, and 0 to 3 are the only ones \ |
| 2161 | defined.", other; |
| 2162 | Invalid, Input, Decode)), |
| 2163 | }; |
| 2164 | let (crop_x, crop_y) = if chroma == 0 || separate_planes { |
| 2165 | (1u64, if frame_mbs_only { 1 } else { 2 }) |
| 2166 | } else { |
| 2167 | (sub_w, sub_h * if frame_mbs_only { 1 } else { 2 }) |
| 2168 | }; |
| 2169 | |
| 2170 | let raw_w = mbs_wide * 16; |
| 2171 | let raw_h = map_units_high * 16 * if frame_mbs_only { 1 } else { 2 }; |
| 2172 | let cut_w = crop_x * (left + right); |
| 2173 | let cut_h = crop_y * (top + bottom); |
| 2174 | if cut_w >= raw_w || cut_h >= raw_h { |
| 2175 | return Err(err!( |
| 2176 | "The sequence parameter set crops {} by {} coded pixels down to nothing, taking {} \ |
| 2177 | from the width and {} from the height.", raw_w, raw_h, cut_w, cut_h; |
| 2178 | Invalid, Input, Range)); |
| 2179 | } |
| 2180 | let w = raw_w - cut_w; |
| 2181 | let h = raw_h - cut_h; |
| 2182 | if w > u16::MAX as u64 || h > u16::MAX as u64 { |
| 2183 | return Err(err!( |
| 2184 | "The sequence parameter set codes a frame of {} by {} pixels, beyond what a visual \ |
| 2185 | sample entry can state.", w, h; |
| 2186 | Invalid, Input, Excessive)); |
| 2187 | } |
| 2188 | Ok((w as u16, h as u16)) |
| 2189 | } |
| 2190 | |
| 2191 | // ------------------------------------------------------------------------- reading a film |
| 2192 | |
| 2193 | const MAX_DEPTH: usize = 16; // the deepest a box tree may nest before it is called a mistake |
| 2194 | |
| 2195 | /// Which codec a track's samples are coded in, as its sample entry names it. |
| 2196 | /// |
| 2197 | /// An enum rather than the four-character code, so that a caller matches on a thing the reader has |
| 2198 | /// already recognised rather than on a byte string of its own. |
| 2199 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 2200 | pub enum Kind { |
| 2201 | Avc, // H.264, described by an avcC record |
| 2202 | Hevc, // HEVC, described by an hvcC record |
| 2203 | Mjpeg, // every sample a whole JPEG, with no configuration record at all |
| 2204 | Other([u8; 4]), // carries its code so that a refusal can name it |
| 2205 | } |
| 2206 | |
| 2207 | impl Kind { |
| 2208 | |
| 2209 | /// The codec a sample entry's four-character code names. |
| 2210 | fn of(code: [u8; 4]) -> Self { |
| 2211 | match &code { |
| 2212 | // `avc1` carries its parameter sets in the configuration record only; `avc3` may also |
| 2213 | // carry them in the samples. Both are read the same way here, because the sample is |
| 2214 | // walked for parameter sets in either case. |
| 2215 | b"avc1" | b"avc3" => Self::Avc, |
| 2216 | b"hvc1" | b"hev1" => Self::Hevc, |
| 2217 | b"jpeg" | b"mjpa" | b"mjpb" => Self::Mjpeg, |
| 2218 | _ => Self::Other(code), |
| 2219 | } |
| 2220 | } |
| 2221 | } |
| 2222 | |
| 2223 | /// One video track of a film, as its sample table describes it. |
| 2224 | /// |
| 2225 | /// This is the reading half of this module, and it exists for one job: telling a decoder where a |
| 2226 | /// film's first coded picture is. It holds an **index and not the film**: the sample table of a |
| 2227 | /// four-gigabyte film is a few tens of kilobytes, and a caller that wants one frame of it should |
| 2228 | /// never have to hold the other four gigabytes to get it. So the samples are spans, and the bytes |
| 2229 | /// they name are the caller's to fetch. |
| 2230 | #[derive(Clone, Debug)] |
| 2231 | pub struct Film { |
| 2232 | kind: Kind, // which codec the track is coded in |
| 2233 | config: Vec<u8>, // the configuration record, where the codec has one |
| 2234 | width: u16, // the coded width the sample entry declares |
| 2235 | height: u16, // and the coded height |
| 2236 | rotation: u16, // how far to turn the picture, degrees clockwise |
| 2237 | aperture: Option<(u32, u32, u32, u32)>, // left, top, width, height of the real picture |
| 2238 | samples: Vec<(u64, u32)>, // each sample's offset in the file and its length |
| 2239 | // Which samples a reader may begin decoding at, as stss lists them, counted from nought. |
| 2240 | // Empty means the box was absent, which per ISO/IEC 14496-12 §8.6.2 means every sample is a |
| 2241 | // sync sample -- the opposite of what an empty list would otherwise suggest, and the reason |
| 2242 | // this is not an Option the caller has to remember to check. |
| 2243 | sync: Vec<u32>, |
| 2244 | } |
| 2245 | |
| 2246 | impl Film { |
| 2247 | |
| 2248 | /// Reads a film's first video track out of a whole file. |
| 2249 | /// |
| 2250 | /// A file with no video track, or with one whose sample table is incomplete, is refused: a |
| 2251 | /// track whose samples cannot be located is not a track a picture can be drawn from. |
| 2252 | pub fn read(bytes: &[u8]) -> Outcome<Self> { |
| 2253 | let mut at = 0usize; |
| 2254 | while at + 8 <= bytes.len() { |
| 2255 | let (size, head) = res!(box_head(bytes, at, bytes.len())); |
| 2256 | if &bytes[at + 4..at + 8] == b"moov" { |
| 2257 | return match res!(movie(bytes, at + head, at + size)) { |
| 2258 | Some(f) => Ok(f), |
| 2259 | None => Err(err!( |
| 2260 | "The file's movie box carries no video track. A film needs a track whose \ |
| 2261 | handler is `vide`."; |
| 2262 | Invalid, Input, Missing)), |
| 2263 | }; |
| 2264 | } |
| 2265 | at += size; |
| 2266 | } |
| 2267 | Err(err!( |
| 2268 | "The file carries no `moov` box, so nothing says where its samples are."; |
| 2269 | Invalid, Input, Missing)) |
| 2270 | } |
| 2271 | |
| 2272 | /// The same, from a `moov` box a caller has lifted out of a file on its own. |
| 2273 | /// |
| 2274 | /// This is what a caller with a file handle rather than a buffer uses: the chunk offsets in |
| 2275 | /// `stco` are absolute file offsets, so the index does not depend on where the movie box itself |
| 2276 | /// sat, and a film of any size can be indexed by reading its metadata alone. QuickTime writes |
| 2277 | /// `moov` at the end of the file as often as at the front, so this is not a rare path. |
| 2278 | pub fn from_moov(moov: &[u8]) -> Outcome<Self> { |
| 2279 | match res!(movie(moov, 0, moov.len())) { |
| 2280 | Some(f) => Ok(f), |
| 2281 | None => Err(err!( |
| 2282 | "The movie box carries no video track. A film needs a track whose handler is \ |
| 2283 | `vide`."; |
| 2284 | Invalid, Input, Missing)), |
| 2285 | } |
| 2286 | } |
| 2287 | |
| 2288 | pub fn kind(&self) -> Kind { |
| 2289 | self.kind |
| 2290 | } |
| 2291 | |
| 2292 | /// The decoder configuration record out of the sample entry: `avcC` or `hvcC`. |
| 2293 | pub fn config(&self) -> &[u8] { |
| 2294 | &self.config |
| 2295 | } |
| 2296 | |
| 2297 | /// The coded size the sample entry declares, which is not the cropped size the parameter set |
| 2298 | /// implies and should not be shown as though it were. |
| 2299 | pub fn size(&self) -> (u16, u16) { |
| 2300 | (self.width, self.height) |
| 2301 | } |
| 2302 | |
| 2303 | /// How far the picture is to be turned before it is shown: 0, 90, 180 or 270 degrees clockwise. |
| 2304 | /// |
| 2305 | /// A phone writes the angle it was held at into the track header's transformation matrix rather |
| 2306 | /// than turning the samples, so a decoder's output is the picture as it was *coded* and this is |
| 2307 | /// what a viewer must do with it. Ignoring it shows a great many holiday films on their side. |
| 2308 | /// It also hides itself well at ninety degrees, where the turned picture has exactly as many |
| 2309 | /// samples as the untured one. |
| 2310 | pub fn rotation(&self) -> u16 { |
| 2311 | self.rotation |
| 2312 | } |
| 2313 | |
| 2314 | /// The rectangle of the coded picture that is actually the picture: left, top, width, height. |
| 2315 | /// |
| 2316 | /// `None` where the track states none, which means the whole of it. A phone stabilises a film |
| 2317 | /// by coding a picture larger than it shows and moving a window about inside it; the window is |
| 2318 | /// the clean aperture, and a viewer that ignores it shows about nine per cent more of the |
| 2319 | /// frame than the film means to show, wobbling margin and all. |
| 2320 | pub fn aperture(&self) -> Option<(u32, u32, u32, u32)> { |
| 2321 | self.aperture |
| 2322 | } |
| 2323 | |
| 2324 | pub fn samples(&self) -> usize { |
| 2325 | self.samples.len() |
| 2326 | } |
| 2327 | |
| 2328 | /// Where one sample sits in the file, and how long it is. |
| 2329 | pub fn span(&self, i: usize) -> Outcome<(u64, u32)> { |
| 2330 | match self.samples.get(i) { |
| 2331 | Some(s) => Ok(*s), |
| 2332 | None => Err(err!( |
| 2333 | "Sample {} was asked for and the track holds {}.", i, self.samples.len(); |
| 2334 | Invalid, Input, Range)), |
| 2335 | } |
| 2336 | } |
| 2337 | |
| 2338 | /// One sample's bytes, out of the file the index was read from. |
| 2339 | pub fn sample<'b>(&self, bytes: &'b [u8], i: usize) -> Outcome<&'b [u8]> { |
| 2340 | let (off, len) = res!(self.span(i)); |
| 2341 | let from = off as usize; |
| 2342 | let to = match from.checked_add(len as usize) { |
| 2343 | Some(to) if to <= bytes.len() => to, |
| 2344 | _ => return Err(err!( |
| 2345 | "Sample {} sits at byte {} and is {} long, in a buffer of {}. A file read in part \ |
| 2346 | cannot give up its samples.", i, off, len, bytes.len(); |
| 2347 | Invalid, Input, Decode)), |
| 2348 | }; |
| 2349 | Ok(&bytes[from..to]) |
| 2350 | } |
| 2351 | |
| 2352 | /// Which sample a decoder may begin at. |
| 2353 | /// |
| 2354 | /// Almost always the first sample of the track, since a film that cannot be played from its |
| 2355 | /// start is a film no player will open, but a track whose `stss` says otherwise is followed |
| 2356 | /// rather than assumed about. |
| 2357 | pub fn first_sync(&self) -> Outcome<usize> { |
| 2358 | match self.sync.first() { |
| 2359 | Some(n) => Ok(*n as usize), |
| 2360 | // An absent `stss` means every sample is a sync sample. |
| 2361 | None if !self.samples.is_empty() => Ok(0), |
| 2362 | None => Err(err!("The track holds no samples."; Invalid, Input, Missing)), |
| 2363 | } |
| 2364 | } |
| 2365 | |
| 2366 | /// Reads a film's first video track out of a file, holding none of the film. |
| 2367 | /// |
| 2368 | /// This is the form a caller wanting one frame of a large film uses: the top-level boxes are |
| 2369 | /// walked by their headers, the `moov` box alone is read, and the handle is left open so that |
| 2370 | /// [`Film::read_sample`] can fetch the one sample the index names. A film of four gigabytes |
| 2371 | /// costs its metadata, and a caller that cannot hold the file -- which is most callers, since |
| 2372 | /// most films are past any sensible buffer -- is not shut out of its first frame. |
| 2373 | pub fn of(f: &mut File) -> Outcome<Self> { |
| 2374 | let moov = match res!(moov_of(f)) { |
| 2375 | Some(m) => m, |
| 2376 | None => return Err(err!( |
| 2377 | "The file carries no `moov` box, so nothing says where its samples are."; |
| 2378 | Invalid, Input, Missing)), |
| 2379 | }; |
| 2380 | Self::from_moov(&moov) |
| 2381 | } |
| 2382 | |
| 2383 | /// One sample's bytes, read out of the file the index was read from. |
| 2384 | /// |
| 2385 | /// The counterpart of [`Film::sample`] for a caller with a handle rather than a buffer. The |
| 2386 | /// offsets in a sample table are absolute file offsets, so this needs nothing of where the |
| 2387 | /// movie box sat. |
| 2388 | pub fn read_sample(&self, f: &mut File, i: usize) -> Outcome<Vec<u8>> { |
| 2389 | let (off, len) = res!(self.span(i)); |
| 2390 | if len > SAMPLE_MAX { |
| 2391 | return Err(err!( |
| 2392 | "Sample {} says it is {} bytes long, and {} is this reader's ceiling for one \ |
| 2393 | coded picture.", i, len, SAMPLE_MAX; |
| 2394 | Invalid, Input, Excessive)); |
| 2395 | } |
| 2396 | let mut buf = vec![0u8; len as usize]; |
| 2397 | res!(f.seek(SeekFrom::Start(off)), IO, File); |
| 2398 | res!(f.read_exact(&mut buf), IO, File); |
| 2399 | Ok(buf) |
| 2400 | } |
| 2401 | |
| 2402 | /// The bytes of the first sample a decoder may begin at. |
| 2403 | /// |
| 2404 | /// The one call a poster frame needs: which sample to start at, and its bytes. |
| 2405 | pub fn read_first_sync(&self, f: &mut File) -> Outcome<Vec<u8>> { |
| 2406 | let i = res!(self.first_sync()); |
| 2407 | self.read_sample(f, i) |
| 2408 | } |
| 2409 | } |
| 2410 | |
| 2411 | // ------------------------------------------------------------- a film's index, out of a file |
| 2412 | |
| 2413 | // The most bytes a movie box may occupy before the file is called a mistake. A moov is an index |
| 2414 | // and not media: the sample tables of a track at this reader's ceiling of a million samples come |
| 2415 | // to a few tens of megabytes, and anything past this is a length field read out of the wrong place |
| 2416 | // rather than a film. |
| 2417 | pub const MOOV_MAX: u64 = 64 * 1024 * 1024; |
| 2418 | |
| 2419 | // The most bytes one sample may occupy: one coded picture. A 4K intra frame is a few megabytes; |
| 2420 | // this is a ceiling against a length that is a mistake, since the length is what a buffer is sized |
| 2421 | // from. |
| 2422 | pub const SAMPLE_MAX: u32 = 64 * 1024 * 1024; |
| 2423 | |
| 2424 | // how much of a movie header is read back, more than either version of the box occupies |
| 2425 | pub const MVHD_BYTES: u64 = 256; |
| 2426 | |
| 2427 | const MAX_BOXES: usize = 4096; // the most boxes walked at one level looking for one of them |
| 2428 | |
| 2429 | /// The body of the first box of a given type between two offsets in a file, as its offset and its |
| 2430 | /// length. |
| 2431 | /// |
| 2432 | /// **Only the box headers are read**: eight bytes each, or the sixteen a 64-bit length needs. An |
| 2433 | /// `mdat` holding two gigabytes of film is stepped over by its declared length and never touched, |
| 2434 | /// which is what makes finding a film's index affordable on a file nobody wants in memory. |
| 2435 | /// |
| 2436 | /// A length that does not fit inside the range being walked ends the walk and answers `None` |
| 2437 | /// rather than failing. A file truncated in transfer is a thing to report absence for, and the |
| 2438 | /// caller asking this question has a plain answer for absence: the box is not there. |
| 2439 | pub fn find_box(f: &mut File, want: &[u8; 4], from: u64, to: u64) |
| 2440 | -> Outcome<Option<(u64, u64)>> |
| 2441 | { |
| 2442 | let mut at = from; |
| 2443 | for _ in 0..MAX_BOXES { |
| 2444 | if at + 8 > to { |
| 2445 | return Ok(None); |
| 2446 | } |
| 2447 | res!(f.seek(SeekFrom::Start(at)), IO, File); |
| 2448 | let mut head = [0u8; 16]; |
| 2449 | if res!(fill(f, &mut head[..8])) != 8 { |
| 2450 | return Ok(None); |
| 2451 | } |
| 2452 | let size = u32::from_be_bytes([head[0], head[1], head[2], head[3]]) as u64; |
| 2453 | let mut kind = [0u8; 4]; |
| 2454 | kind.copy_from_slice(&head[4..8]); |
| 2455 | // A size of nought means the box runs to the end of its parent; a size of one means the |
| 2456 | // real length is the eight bytes after the type (ISO/IEC 14496-12 §4.2). |
| 2457 | let (body, next) = match size { |
| 2458 | 0 => (at + 8, to), |
| 2459 | 1 => { |
| 2460 | if res!(fill(f, &mut head[8..16])) != 8 { |
| 2461 | return Ok(None); |
| 2462 | } |
| 2463 | let mut wide = [0u8; 8]; |
| 2464 | wide.copy_from_slice(&head[8..16]); |
| 2465 | let wide = u64::from_be_bytes(wide); |
| 2466 | if wide < 16 { |
| 2467 | return Ok(None); |
| 2468 | } |
| 2469 | (at + 16, at.saturating_add(wide)) |
| 2470 | }, |
| 2471 | n if n < 8 => return Ok(None), |
| 2472 | n => (at + 8, at.saturating_add(n)), |
| 2473 | }; |
| 2474 | if next > to || next <= at || body > next { |
| 2475 | return Ok(None); |
| 2476 | } |
| 2477 | if &kind == want { |
| 2478 | return Ok(Some((body, next - body))); |
| 2479 | } |
| 2480 | at = next; |
| 2481 | } |
| 2482 | Ok(None) |
| 2483 | } |
| 2484 | |
| 2485 | /// The body of a film's movie box, lifted out of a file. |
| 2486 | /// |
| 2487 | /// What comes back is the box's **children**, which is what [`Film::from_moov`] reads. QuickTime |
| 2488 | /// writes `moov` at the end of a file as often as at the front, and either is reached by the same |
| 2489 | /// walk over the top-level headers. |
| 2490 | pub fn moov_of(f: &mut File) -> Outcome<Option<Vec<u8>>> { |
| 2491 | let end = res!(f.metadata(), IO, File).len(); |
| 2492 | let (body, len) = match res!(find_box(f, b"moov", 0, end)) { |
| 2493 | Some(span) => span, |
| 2494 | None => return Ok(None), |
| 2495 | }; |
| 2496 | if len > MOOV_MAX { |
| 2497 | return Err(err!( |
| 2498 | "A movie box of {} bytes, and {} is this reader's ceiling. A `moov` is an index and \ |
| 2499 | not media.", len, MOOV_MAX; |
| 2500 | Invalid, Input, Excessive)); |
| 2501 | } |
| 2502 | let mut buf = vec![0u8; len as usize]; |
| 2503 | res!(f.seek(SeekFrom::Start(body)), IO, File); |
| 2504 | res!(f.read_exact(&mut buf), IO, File); |
| 2505 | Ok(Some(buf)) |
| 2506 | } |
| 2507 | |
| 2508 | /// The payload of a film's movie header, lifted out of a file. |
| 2509 | /// |
| 2510 | /// The header is `moov`'s first child and carries the timescale, the duration and the times the |
| 2511 | /// film was recorded and last changed. Reaching it costs a handful of eight-byte reads and as many |
| 2512 | /// seeks, and it is deliberately not the whole of `moov`: a caller asking only how long a film runs |
| 2513 | /// should not read a long film's sample tables to find out. |
| 2514 | pub fn mvhd_of(f: &mut File) -> Outcome<Option<Vec<u8>>> { |
| 2515 | let end = res!(f.metadata(), IO, File).len(); |
| 2516 | let (moov, moov_len) = match res!(find_box(f, b"moov", 0, end)) { |
| 2517 | Some(span) => span, |
| 2518 | None => return Ok(None), |
| 2519 | }; |
| 2520 | let (body, len) = match res!(find_box(f, b"mvhd", moov, moov + moov_len)) { |
| 2521 | Some(span) => span, |
| 2522 | None => return Ok(None), |
| 2523 | }; |
| 2524 | let take = len.min(MVHD_BYTES) as usize; |
| 2525 | res!(f.seek(SeekFrom::Start(body)), IO, File); |
| 2526 | let mut buf = vec![0u8; take]; |
| 2527 | let got = res!(fill(f, &mut buf)); |
| 2528 | buf.truncate(got); |
| 2529 | Ok(Some(buf)) |
| 2530 | } |
| 2531 | |
| 2532 | /// The timescale and the duration a movie header carries: ticks a second, and ticks. |
| 2533 | /// |
| 2534 | /// The two are only meaningful together, since the header counts in units of its own choosing. |
| 2535 | /// Version 1 of the box widens both the times and the duration while the timescale stays 32 bits in |
| 2536 | /// both. A timescale of nought, a duration of nought, and the all-ones a writer that did not know |
| 2537 | /// the duration leaves behind are all absence rather than numbers to divide. |
| 2538 | pub fn movie_ticks(mvhd: &[u8]) -> Option<(u32, u64)> { |
| 2539 | let (scale, ticks) = match mvhd.first() { |
| 2540 | Some(0) => { |
| 2541 | if mvhd.len() < 20 { |
| 2542 | return None; |
| 2543 | } |
| 2544 | let scale = u32::from_be_bytes([mvhd[12], mvhd[13], mvhd[14], mvhd[15]]); |
| 2545 | let ticks = u32::from_be_bytes([mvhd[16], mvhd[17], mvhd[18], mvhd[19]]); |
| 2546 | if ticks == u32::MAX { |
| 2547 | return None; |
| 2548 | } |
| 2549 | (scale, ticks as u64) |
| 2550 | }, |
| 2551 | Some(1) => { |
| 2552 | if mvhd.len() < 32 { |
| 2553 | return None; |
| 2554 | } |
| 2555 | let scale = u32::from_be_bytes([mvhd[20], mvhd[21], mvhd[22], mvhd[23]]); |
| 2556 | let mut wide = [0u8; 8]; |
| 2557 | wide.copy_from_slice(&mvhd[24..32]); |
| 2558 | let ticks = u64::from_be_bytes(wide); |
| 2559 | if ticks == u64::MAX { |
| 2560 | return None; |
| 2561 | } |
| 2562 | (scale, ticks) |
| 2563 | }, |
| 2564 | _ => return None, |
| 2565 | }; |
| 2566 | if scale == 0 || ticks == 0 { |
| 2567 | None |
| 2568 | } else { |
| 2569 | Some((scale, ticks)) |
| 2570 | } |
| 2571 | } |
| 2572 | |
| 2573 | /// Reads until the buffer is full or the file ends, answering how many bytes arrived. |
| 2574 | /// |
| 2575 | /// A single `read` may come back short of what was asked for without anything being wrong, and a |
| 2576 | /// box header read short by one byte is a film refused for nothing. |
| 2577 | fn fill(f: &mut File, buf: &mut [u8]) -> Outcome<usize> { |
| 2578 | let mut got = 0usize; |
| 2579 | while got < buf.len() { |
| 2580 | match res!(f.read(&mut buf[got..]), IO, File) { |
| 2581 | 0 => break, |
| 2582 | n => got += n, |
| 2583 | } |
| 2584 | } |
| 2585 | Ok(got) |
| 2586 | } |
| 2587 | |
| 2588 | /// Reads a box header, giving its whole length and the length of the header itself. |
| 2589 | fn box_head(bytes: &[u8], at: usize, to: usize) -> Outcome<(usize, usize)> { |
| 2590 | if at + 8 > to { |
| 2591 | return Err(err!("A box header runs past the end of its parent."; Invalid, Input, Decode)); |
| 2592 | } |
| 2593 | let size = u32::from_be_bytes([bytes[at], bytes[at + 1], bytes[at + 2], bytes[at + 3]]); |
| 2594 | let (size, head) = match size { |
| 2595 | // A size of one means the real one is the next eight bytes (ISO/IEC 14496-12 §4.2). |
| 2596 | 1 => { |
| 2597 | if at + 16 > to { |
| 2598 | return Err(err!( |
| 2599 | "A box says its length is sixty-four bits and its parent ends inside it."; |
| 2600 | Invalid, Input, Decode)); |
| 2601 | } |
| 2602 | let mut wide = [0u8; 8]; |
| 2603 | wide.copy_from_slice(&bytes[at + 8..at + 16]); |
| 2604 | (u64::from_be_bytes(wide) as usize, 16usize) |
| 2605 | }, |
| 2606 | // A size of zero means the box runs to the end of its parent. |
| 2607 | 0 => (to - at, 8usize), |
| 2608 | n => (n as usize, 8usize), |
| 2609 | }; |
| 2610 | if size < head || at.saturating_add(size) > to { |
| 2611 | return Err(err!( |
| 2612 | "A {} box at byte {} says it is {} bytes long, and its parent ends at {}.", |
| 2613 | String::from_utf8_lossy(&bytes[at + 4..at + 8]), at, size, to; |
| 2614 | Invalid, Input, Decode)); |
| 2615 | } |
| 2616 | Ok((size, head)) |
| 2617 | } |
| 2618 | |
| 2619 | /// Walks the children of a box, handing each one's four-character code and body to a visitor. |
| 2620 | /// |
| 2621 | /// The visitor answers whether the walk should descend into it. Every box is length-prefixed and |
| 2622 | /// the lengths have to tile the parent; a box that claims to end beyond its parent is a malformed |
| 2623 | /// file, and is refused rather than clamped, since clamping turns one wrong length into a |
| 2624 | /// plausible-looking picture. |
| 2625 | fn walk<F>(bytes: &[u8], parent: [u8; 4], from: usize, to: usize, depth: usize, visit: &mut F) |
| 2626 | -> Outcome<()> |
| 2627 | where |
| 2628 | F: FnMut([u8; 4], [u8; 4], usize, usize) -> Outcome<bool>, |
| 2629 | { |
| 2630 | if depth > MAX_DEPTH { |
| 2631 | return Err(err!( |
| 2632 | "The box tree nests more than {} deep, which no legal file does.", MAX_DEPTH; |
| 2633 | Invalid, Input, Decode)); |
| 2634 | } |
| 2635 | let mut at = from; |
| 2636 | while at + 8 <= to { |
| 2637 | let (size, head) = res!(box_head(bytes, at, to)); |
| 2638 | let mut kind = [0u8; 4]; |
| 2639 | kind.copy_from_slice(&bytes[at + 4..at + 8]); |
| 2640 | if res!(visit(kind, parent, at + head, at + size)) { |
| 2641 | res!(walk(bytes, kind, at + head, at + size, depth + 1, visit)); |
| 2642 | } |
| 2643 | at += size; |
| 2644 | } |
| 2645 | Ok(()) |
| 2646 | } |
| 2647 | |
| 2648 | /// Reads a four-byte big-endian number out of a box body. |
| 2649 | fn be32(bytes: &[u8], at: usize) -> Outcome<u32> { |
| 2650 | match bytes.get(at..at + 4) { |
| 2651 | Some(s) => Ok(u32::from_be_bytes([s[0], s[1], s[2], s[3]])), |
| 2652 | None => Err(err!("A box ends inside a four-byte field."; Invalid, Input, Decode)), |
| 2653 | } |
| 2654 | } |
| 2655 | |
| 2656 | /// Reads an eight-byte big-endian number out of a box body. |
| 2657 | fn be64(bytes: &[u8], at: usize) -> Outcome<u64> { |
| 2658 | match bytes.get(at..at + 8) { |
| 2659 | Some(s) => Ok(u64::from_be_bytes( |
| 2660 | [s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7]])), |
| 2661 | None => Err(err!("A box ends inside an eight-byte field."; Invalid, Input, Decode)), |
| 2662 | } |
| 2663 | } |
| 2664 | |
| 2665 | /// The tables one track's sample table holds, before they are turned into sample positions. |
| 2666 | #[derive(Default)] |
| 2667 | struct Tables { |
| 2668 | handler: [u8; 4], // vide for a video track |
| 2669 | stsd: Option<(usize, usize)>, // read once the handler says this is video |
| 2670 | kind: Option<Kind>, // the codec, from the sample entry |
| 2671 | config: Option<(usize, usize)>, // the configuration record's span in the file |
| 2672 | clap: Option<(f64, f64, f64, f64)>, // width, height, offsets of the centre |
| 2673 | size: (u16, u16), // the coded size the sample entry declares |
| 2674 | sizes: Vec<u32>, // each sample's length in bytes, from stsz |
| 2675 | runs: Vec<(u32, u32)>, // (first_chunk, samples_per_chunk) from stsc |
| 2676 | chunks: Vec<u64>, // each chunk's offset, from stco or co64 |
| 2677 | sync: Vec<u32>, // sync sample numbers, from one as stss writes them |
| 2678 | rotation: u16, // from the track header's matrix, degrees clockwise |
| 2679 | } |
| 2680 | |
| 2681 | /// Reads the first video track out of a `moov` box. |
| 2682 | fn movie(bytes: &[u8], from: usize, to: usize) -> Outcome<Option<Film>> { |
| 2683 | let mut out: Option<Film> = None; |
| 2684 | let mut at = from; |
| 2685 | while at + 8 <= to { |
| 2686 | let (size, head) = res!(box_head(bytes, at, to)); |
| 2687 | if &bytes[at + 4..at + 8] == b"trak" { |
| 2688 | let mut t = Tables::default(); |
| 2689 | res!(track(bytes, at + head, at + size, &mut t)); |
| 2690 | if &t.handler == b"vide" { |
| 2691 | if let Some((body, end)) = t.stsd { |
| 2692 | res!(sample_description(bytes, body, end, &mut t)); |
| 2693 | } |
| 2694 | out = Some(res!(assemble(bytes, t))); |
| 2695 | break; |
| 2696 | } |
| 2697 | } |
| 2698 | at += size; |
| 2699 | } |
| 2700 | Ok(out) |
| 2701 | } |
| 2702 | |
| 2703 | /// Reads one track's handler and sample table. |
| 2704 | fn track(bytes: &[u8], from: usize, to: usize, t: &mut Tables) -> Outcome<()> { |
| 2705 | walk(bytes, *b"trak", from, to, 0, &mut |kind, parent, body, end| { |
| 2706 | match &kind { |
| 2707 | b"mdia" | b"minf" | b"stbl" => return Ok(true), |
| 2708 | b"hdlr" => { |
| 2709 | // version and flags, then a pre-defined word, then the handler type. |
| 2710 | // |
| 2711 | // **Only the one directly inside `mdia`.** QuickTime puts a second `hdlr` inside |
| 2712 | // `minf` naming the *data* handler -- `alis` for a file, `url ` for a reference -- |
| 2713 | // and a walk that takes whichever it meets last decides a video track is not one. |
| 2714 | // That is how a 2003 camcorder's film comes to be refused as having no video in it. |
| 2715 | if &parent == b"mdia" { |
| 2716 | if let Some(s) = bytes.get(body + 8..body + 12) { |
| 2717 | t.handler.copy_from_slice(s); |
| 2718 | } |
| 2719 | } |
| 2720 | }, |
| 2721 | b"tkhd" => { |
| 2722 | t.rotation = rotation_of(&bytes[body..end.min(bytes.len())]); |
| 2723 | }, |
| 2724 | b"stsd" => { |
| 2725 | // Not read here. A track's boxes arrive in whatever order the writer chose, and a |
| 2726 | // sound track's sample entry is not a visual one; reading every `stsd` as though it |
| 2727 | // were refuses a film for the shape of a track nobody asked about. |
| 2728 | t.stsd = Some((body, end)); |
| 2729 | }, |
| 2730 | b"stsz" => { |
| 2731 | let sample_size = res!(be32(bytes, body + 4)); |
| 2732 | let count = res!(be32(bytes, body + 8)) as usize; |
| 2733 | if count > MAX_SAMPLES { |
| 2734 | return Err(err!( |
| 2735 | "A track holds {} samples, and {} is this reader's ceiling.", |
| 2736 | count, MAX_SAMPLES; |
| 2737 | Invalid, Input, Excessive)); |
| 2738 | } |
| 2739 | t.sizes = if sample_size != 0 { |
| 2740 | vec![sample_size; count] |
| 2741 | } else { |
| 2742 | let mut v = Vec::with_capacity(count); |
| 2743 | for i in 0..count { |
| 2744 | v.push(res!(be32(bytes, body + 12 + i * 4))); |
| 2745 | } |
| 2746 | v |
| 2747 | }; |
| 2748 | }, |
| 2749 | b"stsc" => { |
| 2750 | let count = res!(be32(bytes, body + 4)) as usize; |
| 2751 | if count > MAX_SAMPLES { |
| 2752 | return Err(err!( |
| 2753 | "A sample-to-chunk table holds {} runs, and {} is this reader's ceiling.", |
| 2754 | count, MAX_SAMPLES; |
| 2755 | Invalid, Input, Excessive)); |
| 2756 | } |
| 2757 | t.runs = Vec::with_capacity(count); |
| 2758 | for i in 0..count { |
| 2759 | let first = res!(be32(bytes, body + 8 + i * 12)); |
| 2760 | let per = res!(be32(bytes, body + 12 + i * 12)); |
| 2761 | t.runs.push((first, per)); |
| 2762 | } |
| 2763 | }, |
| 2764 | b"stco" | b"co64" => { |
| 2765 | let wide = &kind == b"co64"; |
| 2766 | let count = res!(be32(bytes, body + 4)) as usize; |
| 2767 | if count > MAX_SAMPLES { |
| 2768 | return Err(err!( |
| 2769 | "A chunk offset table holds {} entries, and {} is this reader's ceiling.", |
| 2770 | count, MAX_SAMPLES; |
| 2771 | Invalid, Input, Excessive)); |
| 2772 | } |
| 2773 | t.chunks = Vec::with_capacity(count); |
| 2774 | for i in 0..count { |
| 2775 | t.chunks.push(if wide { |
| 2776 | res!(be64(bytes, body + 8 + i * 8)) |
| 2777 | } else { |
| 2778 | res!(be32(bytes, body + 8 + i * 4)) as u64 |
| 2779 | }); |
| 2780 | } |
| 2781 | }, |
| 2782 | b"stss" => { |
| 2783 | let count = res!(be32(bytes, body + 4)) as usize; |
| 2784 | if count > MAX_SAMPLES { |
| 2785 | return Err(err!( |
| 2786 | "A sync sample table holds {} entries, and {} is this reader's ceiling.", |
| 2787 | count, MAX_SAMPLES; |
| 2788 | Invalid, Input, Excessive)); |
| 2789 | } |
| 2790 | t.sync = Vec::with_capacity(count); |
| 2791 | for i in 0..count { |
| 2792 | t.sync.push(res!(be32(bytes, body + 8 + i * 4))); |
| 2793 | } |
| 2794 | }, |
| 2795 | _ => {}, |
| 2796 | } |
| 2797 | Ok(false) |
| 2798 | }) |
| 2799 | } |
| 2800 | |
| 2801 | /// How far a track header's transformation matrix turns the picture, in degrees clockwise. |
| 2802 | /// |
| 2803 | /// A phone writes the angle it was held at here rather than turning the samples, so this is what a |
| 2804 | /// viewer must do with a decoder's output. The matrix sits at a fixed offset that depends on the |
| 2805 | /// version -- the version-1 header carries 64-bit times and is twelve bytes longer -- and only the |
| 2806 | /// four entries that rotate are read, because a matrix that is not a rotation is not something a |
| 2807 | /// picture library can act on anyway. Anything else answers nought, which shows the picture as it |
| 2808 | /// was coded. |
| 2809 | /// |
| 2810 | /// `tkhd` is the box's payload, from its version byte onwards. |
| 2811 | pub fn rotation_of(tkhd: &[u8]) -> u16 { |
| 2812 | let ver = match tkhd.first() { |
| 2813 | Some(v) => *v, |
| 2814 | None => return 0, |
| 2815 | }; |
| 2816 | let at = if ver == 1 { 52 } else { 40 }; |
| 2817 | // The matrix is nine values in the order a, b, u, c, d, v, x, y, w (§8.3.2.3), and the four |
| 2818 | // that rotate are a, b, c and d -- which are **not** the first four: the projection entry `u` |
| 2819 | // sits between b and c. Reading four in a row instead takes `u` for `c`, and since `u` is |
| 2820 | // nought in every matrix any camera writes, every rotation then looks like no rotation at all. |
| 2821 | let one = 0x0001_0000i32; |
| 2822 | let mut m = [0i32; 4]; |
| 2823 | for (i, off) in [0usize, 4, 12, 16].iter().enumerate() { |
| 2824 | m[i] = match tkhd.get(at + off..at + off + 4) { |
| 2825 | Some(s) => i32::from_be_bytes([s[0], s[1], s[2], s[3]]), |
| 2826 | None => return 0, |
| 2827 | }; |
| 2828 | } |
| 2829 | match (m[0], m[1], m[2], m[3]) { |
| 2830 | (0, x, y, 0) if x == one && y == -one => 90, |
| 2831 | (x, 0, 0, y) if x == -one && y == -one => 180, |
| 2832 | (0, x, y, 0) if x == -one && y == one => 270, |
| 2833 | _ => 0, |
| 2834 | } |
| 2835 | } |
| 2836 | |
| 2837 | /// Reads the first sample entry of a sample description, and the configuration record inside it. |
| 2838 | fn sample_description(bytes: &[u8], body: usize, end: usize, t: &mut Tables) -> Outcome<()> { |
| 2839 | let count = res!(be32(bytes, body + 4)); |
| 2840 | if count == 0 { |
| 2841 | return Ok(()); |
| 2842 | } |
| 2843 | let at = body + 8; |
| 2844 | let (size, head) = res!(box_head(bytes, at, end)); |
| 2845 | let mut code = [0u8; 4]; |
| 2846 | code.copy_from_slice(&bytes[at + 4..at + 8]); |
| 2847 | t.kind = Some(Kind::of(code)); |
| 2848 | // A visual sample entry: six reserved bytes and a two-byte data reference index, then 70 bytes |
| 2849 | // of visual fields, of which the width and height sit at 16 and 18 (ISO/IEC 14496-12 §8.5.2). |
| 2850 | let visual = at + head + 8; |
| 2851 | if visual + 70 > at + size { |
| 2852 | return Err(err!( |
| 2853 | "A {} sample entry is too short to be a visual one.", String::from_utf8_lossy(&code); |
| 2854 | Invalid, Input, Decode)); |
| 2855 | } |
| 2856 | t.size = ( |
| 2857 | u16::from_be_bytes([bytes[visual + 16], bytes[visual + 17]]), |
| 2858 | u16::from_be_bytes([bytes[visual + 18], bytes[visual + 19]]), |
| 2859 | ); |
| 2860 | // The configuration box sits among the sample entry's own children. |
| 2861 | let mut conf = None; |
| 2862 | let mut clap: Option<(f64, f64, f64, f64)> = None; |
| 2863 | res!(walk(bytes, code, visual + 70, at + size, 0, &mut |kind, _parent, cbody, cend| { |
| 2864 | if matches!(&kind, b"avcC" | b"hvcC") && conf.is_none() { |
| 2865 | conf = Some((cbody, cend)); |
| 2866 | } |
| 2867 | // The clean aperture: the rectangle of the coded picture that is the picture. A phone |
| 2868 | // stabilises a film by coding it larger than it shows and moving the window about inside |
| 2869 | // it, and this is where the result is written -- eight rationals, four of which are the |
| 2870 | // width and height and four the offset of the window's centre from the picture's |
| 2871 | // (ISO/IEC 14496-12 §12.1.4.3). |
| 2872 | if &kind == b"clap" && clap.is_none() && cend >= cbody + 32 { |
| 2873 | let mut v = [0i64; 8]; |
| 2874 | for (i, n) in v.iter_mut().enumerate() { |
| 2875 | *n = match bytes.get(cbody + i * 4..cbody + i * 4 + 4) { |
| 2876 | Some(s) => i32::from_be_bytes([s[0], s[1], s[2], s[3]]) as i64, |
| 2877 | None => return Ok(false), |
| 2878 | }; |
| 2879 | } |
| 2880 | // The denominators are the odd entries, and a zero one is a box to be ignored rather |
| 2881 | // than divided by. |
| 2882 | if v[1] != 0 && v[3] != 0 && v[5] != 0 && v[7] != 0 { |
| 2883 | clap = Some(( |
| 2884 | v[0] as f64 / v[1] as f64, |
| 2885 | v[2] as f64 / v[3] as f64, |
| 2886 | v[4] as f64 / v[5] as f64, |
| 2887 | v[6] as f64 / v[7] as f64, |
| 2888 | )); |
| 2889 | } |
| 2890 | } |
| 2891 | Ok(false) |
| 2892 | })); |
| 2893 | t.config = conf; |
| 2894 | t.clap = clap; |
| 2895 | Ok(()) |
| 2896 | } |
| 2897 | |
| 2898 | /// Turns a sample table into the position and length of every sample. |
| 2899 | fn assemble(bytes: &[u8], t: Tables) -> Outcome<Film> { |
| 2900 | let kind = match t.kind { |
| 2901 | Some(k) => k, |
| 2902 | None => return Err(err!( |
| 2903 | "A video track carries no sample description, so nothing says how it is coded."; |
| 2904 | Invalid, Input, Missing)), |
| 2905 | }; |
| 2906 | if t.runs.is_empty() || t.chunks.is_empty() { |
| 2907 | return Err(err!( |
| 2908 | "A video track's sample table has no sample-to-chunk or chunk offset box, so no \ |
| 2909 | sample can be located."; |
| 2910 | Invalid, Input, Missing)); |
| 2911 | } |
| 2912 | // Walk the runs, laying samples into chunks. `stsc` names the first chunk of each run counted |
| 2913 | // from one, and a run continues until the next one begins (ISO/IEC 14496-12 §8.7.4). |
| 2914 | let mut samples: Vec<(u64, u32)> = Vec::with_capacity(t.sizes.len()); |
| 2915 | let mut n = 0usize; |
| 2916 | for (r, (first, per)) in t.runs.iter().enumerate() { |
| 2917 | if *first == 0 { |
| 2918 | return Err(err!( |
| 2919 | "A sample-to-chunk run begins at chunk 0, and the chunks are counted from one."; |
| 2920 | Invalid, Input, Decode)); |
| 2921 | } |
| 2922 | let start = (*first - 1) as usize; |
| 2923 | let stop = match t.runs.get(r + 1) { |
| 2924 | Some((next, _)) if *next >= 1 => ((*next - 1) as usize).min(t.chunks.len()), |
| 2925 | _ => t.chunks.len(), |
| 2926 | }; |
| 2927 | for c in start..stop { |
| 2928 | let base = t.chunks[c]; |
| 2929 | let mut off = base; |
| 2930 | for _ in 0..*per { |
| 2931 | let len = match t.sizes.get(n) { |
| 2932 | Some(l) => *l, |
| 2933 | // A chunk table that runs on past the sample sizes is not a fault: the sizes |
| 2934 | // are the authority on how many samples there are. |
| 2935 | None => break, |
| 2936 | }; |
| 2937 | samples.push((off, len)); |
| 2938 | off = off.saturating_add(len as u64); |
| 2939 | n += 1; |
| 2940 | } |
| 2941 | } |
| 2942 | } |
| 2943 | if samples.len() != t.sizes.len() { |
| 2944 | return Err(err!( |
| 2945 | "A sample table lays out {} samples and names the size of {}. The two disagree, so \ |
| 2946 | no sample can be trusted to be where the table says.", samples.len(), t.sizes.len(); |
| 2947 | Invalid, Input, Mismatch)); |
| 2948 | } |
| 2949 | let config = match t.config { |
| 2950 | Some((from, to)) => match bytes.get(from..to) { |
| 2951 | Some(s) => s.to_vec(), |
| 2952 | None => return Err(err!( |
| 2953 | "A decoder configuration record runs past the end of the file."; |
| 2954 | Invalid, Input, Decode)), |
| 2955 | }, |
| 2956 | // Motion JPEG has none, and needs none. |
| 2957 | None => Vec::new(), |
| 2958 | }; |
| 2959 | // `stss` counts from one and everything else here counts from nought. |
| 2960 | let mut sync = Vec::with_capacity(t.sync.len()); |
| 2961 | for s in &t.sync { |
| 2962 | if *s == 0 { |
| 2963 | return Err(err!( |
| 2964 | "A sync sample table names sample 0, and the samples are counted from one."; |
| 2965 | Invalid, Input, Decode)); |
| 2966 | } |
| 2967 | sync.push(*s - 1); |
| 2968 | } |
| 2969 | // The aperture is stated as a size and the offset of its centre from the picture's, so the |
| 2970 | // corner it starts at is worked out here rather than by every caller. |
| 2971 | let aperture = t.clap.and_then(|(w, h, dx, dy)| { |
| 2972 | let (full_w, full_h) = (t.size.0 as f64, t.size.1 as f64); |
| 2973 | if w <= 0.0 || h <= 0.0 || w > full_w || h > full_h { |
| 2974 | return None; |
| 2975 | } |
| 2976 | let x = ((full_w - w) / 2.0 + dx).round(); |
| 2977 | let y = ((full_h - h) / 2.0 + dy).round(); |
| 2978 | if x < 0.0 || y < 0.0 || x + w > full_w || y + h > full_h { |
| 2979 | return None; |
| 2980 | } |
| 2981 | Some((x as u32, y as u32, w.round() as u32, h.round() as u32)) |
| 2982 | }); |
| 2983 | Ok(Film { |
| 2984 | kind, |
| 2985 | config, |
| 2986 | width: t.size.0, |
| 2987 | height: t.size.1, |
| 2988 | rotation: t.rotation, |
| 2989 | aperture, |
| 2990 | samples, |
| 2991 | sync, |
| 2992 | }) |
| 2993 | } |
| 2994 | |
| 2995 | #[cfg(test)] |
| 2996 | mod tests { |
| 2997 | use super::*; |
| 2998 | |
| 2999 | #[test] |
| 3000 | fn test_a_quarter_turn_in_a_track_header_is_read_00() -> Outcome<()> { |
| 3001 | // The four entries that rotate are a, b, c and d, and they are not four in a row: the |
| 3002 | // projection entry `u` sits between b and c, and it is nought in every matrix a camera |
| 3003 | // writes. A reader that takes four in a row therefore answers "no rotation" for every |
| 3004 | // turned film there is, which is what this decoder did until a film held sideways said so. |
| 3005 | let one = 0x0001_0000u32; |
| 3006 | let neg = (-(one as i32)) as u32; |
| 3007 | let head = |a: u32, b: u32, c: u32, d: u32| { |
| 3008 | let mut body = vec![0u8; 84]; |
| 3009 | body[40..44].copy_from_slice(&a.to_be_bytes()); |
| 3010 | body[44..48].copy_from_slice(&b.to_be_bytes()); |
| 3011 | // 48 is `u`, and stays nought. |
| 3012 | body[52..56].copy_from_slice(&c.to_be_bytes()); |
| 3013 | body[56..60].copy_from_slice(&d.to_be_bytes()); |
| 3014 | body |
| 3015 | }; |
| 3016 | req!(rotation_of(&head(0, one, neg, 0)), 90u16, "a quarter turn clockwise"); |
| 3017 | req!(rotation_of(&head(neg, 0, 0, neg)), 180u16, "a half turn"); |
| 3018 | req!(rotation_of(&head(0, neg, one, 0)), 270u16, "three quarters clockwise"); |
| 3019 | req!(rotation_of(&head(one, 0, 0, one)), 0u16, "unity is no turn"); |
| 3020 | // A matrix that is not a rotation is shown as it was coded rather than guessed at. |
| 3021 | req!(rotation_of(&head(one, one, one, one)), 0u16, "a matrix that is not a rotation"); |
| 3022 | req!(rotation_of(&[]), 0u16, "an empty header"); |
| 3023 | Ok(()) |
| 3024 | } |
| 3025 | |
| 3026 | /// The sequence parameter set of a 64 by 48 stream, as libx264 wrote it: the NAL unit that |
| 3027 | /// followed the first start code of |
| 3028 | /// `ffmpeg -f lavfi -i testsrc=size=64x48:rate=10 -c:v libx264 -f h264`. |
| 3029 | /// |
| 3030 | /// It carries an emulation prevention byte -- the `03` in `00 00 03 00` at offset 11 -- so |
| 3031 | /// reading its geometry exercises the unescaping as well as the bit reader. |
| 3032 | const SPS: [u8; 21] = [ |
| 3033 | 0x67, 0x42, 0xC0, 0x0A, 0xDA, 0x11, 0xEC, 0x04, 0x40, 0x00, 0x00, |
| 3034 | 0x03, 0x00, 0x40, 0x00, 0x00, 0x05, 0x03, 0xC4, 0x89, 0xA8, |
| 3035 | ]; |
| 3036 | |
| 3037 | /// The matching picture parameter set. |
| 3038 | const PPS: [u8; 4] = [0x68, 0xCE, 0x0F, 0xC8]; |
| 3039 | |
| 3040 | /// An `AVCDecoderConfigurationRecord` around the fixture parameter sets, with a four-byte NAL |
| 3041 | /// length: version 1, the three profile bytes copied from the set, `0xFF` for the reserved bits |
| 3042 | /// and a length of four, `0xE1` for the reserved bits and one sequence parameter set. |
| 3043 | fn avcc() -> Vec<u8> { |
| 3044 | let mut rec = vec![1, SPS[1], SPS[2], SPS[3], 0xFF, 0xE1]; |
| 3045 | rec.extend_from_slice(&(SPS.len() as u16).to_be_bytes()); |
| 3046 | rec.extend_from_slice(&SPS); |
| 3047 | rec.push(1); |
| 3048 | rec.extend_from_slice(&(PPS.len() as u16).to_be_bytes()); |
| 3049 | rec.extend_from_slice(&PPS); |
| 3050 | rec |
| 3051 | } |
| 3052 | |
| 3053 | /// A sample of `n` bytes, wrapped as one NAL unit with a four-byte length prefix. |
| 3054 | fn nal(n: usize) -> Vec<u8> { |
| 3055 | let mut v = ((n as u32).to_be_bytes()).to_vec(); |
| 3056 | v.extend(std::iter::repeat(0x41).take(n)); |
| 3057 | v |
| 3058 | } |
| 3059 | |
| 3060 | /// Finds the first box of the given type at the top level of `buf`, giving its body. |
| 3061 | fn top(buf: &[u8], kind: &[u8; 4]) -> Option<Vec<u8>> { |
| 3062 | let mut at = 0usize; |
| 3063 | while at + 8 <= buf.len() { |
| 3064 | let size = u32::from_be_bytes([buf[at], buf[at + 1], buf[at + 2], buf[at + 3]]) as usize; |
| 3065 | if size < 8 || at + size > buf.len() { |
| 3066 | return None; |
| 3067 | } |
| 3068 | if &buf[at + 4..at + 8] == kind { |
| 3069 | return Some(buf[at + 8..at + size].to_vec()); |
| 3070 | } |
| 3071 | at += size; |
| 3072 | } |
| 3073 | None |
| 3074 | } |
| 3075 | |
| 3076 | /// Finds the first box of the given type anywhere in `buf`, giving the offset of its body, and |
| 3077 | /// fails the test where there is none. |
| 3078 | fn want_box(buf: &[u8], kind: &[u8; 4]) -> Outcome<usize> { |
| 3079 | match buf.windows(4).position(|w| w == kind) { |
| 3080 | Some(i) => Ok(i + 4), |
| 3081 | None => Err(err!( |
| 3082 | "No '{}' box was written.", String::from_utf8_lossy(kind); Test, Missing)), |
| 3083 | } |
| 3084 | } |
| 3085 | |
| 3086 | /// The sequence parameter set of a 64 by 48 stream reads back as 64 by 48. The value is not |
| 3087 | /// this crate's: it is the size given to FFmpeg on the command line that produced the bytes. |
| 3088 | #[test] |
| 3089 | fn test_sps_geometry_00() -> Outcome<()> { |
| 3090 | let (w, h) = res!(sps_geometry(&SPS)); |
| 3091 | req!(w, 64u16); |
| 3092 | req!(h, 48u16); |
| 3093 | Ok(()) |
| 3094 | } |
| 3095 | |
| 3096 | /// The geometry read through the configuration record is the same as the geometry read from the |
| 3097 | /// set directly, and the record's NAL length field says four. |
| 3098 | #[test] |
| 3099 | fn test_avcc_walk_01() -> Outcome<()> { |
| 3100 | let c = Codec::Avc(avcc()); |
| 3101 | req!(res!(c.geometry()), (64u16, 48u16)); |
| 3102 | req!(res!(c.nal_len()), 4usize); |
| 3103 | Ok(()) |
| 3104 | } |
| 3105 | |
| 3106 | /// Every field of the header boxes has a size the specification fixes, so the boxes have sizes |
| 3107 | /// that can be added up by hand. |
| 3108 | /// |
| 3109 | /// `ftyp`: 8 header + 4 major brand + 4 minor version + 4 compatible brands of 4 = 32. |
| 3110 | /// `mvhd`: 8 header + 4 version and flags + 4 + 4 + 4 + 4 times and scales + 4 rate + 2 volume |
| 3111 | /// + 2 reserved + 8 reserved + 36 matrix + 24 pre-defined + 4 next track = 108. |
| 3112 | /// `tkhd`: 8 + 4 + 4 + 4 + 4 + 4 + 4 + 8 + 2 + 2 + 2 + 2 + 36 + 4 + 4 = 92. |
| 3113 | /// `mdhd`: 8 + 4 + 4 + 4 + 4 + 4 + 2 + 2 = 32. |
| 3114 | /// `hdlr`: 8 + 4 + 4 + 4 + 12 + 13 for "VideoHandler" and its terminator = 45. |
| 3115 | /// `vmhd`: 8 + 4 + 2 + 6 = 20. |
| 3116 | /// `stsc`: 8 + 4 + 4 count + 12 for the one entry = 28. |
| 3117 | #[test] |
| 3118 | fn test_fixed_box_sizes_02() -> Outcome<()> { |
| 3119 | // Four brands for either picture coding, so the size is the same and it |
| 3120 | // is the third brand that differs; sound alone drops one and is shorter. |
| 3121 | req!(res!(ftyp(&Codec::Avc(avcc()))).len(), 32usize); |
| 3122 | req!(res!(ftyp(&Codec::Hevc(hvcc(&[(hevc::nal::SPS, &HEVC_SPS[2..])])))).len(), 32usize); |
| 3123 | req!(res!(ftyp(&Codec::Aac(vec![0x11, 0x90]))).len(), 28usize); |
| 3124 | req!(res!(hdlr()).len(), 45usize); |
| 3125 | req!(res!(vmhd()).len(), 20usize); |
| 3126 | req!(res!(stsc()).len(), 28usize); |
| 3127 | |
| 3128 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3129 | res!(t.push(Sample::key(nal(10), 100))); |
| 3130 | req!(res!(t.mvhd(100)).len(), 108usize); |
| 3131 | req!(res!(t.tkhd(100)).len(), 92usize); |
| 3132 | req!(res!(t.mdhd()).len(), 32usize); |
| 3133 | Ok(()) |
| 3134 | } |
| 3135 | |
| 3136 | /// `stts` merges consecutive samples of equal duration into one entry. |
| 3137 | /// |
| 3138 | /// Durations 10, 10, 10, 20, 20, 10 give three entries -- (3, 10), (2, 20), (1, 10) -- so the |
| 3139 | /// box is 8 header + 4 version and flags + 4 entry count + 3 times 8 = 40 bytes, and the last |
| 3140 | /// entry's count is 1. |
| 3141 | #[test] |
| 3142 | fn test_stts_run_length_03() -> Outcome<()> { |
| 3143 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3144 | for (i, d) in [10u32, 10, 10, 20, 20, 10].into_iter().enumerate() { |
| 3145 | res!(t.push(Sample { data: nal(4 + i), dur: d, sync: i == 0, off: 0 })); |
| 3146 | } |
| 3147 | let b = res!(t.stts()); |
| 3148 | req!(b.len(), 40usize); |
| 3149 | req!(&b[4..8], b"stts" as &[u8]); |
| 3150 | req!(u32::from_be_bytes([b[12], b[13], b[14], b[15]]), 3u32); |
| 3151 | let entries: Vec<(u32, u32)> = b[16..].chunks_exact(8) |
| 3152 | .map(|c| ( |
| 3153 | u32::from_be_bytes([c[0], c[1], c[2], c[3]]), |
| 3154 | u32::from_be_bytes([c[4], c[5], c[6], c[7]]), |
| 3155 | )) |
| 3156 | .collect(); |
| 3157 | req!(entries, vec![(3u32, 10u32), (2, 20), (1, 10)]); |
| 3158 | Ok(()) |
| 3159 | } |
| 3160 | |
| 3161 | /// A run of samples of one duration gives exactly one `stts` entry, however many there are. |
| 3162 | #[test] |
| 3163 | fn test_stts_constant_rate_is_one_entry_04() -> Outcome<()> { |
| 3164 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3165 | for i in 0..500 { |
| 3166 | res!(t.push(Sample { data: nal(4), dur: 40, sync: i == 0, off: 0 })); |
| 3167 | } |
| 3168 | let b = res!(t.stts()); |
| 3169 | req!(b.len(), 24usize); |
| 3170 | req!(u32::from_be_bytes([b[12], b[13], b[14], b[15]]), 1u32); |
| 3171 | req!(u32::from_be_bytes([b[16], b[17], b[18], b[19]]), 500u32); |
| 3172 | Ok(()) |
| 3173 | } |
| 3174 | |
| 3175 | /// `stss` is absent where every sample is a sync sample, since the specification reads that |
| 3176 | /// absence as exactly that claim, and present listing one-based sample numbers otherwise. |
| 3177 | #[test] |
| 3178 | fn test_stss_presence_05() -> Outcome<()> { |
| 3179 | let mut all = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3180 | for _ in 0..3 { |
| 3181 | res!(all.push(Sample::key(nal(4), 10))); |
| 3182 | } |
| 3183 | req!(res!(all.stss()).is_none(), true); |
| 3184 | |
| 3185 | let mut some = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3186 | for i in 0..5 { |
| 3187 | res!(some.push(Sample { data: nal(4), dur: 10, sync: i == 0 || i == 3, off: 0 })); |
| 3188 | } |
| 3189 | let b = match res!(some.stss()) { |
| 3190 | Some(b) => b, |
| 3191 | None => return Err(err!( |
| 3192 | "The sync sample table should have been written."; Test)), |
| 3193 | }; |
| 3194 | // 8 header + 4 version and flags + 4 count + 2 entries of 4 = 24. |
| 3195 | req!(b.len(), 24usize); |
| 3196 | req!(u32::from_be_bytes([b[12], b[13], b[14], b[15]]), 2u32); |
| 3197 | req!(u32::from_be_bytes([b[16], b[17], b[18], b[19]]), 1u32); |
| 3198 | req!(u32::from_be_bytes([b[20], b[21], b[22], b[23]]), 4u32); |
| 3199 | Ok(()) |
| 3200 | } |
| 3201 | |
| 3202 | /// Every chunk offset names the first byte of its sample, and the first names the first byte |
| 3203 | /// after the `mdat` header. |
| 3204 | #[test] |
| 3205 | fn test_chunk_offsets_point_at_the_samples_06() -> Outcome<()> { |
| 3206 | let sizes = [11usize, 5, 23, 7]; |
| 3207 | let mut t = res!(Track::new(64, 48, 600, Codec::Avc(avcc()))); |
| 3208 | for (i, n) in sizes.into_iter().enumerate() { |
| 3209 | res!(t.push(Sample { data: nal(n), dur: 60, sync: i == 0, off: 0 })); |
| 3210 | } |
| 3211 | let file = res!(t.finish()); |
| 3212 | |
| 3213 | let at = res!(want_box(&file, b"stco")); |
| 3214 | let count = u32::from_be_bytes([file[at + 4], file[at + 5], file[at + 6], file[at + 7]]); |
| 3215 | req!(count, 4u32); |
| 3216 | |
| 3217 | let mdat = res!(want_box(&file, b"mdat")); |
| 3218 | let mut want = mdat as u32; |
| 3219 | for (i, n) in sizes.into_iter().enumerate() { |
| 3220 | let o = at + 8 + i * 4; |
| 3221 | let got = u32::from_be_bytes([file[o], file[o + 1], file[o + 2], file[o + 3]]); |
| 3222 | req!(got, want); |
| 3223 | // The sample's own first byte is its NAL length field, which is what was written. |
| 3224 | let len = u32::from_be_bytes([ |
| 3225 | file[got as usize], file[got as usize + 1], |
| 3226 | file[got as usize + 2], file[got as usize + 3], |
| 3227 | ]); |
| 3228 | req!(len, n as u32); |
| 3229 | want += (n + 4) as u32; |
| 3230 | } |
| 3231 | req!(want as usize, file.len()); |
| 3232 | Ok(()) |
| 3233 | } |
| 3234 | |
| 3235 | /// The top-level layout is `ftyp`, then `moov`, then `mdat`, and the three account for the |
| 3236 | /// whole file. |
| 3237 | #[test] |
| 3238 | fn test_top_level_layout_07() -> Outcome<()> { |
| 3239 | let mut t = res!(Track::new(64, 48, 90_000, Codec::Avc(avcc()))); |
| 3240 | res!(t.push(Sample::key(nal(30), 3000))); |
| 3241 | res!(t.push(Sample::delta(nal(9), 3000))); |
| 3242 | let file = res!(t.finish()); |
| 3243 | |
| 3244 | let mut kinds = Vec::new(); |
| 3245 | let mut at = 0usize; |
| 3246 | while at + 8 <= file.len() { |
| 3247 | let size = u32::from_be_bytes([file[at], file[at + 1], file[at + 2], file[at + 3]]) |
| 3248 | as usize; |
| 3249 | kinds.push(String::from_utf8_lossy(&file[at + 4..at + 8]).to_string()); |
| 3250 | at += size; |
| 3251 | } |
| 3252 | req!(at, file.len()); |
| 3253 | req!(kinds, vec!["ftyp".to_string(), "moov".to_string(), "mdat".to_string()]); |
| 3254 | |
| 3255 | // The media box holds exactly the samples: two NAL units of 30 and 9 bytes, each with a |
| 3256 | // four-byte length in front of it. |
| 3257 | let mdat = match top(&file, b"mdat") { |
| 3258 | Some(b) => b, |
| 3259 | None => return Err(err!("No media box was written."; Test)), |
| 3260 | }; |
| 3261 | req!(mdat.len(), 47usize); |
| 3262 | Ok(()) |
| 3263 | } |
| 3264 | |
| 3265 | /// The media header keeps the track's timescale, and the movie header restates the same |
| 3266 | /// duration in milliseconds. |
| 3267 | /// |
| 3268 | /// Thirty samples of 3003 ticks at 90000 a second is 90090 ticks, which is 1001 milliseconds. |
| 3269 | #[test] |
| 3270 | fn test_durations_in_their_own_timescales_08() -> Outcome<()> { |
| 3271 | let mut t = res!(Track::new(64, 48, 90_000, Codec::Avc(avcc()))); |
| 3272 | for i in 0..30 { |
| 3273 | res!(t.push(Sample { data: nal(6), dur: 3003, sync: i == 0, off: 0 })); |
| 3274 | } |
| 3275 | req!(t.duration(), 90_090u64); |
| 3276 | let file = res!(t.finish()); |
| 3277 | |
| 3278 | let mdhd = res!(want_box(&file, b"mdhd")); |
| 3279 | req!(u32::from_be_bytes([ |
| 3280 | file[mdhd + 12], file[mdhd + 13], file[mdhd + 14], file[mdhd + 15], |
| 3281 | ]), 90_000u32); |
| 3282 | req!(u32::from_be_bytes([ |
| 3283 | file[mdhd + 16], file[mdhd + 17], file[mdhd + 18], file[mdhd + 19], |
| 3284 | ]), 90_090u32); |
| 3285 | |
| 3286 | let mvhd = res!(want_box(&file, b"mvhd")); |
| 3287 | req!(u32::from_be_bytes([ |
| 3288 | file[mvhd + 12], file[mvhd + 13], file[mvhd + 14], file[mvhd + 15], |
| 3289 | ]), 1000u32); |
| 3290 | req!(u32::from_be_bytes([ |
| 3291 | file[mvhd + 16], file[mvhd + 17], file[mvhd + 18], file[mvhd + 19], |
| 3292 | ]), 1001u32); |
| 3293 | Ok(()) |
| 3294 | } |
| 3295 | |
| 3296 | /// The track header carries the unity matrix and the frame size in 16.16 fixed point. |
| 3297 | #[test] |
| 3298 | fn test_tkhd_matrix_and_size_09() -> Outcome<()> { |
| 3299 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3300 | res!(t.push(Sample::key(nal(8), 40))); |
| 3301 | let b = res!(t.tkhd(40)); |
| 3302 | // 8 header + 4 version and flags + 36 fixed fields brings the matrix to offset 48. |
| 3303 | let m: Vec<u32> = b[48..84].chunks_exact(4) |
| 3304 | .map(|c| u32::from_be_bytes([c[0], c[1], c[2], c[3]])) |
| 3305 | .collect(); |
| 3306 | req!(m, vec![0x0001_0000u32, 0, 0, 0, 0x0001_0000, 0, 0, 0, 0x4000_0000]); |
| 3307 | req!(u32::from_be_bytes([b[84], b[85], b[86], b[87]]), 64u32 << 16); |
| 3308 | req!(u32::from_be_bytes([b[88], b[89], b[90], b[91]]), 48u32 << 16); |
| 3309 | Ok(()) |
| 3310 | } |
| 3311 | |
| 3312 | /// A track that names dimensions the stream does not code is refused, and the message names |
| 3313 | /// both. |
| 3314 | #[test] |
| 3315 | fn test_refuses_a_geometry_mismatch_10() -> Outcome<()> { |
| 3316 | match Track::new(1920, 1080, 1000, Codec::Avc(avcc())) { |
| 3317 | Ok(_) => Err(err!("A 1920 by 1080 track over a 64 by 48 stream was accepted."; Test)), |
| 3318 | Err(e) => { |
| 3319 | let msg = e.to_string(); |
| 3320 | req!(msg.contains("1920"), true, "The message does not name the declared width."); |
| 3321 | req!(msg.contains("64"), true, "The message does not name the coded width."); |
| 3322 | Ok(()) |
| 3323 | }, |
| 3324 | } |
| 3325 | } |
| 3326 | |
| 3327 | /// A timescale of zero, a zero dimension, an empty track, a sample of no duration, a track that |
| 3328 | /// does not begin at a sync sample, and a sample handed over in Annex B are each refused. |
| 3329 | #[test] |
| 3330 | fn test_refusals_11() -> Outcome<()> { |
| 3331 | req!(Track::new(64, 48, 0, Codec::Avc(avcc())).is_err(), true, "A zero timescale passed."); |
| 3332 | req!(Track::new(0, 48, 1000, Codec::Avc(avcc())).is_err(), true, "A zero width passed."); |
| 3333 | |
| 3334 | let empty = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))).finish().is_err(); |
| 3335 | req!(empty, true, "A track with no samples passed."); |
| 3336 | |
| 3337 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3338 | req!(t.push(Sample::key(nal(4), 0)).is_err(), true, "A sample of no duration passed."); |
| 3339 | req!(t.push(Sample::key(Vec::new(), 40)).is_err(), true, "A sample of no bytes passed."); |
| 3340 | |
| 3341 | let mut annexb = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3342 | let e = annexb.push(Sample::key(vec![0, 0, 0, 1, 0x65, 0x88, 0x84], 40)); |
| 3343 | match e { |
| 3344 | Ok(_) => return Err(err!("An Annex B sample was accepted."; Test)), |
| 3345 | Err(e) => req!(e.to_string().contains("Annex B"), true, |
| 3346 | "The message does not name the format that was handed over."), |
| 3347 | } |
| 3348 | |
| 3349 | let mut short = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3350 | req!(short.push(Sample::key(vec![0x00, 0x00, 0x00, 0x40, 0x65], 40)).is_err(), true, |
| 3351 | "A sample whose NAL runs past its end passed."); |
| 3352 | |
| 3353 | let mut nokey = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3354 | res!(nokey.push(Sample::delta(nal(4), 40))); |
| 3355 | let begun = nokey.finish().is_err(); |
| 3356 | req!(begun, true, "A track that begins at a delta sample passed."); |
| 3357 | Ok(()) |
| 3358 | } |
| 3359 | |
| 3360 | /// A configuration record that is truncated, versioned wrongly, or names the forbidden |
| 3361 | /// three-byte NAL length is refused before a file is built around it. |
| 3362 | #[test] |
| 3363 | fn test_refuses_a_bad_configuration_record_12() -> Outcome<()> { |
| 3364 | let stub = Codec::Avc(vec![1, 66, 192, 10]).geometry().is_err(); |
| 3365 | req!(stub, true, "A stub record passed."); |
| 3366 | |
| 3367 | let mut bad_ver = avcc(); |
| 3368 | bad_ver[0] = 2; |
| 3369 | let bad_ver = Codec::Avc(bad_ver).geometry().is_err(); |
| 3370 | req!(bad_ver, true, "Version 2 passed."); |
| 3371 | |
| 3372 | let mut bad_len = avcc(); |
| 3373 | bad_len[4] = 0xFE; // lengthSizeMinusOne = 2, a three-byte length. |
| 3374 | let bad_len = Codec::Avc(bad_len).nal_len().is_err(); |
| 3375 | req!(bad_len, true, "A three-byte NAL length passed."); |
| 3376 | |
| 3377 | let mut cut = avcc(); |
| 3378 | cut.truncate(12); |
| 3379 | let cut = Codec::Avc(cut).geometry().is_err(); |
| 3380 | req!(cut, true, "A truncated parameter set passed."); |
| 3381 | |
| 3382 | let mut no_pps = vec![1, SPS[1], SPS[2], SPS[3], 0xFF, 0xE1]; |
| 3383 | no_pps.extend_from_slice(&(SPS.len() as u16).to_be_bytes()); |
| 3384 | no_pps.extend_from_slice(&SPS); |
| 3385 | no_pps.push(0); |
| 3386 | let no_pps = Codec::Avc(no_pps).geometry().is_err(); |
| 3387 | req!(no_pps, true, "A record with no picture set passed."); |
| 3388 | Ok(()) |
| 3389 | } |
| 3390 | |
| 3391 | /// Rescaling rounds to nearest rather than truncating, so a duration does not creep short. |
| 3392 | /// |
| 3393 | /// 90090 ticks at 90000 a second is 1001 milliseconds exactly; 1 tick at 3 a second is 333.33 |
| 3394 | /// milliseconds, which rounds to 333; 2 ticks at 3 is 666.67, which rounds to 667. |
| 3395 | #[test] |
| 3396 | fn test_rescale_rounds_to_nearest_13() -> Outcome<()> { |
| 3397 | req!(res!(rescale(90_090, 90_000, 1000)), 1001u64); |
| 3398 | req!(res!(rescale(1, 3, 1000)), 333u64); |
| 3399 | req!(res!(rescale(2, 3, 1000)), 667u64); |
| 3400 | req!(res!(rescale(0, 1000, 1000)), 0u64); |
| 3401 | req!(rescale(1, 0, 1000).is_err(), true, "A zero source timescale passed."); |
| 3402 | Ok(()) |
| 3403 | } |
| 3404 | |
| 3405 | /// The emulation prevention bytes come out and nothing else does: `00 00 03 00` is `00 00 00`, |
| 3406 | /// and a `03` that does not follow two zeroes is kept. |
| 3407 | #[test] |
| 3408 | fn test_rbsp_unescaping_14() -> Outcome<()> { |
| 3409 | req!(rbsp(&[0x00, 0x00, 0x03, 0x00, 0x01]), vec![0x00u8, 0x00, 0x00, 0x01]); |
| 3410 | req!(rbsp(&[0x01, 0x03, 0x02]), vec![0x01u8, 0x03, 0x02]); |
| 3411 | req!(rbsp(&[0x00, 0x00, 0x03, 0x03]), vec![0x00u8, 0x00, 0x03]); |
| 3412 | Ok(()) |
| 3413 | } |
| 3414 | |
| 3415 | /// The 64-bit chunk offset table is the same table in a wider field: the same entry count, eight |
| 3416 | /// bytes an entry rather than four, under the type `co64`. |
| 3417 | /// |
| 3418 | /// The choice between the two is made in `finish` by where the last byte of media falls, so it |
| 3419 | /// cannot be reached without building a file of four gibibytes. The table itself is written |
| 3420 | /// here directly instead, which is the part that would be wrong. |
| 3421 | #[test] |
| 3422 | fn test_the_64_bit_offset_table_16() -> Outcome<()> { |
| 3423 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3424 | for i in 0..3 { |
| 3425 | res!(t.push(Sample { data: nal(20), dur: 40, sync: i == 0, off: 0 })); |
| 3426 | } |
| 3427 | let base = 5_000_000_000u64; |
| 3428 | let b = res!(t.offsets(base, true)); |
| 3429 | // 8 header + 4 version and flags + 4 count + 3 entries of 8 = 40. |
| 3430 | req!(b.len(), 40usize); |
| 3431 | req!(&b[4..8], b"co64" as &[u8]); |
| 3432 | req!(u32::from_be_bytes([b[12], b[13], b[14], b[15]]), 3u32); |
| 3433 | let entries: Vec<u64> = b[16..].chunks_exact(8) |
| 3434 | .map(|c| u64::from_be_bytes([c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]])) |
| 3435 | .collect(); |
| 3436 | req!(entries, vec![base, base + 24, base + 48]); |
| 3437 | |
| 3438 | // The 32-bit table refuses the same offsets rather than truncating them. |
| 3439 | req!(t.offsets(base, false).is_err(), true, "A 32-bit table took a 5 GB offset."); |
| 3440 | Ok(()) |
| 3441 | } |
| 3442 | |
| 3443 | /// A file written twice from the same samples is the same file: the header times are written as |
| 3444 | /// unset rather than taken from the clock, so a build is reproducible. |
| 3445 | #[test] |
| 3446 | fn test_output_is_deterministic_15() -> Outcome<()> { |
| 3447 | let build = || -> Outcome<Vec<u8>> { |
| 3448 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3449 | for i in 0..8 { |
| 3450 | res!(t.push(Sample { data: nal(12 + i), dur: 40, sync: i % 4 == 0, off: 0 })); |
| 3451 | } |
| 3452 | t.finish() |
| 3453 | }; |
| 3454 | req!(res!(build()), res!(build())); |
| 3455 | Ok(()) |
| 3456 | } |
| 3457 | #[test] |
| 3458 | fn test_a_written_film_reads_back_17() -> Outcome<()> { |
| 3459 | // The two halves of this module, held to each other. The writer lays out a sample table and |
| 3460 | // the reader walks it back, and what they must agree on is the thing neither can check |
| 3461 | // alone: where each sample's bytes actually are. A chunk offset measured from the wrong |
| 3462 | // origin, or a sample-to-chunk run read as though its first chunk were counted from nought, |
| 3463 | // produces a perfectly well-formed index that points at the wrong bytes. |
| 3464 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3465 | let mut wrote: Vec<Vec<u8>> = Vec::new(); |
| 3466 | for i in 0..6usize { |
| 3467 | let data = nal(20 + i); |
| 3468 | wrote.push(data.clone()); |
| 3469 | res!(t.push(Sample { data, dur: 40, sync: i == 0, off: 0 })); |
| 3470 | } |
| 3471 | let file = res!(t.finish()); |
| 3472 | let film = res!(Film::read(&file)); |
| 3473 | req!(film.kind(), Kind::Avc); |
| 3474 | req!(film.samples(), wrote.len()); |
| 3475 | req!(film.size(), (64u16, 48u16)); |
| 3476 | for (i, want) in wrote.iter().enumerate() { |
| 3477 | let got = res!(film.sample(&file, i)); |
| 3478 | req!(got, &want[..], "sample {} came back from the wrong place", i); |
| 3479 | } |
| 3480 | // The first sample is the only sync sample, and a reader must begin there. |
| 3481 | req!(res!(film.first_sync()), 0usize); |
| 3482 | // A sample past the end is refused rather than answered. |
| 3483 | req!(film.span(wrote.len()).is_err(), true, "a sample past the end was handed out"); |
| 3484 | Ok(()) |
| 3485 | } |
| 3486 | |
| 3487 | #[test] |
| 3488 | fn test_a_track_that_is_turned_says_so_18() -> Outcome<()> { |
| 3489 | // The writer writes a unity matrix, so a film it wrote is shown as it was coded. What is |
| 3490 | // asserted here is the reading of the four entries that matter, because the fault this |
| 3491 | // guards against hides perfectly: a picture turned by ninety degrees has exactly as many |
| 3492 | // samples as one that is not, so a decoder and a viewer that disagree about the angle |
| 3493 | // produce output of the right size and the wrong shape. |
| 3494 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3495 | res!(t.push(Sample { data: nal(24), dur: 40, sync: true, off: 0 })); |
| 3496 | let mut file = res!(t.finish()); |
| 3497 | req!(res!(Film::read(&file)).rotation(), 0u16, "a unity matrix was read as a rotation"); |
| 3498 | |
| 3499 | // Turn it a quarter clockwise by writing the matrix a phone would: a = 0, b = 1, c = -1, |
| 3500 | // d = 0, in 16.16 fixed point. |
| 3501 | // |
| 3502 | // **The four are not four in a row.** The matrix is a, b, u, c, d, v, x, y, w, so c and d |
| 3503 | // are the fourth and fifth entries; this test used to write them third and fourth, which |
| 3504 | // is exactly where the reader used to look for them, so the two agreed with each other and |
| 3505 | // with no real film. Every rotated film in a library of seven thousand was read as |
| 3506 | // upright. The positions below are the specification's. |
| 3507 | let at = match file.windows(4).position(|w| w == b"tkhd") { |
| 3508 | Some(at) => at + 4 + 40, |
| 3509 | None => return Err(err!("the writer emitted no track header."; Test, Missing)), |
| 3510 | }; |
| 3511 | let put = |file: &mut Vec<u8>, i: usize, v: i32| { |
| 3512 | file[at + i * 4..at + i * 4 + 4].copy_from_slice(&(v as u32).to_be_bytes()); |
| 3513 | }; |
| 3514 | let one = 0x0001_0000i32; |
| 3515 | let matrix = |file: &mut Vec<u8>, a: i32, b: i32, c: i32, d: i32| { |
| 3516 | put(file, 0, a); |
| 3517 | put(file, 1, b); |
| 3518 | put(file, 3, c); |
| 3519 | put(file, 4, d); |
| 3520 | }; |
| 3521 | matrix(&mut file, 0, one, -one, 0); |
| 3522 | req!(res!(Film::read(&file)).rotation(), 90u16); |
| 3523 | // And a half turn. |
| 3524 | matrix(&mut file, -one, 0, 0, -one); |
| 3525 | req!(res!(Film::read(&file)).rotation(), 180u16); |
| 3526 | // And three quarters. |
| 3527 | matrix(&mut file, 0, -one, one, 0); |
| 3528 | req!(res!(Film::read(&file)).rotation(), 270u16); |
| 3529 | Ok(()) |
| 3530 | } |
| 3531 | |
| 3532 | /// The presentation times are a real film's, read off the first frames of |
| 3533 | /// `Dominion2018.mkv`: shown at 0, 160, 80, 40, 120 while decoded at 0, 40, |
| 3534 | /// 80, 120, 160. A stream that never reordered would state the same list |
| 3535 | /// twice. |
| 3536 | #[test] |
| 3537 | fn test_a_reordered_run_is_offset_forwards_19() -> Outcome<()> { |
| 3538 | let times = [0i64, 160, 80, 40, 120]; |
| 3539 | let durs = [40u32; 5]; |
| 3540 | let offs = res!(composition_offsets(×, &durs)); |
| 3541 | |
| 3542 | // Not one of them may be negative, whatever the film does: a negative |
| 3543 | // offset says a picture is shown before it is decoded. |
| 3544 | for (i, o) in offs.iter().enumerate() { |
| 3545 | assert!(*o >= 0, "offset {} of sample {} is negative", o, i); |
| 3546 | } |
| 3547 | // And the intervals must survive: every picture keeps its distance from |
| 3548 | // every other, so the whole run is the source's list plus one constant. |
| 3549 | let mut dts = 0i64; |
| 3550 | let mut shift = None; |
| 3551 | for i in 0..times.len() { |
| 3552 | let shown = dts + offs[i] as i64; |
| 3553 | match shift { |
| 3554 | None => shift = Some(shown - times[i]), |
| 3555 | Some(s) => req!(shown - times[i], s), |
| 3556 | } |
| 3557 | dts += durs[i] as i64; |
| 3558 | } |
| 3559 | req!(shift, Some(80i64)); |
| 3560 | Ok(()) |
| 3561 | } |
| 3562 | |
| 3563 | /// A track whose pictures are shown in the order they are decoded must |
| 3564 | /// carry no `ctts` at all -- an absent box is the statement that the two |
| 3565 | /// orders agree, and a table of zeroes says it again for four bytes a |
| 3566 | /// sample. |
| 3567 | #[test] |
| 3568 | fn test_a_stream_in_order_writes_no_offset_table_20() -> Outcome<()> { |
| 3569 | let times = [0i64, 40, 80, 120]; |
| 3570 | let durs = [40u32; 4]; |
| 3571 | let offs = res!(composition_offsets(×, &durs)); |
| 3572 | req!(offs, vec![0i32, 0, 0, 0]); |
| 3573 | |
| 3574 | let mut t = res!(Track::new(64, 48, 1000, Codec::Avc(avcc()))); |
| 3575 | for i in 0..4 { |
| 3576 | res!(t.push(Sample { data: nal(8), dur: 40, sync: i == 0, off: 0 })); |
| 3577 | } |
| 3578 | req!(res!(t.ctts()).is_none(), true); |
| 3579 | Ok(()) |
| 3580 | } |
| 3581 | |
| 3582 | /// An `AudioSpecificConfig` for AAC-LC at 44,100 Hz in two channels, ISO/IEC 14496-3 §1.6.2.1: |
| 3583 | /// object type 2 in five bits, sampling frequency index 4 in four, channel configuration 2 in |
| 3584 | /// four, and three bits of padding. |
| 3585 | const AAC_LC_44100_STEREO: [u8; 2] = [0x12, 0x10]; |
| 3586 | |
| 3587 | /// A picture stream of the fixture geometry, on a millisecond timescale. |
| 3588 | fn picture_stream() -> Stream { |
| 3589 | Stream { |
| 3590 | media: Media::Picture { w: 64, h: 48 }, |
| 3591 | timescale: 1000, |
| 3592 | codec: Codec::Avc(avcc()), |
| 3593 | start: 0, |
| 3594 | } |
| 3595 | } |
| 3596 | |
| 3597 | /// A sound stream on its own sampling rate as its timescale, beginning after the pictures do. |
| 3598 | fn sound_stream(start: u64) -> Stream { |
| 3599 | Stream { |
| 3600 | media: Media::Sound { channels: 2, rate: 44_100 }, |
| 3601 | timescale: 44_100, |
| 3602 | codec: Codec::Aac(AAC_LC_44100_STEREO.to_vec()), |
| 3603 | start, |
| 3604 | } |
| 3605 | } |
| 3606 | |
| 3607 | /// How many boxes of the given type sit at the top level of a box body. |
| 3608 | fn count_boxes(buf: &[u8], kind: &[u8; 4]) -> Outcome<usize> { |
| 3609 | let mut at = 0usize; |
| 3610 | let mut n = 0usize; |
| 3611 | while at + 8 <= buf.len() { |
| 3612 | let size = u32::from_be_bytes([buf[at], buf[at + 1], buf[at + 2], buf[at + 3]]) as usize; |
| 3613 | if size < 8 || at + size > buf.len() { |
| 3614 | return Err(err!( |
| 3615 | "A box of {} bytes at offset {} does not fit the {} bytes given.", |
| 3616 | size, at, buf.len(); |
| 3617 | Test, Invalid)); |
| 3618 | } |
| 3619 | if &buf[at + 4..at + 8] == kind { |
| 3620 | n += 1; |
| 3621 | } |
| 3622 | at += size; |
| 3623 | } |
| 3624 | Ok(n) |
| 3625 | } |
| 3626 | |
| 3627 | /// Each track fragment of a fragment, as the decode time its `tfdt` states and the `data_offset` |
| 3628 | /// its `trun` states, in the order the track fragments are written. |
| 3629 | /// |
| 3630 | /// The boxes are walked by their sizes rather than found by searching for their names, because |
| 3631 | /// a fragment holds two of each and a search finds only the first. |
| 3632 | fn frag_runs(frag: &[u8]) -> Outcome<Vec<(u64, i32)>> { |
| 3633 | if frag.len() < 8 || &frag[4..8] != b"moof" { |
| 3634 | return Err(err!("A fragment must begin with a movie fragment box."; Test, Invalid)); |
| 3635 | } |
| 3636 | let end = u32::from_be_bytes([frag[0], frag[1], frag[2], frag[3]]) as usize; |
| 3637 | if end > frag.len() { |
| 3638 | return Err(err!( |
| 3639 | "The movie fragment box claims {} bytes and the whole fragment is {}.", |
| 3640 | end, frag.len(); |
| 3641 | Test, Invalid)); |
| 3642 | } |
| 3643 | let mut out = Vec::new(); |
| 3644 | let mut at = 8usize; |
| 3645 | while at + 8 <= end { |
| 3646 | let n = u32::from_be_bytes([frag[at], frag[at + 1], frag[at + 2], frag[at + 3]]) as usize; |
| 3647 | if n < 8 || at + n > end { |
| 3648 | return Err(err!( |
| 3649 | "A box of {} bytes at offset {} does not fit the movie fragment.", n, at; |
| 3650 | Test, Invalid)); |
| 3651 | } |
| 3652 | if &frag[at + 4..at + 8] == b"traf" { |
| 3653 | let mut time: Option<u64> = None; |
| 3654 | let mut off: Option<i32> = None; |
| 3655 | let mut k = at + 8; |
| 3656 | while k + 8 <= at + n { |
| 3657 | let m = u32::from_be_bytes([frag[k], frag[k + 1], frag[k + 2], frag[k + 3]]) |
| 3658 | as usize; |
| 3659 | if m < 8 || k + m > at + n { |
| 3660 | return Err(err!( |
| 3661 | "A box of {} bytes at offset {} does not fit the track fragment.", m, k; |
| 3662 | Test, Invalid)); |
| 3663 | } |
| 3664 | // A `tfdt` body is a full box and a 64-bit time; a `trun` body is a full box, |
| 3665 | // the sample count, and then the offset. |
| 3666 | if &frag[k + 4..k + 8] == b"tfdt" && m >= 20 { |
| 3667 | let o = k + 12; |
| 3668 | time = Some(u64::from_be_bytes([ |
| 3669 | frag[o], frag[o + 1], frag[o + 2], frag[o + 3], |
| 3670 | frag[o + 4], frag[o + 5], frag[o + 6], frag[o + 7], |
| 3671 | ])); |
| 3672 | } |
| 3673 | if &frag[k + 4..k + 8] == b"trun" && m >= 20 { |
| 3674 | let o = k + 16; |
| 3675 | off = Some(i32::from_be_bytes([ |
| 3676 | frag[o], frag[o + 1], frag[o + 2], frag[o + 3], |
| 3677 | ])); |
| 3678 | } |
| 3679 | k += m; |
| 3680 | } |
| 3681 | match (time, off) { |
| 3682 | (Some(t), Some(o)) => out.push((t, o)), |
| 3683 | _ => return Err(err!( |
| 3684 | "The track fragment at offset {} carries no decode time or no track run.", |
| 3685 | at; |
| 3686 | Test, Missing)), |
| 3687 | } |
| 3688 | } |
| 3689 | at += n; |
| 3690 | } |
| 3691 | Ok(out) |
| 3692 | } |
| 3693 | |
| 3694 | /// The initialisation segment describes every stream once: a `trak` each under `moov` and a |
| 3695 | /// `trex` each under `mvex`, and a next track id one past the highest in use. |
| 3696 | /// |
| 3697 | /// A missing `trex` is the failure worth guarding: the file opens, the track is listed, and |
| 3698 | /// every fragment of it is ignored, because without the extends box the empty sample tables in |
| 3699 | /// `moov` are the whole of what the track is said to hold. |
| 3700 | #[test] |
| 3701 | fn test_a_fragmented_head_describes_every_stream_21() -> Outcome<()> { |
| 3702 | let f = res!(Fragments::new(vec![picture_stream(), sound_stream(0)])); |
| 3703 | let head = res!(f.head()); |
| 3704 | |
| 3705 | let ftyp = match top(&head, b"ftyp") { |
| 3706 | Some(b) => b, |
| 3707 | None => return Err(err!("No file type box was written."; Test, Missing)), |
| 3708 | }; |
| 3709 | req!(&ftyp[0..4], b"iso5" as &[u8], "the major brand is not the fragmented one"); |
| 3710 | |
| 3711 | let moov = match top(&head, b"moov") { |
| 3712 | Some(b) => b, |
| 3713 | None => return Err(err!("No movie box was written."; Test, Missing)), |
| 3714 | }; |
| 3715 | req!(res!(count_boxes(&moov, b"trak")), 2usize, "one track a stream"); |
| 3716 | req!(res!(count_boxes(&moov, b"mvex")), 1usize, "one movie extends box"); |
| 3717 | |
| 3718 | let mvex = match top(&moov, b"mvex") { |
| 3719 | Some(b) => b, |
| 3720 | None => return Err(err!("No movie extends box was written."; Test, Missing)), |
| 3721 | }; |
| 3722 | req!(res!(count_boxes(&mvex, b"trex")), 2usize, "one track extends box a stream"); |
| 3723 | |
| 3724 | // The next track id is the last field of the movie header, and it must exceed both ids in |
| 3725 | // use rather than count them. |
| 3726 | let mvhd = match top(&moov, b"mvhd") { |
| 3727 | Some(b) => b, |
| 3728 | None => return Err(err!("No movie header was written."; Test, Missing)), |
| 3729 | }; |
| 3730 | let n = mvhd.len(); |
| 3731 | req!(u32::from_be_bytes([mvhd[n - 4], mvhd[n - 3], mvhd[n - 2], mvhd[n - 1]]), 3u32); |
| 3732 | Ok(()) |
| 3733 | } |
| 3734 | |
| 3735 | /// The first track run's data offset clears the movie fragment box and its media header, and |
| 3736 | /// the media box holds exactly the samples. |
| 3737 | /// |
| 3738 | /// The offset is measured from the first byte of the `moof`, because `default-base-is-moof` is |
| 3739 | /// the only flag the track fragment header sets. An offset measured from anywhere else is a |
| 3740 | /// file that opens, reports the right number of frames, and decodes rubbish. |
| 3741 | #[test] |
| 3742 | fn test_the_first_data_offset_clears_the_moof_22() -> Outcome<()> { |
| 3743 | let mut f = res!(Fragments::new(vec![picture_stream()])); |
| 3744 | let sizes = [30usize, 9, 17]; |
| 3745 | let mut samples = Vec::new(); |
| 3746 | for (i, n) in sizes.into_iter().enumerate() { |
| 3747 | samples.push(Sample { data: nal(n), dur: 40, sync: i == 0, off: 0 }); |
| 3748 | } |
| 3749 | let frag = res!(f.next(vec![(0, samples)])); |
| 3750 | |
| 3751 | req!(&frag[4..8], b"moof" as &[u8]); |
| 3752 | let moof = u32::from_be_bytes([frag[0], frag[1], frag[2], frag[3]]) as usize; |
| 3753 | let runs = res!(frag_runs(&frag)); |
| 3754 | req!(runs.len(), 1usize); |
| 3755 | req!(runs[0].1, moof as i32 + 8, "the data offset does not clear the moof and mdat header"); |
| 3756 | |
| 3757 | // The media box follows the movie fragment immediately, and its payload is the samples and |
| 3758 | // nothing else: each is its own bytes with a four-byte NAL length in front. |
| 3759 | req!(&frag[moof + 4..moof + 8], b"mdat" as &[u8]); |
| 3760 | let payload: usize = sizes.iter().map(|n| n + 4).sum(); |
| 3761 | req!(u32::from_be_bytes([ |
| 3762 | frag[moof], frag[moof + 1], frag[moof + 2], frag[moof + 3], |
| 3763 | ]) as usize, payload + 8); |
| 3764 | req!(frag.len(), moof + 8 + payload); |
| 3765 | |
| 3766 | // And the offset names the first byte of the first sample, which is its NAL length field. |
| 3767 | let at = runs[0].1 as usize; |
| 3768 | req!(u32::from_be_bytes([ |
| 3769 | frag[at], frag[at + 1], frag[at + 2], frag[at + 3], |
| 3770 | ]) as usize, sizes[0]); |
| 3771 | Ok(()) |
| 3772 | } |
| 3773 | |
| 3774 | /// A second track fragment's data offset is the first's plus the first's sample bytes, and each |
| 3775 | /// fragment states where its own streams have got to. |
| 3776 | /// |
| 3777 | /// The two halves are one fault in two places. Both offsets are measured from the same origin, |
| 3778 | /// so the second is only right if the first's samples have been counted exactly; and both |
| 3779 | /// streams' decode times carry on across fragments, each in its own timescale and from its own |
| 3780 | /// start, so a stream that began late must still be late in the second fragment. |
| 3781 | #[test] |
| 3782 | fn test_a_later_traf_starts_after_the_earlier_bytes_23() -> Outcome<()> { |
| 3783 | let mut f = res!(Fragments::new(vec![picture_stream(), sound_stream(512)])); |
| 3784 | let pictures = |first: bool| -> Vec<Sample> { |
| 3785 | let mut v = Vec::new(); |
| 3786 | for i in 0..3usize { |
| 3787 | v.push(Sample { data: nal(20 + i), dur: 40, sync: first && i == 0, off: 0 }); |
| 3788 | } |
| 3789 | v |
| 3790 | }; |
| 3791 | let sound = || -> Vec<Sample> { |
| 3792 | vec![Sample::key(vec![0x21; 30], 1024), Sample::key(vec![0x21; 27], 1024)] |
| 3793 | }; |
| 3794 | let first = res!(f.next(vec![(0, pictures(true)), (1, sound())])); |
| 3795 | |
| 3796 | let moof = u32::from_be_bytes([first[0], first[1], first[2], first[3]]) as usize; |
| 3797 | let runs = res!(frag_runs(&first)); |
| 3798 | req!(runs.len(), 2usize); |
| 3799 | req!(runs[0].1, moof as i32 + 8); |
| 3800 | // The pictures come to three NAL units of 20, 21 and 22 bytes, each with a four-byte length |
| 3801 | // in front of it: 75 bytes, after which the sound begins. |
| 3802 | req!(runs[1].1 - runs[0].1, 75i32, "the second run does not follow the first's bytes"); |
| 3803 | req!(runs[0].0, 0u64, "the pictures do not begin at nought"); |
| 3804 | req!(runs[1].0, 512u64, "the sound does not begin where its stream says"); |
| 3805 | |
| 3806 | // The second fragment carries the next sequence number, and each stream's decode time has |
| 3807 | // moved on by that stream's own durations: three pictures of 40 ticks at 1000 a second, and |
| 3808 | // two sound frames of 1024 at 44,100. |
| 3809 | let second = res!(f.next(vec![(0, pictures(false)), (1, sound())])); |
| 3810 | let seq = res!(want_box(&second, b"mfhd")); |
| 3811 | req!(u32::from_be_bytes([ |
| 3812 | second[seq + 4], second[seq + 5], second[seq + 6], second[seq + 7], |
| 3813 | ]), 2u32); |
| 3814 | let runs = res!(frag_runs(&second)); |
| 3815 | req!(runs.len(), 2usize); |
| 3816 | req!(runs[0].0, 120u64); |
| 3817 | req!(runs[1].0, 512u64 + 2048); |
| 3818 | Ok(()) |
| 3819 | } |
| 3820 | |
| 3821 | /// The sequence parameter set of a 96 by 64 HEVC stream, as libx265 wrote it: the NAL unit of |
| 3822 | /// type 33 out of `ffmpeg -f lavfi -i testsrc=size=96x64:rate=10 -frames:v 2 -pix_fmt yuv420p |
| 3823 | /// -c:v libx265 -f hevc`. Main profile, 8-bit 4:2:0, one temporal layer. |
| 3824 | /// |
| 3825 | /// Four of its bytes are emulation prevention -- the `03` of each `00 00 03`, at offsets 7, 12, |
| 3826 | /// 15 and 33 -- and the first of them sits well before the field that codes the width, so a |
| 3827 | /// reading that left them in place answers some other size rather than failing. The unescaped |
| 3828 | /// payload happens to carry no `03` at all, so the opposite fault, unescaping twice, is a |
| 3829 | /// no-op on this particular set and is **not** exercised by it. The two bytes at the front are |
| 3830 | /// the NAL unit header, which is what a record's parameter set array carries. |
| 3831 | const HEVC_SPS: [u8; 40] = [ |
| 3832 | 0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, |
| 3833 | 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x1E, 0xA0, 0x30, |
| 3834 | 0x81, 0x05, 0x96, 0x56, 0x69, 0x24, 0xCA, 0xF0, 0x16, 0x80, |
| 3835 | 0x80, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x05, 0x04, |
| 3836 | ]; |
| 3837 | |
| 3838 | /// The matching picture parameter set, from the same stream. |
| 3839 | /// |
| 3840 | /// Nothing under test reads it. It is here so that the record has the shape a real one has, and |
| 3841 | /// so that the refusal below has a parameter set to carry that is not a sequence parameter set. |
| 3842 | const HEVC_PPS: [u8; 7] = [0x44, 0x01, 0xC1, 0x72, 0xB4, 0x22, 0x40]; |
| 3843 | |
| 3844 | /// The 22 fixed fields of an `HEVCDecoderConfigurationRecord`, copied verbatim out of the |
| 3845 | /// `hvcC` box ffmpeg wrote when it muxed that same stream into an MP4. |
| 3846 | /// |
| 3847 | /// Version 1, then the profile, tier and level of the sets above, then the sampling and the |
| 3848 | /// depths, and finally `0x0F`: one temporal layer, nested, and a four-byte NAL length. |
| 3849 | const HVCC_HEAD: [u8; 22] = [ |
| 3850 | 0x01, 0x01, 0x60, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, |
| 3851 | 0x00, 0x1E, 0xF0, 0x00, 0xFC, 0xFD, 0xF8, 0xF8, 0x00, 0x00, 0x0F, |
| 3852 | ]; |
| 3853 | |
| 3854 | /// A record around the given parameter sets, each as `(NAL unit type, bytes)`, one array |
| 3855 | /// apiece: the array's type byte, a count of one, and the set behind a two-byte length. |
| 3856 | /// |
| 3857 | /// The type byte's top bit is `array_completeness`, which is set because a `hvc1` entry states |
| 3858 | /// that these are all the sets there are; the bit below it is reserved and nought. That is the |
| 3859 | /// byte ffmpeg writes -- `0xA0`, `0xA1`, `0xA2` for the three arrays of the record above. |
| 3860 | fn hvcc(sets: &[(u8, &[u8])]) -> Vec<u8> { |
| 3861 | let mut rec = HVCC_HEAD.to_vec(); |
| 3862 | rec.push(sets.len() as u8); |
| 3863 | for &(kind, set) in sets { |
| 3864 | rec.push(0x80 | kind); |
| 3865 | rec.extend_from_slice(&1u16.to_be_bytes()); // One set in this array. |
| 3866 | rec.extend_from_slice(&(set.len() as u16).to_be_bytes()); |
| 3867 | rec.extend_from_slice(set); |
| 3868 | } |
| 3869 | rec |
| 3870 | } |
| 3871 | |
| 3872 | /// An `hvcC` gives the size its sequence parameter set codes, and a track is held to it. |
| 3873 | /// |
| 3874 | /// The size is not this crate's: 96 by 64 is what FFmpeg was given on the command line that |
| 3875 | /// produced the parameter set, and what `ffprobe` reads back out of the stream it produced. |
| 3876 | /// The set carries emulation prevention bytes before the field that codes the width, so a |
| 3877 | /// reading that left them in place answers some other size rather than failing. |
| 3878 | #[test] |
| 3879 | fn test_an_hvcc_yields_the_size_its_sps_codes_24() -> Outcome<()> { |
| 3880 | let sets: [(u8, &[u8]); 2] = [(33, &HEVC_SPS[..]), (34, &HEVC_PPS[..])]; |
| 3881 | let c = Codec::Hevc(hvcc(&sets)); |
| 3882 | req!(res!(c.geometry()), (96u16, 64u16)); |
| 3883 | req!(res!(c.nal_len()), 4usize); |
| 3884 | req!(c.is_picture(), true); |
| 3885 | req!(c.entry(), b"hvc1"); |
| 3886 | req!(c.config(), b"hvcC"); |
| 3887 | |
| 3888 | // A track is accepted at the size the set codes and refused at any other, which is the |
| 3889 | // whole-file writer's existing check working over HEVC with nothing added to it. |
| 3890 | req!(Track::new(96, 64, 1000, c.clone()).is_ok(), true, "the coded size was refused"); |
| 3891 | req!(Track::new(64, 48, 1000, c.clone()).is_err(), true, |
| 3892 | "a track declared 64 by 48 over a 96 by 64 stream was accepted"); |
| 3893 | |
| 3894 | // And the tiling check is reached for HEVC as it is for AVC. An elementary stream handed |
| 3895 | // straight through is the fault worth naming: it produces a file every demuxer accepts and |
| 3896 | // no decoder plays. |
| 3897 | let mut t = res!(Track::new(96, 64, 1000, c)); |
| 3898 | match t.push(Sample::key(vec![0, 0, 0, 1, 0x26, 0x01, 0xAF], 40)) { |
| 3899 | Ok(_) => return Err(err!("An Annex B HEVC sample was accepted."; Test)), |
| 3900 | Err(e) => req!(e.to_string().contains("Annex B"), true, |
| 3901 | "The message does not name the format that was handed over."), |
| 3902 | } |
| 3903 | Ok(()) |
| 3904 | } |
| 3905 | |
| 3906 | /// A record carrying no sequence parameter set is refused, and the refusal names the type that |
| 3907 | /// is missing and the types that are there. |
| 3908 | /// |
| 3909 | /// Naming both is the point. "No geometry" on its own leaves a caller guessing whether the |
| 3910 | /// record was empty, truncated, or full of the wrong sets, and the three are fixed differently. |
| 3911 | #[test] |
| 3912 | fn test_an_hvcc_with_no_sps_is_refused_by_name_25() -> Outcome<()> { |
| 3913 | let only_pps: [(u8, &[u8]); 1] = [(34, &HEVC_PPS[..])]; |
| 3914 | let msg = match Codec::Hevc(hvcc(&only_pps)).geometry() { |
| 3915 | Ok((w, h)) => return Err(err!( |
| 3916 | "A record carrying only a picture parameter set gave a geometry of {} by {}.", |
| 3917 | w, h; Test)), |
| 3918 | Err(e) => e.to_string(), |
| 3919 | }; |
| 3920 | req!(msg.contains("33"), true, "The message does not name the set that is missing."); |
| 3921 | req!(msg.contains("34"), true, "The message does not name what the record does carry."); |
| 3922 | |
| 3923 | let msg = match Codec::Hevc(hvcc(&[])).geometry() { |
| 3924 | Ok((w, h)) => return Err(err!( |
| 3925 | "A record carrying no parameter sets at all gave a geometry of {} by {}.", |
| 3926 | w, h; Test)), |
| 3927 | Err(e) => e.to_string(), |
| 3928 | }; |
| 3929 | req!(msg.contains("no parameter sets at all"), true, |
| 3930 | "The message does not say that the record carries nothing."); |
| 3931 | Ok(()) |
| 3932 | } |
| 3933 | } |