extern crate ffmpeg_next as ffmpeg;
use ffmpeg::{
codec::decoder::Video as AvDecoder,
software::scaling::{
context::Context as AvScaler,
flag::Flags as AvScalerFlags,
},
util::{
format::pixel::Pixel as AvPixel,
error::EAGAIN,
},
Error as AvError,
Rational as AvRational,
};
use super::{
Error,
Locator,
RawFrame,
io::Reader,
options::Options,
frame::FRAME_PIXEL_FORMAT,
ffi::copy_frame_props,
};
#[cfg(feature = "ndarray")]
use super::{
Frame,
Time,
ffi::convert_frame_to_ndarray_rgb24,
};
type Result<T> = std::result::Result<T, Error>;
pub struct Decoder {
reader: Reader,
reader_stream_index: usize,
decoder: AvDecoder,
decoder_time_base: AvRational,
scaler: AvScaler,
size: (u32, u32),
size_out: (u32, u32),
frame_rate: f32,
}
impl Decoder {
pub fn new(
source: &Locator,
) -> Result<Self> {
Self::from_reader(
Reader::new(source)?,
None,
)
}
pub fn new_with_options(
source: &Locator,
options: &Options,
) -> Result<Self> {
Self::from_reader(
Reader::new_with_options(source, options)?,
None,
)
}
pub fn new_with_options_and_resize(
source: &Locator,
options: &Options,
resize: Resize,
) -> Result<Self> {
Self::from_reader(
Reader::new_with_options(source, options)?,
Some(resize),
)
}
#[cfg(feature = "ndarray")]
pub fn decode_iter(
&mut self,
) -> impl Iterator<Item=Result<(Time, Frame)>> + '_ {
std::iter::from_fn(move || {
Some(self.decode())
})
}
#[cfg(feature = "ndarray")]
pub fn decode(&mut self) -> Result<(Time, Frame)> {
let frame = &mut self.decode_raw()?;
let timestamp = Time::new(Some(frame.packet().dts), self.decoder_time_base);
let frame = convert_frame_to_ndarray_rgb24(frame)
.map_err(Error::BackendError)?;
Ok((timestamp, frame))
}
pub fn decode_raw_iter(
&mut self,
) -> impl Iterator<Item=Result<RawFrame>> + '_ {
std::iter::from_fn(move || {
Some(self.decode_raw())
})
}
pub fn decode_raw(&mut self) -> Result<RawFrame> {
let mut frame: Option<RawFrame> = None;
while frame.is_none() {
let mut packet = self
.reader
.read(self.reader_stream_index)?
.into_inner();
packet.rescale_ts(self.stream_time_base(), self.decoder_time_base);
self.decoder.send_packet(&packet)
.map_err(Error::BackendError)?;
frame = self.decoder_receive_frame()?;
}
let frame = frame.unwrap();
let mut frame_scaled = RawFrame::empty();
self
.scaler
.run(&frame, &mut frame_scaled)
.map_err(Error::BackendError)?;
copy_frame_props(&frame, &mut frame_scaled);
Ok(frame_scaled)
}
pub fn size(&self) -> (u32, u32) {
self.size
}
pub fn size_out(&self) -> (u32, u32) {
self.size_out
}
pub fn frame_rate(&self) -> f32 {
self.frame_rate
}
fn from_reader(
reader: Reader,
resize: Option<Resize>,
) -> Result<Self> {
let reader_stream_index = reader.best_video_stream_index()?;
let reader_stream = reader
.input
.stream(reader_stream_index)
.ok_or(AvError::StreamNotFound)?;
let frame_rate = reader_stream.rate();
let frame_rate = frame_rate.numerator() as f32 / frame_rate.denominator() as f32;
let codec = reader_stream.codec();
let decoder = codec
.decoder()
.video()?;
let decoder_time_base = decoder.time_base();
let (resize_width, resize_height) = match resize {
Some(Resize::Exact(w, h)) => {
Ok((w, h))
},
Some(Resize::Fit(w, h)) => {
calculate_fit_dims(
(decoder.width(), decoder.height()),
(w, h),
).ok_or_else(|| Error::InvalidResizeParameters)
},
Some(Resize::FitEven(w, h)) => {
calculate_fit_dims_even(
(decoder.width(), decoder.height()),
(w, h),
).ok_or_else(|| Error::InvalidResizeParameters)
},
None => {
Ok((decoder.width(), decoder.height()))
}
}?;
if decoder.format() == AvPixel::None ||
decoder.width() == 0 || decoder.height() == 0 {
return Err(Error::MissingCodecParameters);
}
let scaler = AvScaler::get(
decoder.format(),
decoder.width(),
decoder.height(),
FRAME_PIXEL_FORMAT,
resize_width,
resize_height,
AvScalerFlags::AREA)?;
let size = (decoder.width(), decoder.height());
let size_out = (resize_width, resize_height);
Ok(Self {
reader,
reader_stream_index,
decoder,
decoder_time_base,
scaler,
size,
size_out,
frame_rate,
})
}
fn decoder_receive_frame(&mut self) -> Result<Option<RawFrame>> {
let mut frame = RawFrame::empty();
let decode_result = self.decoder.receive_frame(&mut frame);
match decode_result {
Ok(())
=> Ok(Some(frame)),
Err(AvError::Other { errno }) if errno == EAGAIN
=> Ok(None),
Err(err)
=> Err(err.into()),
}
}
fn stream_time_base(&self) -> AvRational {
self
.reader
.input
.stream(self.reader_stream_index)
.unwrap()
.time_base()
}
}
impl Drop for Decoder {
fn drop(&mut self) {
const MAX_DRAIN_ITERATIONS: u32 = 100;
if let Ok(()) = self.decoder.send_eof() {
for _ in 0..MAX_DRAIN_ITERATIONS {
if self.decoder_receive_frame().is_err() {
break;
}
}
}
}
}
pub enum Resize {
Exact(u32, u32),
Fit(u32, u32),
FitEven(u32, u32),
}
fn calculate_fit_dims(
dims: (u32, u32),
fit_dims: (u32, u32),
) -> Option<(u32, u32)> {
let (w, h) = dims;
let (w_max, h_max) = fit_dims;
if w_max >= w && h_max >= h {
Some((w, h))
} else {
let wf = w_max as f32 / w as f32;
let hf = h_max as f32 / h as f32;
let f = wf.min(hf);
let (w_out, h_out) = (
(w as f32 * f) as u32,
(h as f32 * f) as u32,
);
if (w_out > 0) && (h_out > 0) {
Some((w_out, h_out))
} else {
None
}
}
}
fn calculate_fit_dims_even(
dims: (u32, u32),
fit_dims: (u32, u32),
) -> Option<(u32, u32)> {
let (w, h) = dims;
let (mut w_max, mut h_max) = fit_dims;
while w_max > 0 && h_max > 0 {
let wf = w_max as f32 / w as f32;
let hf = h_max as f32 / h as f32;
let f = wf.min(hf).min(1.0);
let out_w = (w as f32 * f).round() as u32;
let out_h = (h as f32 * f).round() as u32;
if (out_w > 0) && (out_h > 0) {
if (out_w % 2 == 0) && (out_h % 2 == 0) {
return Some((out_w, out_h))
} else {
if wf < hf {
w_max -= 1;
} else {
h_max -= 1;
}
}
} else {
break;
}
}
None
}
#[cfg(test)]
mod tests {
use super::{
calculate_fit_dims,
calculate_fit_dims_even,
};
const TESTING_DIM_CANDIDATES: [u32; 8] = [0, 1, 2, 3, 8, 111, 256, 1000];
#[test]
fn calculate_fit_dims_works() {
let testset = generate_testset();
for ((w, h), (fit_w, fit_h)) in testset {
let out = calculate_fit_dims((w, h), (fit_w, fit_h));
if let Some((out_w, out_h)) = out {
let input_dim_zero = w == 0 || h == 0 || fit_w == 0 || fit_h == 0;
let output_dim_zero = out_w == 0 || out_h == 0;
assert!(
(input_dim_zero && output_dim_zero) ||
(!input_dim_zero && !output_dim_zero),
"computed dims are never zero unless the inputs dims were",
);
assert!(
(out_w <= fit_w) && (out_h <= fit_h),
"computed dims fit inside provided dims",
);
}
}
}
#[test]
fn calculate_fit_dims_even_works() {
let testset = generate_testset();
for ((w, h), (fit_w, fit_h)) in testset {
let out = calculate_fit_dims_even((w, h), (fit_w, fit_h));
if let Some((out_w, out_h)) = out {
let input_dim_zero = w == 0 || h == 0 || fit_w == 0 || fit_h == 0;
let output_dim_zero = out_w == 0 || out_h == 0;
assert!(
(input_dim_zero && output_dim_zero) ||
(!input_dim_zero && !output_dim_zero),
"computed dims are never zero unless the inputs dims were",
);
assert!(
(out_w % 2 == 0) && (out_h % 2 == 0),
"computed dims are even",
);
assert!(
(out_w <= fit_w) && (out_h <= fit_h),
"computed dims fit inside provided dims",
);
}
}
}
fn generate_testset() -> Vec<((u32, u32), (u32, u32))> {
let testing_dims = generate_testing_dims();
testing_dims.iter().map(|dims| {
testing_dims.iter().map(|fit_dims| (*dims, *fit_dims))
}).flatten().collect()
}
fn generate_testing_dims() -> Vec<(u32, u32)> {
TESTING_DIM_CANDIDATES.iter().map(|a| {
TESTING_DIM_CANDIDATES.iter().map(|b| (*a, *b))
}).flatten().collect()
}
}