gstreamer-rs/gstreamer-pbutils/src/encoding_profile.rs
Sebastian Dröge d1e562b9f6 Explicitly add dyn keyword to trait objects
Trait objects without are deprecated with the latest nightly and this
makes it more clear that we're doing dynamic dispatch anyway.
2019-06-06 09:09:34 +03:00

650 lines
20 KiB
Rust

// Copyright (C) 2018 Thiago Santos <thiagossantos@gmail.com>
// Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use glib;
use gst;
use gst_pbutils_sys;
use gst_sys;
use std::error;
use std::fmt;
use glib::object::IsA;
use glib::translate::*;
use auto::EncodingAudioProfile;
use auto::EncodingContainerProfile;
use auto::EncodingProfile;
use auto::EncodingVideoProfile;
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>);
fn set_restriction(&self, restriction: Option<&gst::Caps>);
}
impl<O: IsA<EncodingProfile>> EncodingProfileBuilderCommon for O {
fn set_allow_dynamic_output(&self, allow_dynamic_output: bool) {
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_allow_dynamic_output(
self.as_ref().to_glib_none().0,
allow_dynamic_output.to_glib(),
);
}
}
fn set_description(&self, description: Option<&str>) {
let description = description.to_glib_none();
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_description(
self.as_ref().to_glib_none().0,
description.0,
);
}
}
fn set_enabled(&self, enabled: bool) {
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_enabled(
self.as_ref().to_glib_none().0,
enabled.to_glib(),
);
}
}
fn set_format(&self, format: &gst::Caps) {
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_format(
self.as_ref().to_glib_none().0,
format.to_glib_none().0,
);
}
}
fn set_name(&self, name: Option<&str>) {
let name = name.to_glib_none();
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_name(self.as_ref().to_glib_none().0, name.0);
}
}
fn set_presence(&self, presence: u32) {
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_presence(
self.as_ref().to_glib_none().0,
presence,
);
}
}
fn set_preset(&self, preset: Option<&str>) {
let preset = preset.to_glib_none();
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_preset(
self.as_ref().to_glib_none().0,
preset.0,
);
}
}
fn set_preset_name(&self, preset_name: Option<&str>) {
let preset_name = preset_name.to_glib_none();
unsafe {
gst_pbutils_sys::gst_encoding_profile_set_preset_name(
self.as_ref().to_glib_none().0,
preset_name.0,
);
}
}
fn set_restriction(&self, restriction: Option<&gst::Caps>) {
unsafe {
let restriction = match restriction {
Some(restriction) => restriction.to_glib_full(),
None => gst_sys::gst_caps_new_any(),
};
gst_pbutils_sys::gst_encoding_profile_set_restriction(
self.as_ref().to_glib_none().0,
restriction,
);
}
}
}
impl EncodingAudioProfile {
fn new(
format: &gst::Caps,
preset: Option<&str>,
restriction: Option<&gst::Caps>,
presence: u32,
) -> EncodingAudioProfile {
assert_initialized_main_thread!();
let preset = preset.to_glib_none();
let restriction = restriction.to_glib_none();
unsafe {
from_glib_full(gst_pbutils_sys::gst_encoding_audio_profile_new(
format.to_glib_none().0,
preset.0,
restriction.0,
presence,
))
}
}
}
impl EncodingVideoProfile {
fn new(
format: &gst::Caps,
preset: Option<&str>,
restriction: Option<&gst::Caps>,
presence: u32,
) -> EncodingVideoProfile {
assert_initialized_main_thread!();
let preset = preset.to_glib_none();
let restriction = restriction.to_glib_none();
unsafe {
from_glib_full(gst_pbutils_sys::gst_encoding_video_profile_new(
format.to_glib_none().0,
preset.0,
restriction.0,
presence,
))
}
}
fn set_pass(&self, pass: u32) {
unsafe {
gst_pbutils_sys::gst_encoding_video_profile_set_pass(self.to_glib_none().0, pass);
}
}
fn set_variableframerate(&self, variableframerate: bool) {
unsafe {
gst_pbutils_sys::gst_encoding_video_profile_set_variableframerate(
self.to_glib_none().0,
variableframerate.to_glib(),
);
}
}
}
impl EncodingContainerProfile {
fn new(
name: Option<&str>,
description: Option<&str>,
format: &gst::Caps,
preset: Option<&str>,
) -> EncodingContainerProfile {
assert_initialized_main_thread!();
let name = name.to_glib_none();
let description = description.to_glib_none();
let preset = preset.to_glib_none();
unsafe {
from_glib_full(gst_pbutils_sys::gst_encoding_container_profile_new(
name.0,
description.0,
format.to_glib_none().0,
preset.0,
))
}
}
fn add_profile<P: IsA<EncodingProfile>>(
&self,
profile: &P,
) -> Result<(), glib::error::BoolError> {
unsafe {
glib_result_from_gboolean!(
gst_pbutils_sys::gst_encoding_container_profile_add_profile(
self.to_glib_none().0,
profile.as_ref().to_glib_full(),
),
"Failed to add profile",
)
}
}
}
#[derive(Debug, Clone)]
pub struct EncodingProfileBuilderError;
impl fmt::Display for EncodingProfileBuilderError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "failed to build encoding profile")
}
}
impl error::Error for EncodingProfileBuilderError {
fn description(&self) -> &str {
"invalid parameters to build encoding profile"
}
fn cause(&self) -> Option<&dyn error::Error> {
None
}
}
#[derive(Debug)]
struct EncodingProfileBuilderCommonData<'a> {
name: Option<&'a str>,
description: Option<&'a str>,
format: Option<&'a gst::Caps>,
preset: Option<&'a str>,
preset_name: Option<&'a str>,
restriction: Option<&'a gst::Caps>,
presence: u32,
allow_dynamic_output: bool,
enabled: bool,
}
impl<'a> EncodingProfileBuilderCommonData<'a> {
fn new() -> EncodingProfileBuilderCommonData<'a> {
EncodingProfileBuilderCommonData {
name: None,
description: None,
format: None,
preset: None,
preset_name: None,
restriction: None,
presence: 0,
allow_dynamic_output: true,
enabled: true,
}
}
}
pub trait EncodingProfileBuilder<'a>: Sized {
fn name(self, name: &'a str) -> Self;
fn description(self, description: &'a str) -> Self;
fn format(self, format: &'a gst::Caps) -> Self;
fn preset(self, preset: &'a str) -> Self;
fn preset_name(self, preset_name: &'a str) -> Self;
fn restriction(self, format: &'a gst::Caps) -> Self;
fn presence(self, presence: u32) -> Self;
fn allow_dynamic_output(self, allow: bool) -> Self;
fn enabled(self, enabled: bool) -> Self;
}
macro_rules! declare_encoding_profile_builder_common(
($name:ident) => {
impl<'a> Default for $name<'a> {
fn default() -> Self {
Self::new()
}
}
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 format(mut self, format: &'a gst::Caps) -> $name<'a> {
self.base.format = Some(format);
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 restriction(mut self, restriction: &'a gst::Caps) -> $name<'a> {
self.base.restriction = Some(restriction);
self
}
fn presence(mut self, presence: u32) -> $name<'a> {
self.base.presence = presence;
self
}
fn allow_dynamic_output(mut self, allow: bool) -> $name<'a> {
self.base.allow_dynamic_output = allow;
self
}
fn enabled(mut self, enabled: bool) -> $name<'a> {
self.base.enabled = enabled;
self
}
}
}
);
fn set_common_fields<T: EncodingProfileBuilderCommon>(
profile: &T,
base_data: &EncodingProfileBuilderCommonData,
) {
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_restriction(base_data.restriction);
profile.set_presence(base_data.presence);
}
#[derive(Debug)]
pub struct EncodingAudioProfileBuilder<'a> {
base: EncodingProfileBuilderCommonData<'a>,
}
declare_encoding_profile_builder_common!(EncodingAudioProfileBuilder);
impl<'a> EncodingAudioProfileBuilder<'a> {
pub fn new() -> Self {
EncodingAudioProfileBuilder {
base: EncodingProfileBuilderCommonData::new(),
}
}
pub fn build(self) -> Result<EncodingAudioProfile, EncodingProfileBuilderError> {
if self.base.format.is_none() {
return Err(EncodingProfileBuilderError);
}
let profile = EncodingAudioProfile::new(
self.base.format.unwrap(),
self.base.preset,
self.base.restriction,
self.base.presence,
);
set_common_fields(&profile, &self.base);
Ok(profile)
}
}
#[derive(Debug)]
pub struct EncodingVideoProfileBuilder<'a> {
base: EncodingProfileBuilderCommonData<'a>,
pass: u32,
variable_framerate: bool,
}
declare_encoding_profile_builder_common!(EncodingVideoProfileBuilder);
impl<'a> EncodingVideoProfileBuilder<'a> {
pub fn new() -> Self {
EncodingVideoProfileBuilder {
base: EncodingProfileBuilderCommonData::new(),
pass: 0,
variable_framerate: false,
}
}
pub fn pass(mut self, pass: u32) -> Self {
self.pass = pass;
self
}
pub fn variable_framerate(mut self, variable_framerate: bool) -> Self {
self.variable_framerate = variable_framerate;
self
}
pub fn build(self) -> Result<EncodingVideoProfile, EncodingProfileBuilderError> {
if self.base.format.is_none() {
return Err(EncodingProfileBuilderError);
}
let video_profile = EncodingVideoProfile::new(
self.base.format.unwrap(),
self.base.preset,
self.base.restriction,
self.base.presence,
);
video_profile.set_pass(self.pass);
video_profile.set_variableframerate(self.variable_framerate);
set_common_fields(&video_profile, &self.base);
Ok(video_profile)
}
}
#[derive(Debug)]
pub struct EncodingContainerProfileBuilder<'a> {
base: EncodingProfileBuilderCommonData<'a>,
profiles: Vec<EncodingProfile>,
}
declare_encoding_profile_builder_common!(EncodingContainerProfileBuilder);
impl<'a> EncodingContainerProfileBuilder<'a> {
pub fn new() -> Self {
EncodingContainerProfileBuilder {
base: EncodingProfileBuilderCommonData::new(),
profiles: Vec::new(),
}
}
pub fn build(self) -> Result<EncodingContainerProfile, EncodingProfileBuilderError> {
if self.base.format.is_none() {
return Err(EncodingProfileBuilderError);
}
let container_profile = EncodingContainerProfile::new(
self.base.name,
self.base.description,
self.base.format.unwrap(),
self.base.preset,
);
for profile in self.profiles {
container_profile
.add_profile(&profile)
.or_else(|_error| Err(EncodingProfileBuilderError))?;
}
set_common_fields(&container_profile, &self.base);
Ok(container_profile)
}
pub fn add_profile<P: IsA<EncodingProfile>>(mut self, profile: &P) -> Self {
self.profiles.push(profile.as_ref().clone());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use auto::EncodingContainerProfile;
use auto::EncodingContainerProfileExt;
use auto::EncodingProfileExt;
use auto::EncodingVideoProfile;
use gst;
const AUDIO_PROFILE_NAME: &'static str = "audio-profile";
const AUDIO_PROFILE_DESCRIPTION: &'static str = "audio-profile-description";
const PRESET: &'static str = "preset";
const PRESET_NAME: &'static str = "preset-name";
const PRESENCE: u32 = 5;
const ALLOW_DYNAMIC_OUTPUT: bool = false;
const ENABLED: bool = false;
const VIDEO_PROFILE_NAME: &'static str = "video-profile";
const VIDEO_PROFILE_DESCRIPTION: &'static str = "video-profile-description";
const CONTAINER_PROFILE_NAME: &'static str = "container-profile";
const CONTAINER_PROFILE_DESCRIPTION: &'static 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::new_simple("audio/x-raw", &[]);
let restriction = gst::Caps::new_simple("audio/x-raw", &[("format", &"S32LE")]);
let audio_profile = EncodingAudioProfileBuilder::new()
.name(AUDIO_PROFILE_NAME)
.description(AUDIO_PROFILE_DESCRIPTION)
.format(&caps)
.preset(PRESET)
.preset_name(PRESET_NAME)
.restriction(&restriction)
.presence(PRESENCE)
.allow_dynamic_output(ALLOW_DYNAMIC_OUTPUT)
.enabled(ENABLED)
.build()
.unwrap();
assert_eq!(audio_profile.get_name().unwrap(), AUDIO_PROFILE_NAME);
assert_eq!(
audio_profile.get_description().unwrap(),
AUDIO_PROFILE_DESCRIPTION
);
assert_eq!(audio_profile.get_format(), caps);
assert_eq!(audio_profile.get_preset().unwrap(), PRESET);
assert_eq!(audio_profile.get_preset_name().unwrap(), PRESET_NAME);
assert_eq!(audio_profile.get_restriction().unwrap(), restriction);
assert_eq!(audio_profile.get_presence(), PRESENCE);
assert_eq!(
audio_profile.get_allow_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::new_simple("video/x-raw", &[]);
let restriction = gst::Caps::new_simple("video/x-raw", &[("format", &"RGBA")]);
let video_profile = EncodingVideoProfileBuilder::new()
.name(VIDEO_PROFILE_NAME)
.description(VIDEO_PROFILE_DESCRIPTION)
.format(&caps)
.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()
.unwrap();
assert_eq!(video_profile.get_name().unwrap(), VIDEO_PROFILE_NAME);
assert_eq!(
video_profile.get_description().unwrap(),
VIDEO_PROFILE_DESCRIPTION
);
assert_eq!(video_profile.get_format(), caps);
assert_eq!(video_profile.get_preset().unwrap(), PRESET);
assert_eq!(video_profile.get_preset_name().unwrap(), PRESET_NAME);
assert_eq!(video_profile.get_restriction().unwrap(), restriction);
assert_eq!(video_profile.get_presence(), PRESENCE);
assert_eq!(
video_profile.get_allow_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.get_variableframerate(), VARIABLE_FRAMERATE);
assert_eq!(video_profile.get_pass(), PASS);
}
#[test]
fn test_encoding_container_profile_builder() {
gst::init().unwrap();
let container_caps = gst::Caps::new_simple("container/x-caps", &[]);
let restriction = gst::Caps::new_simple("container/x-caps", &[("field", &"somevalue")]);
let video_caps = gst::Caps::new_simple("video/x-raw", &[]);
let audio_caps = gst::Caps::new_simple("audio/x-raw", &[]);
let video_profile = EncodingVideoProfileBuilder::new()
.name(VIDEO_PROFILE_NAME)
.description(VIDEO_PROFILE_DESCRIPTION)
.format(&video_caps)
.build()
.unwrap();
let audio_profile = EncodingAudioProfileBuilder::new()
.name(AUDIO_PROFILE_NAME)
.description(AUDIO_PROFILE_DESCRIPTION)
.format(&audio_caps)
.build()
.unwrap();
let profile = EncodingContainerProfileBuilder::new()
.name(CONTAINER_PROFILE_NAME)
.description(CONTAINER_PROFILE_DESCRIPTION)
.format(&container_caps)
.preset(PRESET)
.preset_name(PRESET_NAME)
.restriction(&restriction)
.presence(PRESENCE)
.allow_dynamic_output(ALLOW_DYNAMIC_OUTPUT)
.enabled(ENABLED)
.add_profile(&audio_profile)
.add_profile(&video_profile)
.build()
.unwrap();
assert_eq!(profile.get_name().unwrap(), CONTAINER_PROFILE_NAME);
assert_eq!(
profile.get_description().unwrap(),
CONTAINER_PROFILE_DESCRIPTION
);
assert_eq!(profile.get_format(), container_caps);
assert_eq!(profile.get_preset().unwrap(), PRESET);
assert_eq!(profile.get_preset_name().unwrap(), PRESET_NAME);
assert_eq!(profile.get_restriction().unwrap(), restriction);
assert_eq!(profile.get_presence(), PRESENCE);
assert_eq!(profile.get_allow_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));
}
}