diff --git a/Cargo.toml b/Cargo.toml index e3a82af..2037c1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,16 +5,16 @@ authors = ["meh. "] license = "WTFPL" [features] -default = ["codec", "device", "filter", "format", "resample", "postprocessing", "software-resample", "software-scaling"] +default = ["codec", "device", "filter", "format", "resampling", "postprocessing", "software-resampling", "software-scaling"] -codec = ["ffmpeg-sys/avcodec"] -device = ["ffmpeg-sys/avdevice", "format"] -filter = ["ffmpeg-sys/avfilter"] -format = ["ffmpeg-sys/avformat", "codec"] -resample = ["ffmpeg-sys/avresample"] -postprocessing = ["ffmpeg-sys/postproc"] -software-resample = ["ffmpeg-sys/swresample"] -software-scaling = ["ffmpeg-sys/swscale"] +codec = ["ffmpeg-sys/avcodec"] +device = ["ffmpeg-sys/avdevice", "format"] +filter = ["ffmpeg-sys/avfilter"] +format = ["ffmpeg-sys/avformat", "codec"] +resampling = ["ffmpeg-sys/avresample"] +postprocessing = ["ffmpeg-sys/postproc"] +software-resampling = ["ffmpeg-sys/swresample"] +software-scaling = ["ffmpeg-sys/swscale", "codec"] [dependencies] libc = "0.1" diff --git a/src/lib.rs b/src/lib.rs index f4b69e8..fc3e71d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,8 @@ pub use codec::threading; #[cfg(feature = "device")] pub mod device; +pub mod software; + fn init_error() { util::error::register_all(); } diff --git a/src/software/mod.rs b/src/software/mod.rs new file mode 100644 index 0000000..c22302f --- /dev/null +++ b/src/software/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature = "software-scaling")] +pub mod scaling; diff --git a/src/software/scaling/capability.rs b/src/software/scaling/capability.rs new file mode 100644 index 0000000..f6aa35b --- /dev/null +++ b/src/software/scaling/capability.rs @@ -0,0 +1,14 @@ +use libc::c_uint; +use ffi::*; + +bitflags! { + flags Capabilities: c_uint { + const MMX = SWS_CPU_CAPS_MMX, + const MMXEXT = SWS_CPU_CAPS_MMXEXT, + const MMX2 = SWS_CPU_CAPS_MMX2, + const _3DNOW = SWS_CPU_CAPS_3DNOW, + const ALTIVEC = SWS_CPU_CAPS_ALTIVEC, + const BFIN = SWS_CPU_CAPS_BFIN, + const SSE2 = SWS_CPU_CAPS_SSE2, + } +} diff --git a/src/software/scaling/color_space.rs b/src/software/scaling/color_space.rs new file mode 100644 index 0000000..3707f92 --- /dev/null +++ b/src/software/scaling/color_space.rs @@ -0,0 +1,41 @@ +use libc::c_int; +use ffi::*; + +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub enum ColorSpace { + Default, + + ITU709, + FCC, + ITU601, + ITU624, + SMPTE170M, + SMPTE240M, +} + +impl From for ColorSpace { + fn from(value: c_int) -> ColorSpace { + match value { + SWS_CS_ITU709 => ColorSpace::ITU709, + SWS_CS_FCC => ColorSpace::FCC, + SWS_CS_DEFAULT => ColorSpace::Default, + SWS_CS_SMPTE240M => ColorSpace::SMPTE240M, + + _ => ColorSpace::Default + } + } +} + +impl Into for ColorSpace { + fn into(self) -> c_int { + match self { + ColorSpace::Default => SWS_CS_DEFAULT, + ColorSpace::ITU709 => SWS_CS_ITU709, + ColorSpace::FCC => SWS_CS_FCC, + ColorSpace::ITU601 => SWS_CS_ITU601, + ColorSpace::ITU624 => SWS_CS_ITU624, + ColorSpace::SMPTE170M => SWS_CS_SMPTE170M, + ColorSpace::SMPTE240M => SWS_CS_SMPTE240M + } + } +} diff --git a/src/software/scaling/context.rs b/src/software/scaling/context.rs new file mode 100644 index 0000000..a41cefe --- /dev/null +++ b/src/software/scaling/context.rs @@ -0,0 +1,102 @@ +use std::ptr; + +use libc::{c_int}; +use ffi::*; +use ::{Error, Picture}; +use ::util::format; +use super::Flags; + +pub struct Context { + pub ptr: *mut SwsContext, + + input: Definition, + output: Definition, +} + +#[derive(Eq, PartialEq, Copy, Clone, Debug)] +pub struct Definition { + pub format: format::Pixel, + pub width: u32, + pub height: u32, +} + +impl Context { + pub fn get(src_format: format::Pixel, src_w: u32, src_h: u32, + dst_format: format::Pixel, dst_w: u32, dst_h: u32, + flags: Flags) -> Result { + unsafe { + let ptr = sws_getContext(src_w as c_int, src_h as c_int, src_format.into(), + dst_w as c_int, dst_h as c_int, dst_format.into(), + flags.bits(), + ptr::null_mut(), ptr::null_mut(), ptr::null_mut()); + + if ptr != ptr::null_mut() { + Ok(Context { + ptr: ptr, + + input: Definition { + format: src_format, + width: src_w, + height: src_h, + }, + + output: Definition { + format: dst_format, + width: dst_w, + height: dst_h, + }, + }) + } + else { + Err(Error::InvalidData) + } + } + } + + pub fn cached(&mut self, + src_format: format::Pixel, src_w: u32, src_h: u32, + dst_format: format::Pixel, dst_w: u32, dst_h: u32, + flags: Flags) { + unsafe { + self.ptr = sws_getCachedContext(self.ptr, + src_w as c_int, src_h as c_int, src_format.into(), + dst_w as c_int, dst_h as c_int, dst_format.into(), + flags.bits(), ptr::null_mut(), ptr::null_mut(), ptr::null()); + } + } + + pub fn input(&self) -> &Definition { + &self.input + } + + pub fn output(&self) -> &Definition { + &self.output + } + + pub fn run(&self, input: &Picture, output: &mut Picture) -> Result<(), Error> { + if input.format() != self.input.format || input.width() != self.input.width || input.height() != self.input.height { + return Err(Error::InputChanged); + } + + if output.format() != self.output.format || output.width() != self.output.width || output.height() != self.output.height { + return Err(Error::OutputChanged); + } + + unsafe { + sws_scale(self.ptr, + (*input.ptr).data.as_ptr() as *const *const _, (*input.ptr).linesize.as_ptr() as *const _, + 0, self.output.height as c_int, + (*output.ptr).data.as_ptr() as *mut *mut _, (*output.ptr).linesize.as_ptr() as *mut _); + } + + Ok(()) + } +} + +impl Drop for Context { + fn drop(&mut self) { + unsafe { + sws_freeContext(self.ptr); + } + } +} diff --git a/src/software/scaling/extensions.rs b/src/software/scaling/extensions.rs new file mode 100644 index 0000000..911c952 --- /dev/null +++ b/src/software/scaling/extensions.rs @@ -0,0 +1,35 @@ +use std::ops::Deref; + +use libc::c_int; +use ffi::*; +use util::format; +use ::{Picture, decoder, Error}; +use super::{Context, Flags, flag}; + +impl<'a> Picture<'a> { + pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { + Context::get(self.format(), self.width(), self.height(), + self.format(), width, height, + flags) + } + + pub fn converter(&self, format: format::Pixel) -> Result { + Context::get(self.format(), self.width(), self.height(), + format, self.width(), self.height(), + flag::FAST_BILINEAR) + } +} + +impl decoder::Video { + pub fn scaler(&self, width: u32, height: u32, flags: Flags) -> Result { + Context::get(self.format(), self.width(), self.height(), + self.format(), width, height, + flags) + } + + pub fn converter(&self, format: format::Pixel) -> Result { + Context::get(self.format(), self.width(), self.height(), + format, self.width(), self.height(), + flag::FAST_BILINEAR) + } +} diff --git a/src/software/scaling/filter.rs b/src/software/scaling/filter.rs new file mode 100644 index 0000000..d391305 --- /dev/null +++ b/src/software/scaling/filter.rs @@ -0,0 +1,80 @@ +use ffi::*; +use super::Vector; + +pub struct Filter { + pub ptr: *mut SwsFilter, +} + +impl Filter { + pub fn get(luma_g_blur: f32, chroma_g_blur: f32, + luma_sharpen: f32, chroma_sharpen: f32, + chroma_h_shift: f32, chroma_v_shift: f32) -> Self { + unsafe { + Filter { + ptr: sws_getDefaultFilter(luma_g_blur, chroma_g_blur, + luma_sharpen, chroma_sharpen, + chroma_h_shift, chroma_v_shift, 0) + } + } + } + + pub fn new() -> Self { + Self::get(0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + } + + pub fn luma_horizontal(&self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumH) + } + } + + pub fn luma_horizontal_mut(&mut self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumH) + } + } + + pub fn luma_vertical(&self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumV) + } + } + + pub fn luma_vertical_mut(&mut self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumV) + } + } + + pub fn chroma_horizontal(&self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumV) + } + } + + pub fn chroma_horizontal_mut(&mut self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumV) + } + } + + pub fn chroma_vertical(&self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumV) + } + } + + pub fn chroma_vertical_mut(&mut self) -> Vector { + unsafe { + Vector::wrap((*self.ptr).lumV) + } + } +} + +impl Drop for Filter { + fn drop(&mut self) { + unsafe { + sws_freeFilter(self.ptr); + } + } +} diff --git a/src/software/scaling/flag.rs b/src/software/scaling/flag.rs new file mode 100644 index 0000000..d0e2530 --- /dev/null +++ b/src/software/scaling/flag.rs @@ -0,0 +1,28 @@ +use libc::c_int; +use ffi::*; + +bitflags! { + flags Flags: c_int { + const FAST_BILINEAR = SWS_FAST_BILINEAR, + const BILINEAR = SWS_BILINEAR, + const BICUBIC = SWS_BICUBIC, + const X = SWS_X, + const POINT = SWS_POINT, + const AREA = SWS_AREA, + const BICUBLIN = SWS_BICUBLIN, + const GAUSS = SWS_GAUSS, + const SINC = SWS_SINC, + const LANCZOS = SWS_LANCZOS, + const SPLINE = SWS_SPLINE, + const SRC_V_CHR_DROP_MASK = SWS_SRC_V_CHR_DROP_MASK, + const SRC_V_CHR_DROP_SHIFT = SWS_SRC_V_CHR_DROP_SHIFT, + const PARAM_DEFAULT = SWS_PARAM_DEFAULT, + const PRINT_INFO = SWS_PRINT_INFO, + const FULL_CHR_H_INT = SWS_FULL_CHR_H_INT, + const FULL_CHR_H_INP = SWS_FULL_CHR_H_INP, + const DIRECT_BGR = SWS_DIRECT_BGR, + const ACCURATE_RND = SWS_ACCURATE_RND, + const BITEXACT = SWS_BITEXACT, + const ERROR_DIFFUSION = SWS_ERROR_DIFFUSION, + } +} diff --git a/src/software/scaling/mod.rs b/src/software/scaling/mod.rs new file mode 100644 index 0000000..ff4ea55 --- /dev/null +++ b/src/software/scaling/mod.rs @@ -0,0 +1,44 @@ +pub mod flag; +pub use self::flag::Flags; + +pub mod capability; +pub use self::capability::Capabilities; + +pub mod color_space; +pub use self::color_space::ColorSpace; + +pub mod support; + +pub mod vector; +pub use self::vector::Vector; + +pub mod filter; +pub use self::filter::Filter; + +pub mod context; +pub use self::context::Context; + +mod extensions; + +use std::ffi::CStr; +use std::str::from_utf8_unchecked; + +use ffi::*; + +pub fn version() -> u32 { + unsafe { + swscale_version() + } +} + +pub fn configuration() -> &'static str { + unsafe { + from_utf8_unchecked(CStr::from_ptr(swscale_configuration()).to_bytes()) + } +} + +pub fn license() -> &'static str { + unsafe { + from_utf8_unchecked(CStr::from_ptr(swscale_license()).to_bytes()) + } +} diff --git a/src/software/scaling/support.rs b/src/software/scaling/support.rs new file mode 100644 index 0000000..bf7c254 --- /dev/null +++ b/src/software/scaling/support.rs @@ -0,0 +1,20 @@ +use ffi::*; +use ::util::format; + +pub fn input(format: format::Pixel) -> bool { + unsafe { + sws_isSupportedInput(format.into()) != 0 + } +} + +pub fn output(format: format::Pixel) -> bool { + unsafe { + sws_isSupportedOutput(format.into()) != 0 + } +} + +pub fn endianness_conversion(format: format::Pixel) -> bool { + unsafe { + sws_isSupportedEndiannessConversion(format.into()) != 0 + } +} diff --git a/src/software/scaling/vector.rs b/src/software/scaling/vector.rs new file mode 100644 index 0000000..aea265d --- /dev/null +++ b/src/software/scaling/vector.rs @@ -0,0 +1,108 @@ +use std::marker::PhantomData; +use std::slice; + +use libc::{c_int, c_double}; +use ffi::*; + +pub struct Vector<'a> { + pub ptr: *mut SwsVector, + + _own: bool, + _marker: PhantomData<&'a ()>, +} + +impl<'a> Vector<'a> { + pub fn wrap(ptr: *mut SwsVector) -> Self { + Vector { ptr: ptr, _own: false, _marker: PhantomData } + } + + pub fn new(length: usize) -> Self { + unsafe { + Vector { ptr: sws_allocVec(length as c_int), _own: true, _marker: PhantomData } + } + } + + pub fn gaussian(variance: f64, quality: f64) -> Self { + unsafe { + Vector { ptr: sws_getGaussianVec(variance as c_double, quality as c_double), _own: true, _marker: PhantomData } + } + } + + pub fn value(value: f64, length: usize) -> Self { + unsafe { + Vector { ptr: sws_getConstVec(value as c_double, length as c_int), _own: true, _marker: PhantomData } + } + } + + pub fn identity() -> Self { + unsafe { + Vector { ptr: sws_getIdentityVec(), _own: true, _marker: PhantomData } + } + } + + pub fn scale(&mut self, scalar: f64) { + unsafe { + sws_scaleVec(self.ptr, scalar as c_double); + } + } + + pub fn normalize(&mut self, height: f64) { + unsafe { + sws_normalizeVec(self.ptr, height as c_double); + } + } + + pub fn conv(&mut self, other: &Vector) { + unsafe { + sws_convVec(self.ptr, other.ptr); + } + } + + pub fn add(&mut self, other: &Vector) { + unsafe { + sws_addVec(self.ptr, other.ptr); + } + } + + pub fn sub(&mut self, other: &Vector) { + unsafe { + sws_subVec(self.ptr, other.ptr); + } + } + + pub fn shift(&mut self, value: usize) { + unsafe { + sws_shiftVec(self.ptr, value as c_int); + } + } + + pub fn coefficients(&self) -> &[f64] { + unsafe { + slice::from_raw_parts((*self.ptr).coeff, (*self.ptr).length as usize) + } + } + + pub fn coefficients_mut(&self) -> &[f64] { + unsafe { + slice::from_raw_parts_mut((*self.ptr).coeff, (*self.ptr).length as usize) + } + } +} + +impl<'a> Clone for Vector<'a> { + fn clone(&self) -> Self { + unsafe { + Vector { ptr: sws_cloneVec(self.ptr), _own: true, _marker: PhantomData } + } + } +} + +impl<'a> Drop for Vector<'a> { + fn drop(&mut self) { + unsafe { + if self._own { + sws_freeVec(self.ptr); + } + } + } +}