mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-03-01 11:00:59 +00:00
webrtcsink: expose stats property
This commit is contained in:
parent
40a4b745e3
commit
921ca7fbab
2 changed files with 77 additions and 6 deletions
|
@ -15,7 +15,7 @@ use std::ops::Mul;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use super::utils::{make_element, StreamProducer};
|
use super::utils::{make_element, StreamProducer};
|
||||||
use super::WebRTCSinkCongestionControl;
|
use super::{WebRTCSinkCongestionControl, WebRTCSinkMitigationMode};
|
||||||
use crate::signaller::Signaller;
|
use crate::signaller::Signaller;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
@ -93,11 +93,13 @@ struct WebRTCPad {
|
||||||
/// stream according to the bitrate, thresholds hardcoded for now
|
/// stream according to the bitrate, thresholds hardcoded for now
|
||||||
struct VideoEncoder {
|
struct VideoEncoder {
|
||||||
factory_name: String,
|
factory_name: String,
|
||||||
|
codec_name: String,
|
||||||
element: gst::Element,
|
element: gst::Element,
|
||||||
filter: gst::Element,
|
filter: gst::Element,
|
||||||
halved_framerate: gst::Fraction,
|
halved_framerate: gst::Fraction,
|
||||||
full_width: i32,
|
full_width: i32,
|
||||||
peer_id: String,
|
peer_id: String,
|
||||||
|
mitigation_mode: WebRTCSinkMitigationMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CongestionController {
|
struct CongestionController {
|
||||||
|
@ -151,6 +153,7 @@ struct Consumer {
|
||||||
/// None if congestion control was disabled
|
/// None if congestion control was disabled
|
||||||
congestion_controller: Option<CongestionController>,
|
congestion_controller: Option<CongestionController>,
|
||||||
sdp: Option<gst_sdp::SDPMessage>,
|
sdp: Option<gst_sdp::SDPMessage>,
|
||||||
|
stats: gst::Structure,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -408,6 +411,7 @@ impl VideoEncoder {
|
||||||
filter: gst::Element,
|
filter: gst::Element,
|
||||||
in_caps: &gst::Caps,
|
in_caps: &gst::Caps,
|
||||||
peer_id: &str,
|
peer_id: &str,
|
||||||
|
codec_name: &str,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let s = in_caps.structure(0).unwrap();
|
let s = in_caps.structure(0).unwrap();
|
||||||
|
|
||||||
|
@ -419,11 +423,13 @@ impl VideoEncoder {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
factory_name: element.factory().unwrap().name().into(),
|
factory_name: element.factory().unwrap().name().into(),
|
||||||
|
codec_name: codec_name.to_string(),
|
||||||
element,
|
element,
|
||||||
filter,
|
filter,
|
||||||
halved_framerate,
|
halved_framerate,
|
||||||
full_width,
|
full_width,
|
||||||
peer_id: peer_id.to_string(),
|
peer_id: peer_id.to_string(),
|
||||||
|
mitigation_mode: WebRTCSinkMitigationMode::NONE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +441,7 @@ impl VideoEncoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_bitrate(&self, element: &super::WebRTCSink, bitrate: i32) {
|
fn set_bitrate(&mut self, element: &super::WebRTCSink, bitrate: i32) {
|
||||||
match self.factory_name.as_str() {
|
match self.factory_name.as_str() {
|
||||||
"vp8enc" | "vp9enc" => self.element.set_property("target-bitrate", bitrate),
|
"vp8enc" | "vp9enc" => self.element.set_property("target-bitrate", bitrate),
|
||||||
"x264enc" | "nvh264enc" => self
|
"x264enc" | "nvh264enc" => self
|
||||||
|
@ -456,15 +462,20 @@ impl VideoEncoder {
|
||||||
if bitrate < 500000 {
|
if bitrate < 500000 {
|
||||||
s.set("width", 360i32.min(self.full_width));
|
s.set("width", 360i32.min(self.full_width));
|
||||||
s.set("framerate", self.halved_framerate);
|
s.set("framerate", self.halved_framerate);
|
||||||
|
self.mitigation_mode =
|
||||||
|
WebRTCSinkMitigationMode::DOWNSAMPLED | WebRTCSinkMitigationMode::DOWNSCALED;
|
||||||
} else if bitrate < 1000000 {
|
} else if bitrate < 1000000 {
|
||||||
s.set("width", 360i32.min(self.full_width));
|
s.set("width", 360i32.min(self.full_width));
|
||||||
s.remove_field("framerate");
|
s.remove_field("framerate");
|
||||||
|
self.mitigation_mode = WebRTCSinkMitigationMode::DOWNSCALED;
|
||||||
} else if bitrate < 2000000 {
|
} else if bitrate < 2000000 {
|
||||||
s.set("width", 720i32.min(self.full_width));
|
s.set("width", 720i32.min(self.full_width));
|
||||||
s.remove_field("framerate");
|
s.remove_field("framerate");
|
||||||
|
self.mitigation_mode = WebRTCSinkMitigationMode::DOWNSCALED;
|
||||||
} else {
|
} else {
|
||||||
s.remove_field("width");
|
s.remove_field("width");
|
||||||
s.remove_field("framerate");
|
s.remove_field("framerate");
|
||||||
|
self.mitigation_mode = WebRTCSinkMitigationMode::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
let caps = gst::Caps::builder_full().structure(s).build();
|
let caps = gst::Caps::builder_full().structure(s).build();
|
||||||
|
@ -481,6 +492,14 @@ impl VideoEncoder {
|
||||||
|
|
||||||
self.filter.set_property("caps", caps);
|
self.filter.set_property("caps", caps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gather_stats(&self) -> gst::Structure {
|
||||||
|
gst::Structure::builder("application/x-webrtcsink-video-encoder-stats")
|
||||||
|
.field("bitrate", self.bitrate())
|
||||||
|
.field("mitigation-mode", self.mitigation_mode)
|
||||||
|
.field("codec-name", self.codec_name.as_str())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CongestionController {
|
impl CongestionController {
|
||||||
|
@ -650,7 +669,7 @@ impl CongestionController {
|
||||||
&mut self,
|
&mut self,
|
||||||
element: &super::WebRTCSink,
|
element: &super::WebRTCSink,
|
||||||
stats: &gst::StructureRef,
|
stats: &gst::StructureRef,
|
||||||
encoders: &Vec<VideoEncoder>,
|
encoders: &mut Vec<VideoEncoder>,
|
||||||
) {
|
) {
|
||||||
let n_encoders = encoders.len() as i32;
|
let n_encoders = encoders.len() as i32;
|
||||||
|
|
||||||
|
@ -696,7 +715,7 @@ impl CongestionController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for encoder in encoders {
|
for encoder in encoders.iter_mut() {
|
||||||
encoder.set_bitrate(element, self.target_bitrate / n_encoders);
|
encoder.set_bitrate(element, self.target_bitrate / n_encoders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -759,6 +778,25 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Consumer {
|
impl Consumer {
|
||||||
|
fn gather_stats(&self) -> gst::Structure {
|
||||||
|
let mut ret = self.stats.to_owned();
|
||||||
|
|
||||||
|
let encoder_stats: Vec<_> = self
|
||||||
|
.encoders
|
||||||
|
.iter()
|
||||||
|
.map(VideoEncoder::gather_stats)
|
||||||
|
.map(|s| s.to_send_value())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let our_stats = gst::Structure::builder("application/x-webrtcsink-consumer-stats")
|
||||||
|
.field("video-encoders", gst::Array::from(encoder_stats))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ret.set("consumer-stats", our_stats);
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_ssrc(&self) -> u32 {
|
fn generate_ssrc(&self) -> u32 {
|
||||||
loop {
|
loop {
|
||||||
let ret = fastrand::u32(..);
|
let ret = fastrand::u32(..);
|
||||||
|
@ -893,11 +931,12 @@ impl Consumer {
|
||||||
pay_filter.set_property("caps", caps);
|
pay_filter.set_property("caps", caps);
|
||||||
|
|
||||||
if codec.is_video {
|
if codec.is_video {
|
||||||
let enc = VideoEncoder::new(
|
let mut enc = VideoEncoder::new(
|
||||||
enc.clone(),
|
enc.clone(),
|
||||||
raw_filter.clone(),
|
raw_filter.clone(),
|
||||||
&webrtc_pad.in_caps,
|
&webrtc_pad.in_caps,
|
||||||
&self.peer_id,
|
&self.peer_id,
|
||||||
|
codec.caps.structure(0).unwrap().name(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(congestion_controller) = self.congestion_controller.as_mut() {
|
if let Some(congestion_controller) = self.congestion_controller.as_mut() {
|
||||||
|
@ -1416,6 +1455,7 @@ impl WebRTCSink {
|
||||||
},
|
},
|
||||||
encoders: Vec::new(),
|
encoders: Vec::new(),
|
||||||
sdp: None,
|
sdp: None,
|
||||||
|
stats: gst::Structure::new_empty("application/x-webrtc-stats"),
|
||||||
};
|
};
|
||||||
|
|
||||||
state
|
state
|
||||||
|
@ -1511,8 +1551,9 @@ impl WebRTCSink {
|
||||||
|
|
||||||
if let Some(consumer) = state.consumers.get_mut(peer_id) {
|
if let Some(consumer) = state.consumers.get_mut(peer_id) {
|
||||||
if let Some(congestion_controller) = consumer.congestion_controller.as_mut() {
|
if let Some(congestion_controller) = consumer.congestion_controller.as_mut() {
|
||||||
congestion_controller.control(element, stats, &consumer.encoders);
|
congestion_controller.control(element, stats, &mut consumer.encoders);
|
||||||
}
|
}
|
||||||
|
consumer.stats = stats.to_owned();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1834,6 +1875,18 @@ impl WebRTCSink {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gather_stats(&self) -> gst::Structure {
|
||||||
|
gst::Structure::from_iter(
|
||||||
|
"application/x-webrtcsink-stats",
|
||||||
|
self.state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.consumers
|
||||||
|
.iter()
|
||||||
|
.map(|(name, consumer)| (name.as_str(), consumer.gather_stats().to_send_value())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn sink_event(&self, pad: &gst::Pad, element: &super::WebRTCSink, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, element: &super::WebRTCSink, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
|
@ -1982,6 +2035,13 @@ impl ObjectImpl for WebRTCSink {
|
||||||
DEFAULT_MAX_BITRATE,
|
DEFAULT_MAX_BITRATE,
|
||||||
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY,
|
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY,
|
||||||
),
|
),
|
||||||
|
glib::ParamSpecBoxed::new(
|
||||||
|
"stats",
|
||||||
|
"Consumer statistics",
|
||||||
|
"Statistics for the current consumers",
|
||||||
|
gst::Structure::static_type(),
|
||||||
|
glib::ParamFlags::READABLE,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2070,6 +2130,7 @@ impl ObjectImpl for WebRTCSink {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.max_bitrate.to_value()
|
settings.max_bitrate.to_value()
|
||||||
}
|
}
|
||||||
|
"stats" => self.gather_stats().to_value(),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,16 @@ pub enum WebRTCSinkCongestionControl {
|
||||||
Homegrown,
|
Homegrown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[glib::flags(name = "GstWebRTCSinkMitigationMode")]
|
||||||
|
enum WebRTCSinkMitigationMode {
|
||||||
|
#[flags_value(name = "No mitigation applied", nick = "none")]
|
||||||
|
NONE = 0b00000000,
|
||||||
|
#[flags_value(name = "Lowered resolution", nick = "downscaled")]
|
||||||
|
DOWNSCALED = 0b00000001,
|
||||||
|
#[flags_value(name = "Lowered framerate", nick = "downsampled")]
|
||||||
|
DOWNSAMPLED = 0b00000010,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
|
|
Loading…
Reference in a new issue