diff --git a/ffmpeg-sys-the-third/src/avutil/pixfmt.rs b/ffmpeg-sys-the-third/src/avutil/pixfmt.rs index b927bbf..c2b0d9c 100644 --- a/ffmpeg-sys-the-third/src/avutil/pixfmt.rs +++ b/ffmpeg-sys-the-third/src/avutil/pixfmt.rs @@ -1,6 +1,26 @@ -use crate::AVPixelFormat; +use libc::c_int; + +use crate::{AVChromaLocation, AVPixelFormat}; use crate::AVPixelFormat::*; +impl AVChromaLocation { + pub fn from_c_int(n: c_int) -> Option { + use AVChromaLocation as AVCL; + + Some(match n { + n if n == AVCL::AVCHROMA_LOC_UNSPECIFIED as c_int => AVCL::AVCHROMA_LOC_UNSPECIFIED, + n if n == AVCL::AVCHROMA_LOC_LEFT as c_int => AVCL::AVCHROMA_LOC_LEFT, + n if n == AVCL::AVCHROMA_LOC_CENTER as c_int => AVCL::AVCHROMA_LOC_CENTER, + n if n == AVCL::AVCHROMA_LOC_TOPLEFT as c_int => AVCL::AVCHROMA_LOC_TOPLEFT, + n if n == AVCL::AVCHROMA_LOC_TOP as c_int => AVCL::AVCHROMA_LOC_TOP, + n if n == AVCL::AVCHROMA_LOC_BOTTOMLEFT as c_int => AVCL::AVCHROMA_LOC_BOTTOMLEFT, + n if n == AVCL::AVCHROMA_LOC_BOTTOM as c_int => AVCL::AVCHROMA_LOC_BOTTOM, + + _ => return None, + }) + } +} + #[cfg(target_endian = "little")] pub const AV_PIX_FMT_RGB32: AVPixelFormat = AV_PIX_FMT_BGRA; #[cfg(target_endian = "little")] diff --git a/src/util/chroma/location.rs b/src/util/chroma/location.rs index 3022937..2a8b611 100644 --- a/src/util/chroma/location.rs +++ b/src/util/chroma/location.rs @@ -1,5 +1,9 @@ +use std::ffi::CString; + use crate::ffi::AVChromaLocation::*; use crate::ffi::*; +use crate::utils; +use crate::Error; #[cfg(feature = "serialize")] use serde::{Deserialize, Serialize}; @@ -15,6 +19,55 @@ pub enum Location { Bottom, } +impl Location { + /// Returns the name of this location. Should usually return Some(name). + pub fn name(self) -> Option<&'static str> { + unsafe { utils::optional_str_from_c_ptr(av_chroma_location_name(self.into())) } + } + + /// Returns a chroma location for the given name or an error if the `name` is invalid + /// or no location was found. + pub fn from_name>(name: S) -> Result { + let Ok(cstr) = CString::new(name.as_ref()) else { + // invalid argument (contains a nul byte) + return Err(Error::from(AVERROR(libc::EINVAL))); + }; + + let ret = unsafe { av_chroma_location_from_name(cstr.as_ptr()) }; + + if ret < 0 { + Err(Error::from(ret)) + } else { + AVChromaLocation::from_c_int(ret) + .map(Location::from) + .ok_or(Error::from(AVERROR(libc::EINVAL))) + } + } + + /// Returns the swscale (x, y) chroma positions for this chroma location. + /// Will panic if `self` is [`Unspecified`][Location::Unspecified]. + #[cfg(feature = "ffmpeg_6_0")] + pub fn pos(self) -> (i32, i32) { + let mut xpos = 0; + let mut ypos = 0; + let ret = unsafe { av_chroma_location_enum_to_pos(&mut xpos, &mut ypos, self.into()) }; + assert_eq!(ret, 0, "av_chroma_location_enum_to_pos returned an error"); + + (xpos as i32, ypos as i32) + } + + /// Returns a chroma location for the given swscale chroma position. + #[cfg(feature = "ffmpeg_6_0")] + pub fn from_pos(x: i32, y: i32) -> Self { + unsafe { + Self::from(av_chroma_location_pos_to_enum( + x as libc::c_int, + y as libc::c_int, + )) + } + } +} + impl From for Location { fn from(value: AVChromaLocation) -> Self { match value { @@ -46,3 +99,50 @@ impl From for AVChromaLocation { } } } + +#[cfg(test)] +mod test { + use libc::EINVAL; + + use super::*; + + #[test] + fn name() { + assert_eq!(Location::BottomLeft.name(), Some("bottomleft")); + assert_eq!(Location::Center.name(), Some("center")); + assert_eq!(Location::Unspecified.name(), Some("unspecified")); + } + + #[test] + fn from_name() { + assert_eq!(Location::from_name("topleft"), Ok(Location::TopLeft)); + assert_eq!( + Location::from_name("asdf"), + Err(Error::Other { errno: EINVAL }) + ); + + let name = "test".to_string() + "\0something else"; + + // important: no panic or segfault! + assert_eq!( + Location::from_name(name), + Err(Error::Other { errno: EINVAL }) + ); + } + + #[test] + #[cfg(feature = "ffmpeg_6_0")] + fn pos() { + assert_eq!(Location::BottomLeft.pos(), (0, 256)); + assert_eq!(Location::Left.pos(), (0, 128)); + assert_eq!(Location::Center.pos(), (128, 128)); + } + + #[test] + #[cfg(feature = "ffmpeg_6_0")] + fn from_pos() { + assert_eq!(Location::from_pos(0, 128), Location::Left); + assert_eq!(Location::from_pos(128, 0), Location::Top); + assert_eq!(Location::from_pos(10, 20), Location::Unspecified); + } +}