1#![deny(missing_docs)]
3
4use std::fmt;
5use std::io;
6use std::io::Read;
7use std::io::Write;
8use std::num;
9use std::str;
10
11const MAX_PARAMS_SIZE: usize = 1024;
12const FILE_MAGICK: &[u8] = b"YUV4MPEG2 ";
13const FRAME_MAGICK: &[u8] = b"FRAME";
14const TERMINATOR: u8 = 0x0A;
15const FIELD_SEP: u8 = b' ';
16const RATIO_SEP: u8 = b':';
17
18#[derive(Debug)]
20pub enum Error {
21 EOF,
24 BadInput,
26 UnknownColorspace,
28 ParseError(ParseError),
31 IoError(io::Error),
33 OutOfMemory,
35}
36
37impl std::error::Error for crate::Error {
38 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
39 match *self {
40 Error::EOF => None,
41 Error::BadInput => None,
42 Error::UnknownColorspace => None,
43 Error::ParseError(ref err) => Some(err),
44 Error::IoError(ref err) => Some(err),
45 Error::OutOfMemory => None,
46 }
47 }
48}
49
50impl fmt::Display for crate::Error {
51 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52 match *self {
53 Error::EOF => write!(f, "End of file"),
54 Error::BadInput => write!(f, "Bad input parameters provided"),
55 Error::UnknownColorspace => write!(f, "Bad input parameters provided"),
56 Error::ParseError(ref err) => err.fmt(f),
57 Error::IoError(ref err) => err.fmt(f),
58 Error::OutOfMemory => write!(f, "Out of memory (limits exceeded)"),
59 }
60 }
61}
62
63pub enum ParseError {
65 InvalidY4M,
67 Int,
69 Utf8,
71 General,
73}
74
75impl std::error::Error for crate::ParseError {
76 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
77 match *self {
78 ParseError::InvalidY4M => None,
79 ParseError::Int => None,
80 ParseError::Utf8 => None,
81 ParseError::General => None,
82 }
83 }
84}
85
86impl fmt::Display for ParseError {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 match self {
89 ParseError::InvalidY4M => write!(f, "Error parsing y4m header"),
90 ParseError::Int => write!(f, "Error parsing Int"),
91 ParseError::Utf8 => write!(f, "Error parsing UTF8"),
92 ParseError::General => write!(f, "General parsing error"),
93 }
94 }
95}
96
97impl fmt::Debug for ParseError {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 match self {
100 ParseError::InvalidY4M => write!(f, "Error parsing y4m header"),
101 ParseError::Int => write!(f, "Error parsing Int"),
102 ParseError::Utf8 => write!(f, "Error parsing UTF8"),
103 ParseError::General => write!(f, "General parsing error"),
104 }
105 }
106}
107
108macro_rules! parse_error {
109 ($p:expr) => {
110 return Err(Error::ParseError($p))
111 };
112}
113
114impl From<io::Error> for Error {
115 fn from(err: io::Error) -> Error {
116 match err.kind() {
117 io::ErrorKind::UnexpectedEof => Error::EOF,
118 _ => Error::IoError(err),
119 }
120 }
121}
122
123impl From<num::ParseIntError> for Error {
124 fn from(_: num::ParseIntError) -> Error {
125 Error::ParseError(ParseError::Int)
126 }
127}
128
129impl From<str::Utf8Error> for Error {
130 fn from(_: str::Utf8Error) -> Error {
131 Error::ParseError(ParseError::Utf8)
132 }
133}
134
135trait EnhancedRead {
136 fn read_until(&mut self, ch: u8, buf: &mut [u8]) -> Result<usize, Error>;
137}
138
139impl<R: Read> EnhancedRead for R {
140 fn read_until(&mut self, ch: u8, buf: &mut [u8]) -> Result<usize, Error> {
144 let mut collected = 0;
145 while collected < buf.len() {
146 let chunk_size = self.read(&mut buf[collected..=collected])?;
147 if chunk_size == 0 {
148 return Err(Error::EOF);
149 }
150 if buf[collected] == ch {
151 return Ok(collected);
152 }
153 collected += chunk_size;
154 }
155 parse_error!(ParseError::General)
156 }
157}
158
159fn parse_bytes(buf: &[u8]) -> Result<usize, Error> {
160 Ok(str::from_utf8(buf)?.parse()?)
162}
163
164#[derive(Debug, Clone)]
166pub struct VendorExtensionString(Vec<u8>);
167
168impl VendorExtensionString {
169 pub fn new(value: Vec<u8>) -> Result<VendorExtensionString, Error> {
178 if value.contains(&b' ') {
179 return Err(Error::BadInput);
180 }
181 Ok(VendorExtensionString(value))
182 }
183 pub fn value(&self) -> &[u8] {
185 self.0.as_slice()
186 }
187}
188
189#[derive(Debug, Clone, Copy)]
191pub struct Ratio {
192 pub num: usize,
194 pub den: usize,
196}
197
198impl Ratio {
199 pub fn new(num: usize, den: usize) -> Ratio {
201 Ratio { num, den }
202 }
203
204 pub fn parse(value: &[u8]) -> Result<Ratio, Error> {
206 let parts: Vec<_> = value.splitn(2, |&b| b == RATIO_SEP).collect();
207 if parts.len() != 2 {
208 parse_error!(ParseError::General)
209 }
210 let num = parse_bytes(parts[0])?;
211 let den = parse_bytes(parts[1])?;
212 Ok(Ratio::new(num, den))
213 }
214}
215
216impl fmt::Display for Ratio {
217 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218 write!(f, "{}:{}", self.num, self.den)
219 }
220}
221
222#[derive(Debug, Clone, Copy)]
232#[non_exhaustive]
233pub enum Colorspace {
234 Cmono,
236 Cmono12,
238 C420,
240 C420p10,
242 C420p12,
244 C420jpeg,
246 C420paldv,
248 C420mpeg2,
250 C422,
252 C422p10,
254 C422p12,
256 C444,
258 C444p10,
260 C444p12,
262}
263
264impl Colorspace {
265 #[inline]
267 pub fn get_bit_depth(self) -> usize {
268 match self {
269 Colorspace::Cmono
270 | Colorspace::C420
271 | Colorspace::C422
272 | Colorspace::C444
273 | Colorspace::C420jpeg
274 | Colorspace::C420paldv
275 | Colorspace::C420mpeg2 => 8,
276 Colorspace::C420p10 | Colorspace::C422p10 | Colorspace::C444p10 => 10,
277 Colorspace::Cmono12
278 | Colorspace::C420p12
279 | Colorspace::C422p12
280 | Colorspace::C444p12 => 12,
281 }
282 }
283
284 #[inline]
286 pub fn get_bytes_per_sample(self) -> usize {
287 if self.get_bit_depth() <= 8 {
288 1
289 } else {
290 2
291 }
292 }
293}
294
295fn get_plane_sizes(width: usize, height: usize, colorspace: Colorspace) -> (usize, usize, usize) {
296 let y_plane_size = width * height * colorspace.get_bytes_per_sample();
297
298 let c420_chroma_size =
299 ((width + 1) / 2) * ((height + 1) / 2) * colorspace.get_bytes_per_sample();
300 let c422_chroma_size = ((width + 1) / 2) * height * colorspace.get_bytes_per_sample();
301
302 let c420_sizes = (y_plane_size, c420_chroma_size, c420_chroma_size);
303 let c422_sizes = (y_plane_size, c422_chroma_size, c422_chroma_size);
304 let c444_sizes = (y_plane_size, y_plane_size, y_plane_size);
305
306 match colorspace {
307 Colorspace::Cmono | Colorspace::Cmono12 => (y_plane_size, 0, 0),
308 Colorspace::C420
309 | Colorspace::C420p10
310 | Colorspace::C420p12
311 | Colorspace::C420jpeg
312 | Colorspace::C420paldv
313 | Colorspace::C420mpeg2 => c420_sizes,
314 Colorspace::C422 | Colorspace::C422p10 | Colorspace::C422p12 => c422_sizes,
315 Colorspace::C444 | Colorspace::C444p10 | Colorspace::C444p12 => c444_sizes,
316 }
317}
318
319#[derive(Clone, Copy, Debug)]
321pub struct Limits {
322 pub bytes: usize,
324}
325
326impl Default for Limits {
327 fn default() -> Limits {
328 Limits {
329 bytes: 1024 * 1024 * 1024,
330 }
331 }
332}
333
334pub struct Decoder<R: Read> {
336 reader: R,
337 params_buf: Vec<u8>,
338 frame_buf: Vec<u8>,
339 raw_params: Vec<u8>,
340 width: usize,
341 height: usize,
342 framerate: Ratio,
343 pixel_aspect: Ratio,
344 colorspace: Colorspace,
345 y_len: usize,
346 u_len: usize,
347}
348
349impl<R: Read> Decoder<R> {
350 pub fn new(reader: R) -> Result<Decoder<R>, Error> {
352 Decoder::new_with_limits(reader, Limits::default())
353 }
354
355 pub fn new_with_limits(mut reader: R, limits: Limits) -> Result<Decoder<R>, Error> {
357 let mut params_buf = vec![0; MAX_PARAMS_SIZE];
358 let end_params_pos = reader.read_until(TERMINATOR, &mut params_buf)?;
359 if end_params_pos < FILE_MAGICK.len() || !params_buf.starts_with(FILE_MAGICK) {
360 parse_error!(ParseError::InvalidY4M)
361 }
362 let raw_params = params_buf[FILE_MAGICK.len()..end_params_pos].to_owned();
363 let mut width = 0;
364 let mut height = 0;
365 let mut framerate = Ratio::new(25, 1);
368 let mut pixel_aspect = Ratio::new(1, 1);
369 let mut colorspace = None;
370 for param in raw_params.split(|&b| b == FIELD_SEP) {
372 if param.is_empty() {
373 continue;
374 }
375 let (name, value) = (param[0], ¶m[1..]);
376 match name {
378 b'W' => width = parse_bytes(value)?,
379 b'H' => height = parse_bytes(value)?,
380 b'F' => framerate = Ratio::parse(value)?,
381 b'A' => pixel_aspect = Ratio::parse(value)?,
382 b'C' => {
383 colorspace = match value {
384 b"mono" => Some(Colorspace::Cmono),
385 b"mono12" => Some(Colorspace::Cmono12),
386 b"420" => Some(Colorspace::C420),
387 b"420p10" => Some(Colorspace::C420p10),
388 b"420p12" => Some(Colorspace::C420p12),
389 b"422" => Some(Colorspace::C422),
390 b"422p10" => Some(Colorspace::C422p10),
391 b"422p12" => Some(Colorspace::C422p12),
392 b"444" => Some(Colorspace::C444),
393 b"444p10" => Some(Colorspace::C444p10),
394 b"444p12" => Some(Colorspace::C444p12),
395 b"420jpeg" => Some(Colorspace::C420jpeg),
396 b"420paldv" => Some(Colorspace::C420paldv),
397 b"420mpeg2" => Some(Colorspace::C420mpeg2),
398 _ => return Err(Error::UnknownColorspace),
399 }
400 }
401 _ => {}
402 }
403 }
404 let colorspace = colorspace.unwrap_or(Colorspace::C420);
405 if width == 0 || height == 0 {
406 parse_error!(ParseError::General)
407 }
408 let (y_len, u_len, v_len) = get_plane_sizes(width, height, colorspace);
409 let frame_size = y_len + u_len + v_len;
410 if frame_size > limits.bytes {
411 return Err(Error::OutOfMemory);
412 }
413 let frame_buf = vec![0; frame_size];
414 Ok(Decoder {
415 reader,
416 params_buf,
417 frame_buf,
418 raw_params,
419 width,
420 height,
421 framerate,
422 pixel_aspect,
423 colorspace,
424 y_len,
425 u_len,
426 })
427 }
428
429 pub fn read_frame(&mut self) -> Result<Frame, Error> {
431 let end_params_pos = self.reader.read_until(TERMINATOR, &mut self.params_buf)?;
432 if end_params_pos < FRAME_MAGICK.len() || !self.params_buf.starts_with(FRAME_MAGICK) {
433 parse_error!(ParseError::InvalidY4M)
434 }
435 let start_params_pos = FRAME_MAGICK.len();
437 let raw_params = if end_params_pos - start_params_pos > 0 {
438 if self.params_buf[start_params_pos] != FIELD_SEP {
440 parse_error!(ParseError::InvalidY4M)
441 }
442 Some(self.params_buf[start_params_pos + 1..end_params_pos].to_owned())
443 } else {
444 None
445 };
446 self.reader.read_exact(&mut self.frame_buf)?;
447 Ok(Frame::new(
448 [
449 &self.frame_buf[0..self.y_len],
450 &self.frame_buf[self.y_len..self.y_len + self.u_len],
451 &self.frame_buf[self.y_len + self.u_len..],
452 ],
453 raw_params,
454 ))
455 }
456
457 #[inline]
459 pub fn get_width(&self) -> usize {
460 self.width
461 }
462 #[inline]
464 pub fn get_height(&self) -> usize {
465 self.height
466 }
467 #[inline]
469 pub fn get_framerate(&self) -> Ratio {
470 self.framerate
471 }
472 #[inline]
474 pub fn get_pixel_aspect(&self) -> Ratio {
475 self.pixel_aspect
476 }
477 #[inline]
483 pub fn get_colorspace(&self) -> Colorspace {
484 self.colorspace
485 }
486 #[inline]
488 pub fn get_raw_params(&self) -> &[u8] {
489 &self.raw_params
490 }
491 #[inline]
493 pub fn get_bit_depth(&self) -> usize {
494 self.colorspace.get_bit_depth()
495 }
496 #[inline]
498 pub fn get_bytes_per_sample(&self) -> usize {
499 self.colorspace.get_bytes_per_sample()
500 }
501}
502
503#[derive(Debug)]
505pub struct Frame<'f> {
506 planes: [&'f [u8]; 3],
507 raw_params: Option<Vec<u8>>,
508}
509
510impl<'f> Frame<'f> {
511 pub fn new(planes: [&'f [u8]; 3], raw_params: Option<Vec<u8>>) -> Frame<'f> {
514 Frame { planes, raw_params }
515 }
516
517 pub fn from_u16(planes: [&'f [u16]; 3], raw_params: Option<Vec<u8>>) -> Frame<'f> {
519 Frame::new(
520 [
521 unsafe {
522 std::slice::from_raw_parts::<u8>(
523 planes[0].as_ptr() as *const u8,
524 planes[0].len() * 2,
525 )
526 },
527 unsafe {
528 std::slice::from_raw_parts::<u8>(
529 planes[1].as_ptr() as *const u8,
530 planes[1].len() * 2,
531 )
532 },
533 unsafe {
534 std::slice::from_raw_parts::<u8>(
535 planes[2].as_ptr() as *const u8,
536 planes[2].len() * 2,
537 )
538 },
539 ],
540 raw_params,
541 )
542 }
543
544 #[inline]
546 pub fn get_y_plane(&self) -> &[u8] {
547 self.planes[0]
548 }
549 #[inline]
551 pub fn get_u_plane(&self) -> &[u8] {
552 self.planes[1]
553 }
554 #[inline]
556 pub fn get_v_plane(&self) -> &[u8] {
557 self.planes[2]
558 }
559 #[inline]
561 pub fn get_raw_params(&self) -> Option<&[u8]> {
562 self.raw_params.as_ref().map(|v| &v[..])
563 }
564}
565
566#[derive(Debug)]
569pub struct EncoderBuilder {
570 width: usize,
571 height: usize,
572 framerate: Ratio,
573 pixel_aspect: Ratio,
574 colorspace: Colorspace,
575 vendor_extensions: Vec<Vec<u8>>,
576}
577
578impl EncoderBuilder {
579 pub fn new(width: usize, height: usize, framerate: Ratio) -> EncoderBuilder {
581 EncoderBuilder {
582 width,
583 height,
584 framerate,
585 pixel_aspect: Ratio::new(1, 1),
586 colorspace: Colorspace::C420,
587 vendor_extensions: vec![],
588 }
589 }
590
591 pub fn with_colorspace(mut self, colorspace: Colorspace) -> Self {
593 self.colorspace = colorspace;
594 self
595 }
596
597 pub fn with_pixel_aspect(mut self, pixel_aspect: Ratio) -> Self {
599 self.pixel_aspect = pixel_aspect;
600 self
601 }
602
603 pub fn append_vendor_extension(mut self, x_option: VendorExtensionString) -> Self {
605 self.vendor_extensions.push(x_option.0);
606 self
607 }
608
609 pub fn write_header<W: Write>(self, mut writer: W) -> Result<Encoder<W>, Error> {
611 writer.write_all(FILE_MAGICK)?;
613 write!(
614 writer,
615 "W{} H{} F{}",
616 self.width, self.height, self.framerate
617 )?;
618 if self.pixel_aspect.num != 1 || self.pixel_aspect.den != 1 {
619 write!(writer, " A{}", self.pixel_aspect)?;
620 }
621 for x_option in self.vendor_extensions.iter() {
622 write!(writer, " X")?;
623 writer.write_all(x_option)?;
624 }
625 write!(writer, " {:?}", self.colorspace)?;
626 writer.write_all(&[TERMINATOR])?;
627 let (y_len, u_len, v_len) = get_plane_sizes(self.width, self.height, self.colorspace);
628 Ok(Encoder {
629 writer,
630 y_len,
631 u_len,
632 v_len,
633 })
634 }
635}
636
637pub struct Encoder<W: Write> {
639 writer: W,
640 y_len: usize,
641 u_len: usize,
642 v_len: usize,
643}
644
645impl<W: Write> Encoder<W> {
646 pub fn write_frame(&mut self, frame: &Frame) -> Result<(), Error> {
648 if frame.get_y_plane().len() != self.y_len
649 || frame.get_u_plane().len() != self.u_len
650 || frame.get_v_plane().len() != self.v_len
651 {
652 return Err(Error::BadInput);
653 }
654 self.writer.write_all(FRAME_MAGICK)?;
655 if let Some(params) = frame.get_raw_params() {
656 self.writer.write_all(&[FIELD_SEP])?;
657 self.writer.write_all(params)?;
658 }
659 self.writer.write_all(&[TERMINATOR])?;
660 self.writer.write_all(frame.get_y_plane())?;
661 self.writer.write_all(frame.get_u_plane())?;
662 self.writer.write_all(frame.get_v_plane())?;
663 Ok(())
664 }
665}
666
667pub fn decode<R: Read>(reader: R) -> Result<Decoder<R>, Error> {
669 Decoder::new(reader)
670}
671
672pub fn encode(width: usize, height: usize, framerate: Ratio) -> EncoderBuilder {
674 EncoderBuilder::new(width, height, framerate)
675}