diff --git a/gstreamer-pbutils/Gir.toml b/gstreamer-pbutils/Gir.toml index 47d4e4607..e64f586e9 100644 --- a/gstreamer-pbutils/Gir.toml +++ b/gstreamer-pbutils/Gir.toml @@ -83,10 +83,89 @@ status = "generate" pattern = ".+" ignore = true - # Codec utils need some special care [[object.function]] - pattern = "codec_utils.*" - ignore = true + pattern = "codec_utils_(aac_caps_set_level_and_profile|h264_caps_set_level_and_profile|h265_caps_set_level_tier_and_profile|mpeg4video_caps_set_level_and_profile)" + # Needs mutable caps references and checks for the caps + manual = true + + [[object.function]] + name = "codec_utils_aac_get_level" + [object.function.return] + nullable_return_is_error = "Failed to get AAC level" + + [[object.function]] + name = "codec_utils_aac_get_profile" + [object.function.return] + nullable_return_is_error = "Failed to get AAC profile" + + [[object.function]] + name = "codec_utils_h264_get_level" + [object.function.return] + nullable_return_is_error = "Failed to get H264 level" + + [[object.function]] + name = "codec_utils_h264_get_profile" + [object.function.return] + nullable_return_is_error = "Failed to get H264 profile" + + [[object.function]] + name = "codec_utils_h264_get_profile_flags_level" + manual = true + + [[object.function]] + name = "codec_utils_h265_get_level" + [object.function.return] + nullable_return_is_error = "Failed to get H265 level" + + [[object.function]] + name = "codec_utils_h265_get_profile" + [object.function.return] + nullable_return_is_error = "Failed to get H265 profile" + + [[object.function]] + name = "codec_utils_h265_get_tier" + [object.function.return] + nullable_return_is_error = "Failed to get H265 tier" + + [[object.function]] + name = "codec_utils_mpeg4video_get_level" + [object.function.return] + nullable_return_is_error = "Failed to get MPEG4 video level" + + [[object.function]] + name = "codec_utils_mpeg4video_get_profile" + [object.function.return] + nullable_return_is_error = "Failed to get MPEG4 video profile" + + [[object.function]] + name = "codec_utils_caps_get_mime_codec" + [object.function.return] + nullable_return_is_error = "Unsupported caps" + + [[object.function]] + name = "codec_utils_opus_create_caps_from_header" + [object.function.return] + nullable_return_is_error = "Failed to create caps from Opus headers" + + [[object.function]] + name = "codec_utils_opus_create_caps" + # Manual checks + manual = true + + [[object.function]] + name = "codec_utils_opus_create_header" + # Manual checks + manual = true + + [[object.function]] + name = "codec_utils_opus_parse_caps" + # Manual checks + manual = true + + [[object.function]] + name = "codec_utils_opus_parse_header" + # Manual checks + manual = true # Plugin installer API needs some manual impls [[object.function]] diff --git a/gstreamer-pbutils/src/auto/functions.rs b/gstreamer-pbutils/src/auto/functions.rs index ef4012021..44927a71f 100644 --- a/gstreamer-pbutils/src/auto/functions.rs +++ b/gstreamer-pbutils/src/auto/functions.rs @@ -10,6 +10,203 @@ use crate::PbUtilsCapsDescriptionFlags; use glib::translate::*; use std::mem; +#[cfg(any(feature = "v1_10", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_10")))] +#[doc(alias = "gst_codec_utils_aac_get_channels")] +pub fn codec_utils_aac_get_channels(audio_config: &[u8]) -> u32 { + assert_initialized_main_thread!(); + let len = audio_config.len() as u32; + unsafe { ffi::gst_codec_utils_aac_get_channels(audio_config.to_glib_none().0, len) } +} + +#[doc(alias = "gst_codec_utils_aac_get_index_from_sample_rate")] +pub fn codec_utils_aac_get_index_from_sample_rate(rate: u32) -> i32 { + assert_initialized_main_thread!(); + unsafe { ffi::gst_codec_utils_aac_get_index_from_sample_rate(rate) } +} + +#[doc(alias = "gst_codec_utils_aac_get_level")] +pub fn codec_utils_aac_get_level(audio_config: &[u8]) -> Result { + assert_initialized_main_thread!(); + let len = audio_config.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_aac_get_level( + audio_config.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get AAC level")) + } +} + +#[doc(alias = "gst_codec_utils_aac_get_profile")] +pub fn codec_utils_aac_get_profile(audio_config: &[u8]) -> Result { + assert_initialized_main_thread!(); + let len = audio_config.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_aac_get_profile( + audio_config.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get AAC profile")) + } +} + +#[cfg(any(feature = "v1_10", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_10")))] +#[doc(alias = "gst_codec_utils_aac_get_sample_rate")] +pub fn codec_utils_aac_get_sample_rate(audio_config: &[u8]) -> u32 { + assert_initialized_main_thread!(); + let len = audio_config.len() as u32; + unsafe { ffi::gst_codec_utils_aac_get_sample_rate(audio_config.to_glib_none().0, len) } +} + +#[doc(alias = "gst_codec_utils_aac_get_sample_rate_from_index")] +pub fn codec_utils_aac_get_sample_rate_from_index(sr_idx: u32) -> u32 { + assert_initialized_main_thread!(); + unsafe { ffi::gst_codec_utils_aac_get_sample_rate_from_index(sr_idx) } +} + +#[cfg(any(feature = "v1_20", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] +#[doc(alias = "gst_codec_utils_caps_get_mime_codec")] +pub fn codec_utils_caps_get_mime_codec(caps: &gst::Caps) -> Result { + assert_initialized_main_thread!(); + unsafe { + Option::<_>::from_glib_full(ffi::gst_codec_utils_caps_get_mime_codec( + caps.to_glib_none().0, + )) + .ok_or_else(|| glib::bool_error!("Unsupported caps")) + } +} + +#[doc(alias = "gst_codec_utils_h264_get_level")] +pub fn codec_utils_h264_get_level(sps: &[u8]) -> Result { + assert_initialized_main_thread!(); + let len = sps.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_h264_get_level( + sps.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get H264 level")) + } +} + +#[doc(alias = "gst_codec_utils_h264_get_level_idc")] +pub fn codec_utils_h264_get_level_idc(level: &str) -> u8 { + assert_initialized_main_thread!(); + unsafe { ffi::gst_codec_utils_h264_get_level_idc(level.to_glib_none().0) } +} + +#[doc(alias = "gst_codec_utils_h264_get_profile")] +pub fn codec_utils_h264_get_profile(sps: &[u8]) -> Result { + assert_initialized_main_thread!(); + let len = sps.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_h264_get_profile( + sps.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get H264 profile")) + } +} + +#[doc(alias = "gst_codec_utils_h265_get_level")] +pub fn codec_utils_h265_get_level( + profile_tier_level: &[u8], +) -> Result { + assert_initialized_main_thread!(); + let len = profile_tier_level.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_h265_get_level( + profile_tier_level.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get H265 level")) + } +} + +#[doc(alias = "gst_codec_utils_h265_get_level_idc")] +pub fn codec_utils_h265_get_level_idc(level: &str) -> u8 { + assert_initialized_main_thread!(); + unsafe { ffi::gst_codec_utils_h265_get_level_idc(level.to_glib_none().0) } +} + +#[doc(alias = "gst_codec_utils_h265_get_profile")] +pub fn codec_utils_h265_get_profile( + profile_tier_level: &[u8], +) -> Result { + assert_initialized_main_thread!(); + let len = profile_tier_level.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_h265_get_profile( + profile_tier_level.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get H265 profile")) + } +} + +#[doc(alias = "gst_codec_utils_h265_get_tier")] +pub fn codec_utils_h265_get_tier( + profile_tier_level: &[u8], +) -> Result { + assert_initialized_main_thread!(); + let len = profile_tier_level.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_h265_get_tier( + profile_tier_level.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get H265 tier")) + } +} + +#[doc(alias = "gst_codec_utils_mpeg4video_get_level")] +pub fn codec_utils_mpeg4video_get_level( + vis_obj_seq: &[u8], +) -> Result { + assert_initialized_main_thread!(); + let len = vis_obj_seq.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_mpeg4video_get_level( + vis_obj_seq.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get MPEG4 video level")) + } +} + +#[doc(alias = "gst_codec_utils_mpeg4video_get_profile")] +pub fn codec_utils_mpeg4video_get_profile( + vis_obj_seq: &[u8], +) -> Result { + assert_initialized_main_thread!(); + let len = vis_obj_seq.len() as u32; + unsafe { + Option::<_>::from_glib_none(ffi::gst_codec_utils_mpeg4video_get_profile( + vis_obj_seq.to_glib_none().0, + len, + )) + .ok_or_else(|| glib::bool_error!("Failed to get MPEG4 video profile")) + } +} + +#[doc(alias = "gst_codec_utils_opus_create_caps_from_header")] +pub fn codec_utils_opus_create_caps_from_header( + header: &gst::Buffer, + comments: Option<&gst::Buffer>, +) -> Result { + assert_initialized_main_thread!(); + unsafe { + Option::<_>::from_glib_full(ffi::gst_codec_utils_opus_create_caps_from_header( + header.to_glib_none().0, + comments.to_glib_none().0, + )) + .ok_or_else(|| glib::bool_error!("Failed to create caps from Opus headers")) + } +} + #[doc(alias = "gst_encoding_list_all_targets")] pub fn encoding_list_all_targets(categoryname: Option<&str>) -> Vec { assert_initialized_main_thread!(); diff --git a/gstreamer-pbutils/src/functions.rs b/gstreamer-pbutils/src/functions.rs index 26a21849b..9d4d3e0c8 100644 --- a/gstreamer-pbutils/src/functions.rs +++ b/gstreamer-pbutils/src/functions.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use glib::translate::*; -use std::ptr; +use std::{mem, ptr}; pub unsafe trait CodecTag<'a>: gst::Tag<'a, TagType = &'a str> {} @@ -85,3 +85,324 @@ pub fn pb_utils_get_codec_description( } } } + +#[doc(alias = "gst_codec_utils_aac_caps_set_level_and_profile")] +pub fn codec_utils_aac_caps_set_level_and_profile( + caps: &mut gst::CapsRef, + audio_config: &[u8], +) -> Result<(), glib::BoolError> { + assert_initialized_main_thread!(); + + assert_eq!(caps.size(), 1); + + let s = caps.structure(0).unwrap(); + assert_eq!(s.name(), "audio/mpeg"); + assert!(s + .get::("mpegversion") + .map_or(false, |v| v == 2 || v == 4)); + + let len = audio_config.len() as u32; + unsafe { + let res: bool = from_glib(ffi::gst_codec_utils_aac_caps_set_level_and_profile( + caps.as_mut_ptr(), + audio_config.to_glib_none().0, + len, + )); + + if res { + Ok(()) + } else { + Err(glib::bool_error!("Failed to set AAC level/profile to caps")) + } + } +} + +#[doc(alias = "gst_codec_utils_h264_caps_set_level_and_profile")] +pub fn codec_utils_h264_caps_set_level_and_profile( + caps: &mut gst::CapsRef, + sps: &[u8], +) -> Result<(), glib::BoolError> { + assert_initialized_main_thread!(); + + assert_eq!(caps.size(), 1); + + let s = caps.structure(0).unwrap(); + assert_eq!(s.name(), "video/x-h264"); + + let len = sps.len() as u32; + unsafe { + let res: bool = from_glib(ffi::gst_codec_utils_h264_caps_set_level_and_profile( + caps.as_mut_ptr(), + sps.to_glib_none().0, + len, + )); + + if res { + Ok(()) + } else { + Err(glib::bool_error!( + "Failed to set H264 level/profile to caps" + )) + } + } +} + +#[cfg(any(feature = "v1_20", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v1_20")))] +#[doc(alias = "gst_codec_utils_h264_get_profile_flags_level")] +pub fn codec_utils_h264_get_profile_flags_level( + codec_data: &[u8], +) -> Result<(u8, u8, u8), glib::BoolError> { + assert_initialized_main_thread!(); + let len = codec_data.len() as u32; + unsafe { + let mut profile = mem::MaybeUninit::uninit(); + let mut flags = mem::MaybeUninit::uninit(); + let mut level = mem::MaybeUninit::uninit(); + glib::result_from_gboolean!( + ffi::gst_codec_utils_h264_get_profile_flags_level( + codec_data.to_glib_none().0, + len, + profile.as_mut_ptr(), + flags.as_mut_ptr(), + level.as_mut_ptr() + ), + "Failed to get H264 profile, flags and level" + )?; + let profile = profile.assume_init(); + let flags = flags.assume_init(); + let level = level.assume_init(); + Ok((profile, flags, level)) + } +} + +#[doc(alias = "gst_codec_utils_h265_caps_set_level_tier_and_profile")] +pub fn codec_utils_h265_caps_set_level_tier_and_profile( + caps: &mut gst::CapsRef, + profile_tier_level: &[u8], +) -> Result<(), glib::BoolError> { + assert_initialized_main_thread!(); + + assert_eq!(caps.size(), 1); + + let s = caps.structure(0).unwrap(); + assert_eq!(s.name(), "video/x-h265"); + + let len = profile_tier_level.len() as u32; + unsafe { + let res: bool = from_glib(ffi::gst_codec_utils_h265_caps_set_level_tier_and_profile( + caps.as_mut_ptr(), + profile_tier_level.to_glib_none().0, + len, + )); + + if res { + Ok(()) + } else { + Err(glib::bool_error!( + "Failed to set H265 level/tier/profile to caps" + )) + } + } +} + +#[doc(alias = "gst_codec_utils_mpeg4video_caps_set_level_and_profile")] +pub fn codec_utils_mpeg4video_caps_set_level_and_profile( + caps: &mut gst::CapsRef, + vis_obj_seq: &[u8], +) -> Result<(), glib::BoolError> { + assert_initialized_main_thread!(); + + assert_eq!(caps.size(), 1); + + let s = caps.structure(0).unwrap(); + assert_eq!(s.name(), "video/mpeg"); + assert!(s.get::("mpegversion").map_or(false, |v| v == 4)); + + let len = vis_obj_seq.len() as u32; + unsafe { + let res: bool = from_glib(ffi::gst_codec_utils_mpeg4video_caps_set_level_and_profile( + caps.as_mut_ptr(), + vis_obj_seq.to_glib_none().0, + len, + )); + + if res { + Ok(()) + } else { + Err(glib::bool_error!( + "Failed to set MPEG4 video level/profile to caps" + )) + } + } +} + +#[doc(alias = "gst_codec_utils_opus_create_caps")] +pub fn codec_utils_opus_create_caps( + rate: u32, + channels: u8, + channel_mapping_family: u8, + stream_count: u8, + coupled_count: u8, + channel_mapping: &[u8], +) -> Result { + assert_initialized_main_thread!(); + + assert!(channel_mapping.is_empty() || channel_mapping.len() == channels as usize); + + unsafe { + let caps = ffi::gst_codec_utils_opus_create_caps( + rate, + channels, + channel_mapping_family, + stream_count, + coupled_count, + if channel_mapping.is_empty() { + ptr::null() + } else { + channel_mapping.to_glib_none().0 + }, + ); + + if caps.is_null() { + Err(glib::bool_error!( + "Failed to create caps from Opus configuration" + )) + } else { + Ok(from_glib_full(caps)) + } + } +} + +#[doc(alias = "gst_codec_utils_opus_create_header")] +#[allow(clippy::too_many_arguments)] +pub fn codec_utils_opus_create_header( + rate: u32, + channels: u8, + channel_mapping_family: u8, + stream_count: u8, + coupled_count: u8, + channel_mapping: &[u8], + pre_skip: u16, + output_gain: i16, +) -> Result { + assert_initialized_main_thread!(); + + assert!(channel_mapping.is_empty() || channel_mapping.len() == channels as usize); + + unsafe { + let header = ffi::gst_codec_utils_opus_create_header( + rate, + channels, + channel_mapping_family, + stream_count, + coupled_count, + if channel_mapping.is_empty() { + ptr::null() + } else { + channel_mapping.to_glib_none().0 + }, + pre_skip, + output_gain, + ); + + if header.is_null() { + Err(glib::bool_error!( + "Failed to create header from Opus configuration" + )) + } else { + Ok(from_glib_full(header)) + } + } +} + +#[doc(alias = "gst_codec_utils_opus_parse_caps")] +pub fn codec_utils_opus_parse_caps( + caps: &gst::Caps, + channel_mapping: Option<&mut [u8; 256]>, +) -> Result<(u32, u8, u8, u8, u8), glib::BoolError> { + assert_initialized_main_thread!(); + + unsafe { + let mut rate = mem::MaybeUninit::uninit(); + let mut channels = mem::MaybeUninit::uninit(); + let mut channel_mapping_family = mem::MaybeUninit::uninit(); + let mut stream_count = mem::MaybeUninit::uninit(); + let mut coupled_count = mem::MaybeUninit::uninit(); + + let res: bool = from_glib(ffi::gst_codec_utils_opus_parse_caps( + mut_override(caps.as_ptr()), + rate.as_mut_ptr(), + channels.as_mut_ptr(), + channel_mapping_family.as_mut_ptr(), + stream_count.as_mut_ptr(), + coupled_count.as_mut_ptr(), + if let Some(channel_mapping) = channel_mapping { + channel_mapping.as_mut_ptr() as *mut [u8; 256] + } else { + ptr::null_mut() + }, + )); + + if res { + Ok(( + rate.assume_init(), + channels.assume_init(), + channel_mapping_family.assume_init(), + stream_count.assume_init(), + coupled_count.assume_init(), + )) + } else { + Err(glib::bool_error!("Failed to parse Opus caps")) + } + } +} + +#[doc(alias = "gst_codec_utils_opus_parse_header")] +#[allow(clippy::type_complexity)] +pub fn codec_utils_opus_parse_header( + header: &gst::Buffer, + channel_mapping: Option<&mut [u8; 256]>, +) -> Result<(u32, u8, u8, u8, u8, u16, i16), glib::BoolError> { + assert_initialized_main_thread!(); + + unsafe { + let mut rate = mem::MaybeUninit::uninit(); + let mut channels = mem::MaybeUninit::uninit(); + let mut channel_mapping_family = mem::MaybeUninit::uninit(); + let mut stream_count = mem::MaybeUninit::uninit(); + let mut coupled_count = mem::MaybeUninit::uninit(); + let mut pre_skip = mem::MaybeUninit::uninit(); + let mut output_gain = mem::MaybeUninit::uninit(); + + let res: bool = from_glib(ffi::gst_codec_utils_opus_parse_header( + mut_override(header.as_ptr()), + rate.as_mut_ptr(), + channels.as_mut_ptr(), + channel_mapping_family.as_mut_ptr(), + stream_count.as_mut_ptr(), + coupled_count.as_mut_ptr(), + if let Some(channel_mapping) = channel_mapping { + channel_mapping.as_mut_ptr() as *mut [u8; 256] + } else { + ptr::null_mut() + }, + pre_skip.as_mut_ptr(), + output_gain.as_mut_ptr(), + )); + + if res { + Ok(( + rate.assume_init(), + channels.assume_init(), + channel_mapping_family.assume_init(), + stream_count.assume_init(), + coupled_count.assume_init(), + pre_skip.assume_init(), + output_gain.assume_init(), + )) + } else { + Err(glib::bool_error!("Failed to parse Opus header")) + } + } +}