mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-06-03 05:49:31 +00:00
Update for glib/gstreamer bindings API changes
This commit is contained in:
parent
875c3efb91
commit
d4ce1a33f2
|
@ -65,51 +65,6 @@ pub struct AudioEcho {
|
||||||
state: Mutex<Option<State>>,
|
state: Mutex<Option<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 4] = [
|
|
||||||
subclass::Property("max-delay", |name| {
|
|
||||||
glib::ParamSpec::uint64(name,
|
|
||||||
"Maximum Delay",
|
|
||||||
"Maximum delay of the echo in nanoseconds (can't be changed in PLAYING or PAUSED state)",
|
|
||||||
0, u64::MAX,
|
|
||||||
DEFAULT_MAX_DELAY,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("delay", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Delay",
|
|
||||||
"Delay of the echo in nanoseconds",
|
|
||||||
0,
|
|
||||||
u64::MAX,
|
|
||||||
DEFAULT_DELAY,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("intensity", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Intensity",
|
|
||||||
"Intensity of the echo",
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
DEFAULT_INTENSITY,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("feedback", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Feedback",
|
|
||||||
"Amount of feedback",
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
DEFAULT_FEEDBACK,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
impl AudioEcho {
|
impl AudioEcho {
|
||||||
fn process<F: Float + ToPrimitive + FromPrimitive>(
|
fn process<F: Float + ToPrimitive + FromPrimitive>(
|
||||||
data: &mut [F],
|
data: &mut [F],
|
||||||
|
@ -134,6 +89,7 @@ impl ObjectSubclass for AudioEcho {
|
||||||
const NAME: &'static str = "RsAudioEcho";
|
const NAME: &'static str = "RsAudioEcho";
|
||||||
type Type = super::AudioEcho;
|
type Type = super::AudioEcho;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -145,78 +101,75 @@ impl ObjectSubclass for AudioEcho {
|
||||||
state: Mutex::new(None),
|
state: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Audio echo",
|
|
||||||
"Filter/Effect/Audio",
|
|
||||||
"Adds an echo or reverb effect to an audio stream",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"audio/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[
|
|
||||||
&gst_audio::AUDIO_FORMAT_F32.to_str(),
|
|
||||||
&gst_audio::AUDIO_FORMAT_F64.to_str(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
("rate", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
("channels", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
("layout", &"interleaved"),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.configure(
|
|
||||||
gst_base::subclass::BaseTransformMode::AlwaysInPlace,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for AudioEcho {
|
impl ObjectImpl for AudioEcho {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::uint64("max-delay",
|
||||||
|
"Maximum Delay",
|
||||||
|
"Maximum delay of the echo in nanoseconds (can't be changed in PLAYING or PAUSED state)",
|
||||||
|
0, u64::MAX,
|
||||||
|
DEFAULT_MAX_DELAY,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"delay",
|
||||||
|
"Delay",
|
||||||
|
"Delay of the echo in nanoseconds",
|
||||||
|
0,
|
||||||
|
u64::MAX,
|
||||||
|
DEFAULT_DELAY,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"intensity",
|
||||||
|
"Intensity",
|
||||||
|
"Intensity of the echo",
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_INTENSITY,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"feedback",
|
||||||
|
"Feedback",
|
||||||
|
"Amount of feedback",
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_FEEDBACK,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("max-delay", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"max-delay" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
if self.state.lock().unwrap().is_none() {
|
if self.state.lock().unwrap().is_none() {
|
||||||
settings.max_delay = value.get_some().expect("type checked upstream");
|
settings.max_delay = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subclass::Property("delay", ..) => {
|
"delay" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.delay = value.get_some().expect("type checked upstream");
|
settings.delay = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("intensity", ..) => {
|
"intensity" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.intensity = value.get_some().expect("type checked upstream");
|
settings.intensity = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("feedback", ..) => {
|
"feedback" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.feedback = value.get_some().expect("type checked upstream");
|
settings.feedback = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -224,23 +177,21 @@ impl ObjectImpl for AudioEcho {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"max-delay" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("max-delay", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.max_delay.to_value()
|
settings.max_delay.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("delay", ..) => {
|
"delay" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.delay.to_value()
|
settings.delay.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("intensity", ..) => {
|
"intensity" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.intensity.to_value()
|
settings.intensity.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("feedback", ..) => {
|
"feedback" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.feedback.to_value()
|
settings.feedback.to_value()
|
||||||
}
|
}
|
||||||
|
@ -249,9 +200,65 @@ impl ObjectImpl for AudioEcho {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for AudioEcho {}
|
impl ElementImpl for AudioEcho {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Audio echo",
|
||||||
|
"Filter/Effect/Audio",
|
||||||
|
"Adds an echo or reverb effect to an audio stream",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"audio/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst_audio::AUDIO_FORMAT_F32.to_str(),
|
||||||
|
&gst_audio::AUDIO_FORMAT_F64.to_str(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
("rate", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
("channels", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
("layout", &"interleaved"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseTransformImpl for AudioEcho {
|
impl BaseTransformImpl for AudioEcho {
|
||||||
|
const MODE: gst_base::subclass::BaseTransformMode =
|
||||||
|
gst_base::subclass::BaseTransformMode::AlwaysInPlace;
|
||||||
|
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||||
|
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||||
|
|
||||||
fn transform_ip(
|
fn transform_ip(
|
||||||
&self,
|
&self,
|
||||||
_element: &Self::Type,
|
_element: &Self::Type,
|
||||||
|
|
|
@ -210,53 +210,6 @@ pub struct AudioLoudNorm {
|
||||||
state: Mutex<Option<State>>,
|
state: Mutex<Option<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 4] = [
|
|
||||||
subclass::Property("loudness-target", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Loudness Target",
|
|
||||||
"Loudness target in LUFS",
|
|
||||||
-70.0,
|
|
||||||
-5.0,
|
|
||||||
DEFAULT_LOUDNESS_TARGET,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("loudness-range-target", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Loudness Range Target",
|
|
||||||
"Loudness range target in LU",
|
|
||||||
1.0,
|
|
||||||
20.0,
|
|
||||||
DEFAULT_LOUDNESS_RANGE_TARGET,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-true-peak", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Maximum True Peak",
|
|
||||||
"Maximum True Peak in dbTP",
|
|
||||||
-9.0,
|
|
||||||
0.0,
|
|
||||||
DEFAULT_MAX_TRUE_PEAK,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("offset", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Offset Gain",
|
|
||||||
"Offset Gain in LU",
|
|
||||||
-99.0,
|
|
||||||
99.0,
|
|
||||||
DEFAULT_OFFSET,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Gain analysis parameters
|
// Gain analysis parameters
|
||||||
const GAIN_LOOKAHEAD: usize = 3 * 192_000; // 3s
|
const GAIN_LOOKAHEAD: usize = 3 * 192_000; // 3s
|
||||||
const FRAME_SIZE: usize = 19_200; // 100ms
|
const FRAME_SIZE: usize = 19_200; // 100ms
|
||||||
|
@ -1752,6 +1705,7 @@ impl ObjectSubclass for AudioLoudNorm {
|
||||||
const NAME: &'static str = "RsAudioLoudNorm";
|
const NAME: &'static str = "RsAudioLoudNorm";
|
||||||
type Type = super::AudioLoudNorm;
|
type Type = super::AudioLoudNorm;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -1796,47 +1750,54 @@ impl ObjectSubclass for AudioLoudNorm {
|
||||||
state: Mutex::new(None),
|
state: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Audio loudness normalizer",
|
|
||||||
"Filter/Effect/Audio",
|
|
||||||
"Normalizes perceived loudness of an audio stream",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"audio/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &gst_audio::AUDIO_FORMAT_F64.to_str()),
|
|
||||||
("rate", &192_000i32),
|
|
||||||
("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
("layout", &"interleaved"),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for AudioLoudNorm {
|
impl ObjectImpl for AudioLoudNorm {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"loudness-target",
|
||||||
|
"Loudness Target",
|
||||||
|
"Loudness target in LUFS",
|
||||||
|
-70.0,
|
||||||
|
-5.0,
|
||||||
|
DEFAULT_LOUDNESS_TARGET,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"loudness-range-target",
|
||||||
|
"Loudness Range Target",
|
||||||
|
"Loudness range target in LU",
|
||||||
|
1.0,
|
||||||
|
20.0,
|
||||||
|
DEFAULT_LOUDNESS_RANGE_TARGET,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"max-true-peak",
|
||||||
|
"Maximum True Peak",
|
||||||
|
"Maximum True Peak in dbTP",
|
||||||
|
-9.0,
|
||||||
|
0.0,
|
||||||
|
DEFAULT_MAX_TRUE_PEAK,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"offset",
|
||||||
|
"Offset Gain",
|
||||||
|
"Offset Gain in LU",
|
||||||
|
-99.0,
|
||||||
|
99.0,
|
||||||
|
DEFAULT_OFFSET,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -1844,23 +1805,27 @@ impl ObjectImpl for AudioLoudNorm {
|
||||||
obj.add_pad(&self.srcpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("loudness-target", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"loudness-target" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.loudness_target = value.get_some().expect("type checked upstream");
|
settings.loudness_target = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("loudness-range-target", ..) => {
|
"loudness-range-target" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.loudness_range_target = value.get_some().expect("type checked upstream");
|
settings.loudness_range_target = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-true-peak", ..) => {
|
"max-true-peak" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.max_true_peak = value.get_some().expect("type checked upstream");
|
settings.max_true_peak = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("offset", ..) => {
|
"offset" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.offset = value.get_some().expect("type checked upstream");
|
settings.offset = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -1868,23 +1833,21 @@ impl ObjectImpl for AudioLoudNorm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"loudness-target" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("loudness-target", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.loudness_target.to_value()
|
settings.loudness_target.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("loudness-range-target", ..) => {
|
"loudness-range-target" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.loudness_range_target.to_value()
|
settings.loudness_range_target.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("max-true-peak", ..) => {
|
"max-true-peak" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.max_true_peak.to_value()
|
settings.max_true_peak.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("offset", ..) => {
|
"offset" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.offset.to_value()
|
settings.offset.to_value()
|
||||||
}
|
}
|
||||||
|
@ -1894,6 +1857,52 @@ impl ObjectImpl for AudioLoudNorm {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for AudioLoudNorm {
|
impl ElementImpl for AudioLoudNorm {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Audio loudness normalizer",
|
||||||
|
"Filter/Effect/Audio",
|
||||||
|
"Normalizes perceived loudness of an audio stream",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"audio/x-raw",
|
||||||
|
&[
|
||||||
|
("format", &gst_audio::AUDIO_FORMAT_F64.to_str()),
|
||||||
|
("rate", &192_000i32),
|
||||||
|
("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
||||||
|
("layout", &"interleaved"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -193,6 +193,7 @@ impl ObjectSubclass for AudioRNNoise {
|
||||||
const NAME: &'static str = "AudioRNNoise";
|
const NAME: &'static str = "AudioRNNoise";
|
||||||
type Type = super::AudioRNNoise;
|
type Type = super::AudioRNNoise;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -203,54 +204,64 @@ impl ObjectSubclass for AudioRNNoise {
|
||||||
state: Mutex::new(None),
|
state: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Audio denoise",
|
|
||||||
"Filter/Effect/Audio",
|
|
||||||
"Removes noise from an audio stream",
|
|
||||||
"Philippe Normand <philn@igalia.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"audio/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &gst_audio::AUDIO_FORMAT_F32.to_str()),
|
|
||||||
("rate", &48000),
|
|
||||||
("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
("layout", &"interleaved"),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.configure(
|
|
||||||
gst_base::subclass::BaseTransformMode::NeverInPlace,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for AudioRNNoise {}
|
impl ObjectImpl for AudioRNNoise {}
|
||||||
impl ElementImpl for AudioRNNoise {}
|
|
||||||
|
impl ElementImpl for AudioRNNoise {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Audio denoise",
|
||||||
|
"Filter/Effect/Audio",
|
||||||
|
"Removes noise from an audio stream",
|
||||||
|
"Philippe Normand <philn@igalia.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"audio/x-raw",
|
||||||
|
&[
|
||||||
|
("format", &gst_audio::AUDIO_FORMAT_F32.to_str()),
|
||||||
|
("rate", &48000),
|
||||||
|
("channels", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
||||||
|
("layout", &"interleaved"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseTransformImpl for AudioRNNoise {
|
impl BaseTransformImpl for AudioRNNoise {
|
||||||
|
const MODE: gst_base::subclass::BaseTransformMode =
|
||||||
|
gst_base::subclass::BaseTransformMode::NeverInPlace;
|
||||||
|
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||||
|
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||||
|
|
||||||
fn set_caps(
|
fn set_caps(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -14,6 +14,7 @@ gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org
|
||||||
claxon = { version = "0.4" }
|
claxon = { version = "0.4" }
|
||||||
byte-slice-cast = "1.0"
|
byte-slice-cast = "1.0"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
|
once_cell = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||||
|
|
|
@ -19,13 +19,22 @@ use atomic_refcell::AtomicRefCell;
|
||||||
|
|
||||||
use byte_slice_cast::*;
|
use byte_slice_cast::*;
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
|
gst::DebugCategory::new(
|
||||||
|
"claxondec",
|
||||||
|
gst::DebugColorFlags::empty(),
|
||||||
|
Some("Claxon FLAC decoder"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
streaminfo: Option<claxon::metadata::StreamInfo>,
|
streaminfo: Option<claxon::metadata::StreamInfo>,
|
||||||
audio_info: Option<gst_audio::AudioInfo>,
|
audio_info: Option<gst_audio::AudioInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClaxonDec {
|
pub struct ClaxonDec {
|
||||||
cat: gst::DebugCategory,
|
|
||||||
state: AtomicRefCell<Option<State>>,
|
state: AtomicRefCell<Option<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +42,7 @@ impl ObjectSubclass for ClaxonDec {
|
||||||
const NAME: &'static str = "ClaxonDec";
|
const NAME: &'static str = "ClaxonDec";
|
||||||
type Type = super::ClaxonDec;
|
type Type = super::ClaxonDec;
|
||||||
type ParentType = gst_audio::AudioDecoder;
|
type ParentType = gst_audio::AudioDecoder;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -40,64 +50,69 @@ impl ObjectSubclass for ClaxonDec {
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
cat: gst::DebugCategory::new(
|
|
||||||
"claxondec",
|
|
||||||
gst::DebugColorFlags::empty(),
|
|
||||||
Some("Claxon FLAC decoder"),
|
|
||||||
),
|
|
||||||
state: AtomicRefCell::new(None),
|
state: AtomicRefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Claxon FLAC decoder",
|
|
||||||
"Decoder/Audio",
|
|
||||||
"Claxon FLAC decoder",
|
|
||||||
"Ruben Gonzalez <rgonzalez@fluendo.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple("audio/x-flac", &[("framed", &true)]);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple(
|
|
||||||
"audio/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[
|
|
||||||
&gst_audio::AudioFormat::S8.to_str(),
|
|
||||||
&gst_audio::AUDIO_FORMAT_S16.to_str(),
|
|
||||||
&gst_audio::AUDIO_FORMAT_S2432.to_str(),
|
|
||||||
&gst_audio::AUDIO_FORMAT_S32.to_str(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
("rate", &gst::IntRange::<i32>::new(1, 655_350)),
|
|
||||||
("channels", &gst::IntRange::<i32>::new(1, 8)),
|
|
||||||
("layout", &"interleaved"),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ClaxonDec {}
|
impl ObjectImpl for ClaxonDec {}
|
||||||
|
|
||||||
impl ElementImpl for ClaxonDec {}
|
impl ElementImpl for ClaxonDec {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Claxon FLAC decoder",
|
||||||
|
"Decoder/Audio",
|
||||||
|
"Claxon FLAC decoder",
|
||||||
|
"Ruben Gonzalez <rgonzalez@fluendo.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple("audio/x-flac", &[("framed", &true)]);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple(
|
||||||
|
"audio/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst_audio::AudioFormat::S8.to_str(),
|
||||||
|
&gst_audio::AUDIO_FORMAT_S16.to_str(),
|
||||||
|
&gst_audio::AUDIO_FORMAT_S2432.to_str(),
|
||||||
|
&gst_audio::AUDIO_FORMAT_S32.to_str(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
("rate", &gst::IntRange::<i32>::new(1, 655_350)),
|
||||||
|
("channels", &gst::IntRange::<i32>::new(1, 8)),
|
||||||
|
("layout", &"interleaved"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template, src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AudioDecoderImpl for ClaxonDec {
|
impl AudioDecoderImpl for ClaxonDec {
|
||||||
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
@ -116,7 +131,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_format(&self, element: &Self::Type, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
fn set_format(&self, element: &Self::Type, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
||||||
gst_debug!(self.cat, obj: element, "Setting format {:?}", caps);
|
gst_debug!(CAT, obj: element, "Setting format {:?}", caps);
|
||||||
|
|
||||||
let mut streaminfo: Option<claxon::metadata::StreamInfo> = None;
|
let mut streaminfo: Option<claxon::metadata::StreamInfo> = None;
|
||||||
let mut audio_info: Option<gst_audio::AudioInfo> = None;
|
let mut audio_info: Option<gst_audio::AudioInfo> = None;
|
||||||
|
@ -127,18 +142,18 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
|
|
||||||
if streamheaders.len() < 2 {
|
if streamheaders.len() < 2 {
|
||||||
gst_debug!(
|
gst_debug!(
|
||||||
self.cat,
|
CAT,
|
||||||
obj: element,
|
obj: element,
|
||||||
"Not enough streamheaders, trying in-band"
|
"Not enough streamheaders, trying in-band"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let ident_buf = streamheaders[0].get::<gst::Buffer>();
|
let ident_buf = streamheaders[0].get::<gst::Buffer>();
|
||||||
if let Ok(Some(ident_buf)) = ident_buf {
|
if let Ok(Some(ident_buf)) = ident_buf {
|
||||||
gst_debug!(self.cat, obj: element, "Got streamheader buffers");
|
gst_debug!(CAT, obj: element, "Got streamheader buffers");
|
||||||
let inmap = ident_buf.map_readable().unwrap();
|
let inmap = ident_buf.map_readable().unwrap();
|
||||||
|
|
||||||
if inmap[0..7] != [0x7f, b'F', b'L', b'A', b'C', 0x01, 0x00] {
|
if inmap[0..7] != [0x7f, b'F', b'L', b'A', b'C', 0x01, 0x00] {
|
||||||
gst_debug!(self.cat, obj: element, "Unknown streamheader format");
|
gst_debug!(CAT, obj: element, "Unknown streamheader format");
|
||||||
} else if let Ok(tstreaminfo) = get_claxon_streaminfo(&inmap[13..]) {
|
} else if let Ok(tstreaminfo) = get_claxon_streaminfo(&inmap[13..]) {
|
||||||
if let Ok(taudio_info) = get_gstaudioinfo(tstreaminfo) {
|
if let Ok(taudio_info) = get_gstaudioinfo(tstreaminfo) {
|
||||||
// To speed up negotiation
|
// To speed up negotiation
|
||||||
|
@ -146,7 +161,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
|| element.negotiate().is_err()
|
|| element.negotiate().is_err()
|
||||||
{
|
{
|
||||||
gst_debug!(
|
gst_debug!(
|
||||||
self.cat,
|
CAT,
|
||||||
obj: element,
|
obj: element,
|
||||||
"Error to negotiate output from based on in-caps streaminfo"
|
"Error to negotiate output from based on in-caps streaminfo"
|
||||||
);
|
);
|
||||||
|
@ -175,7 +190,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
inbuf: Option<&gst::Buffer>,
|
inbuf: Option<&gst::Buffer>,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_debug!(self.cat, obj: element, "Handling buffer {:?}", inbuf);
|
gst_debug!(CAT, obj: element, "Handling buffer {:?}", inbuf);
|
||||||
|
|
||||||
let inbuf = match inbuf {
|
let inbuf = match inbuf {
|
||||||
None => return Ok(gst::FlowSuccess::Ok),
|
None => return Ok(gst::FlowSuccess::Ok),
|
||||||
|
@ -183,7 +198,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
};
|
};
|
||||||
|
|
||||||
let inmap = inbuf.map_readable().map_err(|_| {
|
let inmap = inbuf.map_readable().map_err(|_| {
|
||||||
gst_error!(self.cat, obj: element, "Failed to buffer readable");
|
gst_error!(CAT, obj: element, "Failed to buffer readable");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -191,17 +206,17 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?;
|
let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?;
|
||||||
|
|
||||||
if inmap.as_slice() == b"fLaC" {
|
if inmap.as_slice() == b"fLaC" {
|
||||||
gst_debug!(self.cat, obj: element, "fLaC buffer received");
|
gst_debug!(CAT, obj: element, "fLaC buffer received");
|
||||||
} else if inmap[0] & 0x7F == 0x00 {
|
} else if inmap[0] & 0x7F == 0x00 {
|
||||||
gst_debug!(self.cat, obj: element, "Streaminfo header buffer received");
|
gst_debug!(CAT, obj: element, "Streaminfo header buffer received");
|
||||||
return self.handle_streaminfo_header(element, state, inmap.as_ref());
|
return self.handle_streaminfo_header(element, state, inmap.as_ref());
|
||||||
} else if inmap[0] == 0b1111_1111 && inmap[1] & 0b1111_1100 == 0b1111_1000 {
|
} else if inmap[0] == 0b1111_1111 && inmap[1] & 0b1111_1100 == 0b1111_1000 {
|
||||||
gst_debug!(self.cat, obj: element, "Data buffer received");
|
gst_debug!(CAT, obj: element, "Data buffer received");
|
||||||
return self.handle_data(element, state, inmap.as_ref());
|
return self.handle_data(element, state, inmap.as_ref());
|
||||||
} else {
|
} else {
|
||||||
// info about other headers in flacparse and https://xiph.org/flac/format.html
|
// info about other headers in flacparse and https://xiph.org/flac/format.html
|
||||||
gst_debug!(
|
gst_debug!(
|
||||||
self.cat,
|
CAT,
|
||||||
obj: element,
|
obj: element,
|
||||||
"Other header buffer received {:?}",
|
"Other header buffer received {:?}",
|
||||||
inmap[0] & 0x7F
|
inmap[0] & 0x7F
|
||||||
|
@ -230,7 +245,7 @@ impl ClaxonDec {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
gst_debug!(
|
gst_debug!(
|
||||||
self.cat,
|
CAT,
|
||||||
obj: element,
|
obj: element,
|
||||||
"Successfully parsed headers: {:?}",
|
"Successfully parsed headers: {:?}",
|
||||||
audio_info
|
audio_info
|
||||||
|
|
|
@ -80,49 +80,6 @@ pub struct CsoundFilter {
|
||||||
compiled: AtomicBool,
|
compiled: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 4] = [
|
|
||||||
subclass::Property("loop", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Loop",
|
|
||||||
"loop over the score (can be changed in PLAYING or PAUSED state)",
|
|
||||||
DEFAULT_LOOP,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("location", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Location",
|
|
||||||
"Location of the csd file to be used by csound.
|
|
||||||
Use either location or CSD-text but not both at the same time, if so and error would be triggered",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("csd-text", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"CSD-text",
|
|
||||||
"The content of a csd file passed as a String.
|
|
||||||
Use either location or csd-text but not both at the same time, if so and error would be triggered",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("score_offset", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Score Offset",
|
|
||||||
"Score offset in seconds to start the performance",
|
|
||||||
0.0,
|
|
||||||
f64::MAX,
|
|
||||||
SCORE_OFFSET_DEFAULT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
// Considering an input of size: input_size and the user's ksmps,
|
// Considering an input of size: input_size and the user's ksmps,
|
||||||
// calculates the equivalent output_size
|
// calculates the equivalent output_size
|
||||||
|
@ -361,6 +318,7 @@ impl ObjectSubclass for CsoundFilter {
|
||||||
const NAME: &'static str = "CsoundFilter";
|
const NAME: &'static str = "CsoundFilter";
|
||||||
type Type = super::CsoundFilter;
|
type Type = super::CsoundFilter;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -383,61 +341,63 @@ impl ObjectSubclass for CsoundFilter {
|
||||||
compiled: AtomicBool::new(false),
|
compiled: AtomicBool::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Audio filter",
|
|
||||||
"Filter/Effect/Audio",
|
|
||||||
"Implement an audio filter/effects using Csound",
|
|
||||||
"Natanael Mojica <neithanmo@gmail.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"audio/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &gst_audio::AUDIO_FORMAT_F64.to_str()),
|
|
||||||
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
||||||
("channels", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
||||||
("layout", &"interleaved"),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.configure(
|
|
||||||
gst_base::subclass::BaseTransformMode::NeverInPlace,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for CsoundFilter {
|
impl ObjectImpl for CsoundFilter {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
match *prop {
|
vec![
|
||||||
subclass::Property("loop", ..) => {
|
glib::ParamSpec::boolean(
|
||||||
|
"loop",
|
||||||
|
"Loop",
|
||||||
|
"loop over the score (can be changed in PLAYING or PAUSED state)",
|
||||||
|
DEFAULT_LOOP,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"location",
|
||||||
|
"Location",
|
||||||
|
"Location of the csd file to be used by csound.
|
||||||
|
Use either location or CSD-text but not both at the same time, if so and error would be triggered",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"csd-text",
|
||||||
|
"CSD-text",
|
||||||
|
"The content of a csd file passed as a String.
|
||||||
|
Use either location or csd-text but not both at the same time, if so and error would be triggered",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"score-offset",
|
||||||
|
"Score Offset",
|
||||||
|
"Score offset in seconds to start the performance",
|
||||||
|
0.0,
|
||||||
|
f64::MAX,
|
||||||
|
SCORE_OFFSET_DEFAULT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"loop" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.loop_ = value.get_some().expect("type checked upstream");
|
settings.loop_ = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("location", ..) => {
|
"location" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
if self.state.lock().unwrap().is_none() {
|
if self.state.lock().unwrap().is_none() {
|
||||||
settings.location = match value.get::<String>() {
|
settings.location = match value.get::<String>() {
|
||||||
|
@ -446,7 +406,7 @@ impl ObjectImpl for CsoundFilter {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subclass::Property("csd-text", ..) => {
|
"csd-text" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
if self.state.lock().unwrap().is_none() {
|
if self.state.lock().unwrap().is_none() {
|
||||||
settings.csd_text = match value.get::<String>() {
|
settings.csd_text = match value.get::<String>() {
|
||||||
|
@ -455,7 +415,7 @@ impl ObjectImpl for CsoundFilter {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subclass::Property("score_offset", ..) => {
|
"score_offset" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.offset = value.get_some().expect("type checked upstream");
|
settings.offset = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -463,23 +423,21 @@ impl ObjectImpl for CsoundFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"loop" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("loop", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.loop_.to_value()
|
settings.loop_.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("location", ..) => {
|
"location" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.location.to_value()
|
settings.location.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("csd-text", ..) => {
|
"csd-text" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.csd_text.to_value()
|
settings.csd_text.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("score_offset", ..) => {
|
"score_offset" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.offset.to_value()
|
settings.offset.to_value()
|
||||||
}
|
}
|
||||||
|
@ -488,9 +446,60 @@ impl ObjectImpl for CsoundFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for CsoundFilter {}
|
impl ElementImpl for CsoundFilter {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Audio filter",
|
||||||
|
"Filter/Effect/Audio",
|
||||||
|
"Implement an audio filter/effects using Csound",
|
||||||
|
"Natanael Mojica <neithanmo@gmail.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"audio/x-raw",
|
||||||
|
&[
|
||||||
|
("format", &gst_audio::AUDIO_FORMAT_F64.to_str()),
|
||||||
|
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
|
("channels", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
|
("layout", &"interleaved"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseTransformImpl for CsoundFilter {
|
impl BaseTransformImpl for CsoundFilter {
|
||||||
|
const MODE: gst_base::subclass::BaseTransformMode =
|
||||||
|
gst_base::subclass::BaseTransformMode::NeverInPlace;
|
||||||
|
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||||
|
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||||
|
|
||||||
fn start(&self, _element: &Self::Type) -> std::result::Result<(), gst::ErrorMessage> {
|
fn start(&self, _element: &Self::Type) -> std::result::Result<(), gst::ErrorMessage> {
|
||||||
self.compile_score()?;
|
self.compile_score()?;
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ impl ObjectSubclass for LewtonDec {
|
||||||
const NAME: &'static str = "LewtonDec";
|
const NAME: &'static str = "LewtonDec";
|
||||||
type Type = super::LewtonDec;
|
type Type = super::LewtonDec;
|
||||||
type ParentType = gst_audio::AudioDecoder;
|
type ParentType = gst_audio::AudioDecoder;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -57,48 +58,58 @@ impl ObjectSubclass for LewtonDec {
|
||||||
state: AtomicRefCell::new(None),
|
state: AtomicRefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"lewton Vorbis decoder",
|
|
||||||
"Decoder/Audio",
|
|
||||||
"lewton Vorbis decoder",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple("audio/x-vorbis", &[]);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple(
|
|
||||||
"audio/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &gst_audio::AUDIO_FORMAT_F32.to_str()),
|
|
||||||
("rate", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
("channels", &gst::IntRange::<i32>::new(1, 255)),
|
|
||||||
("layout", &"interleaved"),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for LewtonDec {}
|
impl ObjectImpl for LewtonDec {}
|
||||||
|
|
||||||
impl ElementImpl for LewtonDec {}
|
impl ElementImpl for LewtonDec {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"lewton Vorbis decoder",
|
||||||
|
"Decoder/Audio",
|
||||||
|
"lewton Vorbis decoder",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple("audio/x-vorbis", &[]);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple(
|
||||||
|
"audio/x-raw",
|
||||||
|
&[
|
||||||
|
("format", &gst_audio::AUDIO_FORMAT_F32.to_str()),
|
||||||
|
("rate", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
||||||
|
("channels", &gst::IntRange::<i32>::new(1, 255)),
|
||||||
|
("layout", &"interleaved"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template, src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AudioDecoderImpl for LewtonDec {
|
impl AudioDecoderImpl for LewtonDec {
|
||||||
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
|
|
@ -38,16 +38,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("location", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"File Location",
|
|
||||||
"Location of the file to write",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
Stopped,
|
Stopped,
|
||||||
Started { file: File, position: u64 },
|
Started { file: File, position: u64 },
|
||||||
|
@ -120,6 +110,7 @@ impl ObjectSubclass for FileSink {
|
||||||
const NAME: &'static str = "RsFileSink";
|
const NAME: &'static str = "RsFileSink";
|
||||||
type Type = super::FileSink;
|
type Type = super::FileSink;
|
||||||
type ParentType = gst_base::BaseSink;
|
type ParentType = gst_base::BaseSink;
|
||||||
|
type Interfaces = (gst::URIHandler,);
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -131,38 +122,32 @@ impl ObjectSubclass for FileSink {
|
||||||
state: Mutex::new(Default::default()),
|
state: Mutex::new(Default::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_init(type_: &mut subclass::InitializingType<Self>) {
|
|
||||||
type_.add_interface::<gst::URIHandler>();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"File Sink",
|
|
||||||
"Sink/File",
|
|
||||||
"Write stream to a file",
|
|
||||||
"François Laignel <fengalin@free.fr>, Luis de Bethencourt <luisbg@osg.samsung.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for FileSink {
|
impl ObjectImpl for FileSink {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
match *prop {
|
vec![glib::ParamSpec::string(
|
||||||
subclass::Property("location", ..) => {
|
"location",
|
||||||
|
"File Location",
|
||||||
|
"Location of the file to write",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"location" => {
|
||||||
let res = match value.get::<String>() {
|
let res = match value.get::<String>() {
|
||||||
Ok(Some(location)) => FileLocation::try_from_path_str(location)
|
Ok(Some(location)) => FileLocation::try_from_path_str(location)
|
||||||
.and_then(|file_location| self.set_location(obj, Some(file_location))),
|
.and_then(|file_location| self.set_location(obj, Some(file_location))),
|
||||||
|
@ -178,10 +163,9 @@ impl ObjectImpl for FileSink {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
match *prop {
|
"location" => {
|
||||||
subclass::Property("location", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
let location = settings
|
let location = settings
|
||||||
.location
|
.location
|
||||||
|
@ -195,7 +179,37 @@ impl ObjectImpl for FileSink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for FileSink {}
|
impl ElementImpl for FileSink {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"File Sink",
|
||||||
|
"Sink/File",
|
||||||
|
"Write stream to a file",
|
||||||
|
"François Laignel <fengalin@free.fr>, Luis de Bethencourt <luisbg@osg.samsung.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseSinkImpl for FileSink {
|
impl BaseSinkImpl for FileSink {
|
||||||
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
@ -286,6 +300,12 @@ impl BaseSinkImpl for FileSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl URIHandlerImpl for FileSink {
|
impl URIHandlerImpl for FileSink {
|
||||||
|
const URI_TYPE: gst::URIType = gst::URIType::Sink;
|
||||||
|
|
||||||
|
fn get_protocols() -> &'static [&'static str] {
|
||||||
|
&["file"]
|
||||||
|
}
|
||||||
|
|
||||||
fn get_uri(&self, _element: &Self::Type) -> Option<String> {
|
fn get_uri(&self, _element: &Self::Type) -> Option<String> {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
@ -308,12 +328,4 @@ impl URIHandlerImpl for FileSink {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uri_type() -> gst::URIType {
|
|
||||||
gst::URIType::Sink
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_protocols() -> Vec<String> {
|
|
||||||
vec!["file".to_string()]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,16 +38,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("location", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"File Location",
|
|
||||||
"Location of the file to read from",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
Stopped,
|
Stopped,
|
||||||
Started { file: File, position: u64 },
|
Started { file: File, position: u64 },
|
||||||
|
@ -134,6 +124,7 @@ impl ObjectSubclass for FileSrc {
|
||||||
const NAME: &'static str = "RsFileSrc";
|
const NAME: &'static str = "RsFileSrc";
|
||||||
type Type = super::FileSrc;
|
type Type = super::FileSrc;
|
||||||
type ParentType = gst_base::BaseSrc;
|
type ParentType = gst_base::BaseSrc;
|
||||||
|
type Interfaces = (gst::URIHandler,);
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -145,38 +136,32 @@ impl ObjectSubclass for FileSrc {
|
||||||
state: Mutex::new(Default::default()),
|
state: Mutex::new(Default::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_init(type_: &mut subclass::InitializingType<Self>) {
|
|
||||||
type_.add_interface::<gst::URIHandler>();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"File Source",
|
|
||||||
"Source/File",
|
|
||||||
"Read stream from a file",
|
|
||||||
"François Laignel <fengalin@free.fr>, Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for FileSrc {
|
impl ObjectImpl for FileSrc {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
match *prop {
|
vec![glib::ParamSpec::string(
|
||||||
subclass::Property("location", ..) => {
|
"location",
|
||||||
|
"File Location",
|
||||||
|
"Location of the file to read from",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"location" => {
|
||||||
let res = match value.get::<String>() {
|
let res = match value.get::<String>() {
|
||||||
Ok(Some(location)) => FileLocation::try_from_path_str(location)
|
Ok(Some(location)) => FileLocation::try_from_path_str(location)
|
||||||
.and_then(|file_location| self.set_location(obj, Some(file_location))),
|
.and_then(|file_location| self.set_location(obj, Some(file_location))),
|
||||||
|
@ -192,10 +177,9 @@ impl ObjectImpl for FileSrc {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
match *prop {
|
"location" => {
|
||||||
subclass::Property("location", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
let location = settings
|
let location = settings
|
||||||
.location
|
.location
|
||||||
|
@ -215,7 +199,37 @@ impl ObjectImpl for FileSrc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for FileSrc {}
|
impl ElementImpl for FileSrc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"File Source",
|
||||||
|
"Source/File",
|
||||||
|
"Read stream from a file",
|
||||||
|
"François Laignel <fengalin@free.fr>, Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseSrcImpl for FileSrc {
|
impl BaseSrcImpl for FileSrc {
|
||||||
fn is_seekable(&self, _src: &Self::Type) -> bool {
|
fn is_seekable(&self, _src: &Self::Type) -> bool {
|
||||||
|
@ -339,6 +353,12 @@ impl BaseSrcImpl for FileSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl URIHandlerImpl for FileSrc {
|
impl URIHandlerImpl for FileSrc {
|
||||||
|
const URI_TYPE: gst::URIType = gst::URIType::Src;
|
||||||
|
|
||||||
|
fn get_protocols() -> &'static [&'static str] {
|
||||||
|
&["file"]
|
||||||
|
}
|
||||||
|
|
||||||
fn get_uri(&self, _element: &Self::Type) -> Option<String> {
|
fn get_uri(&self, _element: &Self::Type) -> Option<String> {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
@ -361,12 +381,4 @@ impl URIHandlerImpl for FileSrc {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uri_type() -> gst::URIType {
|
|
||||||
gst::URIType::Src
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_protocols() -> Vec<String> {
|
|
||||||
vec!["file".to_string()]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,27 +41,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 2] = [
|
|
||||||
subclass::Property("receiver-key", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Receiver Key",
|
|
||||||
"The private key of the Reeiver",
|
|
||||||
glib::Bytes::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("sender-key", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Sender Key",
|
|
||||||
"The public key of the Sender",
|
|
||||||
glib::Bytes::static_type(),
|
|
||||||
glib::ParamFlags::WRITABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct Props {
|
struct Props {
|
||||||
receiver_key: Option<glib::Bytes>,
|
receiver_key: Option<glib::Bytes>,
|
||||||
|
@ -570,6 +549,7 @@ impl ObjectSubclass for Decrypter {
|
||||||
const NAME: &'static str = "RsSodiumDecryptor";
|
const NAME: &'static str = "RsSodiumDecryptor";
|
||||||
type Type = super::Decrypter;
|
type Type = super::Decrypter;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -621,38 +601,32 @@ impl ObjectSubclass for Decrypter {
|
||||||
state,
|
state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Decrypter",
|
|
||||||
"Generic",
|
|
||||||
"libsodium-based file decrypter",
|
|
||||||
"Jordan Petridis <jordan@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&gst::Caps::new_any(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::builder("application/x-sodium-encrypted").build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Decrypter {
|
impl ObjectImpl for Decrypter {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"receiver-key",
|
||||||
|
"Receiver Key",
|
||||||
|
"The private key of the Reeiver",
|
||||||
|
glib::Bytes::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"sender-key",
|
||||||
|
"Sender Key",
|
||||||
|
"The public key of the Sender",
|
||||||
|
glib::Bytes::static_type(),
|
||||||
|
glib::ParamFlags::WRITABLE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -660,16 +634,20 @@ impl ObjectImpl for Decrypter {
|
||||||
obj.add_pad(&self.srcpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("sender-key", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"sender-key" => {
|
||||||
let mut props = self.props.lock().unwrap();
|
let mut props = self.props.lock().unwrap();
|
||||||
props.sender_key = value.get().expect("type checked upstream");
|
props.sender_key = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
|
||||||
subclass::Property("receiver-key", ..) => {
|
"receiver-key" => {
|
||||||
let mut props = self.props.lock().unwrap();
|
let mut props = self.props.lock().unwrap();
|
||||||
props.receiver_key = value.get().expect("type checked upstream");
|
props.receiver_key = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -678,11 +656,9 @@ impl ObjectImpl for Decrypter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"receiver-key" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("receiver-key", ..) => {
|
|
||||||
let props = self.props.lock().unwrap();
|
let props = self.props.lock().unwrap();
|
||||||
props.receiver_key.to_value()
|
props.receiver_key.to_value()
|
||||||
}
|
}
|
||||||
|
@ -693,6 +669,44 @@ impl ObjectImpl for Decrypter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Decrypter {
|
impl ElementImpl for Decrypter {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Decrypter",
|
||||||
|
"Generic",
|
||||||
|
"libsodium-based file decrypter",
|
||||||
|
"Jordan Petridis <jordan@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&gst::Caps::new_any(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_caps = gst::Caps::builder("application/x-sodium-encrypted").build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -44,38 +44,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 3] = [
|
|
||||||
subclass::Property("receiver-key", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Receiver Key",
|
|
||||||
"The public key of the Receiver",
|
|
||||||
glib::Bytes::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("sender-key", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Sender Key",
|
|
||||||
"The private key of the Sender",
|
|
||||||
glib::Bytes::static_type(),
|
|
||||||
glib::ParamFlags::WRITABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("block-size", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Block Size",
|
|
||||||
"The block-size of the chunks",
|
|
||||||
1024,
|
|
||||||
std::u32::MAX,
|
|
||||||
32768,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Props {
|
struct Props {
|
||||||
receiver_key: Option<glib::Bytes>,
|
receiver_key: Option<glib::Bytes>,
|
||||||
|
@ -392,6 +360,7 @@ impl ObjectSubclass for Encrypter {
|
||||||
const NAME: &'static str = "RsSodiumEncrypter";
|
const NAME: &'static str = "RsSodiumEncrypter";
|
||||||
type Type = super::Encrypter;
|
type Type = super::Encrypter;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -444,38 +413,41 @@ impl ObjectSubclass for Encrypter {
|
||||||
state,
|
state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Encrypter",
|
|
||||||
"Generic",
|
|
||||||
"libsodium-based file encrypter",
|
|
||||||
"Jordan Petridis <jordan@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::builder("application/x-sodium-encrypted").build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&gst::Caps::new_any(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Encrypter {
|
impl ObjectImpl for Encrypter {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"receiver-key",
|
||||||
|
"Receiver Key",
|
||||||
|
"The public key of the Receiver",
|
||||||
|
glib::Bytes::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"sender-key",
|
||||||
|
"Sender Key",
|
||||||
|
"The private key of the Sender",
|
||||||
|
glib::Bytes::static_type(),
|
||||||
|
glib::ParamFlags::WRITABLE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"block-size",
|
||||||
|
"Block Size",
|
||||||
|
"The block-size of the chunks",
|
||||||
|
1024,
|
||||||
|
std::u32::MAX,
|
||||||
|
32768,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -483,21 +455,25 @@ impl ObjectImpl for Encrypter {
|
||||||
obj.add_pad(&self.srcpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("sender-key", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"sender-key" => {
|
||||||
let mut props = self.props.lock().unwrap();
|
let mut props = self.props.lock().unwrap();
|
||||||
props.sender_key = value.get().expect("type checked upstream");
|
props.sender_key = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
|
||||||
subclass::Property("receiver-key", ..) => {
|
"receiver-key" => {
|
||||||
let mut props = self.props.lock().unwrap();
|
let mut props = self.props.lock().unwrap();
|
||||||
props.receiver_key = value.get().expect("type checked upstream");
|
props.receiver_key = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
|
||||||
subclass::Property("block-size", ..) => {
|
"block-size" => {
|
||||||
let mut props = self.props.lock().unwrap();
|
let mut props = self.props.lock().unwrap();
|
||||||
props.block_size = value.get_some().expect("type checked upstream");
|
props.block_size = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -506,16 +482,14 @@ impl ObjectImpl for Encrypter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"receiver-key" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("receiver-key", ..) => {
|
|
||||||
let props = self.props.lock().unwrap();
|
let props = self.props.lock().unwrap();
|
||||||
props.receiver_key.to_value()
|
props.receiver_key.to_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
subclass::Property("block-size", ..) => {
|
"block-size" => {
|
||||||
let props = self.props.lock().unwrap();
|
let props = self.props.lock().unwrap();
|
||||||
props.block_size.to_value()
|
props.block_size.to_value()
|
||||||
}
|
}
|
||||||
|
@ -526,6 +500,44 @@ impl ObjectImpl for Encrypter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Encrypter {
|
impl ElementImpl for Encrypter {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Encrypter",
|
||||||
|
"Generic",
|
||||||
|
"libsodium-based file encrypter",
|
||||||
|
"Jordan Petridis <jordan@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let src_caps = gst::Caps::builder("application/x-sodium-encrypted").build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&gst::Caps::new_any(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -66,58 +66,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 5] = [
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-buffers", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Max Buffers",
|
|
||||||
"Maximum number of buffers to queue up",
|
|
||||||
1,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_MAX_BUFFERS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("caps", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Caps",
|
|
||||||
"Caps to use",
|
|
||||||
gst::Caps::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("do-timestamp", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Do Timestamp",
|
|
||||||
"Timestamp buffers with the current running time on arrival",
|
|
||||||
DEFAULT_DO_TIMESTAMP,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
gst::DebugCategory::new(
|
gst::DebugCategory::new(
|
||||||
"ts-appsrc",
|
"ts-appsrc",
|
||||||
|
@ -560,68 +508,12 @@ impl ObjectSubclass for AppSrc {
|
||||||
const NAME: &'static str = "RsTsAppSrc";
|
const NAME: &'static str = "RsTsAppSrc";
|
||||||
type Type = super::AppSrc;
|
type Type = super::AppSrc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing app source",
|
|
||||||
"Source/Generic",
|
|
||||||
"Thread-sharing app source",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.add_signal_with_class_handler(
|
|
||||||
"push-buffer",
|
|
||||||
glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION,
|
|
||||||
&[gst::Buffer::static_type()],
|
|
||||||
bool::static_type(),
|
|
||||||
|_, args| {
|
|
||||||
let element = args[0]
|
|
||||||
.get::<super::AppSrc>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let buffer = args[1]
|
|
||||||
.get::<gst::Buffer>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let appsrc = Self::from_instance(&element);
|
|
||||||
|
|
||||||
Some(appsrc.push_buffer(&element, buffer).to_value())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
klass.add_signal_with_class_handler(
|
|
||||||
"end-of-stream",
|
|
||||||
glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION,
|
|
||||||
&[],
|
|
||||||
bool::static_type(),
|
|
||||||
|_, args| {
|
|
||||||
let element = args[0]
|
|
||||||
.get::<super::AppSrc>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let appsrc = Self::from_instance(&element);
|
|
||||||
Some(appsrc.end_of_stream(&element).to_value())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let src_pad_handler = AppSrcPadHandler::default();
|
let src_pad_handler = AppSrcPadHandler::default();
|
||||||
|
|
||||||
|
@ -639,43 +531,134 @@ impl ObjectSubclass for AppSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for AppSrc {
|
impl ObjectImpl for AppSrc {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"max-buffers",
|
||||||
|
"Max Buffers",
|
||||||
|
"Maximum number of buffers to queue up",
|
||||||
|
1,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_MAX_BUFFERS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"caps",
|
||||||
|
"Caps",
|
||||||
|
"Caps to use",
|
||||||
|
gst::Caps::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"do-timestamp",
|
||||||
|
"Do Timestamp",
|
||||||
|
"Timestamp buffers with the current running time on arrival",
|
||||||
|
DEFAULT_DO_TIMESTAMP,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signals() -> &'static [glib::subclass::Signal] {
|
||||||
|
static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::subclass::Signal::builder(
|
||||||
|
"push-buffer",
|
||||||
|
&[gst::Buffer::static_type()],
|
||||||
|
bool::static_type(),
|
||||||
|
)
|
||||||
|
.action()
|
||||||
|
.class_handler(|_, args| {
|
||||||
|
let element = args[0]
|
||||||
|
.get::<super::AppSrc>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let buffer = args[1]
|
||||||
|
.get::<gst::Buffer>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let appsrc = AppSrc::from_instance(&element);
|
||||||
|
|
||||||
|
Some(appsrc.push_buffer(&element, buffer).to_value())
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
glib::subclass::Signal::builder("end-of-stream", &[], bool::static_type())
|
||||||
|
.action()
|
||||||
|
.class_handler(|_, args| {
|
||||||
|
let element = args[0]
|
||||||
|
.get::<super::AppSrc>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let appsrc = AppSrc::from_instance(&element);
|
||||||
|
|
||||||
|
Some(appsrc.end_of_stream(&element).to_value())
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
SIGNALS.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("caps", ..) => {
|
"caps" => {
|
||||||
settings.caps = value.get().expect("type checked upstream");
|
settings.caps = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-buffers", ..) => {
|
"max-buffers" => {
|
||||||
settings.max_buffers = value.get_some().expect("type checked upstream");
|
settings.max_buffers = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("do-timestamp", ..) => {
|
"do-timestamp" => {
|
||||||
settings.do_timestamp = value.get_some().expect("type checked upstream");
|
settings.do_timestamp = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
|
||||||
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("context", ..) => settings.context.to_value(),
|
"context" => settings.context.to_value(),
|
||||||
subclass::Property("context-wait", ..) => settings.context_wait.to_value(),
|
"context-wait" => settings.context_wait.to_value(),
|
||||||
subclass::Property("caps", ..) => settings.caps.to_value(),
|
"caps" => settings.caps.to_value(),
|
||||||
subclass::Property("max-buffers", ..) => settings.max_buffers.to_value(),
|
"max-buffers" => settings.max_buffers.to_value(),
|
||||||
subclass::Property("do-timestamp", ..) => settings.do_timestamp.to_value(),
|
"do-timestamp" => settings.do_timestamp.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -690,6 +673,36 @@ impl ObjectImpl for AppSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for AppSrc {
|
impl ElementImpl for AppSrc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing app source",
|
||||||
|
"Source/Generic",
|
||||||
|
"Thread-sharing app source",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -55,38 +55,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 3] = [
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("active-pad", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Active Pad",
|
|
||||||
"Currently active pad",
|
|
||||||
gst::Pad::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct InputSelectorPadSinkHandlerInner {
|
struct InputSelectorPadSinkHandlerInner {
|
||||||
segment: Option<gst::Segment>,
|
segment: Option<gst::Segment>,
|
||||||
|
@ -429,42 +397,12 @@ impl ObjectSubclass for InputSelector {
|
||||||
const NAME: &'static str = "RsTsInputSelector";
|
const NAME: &'static str = "RsTsInputSelector";
|
||||||
type Type = super::InputSelector;
|
type Type = super::InputSelector;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing input selector",
|
|
||||||
"Generic",
|
|
||||||
"Simple input selector element",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink_%u",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Request,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
Self {
|
Self {
|
||||||
src_pad: PadSrc::new(
|
src_pad: PadSrc::new(
|
||||||
|
@ -479,22 +417,58 @@ impl ObjectSubclass for InputSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for InputSelector {
|
impl ObjectImpl for InputSelector {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::object(
|
||||||
|
"active-pad",
|
||||||
|
"Active Pad",
|
||||||
|
"Currently active pad",
|
||||||
|
gst::Pad::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("context", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"context" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("active-pad", ..) => {
|
"active-pad" => {
|
||||||
let pad = value.get::<gst::Pad>().expect("type checked upstream");
|
let pad = value.get::<gst::Pad>().expect("type checked upstream");
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
let pads = self.pads.lock().unwrap();
|
let pads = self.pads.lock().unwrap();
|
||||||
|
@ -526,19 +500,17 @@ impl ObjectImpl for InputSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"context" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("context", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.context.to_value()
|
settings.context.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.context_wait.to_value()
|
settings.context_wait.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("active-pad", ..) => {
|
"active-pad" => {
|
||||||
let state = self.state.lock().unwrap();
|
let state = self.state.lock().unwrap();
|
||||||
let active_pad = state.active_sinkpad.clone();
|
let active_pad = state.active_sinkpad.clone();
|
||||||
active_pad.to_value()
|
active_pad.to_value()
|
||||||
|
@ -556,6 +528,44 @@ impl ObjectImpl for InputSelector {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for InputSelector {
|
impl ElementImpl for InputSelector {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing input selector",
|
||||||
|
"Generic",
|
||||||
|
"Simple input selector element",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink_%u",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Request,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template, src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -72,80 +72,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 7] = [
|
|
||||||
subclass::Property("latency", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Buffer latency in ms",
|
|
||||||
"Amount of ms to buffer",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_LATENCY_MS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("do-lost", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Do Lost",
|
|
||||||
"Send an event downstream when a packet is lost",
|
|
||||||
DEFAULT_DO_LOST,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-dropout-time", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Max dropout time",
|
|
||||||
"The maximum time (milliseconds) of missing packets tolerated.",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_MAX_DROPOUT_TIME,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-misorder-time", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Max misorder time",
|
|
||||||
"The maximum time (milliseconds) of misordered packets tolerated.",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_MAX_MISORDER_TIME,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("stats", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Statistics",
|
|
||||||
"Various statistics",
|
|
||||||
gst::Structure::static_type(),
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Eq)]
|
#[derive(Eq)]
|
||||||
struct GapPacket {
|
struct GapPacket {
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
|
@ -1413,63 +1339,12 @@ impl ObjectSubclass for JitterBuffer {
|
||||||
const NAME: &'static str = "RsTsJitterBuffer";
|
const NAME: &'static str = "RsTsJitterBuffer";
|
||||||
type Type = super::JitterBuffer;
|
type Type = super::JitterBuffer;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing jitterbuffer",
|
|
||||||
"Generic",
|
|
||||||
"Simple jitterbuffer",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
klass.add_signal(
|
|
||||||
"request-pt-map",
|
|
||||||
glib::SignalFlags::RUN_LAST,
|
|
||||||
&[u32::static_type()],
|
|
||||||
gst::Caps::static_type(),
|
|
||||||
);
|
|
||||||
|
|
||||||
klass.add_signal_with_class_handler(
|
|
||||||
"clear-pt-map",
|
|
||||||
glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION,
|
|
||||||
&[],
|
|
||||||
glib::types::Type::Unit,
|
|
||||||
|_, args| {
|
|
||||||
let element = args[0]
|
|
||||||
.get::<super::JitterBuffer>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let jb = Self::from_instance(&element);
|
|
||||||
jb.clear_pt_map(&element);
|
|
||||||
None
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let sink_pad_handler = SinkHandler::default();
|
let sink_pad_handler = SinkHandler::default();
|
||||||
let src_pad_handler = SrcHandler::default();
|
let src_pad_handler = SrcHandler::default();
|
||||||
|
@ -1493,11 +1368,102 @@ impl ObjectSubclass for JitterBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for JitterBuffer {
|
impl ObjectImpl for JitterBuffer {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"latency",
|
||||||
|
"Buffer latency in ms",
|
||||||
|
"Amount of ms to buffer",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_LATENCY_MS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"do-lost",
|
||||||
|
"Do Lost",
|
||||||
|
"Send an event downstream when a packet is lost",
|
||||||
|
DEFAULT_DO_LOST,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"max-dropout-time",
|
||||||
|
"Max dropout time",
|
||||||
|
"The maximum time (milliseconds) of missing packets tolerated.",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_MAX_DROPOUT_TIME,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"max-misorder-time",
|
||||||
|
"Max misorder time",
|
||||||
|
"The maximum time (milliseconds) of misordered packets tolerated.",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_MAX_MISORDER_TIME,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"stats",
|
||||||
|
"Statistics",
|
||||||
|
"Various statistics",
|
||||||
|
gst::Structure::static_type(),
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("latency", ..) => {
|
}
|
||||||
|
|
||||||
|
fn signals() -> &'static [glib::subclass::Signal] {
|
||||||
|
static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::subclass::Signal::builder("clear-pt-map", &[], glib::types::Type::Unit)
|
||||||
|
.action()
|
||||||
|
.class_handler(|_, args| {
|
||||||
|
let element = args[0]
|
||||||
|
.get::<super::JitterBuffer>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let jb = JitterBuffer::from_instance(&element);
|
||||||
|
jb.clear_pt_map(&element);
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
SIGNALS.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"latency" => {
|
||||||
let latency_ms = {
|
let latency_ms = {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.latency_ms = value.get_some().expect("type checked upstream");
|
settings.latency_ms = value.get_some().expect("type checked upstream");
|
||||||
|
@ -1509,26 +1475,26 @@ impl ObjectImpl for JitterBuffer {
|
||||||
|
|
||||||
let _ = obj.post_message(gst::message::Latency::builder().src(obj).build());
|
let _ = obj.post_message(gst::message::Latency::builder().src(obj).build());
|
||||||
}
|
}
|
||||||
subclass::Property("do-lost", ..) => {
|
"do-lost" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.do_lost = value.get_some().expect("type checked upstream");
|
settings.do_lost = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-dropout-time", ..) => {
|
"max-dropout-time" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.max_dropout_time = value.get_some().expect("type checked upstream");
|
settings.max_dropout_time = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-misorder-time", ..) => {
|
"max-misorder-time" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.max_misorder_time = value.get_some().expect("type checked upstream");
|
settings.max_misorder_time = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -1536,27 +1502,25 @@ impl ObjectImpl for JitterBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"latency" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("latency", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.latency_ms.to_value()
|
settings.latency_ms.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("do-lost", ..) => {
|
"do-lost" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.do_lost.to_value()
|
settings.do_lost.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("max-dropout-time", ..) => {
|
"max-dropout-time" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.max_dropout_time.to_value()
|
settings.max_dropout_time.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("max-misorder-time", ..) => {
|
"max-misorder-time" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.max_misorder_time.to_value()
|
settings.max_misorder_time.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("stats", ..) => {
|
"stats" => {
|
||||||
let state = self.state.lock().unwrap();
|
let state = self.state.lock().unwrap();
|
||||||
let s = gst::Structure::new(
|
let s = gst::Structure::new(
|
||||||
"application/x-rtp-jitterbuffer-stats",
|
"application/x-rtp-jitterbuffer-stats",
|
||||||
|
@ -1568,11 +1532,11 @@ impl ObjectImpl for JitterBuffer {
|
||||||
);
|
);
|
||||||
s.to_value()
|
s.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.context.to_value()
|
settings.context.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.context_wait.to_value()
|
settings.context_wait.to_value()
|
||||||
}
|
}
|
||||||
|
@ -1590,6 +1554,45 @@ impl ObjectImpl for JitterBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for JitterBuffer {
|
impl ElementImpl for JitterBuffer {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing jitterbuffer",
|
||||||
|
"Generic",
|
||||||
|
"Simple jitterbuffer",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template, src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -93,81 +93,6 @@ impl Default for SettingsSrc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES_SRC: [subclass::Property; 6] = [
|
|
||||||
subclass::Property("max-size-buffers", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Max Size Buffers",
|
|
||||||
"Maximum number of buffers to queue (0=unlimited)",
|
|
||||||
0,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_MAX_SIZE_BUFFERS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-size-bytes", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Max Size Bytes",
|
|
||||||
"Maximum number of bytes to queue (0=unlimited)",
|
|
||||||
0,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_MAX_SIZE_BYTES,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-size-time", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Max Size Time",
|
|
||||||
"Maximum number of nanoseconds to queue (0=unlimited)",
|
|
||||||
0,
|
|
||||||
u64::MAX - 1,
|
|
||||||
DEFAULT_MAX_SIZE_TIME,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("proxy-context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Proxy Context",
|
|
||||||
"Context name of the proxy to share with",
|
|
||||||
Some(DEFAULT_PROXY_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
static PROPERTIES_SINK: [subclass::Property; 1] = [subclass::Property("proxy-context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Proxy Context",
|
|
||||||
"Context name of the proxy to share with",
|
|
||||||
Some(DEFAULT_PROXY_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
// TODO: Refactor into a Sender and Receiver instead of the have_ booleans
|
// TODO: Refactor into a Sender and Receiver instead of the have_ booleans
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -650,34 +575,13 @@ impl ProxySink {
|
||||||
impl ObjectSubclass for ProxySink {
|
impl ObjectSubclass for ProxySink {
|
||||||
const NAME: &'static str = "RsTsProxySink";
|
const NAME: &'static str = "RsTsProxySink";
|
||||||
type Type = super::ProxySink;
|
type Type = super::ProxySink;
|
||||||
|
type Interfaces = ();
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing proxy sink",
|
|
||||||
"Sink/Generic",
|
|
||||||
"Thread-sharing proxy sink",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES_SINK);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sink_pad: PadSink::new(
|
sink_pad: PadSink::new(
|
||||||
|
@ -691,12 +595,30 @@ impl ObjectSubclass for ProxySink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ProxySink {
|
impl ObjectImpl for ProxySink {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES_SINK[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::string(
|
||||||
|
"proxy-context",
|
||||||
|
"Proxy Context",
|
||||||
|
"Context name of the proxy to share with",
|
||||||
|
Some(DEFAULT_PROXY_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("proxy-context", ..) => {
|
"proxy-context" => {
|
||||||
settings.proxy_context = value
|
settings.proxy_context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
|
@ -706,12 +628,10 @@ impl ObjectImpl for ProxySink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES_SINK[id];
|
|
||||||
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("proxy-context", ..) => settings.proxy_context.to_value(),
|
"proxy-context" => settings.proxy_context.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -726,6 +646,37 @@ impl ObjectImpl for ProxySink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for ProxySink {
|
impl ElementImpl for ProxySink {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing proxy sink",
|
||||||
|
"Sink/Generic",
|
||||||
|
"Thread-sharing proxy sink",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
@ -1166,33 +1117,12 @@ impl ObjectSubclass for ProxySrc {
|
||||||
const NAME: &'static str = "RsTsProxySrc";
|
const NAME: &'static str = "RsTsProxySrc";
|
||||||
type Type = super::ProxySrc;
|
type Type = super::ProxySrc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing proxy source",
|
|
||||||
"Source/Generic",
|
|
||||||
"Thread-sharing proxy source",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES_SRC);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
@ -1212,30 +1142,93 @@ impl ObjectSubclass for ProxySrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ProxySrc {
|
impl ObjectImpl for ProxySrc {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES_SRC[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"proxy-context",
|
||||||
|
"Proxy Context",
|
||||||
|
"Context name of the proxy to share with",
|
||||||
|
Some(DEFAULT_PROXY_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"max-size-buffers",
|
||||||
|
"Max Size Buffers",
|
||||||
|
"Maximum number of buffers to queue (0=unlimited)",
|
||||||
|
0,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_MAX_SIZE_BUFFERS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"max-size-bytes",
|
||||||
|
"Max Size Bytes",
|
||||||
|
"Maximum number of bytes to queue (0=unlimited)",
|
||||||
|
0,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_MAX_SIZE_BYTES,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"max-size-time",
|
||||||
|
"Max Size Time",
|
||||||
|
"Maximum number of nanoseconds to queue (0=unlimited)",
|
||||||
|
0,
|
||||||
|
u64::MAX - 1,
|
||||||
|
DEFAULT_MAX_SIZE_TIME,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("max-size-buffers", ..) => {
|
"max-size-buffers" => {
|
||||||
settings.max_size_buffers = value.get_some().expect("type checked upstream");
|
settings.max_size_buffers = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-size-bytes", ..) => {
|
"max-size-bytes" => {
|
||||||
settings.max_size_bytes = value.get_some().expect("type checked upstream");
|
settings.max_size_bytes = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-size-time", ..) => {
|
"max-size-time" => {
|
||||||
settings.max_size_time = value.get_some().expect("type checked upstream");
|
settings.max_size_time = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("proxy-context", ..) => {
|
"proxy-context" => {
|
||||||
settings.proxy_context = value
|
settings.proxy_context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
|
@ -1245,17 +1238,15 @@ impl ObjectImpl for ProxySrc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES_SRC[id];
|
|
||||||
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("max-size-buffers", ..) => settings.max_size_buffers.to_value(),
|
"max-size-buffers" => settings.max_size_buffers.to_value(),
|
||||||
subclass::Property("max-size-bytes", ..) => settings.max_size_bytes.to_value(),
|
"max-size-bytes" => settings.max_size_bytes.to_value(),
|
||||||
subclass::Property("max-size-time", ..) => settings.max_size_time.to_value(),
|
"max-size-time" => settings.max_size_time.to_value(),
|
||||||
subclass::Property("context", ..) => settings.context.to_value(),
|
"context" => settings.context.to_value(),
|
||||||
subclass::Property("context-wait", ..) => settings.context_wait.to_value(),
|
"context-wait" => settings.context_wait.to_value(),
|
||||||
subclass::Property("proxy-context", ..) => settings.proxy_context.to_value(),
|
"proxy-context" => settings.proxy_context.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1270,6 +1261,37 @@ impl ObjectImpl for ProxySrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for ProxySrc {
|
impl ElementImpl for ProxySrc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing proxy source",
|
||||||
|
"Source/Generic",
|
||||||
|
"Thread-sharing proxy source",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -65,62 +65,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 5] = [
|
|
||||||
subclass::Property("max-size-buffers", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Max Size Buffers",
|
|
||||||
"Maximum number of buffers to queue (0=unlimited)",
|
|
||||||
0,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_MAX_SIZE_BUFFERS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-size-bytes", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Max Size Bytes",
|
|
||||||
"Maximum number of bytes to queue (0=unlimited)",
|
|
||||||
0,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_MAX_SIZE_BYTES,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-size-time", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Max Size Time",
|
|
||||||
"Maximum number of nanoseconds to queue (0=unlimited)",
|
|
||||||
0,
|
|
||||||
u64::MAX - 1,
|
|
||||||
DEFAULT_MAX_SIZE_TIME,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct PendingQueue {
|
struct PendingQueue {
|
||||||
more_queue_space_sender: Option<oneshot::Sender<()>>,
|
more_queue_space_sender: Option<oneshot::Sender<()>>,
|
||||||
|
@ -752,42 +696,12 @@ impl ObjectSubclass for Queue {
|
||||||
const NAME: &'static str = "RsTsQueue";
|
const NAME: &'static str = "RsTsQueue";
|
||||||
type Type = super::Queue;
|
type Type = super::Queue;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing queue",
|
|
||||||
"Generic",
|
|
||||||
"Simple data queue",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sink_pad: PadSink::new(
|
sink_pad: PadSink::new(
|
||||||
|
@ -808,43 +722,97 @@ impl ObjectSubclass for Queue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Queue {
|
impl ObjectImpl for Queue {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"max-size-buffers",
|
||||||
|
"Max Size Buffers",
|
||||||
|
"Maximum number of buffers to queue (0=unlimited)",
|
||||||
|
0,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_MAX_SIZE_BUFFERS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"max-size-bytes",
|
||||||
|
"Max Size Bytes",
|
||||||
|
"Maximum number of bytes to queue (0=unlimited)",
|
||||||
|
0,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_MAX_SIZE_BYTES,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"max-size-time",
|
||||||
|
"Max Size Time",
|
||||||
|
"Maximum number of nanoseconds to queue (0=unlimited)",
|
||||||
|
0,
|
||||||
|
u64::MAX - 1,
|
||||||
|
DEFAULT_MAX_SIZE_TIME,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("max-size-buffers", ..) => {
|
"max-size-buffers" => {
|
||||||
settings.max_size_buffers = value.get_some().expect("type checked upstream");
|
settings.max_size_buffers = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-size-bytes", ..) => {
|
"max-size-bytes" => {
|
||||||
settings.max_size_bytes = value.get_some().expect("type checked upstream");
|
settings.max_size_bytes = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-size-time", ..) => {
|
"max-size-time" => {
|
||||||
settings.max_size_time = value.get_some().expect("type checked upstream");
|
settings.max_size_time = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
|
||||||
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("max-size-buffers", ..) => settings.max_size_buffers.to_value(),
|
"max-size-buffers" => settings.max_size_buffers.to_value(),
|
||||||
subclass::Property("max-size-bytes", ..) => settings.max_size_bytes.to_value(),
|
"max-size-bytes" => settings.max_size_bytes.to_value(),
|
||||||
subclass::Property("max-size-time", ..) => settings.max_size_time.to_value(),
|
"max-size-time" => settings.max_size_time.to_value(),
|
||||||
subclass::Property("context", ..) => settings.context.to_value(),
|
"context" => settings.context.to_value(),
|
||||||
subclass::Property("context-wait", ..) => settings.context_wait.to_value(),
|
"context-wait" => settings.context_wait.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -858,6 +826,45 @@ impl ObjectImpl for Queue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Queue {
|
impl ElementImpl for Queue {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing queue",
|
||||||
|
"Generic",
|
||||||
|
"Simple data queue",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template, src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -75,69 +75,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 6] = [
|
|
||||||
subclass::Property("host", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Host",
|
|
||||||
"The host IP address to receive packets from",
|
|
||||||
DEFAULT_HOST,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("port", |name| {
|
|
||||||
glib::ParamSpec::int(
|
|
||||||
name,
|
|
||||||
"Port",
|
|
||||||
"Port to receive packets from",
|
|
||||||
0,
|
|
||||||
u16::MAX as i32,
|
|
||||||
DEFAULT_PORT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("caps", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Caps",
|
|
||||||
"Caps to use",
|
|
||||||
gst::Caps::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("blocksize", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Blocksize",
|
|
||||||
"Size in bytes to read per buffer (-1 = default)",
|
|
||||||
0,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_BLOCKSIZE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
struct TcpClientReader(tokio::net::TcpStream);
|
struct TcpClientReader(tokio::net::TcpStream);
|
||||||
|
|
||||||
impl TcpClientReader {
|
impl TcpClientReader {
|
||||||
|
@ -612,32 +549,12 @@ impl ObjectSubclass for TcpClientSrc {
|
||||||
const NAME: &'static str = "RsTsTcpClientSrc";
|
const NAME: &'static str = "RsTsTcpClientSrc";
|
||||||
type Type = super::TcpClientSrc;
|
type Type = super::TcpClientSrc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing TCP client source",
|
|
||||||
"Source/Network",
|
|
||||||
"Receives data over the network via TCP",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>, LEE Dongjun <redongjun@gmail.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let src_pad_handler = TcpClientSrcPadHandler::default();
|
let src_pad_handler = TcpClientSrcPadHandler::default();
|
||||||
|
|
||||||
|
@ -654,47 +571,106 @@ impl ObjectSubclass for TcpClientSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for TcpClientSrc {
|
impl ObjectImpl for TcpClientSrc {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"host",
|
||||||
|
"Host",
|
||||||
|
"The host IP address to receive packets from",
|
||||||
|
DEFAULT_HOST,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int(
|
||||||
|
"port",
|
||||||
|
"Port",
|
||||||
|
"Port to receive packets from",
|
||||||
|
0,
|
||||||
|
u16::MAX as i32,
|
||||||
|
DEFAULT_PORT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"caps",
|
||||||
|
"Caps",
|
||||||
|
"Caps to use",
|
||||||
|
gst::Caps::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"blocksize",
|
||||||
|
"Blocksize",
|
||||||
|
"Size in bytes to read per buffer (-1 = default)",
|
||||||
|
0,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_BLOCKSIZE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("host", ..) => {
|
"host" => {
|
||||||
settings.host = value.get().expect("type checked upstream");
|
settings.host = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("port", ..) => {
|
"port" => {
|
||||||
settings.port = value.get_some().expect("type checked upstream");
|
settings.port = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("caps", ..) => {
|
"caps" => {
|
||||||
settings.caps = value.get().expect("type checked upstream");
|
settings.caps = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("blocksize", ..) => {
|
"blocksize" => {
|
||||||
settings.blocksize = value.get_some().expect("type checked upstream");
|
settings.blocksize = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
|
||||||
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("host", ..) => settings.host.to_value(),
|
"host" => settings.host.to_value(),
|
||||||
subclass::Property("port", ..) => settings.port.to_value(),
|
"port" => settings.port.to_value(),
|
||||||
subclass::Property("caps", ..) => settings.caps.to_value(),
|
"caps" => settings.caps.to_value(),
|
||||||
subclass::Property("blocksize", ..) => settings.blocksize.to_value(),
|
"blocksize" => settings.blocksize.to_value(),
|
||||||
subclass::Property("context", ..) => settings.context.to_value(),
|
"context" => settings.context.to_value(),
|
||||||
subclass::Property("context-wait", ..) => settings.context_wait.to_value(),
|
"context-wait" => settings.context_wait.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -709,6 +685,36 @@ impl ObjectImpl for TcpClientSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for TcpClientSrc {
|
impl ElementImpl for TcpClientSrc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing TCP client source",
|
||||||
|
"Source/Network",
|
||||||
|
"Receives data over the network via TCP",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>, LEE Dongjun <redongjun@gmail.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -118,174 +118,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 17] = [
|
|
||||||
subclass::Property("sync", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Sync",
|
|
||||||
"Sync on the clock",
|
|
||||||
DEFAULT_SYNC,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("bind-address", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Bind Address",
|
|
||||||
"Address to bind the socket to",
|
|
||||||
Some(DEFAULT_BIND_ADDRESS),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("bind-port", |name| {
|
|
||||||
glib::ParamSpec::int(
|
|
||||||
name,
|
|
||||||
"Bind Port",
|
|
||||||
"Port to bind the socket to",
|
|
||||||
0,
|
|
||||||
u16::MAX as i32,
|
|
||||||
DEFAULT_BIND_PORT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("bind-address-v6", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Bind Address V6",
|
|
||||||
"Address to bind the V6 socket to",
|
|
||||||
Some(DEFAULT_BIND_ADDRESS_V6),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("bind-port-v6", |name| {
|
|
||||||
glib::ParamSpec::int(
|
|
||||||
name,
|
|
||||||
"Bind Port",
|
|
||||||
"Port to bind the V6 socket to",
|
|
||||||
0,
|
|
||||||
u16::MAX as i32,
|
|
||||||
DEFAULT_BIND_PORT_V6,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("socket", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Socket",
|
|
||||||
"Socket to use for UDP transmission. (None == allocate)",
|
|
||||||
gio::Socket::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("used-socket", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Used Socket",
|
|
||||||
"Socket currently in use for UDP transmission. (None = no socket)",
|
|
||||||
gio::Socket::static_type(),
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("socket-v6", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Socket V6",
|
|
||||||
"IPV6 Socket to use for UDP transmission. (None == allocate)",
|
|
||||||
gio::Socket::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("used-socket-v6", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Used Socket V6",
|
|
||||||
"V6 Socket currently in use for UDP transmission. (None = no socket)",
|
|
||||||
gio::Socket::static_type(),
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("auto-multicast", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Auto multicast",
|
|
||||||
"Automatically join/leave the multicast groups, FALSE means user has to do it himself",
|
|
||||||
DEFAULT_AUTO_MULTICAST,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("loop", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Loop",
|
|
||||||
"Set the multicast loop parameter.",
|
|
||||||
DEFAULT_LOOP,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("ttl", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Time To Live",
|
|
||||||
"Used for setting the unicast TTL parameter",
|
|
||||||
0,
|
|
||||||
u8::MAX as u32,
|
|
||||||
DEFAULT_TTL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("ttl-mc", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Time To Live Multicast",
|
|
||||||
"Used for setting the multicast TTL parameter",
|
|
||||||
0,
|
|
||||||
u8::MAX as u32,
|
|
||||||
DEFAULT_TTL_MC,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("qos-dscp", |name| {
|
|
||||||
glib::ParamSpec::int(
|
|
||||||
name,
|
|
||||||
"QoS DSCP",
|
|
||||||
"Quality of Service, differentiated services code point (-1 default)",
|
|
||||||
-1,
|
|
||||||
63,
|
|
||||||
DEFAULT_QOS_DSCP,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("clients", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Clients",
|
|
||||||
"A comma separated list of host:port pairs with destinations",
|
|
||||||
Some(DEFAULT_CLIENTS),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum TaskItem {
|
enum TaskItem {
|
||||||
Buffer(gst::Buffer),
|
Buffer(gst::Buffer),
|
||||||
|
@ -1127,107 +959,12 @@ impl ObjectSubclass for UdpSink {
|
||||||
const NAME: &'static str = "RsTsUdpSink";
|
const NAME: &'static str = "RsTsUdpSink";
|
||||||
type Type = super::UdpSink;
|
type Type = super::UdpSink;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing UDP sink",
|
|
||||||
"Sink/Network",
|
|
||||||
"Thread-sharing UDP sink",
|
|
||||||
"Mathieu <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
klass.add_signal_with_class_handler(
|
|
||||||
"add",
|
|
||||||
glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION,
|
|
||||||
&[String::static_type(), i32::static_type()],
|
|
||||||
glib::types::Type::Unit,
|
|
||||||
|_, args| {
|
|
||||||
let element = args[0]
|
|
||||||
.get::<super::UdpSink>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let host = args[1]
|
|
||||||
.get::<String>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let port = args[2]
|
|
||||||
.get::<i32>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
|
|
||||||
if let Ok(addr) = try_into_socket_addr(&element, &host, port) {
|
|
||||||
let udpsink = Self::from_instance(&element);
|
|
||||||
udpsink.add_client(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
klass.add_signal_with_class_handler(
|
|
||||||
"remove",
|
|
||||||
glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION,
|
|
||||||
&[String::static_type(), i32::static_type()],
|
|
||||||
glib::types::Type::Unit,
|
|
||||||
|_, args| {
|
|
||||||
let element = args[0]
|
|
||||||
.get::<super::UdpSink>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let host = args[1]
|
|
||||||
.get::<String>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
let port = args[2]
|
|
||||||
.get::<i32>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
|
|
||||||
let udpsink = Self::from_instance(&element);
|
|
||||||
|
|
||||||
if let Ok(addr) = try_into_socket_addr(&element, &host, port) {
|
|
||||||
udpsink.remove_client(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
klass.add_signal_with_class_handler(
|
|
||||||
"clear",
|
|
||||||
glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION,
|
|
||||||
&[],
|
|
||||||
glib::types::Type::Unit,
|
|
||||||
|_, args| {
|
|
||||||
let element = args[0]
|
|
||||||
.get::<super::UdpSink>()
|
|
||||||
.expect("signal arg")
|
|
||||||
.expect("missing signal arg");
|
|
||||||
|
|
||||||
let udpsink = Self::from_instance(&element);
|
|
||||||
udpsink.clear_clients(std::iter::empty());
|
|
||||||
|
|
||||||
None
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let settings = Arc::new(StdMutex::new(Settings::default()));
|
let settings = Arc::new(StdMutex::new(Settings::default()));
|
||||||
let sink_pad_handler = UdpSinkPadHandler::new(Arc::clone(&settings));
|
let sink_pad_handler = UdpSinkPadHandler::new(Arc::clone(&settings));
|
||||||
|
@ -1245,66 +982,289 @@ impl ObjectSubclass for UdpSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for UdpSink {
|
impl ObjectImpl for UdpSink {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"sync",
|
||||||
|
"Sync",
|
||||||
|
"Sync on the clock",
|
||||||
|
DEFAULT_SYNC,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"bind-address",
|
||||||
|
"Bind Address",
|
||||||
|
"Address to bind the socket to",
|
||||||
|
Some(DEFAULT_BIND_ADDRESS),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int(
|
||||||
|
"bind-port",
|
||||||
|
"Bind Port",
|
||||||
|
"Port to bind the socket to",
|
||||||
|
0,
|
||||||
|
u16::MAX as i32,
|
||||||
|
DEFAULT_BIND_PORT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"bind-address-v6",
|
||||||
|
"Bind Address V6",
|
||||||
|
"Address to bind the V6 socket to",
|
||||||
|
Some(DEFAULT_BIND_ADDRESS_V6),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int(
|
||||||
|
"bind-port-v6",
|
||||||
|
"Bind Port",
|
||||||
|
"Port to bind the V6 socket to",
|
||||||
|
0,
|
||||||
|
u16::MAX as i32,
|
||||||
|
DEFAULT_BIND_PORT_V6,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::object(
|
||||||
|
"socket",
|
||||||
|
"Socket",
|
||||||
|
"Socket to use for UDP transmission. (None == allocate)",
|
||||||
|
gio::Socket::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::object(
|
||||||
|
"used-socket",
|
||||||
|
"Used Socket",
|
||||||
|
"Socket currently in use for UDP transmission. (None = no socket)",
|
||||||
|
gio::Socket::static_type(),
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::object(
|
||||||
|
"socket-v6",
|
||||||
|
"Socket V6",
|
||||||
|
"IPV6 Socket to use for UDP transmission. (None == allocate)",
|
||||||
|
gio::Socket::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::object(
|
||||||
|
"used-socket-v6",
|
||||||
|
"Used Socket V6",
|
||||||
|
"V6 Socket currently in use for UDP transmission. (None = no socket)",
|
||||||
|
gio::Socket::static_type(),
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"auto-multicast",
|
||||||
|
"Auto multicast",
|
||||||
|
"Automatically join/leave the multicast groups, FALSE means user has to do it himself",
|
||||||
|
DEFAULT_AUTO_MULTICAST,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"loop",
|
||||||
|
"Loop",
|
||||||
|
"Set the multicast loop parameter.",
|
||||||
|
DEFAULT_LOOP,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"ttl",
|
||||||
|
"Time To Live",
|
||||||
|
"Used for setting the unicast TTL parameter",
|
||||||
|
0,
|
||||||
|
u8::MAX as u32,
|
||||||
|
DEFAULT_TTL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"ttl-mc",
|
||||||
|
"Time To Live Multicast",
|
||||||
|
"Used for setting the multicast TTL parameter",
|
||||||
|
0,
|
||||||
|
u8::MAX as u32,
|
||||||
|
DEFAULT_TTL_MC,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int(
|
||||||
|
"qos-dscp",
|
||||||
|
"QoS DSCP",
|
||||||
|
"Quality of Service, differentiated services code point (-1 default)",
|
||||||
|
-1,
|
||||||
|
63,
|
||||||
|
DEFAULT_QOS_DSCP,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"clients",
|
||||||
|
"Clients",
|
||||||
|
"A comma separated list of host:port pairs with destinations",
|
||||||
|
Some(DEFAULT_CLIENTS),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signals() -> &'static [glib::subclass::Signal] {
|
||||||
|
static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::subclass::Signal::builder(
|
||||||
|
"add",
|
||||||
|
&[String::static_type(), i32::static_type()],
|
||||||
|
glib::types::Type::Unit,
|
||||||
|
)
|
||||||
|
.action()
|
||||||
|
.class_handler(|_, args| {
|
||||||
|
let element = args[0]
|
||||||
|
.get::<super::UdpSink>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let host = args[1]
|
||||||
|
.get::<String>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let port = args[2]
|
||||||
|
.get::<i32>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
|
||||||
|
if let Ok(addr) = try_into_socket_addr(&element, &host, port) {
|
||||||
|
let udpsink = UdpSink::from_instance(&element);
|
||||||
|
udpsink.add_client(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
glib::subclass::Signal::builder(
|
||||||
|
"remove",
|
||||||
|
&[String::static_type(), i32::static_type()],
|
||||||
|
glib::types::Type::Unit,
|
||||||
|
)
|
||||||
|
.action()
|
||||||
|
.class_handler(|_, args| {
|
||||||
|
let element = args[0]
|
||||||
|
.get::<super::UdpSink>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let host = args[1]
|
||||||
|
.get::<String>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
let port = args[2]
|
||||||
|
.get::<i32>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
|
||||||
|
if let Ok(addr) = try_into_socket_addr(&element, &host, port) {
|
||||||
|
let udpsink = UdpSink::from_instance(&element);
|
||||||
|
udpsink.remove_client(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
glib::subclass::Signal::builder("clear", &[], glib::types::Type::Unit)
|
||||||
|
.action()
|
||||||
|
.class_handler(|_, args| {
|
||||||
|
let element = args[0]
|
||||||
|
.get::<super::UdpSink>()
|
||||||
|
.expect("signal arg")
|
||||||
|
.expect("missing signal arg");
|
||||||
|
|
||||||
|
let udpsink = UdpSink::from_instance(&element);
|
||||||
|
udpsink.clear_clients(std::iter::empty());
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.build(),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
SIGNALS.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("sync", ..) => {
|
"sync" => {
|
||||||
settings.sync = value.get_some().expect("type checked upstream");
|
settings.sync = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("bind-address", ..) => {
|
"bind-address" => {
|
||||||
settings.bind_address = value
|
settings.bind_address = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("bind-port", ..) => {
|
"bind-port" => {
|
||||||
settings.bind_port = value.get_some().expect("type checked upstream");
|
settings.bind_port = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("bind-address-v6", ..) => {
|
"bind-address-v6" => {
|
||||||
settings.bind_address_v6 = value
|
settings.bind_address_v6 = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("bind-port-v6", ..) => {
|
"bind-port-v6" => {
|
||||||
settings.bind_port_v6 = value.get_some().expect("type checked upstream");
|
settings.bind_port_v6 = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("socket", ..) => {
|
"socket" => {
|
||||||
settings.socket = value
|
settings.socket = value
|
||||||
.get::<gio::Socket>()
|
.get::<gio::Socket>()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.map(|socket| GioSocketWrapper::new(&socket));
|
.map(|socket| GioSocketWrapper::new(&socket));
|
||||||
}
|
}
|
||||||
subclass::Property("used-socket", ..) => {
|
"used-socket" => {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
subclass::Property("socket-v6", ..) => {
|
"socket-v6" => {
|
||||||
settings.socket_v6 = value
|
settings.socket_v6 = value
|
||||||
.get::<gio::Socket>()
|
.get::<gio::Socket>()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.map(|socket| GioSocketWrapper::new(&socket));
|
.map(|socket| GioSocketWrapper::new(&socket));
|
||||||
}
|
}
|
||||||
subclass::Property("used-socket-v6", ..) => {
|
"used-socket-v6" => {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
subclass::Property("auto-multicast", ..) => {
|
"auto-multicast" => {
|
||||||
settings.auto_multicast = value.get_some().expect("type checked upstream");
|
settings.auto_multicast = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("loop", ..) => {
|
"loop" => {
|
||||||
settings.multicast_loop = value.get_some().expect("type checked upstream");
|
settings.multicast_loop = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("ttl", ..) => {
|
"ttl" => {
|
||||||
settings.ttl = value.get_some().expect("type checked upstream");
|
settings.ttl = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("ttl-mc", ..) => {
|
"ttl-mc" => {
|
||||||
settings.ttl_mc = value.get_some().expect("type checked upstream");
|
settings.ttl_mc = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("qos-dscp", ..) => {
|
"qos-dscp" => {
|
||||||
settings.qos_dscp = value.get_some().expect("type checked upstream");
|
settings.qos_dscp = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("clients", ..) => {
|
"clients" => {
|
||||||
let clients: String = value
|
let clients: String = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
|
@ -1329,55 +1289,53 @@ impl ObjectImpl for UdpSink {
|
||||||
|
|
||||||
self.clear_clients(clients_iter);
|
self.clear_clients(clients_iter);
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
|
||||||
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("sync", ..) => settings.sync.to_value(),
|
"sync" => settings.sync.to_value(),
|
||||||
subclass::Property("bind-address", ..) => settings.bind_address.to_value(),
|
"bind-address" => settings.bind_address.to_value(),
|
||||||
subclass::Property("bind-port", ..) => settings.bind_port.to_value(),
|
"bind-port" => settings.bind_port.to_value(),
|
||||||
subclass::Property("bind-address-v6", ..) => settings.bind_address_v6.to_value(),
|
"bind-address-v6" => settings.bind_address_v6.to_value(),
|
||||||
subclass::Property("bind-port-v6", ..) => settings.bind_port_v6.to_value(),
|
"bind-port-v6" => settings.bind_port_v6.to_value(),
|
||||||
subclass::Property("socket", ..) => settings
|
"socket" => settings
|
||||||
.socket
|
.socket
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(GioSocketWrapper::as_socket)
|
.map(GioSocketWrapper::as_socket)
|
||||||
.to_value(),
|
.to_value(),
|
||||||
subclass::Property("used-socket", ..) => settings
|
"used-socket" => settings
|
||||||
.used_socket
|
.used_socket
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(GioSocketWrapper::as_socket)
|
.map(GioSocketWrapper::as_socket)
|
||||||
.to_value(),
|
.to_value(),
|
||||||
subclass::Property("socket-v6", ..) => settings
|
"socket-v6" => settings
|
||||||
.socket_v6
|
.socket_v6
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(GioSocketWrapper::as_socket)
|
.map(GioSocketWrapper::as_socket)
|
||||||
.to_value(),
|
.to_value(),
|
||||||
subclass::Property("used-socket-v6", ..) => settings
|
"used-socket-v6" => settings
|
||||||
.used_socket_v6
|
.used_socket_v6
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(GioSocketWrapper::as_socket)
|
.map(GioSocketWrapper::as_socket)
|
||||||
.to_value(),
|
.to_value(),
|
||||||
subclass::Property("auto-multicast", ..) => settings.sync.to_value(),
|
"auto-multicast" => settings.sync.to_value(),
|
||||||
subclass::Property("loop", ..) => settings.multicast_loop.to_value(),
|
"loop" => settings.multicast_loop.to_value(),
|
||||||
subclass::Property("ttl", ..) => settings.ttl.to_value(),
|
"ttl" => settings.ttl.to_value(),
|
||||||
subclass::Property("ttl-mc", ..) => settings.ttl_mc.to_value(),
|
"ttl-mc" => settings.ttl_mc.to_value(),
|
||||||
subclass::Property("qos-dscp", ..) => settings.qos_dscp.to_value(),
|
"qos-dscp" => settings.qos_dscp.to_value(),
|
||||||
subclass::Property("clients", ..) => {
|
"clients" => {
|
||||||
drop(settings);
|
drop(settings);
|
||||||
|
|
||||||
let clients: Vec<String> = self
|
let clients: Vec<String> = self
|
||||||
|
@ -1389,8 +1347,8 @@ impl ObjectImpl for UdpSink {
|
||||||
|
|
||||||
clients.join(",").to_value()
|
clients.join(",").to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => settings.context.to_value(),
|
"context" => settings.context.to_value(),
|
||||||
subclass::Property("context-wait", ..) => settings.context_wait.to_value(),
|
"context-wait" => settings.context_wait.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1405,6 +1363,37 @@ impl ObjectImpl for UdpSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for UdpSink {
|
impl ElementImpl for UdpSink {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing UDP sink",
|
||||||
|
"Sink/Network",
|
||||||
|
"Thread-sharing UDP sink",
|
||||||
|
"Mathieu <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -84,105 +84,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 10] = [
|
|
||||||
subclass::Property("address", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Address",
|
|
||||||
"Address/multicast group to listen on",
|
|
||||||
DEFAULT_ADDRESS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("port", |name| {
|
|
||||||
glib::ParamSpec::int(
|
|
||||||
name,
|
|
||||||
"Port",
|
|
||||||
"Port to listen on",
|
|
||||||
0,
|
|
||||||
u16::MAX as i32,
|
|
||||||
DEFAULT_PORT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("reuse", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Reuse",
|
|
||||||
"Allow reuse of the port",
|
|
||||||
DEFAULT_REUSE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("caps", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Caps",
|
|
||||||
"Caps to use",
|
|
||||||
gst::Caps::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("mtu", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"MTU",
|
|
||||||
"Maximum expected packet size. This directly defines the allocation size of the receive buffer pool",
|
|
||||||
0,
|
|
||||||
i32::MAX as u32,
|
|
||||||
DEFAULT_MTU,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("socket", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Socket",
|
|
||||||
"Socket to use for UDP reception. (None == allocate)",
|
|
||||||
gio::Socket::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("used-socket", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Used Socket",
|
|
||||||
"Socket currently in use for UDP reception. (None = no socket)",
|
|
||||||
gio::Socket::static_type(),
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("context-wait", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Context Wait",
|
|
||||||
"Throttle poll loop to run at most once every this many ms",
|
|
||||||
0,
|
|
||||||
1000,
|
|
||||||
DEFAULT_CONTEXT_WAIT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("retrieve-sender-address", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Retrieve sender address",
|
|
||||||
"Whether to retrieve the sender address and add it to buffers as meta. Disabling this might result in minor performance improvements in certain scenarios",
|
|
||||||
DEFAULT_RETRIEVE_SENDER_ADDRESS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct UdpReader(tokio::net::UdpSocket);
|
struct UdpReader(tokio::net::UdpSocket);
|
||||||
|
|
||||||
|
@ -789,48 +690,12 @@ impl ObjectSubclass for UdpSrc {
|
||||||
const NAME: &'static str = "RsTsUdpSrc";
|
const NAME: &'static str = "RsTsUdpSrc";
|
||||||
type Type = super::UdpSrc;
|
type Type = super::UdpSrc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing UDP source",
|
|
||||||
"Source/Network",
|
|
||||||
"Receives data over the network via UDP",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
{
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
#[cfg(windows)]
|
|
||||||
{
|
|
||||||
let properties = PROPERTIES
|
|
||||||
.iter()
|
|
||||||
.filter(|p| match *p {
|
|
||||||
subclass::Property("socket", ..) | subclass::Property("used-socket", ..) => {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
klass.install_properties(properties.as_slice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let src_pad_handler = UdpSrcPadHandler::default();
|
let src_pad_handler = UdpSrcPadHandler::default();
|
||||||
|
|
||||||
|
@ -847,76 +712,167 @@ impl ObjectSubclass for UdpSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for UdpSrc {
|
impl ObjectImpl for UdpSrc {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
let mut properties = vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"context-wait",
|
||||||
|
"Context Wait",
|
||||||
|
"Throttle poll loop to run at most once every this many ms",
|
||||||
|
0,
|
||||||
|
1000,
|
||||||
|
DEFAULT_CONTEXT_WAIT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"address",
|
||||||
|
"Address",
|
||||||
|
"Address/multicast group to listen on",
|
||||||
|
DEFAULT_ADDRESS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int(
|
||||||
|
"port",
|
||||||
|
"Port",
|
||||||
|
"Port to listen on",
|
||||||
|
0,
|
||||||
|
u16::MAX as i32,
|
||||||
|
DEFAULT_PORT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"reuse",
|
||||||
|
"Reuse",
|
||||||
|
"Allow reuse of the port",
|
||||||
|
DEFAULT_REUSE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"caps",
|
||||||
|
"Caps",
|
||||||
|
"Caps to use",
|
||||||
|
gst::Caps::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"mtu",
|
||||||
|
"MTU",
|
||||||
|
"Maximum expected packet size. This directly defines the allocation size of the receive buffer pool",
|
||||||
|
0,
|
||||||
|
i32::MAX as u32,
|
||||||
|
DEFAULT_MTU,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"retrieve-sender-address",
|
||||||
|
"Retrieve sender address",
|
||||||
|
"Whether to retrieve the sender address and add it to buffers as meta. Disabling this might result in minor performance improvements in certain scenarios",
|
||||||
|
DEFAULT_RETRIEVE_SENDER_ADDRESS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
properties.push(glib::ParamSpec::object(
|
||||||
|
"socket",
|
||||||
|
"Socket",
|
||||||
|
"Socket to use for UDP reception. (None == allocate)",
|
||||||
|
gio::Socket::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
));
|
||||||
|
properties.push(glib::ParamSpec::object(
|
||||||
|
"used-socket",
|
||||||
|
"Used Socket",
|
||||||
|
"Socket currently in use for UDP reception. (None = no socket)",
|
||||||
|
gio::Socket::static_type(),
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
properties
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("address", ..) => {
|
"address" => {
|
||||||
settings.address = value.get().expect("type checked upstream");
|
settings.address = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("port", ..) => {
|
"port" => {
|
||||||
settings.port = value.get_some().expect("type checked upstream");
|
settings.port = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("reuse", ..) => {
|
"reuse" => {
|
||||||
settings.reuse = value.get_some().expect("type checked upstream");
|
settings.reuse = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("caps", ..) => {
|
"caps" => {
|
||||||
settings.caps = value.get().expect("type checked upstream");
|
settings.caps = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("mtu", ..) => {
|
"mtu" => {
|
||||||
settings.mtu = value.get_some().expect("type checked upstream");
|
settings.mtu = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("socket", ..) => {
|
"socket" => {
|
||||||
settings.socket = value
|
settings.socket = value
|
||||||
.get::<gio::Socket>()
|
.get::<gio::Socket>()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.map(|socket| GioSocketWrapper::new(&socket));
|
.map(|socket| GioSocketWrapper::new(&socket));
|
||||||
}
|
}
|
||||||
subclass::Property("used-socket", ..) => {
|
"used-socket" => {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
subclass::Property("context", ..) => {
|
"context" => {
|
||||||
settings.context = value
|
settings.context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
.unwrap_or_else(|| "".into());
|
.unwrap_or_else(|| "".into());
|
||||||
}
|
}
|
||||||
subclass::Property("context-wait", ..) => {
|
"context-wait" => {
|
||||||
settings.context_wait = value.get_some().expect("type checked upstream");
|
settings.context_wait = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("retrieve-sender-address", ..) => {
|
"retrieve-sender-address" => {
|
||||||
settings.retrieve_sender_address = value.get_some().expect("type checked upstream");
|
settings.retrieve_sender_address = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
|
||||||
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("address", ..) => settings.address.to_value(),
|
"address" => settings.address.to_value(),
|
||||||
subclass::Property("port", ..) => settings.port.to_value(),
|
"port" => settings.port.to_value(),
|
||||||
subclass::Property("reuse", ..) => settings.reuse.to_value(),
|
"reuse" => settings.reuse.to_value(),
|
||||||
subclass::Property("caps", ..) => settings.caps.to_value(),
|
"caps" => settings.caps.to_value(),
|
||||||
subclass::Property("mtu", ..) => settings.mtu.to_value(),
|
"mtu" => settings.mtu.to_value(),
|
||||||
subclass::Property("socket", ..) => settings
|
"socket" => settings
|
||||||
.socket
|
.socket
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(GioSocketWrapper::as_socket)
|
.map(GioSocketWrapper::as_socket)
|
||||||
.to_value(),
|
.to_value(),
|
||||||
subclass::Property("used-socket", ..) => settings
|
"used-socket" => settings
|
||||||
.used_socket
|
.used_socket
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(GioSocketWrapper::as_socket)
|
.map(GioSocketWrapper::as_socket)
|
||||||
.to_value(),
|
.to_value(),
|
||||||
subclass::Property("context", ..) => settings.context.to_value(),
|
"context" => settings.context.to_value(),
|
||||||
subclass::Property("context-wait", ..) => settings.context_wait.to_value(),
|
"context-wait" => settings.context_wait.to_value(),
|
||||||
subclass::Property("retrieve-sender-address", ..) => {
|
"retrieve-sender-address" => settings.retrieve_sender_address.to_value(),
|
||||||
settings.retrieve_sender_address.to_value()
|
|
||||||
}
|
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -931,6 +887,36 @@ impl ObjectImpl for UdpSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for UdpSrc {
|
impl ElementImpl for UdpSrc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing UDP source",
|
||||||
|
"Source/Network",
|
||||||
|
"Receives data over the network via UDP",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -69,17 +69,6 @@ struct ItemSender {
|
||||||
mod imp_src {
|
mod imp_src {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
static SRC_PROPERTIES: [glib::subclass::Property; 1] =
|
|
||||||
[glib::subclass::Property("context", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Context",
|
|
||||||
"Context name to share threads with",
|
|
||||||
Some(DEFAULT_CONTEXT),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
context: String,
|
context: String,
|
||||||
|
@ -316,32 +305,12 @@ mod imp_src {
|
||||||
const NAME: &'static str = "TsElementSrcTest";
|
const NAME: &'static str = "TsElementSrcTest";
|
||||||
type Type = super::ElementSrcTest;
|
type Type = super::ElementSrcTest;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = glib::subclass::simple::ClassStruct<Self>;
|
type Class = glib::subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing Test Src Element",
|
|
||||||
"Generic",
|
|
||||||
"Src Element for Pad Src Test",
|
|
||||||
"François Laignel <fengalin@free.fr>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&SRC_PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
ElementSrcTest {
|
ElementSrcTest {
|
||||||
src_pad: PadSrc::new(
|
src_pad: PadSrc::new(
|
||||||
|
@ -356,11 +325,29 @@ mod imp_src {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ElementSrcTest {
|
impl ObjectImpl for ElementSrcTest {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &SRC_PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::string(
|
||||||
|
"context",
|
||||||
|
"Context",
|
||||||
|
"Context name to share threads with",
|
||||||
|
Some(DEFAULT_CONTEXT),
|
||||||
|
glib::ParamFlags::WRITABLE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
glib::subclass::Property("context", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"context" => {
|
||||||
let context = value
|
let context = value
|
||||||
.get()
|
.get()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
|
@ -380,6 +367,36 @@ mod imp_src {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for ElementSrcTest {
|
impl ElementImpl for ElementSrcTest {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing Test Src Element",
|
||||||
|
"Generic",
|
||||||
|
"Src Element for Pad Src Test",
|
||||||
|
"François Laignel <fengalin@free.fr>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
@ -452,17 +469,6 @@ unsafe impl Sync for ElementSrcTest {}
|
||||||
mod imp_sink {
|
mod imp_sink {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
static SINK_PROPERTIES: [glib::subclass::Property; 1] =
|
|
||||||
[glib::subclass::Property("sender", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Sender",
|
|
||||||
"Channel sender to forward the incoming items to",
|
|
||||||
ItemSender::get_type(),
|
|
||||||
glib::ParamFlags::WRITABLE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct PadSinkTestHandler;
|
struct PadSinkTestHandler;
|
||||||
|
|
||||||
|
@ -634,32 +640,12 @@ mod imp_sink {
|
||||||
const NAME: &'static str = "TsElementSinkTest";
|
const NAME: &'static str = "TsElementSinkTest";
|
||||||
type Type = super::ElementSinkTest;
|
type Type = super::ElementSinkTest;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = glib::subclass::simple::ClassStruct<Self>;
|
type Class = glib::subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
glib::object_subclass!();
|
glib::object_subclass!();
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Thread-sharing Test Sink Element",
|
|
||||||
"Generic",
|
|
||||||
"Sink Element for Pad Test",
|
|
||||||
"François Laignel <fengalin@free.fr>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&SINK_PROPERTIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
ElementSinkTest {
|
ElementSinkTest {
|
||||||
sink_pad: PadSink::new(
|
sink_pad: PadSink::new(
|
||||||
|
@ -673,11 +659,29 @@ mod imp_sink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ElementSinkTest {
|
impl ObjectImpl for ElementSinkTest {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &SINK_PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::boxed(
|
||||||
|
"sender",
|
||||||
|
"Sender",
|
||||||
|
"Channel sender to forward the incoming items to",
|
||||||
|
ItemSender::get_type(),
|
||||||
|
glib::ParamFlags::WRITABLE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
glib::subclass::Property("sender", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"sender" => {
|
||||||
let ItemSender { sender } = value
|
let ItemSender { sender } = value
|
||||||
.get::<&ItemSender>()
|
.get::<&ItemSender>()
|
||||||
.expect("type checked upstream")
|
.expect("type checked upstream")
|
||||||
|
@ -697,6 +701,36 @@ mod imp_sink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for ElementSinkTest {
|
impl ElementImpl for ElementSinkTest {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Thread-sharing Test Sink Element",
|
||||||
|
"Generic",
|
||||||
|
"Sink Element for Pad Test",
|
||||||
|
"François Laignel <fengalin@free.fr>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -69,110 +69,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 11] = [
|
|
||||||
subclass::Property("location", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Location",
|
|
||||||
"URL to read from",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("user-agent", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"User-Agent",
|
|
||||||
"Value of the User-Agent HTTP request header field",
|
|
||||||
DEFAULT_USER_AGENT.into(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("is-live", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Is Live",
|
|
||||||
"Act like a live source",
|
|
||||||
DEFAULT_IS_LIVE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("user-id", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"User-id",
|
|
||||||
"HTTP location URI user id for authentication",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("user-pw", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"User-pw",
|
|
||||||
"HTTP location URI user password for authentication",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("timeout", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Timeout",
|
|
||||||
"Value in seconds to timeout a blocking I/O (0 = No timeout).",
|
|
||||||
0,
|
|
||||||
3600,
|
|
||||||
DEFAULT_TIMEOUT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("compress", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Compress",
|
|
||||||
"Allow compressed content encodings",
|
|
||||||
DEFAULT_COMPRESS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("extra-headers", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Extra Headers",
|
|
||||||
"Extra headers to append to the HTTP request",
|
|
||||||
gst::Structure::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("cookies", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Cookies",
|
|
||||||
"HTTP request cookies",
|
|
||||||
Vec::<String>::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("iradio-mode", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"I-Radio Mode",
|
|
||||||
"Enable internet radio mode (ask server to send shoutcast/icecast metadata interleaved with the actual stream data",
|
|
||||||
DEFAULT_IRADIO_MODE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("keep-alive", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Keep Alive",
|
|
||||||
"Use HTTP persistent connections",
|
|
||||||
DEFAULT_KEEP_ALIVE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
const REQWEST_CLIENT_CONTEXT: &str = "gst.reqwest.client";
|
const REQWEST_CLIENT_CONTEXT: &str = "gst.reqwest.client";
|
||||||
|
|
||||||
#[derive(Clone, Debug, glib::GBoxed)]
|
#[derive(Clone, Debug, glib::GBoxed)]
|
||||||
|
@ -681,10 +577,103 @@ impl ReqwestHttpSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ReqwestHttpSrc {
|
impl ObjectImpl for ReqwestHttpSrc {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
match *prop {
|
vec![
|
||||||
subclass::Property("location", ..) => {
|
glib::ParamSpec::string(
|
||||||
|
"location",
|
||||||
|
"Location",
|
||||||
|
"URL to read from",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"user-agent",
|
||||||
|
"User-Agent",
|
||||||
|
"Value of the User-Agent HTTP request header field",
|
||||||
|
DEFAULT_USER_AGENT.into(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"is-live",
|
||||||
|
"Is Live",
|
||||||
|
"Act like a live source",
|
||||||
|
DEFAULT_IS_LIVE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"user-id",
|
||||||
|
"User-id",
|
||||||
|
"HTTP location URI user id for authentication",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"user-pw",
|
||||||
|
"User-pw",
|
||||||
|
"HTTP location URI user password for authentication",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"timeout",
|
||||||
|
"Timeout",
|
||||||
|
"Value in seconds to timeout a blocking I/O (0 = No timeout).",
|
||||||
|
0,
|
||||||
|
3600,
|
||||||
|
DEFAULT_TIMEOUT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"compress",
|
||||||
|
"Compress",
|
||||||
|
"Allow compressed content encodings",
|
||||||
|
DEFAULT_COMPRESS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"extra-headers",
|
||||||
|
"Extra Headers",
|
||||||
|
"Extra headers to append to the HTTP request",
|
||||||
|
gst::Structure::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"cookies",
|
||||||
|
"Cookies",
|
||||||
|
"HTTP request cookies",
|
||||||
|
Vec::<String>::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"iradio-mode",
|
||||||
|
"I-Radio Mode",
|
||||||
|
"Enable internet radio mode (ask server to send shoutcast/icecast metadata interleaved with the actual stream data",
|
||||||
|
DEFAULT_IRADIO_MODE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"keep-alive",
|
||||||
|
"Keep Alive",
|
||||||
|
"Use HTTP persistent connections",
|
||||||
|
DEFAULT_KEEP_ALIVE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"location" => {
|
||||||
let location = value.get::<&str>().expect("type checked upstream");
|
let location = value.get::<&str>().expect("type checked upstream");
|
||||||
if let Err(err) = self.set_location(obj, location) {
|
if let Err(err) = self.set_location(obj, location) {
|
||||||
gst_error!(
|
gst_error!(
|
||||||
|
@ -695,7 +684,7 @@ impl ObjectImpl for ReqwestHttpSrc {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subclass::Property("user-agent", ..) => {
|
"user-agent" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let user_agent = value
|
let user_agent = value
|
||||||
.get()
|
.get()
|
||||||
|
@ -703,46 +692,46 @@ impl ObjectImpl for ReqwestHttpSrc {
|
||||||
.unwrap_or_else(|| DEFAULT_USER_AGENT.into());
|
.unwrap_or_else(|| DEFAULT_USER_AGENT.into());
|
||||||
settings.user_agent = user_agent;
|
settings.user_agent = user_agent;
|
||||||
}
|
}
|
||||||
subclass::Property("is-live", ..) => {
|
"is-live" => {
|
||||||
let is_live = value.get_some().expect("type checked upstream");
|
let is_live = value.get_some().expect("type checked upstream");
|
||||||
obj.set_live(is_live);
|
obj.set_live(is_live);
|
||||||
}
|
}
|
||||||
subclass::Property("user-id", ..) => {
|
"user-id" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let user_id = value.get().expect("type checked upstream");
|
let user_id = value.get().expect("type checked upstream");
|
||||||
settings.user_id = user_id;
|
settings.user_id = user_id;
|
||||||
}
|
}
|
||||||
subclass::Property("user-pw", ..) => {
|
"user-pw" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let user_pw = value.get().expect("type checked upstream");
|
let user_pw = value.get().expect("type checked upstream");
|
||||||
settings.user_pw = user_pw;
|
settings.user_pw = user_pw;
|
||||||
}
|
}
|
||||||
subclass::Property("timeout", ..) => {
|
"timeout" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let timeout = value.get_some().expect("type checked upstream");
|
let timeout = value.get_some().expect("type checked upstream");
|
||||||
settings.timeout = timeout;
|
settings.timeout = timeout;
|
||||||
}
|
}
|
||||||
subclass::Property("compress", ..) => {
|
"compress" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let compress = value.get_some().expect("type checked upstream");
|
let compress = value.get_some().expect("type checked upstream");
|
||||||
settings.compress = compress;
|
settings.compress = compress;
|
||||||
}
|
}
|
||||||
subclass::Property("extra-headers", ..) => {
|
"extra-headers" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let extra_headers = value.get().expect("type checked upstream");
|
let extra_headers = value.get().expect("type checked upstream");
|
||||||
settings.extra_headers = extra_headers;
|
settings.extra_headers = extra_headers;
|
||||||
}
|
}
|
||||||
subclass::Property("cookies", ..) => {
|
"cookies" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let cookies = value.get().expect("type checked upstream");
|
let cookies = value.get().expect("type checked upstream");
|
||||||
settings.cookies = cookies.unwrap_or_else(Vec::new);
|
settings.cookies = cookies.unwrap_or_else(Vec::new);
|
||||||
}
|
}
|
||||||
subclass::Property("iradio-mode", ..) => {
|
"iradio-mode" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let iradio_mode = value.get_some().expect("type checked upstream");
|
let iradio_mode = value.get_some().expect("type checked upstream");
|
||||||
settings.iradio_mode = iradio_mode;
|
settings.iradio_mode = iradio_mode;
|
||||||
}
|
}
|
||||||
subclass::Property("keep-alive", ..) => {
|
"keep-alive" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let keep_alive = value.get_some().expect("type checked upstream");
|
let keep_alive = value.get_some().expect("type checked upstream");
|
||||||
settings.keep_alive = keep_alive;
|
settings.keep_alive = keep_alive;
|
||||||
|
@ -751,49 +740,48 @@ impl ObjectImpl for ReqwestHttpSrc {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
match *prop {
|
"location" => {
|
||||||
subclass::Property("location", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
let location = settings.location.as_ref().map(Url::to_string);
|
let location = settings.location.as_ref().map(Url::to_string);
|
||||||
|
|
||||||
location.to_value()
|
location.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("user-agent", ..) => {
|
"user-agent" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.user_agent.to_value()
|
settings.user_agent.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("is-live", ..) => obj.is_live().to_value(),
|
"is-live" => obj.is_live().to_value(),
|
||||||
subclass::Property("user-id", ..) => {
|
"user-id" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.user_id.to_value()
|
settings.user_id.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("user-pw", ..) => {
|
"user-pw" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.user_pw.to_value()
|
settings.user_pw.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("timeout", ..) => {
|
"timeout" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.timeout.to_value()
|
settings.timeout.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("compress", ..) => {
|
"compress" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.compress.to_value()
|
settings.compress.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("extra-headers", ..) => {
|
"extra-headers" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.extra_headers.to_value()
|
settings.extra_headers.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("cookies", ..) => {
|
"cookies" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.cookies.to_value()
|
settings.cookies.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("iradio-mode", ..) => {
|
"iradio-mode" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.iradio_mode.to_value()
|
settings.iradio_mode.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("keep-alive", ..) => {
|
"keep-alive" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.keep_alive.to_value()
|
settings.keep_alive.to_value()
|
||||||
}
|
}
|
||||||
|
@ -809,6 +797,36 @@ impl ObjectImpl for ReqwestHttpSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for ReqwestHttpSrc {
|
impl ElementImpl for ReqwestHttpSrc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"HTTP Source",
|
||||||
|
"Source/Network/HTTP",
|
||||||
|
"Read stream from an HTTP/HTTPS location",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn set_context(&self, element: &Self::Type, context: &gst::Context) {
|
fn set_context(&self, element: &Self::Type, context: &gst::Context) {
|
||||||
if context.get_context_type() == REQWEST_CLIENT_CONTEXT {
|
if context.get_context_type() == REQWEST_CLIENT_CONTEXT {
|
||||||
let mut external_client = self.external_client.lock().unwrap();
|
let mut external_client = self.external_client.lock().unwrap();
|
||||||
|
@ -1077,6 +1095,12 @@ impl PushSrcImpl for ReqwestHttpSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl URIHandlerImpl for ReqwestHttpSrc {
|
impl URIHandlerImpl for ReqwestHttpSrc {
|
||||||
|
const URI_TYPE: gst::URIType = gst::URIType::Src;
|
||||||
|
|
||||||
|
fn get_protocols() -> &'static [&'static str] {
|
||||||
|
&["http", "https"]
|
||||||
|
}
|
||||||
|
|
||||||
fn get_uri(&self, _element: &Self::Type) -> Option<String> {
|
fn get_uri(&self, _element: &Self::Type) -> Option<String> {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
@ -1086,20 +1110,13 @@ impl URIHandlerImpl for ReqwestHttpSrc {
|
||||||
fn set_uri(&self, element: &Self::Type, uri: &str) -> Result<(), glib::Error> {
|
fn set_uri(&self, element: &Self::Type, uri: &str) -> Result<(), glib::Error> {
|
||||||
self.set_location(&element, Some(uri))
|
self.set_location(&element, Some(uri))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uri_type() -> gst::URIType {
|
|
||||||
gst::URIType::Src
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_protocols() -> Vec<String> {
|
|
||||||
vec!["http".to_string(), "https".to_string()]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectSubclass for ReqwestHttpSrc {
|
impl ObjectSubclass for ReqwestHttpSrc {
|
||||||
const NAME: &'static str = "ReqwestHttpSrc";
|
const NAME: &'static str = "ReqwestHttpSrc";
|
||||||
type Type = super::ReqwestHttpSrc;
|
type Type = super::ReqwestHttpSrc;
|
||||||
type ParentType = gst_base::PushSrc;
|
type ParentType = gst_base::PushSrc;
|
||||||
|
type Interfaces = (gst::URIHandler,);
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -1114,29 +1131,4 @@ impl ObjectSubclass for ReqwestHttpSrc {
|
||||||
canceller: Mutex::new(None),
|
canceller: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_init(type_: &mut subclass::InitializingType<Self>) {
|
|
||||||
type_.add_interface::<gst::URIHandler>();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"HTTP Source",
|
|
||||||
"Source/Network/HTTP",
|
|
||||||
"Read stream from an HTTP/HTTPS location",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,40 +116,6 @@ const DEFAULT_LATENCY_MS: u32 = 8000;
|
||||||
const DEFAULT_USE_PARTIAL_RESULTS: bool = true;
|
const DEFAULT_USE_PARTIAL_RESULTS: bool = true;
|
||||||
const GRANULARITY_MS: u32 = 100;
|
const GRANULARITY_MS: u32 = 100;
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 3] = [
|
|
||||||
subclass::Property("language-code", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Language Code",
|
|
||||||
"The Language of the Stream, see \
|
|
||||||
<https://docs.aws.amazon.com/transcribe/latest/dg/how-streaming-transcription.html> \
|
|
||||||
for an up to date list of allowed languages",
|
|
||||||
Some("en-US"),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("use-partial-results", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Latency",
|
|
||||||
"Whether partial results from AWS should be used",
|
|
||||||
DEFAULT_USE_PARTIAL_RESULTS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("latency", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Latency",
|
|
||||||
"Amount of milliseconds to allow AWS transcribe",
|
|
||||||
2 * GRANULARITY_MS,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_LATENCY_MS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
latency_ms: u32,
|
latency_ms: u32,
|
||||||
|
@ -1010,6 +976,7 @@ impl ObjectSubclass for Transcriber {
|
||||||
const NAME: &'static str = "RsAwsTranscriber";
|
const NAME: &'static str = "RsAwsTranscriber";
|
||||||
type Type = super::Transcriber;
|
type Type = super::Transcriber;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -1063,45 +1030,43 @@ impl ObjectSubclass for Transcriber {
|
||||||
ws_sink: AtomicRefCell::new(None),
|
ws_sink: AtomicRefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Transcriber",
|
|
||||||
"Audio/Text/Filter",
|
|
||||||
"Speech to Text filter, using AWS transcribe",
|
|
||||||
"Jordan Petridis <jordan@centricular.com>, Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::builder("text/x-raw")
|
|
||||||
.field("format", &"utf8")
|
|
||||||
.build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::builder("audio/x-raw")
|
|
||||||
.field("format", &"S16LE")
|
|
||||||
.field("rate", &gst::IntRange::<i32>::new(8000, 48000))
|
|
||||||
.field("channels", &1)
|
|
||||||
.build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Transcriber {
|
impl ObjectImpl for Transcriber {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"language-code",
|
||||||
|
"Language Code",
|
||||||
|
"The Language of the Stream, see \
|
||||||
|
<https://docs.aws.amazon.com/transcribe/latest/dg/how-streaming-transcription.html> \
|
||||||
|
for an up to date list of allowed languages",
|
||||||
|
Some("en-US"),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"use-partial-results",
|
||||||
|
"Latency",
|
||||||
|
"Whether partial results from AWS should be used",
|
||||||
|
DEFAULT_USE_PARTIAL_RESULTS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"latency",
|
||||||
|
"Latency",
|
||||||
|
"Amount of milliseconds to allow AWS transcribe",
|
||||||
|
2 * GRANULARITY_MS,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_LATENCY_MS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -1110,19 +1075,23 @@ impl ObjectImpl for Transcriber {
|
||||||
obj.set_element_flags(gst::ElementFlags::PROVIDE_CLOCK | gst::ElementFlags::REQUIRE_CLOCK);
|
obj.set_element_flags(gst::ElementFlags::PROVIDE_CLOCK | gst::ElementFlags::REQUIRE_CLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("language_code", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"language_code" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.language_code = value.get().expect("type checked upstream");
|
settings.language_code = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("latency", ..) => {
|
"latency" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.latency_ms = value.get_some().expect("type checked upstream");
|
settings.latency_ms = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("use-partial-results", ..) => {
|
"use-partial-results" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.use_partial_results = value.get_some().expect("type checked upstream");
|
settings.use_partial_results = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -1130,19 +1099,17 @@ impl ObjectImpl for Transcriber {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"language-code" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("language-code", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.language_code.to_value()
|
settings.language_code.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("latency", ..) => {
|
"latency" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.latency_ms.to_value()
|
settings.latency_ms.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("use-partial-results", ..) => {
|
"use-partial-results" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.use_partial_results.to_value()
|
settings.use_partial_results.to_value()
|
||||||
}
|
}
|
||||||
|
@ -1152,6 +1119,51 @@ impl ObjectImpl for Transcriber {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Transcriber {
|
impl ElementImpl for Transcriber {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Transcriber",
|
||||||
|
"Audio/Text/Filter",
|
||||||
|
"Speech to Text filter, using AWS transcribe",
|
||||||
|
"Jordan Petridis <jordan@centricular.com>, Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let src_caps = gst::Caps::builder("text/x-raw")
|
||||||
|
.field("format", &"utf8")
|
||||||
|
.build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_caps = gst::Caps::builder("audio/x-raw")
|
||||||
|
.field("format", &"S16LE")
|
||||||
|
.field("rate", &gst::IntRange::<i32>::new(8000, 48000))
|
||||||
|
.field("channels", &1)
|
||||||
|
.build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -116,47 +116,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 4] = [
|
|
||||||
subclass::Property("bucket", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"S3 Bucket",
|
|
||||||
"The bucket of the file to write",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("key", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"S3 Key",
|
|
||||||
"The key of the file to write",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("region", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"AWS Region",
|
|
||||||
"An AWS region (e.g. eu-west-2).",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("part-size", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Part size",
|
|
||||||
"A size (in bytes) of an individual part used for multipart upload.",
|
|
||||||
5 * 1024 * 1024, // 5 MB
|
|
||||||
5 * 1024 * 1024 * 1024, // 5 GB
|
|
||||||
DEFAULT_BUFFER_SIZE,
|
|
||||||
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
impl S3Sink {
|
impl S3Sink {
|
||||||
fn flush_current_buffer(
|
fn flush_current_buffer(
|
||||||
&self,
|
&self,
|
||||||
|
@ -386,6 +345,7 @@ impl ObjectSubclass for S3Sink {
|
||||||
const NAME: &'static str = "RusotoS3Sink";
|
const NAME: &'static str = "RusotoS3Sink";
|
||||||
type Type = super::S3Sink;
|
type Type = super::S3Sink;
|
||||||
type ParentType = gst_base::BaseSink;
|
type ParentType = gst_base::BaseSink;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -398,42 +358,65 @@ impl ObjectSubclass for S3Sink {
|
||||||
canceller: Mutex::new(None),
|
canceller: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Amazon S3 sink",
|
|
||||||
"Source/Network",
|
|
||||||
"Writes an object to Amazon S3",
|
|
||||||
"Marcin Kolny <mkolny@amazon.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for S3Sink {
|
impl ObjectImpl for S3Sink {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id as usize];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"bucket",
|
||||||
|
"S3 Bucket",
|
||||||
|
"The bucket of the file to write",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"key",
|
||||||
|
"S3 Key",
|
||||||
|
"The key of the file to write",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"region",
|
||||||
|
"AWS Region",
|
||||||
|
"An AWS region (e.g. eu-west-2).",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"part-size",
|
||||||
|
"Part size",
|
||||||
|
"A size (in bytes) of an individual part used for multipart upload.",
|
||||||
|
5 * 1024 * 1024, // 5 MB
|
||||||
|
5 * 1024 * 1024 * 1024, // 5 GB
|
||||||
|
DEFAULT_BUFFER_SIZE,
|
||||||
|
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("bucket", ..) => {
|
"bucket" => {
|
||||||
settings.bucket = value.get::<String>().expect("type checked upstream");
|
settings.bucket = value.get::<String>().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("key", ..) => {
|
"key" => {
|
||||||
settings.key = value.get::<String>().expect("type checked upstream");
|
settings.key = value.get::<String>().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("region", ..) => {
|
"region" => {
|
||||||
settings.region = Region::from_str(
|
settings.region = Region::from_str(
|
||||||
&value
|
&value
|
||||||
.get::<String>()
|
.get::<String>()
|
||||||
|
@ -442,28 +425,57 @@ impl ObjectImpl for S3Sink {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
subclass::Property("part-size", ..) => {
|
"part-size" => {
|
||||||
settings.buffer_size = value.get_some::<u64>().expect("type checked upstream");
|
settings.buffer_size = value.get_some::<u64>().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id as usize];
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
match *prop {
|
match pspec.get_name() {
|
||||||
subclass::Property("key", ..) => settings.key.to_value(),
|
"key" => settings.key.to_value(),
|
||||||
subclass::Property("bucket", ..) => settings.bucket.to_value(),
|
"bucket" => settings.bucket.to_value(),
|
||||||
subclass::Property("region", ..) => settings.region.name().to_value(),
|
"region" => settings.region.name().to_value(),
|
||||||
subclass::Property("part-size", ..) => settings.buffer_size.to_value(),
|
"part-size" => settings.buffer_size.to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for S3Sink {}
|
impl ElementImpl for S3Sink {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Amazon S3 sink",
|
||||||
|
"Source/Network",
|
||||||
|
"Writes an object to Amazon S3",
|
||||||
|
"Marcin Kolny <mkolny@amazon.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseSinkImpl for S3Sink {
|
impl BaseSinkImpl for S3Sink {
|
||||||
fn start(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
|
|
@ -51,16 +51,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("uri", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"URI",
|
|
||||||
"The S3 object URI",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE, /* + GST_PARAM_MUTABLE_READY) */
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
impl S3Src {
|
impl S3Src {
|
||||||
fn cancel(&self) {
|
fn cancel(&self) {
|
||||||
let mut canceller = self.canceller.lock().unwrap();
|
let mut canceller = self.canceller.lock().unwrap();
|
||||||
|
@ -210,6 +200,7 @@ impl ObjectSubclass for S3Src {
|
||||||
const NAME: &'static str = "RusotoS3Src";
|
const NAME: &'static str = "RusotoS3Src";
|
||||||
type Type = super::S3Src;
|
type Type = super::S3Src;
|
||||||
type ParentType = gst_base::BaseSrc;
|
type ParentType = gst_base::BaseSrc;
|
||||||
|
type Interfaces = (gst::URIHandler,);
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -222,50 +213,41 @@ impl ObjectSubclass for S3Src {
|
||||||
canceller: Mutex::new(None),
|
canceller: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_init(typ: &mut subclass::InitializingType<Self>) {
|
|
||||||
typ.add_interface::<gst::URIHandler>();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Amazon S3 source",
|
|
||||||
"Source/Network",
|
|
||||||
"Reads an object from Amazon S3",
|
|
||||||
"Arun Raghavan <arun@arunraghavan.net>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for S3Src {
|
impl ObjectImpl for S3Src {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id as usize];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::string(
|
||||||
|
"uri",
|
||||||
|
"URI",
|
||||||
|
"The S3 object URI",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("uri", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"uri" => {
|
||||||
let _ = self.set_uri(obj, value.get().expect("type checked upstream"));
|
let _ = self.set_uri(obj, value.get().expect("type checked upstream"));
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id as usize];
|
match pspec.get_name() {
|
||||||
|
"uri" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("uri", ..) => {
|
|
||||||
let url = match *self.url.lock().unwrap() {
|
let url = match *self.url.lock().unwrap() {
|
||||||
Some(ref url) => url.to_string(),
|
Some(ref url) => url.to_string(),
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
|
@ -287,10 +269,44 @@ impl ObjectImpl for S3Src {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for S3Src {
|
impl ElementImpl for S3Src {
|
||||||
// No overrides
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Amazon S3 source",
|
||||||
|
"Source/Network",
|
||||||
|
"Reads an object from Amazon S3",
|
||||||
|
"Arun Raghavan <arun@arunraghavan.net>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl URIHandlerImpl for S3Src {
|
impl URIHandlerImpl for S3Src {
|
||||||
|
const URI_TYPE: gst::URIType = gst::URIType::Src;
|
||||||
|
|
||||||
|
fn get_protocols() -> &'static [&'static str] {
|
||||||
|
&["s3"]
|
||||||
|
}
|
||||||
|
|
||||||
fn get_uri(&self, _: &Self::Type) -> Option<String> {
|
fn get_uri(&self, _: &Self::Type) -> Option<String> {
|
||||||
self.url.lock().unwrap().as_ref().map(|s| s.to_string())
|
self.url.lock().unwrap().as_ref().map(|s| s.to_string())
|
||||||
}
|
}
|
||||||
|
@ -298,14 +314,6 @@ impl URIHandlerImpl for S3Src {
|
||||||
fn set_uri(&self, element: &Self::Type, uri: &str) -> Result<(), glib::Error> {
|
fn set_uri(&self, element: &Self::Type, uri: &str) -> Result<(), glib::Error> {
|
||||||
self.set_uri(element, Some(uri))
|
self.set_uri(element, Some(uri))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uri_type() -> gst::URIType {
|
|
||||||
gst::URIType::Src
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_protocols() -> Vec<String> {
|
|
||||||
vec!["s3".to_string()]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseSrcImpl for S3Src {
|
impl BaseSrcImpl for S3Src {
|
||||||
|
|
|
@ -205,6 +205,7 @@ impl ObjectSubclass for JsonGstEnc {
|
||||||
const NAME: &'static str = "RsJsonGstEnc";
|
const NAME: &'static str = "RsJsonGstEnc";
|
||||||
type Type = super::JsonGstEnc;
|
type Type = super::JsonGstEnc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -238,36 +239,6 @@ impl ObjectSubclass for JsonGstEnc {
|
||||||
state: Mutex::new(State::default()),
|
state: Mutex::new(State::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"GStreamer buffers to JSON",
|
|
||||||
"Encoder/JSON",
|
|
||||||
"Wraps buffers containing any valid top-level JSON structures \
|
|
||||||
into higher level JSON objects, and outputs those as ndjson",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-json").build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-json").build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for JsonGstEnc {
|
impl ObjectImpl for JsonGstEnc {
|
||||||
|
@ -280,6 +251,46 @@ impl ObjectImpl for JsonGstEnc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for JsonGstEnc {
|
impl ElementImpl for JsonGstEnc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"GStreamer buffers to JSON",
|
||||||
|
"Encoder/JSON",
|
||||||
|
"Wraps buffers containing any valid top-level JSON structures \
|
||||||
|
into higher level JSON objects, and outputs those as ndjson",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::builder("application/x-json").build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("application/x-json").build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -867,6 +867,7 @@ impl ObjectSubclass for JsonGstParse {
|
||||||
const NAME: &'static str = "RsJsonGstParse";
|
const NAME: &'static str = "RsJsonGstParse";
|
||||||
type Type = super::JsonGstParse;
|
type Type = super::JsonGstParse;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -934,35 +935,6 @@ impl ObjectSubclass for JsonGstParse {
|
||||||
state: Mutex::new(State::default()),
|
state: Mutex::new(State::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"JSON GStreamer parser",
|
|
||||||
"Parser/JSON",
|
|
||||||
"Parses ndjson as output by jsongstenc",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-json").build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for JsonGstParse {
|
impl ObjectImpl for JsonGstParse {
|
||||||
|
@ -975,6 +947,45 @@ impl ObjectImpl for JsonGstParse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for JsonGstParse {
|
impl ElementImpl for JsonGstParse {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"JSON GStreamer parser",
|
||||||
|
"Parser/JSON",
|
||||||
|
"Parses ndjson as output by jsongstenc",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::builder("application/x-json").build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -45,52 +45,6 @@ const DEFAULT_COLUMNS: u32 = 32; /* CEA 608 max columns */
|
||||||
const DEFAULT_LINES: u32 = 0;
|
const DEFAULT_LINES: u32 = 0;
|
||||||
const DEFAULT_ACCUMULATE: i64 = -1;
|
const DEFAULT_ACCUMULATE: i64 = -1;
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 4] = [
|
|
||||||
subclass::Property("dictionary", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Dictionary",
|
|
||||||
"Path to a dictionary to load at runtime to perform hyphenation, see \
|
|
||||||
<https://docs.rs/crate/hyphenation/0.7.1> for more information",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("columns", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Columns",
|
|
||||||
"Maximum number of columns for any given line",
|
|
||||||
1,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_COLUMNS,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("lines", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Lines",
|
|
||||||
"Split input buffer into output buffers with max lines (0=do not split)",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_LINES,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("accumulate-time", |name| {
|
|
||||||
glib::ParamSpec::int64(
|
|
||||||
name,
|
|
||||||
"accumulate-time",
|
|
||||||
"Cut-off time for input text accumulation (-1=do not accumulate)",
|
|
||||||
-1,
|
|
||||||
std::i64::MAX,
|
|
||||||
DEFAULT_ACCUMULATE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
dictionary: Option<String>,
|
dictionary: Option<String>,
|
||||||
|
@ -409,6 +363,7 @@ impl ObjectSubclass for TextWrap {
|
||||||
const NAME: &'static str = "RsTextWrap";
|
const NAME: &'static str = "RsTextWrap";
|
||||||
type Type = super::TextWrap;
|
type Type = super::TextWrap;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -449,41 +404,53 @@ impl ObjectSubclass for TextWrap {
|
||||||
state,
|
state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Text Wrapper",
|
|
||||||
"Text/Filter",
|
|
||||||
"Breaks text into fixed-size lines, with optional hyphenationz",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("text/x-raw")
|
|
||||||
.field("format", &"utf8")
|
|
||||||
.build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for TextWrap {
|
impl ObjectImpl for TextWrap {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"dictionary",
|
||||||
|
"Dictionary",
|
||||||
|
"Path to a dictionary to load at runtime to perform hyphenation, see \
|
||||||
|
<https://docs.rs/crate/hyphenation/0.7.1> for more information",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"columns",
|
||||||
|
"Columns",
|
||||||
|
"Maximum number of columns for any given line",
|
||||||
|
1,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_COLUMNS,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"lines",
|
||||||
|
"Lines",
|
||||||
|
"Split input buffer into output buffers with max lines (0=do not split)",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_LINES,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int64(
|
||||||
|
"accumulate-time",
|
||||||
|
"accumulate-time",
|
||||||
|
"Cut-off time for input text accumulation (-1=do not accumulate)",
|
||||||
|
-1,
|
||||||
|
std::i64::MAX,
|
||||||
|
DEFAULT_ACCUMULATE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -491,27 +458,31 @@ impl ObjectImpl for TextWrap {
|
||||||
obj.add_pad(&self.srcpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("dictionary", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"dictionary" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
settings.dictionary = value.get().expect("type checked upstream");
|
settings.dictionary = value.get().expect("type checked upstream");
|
||||||
state.options = None;
|
state.options = None;
|
||||||
}
|
}
|
||||||
subclass::Property("columns", ..) => {
|
"columns" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
settings.columns = value.get_some().expect("type checked upstream");
|
settings.columns = value.get_some().expect("type checked upstream");
|
||||||
state.options = None;
|
state.options = None;
|
||||||
}
|
}
|
||||||
subclass::Property("lines", ..) => {
|
"lines" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.lines = value.get_some().expect("type checked upstream");
|
settings.lines = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("accumulate-time", ..) => {
|
"accumulate-time" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.accumulate_time = match value.get_some().expect("type checked upstream") {
|
settings.accumulate_time = match value.get_some().expect("type checked upstream") {
|
||||||
-1i64 => gst::CLOCK_TIME_NONE,
|
-1i64 => gst::CLOCK_TIME_NONE,
|
||||||
|
@ -522,23 +493,21 @@ impl ObjectImpl for TextWrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"dictionary" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("dictionary", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.dictionary.to_value()
|
settings.dictionary.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("columns", ..) => {
|
"columns" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.columns.to_value()
|
settings.columns.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("lines", ..) => {
|
"lines" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.lines.to_value()
|
settings.lines.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("accumulate-time", ..) => {
|
"accumulate-time" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
match settings.accumulate_time.0 {
|
match settings.accumulate_time.0 {
|
||||||
Some(time) => (time as i64).to_value(),
|
Some(time) => (time as i64).to_value(),
|
||||||
|
@ -551,6 +520,46 @@ impl ObjectImpl for TextWrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for TextWrap {
|
impl ElementImpl for TextWrap {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Text Wrapper",
|
||||||
|
"Text/Filter",
|
||||||
|
"Breaks text into fixed-size lines, with optional hyphenation",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::builder("text/x-raw")
|
||||||
|
.field("format", &"utf8")
|
||||||
|
.build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -118,6 +118,7 @@ impl ObjectSubclass for Identity {
|
||||||
const NAME: &'static str = "RsIdentity";
|
const NAME: &'static str = "RsIdentity";
|
||||||
type Type = super::Identity;
|
type Type = super::Identity;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -185,50 +186,6 @@ impl ObjectSubclass for Identity {
|
||||||
// into the debug logs
|
// into the debug logs
|
||||||
Self { srcpad, sinkpad }
|
Self { srcpad, sinkpad }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called exactly once when registering the type. Used for
|
|
||||||
// setting up metadata for all instances, e.g. the name and
|
|
||||||
// classification and the pad templates with their caps.
|
|
||||||
//
|
|
||||||
// Actual instances can create pads based on those pad templates
|
|
||||||
// with a subset of the caps given here.
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
// Set the element specific metadata. This information is what
|
|
||||||
// is visible from gst-inspect-1.0 and can also be programatically
|
|
||||||
// retrieved from the gst::Registry after initial registration
|
|
||||||
// without having to load the plugin in memory.
|
|
||||||
klass.set_metadata(
|
|
||||||
"Identity",
|
|
||||||
"Generic",
|
|
||||||
"Does nothing with the data",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create and add pad templates for our sink and source pad. These
|
|
||||||
// are later used for actually creating the pads and beforehand
|
|
||||||
// already provide information to GStreamer about all possible
|
|
||||||
// pads that could exist for this type.
|
|
||||||
|
|
||||||
// Our element can accept any possible caps on both pads
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of glib::Object virtual methods
|
// Implementation of glib::Object virtual methods
|
||||||
|
@ -247,6 +204,55 @@ impl ObjectImpl for Identity {
|
||||||
|
|
||||||
// Implementation of gst::Element virtual methods
|
// Implementation of gst::Element virtual methods
|
||||||
impl ElementImpl for Identity {
|
impl ElementImpl for Identity {
|
||||||
|
// Set the element specific metadata. This information is what
|
||||||
|
// is visible from gst-inspect-1.0 and can also be programatically
|
||||||
|
// retrieved from the gst::Registry after initial registration
|
||||||
|
// without having to load the plugin in memory.
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Identity",
|
||||||
|
"Generic",
|
||||||
|
"Does nothing with the data",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and add pad templates for our sink and source pad. These
|
||||||
|
// are later used for actually creating the pads and beforehand
|
||||||
|
// already provide information to GStreamer about all possible
|
||||||
|
// pads that could exist for this type.
|
||||||
|
//
|
||||||
|
// Actual instances can create pads based on those pad templates
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
// Our element can accept any possible caps on both pads
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
// Called whenever the state of the element should be changed. This allows for
|
// Called whenever the state of the element should be changed. This allows for
|
||||||
// starting up the element, allocating/deallocating resources or shutting down
|
// starting up the element, allocating/deallocating resources or shutting down
|
||||||
// the element again.
|
// the element again.
|
||||||
|
|
|
@ -41,18 +41,6 @@ pub struct ProgressBin {
|
||||||
output_type: Mutex<ProgressBinOutput>,
|
output_type: Mutex<ProgressBinOutput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata for the element's properties
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("output", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Output",
|
|
||||||
"Defines the output type of the progressbin",
|
|
||||||
ProgressBinOutput::static_type(),
|
|
||||||
DEFAULT_OUTPUT_TYPE as i32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
// This trait registers our type with the GObject object system and
|
// This trait registers our type with the GObject object system and
|
||||||
// provides the entry points for creating a new instance and setting
|
// provides the entry points for creating a new instance and setting
|
||||||
// up the class data
|
// up the class data
|
||||||
|
@ -60,6 +48,7 @@ impl ObjectSubclass for ProgressBin {
|
||||||
const NAME: &'static str = "RsProgressBin";
|
const NAME: &'static str = "RsProgressBin";
|
||||||
type Type = super::ProgressBin;
|
type Type = super::ProgressBin;
|
||||||
type ParentType = gst::Bin;
|
type ParentType = gst::Bin;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -92,64 +81,37 @@ impl ObjectSubclass for ProgressBin {
|
||||||
output_type: Mutex::new(ProgressBinOutput::Println),
|
output_type: Mutex::new(ProgressBinOutput::Println),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called exactly once when registering the type. Used for
|
|
||||||
// setting up metadata for all instances, e.g. the name and
|
|
||||||
// classification and the pad templates with their caps.
|
|
||||||
//
|
|
||||||
// Actual instances can create pads based on those pad templates
|
|
||||||
// with a subset of the caps given here.
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
// Set the element specific metadata. This information is what
|
|
||||||
// is visible from gst-inspect-1.0 and can also be programatically
|
|
||||||
// retrieved from the gst::Registry after initial registration
|
|
||||||
// without having to load the plugin in memory.
|
|
||||||
klass.set_metadata(
|
|
||||||
"ProgressBin",
|
|
||||||
"Generic",
|
|
||||||
"Prints progress information to stdout",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create and add pad templates for our sink and source pad. These
|
|
||||||
// are later used for actually creating the pads and beforehand
|
|
||||||
// already provide information to GStreamer about all possible
|
|
||||||
// pads that could exist for this type.
|
|
||||||
|
|
||||||
// Our element can accept any possible caps on both pads
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
// Install all our properties
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of glib::Object virtual methods
|
// Implementation of glib::Object virtual methods
|
||||||
impl ObjectImpl for ProgressBin {
|
impl ObjectImpl for ProgressBin {
|
||||||
|
// Metadata for the element's properties
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::enum_(
|
||||||
|
"output",
|
||||||
|
"Output",
|
||||||
|
"Defines the output type of the progressbin",
|
||||||
|
ProgressBinOutput::static_type(),
|
||||||
|
DEFAULT_OUTPUT_TYPE as i32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
// Called whenever a value of a property is changed. It can be called
|
// Called whenever a value of a property is changed. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("output", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"output" => {
|
||||||
let mut output_type = self.output_type.lock().unwrap();
|
let mut output_type = self.output_type.lock().unwrap();
|
||||||
let new_output_type = value
|
let new_output_type = value
|
||||||
.get_some::<ProgressBinOutput>()
|
.get_some::<ProgressBinOutput>()
|
||||||
|
@ -169,11 +131,9 @@ impl ObjectImpl for ProgressBin {
|
||||||
|
|
||||||
// Called whenever a value of a property is read. It can be called
|
// Called whenever a value of a property is read. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"output" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("output", ..) => {
|
|
||||||
let output_type = self.output_type.lock().unwrap();
|
let output_type = self.output_type.lock().unwrap();
|
||||||
output_type.to_value()
|
output_type.to_value()
|
||||||
}
|
}
|
||||||
|
@ -207,7 +167,56 @@ impl ObjectImpl for ProgressBin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of gst::Element virtual methods
|
// Implementation of gst::Element virtual methods
|
||||||
impl ElementImpl for ProgressBin {}
|
impl ElementImpl for ProgressBin {
|
||||||
|
// Set the element specific metadata. This information is what
|
||||||
|
// is visible from gst-inspect-1.0 and can also be programatically
|
||||||
|
// retrieved from the gst::Registry after initial registration
|
||||||
|
// without having to load the plugin in memory.
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"ProgressBin",
|
||||||
|
"Generic",
|
||||||
|
"Prints progress information to stdout",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
// Create and add pad templates for our sink and source pad. These
|
||||||
|
// are later used for actually creating the pads and beforehand
|
||||||
|
// already provide information to GStreamer about all possible
|
||||||
|
// pads that could exist for this type.
|
||||||
|
//
|
||||||
|
// Actual instances can create pads based on those pad templates
|
||||||
|
// with a subset of the caps given here.
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
// Our element can accept any possible caps on both pads
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation of gst::Bin virtual methods
|
// Implementation of gst::Bin virtual methods
|
||||||
impl BinImpl for ProgressBin {
|
impl BinImpl for ProgressBin {
|
||||||
|
|
|
@ -48,30 +48,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata for the properties
|
|
||||||
static PROPERTIES: [subclass::Property; 2] = [
|
|
||||||
subclass::Property("invert", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Invert",
|
|
||||||
"Invert grayscale output",
|
|
||||||
DEFAULT_INVERT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("shift", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Shift",
|
|
||||||
"Shift grayscale output (wrapping around)",
|
|
||||||
0,
|
|
||||||
255,
|
|
||||||
DEFAULT_SHIFT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Stream-specific state, i.e. video format configuration
|
// Stream-specific state, i.e. video format configuration
|
||||||
struct State {
|
struct State {
|
||||||
in_info: gst_video::VideoInfo,
|
in_info: gst_video::VideoInfo,
|
||||||
|
@ -118,6 +94,7 @@ impl ObjectSubclass for Rgb2Gray {
|
||||||
const NAME: &'static str = "RsRgb2Gray";
|
const NAME: &'static str = "RsRgb2Gray";
|
||||||
type Type = super::Rgb2Gray;
|
type Type = super::Rgb2Gray;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -132,122 +109,47 @@ impl ObjectSubclass for Rgb2Gray {
|
||||||
state: Mutex::new(None),
|
state: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called exactly once when registering the type. Used for
|
|
||||||
// setting up metadata for all instances, e.g. the name and
|
|
||||||
// classification and the pad templates with their caps.
|
|
||||||
//
|
|
||||||
// Actual instances can create pads based on those pad templates
|
|
||||||
// with a subset of the caps given here. In case of basetransform,
|
|
||||||
// a "src" and "sink" pad template are required here and the base class
|
|
||||||
// will automatically instantiate pads for them.
|
|
||||||
//
|
|
||||||
// Our element here can convert BGRx to BGRx or GRAY8, both being grayscale.
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
// Set the element specific metadata. This information is what
|
|
||||||
// is visible from gst-inspect-1.0 and can also be programatically
|
|
||||||
// retrieved from the gst::Registry after initial registration
|
|
||||||
// without having to load the plugin in memory.
|
|
||||||
klass.set_metadata(
|
|
||||||
"RGB-GRAY Converter",
|
|
||||||
"Filter/Effect/Converter/Video",
|
|
||||||
"Converts RGB to GRAY or grayscale RGB",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create and add pad templates for our sink and source pad. These
|
|
||||||
// are later used for actually creating the pads and beforehand
|
|
||||||
// already provide information to GStreamer about all possible
|
|
||||||
// pads that could exist for this type.
|
|
||||||
|
|
||||||
// On the src pad, we can produce BGRx and GRAY8 of any
|
|
||||||
// width/height and with any framerate
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[
|
|
||||||
&gst_video::VideoFormat::Bgrx.to_str(),
|
|
||||||
&gst_video::VideoFormat::Gray8.to_str(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(0, 1),
|
|
||||||
gst::Fraction::new(i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// The src pad template must be named "src" for basetransform
|
|
||||||
// and specific a pad that is always there
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
// On the sink pad, we can accept BGRx of any
|
|
||||||
// width/height and with any framerate
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &gst_video::VideoFormat::Bgrx.to_str()),
|
|
||||||
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(0, 1),
|
|
||||||
gst::Fraction::new(i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// The sink pad template must be named "sink" for basetransform
|
|
||||||
// and specific a pad that is always there
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
// Install all our properties
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
// Configure basetransform so that we are never running in-place,
|
|
||||||
// don't passthrough on same caps and also never call transform_ip
|
|
||||||
// in passthrough mode (which does not matter for us here).
|
|
||||||
//
|
|
||||||
// We could work in-place for BGRx->BGRx but don't do here for simplicity
|
|
||||||
// for now.
|
|
||||||
klass.configure(
|
|
||||||
gst_base::subclass::BaseTransformMode::NeverInPlace,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of glib::Object virtual methods
|
// Implementation of glib::Object virtual methods
|
||||||
impl ObjectImpl for Rgb2Gray {
|
impl ObjectImpl for Rgb2Gray {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
// Metadata for the properties
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"invert",
|
||||||
|
"Invert",
|
||||||
|
"Invert grayscale output",
|
||||||
|
DEFAULT_INVERT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"shift",
|
||||||
|
"Shift",
|
||||||
|
"Shift grayscale output (wrapping around)",
|
||||||
|
0,
|
||||||
|
255,
|
||||||
|
DEFAULT_SHIFT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
// Called whenever a value of a property is changed. It can be called
|
// Called whenever a value of a property is changed. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("invert", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"invert" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let invert = value.get_some().expect("type checked upstream");
|
let invert = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -259,7 +161,7 @@ impl ObjectImpl for Rgb2Gray {
|
||||||
);
|
);
|
||||||
settings.invert = invert;
|
settings.invert = invert;
|
||||||
}
|
}
|
||||||
subclass::Property("shift", ..) => {
|
"shift" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let shift = value.get_some().expect("type checked upstream");
|
let shift = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -277,15 +179,13 @@ impl ObjectImpl for Rgb2Gray {
|
||||||
|
|
||||||
// Called whenever a value of a property is read. It can be called
|
// Called whenever a value of a property is read. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"invert" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("invert", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.invert.to_value()
|
settings.invert.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("shift", ..) => {
|
"shift" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.shift.to_value()
|
settings.shift.to_value()
|
||||||
}
|
}
|
||||||
|
@ -295,10 +195,112 @@ impl ObjectImpl for Rgb2Gray {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of gst::Element virtual methods
|
// Implementation of gst::Element virtual methods
|
||||||
impl ElementImpl for Rgb2Gray {}
|
impl ElementImpl for Rgb2Gray {
|
||||||
|
// Set the element specific metadata. This information is what
|
||||||
|
// is visible from gst-inspect-1.0 and can also be programatically
|
||||||
|
// retrieved from the gst::Registry after initial registration
|
||||||
|
// without having to load the plugin in memory.
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"RGB-GRAY Converter",
|
||||||
|
"Filter/Effect/Converter/Video",
|
||||||
|
"Converts RGB to GRAY or grayscale RGB",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and add pad templates for our sink and source pad. These
|
||||||
|
// are later used for actually creating the pads and beforehand
|
||||||
|
// already provide information to GStreamer about all possible
|
||||||
|
// pads that could exist for this type.
|
||||||
|
//
|
||||||
|
// Our element here can convert BGRx to BGRx or GRAY8, both being grayscale.
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
// On the src pad, we can produce BGRx and GRAY8 of any
|
||||||
|
// width/height and with any framerate
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst_video::VideoFormat::Bgrx.to_str(),
|
||||||
|
&gst_video::VideoFormat::Gray8.to_str(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(0, 1),
|
||||||
|
gst::Fraction::new(i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
// The src pad template must be named "src" for basetransform
|
||||||
|
// and specific a pad that is always there
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// On the sink pad, we can accept BGRx of any
|
||||||
|
// width/height and with any framerate
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
("format", &gst_video::VideoFormat::Bgrx.to_str()),
|
||||||
|
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(0, 1),
|
||||||
|
gst::Fraction::new(i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
// The sink pad template must be named "sink" for basetransform
|
||||||
|
// and specific a pad that is always there
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implementation of gst_base::BaseTransform virtual methods
|
// Implementation of gst_base::BaseTransform virtual methods
|
||||||
impl BaseTransformImpl for Rgb2Gray {
|
impl BaseTransformImpl for Rgb2Gray {
|
||||||
|
// Configure basetransform so that we are never running in-place,
|
||||||
|
// don't passthrough on same caps and also never call transform_ip
|
||||||
|
// in passthrough mode (which does not matter for us here).
|
||||||
|
//
|
||||||
|
// We could work in-place for BGRx->BGRx but don't do here for simplicity
|
||||||
|
// for now.
|
||||||
|
const MODE: gst_base::subclass::BaseTransformMode =
|
||||||
|
gst_base::subclass::BaseTransformMode::NeverInPlace;
|
||||||
|
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||||
|
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||||
|
|
||||||
// Called for converting caps from one pad to another to account for any
|
// Called for converting caps from one pad to another to account for any
|
||||||
// changes in the media format this element is performing.
|
// changes in the media format this element is performing.
|
||||||
//
|
//
|
||||||
|
|
|
@ -64,61 +64,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata for the properties
|
|
||||||
static PROPERTIES: [subclass::Property; 5] = [
|
|
||||||
subclass::Property("samples-per-buffer", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Samples Per Buffer",
|
|
||||||
"Number of samples per output buffer",
|
|
||||||
1,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_SAMPLES_PER_BUFFER,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("freq", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Frequency",
|
|
||||||
"Frequency",
|
|
||||||
1,
|
|
||||||
u32::MAX,
|
|
||||||
DEFAULT_FREQ,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("volume", |name| {
|
|
||||||
glib::ParamSpec::double(
|
|
||||||
name,
|
|
||||||
"Volume",
|
|
||||||
"Output volume",
|
|
||||||
0.0,
|
|
||||||
10.0,
|
|
||||||
DEFAULT_VOLUME,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("mute", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Mute",
|
|
||||||
"Mute",
|
|
||||||
DEFAULT_MUTE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("is-live", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Is Live",
|
|
||||||
"(Pseudo) live output",
|
|
||||||
DEFAULT_IS_LIVE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Stream-specific state, i.e. audio format configuration
|
// Stream-specific state, i.e. audio format configuration
|
||||||
// and sample offset
|
// and sample offset
|
||||||
struct State {
|
struct State {
|
||||||
|
@ -203,6 +148,7 @@ impl ObjectSubclass for SineSrc {
|
||||||
const NAME: &'static str = "RsSineSrc";
|
const NAME: &'static str = "RsSineSrc";
|
||||||
type Type = super::SineSrc;
|
type Type = super::SineSrc;
|
||||||
type ParentType = gst_base::PushSrc;
|
type ParentType = gst_base::PushSrc;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -221,69 +167,61 @@ impl ObjectSubclass for SineSrc {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called exactly once when registering the type. Used for
|
|
||||||
// setting up metadata for all instances, e.g. the name and
|
|
||||||
// classification and the pad templates with their caps.
|
|
||||||
//
|
|
||||||
// Actual instances can create pads based on those pad templates
|
|
||||||
// with a subset of the caps given here. In case of basesrc,
|
|
||||||
// a "src" and "sink" pad template are required here and the base class
|
|
||||||
// will automatically instantiate pads for them.
|
|
||||||
//
|
|
||||||
// Our element here can output f32 and f64
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
// Set the element specific metadata. This information is what
|
|
||||||
// is visible from gst-inspect-1.0 and can also be programatically
|
|
||||||
// retrieved from the gst::Registry after initial registration
|
|
||||||
// without having to load the plugin in memory.
|
|
||||||
klass.set_metadata(
|
|
||||||
"Sine Wave Source",
|
|
||||||
"Source/Audio",
|
|
||||||
"Creates a sine wave",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create and add pad templates for our sink and source pad. These
|
|
||||||
// are later used for actually creating the pads and beforehand
|
|
||||||
// already provide information to GStreamer about all possible
|
|
||||||
// pads that could exist for this type.
|
|
||||||
|
|
||||||
// On the src pad, we can produce F32/F64 with any sample rate
|
|
||||||
// and any number of channels
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"audio/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[
|
|
||||||
&gst_audio::AUDIO_FORMAT_F32.to_str(),
|
|
||||||
&gst_audio::AUDIO_FORMAT_F64.to_str(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
("layout", &"interleaved"),
|
|
||||||
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
||||||
("channels", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
// The src pad template must be named "src" for basesrc
|
|
||||||
// and specific a pad that is always there
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
// Install all our properties
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of glib::Object virtual methods
|
// Implementation of glib::Object virtual methods
|
||||||
impl ObjectImpl for SineSrc {
|
impl ObjectImpl for SineSrc {
|
||||||
|
// Metadata for the properties
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"samples-per-buffer",
|
||||||
|
"Samples Per Buffer",
|
||||||
|
"Number of samples per output buffer",
|
||||||
|
1,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_SAMPLES_PER_BUFFER,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"freq",
|
||||||
|
"Frequency",
|
||||||
|
"Frequency",
|
||||||
|
1,
|
||||||
|
u32::MAX,
|
||||||
|
DEFAULT_FREQ,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::double(
|
||||||
|
"volume",
|
||||||
|
"Volume",
|
||||||
|
"Output volume",
|
||||||
|
0.0,
|
||||||
|
10.0,
|
||||||
|
DEFAULT_VOLUME,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"mute",
|
||||||
|
"Mute",
|
||||||
|
"Mute",
|
||||||
|
DEFAULT_MUTE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"is-live",
|
||||||
|
"Is Live",
|
||||||
|
"(Pseudo) live output",
|
||||||
|
DEFAULT_IS_LIVE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
// Called right after construction of a new instance
|
// Called right after construction of a new instance
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
// Call the parent class' ::constructed() implementation first
|
// Call the parent class' ::constructed() implementation first
|
||||||
|
@ -297,11 +235,15 @@ impl ObjectImpl for SineSrc {
|
||||||
|
|
||||||
// Called whenever a value of a property is changed. It can be called
|
// Called whenever a value of a property is changed. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("samples-per-buffer", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"samples-per-buffer" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let samples_per_buffer = value.get_some().expect("type checked upstream");
|
let samples_per_buffer = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -316,7 +258,7 @@ impl ObjectImpl for SineSrc {
|
||||||
|
|
||||||
let _ = obj.post_message(gst::message::Latency::builder().src(obj).build());
|
let _ = obj.post_message(gst::message::Latency::builder().src(obj).build());
|
||||||
}
|
}
|
||||||
subclass::Property("freq", ..) => {
|
"freq" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let freq = value.get_some().expect("type checked upstream");
|
let freq = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -328,7 +270,7 @@ impl ObjectImpl for SineSrc {
|
||||||
);
|
);
|
||||||
settings.freq = freq;
|
settings.freq = freq;
|
||||||
}
|
}
|
||||||
subclass::Property("volume", ..) => {
|
"volume" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let volume = value.get_some().expect("type checked upstream");
|
let volume = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -340,7 +282,7 @@ impl ObjectImpl for SineSrc {
|
||||||
);
|
);
|
||||||
settings.volume = volume;
|
settings.volume = volume;
|
||||||
}
|
}
|
||||||
subclass::Property("mute", ..) => {
|
"mute" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let mute = value.get_some().expect("type checked upstream");
|
let mute = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -352,7 +294,7 @@ impl ObjectImpl for SineSrc {
|
||||||
);
|
);
|
||||||
settings.mute = mute;
|
settings.mute = mute;
|
||||||
}
|
}
|
||||||
subclass::Property("is-live", ..) => {
|
"is-live" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let is_live = value.get_some().expect("type checked upstream");
|
let is_live = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -370,27 +312,25 @@ impl ObjectImpl for SineSrc {
|
||||||
|
|
||||||
// Called whenever a value of a property is read. It can be called
|
// Called whenever a value of a property is read. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"samples-per-buffer" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("samples-per-buffer", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.samples_per_buffer.to_value()
|
settings.samples_per_buffer.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("freq", ..) => {
|
"freq" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.freq.to_value()
|
settings.freq.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("volume", ..) => {
|
"volume" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.volume.to_value()
|
settings.volume.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("mute", ..) => {
|
"mute" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.mute.to_value()
|
settings.mute.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("is-live", ..) => {
|
"is-live" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.is_live.to_value()
|
settings.is_live.to_value()
|
||||||
}
|
}
|
||||||
|
@ -401,6 +341,62 @@ impl ObjectImpl for SineSrc {
|
||||||
|
|
||||||
// Implementation of gst::Element virtual methods
|
// Implementation of gst::Element virtual methods
|
||||||
impl ElementImpl for SineSrc {
|
impl ElementImpl for SineSrc {
|
||||||
|
// Set the element specific metadata. This information is what
|
||||||
|
// is visible from gst-inspect-1.0 and can also be programatically
|
||||||
|
// retrieved from the gst::Registry after initial registration
|
||||||
|
// without having to load the plugin in memory.
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Sine Wave Source",
|
||||||
|
"Source/Audio",
|
||||||
|
"Creates a sine wave",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and add pad templates for our sink and source pad. These
|
||||||
|
// are later used for actually creating the pads and beforehand
|
||||||
|
// already provide information to GStreamer about all possible
|
||||||
|
// pads that could exist for this type.
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
// On the src pad, we can produce F32/F64 with any sample rate
|
||||||
|
// and any number of channels
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"audio/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst_audio::AUDIO_FORMAT_F32.to_str(),
|
||||||
|
&gst_audio::AUDIO_FORMAT_F64.to_str(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
("layout", &"interleaved"),
|
||||||
|
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
|
("channels", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
// The src pad template must be named "src" for basesrc
|
||||||
|
// and specific a pad that is always there
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
// Called whenever the state of the element should be changed. This allows for
|
// Called whenever the state of the element should be changed. This allows for
|
||||||
// starting up the element, allocating/deallocating resources or shutting down
|
// starting up the element, allocating/deallocating resources or shutting down
|
||||||
// the element again.
|
// the element again.
|
||||||
|
|
|
@ -35,16 +35,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("source", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Source",
|
|
||||||
"Source",
|
|
||||||
gst::Element::static_type(),
|
|
||||||
glib::ParamFlags::WRITABLE | glib::ParamFlags::CONSTRUCT_ONLY,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
struct Stream {
|
struct Stream {
|
||||||
source_pad: gst::Pad,
|
source_pad: gst::Pad,
|
||||||
ghost_pad: gst::GhostPad,
|
ghost_pad: gst::GhostPad,
|
||||||
|
@ -67,6 +57,7 @@ impl ObjectSubclass for CustomSource {
|
||||||
const NAME: &'static str = "FallbackSrcCustomSource";
|
const NAME: &'static str = "FallbackSrcCustomSource";
|
||||||
type Type = super::CustomSource;
|
type Type = super::CustomSource;
|
||||||
type ParentType = gst::Bin;
|
type ParentType = gst::Bin;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -82,35 +73,32 @@ impl ObjectSubclass for CustomSource {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"audio_%u",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Sometimes,
|
|
||||||
&gst::Caps::new_any(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"video_%u",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Sometimes,
|
|
||||||
&gst::Caps::new_any(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for CustomSource {
|
impl ObjectImpl for CustomSource {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::object(
|
||||||
|
"source",
|
||||||
|
"Source",
|
||||||
|
"Source",
|
||||||
|
gst::Element::static_type(),
|
||||||
|
glib::ParamFlags::WRITABLE | glib::ParamFlags::CONSTRUCT_ONLY,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("source", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"source" => {
|
||||||
let source = value.get::<gst::Element>().unwrap().unwrap();
|
let source = value.get::<gst::Element>().unwrap().unwrap();
|
||||||
self.source.set(source.clone()).unwrap();
|
self.source.set(source.clone()).unwrap();
|
||||||
obj.add(&source).unwrap();
|
obj.add(&source).unwrap();
|
||||||
|
@ -129,6 +117,30 @@ impl ObjectImpl for CustomSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for CustomSource {
|
impl ElementImpl for CustomSource {
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let audio_src_pad_template = gst::PadTemplate::new(
|
||||||
|
"audio_%u",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Sometimes,
|
||||||
|
&gst::Caps::new_any(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let video_src_pad_template = gst::PadTemplate::new(
|
||||||
|
"video_%u",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Sometimes,
|
||||||
|
&gst::Caps::new_any(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![audio_src_pad_template, video_src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -177,137 +177,11 @@ pub struct FallbackSrc {
|
||||||
state: Mutex<Option<State>>,
|
state: Mutex<Option<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 13] = [
|
|
||||||
subclass::Property("enable-audio", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Enable Audio",
|
|
||||||
"Enable the audio stream, this will output silence if there's no audio in the configured URI",
|
|
||||||
true,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("enable-video", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Enable Video",
|
|
||||||
"Enable the video stream, this will output black or the fallback video if there's no video in the configured URI",
|
|
||||||
true,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("uri", |name| {
|
|
||||||
glib::ParamSpec::string(name, "URI", "URI to use", None, glib::ParamFlags::READWRITE)
|
|
||||||
}),
|
|
||||||
subclass::Property("source", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Source",
|
|
||||||
"Source to use instead of the URI",
|
|
||||||
gst::Element::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("fallback-uri", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"Fallback URI",
|
|
||||||
"Fallback URI to use for video in case the main stream doesn't work",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("timeout", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Timeout",
|
|
||||||
"Timeout for switching to the fallback URI",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
5 * gst::SECOND_VAL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("restart-timeout", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Timeout",
|
|
||||||
"Timeout for restarting an active source",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
5 * gst::SECOND_VAL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("retry-timeout", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Retry Timeout",
|
|
||||||
"Timeout for stopping after repeated failure",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
60 * gst::SECOND_VAL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("restart-on-eos", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Restart on EOS",
|
|
||||||
"Restart source on EOS",
|
|
||||||
false,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("status", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Status",
|
|
||||||
"Current source status",
|
|
||||||
Status::static_type(),
|
|
||||||
Status::Stopped as i32,
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("min-latency", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Minimum Latency",
|
|
||||||
"When the main source has a higher latency than the fallback source \
|
|
||||||
this allows to configure a minimum latency that would be configured \
|
|
||||||
if initially the fallback is enabled",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
0,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("buffer-duration", |name| {
|
|
||||||
glib::ParamSpec::int64(
|
|
||||||
name,
|
|
||||||
"Buffer Duration",
|
|
||||||
"Buffer duration when buffering streams (-1 default value)",
|
|
||||||
-1,
|
|
||||||
std::i64::MAX,
|
|
||||||
-1,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("statistics", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Statistics",
|
|
||||||
"Various statistics",
|
|
||||||
gst::Structure::static_type(),
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
impl ObjectSubclass for FallbackSrc {
|
impl ObjectSubclass for FallbackSrc {
|
||||||
const NAME: &'static str = "FallbackSrc";
|
const NAME: &'static str = "FallbackSrc";
|
||||||
type Type = super::FallbackSrc;
|
type Type = super::FallbackSrc;
|
||||||
type ParentType = gst::Bin;
|
type ParentType = gst::Bin;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -319,58 +193,125 @@ impl ObjectSubclass for FallbackSrc {
|
||||||
state: Mutex::new(None),
|
state: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Fallback Source",
|
|
||||||
"Generic/Source",
|
|
||||||
"Live source with uridecodebin3 or custom source, and fallback image stream",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"audio",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Sometimes,
|
|
||||||
&gst::Caps::new_any(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"video",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Sometimes,
|
|
||||||
&gst::Caps::new_any(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.add_signal_with_class_handler_and_accumulator(
|
|
||||||
"update-uri",
|
|
||||||
glib::SignalFlags::RUN_LAST | glib::SignalFlags::ACTION,
|
|
||||||
&[String::static_type()],
|
|
||||||
String::static_type(),
|
|
||||||
|_token, args| {
|
|
||||||
// Simplify return the input by default
|
|
||||||
Some(args[1].clone())
|
|
||||||
},
|
|
||||||
|_hint, ret, value| {
|
|
||||||
*ret = value.clone();
|
|
||||||
false
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for FallbackSrc {
|
impl ObjectImpl for FallbackSrc {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"enable-audio",
|
||||||
|
"Enable Audio",
|
||||||
|
"Enable the audio stream, this will output silence if there's no audio in the configured URI",
|
||||||
|
true,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"enable-video",
|
||||||
|
"Enable Video",
|
||||||
|
"Enable the video stream, this will output black or the fallback video if there's no video in the configured URI",
|
||||||
|
true,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string("uri", "URI", "URI to use", None, glib::ParamFlags::READWRITE),
|
||||||
|
glib::ParamSpec::object(
|
||||||
|
"source",
|
||||||
|
"Source",
|
||||||
|
"Source to use instead of the URI",
|
||||||
|
gst::Element::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"fallback-uri",
|
||||||
|
"Fallback URI",
|
||||||
|
"Fallback URI to use for video in case the main stream doesn't work",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"timeout",
|
||||||
|
"Timeout",
|
||||||
|
"Timeout for switching to the fallback URI",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
5 * gst::SECOND_VAL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"restart-timeout",
|
||||||
|
"Timeout",
|
||||||
|
"Timeout for restarting an active source",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
5 * gst::SECOND_VAL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"retry-timeout",
|
||||||
|
"Retry Timeout",
|
||||||
|
"Timeout for stopping after repeated failure",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
60 * gst::SECOND_VAL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"restart-on-eos",
|
||||||
|
"Restart on EOS",
|
||||||
|
"Restart source on EOS",
|
||||||
|
false,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::enum_(
|
||||||
|
"status",
|
||||||
|
"Status",
|
||||||
|
"Current source status",
|
||||||
|
Status::static_type(),
|
||||||
|
Status::Stopped as i32,
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"min-latency",
|
||||||
|
"Minimum Latency",
|
||||||
|
"When the main source has a higher latency than the fallback source \
|
||||||
|
this allows to configure a minimum latency that would be configured \
|
||||||
|
if initially the fallback is enabled",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
0,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int64(
|
||||||
|
"buffer-duration",
|
||||||
|
"Buffer Duration",
|
||||||
|
"Buffer duration when buffering streams (-1 default value)",
|
||||||
|
-1,
|
||||||
|
std::i64::MAX,
|
||||||
|
-1,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"statistics",
|
||||||
|
"Statistics",
|
||||||
|
"Various statistics",
|
||||||
|
gst::Structure::static_type(),
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("enable-audio", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"enable-audio" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -382,7 +323,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.enable_audio = new_value;
|
settings.enable_audio = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("enable-video", ..) => {
|
"enable-video" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -394,7 +335,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.enable_video = new_value;
|
settings.enable_video = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("uri", ..) => {
|
"uri" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get().expect("type checked upstream");
|
let new_value = value.get().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -406,7 +347,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.uri = new_value;
|
settings.uri = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("source", ..) => {
|
"source" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get().expect("type checked upstream");
|
let new_value = value.get().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -418,7 +359,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.source = new_value;
|
settings.source = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("fallback-uri", ..) => {
|
"fallback-uri" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get().expect("type checked upstream");
|
let new_value = value.get().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -430,7 +371,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.fallback_uri = new_value;
|
settings.fallback_uri = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("timeout", ..) => {
|
"timeout" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -442,7 +383,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.timeout = new_value;
|
settings.timeout = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("restart-timeout", ..) => {
|
"restart-timeout" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -454,7 +395,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.restart_timeout = new_value;
|
settings.restart_timeout = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("retry-timeout", ..) => {
|
"retry-timeout" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -466,7 +407,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.retry_timeout = new_value;
|
settings.retry_timeout = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("restart-on-eos", ..) => {
|
"restart-on-eos" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -478,7 +419,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.restart_on_eos = new_value;
|
settings.restart_on_eos = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("min-latency", ..) => {
|
"min-latency" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -490,7 +431,7 @@ impl ObjectImpl for FallbackSrc {
|
||||||
);
|
);
|
||||||
settings.min_latency = new_value;
|
settings.min_latency = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("buffer-duration", ..) => {
|
"buffer-duration" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -509,47 +450,45 @@ impl ObjectImpl for FallbackSrc {
|
||||||
// Called whenever a value of a property is read. It can be called
|
// Called whenever a value of a property is read. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
#[allow(clippy::blocks_in_if_conditions)]
|
#[allow(clippy::blocks_in_if_conditions)]
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"enable-audio" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("enable-audio", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.enable_audio.to_value()
|
settings.enable_audio.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("enable-video", ..) => {
|
"enable-video" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.enable_video.to_value()
|
settings.enable_video.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("uri", ..) => {
|
"uri" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.uri.to_value()
|
settings.uri.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("source", ..) => {
|
"source" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.source.to_value()
|
settings.source.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("fallback-uri", ..) => {
|
"fallback-uri" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.fallback_uri.to_value()
|
settings.fallback_uri.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("timeout", ..) => {
|
"timeout" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.timeout.to_value()
|
settings.timeout.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("restart-timeout", ..) => {
|
"restart-timeout" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.restart_timeout.to_value()
|
settings.restart_timeout.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("retry-timeout", ..) => {
|
"retry-timeout" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.retry_timeout.to_value()
|
settings.retry_timeout.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("restart-on-eos", ..) => {
|
"restart-on-eos" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.restart_on_eos.to_value()
|
settings.restart_on_eos.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("status", ..) => {
|
"status" => {
|
||||||
let state_guard = self.state.lock().unwrap();
|
let state_guard = self.state.lock().unwrap();
|
||||||
|
|
||||||
// If we have no state then we'r stopped
|
// If we have no state then we'r stopped
|
||||||
|
@ -601,19 +540,42 @@ impl ObjectImpl for FallbackSrc {
|
||||||
// Otherwise we're running now
|
// Otherwise we're running now
|
||||||
Status::Running.to_value()
|
Status::Running.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("min-latency", ..) => {
|
"min-latency" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.min_latency.to_value()
|
settings.min_latency.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("buffer-duration", ..) => {
|
"buffer-duration" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.buffer_duration.to_value()
|
settings.buffer_duration.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("statistics", ..) => self.get_stats().to_value(),
|
"statistics" => self.get_stats().to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signals() -> &'static [glib::subclass::Signal] {
|
||||||
|
static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| {
|
||||||
|
vec![glib::subclass::Signal::builder(
|
||||||
|
"update-uri",
|
||||||
|
&[String::static_type()],
|
||||||
|
String::static_type(),
|
||||||
|
)
|
||||||
|
.action()
|
||||||
|
.class_handler(|_token, args| {
|
||||||
|
// Simply return the input by default
|
||||||
|
Some(args[1].clone())
|
||||||
|
})
|
||||||
|
.accumulator(|_hint, ret, value| {
|
||||||
|
// First signal handler wins
|
||||||
|
*ret = value.clone();
|
||||||
|
false
|
||||||
|
})
|
||||||
|
.build()]
|
||||||
|
});
|
||||||
|
|
||||||
|
SIGNALS.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -624,6 +586,43 @@ impl ObjectImpl for FallbackSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for FallbackSrc {
|
impl ElementImpl for FallbackSrc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Fallback Source",
|
||||||
|
"Generic/Source",
|
||||||
|
"Live source with uridecodebin3 or custom source, and fallback image stream",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let audio_src_pad_template = gst::PadTemplate::new(
|
||||||
|
"audio",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Sometimes,
|
||||||
|
&gst::Caps::new_any(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let video_src_pad_template = gst::PadTemplate::new(
|
||||||
|
"video",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Sometimes,
|
||||||
|
&gst::Caps::new_any(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![audio_src_pad_template, video_src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -35,29 +35,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 2] = [
|
|
||||||
subclass::Property("uri", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"URI",
|
|
||||||
"URI to use for video in case the main stream doesn't work",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("min-latency", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Minimum Latency",
|
|
||||||
"Minimum Latency",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
0,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
uri: Option<String>,
|
uri: Option<String>,
|
||||||
|
@ -89,6 +66,7 @@ impl ObjectSubclass for VideoFallbackSource {
|
||||||
const NAME: &'static str = "FallbackSrcVideoFallbackSource";
|
const NAME: &'static str = "FallbackSrcVideoFallbackSource";
|
||||||
type Type = super::VideoFallbackSource;
|
type Type = super::VideoFallbackSource;
|
||||||
type ParentType = gst::Bin;
|
type ParentType = gst::Bin;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -105,26 +83,43 @@ impl ObjectSubclass for VideoFallbackSource {
|
||||||
settings: Mutex::new(Settings::default()),
|
settings: Mutex::new(Settings::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&gst::Caps::new_any(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for VideoFallbackSource {
|
impl ObjectImpl for VideoFallbackSource {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"uri",
|
||||||
|
"URI",
|
||||||
|
"URI to use for video in case the main stream doesn't work",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"min-latency",
|
||||||
|
"Minimum Latency",
|
||||||
|
"Minimum Latency",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
0,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("uri", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"uri" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get().expect("type checked upstream");
|
let new_value = value.get().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -136,7 +131,7 @@ impl ObjectImpl for VideoFallbackSource {
|
||||||
);
|
);
|
||||||
settings.uri = new_value;
|
settings.uri = new_value;
|
||||||
}
|
}
|
||||||
subclass::Property("min-latency", ..) => {
|
"min-latency" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let new_value = value.get_some().expect("type checked upstream");
|
let new_value = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -152,15 +147,13 @@ impl ObjectImpl for VideoFallbackSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"uri" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("uri", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.uri.to_value()
|
settings.uri.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("min-latency", ..) => {
|
"min-latency" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.min_latency.to_value()
|
settings.min_latency.to_value()
|
||||||
}
|
}
|
||||||
|
@ -178,6 +171,22 @@ impl ObjectImpl for VideoFallbackSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for VideoFallbackSource {
|
impl ElementImpl for VideoFallbackSource {
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&gst::Caps::new_any(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -123,58 +123,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 5] = [
|
|
||||||
subclass::Property("timeout", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Timeout",
|
|
||||||
"Timeout in nanoseconds",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
DEFAULT_TIMEOUT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("active-pad", |name| {
|
|
||||||
glib::ParamSpec::object(
|
|
||||||
name,
|
|
||||||
"Active Pad",
|
|
||||||
"Currently active pad. Writes are ignored if auto-switch=true",
|
|
||||||
gst::Pad::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("auto-switch", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Automatically switch pads",
|
|
||||||
"Automatically switch pads (If true, prefer primary sink, otherwise manual selection via the active-pad property)",
|
|
||||||
DEFAULT_AUTO_SWITCH,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("primary-health", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Primary stream state",
|
|
||||||
"Reports the health of the primary stream on the sink pad",
|
|
||||||
StreamHealth::static_type(),
|
|
||||||
DEFAULT_STREAM_HEALTH as i32,
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("fallback-health", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Fallback stream state",
|
|
||||||
"Reports the health of the fallback stream on the fallback_sink pad",
|
|
||||||
StreamHealth::static_type(),
|
|
||||||
DEFAULT_STREAM_HEALTH as i32,
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
impl OutputState {
|
impl OutputState {
|
||||||
fn get_health(
|
fn get_health(
|
||||||
&self,
|
&self,
|
||||||
|
@ -697,6 +645,7 @@ impl ObjectSubclass for FallbackSwitch {
|
||||||
const NAME: &'static str = "FallbackSwitch";
|
const NAME: &'static str = "FallbackSwitch";
|
||||||
type Type = super::FallbackSwitch;
|
type Type = super::FallbackSwitch;
|
||||||
type ParentType = gst_base::Aggregator;
|
type ParentType = gst_base::Aggregator;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -717,62 +666,72 @@ impl ObjectSubclass for FallbackSwitch {
|
||||||
settings: Mutex::new(Settings::default()),
|
settings: Mutex::new(Settings::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Fallback Switch",
|
|
||||||
"Generic",
|
|
||||||
"Allows switching to a fallback input after a given timeout",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::with_gtype(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
gst_base::AggregatorPad::static_type(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::with_gtype(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
gst_base::AggregatorPad::static_type(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let fallbacksink_pad_template = gst::PadTemplate::with_gtype(
|
|
||||||
"fallback_sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Request,
|
|
||||||
&caps,
|
|
||||||
gst_base::AggregatorPad::static_type(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(fallbacksink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for FallbackSwitch {
|
impl ObjectImpl for FallbackSwitch {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"timeout",
|
||||||
|
"Timeout",
|
||||||
|
"Timeout in nanoseconds",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
DEFAULT_TIMEOUT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::object(
|
||||||
|
"active-pad",
|
||||||
|
"Active Pad",
|
||||||
|
"Currently active pad. Writes are ignored if auto-switch=true",
|
||||||
|
gst::Pad::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"auto-switch",
|
||||||
|
"Automatically switch pads",
|
||||||
|
"Automatically switch pads (If true, prefer primary sink, otherwise manual selection via the active-pad property)",
|
||||||
|
DEFAULT_AUTO_SWITCH,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::enum_(
|
||||||
|
"primary-health",
|
||||||
|
"Primary stream state",
|
||||||
|
"Reports the health of the primary stream on the sink pad",
|
||||||
|
StreamHealth::static_type(),
|
||||||
|
DEFAULT_STREAM_HEALTH as i32,
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::enum_(
|
||||||
|
"fallback-health",
|
||||||
|
"Fallback stream state",
|
||||||
|
"Reports the health of the fallback stream on the fallback_sink pad",
|
||||||
|
StreamHealth::static_type(),
|
||||||
|
DEFAULT_STREAM_HEALTH as i32,
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
obj.add_pad(&self.primary_sinkpad).unwrap();
|
obj.add_pad(&self.primary_sinkpad).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("timeout", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"timeout" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let timeout = value.get_some().expect("type checked upstream");
|
let timeout = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -785,7 +744,7 @@ impl ObjectImpl for FallbackSwitch {
|
||||||
settings.timeout = timeout;
|
settings.timeout = timeout;
|
||||||
drop(settings);
|
drop(settings);
|
||||||
}
|
}
|
||||||
subclass::Property("active-pad", ..) => {
|
"active-pad" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
if settings.auto_switch {
|
if settings.auto_switch {
|
||||||
gst_warning!(
|
gst_warning!(
|
||||||
|
@ -804,7 +763,7 @@ impl ObjectImpl for FallbackSwitch {
|
||||||
}
|
}
|
||||||
drop(settings);
|
drop(settings);
|
||||||
}
|
}
|
||||||
subclass::Property("auto-switch", ..) => {
|
"auto-switch" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.auto_switch = value.get_some().expect("type checked upstream");
|
settings.auto_switch = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -812,27 +771,25 @@ impl ObjectImpl for FallbackSwitch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"timeout" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("timeout", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.timeout.to_value()
|
settings.timeout.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("active-pad", ..) => {
|
"active-pad" => {
|
||||||
let active_pad = self.active_sinkpad.lock().unwrap().clone();
|
let active_pad = self.active_sinkpad.lock().unwrap().clone();
|
||||||
active_pad.to_value()
|
active_pad.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("auto-switch", ..) => {
|
"auto-switch" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.auto_switch.to_value()
|
settings.auto_switch.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("primary-health", ..) => {
|
"primary-health" => {
|
||||||
let state = self.output_state.lock().unwrap();
|
let state = self.output_state.lock().unwrap();
|
||||||
state.primary.stream_health.to_value()
|
state.primary.stream_health.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("fallback-health", ..) => {
|
"fallback-health" => {
|
||||||
let state = self.output_state.lock().unwrap();
|
let state = self.output_state.lock().unwrap();
|
||||||
state.fallback.stream_health.to_value()
|
state.fallback.stream_health.to_value()
|
||||||
}
|
}
|
||||||
|
@ -842,6 +799,59 @@ impl ObjectImpl for FallbackSwitch {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for FallbackSwitch {
|
impl ElementImpl for FallbackSwitch {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Fallback Switch",
|
||||||
|
"Generic",
|
||||||
|
"Allows switching to a fallback input after a given timeout",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::with_gtype(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
gst_base::AggregatorPad::static_type(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::with_gtype(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
gst_base::AggregatorPad::static_type(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let fallbacksink_pad_template = gst::PadTemplate::with_gtype(
|
||||||
|
"fallback_sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Request,
|
||||||
|
&caps,
|
||||||
|
gst_base::AggregatorPad::static_type(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
src_pad_template,
|
||||||
|
sink_pad_template,
|
||||||
|
fallbacksink_pad_template,
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn request_new_pad(
|
fn request_new_pad(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -47,27 +47,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 2] = [
|
|
||||||
subclass::Property("record", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Record",
|
|
||||||
"Enable/disable recording",
|
|
||||||
DEFAULT_RECORD,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("recording", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Recording",
|
|
||||||
"Whether recording is currently taking place",
|
|
||||||
DEFAULT_RECORD,
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Stream {
|
struct Stream {
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
|
@ -1642,6 +1621,7 @@ impl ObjectSubclass for ToggleRecord {
|
||||||
const NAME: &'static str = "RsToggleRecord";
|
const NAME: &'static str = "RsToggleRecord";
|
||||||
type Type = super::ToggleRecord;
|
type Type = super::ToggleRecord;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -1720,62 +1700,41 @@ impl ObjectSubclass for ToggleRecord {
|
||||||
pads: Mutex::new(pads),
|
pads: Mutex::new(pads),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.set_metadata(
|
|
||||||
"Toggle Record",
|
|
||||||
"Generic",
|
|
||||||
"Valve that ensures multiple streams start/end at the same time",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::new_any();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src_%u",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Sometimes,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink_%u",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Request,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for ToggleRecord {
|
impl ObjectImpl for ToggleRecord {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"record",
|
||||||
|
"Record",
|
||||||
|
"Enable/disable recording",
|
||||||
|
DEFAULT_RECORD,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"recording",
|
||||||
|
"Recording",
|
||||||
|
"Whether recording is currently taking place",
|
||||||
|
DEFAULT_RECORD,
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("record", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"record" => {
|
||||||
let mut settings = self.settings.lock();
|
let mut settings = self.settings.lock();
|
||||||
let record = value.get_some().expect("type checked upstream");
|
let record = value.get_some().expect("type checked upstream");
|
||||||
gst_debug!(
|
gst_debug!(
|
||||||
|
@ -1792,15 +1751,13 @@ impl ObjectImpl for ToggleRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"record" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("record", ..) => {
|
|
||||||
let settings = self.settings.lock();
|
let settings = self.settings.lock();
|
||||||
settings.record.to_value()
|
settings.record.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("recording", ..) => {
|
"recording" => {
|
||||||
let rec_state = self.state.lock();
|
let rec_state = self.state.lock();
|
||||||
(rec_state.recording_state == RecordingState::Recording).to_value()
|
(rec_state.recording_state == RecordingState::Recording).to_value()
|
||||||
}
|
}
|
||||||
|
@ -1817,6 +1774,65 @@ impl ObjectImpl for ToggleRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for ToggleRecord {
|
impl ElementImpl for ToggleRecord {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Toggle Record",
|
||||||
|
"Generic",
|
||||||
|
"Valve that ensures multiple streams start/end at the same time",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let secondary_src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src_%u",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Sometimes,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let secondary_sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink_%u",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Request,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
src_pad_template,
|
||||||
|
sink_pad_template,
|
||||||
|
secondary_src_pad_template,
|
||||||
|
secondary_sink_pad_template,
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -32,6 +32,7 @@ impl ObjectSubclass for CdgDec {
|
||||||
const NAME: &'static str = "CdgDec";
|
const NAME: &'static str = "CdgDec";
|
||||||
type Type = super::CdgDec;
|
type Type = super::CdgDec;
|
||||||
type ParentType = gst_video::VideoDecoder;
|
type ParentType = gst_video::VideoDecoder;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -43,48 +44,58 @@ impl ObjectSubclass for CdgDec {
|
||||||
output_info: Mutex::new(None),
|
output_info: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"CDG decoder",
|
|
||||||
"Decoder/Video",
|
|
||||||
"CDG decoder",
|
|
||||||
"Guillaume Desmottes <guillaume.desmottes@collabora.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple("video/x-cdg", &[("parsed", &true)]);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &gst_video::VideoFormat::Rgba.to_str()),
|
|
||||||
("width", &(CDG_WIDTH as i32)),
|
|
||||||
("height", &(CDG_HEIGHT as i32)),
|
|
||||||
("framerate", &gst::Fraction::new(0, 1)),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for CdgDec {}
|
impl ObjectImpl for CdgDec {}
|
||||||
|
|
||||||
impl ElementImpl for CdgDec {}
|
impl ElementImpl for CdgDec {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"CDG decoder",
|
||||||
|
"Decoder/Video",
|
||||||
|
"CDG decoder",
|
||||||
|
"Guillaume Desmottes <guillaume.desmottes@collabora.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple("video/x-cdg", &[("parsed", &true)]);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
("format", &gst_video::VideoFormat::Rgba.to_str()),
|
||||||
|
("width", &(CDG_WIDTH as i32)),
|
||||||
|
("height", &(CDG_HEIGHT as i32)),
|
||||||
|
("framerate", &gst::Fraction::new(0, 1)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VideoDecoderImpl for CdgDec {
|
impl VideoDecoderImpl for CdgDec {
|
||||||
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
|
|
@ -39,6 +39,7 @@ impl ObjectSubclass for CdgParse {
|
||||||
const NAME: &'static str = "CdgParse";
|
const NAME: &'static str = "CdgParse";
|
||||||
type Type = super::CdgParse;
|
type Type = super::CdgParse;
|
||||||
type ParentType = gst_base::BaseParse;
|
type ParentType = gst_base::BaseParse;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -47,48 +48,58 @@ impl ObjectSubclass for CdgParse {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"CDG parser",
|
|
||||||
"Codec/Parser/Video",
|
|
||||||
"CDG parser",
|
|
||||||
"Guillaume Desmottes <guillaume.desmottes@collabora.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple("video/x-cdg", &[]);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple(
|
|
||||||
"video/x-cdg",
|
|
||||||
&[
|
|
||||||
("width", &(CDG_WIDTH as i32)),
|
|
||||||
("height", &(CDG_HEIGHT as i32)),
|
|
||||||
("framerate", &gst::Fraction::new(0, 1)),
|
|
||||||
("parsed", &true),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for CdgParse {}
|
impl ObjectImpl for CdgParse {}
|
||||||
|
|
||||||
impl ElementImpl for CdgParse {}
|
impl ElementImpl for CdgParse {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"CDG parser",
|
||||||
|
"Codec/Parser/Video",
|
||||||
|
"CDG parser",
|
||||||
|
"Guillaume Desmottes <guillaume.desmottes@collabora.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple("video/x-cdg", &[]);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple(
|
||||||
|
"video/x-cdg",
|
||||||
|
&[
|
||||||
|
("width", &(CDG_WIDTH as i32)),
|
||||||
|
("height", &(CDG_HEIGHT as i32)),
|
||||||
|
("framerate", &gst::Fraction::new(0, 1)),
|
||||||
|
("parsed", &true),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn bytes_to_time(bytes: Bytes) -> gst::ClockTime {
|
fn bytes_to_time(bytes: Bytes) -> gst::ClockTime {
|
||||||
match bytes {
|
match bytes {
|
||||||
|
|
|
@ -77,38 +77,6 @@ pub struct CCDetect {
|
||||||
state: Mutex<Option<State>>,
|
state: Mutex<Option<State>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 3] = [
|
|
||||||
subclass::Property("window", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Window",
|
|
||||||
"Window of time (in ns) to determine if captions exist in the stream",
|
|
||||||
0,
|
|
||||||
u64::MAX,
|
|
||||||
DEFAULT_WINDOW,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("cc608", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"cc608",
|
|
||||||
"Whether CEA608 captions (CC1/CC3) have been detected",
|
|
||||||
DEFAULT_CC608,
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("cc708", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"cc608",
|
|
||||||
"Whether CEA708 captions (cc_data) have been detected",
|
|
||||||
DEFAULT_CC708,
|
|
||||||
glib::ParamFlags::READABLE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct CCPacketContents {
|
struct CCPacketContents {
|
||||||
cc608: bool,
|
cc608: bool,
|
||||||
|
@ -387,6 +355,7 @@ impl ObjectSubclass for CCDetect {
|
||||||
const NAME: &'static str = "CCDetect";
|
const NAME: &'static str = "CCDetect";
|
||||||
type Type = super::CCDetect;
|
type Type = super::CCDetect;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -398,58 +367,50 @@ impl ObjectSubclass for CCDetect {
|
||||||
state: Mutex::new(None),
|
state: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Closed Caption Detect",
|
|
||||||
"Filter/Video/ClosedCaption/Detect",
|
|
||||||
"Detect if valid closed captions are present in a stream",
|
|
||||||
"Matthew Waters <matthew@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::new_empty();
|
|
||||||
{
|
|
||||||
let caps = caps.get_mut().unwrap();
|
|
||||||
let s = gst::Structure::builder("closedcaption/x-cea-708")
|
|
||||||
.field("format", &gst::List::new(&[&"cc_data", &"cdp"]))
|
|
||||||
.build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.configure(
|
|
||||||
gst_base::subclass::BaseTransformMode::AlwaysInPlace,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for CCDetect {
|
impl ObjectImpl for CCDetect {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"window",
|
||||||
|
"Window",
|
||||||
|
"Window of time (in ns) to determine if captions exist in the stream",
|
||||||
|
0,
|
||||||
|
u64::MAX,
|
||||||
|
DEFAULT_WINDOW,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"cc608",
|
||||||
|
"cc608",
|
||||||
|
"Whether CEA608 captions (CC1/CC3) have been detected",
|
||||||
|
DEFAULT_CC608,
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"cc708",
|
||||||
|
"cc608",
|
||||||
|
"Whether CEA708 captions (cc_data) have been detected",
|
||||||
|
DEFAULT_CC708,
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("window", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"window" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.window = value.get_some().expect("type checked upstream");
|
settings.window = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -457,19 +418,17 @@ impl ObjectImpl for CCDetect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"window" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("window", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.window.to_value()
|
settings.window.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("cc608", ..) => {
|
"cc608" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.cc608.to_value()
|
settings.cc608.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("cc708", ..) => {
|
"cc708" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.cc708.to_value()
|
settings.cc708.to_value()
|
||||||
}
|
}
|
||||||
|
@ -478,9 +437,60 @@ impl ObjectImpl for CCDetect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for CCDetect {}
|
impl ElementImpl for CCDetect {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Closed Caption Detect",
|
||||||
|
"Filter/Video/ClosedCaption/Detect",
|
||||||
|
"Detect if valid closed captions are present in a stream",
|
||||||
|
"Matthew Waters <matthew@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let mut caps = gst::Caps::new_empty();
|
||||||
|
{
|
||||||
|
let caps = caps.get_mut().unwrap();
|
||||||
|
let s = gst::Structure::builder("closedcaption/x-cea-708")
|
||||||
|
.field("format", &gst::List::new(&[&"cc_data", &"cdp"]))
|
||||||
|
.build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseTransformImpl for CCDetect {
|
impl BaseTransformImpl for CCDetect {
|
||||||
|
const MODE: gst_base::subclass::BaseTransformMode =
|
||||||
|
gst_base::subclass::BaseTransformMode::AlwaysInPlace;
|
||||||
|
const TRANSFORM_IP_ON_PASSTHROUGH: bool = true;
|
||||||
|
const PASSTHROUGH_ON_SAME_CAPS: bool = true;
|
||||||
|
|
||||||
fn transform_ip_passthrough(
|
fn transform_ip_passthrough(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -388,6 +388,7 @@ impl ObjectSubclass for Cea608Overlay {
|
||||||
const NAME: &'static str = "RsCea608Overlay";
|
const NAME: &'static str = "RsCea608Overlay";
|
||||||
type Type = super::Cea608Overlay;
|
type Type = super::Cea608Overlay;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -424,38 +425,6 @@ impl ObjectSubclass for Cea608Overlay {
|
||||||
state: Mutex::new(State::default()),
|
state: Mutex::new(State::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Cea 608 overlay",
|
|
||||||
"Video/Overlay/Subtitle",
|
|
||||||
"Renders CEA 608 closed caption meta over raw video frames",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst_video::VideoFormat::iter_raw()
|
|
||||||
.into_video_caps()
|
|
||||||
.unwrap()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Cea608Overlay {
|
impl ObjectImpl for Cea608Overlay {
|
||||||
|
@ -468,6 +437,48 @@ impl ObjectImpl for Cea608Overlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Cea608Overlay {
|
impl ElementImpl for Cea608Overlay {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Cea 608 overlay",
|
||||||
|
"Video/Overlay/Subtitle",
|
||||||
|
"Renders CEA 608 closed caption meta over raw video frames",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst_video::VideoFormat::iter_raw()
|
||||||
|
.into_video_caps()
|
||||||
|
.unwrap()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -375,6 +375,7 @@ impl ObjectSubclass for Cea608ToTt {
|
||||||
const NAME: &'static str = "Cea608ToTt";
|
const NAME: &'static str = "Cea608ToTt";
|
||||||
type Type = super::Cea608ToTt;
|
type Type = super::Cea608ToTt;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -411,56 +412,6 @@ impl ObjectSubclass for Cea608ToTt {
|
||||||
state: AtomicRefCell::new(State::default()),
|
state: AtomicRefCell::new(State::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"CEA-608 to TT",
|
|
||||||
"Generic",
|
|
||||||
"Converts CEA-608 Closed Captions to SRT/VTT timed text",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::new_empty();
|
|
||||||
{
|
|
||||||
let caps = caps.get_mut().unwrap();
|
|
||||||
|
|
||||||
// WebVTT
|
|
||||||
let s = gst::Structure::builder("application/x-subtitle-vtt").build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
|
|
||||||
// SRT
|
|
||||||
let s = gst::Structure::builder("application/x-subtitle").build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
|
|
||||||
// Raw timed text
|
|
||||||
let s = gst::Structure::builder("text/x-raw")
|
|
||||||
.field("format", &"utf8")
|
|
||||||
.build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
|
||||||
.field("format", &"raw")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Cea608ToTt {
|
impl ObjectImpl for Cea608ToTt {
|
||||||
|
@ -473,6 +424,66 @@ impl ObjectImpl for Cea608ToTt {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Cea608ToTt {
|
impl ElementImpl for Cea608ToTt {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"CEA-608 to TT",
|
||||||
|
"Generic",
|
||||||
|
"Converts CEA-608 Closed Captions to SRT/VTT timed text",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let mut caps = gst::Caps::new_empty();
|
||||||
|
{
|
||||||
|
let caps = caps.get_mut().unwrap();
|
||||||
|
|
||||||
|
// WebVTT
|
||||||
|
let s = gst::Structure::builder("application/x-subtitle-vtt").build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
|
||||||
|
// SRT
|
||||||
|
let s = gst::Structure::builder("application/x-subtitle").build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
|
||||||
|
// Raw timed text
|
||||||
|
let s = gst::Structure::builder("text/x-raw")
|
||||||
|
.field("format", &"utf8")
|
||||||
|
.build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
||||||
|
.field("format", &"raw")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -69,27 +69,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 2] = [
|
|
||||||
subclass::Property("uuid", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"UUID",
|
|
||||||
"UUID for the output file",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("creation-date", |name| {
|
|
||||||
glib::ParamSpec::boxed(
|
|
||||||
name,
|
|
||||||
"Creation Date",
|
|
||||||
"Creation date for the output file",
|
|
||||||
glib::DateTime::static_type(),
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub struct MccEnc {
|
pub struct MccEnc {
|
||||||
srcpad: gst::Pad,
|
srcpad: gst::Pad,
|
||||||
sinkpad: gst::Pad,
|
sinkpad: gst::Pad,
|
||||||
|
@ -470,6 +449,7 @@ impl ObjectSubclass for MccEnc {
|
||||||
const NAME: &'static str = "RsMccEnc";
|
const NAME: &'static str = "RsMccEnc";
|
||||||
type Type = super::MccEnc;
|
type Type = super::MccEnc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -519,74 +499,45 @@ impl ObjectSubclass for MccEnc {
|
||||||
settings: Mutex::new(Settings::default()),
|
settings: Mutex::new(Settings::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Mcc Encoder",
|
|
||||||
"Encoder/ClosedCaption",
|
|
||||||
"Encodes MCC Closed Caption Files",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::new_empty();
|
|
||||||
{
|
|
||||||
let caps = caps.get_mut().unwrap();
|
|
||||||
|
|
||||||
let framerates = gst::List::new(&[
|
|
||||||
&gst::Fraction::new(24, 1),
|
|
||||||
&gst::Fraction::new(25, 1),
|
|
||||||
&gst::Fraction::new(30000, 1001),
|
|
||||||
&gst::Fraction::new(30, 1),
|
|
||||||
&gst::Fraction::new(50, 1),
|
|
||||||
&gst::Fraction::new(60000, 1001),
|
|
||||||
&gst::Fraction::new(60, 1),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let s = gst::Structure::builder("closedcaption/x-cea-708")
|
|
||||||
.field("format", &"cdp")
|
|
||||||
.field("framerate", &framerates)
|
|
||||||
.build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
|
|
||||||
let s = gst::Structure::builder("closedcaption/x-cea-608")
|
|
||||||
.field("format", &"s334-1a")
|
|
||||||
.field("framerate", &framerates)
|
|
||||||
.build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
}
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-mcc").build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for MccEnc {
|
impl ObjectImpl for MccEnc {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::string(
|
||||||
|
"uuid",
|
||||||
|
"UUID",
|
||||||
|
"UUID for the output file",
|
||||||
|
None,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boxed(
|
||||||
|
"creation-date",
|
||||||
|
"Creation Date",
|
||||||
|
"Creation date for the output file",
|
||||||
|
glib::DateTime::static_type(),
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("uuid", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"uuid" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.uuid = value.get().expect("type checked upstream");
|
settings.uuid = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("creation-date", ..) => {
|
"creation-date" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.creation_date = value.get().expect("type checked upstream");
|
settings.creation_date = value.get().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -594,15 +545,13 @@ impl ObjectImpl for MccEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"uuid" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("uuid", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.uuid.to_value()
|
settings.uuid.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("creation-date", ..) => {
|
"creation-date" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.creation_date.to_value()
|
settings.creation_date.to_value()
|
||||||
}
|
}
|
||||||
|
@ -619,6 +568,70 @@ impl ObjectImpl for MccEnc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for MccEnc {
|
impl ElementImpl for MccEnc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Mcc Encoder",
|
||||||
|
"Encoder/ClosedCaption",
|
||||||
|
"Encodes MCC Closed Caption Files",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let mut caps = gst::Caps::new_empty();
|
||||||
|
{
|
||||||
|
let caps = caps.get_mut().unwrap();
|
||||||
|
|
||||||
|
let framerates = gst::List::new(&[
|
||||||
|
&gst::Fraction::new(24, 1),
|
||||||
|
&gst::Fraction::new(25, 1),
|
||||||
|
&gst::Fraction::new(30000, 1001),
|
||||||
|
&gst::Fraction::new(30, 1),
|
||||||
|
&gst::Fraction::new(50, 1),
|
||||||
|
&gst::Fraction::new(60000, 1001),
|
||||||
|
&gst::Fraction::new(60, 1),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let s = gst::Structure::builder("closedcaption/x-cea-708")
|
||||||
|
.field("format", &"cdp")
|
||||||
|
.field("framerate", &framerates)
|
||||||
|
.build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
|
||||||
|
let s = gst::Structure::builder("closedcaption/x-cea-608")
|
||||||
|
.field("format", &"s334-1a")
|
||||||
|
.field("framerate", &framerates)
|
||||||
|
.build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
}
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("application/x-mcc").build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -1122,6 +1122,7 @@ impl ObjectSubclass for MccParse {
|
||||||
const NAME: &'static str = "RsMccParse";
|
const NAME: &'static str = "RsMccParse";
|
||||||
type Type = super::MccParse;
|
type Type = super::MccParse;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -1184,56 +1185,6 @@ impl ObjectSubclass for MccParse {
|
||||||
state: Mutex::new(State::default()),
|
state: Mutex::new(State::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Mcc Parse",
|
|
||||||
"Parser/ClosedCaption",
|
|
||||||
"Parses MCC Closed Caption Files",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::new_empty();
|
|
||||||
{
|
|
||||||
let caps = caps.get_mut().unwrap();
|
|
||||||
let framerate = gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(1, std::i32::MAX),
|
|
||||||
gst::Fraction::new(std::i32::MAX, 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
let s = gst::Structure::builder("closedcaption/x-cea-708")
|
|
||||||
.field("format", &"cdp")
|
|
||||||
.field("framerate", &framerate)
|
|
||||||
.build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
|
|
||||||
let s = gst::Structure::builder("closedcaption/x-cea-608")
|
|
||||||
.field("format", &"s334-1a")
|
|
||||||
.field("framerate", &framerate)
|
|
||||||
.build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
}
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-mcc")
|
|
||||||
.field("version", &gst::List::new(&[&1i32, &2i32]))
|
|
||||||
.build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for MccParse {
|
impl ObjectImpl for MccParse {
|
||||||
|
@ -1246,6 +1197,66 @@ impl ObjectImpl for MccParse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for MccParse {
|
impl ElementImpl for MccParse {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Mcc Parse",
|
||||||
|
"Parser/ClosedCaption",
|
||||||
|
"Parses MCC Closed Caption Files",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let mut caps = gst::Caps::new_empty();
|
||||||
|
{
|
||||||
|
let caps = caps.get_mut().unwrap();
|
||||||
|
let framerate = gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(1, std::i32::MAX),
|
||||||
|
gst::Fraction::new(std::i32::MAX, 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
let s = gst::Structure::builder("closedcaption/x-cea-708")
|
||||||
|
.field("format", &"cdp")
|
||||||
|
.field("framerate", &framerate)
|
||||||
|
.build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
|
||||||
|
let s = gst::Structure::builder("closedcaption/x-cea-608")
|
||||||
|
.field("format", &"s334-1a")
|
||||||
|
.field("framerate", &framerate)
|
||||||
|
.build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
}
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("application/x-mcc")
|
||||||
|
.field("version", &gst::List::new(&[&1i32, &2i32]))
|
||||||
|
.build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -337,6 +337,7 @@ impl ObjectSubclass for SccEnc {
|
||||||
const NAME: &'static str = "RsSccEnc";
|
const NAME: &'static str = "RsSccEnc";
|
||||||
type Type = super::SccEnc;
|
type Type = super::SccEnc;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -385,40 +386,6 @@ impl ObjectSubclass for SccEnc {
|
||||||
state: Mutex::new(State::default()),
|
state: Mutex::new(State::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Scc Encoder",
|
|
||||||
"Encoder/ClosedCaption",
|
|
||||||
"Encodes SCC Closed Caption Files",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let framerates =
|
|
||||||
gst::List::new(&[&gst::Fraction::new(30000, 1001), &gst::Fraction::new(30, 1)]);
|
|
||||||
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
|
||||||
.field("format", &"raw")
|
|
||||||
.field("framerate", &framerates)
|
|
||||||
.build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-scc").build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for SccEnc {
|
impl ObjectImpl for SccEnc {
|
||||||
|
@ -431,6 +398,50 @@ impl ObjectImpl for SccEnc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for SccEnc {
|
impl ElementImpl for SccEnc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Scc Encoder",
|
||||||
|
"Encoder/ClosedCaption",
|
||||||
|
"Encodes SCC Closed Caption Files",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let framerates =
|
||||||
|
gst::List::new(&[&gst::Fraction::new(30000, 1001), &gst::Fraction::new(30, 1)]);
|
||||||
|
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
||||||
|
.field("format", &"raw")
|
||||||
|
.field("framerate", &framerates)
|
||||||
|
.build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("application/x-scc").build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -1002,6 +1002,7 @@ impl ObjectSubclass for SccParse {
|
||||||
const NAME: &'static str = "RsSccParse";
|
const NAME: &'static str = "RsSccParse";
|
||||||
type Type = super::SccParse;
|
type Type = super::SccParse;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -1064,41 +1065,6 @@ impl ObjectSubclass for SccParse {
|
||||||
state: Mutex::new(State::default()),
|
state: Mutex::new(State::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Scc Parse",
|
|
||||||
"Parser/ClosedCaption",
|
|
||||||
"Parses SCC Closed Caption Files",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
|
||||||
.field("format", &"raw")
|
|
||||||
.field(
|
|
||||||
"framerate",
|
|
||||||
&gst::List::new(&[&gst::Fraction::new(30000, 1001), &gst::Fraction::new(30, 1)]),
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-scc").build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for SccParse {
|
impl ObjectImpl for SccParse {
|
||||||
|
@ -1111,6 +1077,54 @@ impl ObjectImpl for SccParse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for SccParse {
|
impl ElementImpl for SccParse {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Scc Parse",
|
||||||
|
"Parser/ClosedCaption",
|
||||||
|
"Parses SCC Closed Caption Files",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>, Jordan Petridis <jordan@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
||||||
|
.field("format", &"raw")
|
||||||
|
.field(
|
||||||
|
"framerate",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst::Fraction::new(30000, 1001),
|
||||||
|
&gst::Fraction::new(30, 1),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("application/x-scc").build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -95,17 +95,6 @@ const DEFAULT_FPS_D: i32 = 1;
|
||||||
|
|
||||||
const DEFAULT_MODE: Cea608Mode = Cea608Mode::RollUp2;
|
const DEFAULT_MODE: Cea608Mode = Cea608Mode::RollUp2;
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("mode", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Mode",
|
|
||||||
"Which mode to operate in",
|
|
||||||
Cea608Mode::static_type(),
|
|
||||||
DEFAULT_MODE as i32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
mode: Cea608Mode,
|
mode: Cea608Mode,
|
||||||
|
@ -958,6 +947,7 @@ impl ObjectSubclass for TtToCea608 {
|
||||||
const NAME: &'static str = "TtToCea608";
|
const NAME: &'static str = "TtToCea608";
|
||||||
type Type = super::TtToCea608;
|
type Type = super::TtToCea608;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -995,61 +985,24 @@ impl ObjectSubclass for TtToCea608 {
|
||||||
settings: Mutex::new(Settings::default()),
|
settings: Mutex::new(Settings::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"TT to CEA-608",
|
|
||||||
"Generic",
|
|
||||||
"Converts timed text to CEA-608 Closed Captions",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::new_empty();
|
|
||||||
{
|
|
||||||
let caps = caps.get_mut().unwrap();
|
|
||||||
|
|
||||||
let s = gst::Structure::new_empty("text/x-raw");
|
|
||||||
caps.append_structure(s);
|
|
||||||
|
|
||||||
let s = gst::Structure::builder("application/x-json")
|
|
||||||
.field("format", &"cea608")
|
|
||||||
.build();
|
|
||||||
caps.append_structure(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let framerate = gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(1, std::i32::MAX),
|
|
||||||
gst::Fraction::new(std::i32::MAX, 1),
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
|
||||||
.field("format", &"raw")
|
|
||||||
.field("framerate", &framerate)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for TtToCea608 {
|
impl ObjectImpl for TtToCea608 {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::enum_(
|
||||||
|
"mode",
|
||||||
|
"Mode",
|
||||||
|
"Which mode to operate in",
|
||||||
|
Cea608Mode::static_type(),
|
||||||
|
DEFAULT_MODE as i32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -1057,11 +1010,15 @@ impl ObjectImpl for TtToCea608 {
|
||||||
obj.add_pad(&self.srcpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("mode", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"mode" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.mode = value
|
settings.mode = value
|
||||||
.get_some::<Cea608Mode>()
|
.get_some::<Cea608Mode>()
|
||||||
|
@ -1071,11 +1028,9 @@ impl ObjectImpl for TtToCea608 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"mode" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("mode", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.mode.to_value()
|
settings.mode.to_value()
|
||||||
}
|
}
|
||||||
|
@ -1085,6 +1040,66 @@ impl ObjectImpl for TtToCea608 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for TtToCea608 {
|
impl ElementImpl for TtToCea608 {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"TT to CEA-608",
|
||||||
|
"Generic",
|
||||||
|
"Converts timed text to CEA-608 Closed Captions",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let mut caps = gst::Caps::new_empty();
|
||||||
|
{
|
||||||
|
let caps = caps.get_mut().unwrap();
|
||||||
|
|
||||||
|
let s = gst::Structure::new_empty("text/x-raw");
|
||||||
|
caps.append_structure(s);
|
||||||
|
|
||||||
|
let s = gst::Structure::builder("application/x-json")
|
||||||
|
.field("format", &"cea608")
|
||||||
|
.build();
|
||||||
|
caps.append_structure(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let framerate = gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(1, std::i32::MAX),
|
||||||
|
gst::Fraction::new(std::i32::MAX, 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("closedcaption/x-cea-608")
|
||||||
|
.field("format", &"raw")
|
||||||
|
.field("framerate", &framerate)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn change_state(
|
fn change_state(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -37,17 +37,6 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
|
|
||||||
const DEFAULT_MODE: Cea608Mode = Cea608Mode::RollUp2;
|
const DEFAULT_MODE: Cea608Mode = Cea608Mode::RollUp2;
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("mode", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Mode",
|
|
||||||
"Which mode to operate in",
|
|
||||||
Cea608Mode::static_type(),
|
|
||||||
DEFAULT_MODE as i32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
mode: Cea608Mode,
|
mode: Cea608Mode,
|
||||||
|
@ -156,12 +145,54 @@ impl TtToJson {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for TtToJson {}
|
impl ElementImpl for TtToJson {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Timed text to JSON encoder",
|
||||||
|
"Encoder/ClosedCaption",
|
||||||
|
"Encodes Timed Text to JSON",
|
||||||
|
"Mathieu Duponchelle <mathieu@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::builder("text/x-raw")
|
||||||
|
.field("format", &"utf8")
|
||||||
|
.build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("application/x-json").build();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ObjectSubclass for TtToJson {
|
impl ObjectSubclass for TtToJson {
|
||||||
const NAME: &'static str = "RsTtToJson";
|
const NAME: &'static str = "RsTtToJson";
|
||||||
type Type = super::TtToJson;
|
type Type = super::TtToJson;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -195,42 +226,24 @@ impl ObjectSubclass for TtToJson {
|
||||||
settings: Mutex::new(Settings::default()),
|
settings: Mutex::new(Settings::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Timed text to JSON encoder",
|
|
||||||
"Encoder/ClosedCaption",
|
|
||||||
"Encodes Timed Text to JSON",
|
|
||||||
"Mathieu Duponchelle <mathieu@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("text/x-raw")
|
|
||||||
.field("format", &"utf8")
|
|
||||||
.build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("application/x-json").build();
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for TtToJson {
|
impl ObjectImpl for TtToJson {
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::enum_(
|
||||||
|
"mode",
|
||||||
|
"Mode",
|
||||||
|
"Which mode to operate in",
|
||||||
|
Cea608Mode::static_type(),
|
||||||
|
DEFAULT_MODE as i32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn constructed(&self, obj: &Self::Type) {
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
self.parent_constructed(obj);
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
@ -238,11 +251,15 @@ impl ObjectImpl for TtToJson {
|
||||||
obj.add_pad(&self.srcpad).unwrap();
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn set_property(
|
||||||
let prop = &PROPERTIES[id];
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
match *prop {
|
_id: usize,
|
||||||
subclass::Property("mode", ..) => {
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"mode" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.mode = value
|
settings.mode = value
|
||||||
.get_some::<Cea608Mode>()
|
.get_some::<Cea608Mode>()
|
||||||
|
@ -252,11 +269,9 @@ impl ObjectImpl for TtToJson {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"mode" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("mode", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.mode.to_value()
|
settings.mode.to_value()
|
||||||
}
|
}
|
||||||
|
|
|
@ -349,6 +349,7 @@ impl ObjectSubclass for Dav1dDec {
|
||||||
const NAME: &'static str = "RsDav1dDec";
|
const NAME: &'static str = "RsDav1dDec";
|
||||||
type Type = super::Dav1dDec;
|
type Type = super::Dav1dDec;
|
||||||
type ParentType = gst_video::VideoDecoder;
|
type ParentType = gst_video::VideoDecoder;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -364,54 +365,64 @@ impl ObjectSubclass for Dav1dDec {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"Dav1d AV1 Decoder",
|
|
||||||
"Codec/Decoder/Video",
|
|
||||||
"Decode AV1 video streams with dav1d",
|
|
||||||
"Philippe Normand <philn@igalia.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple("video/x-av1", &[]);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &gst::List::from_owned(video_output_formats())),
|
|
||||||
("width", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(0, 1),
|
|
||||||
gst::Fraction::new(i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Dav1dDec {}
|
impl ObjectImpl for Dav1dDec {}
|
||||||
|
|
||||||
impl ElementImpl for Dav1dDec {}
|
impl ElementImpl for Dav1dDec {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"Dav1d AV1 Decoder",
|
||||||
|
"Codec/Decoder/Video",
|
||||||
|
"Decode AV1 video streams with dav1d",
|
||||||
|
"Philippe Normand <philn@igalia.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple("video/x-av1", &[]);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
("format", &gst::List::from_owned(video_output_formats())),
|
||||||
|
("width", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(0, 1),
|
||||||
|
gst::Fraction::new(i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VideoDecoderImpl for Dav1dDec {
|
impl VideoDecoderImpl for Dav1dDec {
|
||||||
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
|
|
@ -124,6 +124,7 @@ impl ObjectSubclass for FlvDemux {
|
||||||
const NAME: &'static str = "RsFlvDemux";
|
const NAME: &'static str = "RsFlvDemux";
|
||||||
type Type = super::FlvDemux;
|
type Type = super::FlvDemux;
|
||||||
type ParentType = gst::Element;
|
type ParentType = gst::Element;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -176,100 +177,6 @@ impl ObjectSubclass for FlvDemux {
|
||||||
flow_combiner: Mutex::new(gst_base::UniqueFlowCombiner::new()),
|
flow_combiner: Mutex::new(gst_base::UniqueFlowCombiner::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"FLV Demuxer",
|
|
||||||
"Codec/Demuxer",
|
|
||||||
"Demuxes FLV Streams",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::new_empty();
|
|
||||||
{
|
|
||||||
let caps = caps.get_mut().unwrap();
|
|
||||||
|
|
||||||
caps.append(
|
|
||||||
gst::Caps::builder("audio/mpeg")
|
|
||||||
.field("mpegversion", &1i32)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
caps.append(
|
|
||||||
gst::Caps::builder("audio/x-raw")
|
|
||||||
.field("layout", &"interleaved")
|
|
||||||
.field("format", &gst::List::new(&[&"U8", &"S16LE"]))
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
caps.append(
|
|
||||||
gst::Caps::builder("audio/x-adpcm")
|
|
||||||
.field("layout", &"swf")
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
caps.append(gst::Caps::builder("audio/x-nellymoser").build());
|
|
||||||
caps.append(gst::Caps::builder("audio/x-alaw").build());
|
|
||||||
caps.append(gst::Caps::builder("audio/x-mulaw").build());
|
|
||||||
caps.append(
|
|
||||||
gst::Caps::builder("audio/mpeg")
|
|
||||||
.field("mpegversion", &4i32)
|
|
||||||
.field("framed", &true)
|
|
||||||
.field("stream-format", &"raw")
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
caps.append(gst::Caps::builder("audio/x-speex").build());
|
|
||||||
}
|
|
||||||
let audiosrc_pad_template = gst::PadTemplate::new(
|
|
||||||
"audio",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Sometimes,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(audiosrc_pad_template);
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::new_empty();
|
|
||||||
{
|
|
||||||
let caps = caps.get_mut().unwrap();
|
|
||||||
|
|
||||||
caps.append(
|
|
||||||
gst::Caps::builder("video/x-flash-video")
|
|
||||||
.field("flvversion", &1i32)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
caps.append(gst::Caps::builder("video/x-flash-screen").build());
|
|
||||||
caps.append(gst::Caps::builder("video/x-vp6-flash").build());
|
|
||||||
caps.append(gst::Caps::builder("video/x-vp6-flash-alpha").build());
|
|
||||||
caps.append(gst::Caps::builder("video/x-flash-screen2").build());
|
|
||||||
caps.append(
|
|
||||||
gst::Caps::builder("video/x-h264")
|
|
||||||
.field("stream-format", &"avc")
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
caps.append(gst::Caps::builder("video/x-h263").build());
|
|
||||||
caps.append(
|
|
||||||
gst::Caps::builder("video/mpeg")
|
|
||||||
.field("mpegversion", &4i32)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let videosrc_pad_template = gst::PadTemplate::new(
|
|
||||||
"video",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Sometimes,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(videosrc_pad_template);
|
|
||||||
|
|
||||||
let caps = gst::Caps::builder("video/x-flv").build();
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for FlvDemux {
|
impl ObjectImpl for FlvDemux {
|
||||||
|
@ -280,7 +187,114 @@ impl ObjectImpl for FlvDemux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for FlvDemux {}
|
impl ElementImpl for FlvDemux {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"FLV Demuxer",
|
||||||
|
"Codec/Demuxer",
|
||||||
|
"Demuxes FLV Streams",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let mut caps = gst::Caps::new_empty();
|
||||||
|
{
|
||||||
|
let caps = caps.get_mut().unwrap();
|
||||||
|
|
||||||
|
caps.append(
|
||||||
|
gst::Caps::builder("audio/mpeg")
|
||||||
|
.field("mpegversion", &1i32)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
caps.append(
|
||||||
|
gst::Caps::builder("audio/x-raw")
|
||||||
|
.field("layout", &"interleaved")
|
||||||
|
.field("format", &gst::List::new(&[&"U8", &"S16LE"]))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
caps.append(
|
||||||
|
gst::Caps::builder("audio/x-adpcm")
|
||||||
|
.field("layout", &"swf")
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
caps.append(gst::Caps::builder("audio/x-nellymoser").build());
|
||||||
|
caps.append(gst::Caps::builder("audio/x-alaw").build());
|
||||||
|
caps.append(gst::Caps::builder("audio/x-mulaw").build());
|
||||||
|
caps.append(
|
||||||
|
gst::Caps::builder("audio/mpeg")
|
||||||
|
.field("mpegversion", &4i32)
|
||||||
|
.field("framed", &true)
|
||||||
|
.field("stream-format", &"raw")
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
caps.append(gst::Caps::builder("audio/x-speex").build());
|
||||||
|
}
|
||||||
|
let audiosrc_pad_template = gst::PadTemplate::new(
|
||||||
|
"audio",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Sometimes,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut caps = gst::Caps::new_empty();
|
||||||
|
{
|
||||||
|
let caps = caps.get_mut().unwrap();
|
||||||
|
|
||||||
|
caps.append(
|
||||||
|
gst::Caps::builder("video/x-flash-video")
|
||||||
|
.field("flvversion", &1i32)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
caps.append(gst::Caps::builder("video/x-flash-screen").build());
|
||||||
|
caps.append(gst::Caps::builder("video/x-vp6-flash").build());
|
||||||
|
caps.append(gst::Caps::builder("video/x-vp6-flash-alpha").build());
|
||||||
|
caps.append(gst::Caps::builder("video/x-flash-screen2").build());
|
||||||
|
caps.append(
|
||||||
|
gst::Caps::builder("video/x-h264")
|
||||||
|
.field("stream-format", &"avc")
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
caps.append(gst::Caps::builder("video/x-h263").build());
|
||||||
|
caps.append(
|
||||||
|
gst::Caps::builder("video/mpeg")
|
||||||
|
.field("mpegversion", &4i32)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let videosrc_pad_template = gst::PadTemplate::new(
|
||||||
|
"video",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Sometimes,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let caps = gst::Caps::builder("video/x-flv").build();
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
audiosrc_pad_template,
|
||||||
|
videosrc_pad_template,
|
||||||
|
sink_pad_template,
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FlvDemux {
|
impl FlvDemux {
|
||||||
fn sink_activate(
|
fn sink_activate(
|
||||||
|
|
|
@ -81,18 +81,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("repeat", |name| {
|
|
||||||
glib::ParamSpec::int(
|
|
||||||
name,
|
|
||||||
"Repeat",
|
|
||||||
"Repeat (-1 to loop forever, 0 .. n finite repetitions)",
|
|
||||||
-1,
|
|
||||||
std::u16::MAX as i32,
|
|
||||||
DEFAULT_REPEAT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
})];
|
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
video_info: gst_video::VideoInfo,
|
video_info: gst_video::VideoInfo,
|
||||||
cache: Arc<CacheBuffer>,
|
cache: Arc<CacheBuffer>,
|
||||||
|
@ -146,6 +134,7 @@ impl ObjectSubclass for GifEnc {
|
||||||
const NAME: &'static str = "GifEnc";
|
const NAME: &'static str = "GifEnc";
|
||||||
type Type = super::GifEnc;
|
type Type = super::GifEnc;
|
||||||
type ParentType = gst_video::VideoEncoder;
|
type ParentType = gst_video::VideoEncoder;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -157,69 +146,34 @@ impl ObjectSubclass for GifEnc {
|
||||||
settings: Mutex::new(Default::default()),
|
settings: Mutex::new(Default::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"GIF encoder",
|
|
||||||
"Encoder/Video",
|
|
||||||
"GIF encoder",
|
|
||||||
"Markus Ebner <info@ebner-markus.de>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[
|
|
||||||
&gst_video::VideoFormat::Rgb.to_str(),
|
|
||||||
&gst_video::VideoFormat::Rgba.to_str(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
("width", &gst::IntRange::<i32>::new(1, std::u16::MAX as i32)),
|
|
||||||
(
|
|
||||||
"height",
|
|
||||||
&gst::IntRange::<i32>::new(1, std::u16::MAX as i32),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(1, 1),
|
|
||||||
// frame-delay timing in gif is a multiple of 10ms -> max 100fps
|
|
||||||
gst::Fraction::new(100, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple("image/gif", &[]);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for GifEnc {
|
impl ObjectImpl for GifEnc {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![glib::ParamSpec::int(
|
||||||
|
"repeat",
|
||||||
|
"Repeat",
|
||||||
|
"Repeat (-1 to loop forever, 0 .. n finite repetitions)",
|
||||||
|
-1,
|
||||||
|
std::u16::MAX as i32,
|
||||||
|
DEFAULT_REPEAT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
)]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("repeat", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"repeat" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.repeat = value.get_some().expect("type checked upstream");
|
settings.repeat = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
|
@ -227,11 +181,9 @@ impl ObjectImpl for GifEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"repeat" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("repeat", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.repeat.to_value()
|
settings.repeat.to_value()
|
||||||
}
|
}
|
||||||
|
@ -240,7 +192,70 @@ impl ObjectImpl for GifEnc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for GifEnc {}
|
impl ElementImpl for GifEnc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"GIF encoder",
|
||||||
|
"Encoder/Video",
|
||||||
|
"GIF encoder",
|
||||||
|
"Markus Ebner <info@ebner-markus.de>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst_video::VideoFormat::Rgb.to_str(),
|
||||||
|
&gst_video::VideoFormat::Rgba.to_str(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
("width", &gst::IntRange::<i32>::new(1, std::u16::MAX as i32)),
|
||||||
|
(
|
||||||
|
"height",
|
||||||
|
&gst::IntRange::<i32>::new(1, std::u16::MAX as i32),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(1, 1),
|
||||||
|
// frame-delay timing in gif is a multiple of 10ms -> max 100fps
|
||||||
|
gst::Fraction::new(100, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple("image/gif", &[]);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VideoEncoderImpl for GifEnc {
|
impl VideoEncoderImpl for GifEnc {
|
||||||
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
|
|
@ -55,76 +55,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata for the properties
|
|
||||||
static PROPERTIES: [subclass::Property; 6] = [
|
|
||||||
subclass::Property("hue-ref", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Hue reference",
|
|
||||||
"Hue reference in degrees",
|
|
||||||
f32::MIN,
|
|
||||||
f32::MAX,
|
|
||||||
DEFAULT_HUE_REF,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("hue-var", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Hue variation",
|
|
||||||
"Allowed hue variation from the reference hue angle, in degrees",
|
|
||||||
0.0,
|
|
||||||
180.0,
|
|
||||||
DEFAULT_HUE_VAR,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("saturation-ref", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Saturation reference",
|
|
||||||
"Reference saturation value",
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
DEFAULT_SATURATION_REF,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("saturation-var", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Saturation variation",
|
|
||||||
"Allowed saturation variation from the reference value",
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
DEFAULT_SATURATION_VAR,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("value-ref", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Value reference",
|
|
||||||
"Reference value value",
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
DEFAULT_VALUE_REF,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("value-var", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Value variation",
|
|
||||||
"Allowed value variation from the reference value",
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
DEFAULT_VALUE_VAR,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Stream-specific state, i.e. video format configuration
|
// Stream-specific state, i.e. video format configuration
|
||||||
struct State {
|
struct State {
|
||||||
in_info: gst_video::VideoInfo,
|
in_info: gst_video::VideoInfo,
|
||||||
|
@ -149,6 +79,7 @@ impl ObjectSubclass for HsvDetector {
|
||||||
const NAME: &'static str = "HsvDetector";
|
const NAME: &'static str = "HsvDetector";
|
||||||
type Type = super::HsvDetector;
|
type Type = super::HsvDetector;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -162,90 +93,81 @@ impl ObjectSubclass for HsvDetector {
|
||||||
state: AtomicRefCell::new(None),
|
state: AtomicRefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"HSV detector",
|
|
||||||
"Filter/Effect/Converter/Video",
|
|
||||||
"Works within the HSV colorspace to mark positive pixels",
|
|
||||||
"Julien Bardagi <julien.bardagi@gmail.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
// src pad capabilities
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[&gst_video::VideoFormat::Rgba.to_str()]),
|
|
||||||
),
|
|
||||||
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(0, 1),
|
|
||||||
gst::Fraction::new(i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
// sink pad capabilities
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]),
|
|
||||||
),
|
|
||||||
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(0, 1),
|
|
||||||
gst::Fraction::new(i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
// Install all our properties
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.configure(
|
|
||||||
gst_base::subclass::BaseTransformMode::NeverInPlace,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for HsvDetector {
|
impl ObjectImpl for HsvDetector {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"hue-ref",
|
||||||
|
"Hue reference",
|
||||||
|
"Hue reference in degrees",
|
||||||
|
f32::MIN,
|
||||||
|
f32::MAX,
|
||||||
|
DEFAULT_HUE_REF,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"hue-var",
|
||||||
|
"Hue variation",
|
||||||
|
"Allowed hue variation from the reference hue angle, in degrees",
|
||||||
|
0.0,
|
||||||
|
180.0,
|
||||||
|
DEFAULT_HUE_VAR,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"saturation-ref",
|
||||||
|
"Saturation reference",
|
||||||
|
"Reference saturation value",
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_SATURATION_REF,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"saturation-var",
|
||||||
|
"Saturation variation",
|
||||||
|
"Allowed saturation variation from the reference value",
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_SATURATION_VAR,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"value-ref",
|
||||||
|
"Value reference",
|
||||||
|
"Reference value value",
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_VALUE_REF,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"value-var",
|
||||||
|
"Value variation",
|
||||||
|
"Allowed value variation from the reference value",
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
DEFAULT_VALUE_VAR,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("hue-ref", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"hue-ref" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let hue_ref = value.get_some().expect("type checked upstream");
|
let hue_ref = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -257,7 +179,7 @@ impl ObjectImpl for HsvDetector {
|
||||||
);
|
);
|
||||||
settings.hue_ref = hue_ref;
|
settings.hue_ref = hue_ref;
|
||||||
}
|
}
|
||||||
subclass::Property("hue-var", ..) => {
|
"hue-var" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let hue_var = value.get_some().expect("type checked upstream");
|
let hue_var = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -269,7 +191,7 @@ impl ObjectImpl for HsvDetector {
|
||||||
);
|
);
|
||||||
settings.hue_var = hue_var;
|
settings.hue_var = hue_var;
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-ref", ..) => {
|
"saturation-ref" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let saturation_ref = value.get_some().expect("type checked upstream");
|
let saturation_ref = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -281,7 +203,7 @@ impl ObjectImpl for HsvDetector {
|
||||||
);
|
);
|
||||||
settings.saturation_ref = saturation_ref;
|
settings.saturation_ref = saturation_ref;
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-var", ..) => {
|
"saturation-var" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let saturation_var = value.get_some().expect("type checked upstream");
|
let saturation_var = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -293,7 +215,7 @@ impl ObjectImpl for HsvDetector {
|
||||||
);
|
);
|
||||||
settings.saturation_var = saturation_var;
|
settings.saturation_var = saturation_var;
|
||||||
}
|
}
|
||||||
subclass::Property("value-ref", ..) => {
|
"value-ref" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let value_ref = value.get_some().expect("type checked upstream");
|
let value_ref = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -305,7 +227,7 @@ impl ObjectImpl for HsvDetector {
|
||||||
);
|
);
|
||||||
settings.value_ref = value_ref;
|
settings.value_ref = value_ref;
|
||||||
}
|
}
|
||||||
subclass::Property("value-var", ..) => {
|
"value-var" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let value_var = value.get_some().expect("type checked upstream");
|
let value_var = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -323,31 +245,29 @@ impl ObjectImpl for HsvDetector {
|
||||||
|
|
||||||
// Called whenever a value of a property is read. It can be called
|
// Called whenever a value of a property is read. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"hue-ref" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("hue-ref", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.hue_ref.to_value()
|
settings.hue_ref.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("hue-var", ..) => {
|
"hue-var" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.hue_var.to_value()
|
settings.hue_var.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-ref", ..) => {
|
"saturation-ref" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.saturation_ref.to_value()
|
settings.saturation_ref.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-var", ..) => {
|
"saturation-var" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.saturation_var.to_value()
|
settings.saturation_var.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("value-ref", ..) => {
|
"value-ref" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.value_ref.to_value()
|
settings.value_ref.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("value-var", ..) => {
|
"value-var" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.value_var.to_value()
|
settings.value_var.to_value()
|
||||||
}
|
}
|
||||||
|
@ -356,9 +276,90 @@ impl ObjectImpl for HsvDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for HsvDetector {}
|
impl ElementImpl for HsvDetector {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"HSV detector",
|
||||||
|
"Filter/Effect/Converter/Video",
|
||||||
|
"Works within the HSV colorspace to mark positive pixels",
|
||||||
|
"Julien Bardagi <julien.bardagi@gmail.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[&gst_video::VideoFormat::Rgba.to_str()]),
|
||||||
|
),
|
||||||
|
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(0, 1),
|
||||||
|
gst::Fraction::new(i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// sink pad capabilities
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]),
|
||||||
|
),
|
||||||
|
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(0, 1),
|
||||||
|
gst::Fraction::new(i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseTransformImpl for HsvDetector {
|
impl BaseTransformImpl for HsvDetector {
|
||||||
|
const MODE: gst_base::subclass::BaseTransformMode =
|
||||||
|
gst_base::subclass::BaseTransformMode::NeverInPlace;
|
||||||
|
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||||
|
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||||
|
|
||||||
fn transform_caps(
|
fn transform_caps(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
element: &Self::Type,
|
||||||
|
|
|
@ -51,65 +51,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata for the properties
|
|
||||||
static PROPERTIES: [subclass::Property; 5] = [
|
|
||||||
subclass::Property("hue-shift", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Hue shift",
|
|
||||||
"Hue shifting in degrees",
|
|
||||||
f32::MIN,
|
|
||||||
f32::MAX,
|
|
||||||
DEFAULT_HUE_SHIFT,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("saturation-mul", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Saturation multiplier",
|
|
||||||
"Saturation multiplier to apply to the saturation value (before offset)",
|
|
||||||
f32::MIN,
|
|
||||||
f32::MAX,
|
|
||||||
DEFAULT_SATURATION_MUL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("saturation-off", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Saturation offset",
|
|
||||||
"Saturation offset to add to the saturation value (after multiplier)",
|
|
||||||
f32::MIN,
|
|
||||||
f32::MAX,
|
|
||||||
DEFAULT_SATURATION_OFF,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("value-mul", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Value multiplier",
|
|
||||||
"Value multiplier to apply to the value (before offset)",
|
|
||||||
f32::MIN,
|
|
||||||
f32::MAX,
|
|
||||||
DEFAULT_VALUE_MUL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("value-off", |name| {
|
|
||||||
glib::ParamSpec::float(
|
|
||||||
name,
|
|
||||||
"Value offset",
|
|
||||||
"Value offset to add to the value (after multiplier)",
|
|
||||||
f32::MIN,
|
|
||||||
f32::MAX,
|
|
||||||
DEFAULT_VALUE_OFF,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Stream-specific state, i.e. video format configuration
|
// Stream-specific state, i.e. video format configuration
|
||||||
struct State {
|
struct State {
|
||||||
info: gst_video::VideoInfo,
|
info: gst_video::VideoInfo,
|
||||||
|
@ -133,6 +74,7 @@ impl ObjectSubclass for HsvFilter {
|
||||||
const NAME: &'static str = "HsvFilter";
|
const NAME: &'static str = "HsvFilter";
|
||||||
type Type = super::HsvFilter;
|
type Type = super::HsvFilter;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_base::BaseTransform;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -146,70 +88,72 @@ impl ObjectSubclass for HsvFilter {
|
||||||
state: AtomicRefCell::new(None),
|
state: AtomicRefCell::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"HSV filter",
|
|
||||||
"Filter/Effect/Converter/Video",
|
|
||||||
"Works within the HSV colorspace to apply tranformations to incoming frames",
|
|
||||||
"Julien Bardagi <julien.bardagi@gmail.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
// src pad capabilities
|
|
||||||
let caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]),
|
|
||||||
),
|
|
||||||
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(0, 1),
|
|
||||||
gst::Fraction::new(i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
// Install all our properties
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
|
|
||||||
klass.configure(
|
|
||||||
gst_base::subclass::BaseTransformMode::AlwaysInPlace,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for HsvFilter {
|
impl ObjectImpl for HsvFilter {
|
||||||
fn set_property(&self, obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"hue-shift",
|
||||||
|
"Hue shift",
|
||||||
|
"Hue shifting in degrees",
|
||||||
|
f32::MIN,
|
||||||
|
f32::MAX,
|
||||||
|
DEFAULT_HUE_SHIFT,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"saturation-mul",
|
||||||
|
"Saturation multiplier",
|
||||||
|
"Saturation multiplier to apply to the saturation value (before offset)",
|
||||||
|
f32::MIN,
|
||||||
|
f32::MAX,
|
||||||
|
DEFAULT_SATURATION_MUL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"saturation-off",
|
||||||
|
"Saturation offset",
|
||||||
|
"Saturation offset to add to the saturation value (after multiplier)",
|
||||||
|
f32::MIN,
|
||||||
|
f32::MAX,
|
||||||
|
DEFAULT_SATURATION_OFF,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"value-mul",
|
||||||
|
"Value multiplier",
|
||||||
|
"Value multiplier to apply to the value (before offset)",
|
||||||
|
f32::MIN,
|
||||||
|
f32::MAX,
|
||||||
|
DEFAULT_VALUE_MUL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::float(
|
||||||
|
"value-off",
|
||||||
|
"Value offset",
|
||||||
|
"Value offset to add to the value (after multiplier)",
|
||||||
|
f32::MIN,
|
||||||
|
f32::MAX,
|
||||||
|
DEFAULT_VALUE_OFF,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("hue-shift", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"hue-shift" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let hue_shift = value.get_some().expect("type checked upstream");
|
let hue_shift = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -221,7 +165,7 @@ impl ObjectImpl for HsvFilter {
|
||||||
);
|
);
|
||||||
settings.hue_shift = hue_shift;
|
settings.hue_shift = hue_shift;
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-mul", ..) => {
|
"saturation-mul" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let saturation_mul = value.get_some().expect("type checked upstream");
|
let saturation_mul = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -233,7 +177,7 @@ impl ObjectImpl for HsvFilter {
|
||||||
);
|
);
|
||||||
settings.saturation_mul = saturation_mul;
|
settings.saturation_mul = saturation_mul;
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-off", ..) => {
|
"saturation-off" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let saturation_off = value.get_some().expect("type checked upstream");
|
let saturation_off = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -245,7 +189,7 @@ impl ObjectImpl for HsvFilter {
|
||||||
);
|
);
|
||||||
settings.saturation_off = saturation_off;
|
settings.saturation_off = saturation_off;
|
||||||
}
|
}
|
||||||
subclass::Property("value-mul", ..) => {
|
"value-mul" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let value_mul = value.get_some().expect("type checked upstream");
|
let value_mul = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -257,7 +201,7 @@ impl ObjectImpl for HsvFilter {
|
||||||
);
|
);
|
||||||
settings.value_mul = value_mul;
|
settings.value_mul = value_mul;
|
||||||
}
|
}
|
||||||
subclass::Property("value-off", ..) => {
|
"value-off" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let value_off = value.get_some().expect("type checked upstream");
|
let value_off = value.get_some().expect("type checked upstream");
|
||||||
gst_info!(
|
gst_info!(
|
||||||
|
@ -275,27 +219,25 @@ impl ObjectImpl for HsvFilter {
|
||||||
|
|
||||||
// Called whenever a value of a property is read. It can be called
|
// Called whenever a value of a property is read. It can be called
|
||||||
// at any time from any thread.
|
// at any time from any thread.
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"hue-shift" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("hue-shift", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.hue_shift.to_value()
|
settings.hue_shift.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-mul", ..) => {
|
"saturation-mul" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.saturation_mul.to_value()
|
settings.saturation_mul.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("saturation-off", ..) => {
|
"saturation-off" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.saturation_off.to_value()
|
settings.saturation_off.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("value-mul", ..) => {
|
"value-mul" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.value_mul.to_value()
|
settings.value_mul.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("value-off", ..) => {
|
"value-off" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.value_off.to_value()
|
settings.value_off.to_value()
|
||||||
}
|
}
|
||||||
|
@ -304,9 +246,71 @@ impl ObjectImpl for HsvFilter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for HsvFilter {}
|
impl ElementImpl for HsvFilter {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"HSV filter",
|
||||||
|
"Filter/Effect/Converter/Video",
|
||||||
|
"Works within the HSV colorspace to apply tranformations to incoming frames",
|
||||||
|
"Julien Bardagi <julien.bardagi@gmail.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
// src pad capabilities
|
||||||
|
let caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[&gst_video::VideoFormat::Rgbx.to_str()]),
|
||||||
|
),
|
||||||
|
("width", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(0, i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(0, 1),
|
||||||
|
gst::Fraction::new(i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseTransformImpl for HsvFilter {
|
impl BaseTransformImpl for HsvFilter {
|
||||||
|
const MODE: gst_base::subclass::BaseTransformMode =
|
||||||
|
gst_base::subclass::BaseTransformMode::AlwaysInPlace;
|
||||||
|
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||||
|
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||||
|
|
||||||
fn get_unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> {
|
fn get_unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> {
|
||||||
gst_video::VideoInfo::from_caps(caps)
|
gst_video::VideoInfo::from_caps(caps)
|
||||||
.map(|info| info.size())
|
.map(|info| info.size())
|
||||||
|
|
|
@ -60,117 +60,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 10] = [
|
|
||||||
subclass::Property("speed-preset", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Speed Preset",
|
|
||||||
"Speed preset (10 fastest, 0 slowest)",
|
|
||||||
0,
|
|
||||||
10,
|
|
||||||
DEFAULT_SPEED_PRESET,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("low-latency", |name| {
|
|
||||||
glib::ParamSpec::boolean(
|
|
||||||
name,
|
|
||||||
"Low Latency",
|
|
||||||
"Low Latency",
|
|
||||||
DEFAULT_LOW_LATENCY,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("min-key-frame-interval", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Min Key Frame Interval",
|
|
||||||
"Min Key Frame Interval",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
DEFAULT_MIN_KEY_FRAME_INTERVAL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("max-key-frame-interval", |name| {
|
|
||||||
glib::ParamSpec::uint64(
|
|
||||||
name,
|
|
||||||
"Max Key Frame Interval",
|
|
||||||
"Max Key Frame Interval",
|
|
||||||
0,
|
|
||||||
std::u64::MAX,
|
|
||||||
DEFAULT_MAX_KEY_FRAME_INTERVAL,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("bitrate", |name| {
|
|
||||||
glib::ParamSpec::int(
|
|
||||||
name,
|
|
||||||
"Bitrate",
|
|
||||||
"Bitrate",
|
|
||||||
0,
|
|
||||||
std::i32::MAX,
|
|
||||||
DEFAULT_BITRATE,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("quantizer", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Quantizer",
|
|
||||||
"Quantizer",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_QUANTIZER as u32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("tile-cols", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Tile Cols",
|
|
||||||
"Tile Cols",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_TILE_COLS as u32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("tile-rows", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Tile Rows",
|
|
||||||
"Tile Rows",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_TILE_ROWS as u32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("tiles", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Tiles",
|
|
||||||
"Tiles",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_TILES as u32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("threads", |name| {
|
|
||||||
glib::ParamSpec::uint(
|
|
||||||
name,
|
|
||||||
"Threads",
|
|
||||||
"Threads",
|
|
||||||
0,
|
|
||||||
std::u32::MAX,
|
|
||||||
DEFAULT_THREADS as u32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
enum Context {
|
enum Context {
|
||||||
Eight(rav1e::Context<u8>),
|
Eight(rav1e::Context<u8>),
|
||||||
Sixteen(rav1e::Context<u16>),
|
Sixteen(rav1e::Context<u16>),
|
||||||
|
@ -319,6 +208,7 @@ impl ObjectSubclass for Rav1Enc {
|
||||||
const NAME: &'static str = "Rav1Enc";
|
const NAME: &'static str = "Rav1Enc";
|
||||||
type Type = super::Rav1Enc;
|
type Type = super::Rav1Enc;
|
||||||
type ParentType = gst_video::VideoEncoder;
|
type ParentType = gst_video::VideoEncoder;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -330,112 +220,154 @@ impl ObjectSubclass for Rav1Enc {
|
||||||
settings: Mutex::new(Default::default()),
|
settings: Mutex::new(Default::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"rav1e AV1 encoder",
|
|
||||||
"Encoder/Video",
|
|
||||||
"rav1e AV1 encoder",
|
|
||||||
"Sebastian Dröge <sebastian@centricular.com>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[
|
|
||||||
&gst_video::VideoFormat::I420.to_str(),
|
|
||||||
&gst_video::VideoFormat::Y42b.to_str(),
|
|
||||||
&gst_video::VideoFormat::Y444.to_str(),
|
|
||||||
&gst_video::VideoFormat::I42010le.to_str(),
|
|
||||||
&gst_video::VideoFormat::I42210le.to_str(),
|
|
||||||
&gst_video::VideoFormat::Y44410le.to_str(),
|
|
||||||
&gst_video::VideoFormat::I42012le.to_str(),
|
|
||||||
&gst_video::VideoFormat::I42212le.to_str(),
|
|
||||||
&gst_video::VideoFormat::Y44412le.to_str(),
|
|
||||||
// &gst_video::VideoFormat::Gray8.to_str(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(0, 1),
|
|
||||||
gst::Fraction::new(std::i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple("video/x-av1", &[]);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for Rav1Enc {
|
impl ObjectImpl for Rav1Enc {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"speed-preset",
|
||||||
|
"Speed Preset",
|
||||||
|
"Speed preset (10 fastest, 0 slowest)",
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
DEFAULT_SPEED_PRESET,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::boolean(
|
||||||
|
"low-latency",
|
||||||
|
"Low Latency",
|
||||||
|
"Low Latency",
|
||||||
|
DEFAULT_LOW_LATENCY,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"min-key-frame-interval",
|
||||||
|
"Min Key Frame Interval",
|
||||||
|
"Min Key Frame Interval",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
DEFAULT_MIN_KEY_FRAME_INTERVAL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint64(
|
||||||
|
"max-key-frame-interval",
|
||||||
|
"Max Key Frame Interval",
|
||||||
|
"Max Key Frame Interval",
|
||||||
|
0,
|
||||||
|
std::u64::MAX,
|
||||||
|
DEFAULT_MAX_KEY_FRAME_INTERVAL,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::int(
|
||||||
|
"bitrate",
|
||||||
|
"Bitrate",
|
||||||
|
"Bitrate",
|
||||||
|
0,
|
||||||
|
std::i32::MAX,
|
||||||
|
DEFAULT_BITRATE,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"quantizer",
|
||||||
|
"Quantizer",
|
||||||
|
"Quantizer",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_QUANTIZER as u32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"tile-cols",
|
||||||
|
"Tile Cols",
|
||||||
|
"Tile Cols",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_TILE_COLS as u32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"tile-rows",
|
||||||
|
"Tile Rows",
|
||||||
|
"Tile Rows",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_TILE_ROWS as u32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"tiles",
|
||||||
|
"Tiles",
|
||||||
|
"Tiles",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_TILES as u32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::uint(
|
||||||
|
"threads",
|
||||||
|
"Threads",
|
||||||
|
"Threads",
|
||||||
|
0,
|
||||||
|
std::u32::MAX,
|
||||||
|
DEFAULT_THREADS as u32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("speed-preset", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"speed-preset" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.speed_preset = value.get_some().expect("type checked upstream");
|
settings.speed_preset = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("low-latency", ..) => {
|
"low-latency" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.low_latency = value.get_some().expect("type checked upstream");
|
settings.low_latency = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("min-key-frame-interval", ..) => {
|
"min-key-frame-interval" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.min_key_frame_interval = value.get_some().expect("type checked upstream");
|
settings.min_key_frame_interval = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("max-key-frame-interval", ..) => {
|
"max-key-frame-interval" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.max_key_frame_interval = value.get_some().expect("type checked upstream");
|
settings.max_key_frame_interval = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("bitrate", ..) => {
|
"bitrate" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.bitrate = value.get_some().expect("type checked upstream");
|
settings.bitrate = value.get_some().expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("quantizer", ..) => {
|
"quantizer" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.quantizer =
|
settings.quantizer =
|
||||||
value.get_some::<u32>().expect("type checked upstream") as usize;
|
value.get_some::<u32>().expect("type checked upstream") as usize;
|
||||||
}
|
}
|
||||||
subclass::Property("tile-cols", ..) => {
|
"tile-cols" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.tile_cols =
|
settings.tile_cols =
|
||||||
value.get_some::<u32>().expect("type checked upstream") as usize;
|
value.get_some::<u32>().expect("type checked upstream") as usize;
|
||||||
}
|
}
|
||||||
subclass::Property("tile-rows", ..) => {
|
"tile-rows" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.tile_rows =
|
settings.tile_rows =
|
||||||
value.get_some::<u32>().expect("type checked upstream") as usize;
|
value.get_some::<u32>().expect("type checked upstream") as usize;
|
||||||
}
|
}
|
||||||
subclass::Property("tiles", ..) => {
|
"tiles" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.tiles = value.get_some::<u32>().expect("type checked upstream") as usize;
|
settings.tiles = value.get_some::<u32>().expect("type checked upstream") as usize;
|
||||||
}
|
}
|
||||||
subclass::Property("threads", ..) => {
|
"threads" => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.threads = value.get_some::<u32>().expect("type checked upstream") as usize;
|
settings.threads = value.get_some::<u32>().expect("type checked upstream") as usize;
|
||||||
}
|
}
|
||||||
|
@ -443,47 +375,45 @@ impl ObjectImpl for Rav1Enc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"speed-preset" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("speed-preset", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.speed_preset.to_value()
|
settings.speed_preset.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("low-latency", ..) => {
|
"low-latency" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.low_latency.to_value()
|
settings.low_latency.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("min-key-frame-interval", ..) => {
|
"min-key-frame-interval" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.min_key_frame_interval.to_value()
|
settings.min_key_frame_interval.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("max-key-frame-interval", ..) => {
|
"max-key-frame-interval" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.max_key_frame_interval.to_value()
|
settings.max_key_frame_interval.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("bitrate", ..) => {
|
"bitrate" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.bitrate.to_value()
|
settings.bitrate.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("quantizer", ..) => {
|
"quantizer" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
(settings.quantizer as u32).to_value()
|
(settings.quantizer as u32).to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("tile-cols", ..) => {
|
"tile-cols" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
(settings.tile_cols as u32).to_value()
|
(settings.tile_cols as u32).to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("tile-rows", ..) => {
|
"tile-rows" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
(settings.tile_rows as u32).to_value()
|
(settings.tile_rows as u32).to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("tiles", ..) => {
|
"tiles" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
(settings.tiles as u32).to_value()
|
(settings.tiles as u32).to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("threads", ..) => {
|
"threads" => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
(settings.threads as u32).to_value()
|
(settings.threads as u32).to_value()
|
||||||
}
|
}
|
||||||
|
@ -492,7 +422,74 @@ impl ObjectImpl for Rav1Enc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for Rav1Enc {}
|
impl ElementImpl for Rav1Enc {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"rav1e AV1 encoder",
|
||||||
|
"Encoder/Video",
|
||||||
|
"rav1e AV1 encoder",
|
||||||
|
"Sebastian Dröge <sebastian@centricular.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst_video::VideoFormat::I420.to_str(),
|
||||||
|
&gst_video::VideoFormat::Y42b.to_str(),
|
||||||
|
&gst_video::VideoFormat::Y444.to_str(),
|
||||||
|
&gst_video::VideoFormat::I42010le.to_str(),
|
||||||
|
&gst_video::VideoFormat::I42210le.to_str(),
|
||||||
|
&gst_video::VideoFormat::Y44410le.to_str(),
|
||||||
|
&gst_video::VideoFormat::I42012le.to_str(),
|
||||||
|
&gst_video::VideoFormat::I42212le.to_str(),
|
||||||
|
&gst_video::VideoFormat::Y44412le.to_str(),
|
||||||
|
// &gst_video::VideoFormat::Gray8.to_str(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(0, 1),
|
||||||
|
gst::Fraction::new(std::i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple("video/x-av1", &[]);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template, sink_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VideoEncoderImpl for Rav1Enc {
|
impl VideoEncoderImpl for Rav1Enc {
|
||||||
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
|
|
@ -100,29 +100,6 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 2] = [
|
|
||||||
subclass::Property("compression-level", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Compression level",
|
|
||||||
"Selects the compression algorithm to use",
|
|
||||||
CompressionLevel::static_type(),
|
|
||||||
DEFAULT_COMPRESSION_LEVEL as i32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("filter", |name| {
|
|
||||||
glib::ParamSpec::enum_(
|
|
||||||
name,
|
|
||||||
"Filter",
|
|
||||||
"Selects the filter type to applied",
|
|
||||||
FilterType::static_type(),
|
|
||||||
DEFAULT_FILTER_TYPE as i32,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
video_info: gst_video::VideoInfo,
|
video_info: gst_video::VideoInfo,
|
||||||
cache: Arc<CacheBuffer>,
|
cache: Arc<CacheBuffer>,
|
||||||
|
@ -192,6 +169,7 @@ impl ObjectSubclass for PngEncoder {
|
||||||
const NAME: &'static str = "PngEncoder";
|
const NAME: &'static str = "PngEncoder";
|
||||||
type Type = super::PngEncoder;
|
type Type = super::PngEncoder;
|
||||||
type ParentType = gst_video::VideoEncoder;
|
type ParentType = gst_video::VideoEncoder;
|
||||||
|
type Interfaces = ();
|
||||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||||
type Class = subclass::simple::ClassStruct<Self>;
|
type Class = subclass::simple::ClassStruct<Self>;
|
||||||
|
|
||||||
|
@ -203,72 +181,49 @@ impl ObjectSubclass for PngEncoder {
|
||||||
settings: Mutex::new(Default::default()),
|
settings: Mutex::new(Default::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut Self::Class) {
|
|
||||||
klass.set_metadata(
|
|
||||||
"PNG encoder",
|
|
||||||
"Encoder/Video",
|
|
||||||
"PNG encoder",
|
|
||||||
"Natanael Mojica <neithanmo@gmail>",
|
|
||||||
);
|
|
||||||
|
|
||||||
let sink_caps = gst::Caps::new_simple(
|
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
(
|
|
||||||
"format",
|
|
||||||
&gst::List::new(&[
|
|
||||||
&gst_video::VideoFormat::Gray8.to_str(),
|
|
||||||
&gst_video::VideoFormat::Gray16Be.to_str(),
|
|
||||||
&gst_video::VideoFormat::Rgb.to_str(),
|
|
||||||
&gst_video::VideoFormat::Rgba.to_str(),
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&gst::FractionRange::new(
|
|
||||||
gst::Fraction::new(1, 1),
|
|
||||||
gst::Fraction::new(std::i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let sink_pad_template = gst::PadTemplate::new(
|
|
||||||
"sink",
|
|
||||||
gst::PadDirection::Sink,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&sink_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(sink_pad_template);
|
|
||||||
|
|
||||||
let src_caps = gst::Caps::new_simple("image/png", &[]);
|
|
||||||
let src_pad_template = gst::PadTemplate::new(
|
|
||||||
"src",
|
|
||||||
gst::PadDirection::Src,
|
|
||||||
gst::PadPresence::Always,
|
|
||||||
&src_caps,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
klass.add_pad_template(src_pad_template);
|
|
||||||
klass.install_properties(&PROPERTIES);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for PngEncoder {
|
impl ObjectImpl for PngEncoder {
|
||||||
fn set_property(&self, _obj: &Self::Type, id: usize, value: &glib::Value) {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
let prop = &PROPERTIES[id];
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpec::enum_(
|
||||||
|
"compression-level",
|
||||||
|
"Compression level",
|
||||||
|
"Selects the compression algorithm to use",
|
||||||
|
CompressionLevel::static_type(),
|
||||||
|
DEFAULT_COMPRESSION_LEVEL as i32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
glib::ParamSpec::enum_(
|
||||||
|
"filter",
|
||||||
|
"Filter",
|
||||||
|
"Selects the filter type to applied",
|
||||||
|
FilterType::static_type(),
|
||||||
|
DEFAULT_FILTER_TYPE as i32,
|
||||||
|
glib::ParamFlags::READWRITE,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
match *prop {
|
PROPERTIES.as_ref()
|
||||||
subclass::Property("compression-level", ..) => {
|
}
|
||||||
|
|
||||||
|
fn set_property(
|
||||||
|
&self,
|
||||||
|
_obj: &Self::Type,
|
||||||
|
_id: usize,
|
||||||
|
value: &glib::Value,
|
||||||
|
pspec: &glib::ParamSpec,
|
||||||
|
) {
|
||||||
|
match pspec.get_name() {
|
||||||
|
"compression-level" => {
|
||||||
let mut settings = self.settings.lock();
|
let mut settings = self.settings.lock();
|
||||||
settings.compression = value
|
settings.compression = value
|
||||||
.get_some::<CompressionLevel>()
|
.get_some::<CompressionLevel>()
|
||||||
.expect("type checked upstream");
|
.expect("type checked upstream");
|
||||||
}
|
}
|
||||||
subclass::Property("filter", ..) => {
|
"filter" => {
|
||||||
let mut settings = self.settings.lock();
|
let mut settings = self.settings.lock();
|
||||||
settings.filter = value
|
settings.filter = value
|
||||||
.get_some::<FilterType>()
|
.get_some::<FilterType>()
|
||||||
|
@ -278,15 +233,13 @@ impl ObjectImpl for PngEncoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(&self, _obj: &Self::Type, id: usize) -> glib::Value {
|
fn get_property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let prop = &PROPERTIES[id];
|
match pspec.get_name() {
|
||||||
|
"compression-level" => {
|
||||||
match *prop {
|
|
||||||
subclass::Property("compression-level", ..) => {
|
|
||||||
let settings = self.settings.lock();
|
let settings = self.settings.lock();
|
||||||
settings.compression.to_value()
|
settings.compression.to_value()
|
||||||
}
|
}
|
||||||
subclass::Property("filter", ..) => {
|
"filter" => {
|
||||||
let settings = self.settings.lock();
|
let settings = self.settings.lock();
|
||||||
settings.filter.to_value()
|
settings.filter.to_value()
|
||||||
}
|
}
|
||||||
|
@ -295,7 +248,68 @@ impl ObjectImpl for PngEncoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ElementImpl for PngEncoder {}
|
impl ElementImpl for PngEncoder {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"PNG encoder",
|
||||||
|
"Encoder/Video",
|
||||||
|
"PNG encoder",
|
||||||
|
"Natanael Mojica <neithanmo@gmail>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let sink_caps = gst::Caps::new_simple(
|
||||||
|
"video/x-raw",
|
||||||
|
&[
|
||||||
|
(
|
||||||
|
"format",
|
||||||
|
&gst::List::new(&[
|
||||||
|
&gst_video::VideoFormat::Gray8.to_str(),
|
||||||
|
&gst_video::VideoFormat::Gray16Be.to_str(),
|
||||||
|
&gst_video::VideoFormat::Rgb.to_str(),
|
||||||
|
&gst_video::VideoFormat::Rgba.to_str(),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
("width", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
||||||
|
("height", &gst::IntRange::<i32>::new(1, std::i32::MAX)),
|
||||||
|
(
|
||||||
|
"framerate",
|
||||||
|
&gst::FractionRange::new(
|
||||||
|
gst::Fraction::new(1, 1),
|
||||||
|
gst::Fraction::new(std::i32::MAX, 1),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let sink_pad_template = gst::PadTemplate::new(
|
||||||
|
"sink",
|
||||||
|
gst::PadDirection::Sink,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&sink_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let src_caps = gst::Caps::new_simple("image/png", &[]);
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&src_caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![sink_pad_template, src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VideoEncoderImpl for PngEncoder {
|
impl VideoEncoderImpl for PngEncoder {
|
||||||
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
fn stop(&self, _element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
||||||
|
|
Loading…
Reference in a new issue