revc 0.1.3

Rust Essential Video Coding (MPEG-5 EVC)
Documentation
use super::Muxer;
use crate::{map_y4m_error, Data, IFVCA_CLIP};

use std::cmp::*;
use std::fs::File;
use std::io;
use std::io::Write;
use std::slice;

use revc::api::frame::*;
use revc::api::Rational;

pub enum Y4mMuxer {
    writer(Option<Box<dyn Write>>),
    encoder(y4m::Encoder<Box<dyn Write>>),
}

impl Y4mMuxer {
    pub fn new(path: &str) -> Box<dyn Muxer> {
        Box::new(Y4mMuxer::writer(Some(match path {
            "-" => Box::new(io::stdout()),
            f => Box::new(File::create(&f).unwrap()),
        })))
    }
}

impl Muxer for Y4mMuxer {
    fn write(&mut self, data: Data, bit_depth: u8, frame_rate: Rational) -> io::Result<()> {
        if let Y4mMuxer::writer(writer) = self {
            if let Some(writer) = writer.take() {
                if let Data::RefFrame(frame) = &data {
                    let f = frame.borrow();
                    let width = f.planes[0].cfg.width;
                    let height = f.planes[0].cfg.height;
                    *self = Y4mMuxer::encoder(
                        y4m::EncoderBuilder::new(
                            width,
                            height,
                            y4m::Ratio::new(frame_rate.num as usize, frame_rate.den as usize),
                        )
                        .write_header(writer)
                        .map_err(|e| map_y4m_error(e))?,
                    );
                }
            }
        }

        if let (Data::RefFrame(frame), Y4mMuxer::encoder(encoder)) = (&data, self) {
            let f = frame.borrow();
            let bytes_per_sample = if bit_depth > 8 { 2 } else { 1 };
            let pitch_y = f.planes[0].cfg.width * bytes_per_sample;
            let height = f.planes[0].cfg.height;
            let chroma_sampling_period = f.chroma_sampling.sampling_period();
            let (pitch_uv, height_uv) = (
                (pitch_y * bytes_per_sample) / chroma_sampling_period.0,
                height / chroma_sampling_period.1,
            );

            let (mut rec_y, mut rec_u, mut rec_v) = (
                vec![128u8; pitch_y * height],
                vec![128u8; pitch_uv * height_uv],
                vec![128u8; pitch_uv * height_uv],
            );

            let (stride_y, stride_u, stride_v) = (
                f.planes[0].cfg.stride,
                f.planes[1].cfg.stride,
                f.planes[2].cfg.stride,
            );

            for (line, line_out) in f.planes[0]
                .data_origin()
                .chunks(stride_y)
                .zip(rec_y.chunks_mut(pitch_y))
            {
                if bit_depth > 8 {
                    unsafe {
                        line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                            line.as_ptr() as *const u8,
                            pitch_y,
                        ));
                    }
                } else {
                    line_out.copy_from_slice(
                        &line
                            .iter()
                            .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2)))
                            .collect::<Vec<u8>>()[..pitch_y],
                    );
                }
            }
            for (line, line_out) in f.planes[1]
                .data_origin()
                .chunks(stride_u)
                .zip(rec_u.chunks_mut(pitch_uv))
            {
                if bit_depth > 8 {
                    unsafe {
                        line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                            line.as_ptr() as *const u8,
                            pitch_uv,
                        ));
                    }
                } else {
                    line_out.copy_from_slice(
                        &line
                            .iter()
                            .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2)))
                            .collect::<Vec<u8>>()[..pitch_uv],
                    );
                }
            }
            for (line, line_out) in f.planes[2]
                .data_origin()
                .chunks(stride_v)
                .zip(rec_v.chunks_mut(pitch_uv))
            {
                if bit_depth > 8 {
                    unsafe {
                        line_out.copy_from_slice(slice::from_raw_parts::<u8>(
                            line.as_ptr() as *const u8,
                            pitch_uv,
                        ));
                    }
                } else {
                    line_out.copy_from_slice(
                        &line
                            .iter()
                            .map(|&v| u8::cast_from(IFVCA_CLIP(0, 255, (v + 2) >> 2)))
                            .collect::<Vec<u8>>()[..pitch_uv],
                    );
                }
            }

            let rec_frame = y4m::Frame::new([&rec_y, &rec_u, &rec_v], None);
            encoder
                .write_frame(&rec_frame)
                .map_err(|e| map_y4m_error(e))?;

            Ok(())
        } else {
            Err(io::Error::new(
                io::ErrorKind::InvalidData,
                "Invalid Frame Data for Y4mMuxer",
            ))
        }
    }
}