gstreamer-rs/gstreamer-pbutils/src/encoding_profile.rs
Bilal Elmoussaoui 4ebec84f5e Adapt to no longer renamed ffi crates
Allows us to set all the crates in the main workspace file, so changing
their versions or branch is much simpler and reduce the amount of noise
in the diff

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1450>
2024-06-02 11:20:55 +02:00

793 lines
25 KiB
Rust

// Take a look at the license at the top of the repository in the LICENSE file.
use glib::{prelude::*, translate::*};
#[cfg(feature = "v1_20")]
use crate::ElementProperties;
use crate::{
ffi, EncodingAudioProfile, EncodingContainerProfile, EncodingProfile, EncodingVideoProfile,
};
mod sealed {
pub trait Sealed {}
impl<T: super::IsA<super::EncodingProfile>> Sealed for T {}
}
pub trait EncodingProfileExtManual: sealed::Sealed + IsA<EncodingProfile> + 'static {
#[cfg(feature = "v1_20")]
#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
#[doc(alias = "gst_encoding_profile_get_element_properties")]
#[doc(alias = "get_element_properties")]
fn element_properties(&self) -> Option<ElementProperties> {
unsafe {
from_glib_full::<_, Option<_>>(ffi::gst_encoding_profile_get_element_properties(
self.as_ref().to_glib_none().0,
))
.map(ElementProperties)
}
}
}
impl<O: IsA<EncodingProfile>> EncodingProfileExtManual for O {}
trait EncodingProfileBuilderCommon {
fn set_allow_dynamic_output(&self, allow_dynamic_output: bool);
fn set_description(&self, description: Option<&str>);
fn set_enabled(&self, enabled: bool);
fn set_format(&self, format: &gst::Caps);
fn set_name(&self, name: Option<&str>);
fn set_presence(&self, presence: u32);
fn set_preset(&self, preset: Option<&str>);
fn set_preset_name(&self, preset_name: Option<&str>);
#[cfg(feature = "v1_18")]
fn set_single_segment(&self, single_segment: bool);
#[cfg(feature = "v1_20")]
fn set_element_properties(&self, element_properties: ElementProperties);
}
impl<O: IsA<EncodingProfile>> EncodingProfileBuilderCommon for O {
// checker-ignore-item
fn set_allow_dynamic_output(&self, allow_dynamic_output: bool) {
unsafe {
ffi::gst_encoding_profile_set_allow_dynamic_output(
self.as_ref().to_glib_none().0,
allow_dynamic_output.into_glib(),
);
}
}
// checker-ignore-item
fn set_description(&self, description: Option<&str>) {
let description = description.to_glib_none();
unsafe {
ffi::gst_encoding_profile_set_description(
self.as_ref().to_glib_none().0,
description.0,
);
}
}
// checker-ignore-item
fn set_enabled(&self, enabled: bool) {
unsafe {
ffi::gst_encoding_profile_set_enabled(
self.as_ref().to_glib_none().0,
enabled.into_glib(),
);
}
}
// checker-ignore-item
fn set_format(&self, format: &gst::Caps) {
unsafe {
ffi::gst_encoding_profile_set_format(
self.as_ref().to_glib_none().0,
format.to_glib_none().0,
);
}
}
// checker-ignore-item
fn set_name(&self, name: Option<&str>) {
let name = name.to_glib_none();
unsafe {
ffi::gst_encoding_profile_set_name(self.as_ref().to_glib_none().0, name.0);
}
}
// checker-ignore-item
fn set_presence(&self, presence: u32) {
unsafe {
ffi::gst_encoding_profile_set_presence(self.as_ref().to_glib_none().0, presence);
}
}
// checker-ignore-item
fn set_preset(&self, preset: Option<&str>) {
let preset = preset.to_glib_none();
unsafe {
ffi::gst_encoding_profile_set_preset(self.as_ref().to_glib_none().0, preset.0);
}
}
// checker-ignore-item
fn set_preset_name(&self, preset_name: Option<&str>) {
let preset_name = preset_name.to_glib_none();
unsafe {
ffi::gst_encoding_profile_set_preset_name(
self.as_ref().to_glib_none().0,
preset_name.0,
);
}
}
// checker-ignore-item
#[cfg(feature = "v1_18")]
fn set_single_segment(&self, single_segment: bool) {
unsafe {
ffi::gst_encoding_profile_set_single_segment(
self.as_ref().to_glib_none().0,
single_segment.into_glib(),
);
}
}
// checker-ignore-item
#[cfg(feature = "v1_20")]
fn set_element_properties(&self, element_properties: ElementProperties) {
unsafe {
ffi::gst_encoding_profile_set_element_properties(
self.as_ref().to_glib_none().0,
element_properties.into_inner().into_glib_ptr(),
);
}
}
}
pub trait EncodingProfileHasRestrictionGetter {
#[doc(alias = "get_restriction")]
#[doc(alias = "gst_encoding_profile_get_restriction")]
fn restriction(&self) -> Option<gst::Caps>;
}
macro_rules! declare_encoding_profile_has_restriction(
($name:ident) => {
impl EncodingProfileHasRestrictionGetter for $name {
// checker-ignore-item
fn restriction(&self) -> Option<gst::Caps> {
let profile: &EncodingProfile = glib::object::Cast::upcast_ref(self);
unsafe {
from_glib_full(ffi::gst_encoding_profile_get_restriction(
profile.to_glib_none().0,
))
}
}
}
}
);
impl EncodingAudioProfile {
// checker-ignore-item
fn new(
format: &gst::Caps,
preset: Option<&str>,
restriction: Option<&gst::Caps>,
presence: u32,
) -> EncodingAudioProfile {
skip_assert_initialized!();
let preset = preset.to_glib_none();
let restriction = restriction.to_glib_none();
unsafe {
from_glib_full(ffi::gst_encoding_audio_profile_new(
format.to_glib_none().0,
preset.0,
restriction.0,
presence,
))
}
}
#[doc(alias = "gst_encoding_audio_profile_new")]
pub fn builder(format: &gst::Caps) -> EncodingAudioProfileBuilder {
assert_initialized_main_thread!();
EncodingAudioProfileBuilder::new(format)
}
}
declare_encoding_profile_has_restriction!(EncodingAudioProfile);
impl EncodingVideoProfile {
// checker-ignore-item
fn new(
format: &gst::Caps,
preset: Option<&str>,
restriction: Option<&gst::Caps>,
presence: u32,
) -> EncodingVideoProfile {
skip_assert_initialized!();
let preset = preset.to_glib_none();
let restriction = restriction.to_glib_none();
unsafe {
from_glib_full(ffi::gst_encoding_video_profile_new(
format.to_glib_none().0,
preset.0,
restriction.0,
presence,
))
}
}
#[doc(alias = "gst_encoding_video_profile_new")]
pub fn builder(format: &gst::Caps) -> EncodingVideoProfileBuilder {
assert_initialized_main_thread!();
EncodingVideoProfileBuilder::new(format)
}
// checker-ignore-item
fn set_pass(&self, pass: u32) {
unsafe {
ffi::gst_encoding_video_profile_set_pass(self.to_glib_none().0, pass);
}
}
// checker-ignore-item
fn set_variableframerate(&self, variableframerate: bool) {
unsafe {
ffi::gst_encoding_video_profile_set_variableframerate(
self.to_glib_none().0,
variableframerate.into_glib(),
);
}
}
}
declare_encoding_profile_has_restriction!(EncodingVideoProfile);
impl EncodingContainerProfile {
// checker-ignore-item
fn new(
name: Option<&str>,
description: Option<&str>,
format: &gst::Caps,
preset: Option<&str>,
) -> EncodingContainerProfile {
skip_assert_initialized!();
let name = name.to_glib_none();
let description = description.to_glib_none();
let preset = preset.to_glib_none();
unsafe {
from_glib_full(ffi::gst_encoding_container_profile_new(
name.0,
description.0,
format.to_glib_none().0,
preset.0,
))
}
}
#[doc(alias = "gst_encoding_container_profile_new")]
pub fn builder(format: &gst::Caps) -> EncodingContainerProfileBuilder {
assert_initialized_main_thread!();
EncodingContainerProfileBuilder::new(format)
}
// checker-ignore-item
fn add_profile(&self, profile: impl IsA<EncodingProfile>) {
unsafe {
let res = ffi::gst_encoding_container_profile_add_profile(
self.to_glib_none().0,
profile.upcast().into_glib_ptr(),
);
// Can't possibly fail unless we pass random pointers
debug_assert_ne!(res, glib::ffi::GFALSE);
}
}
}
#[derive(Debug)]
struct EncodingProfileBuilderCommonData<'a> {
format: &'a gst::Caps,
name: Option<&'a str>,
description: Option<&'a str>,
preset: Option<&'a str>,
preset_name: Option<&'a str>,
presence: u32,
allow_dynamic_output: bool,
enabled: bool,
#[cfg(feature = "v1_18")]
single_segment: bool,
#[cfg(feature = "v1_20")]
element_properties: Option<ElementProperties>,
}
impl<'a> EncodingProfileBuilderCommonData<'a> {
fn new(format: &'a gst::Caps) -> EncodingProfileBuilderCommonData<'a> {
skip_assert_initialized!();
EncodingProfileBuilderCommonData {
name: None,
description: None,
format,
preset: None,
preset_name: None,
presence: 0,
allow_dynamic_output: true,
enabled: true,
#[cfg(feature = "v1_18")]
single_segment: false,
#[cfg(feature = "v1_20")]
element_properties: None,
}
}
}
pub trait EncodingProfileBuilder<'a>: Sized {
#[doc(alias = "gst_encoding_profile_set_name")]
#[must_use]
fn name(self, name: &'a str) -> Self;
#[doc(alias = "gst_encoding_profile_set_description")]
#[must_use]
fn description(self, description: &'a str) -> Self;
#[doc(alias = "gst_encoding_profile_set_preset")]
#[must_use]
fn preset(self, preset: &'a str) -> Self;
#[doc(alias = "gst_encoding_profile_set_preset_name")]
#[must_use]
fn preset_name(self, preset_name: &'a str) -> Self;
#[doc(alias = "gst_encoding_profile_set_presence")]
#[must_use]
fn presence(self, presence: u32) -> Self;
#[doc(alias = "gst_encoding_profile_set_presence")]
#[must_use]
fn presence_if_some(self, presence: Option<u32>) -> Self;
#[doc(alias = "gst_encoding_profile_set_allow_dynamic_output")]
#[must_use]
fn allow_dynamic_output(self, allow: bool) -> Self;
#[doc(alias = "gst_encoding_profile_set_allow_dynamic_output")]
#[must_use]
fn allow_dynamic_output_if_some(self, allow_dynamic_output: Option<bool>) -> Self;
#[doc(alias = "gst_encoding_profile_set_enabled")]
#[must_use]
fn enabled(self, enabled: bool) -> Self;
#[doc(alias = "gst_encoding_profile_set_enabled")]
#[must_use]
fn enabled_if_some(self, enabled: Option<bool>) -> Self;
#[cfg(feature = "v1_18")]
#[doc(alias = "gst_encoding_profile_set_single_segment")]
#[must_use]
fn single_segment(self, single_segment: bool) -> Self;
#[cfg(feature = "v1_18")]
#[doc(alias = "gst_encoding_profile_set_single_segment")]
#[must_use]
fn single_segment_if_some(self, single_segment: Option<bool>) -> Self;
#[cfg(feature = "v1_20")]
#[doc(alias = "gst_encoding_profile_set_element_properties")]
#[must_use]
fn element_properties(self, element_properties: ElementProperties) -> Self;
#[cfg(feature = "v1_20")]
#[doc(alias = "gst_encoding_profile_set_element_properties")]
#[must_use]
fn element_properties_if_some(self, element_properties: Option<ElementProperties>) -> Self;
}
macro_rules! declare_encoding_profile_builder_common(
($name:ident) => {
impl<'a> EncodingProfileBuilder<'a> for $name<'a> {
fn name(mut self, name: &'a str) -> $name<'a> {
self.base.name = Some(name);
self
}
fn description(mut self, description: &'a str) -> $name<'a> {
self.base.description = Some(description);
self
}
fn preset(mut self, preset: &'a str) -> $name<'a> {
self.base.preset = Some(preset);
self
}
fn preset_name(mut self, preset_name: &'a str) -> $name<'a> {
self.base.preset_name = Some(preset_name);
self
}
fn presence(mut self, presence: u32) -> $name<'a> {
self.base.presence = presence;
self
}
fn presence_if_some(self, presence: Option<u32>) -> $name<'a> {
if let Some(presence) = presence {
self.presence(presence)
} else {
self
}
}
fn allow_dynamic_output(mut self, allow: bool) -> $name<'a> {
self.base.allow_dynamic_output = allow;
self
}
fn allow_dynamic_output_if_some(self, allow_dynamic_output: Option<bool>) -> $name<'a> {
if let Some(allow_dynamic_output) = allow_dynamic_output {
self.allow_dynamic_output(allow_dynamic_output)
} else {
self
}
}
fn enabled(mut self, enabled: bool) -> $name<'a> {
self.base.enabled = enabled;
self
}
fn enabled_if_some(self, enabled: Option<bool>) -> $name<'a> {
if let Some(enabled) = enabled {
self.enabled(enabled)
} else {
self
}
}
#[cfg(feature = "v1_18")]
fn single_segment(mut self, single_segment: bool) -> $name<'a> {
self.base.single_segment = single_segment;
self
}
#[cfg(feature = "v1_18")]
fn single_segment_if_some(self, single_segment: Option<bool>) -> $name<'a> {
if let Some(single_segment) = single_segment {
self.single_segment(single_segment)
} else {
self
}
}
#[cfg(feature = "v1_20")]
fn element_properties(mut self, element_properties: ElementProperties) -> $name<'a> {
self.base.element_properties = Some(element_properties);
self
}
#[cfg(feature = "v1_20")]
fn element_properties_if_some(self, element_properties: Option<ElementProperties>) -> $name<'a> {
if let Some(element_properties) = element_properties {
self.element_properties(element_properties)
} else {
self
}
}
}
}
);
fn set_common_fields<T: EncodingProfileBuilderCommon>(
profile: &T,
base_data: EncodingProfileBuilderCommonData,
) {
skip_assert_initialized!();
profile.set_format(base_data.format);
profile.set_name(base_data.name);
profile.set_description(base_data.description);
profile.set_preset(base_data.preset);
profile.set_preset_name(base_data.preset_name);
profile.set_allow_dynamic_output(base_data.allow_dynamic_output);
profile.set_enabled(base_data.enabled);
profile.set_presence(base_data.presence);
#[cfg(feature = "v1_18")]
{
profile.set_single_segment(base_data.single_segment);
}
#[cfg(feature = "v1_20")]
{
let mut base_data = base_data;
if let Some(element_properties) = base_data.element_properties.take() {
profile.set_element_properties(element_properties);
}
}
}
#[derive(Debug)]
#[must_use = "The builder must be built to be used"]
pub struct EncodingAudioProfileBuilder<'a> {
base: EncodingProfileBuilderCommonData<'a>,
restriction: Option<&'a gst::Caps>,
}
declare_encoding_profile_builder_common!(EncodingAudioProfileBuilder);
impl<'a> EncodingAudioProfileBuilder<'a> {
fn new(format: &'a gst::Caps) -> Self {
skip_assert_initialized!();
EncodingAudioProfileBuilder {
base: EncodingProfileBuilderCommonData::new(format),
restriction: None,
}
}
#[doc(alias = "gst_encoding_profile_set_restriction")]
pub fn restriction(mut self, restriction: &'a gst::Caps) -> Self {
self.restriction = Some(restriction);
self
}
#[must_use = "Building the profile without using it has no effect"]
pub fn build(self) -> EncodingAudioProfile {
let profile = EncodingAudioProfile::new(
self.base.format,
self.base.preset,
self.restriction,
self.base.presence,
);
set_common_fields(&profile, self.base);
profile
}
}
#[derive(Debug)]
#[must_use = "The builder must be built to be used"]
pub struct EncodingVideoProfileBuilder<'a> {
base: EncodingProfileBuilderCommonData<'a>,
restriction: Option<&'a gst::Caps>,
pass: u32,
variable_framerate: bool,
}
declare_encoding_profile_builder_common!(EncodingVideoProfileBuilder);
impl<'a> EncodingVideoProfileBuilder<'a> {
fn new(format: &'a gst::Caps) -> Self {
skip_assert_initialized!();
EncodingVideoProfileBuilder {
base: EncodingProfileBuilderCommonData::new(format),
restriction: None,
pass: 0,
variable_framerate: false,
}
}
#[doc(alias = "gst_encoding_video_profile_set_pass")]
pub fn pass(mut self, pass: u32) -> Self {
self.pass = pass;
self
}
#[doc(alias = "gst_encoding_video_profile_set_variableframerate")]
pub fn variable_framerate(mut self, variable_framerate: bool) -> Self {
self.variable_framerate = variable_framerate;
self
}
#[doc(alias = "gst_encoding_profile_set_restriction")]
pub fn restriction(mut self, restriction: &'a gst::Caps) -> Self {
self.restriction = Some(restriction);
self
}
#[must_use = "Building the profile without using it has no effect"]
pub fn build(self) -> EncodingVideoProfile {
let video_profile = EncodingVideoProfile::new(
self.base.format,
self.base.preset,
self.restriction,
self.base.presence,
);
video_profile.set_pass(self.pass);
video_profile.set_variableframerate(self.variable_framerate);
set_common_fields(&video_profile, self.base);
video_profile
}
}
#[derive(Debug)]
#[must_use = "The builder must be built to be used"]
pub struct EncodingContainerProfileBuilder<'a> {
base: EncodingProfileBuilderCommonData<'a>,
profiles: Vec<EncodingProfile>,
}
declare_encoding_profile_builder_common!(EncodingContainerProfileBuilder);
impl<'a> EncodingContainerProfileBuilder<'a> {
fn new(format: &'a gst::Caps) -> Self {
skip_assert_initialized!();
EncodingContainerProfileBuilder {
base: EncodingProfileBuilderCommonData::new(format),
profiles: Vec::new(),
}
}
#[must_use = "Building the profile without using it has no effect"]
pub fn build(self) -> EncodingContainerProfile {
let container_profile = EncodingContainerProfile::new(
self.base.name,
self.base.description,
self.base.format,
self.base.preset,
);
for profile in self.profiles {
container_profile.add_profile(profile);
}
set_common_fields(&container_profile, self.base);
container_profile
}
#[doc(alias = "gst_encoding_container_profile_add_profile")]
pub fn add_profile(mut self, profile: impl IsA<EncodingProfile>) -> Self {
self.profiles.push(profile.upcast());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
auto::{EncodingContainerProfile, EncodingVideoProfile},
prelude::*,
};
const AUDIO_PROFILE_NAME: &str = "audio-profile";
const AUDIO_PROFILE_DESCRIPTION: &str = "audio-profile-description";
const PRESET: &str = "preset";
const PRESET_NAME: &str = "preset-name";
const PRESENCE: u32 = 5;
const ALLOW_DYNAMIC_OUTPUT: bool = false;
const ENABLED: bool = false;
const VIDEO_PROFILE_NAME: &str = "video-profile";
const VIDEO_PROFILE_DESCRIPTION: &str = "video-profile-description";
const CONTAINER_PROFILE_NAME: &str = "container-profile";
const CONTAINER_PROFILE_DESCRIPTION: &str = "container-profile-description";
// Video profile exclusive attributes
const PASS: u32 = 8;
const VARIABLE_FRAMERATE: bool = true;
#[test]
fn test_encoding_audio_profile_builder() {
gst::init().unwrap();
let caps = gst::Caps::builder("audio/x-raw").build();
let restriction = gst_audio::AudioCapsBuilder::new()
.format(gst_audio::AudioFormat::S32le)
.build();
let audio_profile = EncodingAudioProfile::builder(&caps)
.name(AUDIO_PROFILE_NAME)
.description(AUDIO_PROFILE_DESCRIPTION)
.preset(PRESET)
.preset_name(PRESET_NAME)
.restriction(&restriction)
.presence(PRESENCE)
.allow_dynamic_output(ALLOW_DYNAMIC_OUTPUT)
.enabled(ENABLED)
.build();
assert_eq!(audio_profile.name().unwrap(), AUDIO_PROFILE_NAME);
assert_eq!(
audio_profile.description().unwrap(),
AUDIO_PROFILE_DESCRIPTION
);
assert_eq!(audio_profile.format(), caps);
assert_eq!(audio_profile.preset().unwrap(), PRESET);
assert_eq!(audio_profile.preset_name().unwrap(), PRESET_NAME);
assert_eq!(audio_profile.restriction().unwrap(), restriction);
assert_eq!(audio_profile.presence(), PRESENCE);
assert_eq!(audio_profile.allows_dynamic_output(), ALLOW_DYNAMIC_OUTPUT);
assert_eq!(audio_profile.is_enabled(), ENABLED);
}
#[test]
fn test_encoding_video_profile_builder() {
gst::init().unwrap();
let caps = gst::Caps::builder("video/x-raw").build();
let restriction = gst_video::VideoCapsBuilder::new()
.format(gst_video::VideoFormat::Rgba)
.build();
let video_profile = EncodingVideoProfile::builder(&caps)
.name(VIDEO_PROFILE_NAME)
.description(VIDEO_PROFILE_DESCRIPTION)
.preset(PRESET)
.preset_name(PRESET_NAME)
.restriction(&restriction)
.presence(PRESENCE)
.allow_dynamic_output(ALLOW_DYNAMIC_OUTPUT)
.enabled(ENABLED)
.pass(PASS)
.variable_framerate(VARIABLE_FRAMERATE)
.build();
assert_eq!(video_profile.name().unwrap(), VIDEO_PROFILE_NAME);
assert_eq!(
video_profile.description().unwrap(),
VIDEO_PROFILE_DESCRIPTION
);
assert_eq!(video_profile.format(), caps);
assert_eq!(video_profile.preset().unwrap(), PRESET);
assert_eq!(video_profile.preset_name().unwrap(), PRESET_NAME);
assert_eq!(video_profile.restriction().unwrap(), restriction);
assert_eq!(video_profile.presence(), PRESENCE);
assert_eq!(video_profile.allows_dynamic_output(), ALLOW_DYNAMIC_OUTPUT);
assert_eq!(video_profile.is_enabled(), ENABLED);
let video_profile: EncodingVideoProfile =
glib::object::Cast::downcast(video_profile).ok().unwrap();
assert_eq!(video_profile.is_variableframerate(), VARIABLE_FRAMERATE);
assert_eq!(video_profile.pass(), PASS);
}
#[test]
fn test_encoding_container_profile_builder() {
gst::init().unwrap();
let container_caps = gst::Caps::builder("container/x-caps").build();
let video_caps = gst::Caps::builder("video/x-raw").build();
let audio_caps = gst::Caps::builder("audio/x-raw").build();
let video_profile = EncodingVideoProfile::builder(&video_caps)
.name(VIDEO_PROFILE_NAME)
.description(VIDEO_PROFILE_DESCRIPTION)
.build();
let audio_profile = EncodingAudioProfile::builder(&audio_caps)
.name(AUDIO_PROFILE_NAME)
.description(AUDIO_PROFILE_DESCRIPTION)
.build();
let profile = EncodingContainerProfile::builder(&container_caps)
.name(CONTAINER_PROFILE_NAME)
.description(CONTAINER_PROFILE_DESCRIPTION)
.preset(PRESET)
.preset_name(PRESET_NAME)
.presence(PRESENCE)
.allow_dynamic_output(ALLOW_DYNAMIC_OUTPUT)
.enabled(ENABLED)
.add_profile(audio_profile.clone())
.add_profile(video_profile.clone())
.build();
assert_eq!(profile.name().unwrap(), CONTAINER_PROFILE_NAME);
assert_eq!(
profile.description().unwrap(),
CONTAINER_PROFILE_DESCRIPTION
);
assert_eq!(profile.format(), container_caps);
assert_eq!(profile.preset().unwrap(), PRESET);
assert_eq!(profile.preset_name().unwrap(), PRESET_NAME);
assert_eq!(profile.presence(), PRESENCE);
assert_eq!(profile.allows_dynamic_output(), ALLOW_DYNAMIC_OUTPUT);
assert_eq!(profile.is_enabled(), ENABLED);
let container_profile: EncodingContainerProfile =
glib::object::Cast::downcast(profile).ok().unwrap();
assert!(container_profile.contains_profile(&video_profile));
assert!(container_profile.contains_profile(&audio_profile));
}
}