Commit d92d5259 authored by Josh Holmer's avatar Josh Holmer Committed by Thomas Daede

Refactor decoder to be codec agnostic

This allows implementing other decoders in the future, and isolates the
decoding logic into one section of the codebase, making it easier to
refactor the encode/decode tests to handle lookahead and frame
reordering the same way as rav1e.
parent e7181fb4
......@@ -19,6 +19,7 @@ use std::collections::BTreeMap;
use std::sync::Arc;
use util::Fixed;
use std::collections::BTreeSet;
use decoder::VideoDetails;
const LOOKAHEAD_FRAMES: u64 = 10;
......@@ -249,33 +250,10 @@ pub struct ColorDescription {
pub matrix_coefficients: MatrixCoefficients
}
/// Frame-specific information
#[derive(Clone, Copy, Debug)]
pub struct FrameInfo {
pub width: usize,
pub height: usize,
pub bit_depth: usize,
pub chroma_sampling: ChromaSampling,
pub chroma_sample_position: ChromaSamplePosition
}
impl Default for FrameInfo {
fn default() -> FrameInfo {
FrameInfo {
width: 640,
height: 480,
bit_depth: 8,
chroma_sampling: Default::default(),
chroma_sample_position: Default::default()
}
}
}
/// Contain all the encoder configuration
#[derive(Clone, Copy, Debug)]
pub struct Config {
pub frame_info: FrameInfo,
pub timebase: Rational,
pub video_info: VideoDetails,
pub enc: EncoderConfig
}
......@@ -312,7 +290,7 @@ impl Config {
packet_data: Vec::new(),
segment_start_idx: 0,
segment_start_frame: 0,
keyframe_detector: SceneChangeDetector::new(&self.frame_info),
keyframe_detector: SceneChangeDetector::new(&self.video_info),
config: *self,
}
}
......@@ -335,7 +313,7 @@ pub struct Context {
segment_start_idx: u64,
segment_start_frame: u64,
keyframe_detector: SceneChangeDetector,
config: Config,
pub config: Config,
}
#[derive(Clone, Copy, Debug)]
......@@ -374,9 +352,9 @@ impl fmt::Display for Packet {
impl Context {
pub fn new_frame(&self) -> Arc<Frame> {
Arc::new(Frame::new(
self.config.frame_info.width.align_power_of_two(3),
self.config.frame_info.height.align_power_of_two(3),
self.config.frame_info.chroma_sampling
self.config.video_info.width.align_power_of_two(3),
self.config.video_info.height.align_power_of_two(3),
self.config.video_info.chroma_sampling
))
}
......@@ -463,10 +441,10 @@ impl Context {
// The first frame will always be a key frame
let fi = FrameInvariants::new_key_frame(
&FrameInvariants::new(
self.config.frame_info.width,
self.config.frame_info.height,
self.config.video_info.width,
self.config.video_info.height,
self.config.enc,
Sequence::new(&self.config.frame_info)
Sequence::new(&self.config.video_info)
),
0
);
......
......@@ -17,7 +17,8 @@ use std::io::prelude::*;
use std::sync::Arc;
use std::time::Instant;
use y4m;
use y4m::Colorspace;
use rav1e::decoder::VideoDetails;
use rav1e::decoder::Decoder;
pub struct EncoderIO {
pub input: Box<dyn Read>,
......@@ -204,23 +205,6 @@ fn parse_config(matches: &ArgMatches) -> EncoderConfig {
cfg
}
pub fn map_y4m_color_space(
color_space: y4m::Colorspace
) -> (ChromaSampling, ChromaSamplePosition) {
use y4m::Colorspace::*;
use ChromaSampling::*;
use ChromaSamplePosition::*;
match color_space {
C420jpeg | C420paldv => (Cs420, Unknown),
C420mpeg2 => (Cs420, Vertical),
C420 | C420p10 | C420p12 => (Cs420, Colocated),
C422 | C422p10 | C422p12 => (Cs422, Colocated),
C444 | C444p10 | C444p12 => (Cs444, Colocated),
_ =>
panic!("Chroma characteristics unknown for the specified color space.")
}
}
#[derive(Debug, Clone, Copy)]
pub struct FrameSummary {
// Frame size in bytes
......@@ -257,36 +241,17 @@ impl fmt::Display for FrameSummary {
}
}
fn read_frame_batch(ctx: &mut Context, y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>, y4m_details: Y4MDetails) {
fn read_frame_batch<D: Decoder>(ctx: &mut Context, decoder: &mut D, video_info: VideoDetails) {
loop {
if ctx.needs_more_lookahead() {
match y4m_dec.read_frame() {
Ok(y4m_frame) => {
let y4m_y = y4m_frame.get_y_plane();
let y4m_u = y4m_frame.get_u_plane();
let y4m_v = y4m_frame.get_v_plane();
let mut input = ctx.new_frame();
{
let input = Arc::get_mut(&mut input).unwrap();
input.planes[0].copy_from_raw_u8(&y4m_y, y4m_details.width * y4m_details.bytes, y4m_details.bytes);
input.planes[1].copy_from_raw_u8(
&y4m_u,
y4m_details.width * y4m_details.bytes / 2,
y4m_details.bytes
);
input.planes[2].copy_from_raw_u8(
&y4m_v,
y4m_details.width * y4m_details.bytes / 2,
y4m_details.bytes
);
}
match y4m_details.bits {
match decoder.read_frame(&video_info) {
Ok(frame) => {
match video_info.bits {
8 | 10 | 12 => {}
_ => panic!("unknown input bit depth!")
}
let _ = ctx.send_frame(Some(input));
let _ = ctx.send_frame(Some(Arc::new(frame)));
continue;
}
_ => {
......@@ -302,35 +267,6 @@ fn read_frame_batch(ctx: &mut Context, y4m_dec: &mut y4m::Decoder<'_, Box<dyn Re
}
}
#[derive(Debug, Clone, Copy)]
struct Y4MDetails {
width: usize,
height: usize,
bits: usize,
bytes: usize,
color_space: Colorspace,
bit_depth: usize,
}
impl Y4MDetails {
fn new(y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>) -> Self {
let width = y4m_dec.get_width();
let height = y4m_dec.get_height();
let bits = y4m_dec.get_bit_depth();
let bytes = y4m_dec.get_bytes_per_sample();
let color_space = y4m_dec.get_colorspace();
let bit_depth = color_space.get_bit_depth();
Y4MDetails {
width,
height,
bits,
bytes,
color_space,
bit_depth,
}
}
}
// Encode and write a frame.
// Returns frame information in a `Result`.
pub fn process_frame(
......@@ -338,7 +274,7 @@ pub fn process_frame(
y4m_dec: &mut y4m::Decoder<'_, Box<dyn Read>>,
mut y4m_enc: Option<&mut y4m::Encoder<'_, Box<dyn Write>>>
) -> Result<Vec<FrameSummary>, ()> {
let y4m_details = Y4MDetails::new(y4m_dec);
let y4m_details = y4m_dec.get_video_details();
let mut frame_summaries = Vec::new();
read_frame_batch(ctx, y4m_dec, y4m_details);
let pkt_wrapped = ctx.receive_packet();
......@@ -347,7 +283,7 @@ pub fn process_frame(
if let Some(y4m_enc_uw) = y4m_enc.as_mut() {
if let Some(ref rec) = pkt.rec {
let pitch_y = if y4m_details.bit_depth > 8 { y4m_details.width * 2 } else { y4m_details.width };
let chroma_sampling_period = map_y4m_color_space(y4m_details.color_space).0.sampling_period();
let chroma_sampling_period = y4m_details.chroma_sampling.sampling_period();
let (pitch_uv, height_uv) = (
pitch_y / chroma_sampling_period.0,
y4m_details.height / chroma_sampling_period.1
......@@ -433,7 +369,7 @@ pub fn process_frame(
#[derive(Debug, Clone)]
pub struct ProgressInfo {
// Frame rate of the video
frame_rate: y4m::Ratio,
frame_rate: Rational,
// The length of the whole video, in frames, if known
total_frames: Option<usize>,
// The time the encode was started
......@@ -450,7 +386,7 @@ pub struct ProgressInfo {
}
impl ProgressInfo {
pub fn new(frame_rate: y4m::Ratio, total_frames: Option<usize>, show_psnr: bool) -> Self {
pub fn new(frame_rate: Rational, total_frames: Option<usize>, show_psnr: bool) -> Self {
Self {
frame_rate,
total_frames,
......
......@@ -17,39 +17,27 @@ use rav1e::*;
use std::io;
use std::io::Write;
use rav1e::decoder::Decoder;
fn main() {
let mut cli = parse_cli();
let mut y4m_dec = y4m::decode(&mut cli.io.input).unwrap();
let width = y4m_dec.get_width();
let height = y4m_dec.get_height();
let framerate = y4m_dec.get_framerate();
let color_space = y4m_dec.get_colorspace();
let video_info = y4m_dec.get_video_details();
let mut y4m_enc = match cli.io.rec.as_mut() {
Some(rec) => Some(
y4m::encode(width, height, framerate)
.with_colorspace(color_space)
y4m::encode(
video_info.width,
video_info.height,
y4m::Ratio::new(video_info.framerate.num as usize, video_info.framerate.den as usize)
).with_colorspace(video_info.color_space)
.write_header(rec)
.unwrap()
),
None => None
};
let (chroma_sampling, chroma_sample_position) = map_y4m_color_space(color_space);
let bit_depth = color_space.get_bit_depth();
let cfg = Config {
frame_info: FrameInfo {
width,
height,
bit_depth,
chroma_sampling,
chroma_sample_position,
..Default::default()
},
timebase: Rational::new(framerate.den as u64, framerate.num as u64),
video_info,
enc: cli.enc
};
......@@ -58,18 +46,25 @@ fn main() {
let stderr = io::stderr();
let mut err = stderr.lock();
let _ = writeln!(err, "{}x{} @ {}/{} fps", width, height, framerate.num, framerate.den);
let _ = writeln!(
err,
"{}x{} @ {}/{} fps",
video_info.width,
video_info.height,
video_info.framerate.num,
video_info.framerate.den
);
write_ivf_header(
&mut cli.io.output,
width,
height,
framerate.num,
framerate.den
video_info.width,
video_info.height,
video_info.framerate.num as usize,
video_info.framerate.den as usize
);
let mut progress = ProgressInfo::new(
framerate,
video_info.framerate,
if cli.limit == 0 { None } else { Some(cli.limit) },
cfg.enc.show_psnr
);
......
use encoder::Frame;
use y4m::Colorspace;
use std::io;
use encoder::ChromaSampling;
use encoder::ChromaSamplePosition;
use api::Rational;
pub mod y4m;
pub trait Decoder {
fn get_video_details(&self) -> VideoDetails;
fn read_frame(&mut self, cfg: &VideoDetails) -> Result<Frame, DecodeError>;
}
#[derive(Debug, Clone, Copy)]
pub struct VideoDetails {
pub width: usize,
pub height: usize,
pub bits: usize,
pub bytes: usize,
pub color_space: Colorspace,
pub bit_depth: usize,
pub chroma_sampling: ChromaSampling,
pub chroma_sample_position: ChromaSamplePosition,
pub framerate: Rational,
}
impl Default for VideoDetails {
fn default() -> Self {
VideoDetails {
width: 640,
height: 480,
bits: 8,
bytes: 1,
color_space: Colorspace::Cmono,
bit_depth: 8,
chroma_sampling: ChromaSampling::Cs420,
chroma_sample_position: ChromaSamplePosition::Unknown,
framerate: Rational { num: 1, den: 1 }
}
}
}
#[derive(Debug)]
pub enum DecodeError {
EOF,
BadInput,
UnknownColorspace,
ParseError,
IoError(io::Error),
}
\ No newline at end of file
use std::io::Read;
use api::Rational;
use decoder::DecodeError;
use decoder::Decoder;
use decoder::VideoDetails;
use encoder::ChromaSamplePosition;
use encoder::ChromaSampling;
use encoder::Frame;
use util::Fixed;
impl Decoder for y4m::Decoder<'_, Box<dyn Read>> {
fn get_video_details(&self) -> VideoDetails {
let width = self.get_width();
let height = self.get_height();
let bits = self.get_bit_depth();
let bytes = self.get_bytes_per_sample();
let color_space = self.get_colorspace();
let bit_depth = color_space.get_bit_depth();
let (chroma_sampling, chroma_sample_position) = map_y4m_color_space(color_space);
let framerate = self.get_framerate();
let framerate = Rational::new(framerate.num as u64, framerate.den as u64);
VideoDetails {
width,
height,
bits,
bytes,
color_space,
bit_depth,
chroma_sampling,
chroma_sample_position,
framerate,
}
}
fn read_frame(&mut self, cfg: &VideoDetails) -> Result<Frame, DecodeError> {
self.read_frame()
.map(|frame| {
let mut f = Frame::new(
cfg.width.align_power_of_two(3),
cfg.height.align_power_of_two(3),
cfg.chroma_sampling
);
f.planes[0].copy_from_raw_u8(frame.get_y_plane(), cfg.width * cfg.bytes, cfg.bytes);
f.planes[1].copy_from_raw_u8(
frame.get_u_plane(),
cfg.width * cfg.bytes / 2,
cfg.bytes
);
f.planes[2].copy_from_raw_u8(
frame.get_v_plane(),
cfg.width * cfg.bytes / 2,
cfg.bytes
);
f
})
.map_err(|e| e.into())
}
}
impl From<y4m::Error> for DecodeError {
fn from(e: y4m::Error) -> DecodeError {
match e {
y4m::Error::EOF => DecodeError::EOF,
y4m::Error::BadInput => DecodeError::BadInput,
y4m::Error::UnknownColorspace => DecodeError::UnknownColorspace,
y4m::Error::ParseError => DecodeError::ParseError,
y4m::Error::IoError(e) => DecodeError::IoError(e),
}
}
}
pub fn map_y4m_color_space(
color_space: y4m::Colorspace
) -> (ChromaSampling, ChromaSamplePosition) {
use y4m::Colorspace::*;
use ChromaSampling::*;
use ChromaSamplePosition::*;
match color_space {
C420jpeg | C420paldv => (Cs420, Unknown),
C420mpeg2 => (Cs420, Vertical),
C420 | C420p10 | C420p12 => (Cs420, Colocated),
C422 | C422p10 | C422p12 => (Cs422, Colocated),
C444 | C444p10 | C444p12 => (Cs444, Colocated),
_ =>
panic!("Chroma characteristics unknown for the specified color space.")
}
}
\ No newline at end of file
......@@ -30,6 +30,7 @@ use std::{fmt, io};
use std::io::Write;
use std::rc::Rc;
use std::sync::Arc;
use decoder::VideoDetails;
extern {
pub fn av1_rtcd();
......@@ -287,7 +288,7 @@ pub struct Sequence {
}
impl Sequence {
pub fn new(info: &FrameInfo) -> Sequence {
pub fn new(info: &VideoDetails) -> Sequence {
let width_bits = 32 - (info.width as u32).leading_zeros();
let height_bits = 32 - (info.height as u32).leading_zeros();
assert!(width_bits <= 16);
......
......@@ -24,6 +24,7 @@ extern crate dav1d_sys;
#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;
extern crate y4m;
pub mod ec;
pub mod partition;
......@@ -41,6 +42,7 @@ pub mod deblock;
pub mod segmentation;
pub mod cdef;
pub mod lrf;
pub mod decoder;
pub mod encoder;
pub mod mc;
pub mod me;
......
......@@ -7,10 +7,10 @@
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.
use api::FrameInfo;
use encoder::Frame;
use std::sync::Arc;
use decoder::VideoDetails;
/// Detects fast cuts using changes in colour and intensity between frames.
/// Since the difference between frames is used, only fast cuts are detected
......@@ -41,9 +41,9 @@ impl Default for SceneChangeDetector {
}
impl SceneChangeDetector {
pub fn new(frame_info: &FrameInfo) -> Self {
pub fn new(video_info: &VideoDetails) -> Self {
let mut detector = Self::default();
detector.threshold = detector.threshold * frame_info.bit_depth as u8 / 8;
detector.threshold = detector.threshold * video_info.bit_depth as u8 / 8;
detector
}
......
......@@ -20,6 +20,11 @@ use std::{mem, ptr, slice};
use std::collections::VecDeque;
use std::ffi::CStr;
use std::sync::Arc;
use decoder::Decoder;
use decoder::VideoDetails;
use decoder::DecodeError;
use util::Fixed;
use y4m::Colorspace;
fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
for plane in frame.planes.iter_mut() {
......@@ -34,7 +39,7 @@ fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
}
struct AomDecoder {
dec: aom_codec_ctx
dec: aom_codec_ctx,
}
fn setup_decoder(w: usize, h: usize) -> AomDecoder {
......@@ -70,6 +75,23 @@ impl Drop for AomDecoder {
}
}
impl Decoder for AomDecoder {
fn get_video_details(&self) -> VideoDetails {
unimplemented!()
}
fn read_frame(&mut self, cfg: &VideoDetails) -> Result<Frame, DecodeError> {
let mut f = Frame::new(
cfg.width.align_power_of_two(3),
cfg.height.align_power_of_two(3),
cfg.chroma_sampling
);
let mut ra = ChaChaRng::from_seed([0; 32]);
fill_frame(&mut ra, &mut f);
Ok(f)
}
}
fn setup_encoder(
w: usize, h: usize, speed: usize, quantizer: usize, bit_depth: usize,
chroma_sampling: ChromaSampling, min_keyint: u64, max_keyint: u64,
......@@ -87,14 +109,17 @@ fn setup_encoder(
enc.low_latency = low_latency;
let cfg = Config {
frame_info: FrameInfo {
video_info: VideoDetails {
width: w,
height: h,
bits: bit_depth,
bytes: 1,
color_space: Colorspace::C420,
bit_depth,
chroma_sampling,
..Default::default()
chroma_sample_position: ChromaSamplePosition::Unknown,
framerate: Rational::new(1000, 1),
},
timebase: Rational::new(1, 1000),
enc
};
......@@ -289,8 +314,6 @@ fn encode_decode(
w: usize, h: usize, speed: usize, quantizer: usize, limit: usize,
bit_depth: usize, min_keyint: u64, max_keyint: u64, low_latency: bool
) {
let mut ra = ChaChaRng::from_seed([0; 32]);
let mut dec = setup_decoder(w, h);
let mut ctx =
setup_encoder(w, h, speed, quantizer, bit_depth, ChromaSampling::Cs420,
......@@ -303,10 +326,8 @@ fn encode_decode(
let mut rec_fifo = VecDeque::new();
for _ in 0..limit {
let mut input = ctx.new_frame();
fill_frame(&mut ra, Arc::get_mut(&mut input).unwrap());
let _ = ctx.send_frame(Some(input));
let mut input = dec.read_frame(&ctx.config.video_info).unwrap();
let _ = ctx.send_frame(Some(Arc::new(input)));
let mut done = false;
let mut corrupted_count = 0;
......
......@@ -14,6 +14,11 @@ use std::collections::VecDeque;
use std::sync::Arc;
use dav1d_sys::*;
use decoder::VideoDetails;
use util::Fixed;
use decoder::DecodeError;
use decoder::Decoder;
use y4m::Colorspace;
fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
for plane in frame.planes.iter_mut() {
......@@ -27,14 +32,31 @@ fn fill_frame(ra: &mut ChaChaRng, frame: &mut Frame) {
}
}
struct Decoder {
struct Dav1dDecoder {
dec: *mut Dav1dContext
}
fn setup_decoder() -> Decoder {
impl Decoder for Dav1dDecoder {
fn get_video_details(&self) -> VideoDetails {
unimplemented!()
}
fn read_frame(&mut self, cfg: &VideoDetails) -> Result<Frame, DecodeError> {
let mut f = Frame::new(
cfg.width.align_power_of_two(3),
cfg.height.align_power_of_two(3),
cfg.chroma_sampling
);
let mut ra = ChaChaRng::from_seed([0; 32]);
fill_frame(&mut ra, &mut f);
Ok(f)
}
}
fn setup_decoder() -> Dav1dDecoder {
unsafe {
let mut settings = mem::uninitialized();
let mut dec: Decoder = mem::uninitialized();
let mut dec: Dav1dDecoder = mem::uninitialized();
dav1d_default_settings(&mut settings);
......@@ -48,7 +70,7 @@ fn setup_decoder() -> Decoder {
}
}
impl Drop for Decoder {
impl Drop for Dav1dDecoder {