mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-01-23 17:38:20 +00:00
webrtcsink: expose signal for initial encoder configuration
+ Update README
This commit is contained in:
parent
b5443c5966
commit
983fcf2fbd
3 changed files with 107 additions and 37 deletions
20
README.md
20
README.md
|
@ -35,26 +35,30 @@ useful alternative.
|
||||||
While this is not on the roadmap at the moment, nothing in the design prevents
|
While this is not on the roadmap at the moment, nothing in the design prevents
|
||||||
implementing this optimization.
|
implementing this optimization.
|
||||||
|
|
||||||
* Congestion control: the element levarages transport-wide congestion control
|
* Congestion control: the element leverages transport-wide congestion control
|
||||||
feedback messages in order to adapt the bitrate of individual consumers' video
|
feedback messages in order to adapt the bitrate of individual consumers' video
|
||||||
encoders to the available bandwidth.
|
encoders to the available bandwidth.
|
||||||
|
|
||||||
* Configuration: the level of user control over the element is at the moment quite
|
* Configuration: the level of user control over the element is slowly expanding,
|
||||||
narrow, as the only interface exposed is control over proposed codecs, as well
|
consult `gst-inspect-1.0` for more information on the available properties and
|
||||||
as their order of priority, and disabling congestion control. Consult `gst-inspect=1.0`
|
signals.
|
||||||
for more information.
|
|
||||||
|
|
||||||
More features are on the roadmap, focusing on mechanisms for mitigating packet
|
* Packet loss mitigation: webrtcsink now supports sending protection packets for
|
||||||
loss.
|
Forward Error Correction, modulating the amount as a function of the available
|
||||||
|
bandwidth, and can honor retransmission requests. Both features can be disabled
|
||||||
|
via properties.
|
||||||
|
|
||||||
It is important to note that full control over the individual elements used by
|
It is important to note that full control over the individual elements used by
|
||||||
`webrtcsink` is *not* on the roadmap, as it will act as a black box in that respect,
|
`webrtcsink` is *not* on the roadmap, as it will act as a black box in that respect,
|
||||||
for example `webrtcsink` wants to reserve control over the bitrate for congestion
|
for example `webrtcsink` wants to reserve control over the bitrate for congestion
|
||||||
control.
|
control.
|
||||||
|
|
||||||
|
A signal is now available however for the application to provide the initial
|
||||||
|
configuration for the encoders `webrtcsink` instantiates.
|
||||||
|
|
||||||
If more granular control is required, applications should use `webrtcbin` directly,
|
If more granular control is required, applications should use `webrtcbin` directly,
|
||||||
`webrtcsink` will focus on trying to just do the right thing, although it might
|
`webrtcsink` will focus on trying to just do the right thing, although it might
|
||||||
expose interfaces to guide and tune the heuristics it employs.
|
expose more interfaces to guide and tune the heuristics it employs.
|
||||||
|
|
||||||
[example project]: https://github.com/centricular/webrtcsink-custom-signaller
|
[example project]: https://github.com/centricular/webrtcsink-custom-signaller
|
||||||
|
|
||||||
|
|
|
@ -133,6 +133,24 @@ async fn run(args: Args) -> Result<(), Error> {
|
||||||
.by_name("ws")
|
.by_name("ws")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
ws.connect("encoder-setup", false, |values| {
|
||||||
|
let encoder = values[3].get::<gst::Element>().unwrap();
|
||||||
|
|
||||||
|
info!("Encoder: {}", encoder.factory().unwrap().name());
|
||||||
|
|
||||||
|
let configured = match encoder.factory().unwrap().name().as_str() {
|
||||||
|
"does-not-exist" => {
|
||||||
|
// One could configure a hardware encoder to their liking here,
|
||||||
|
// and return true to make sure webrtcsink does not do any configuration
|
||||||
|
// of its own
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(configured.to_value())
|
||||||
|
});
|
||||||
|
|
||||||
let ws_clone = ws.downgrade();
|
let ws_clone = ws.downgrade();
|
||||||
let state_clone = state.clone();
|
let state_clone = state.clone();
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
|
|
|
@ -296,6 +296,45 @@ fn make_converter_for_video_caps(caps: &gst::Caps) -> Result<gst::Element, Error
|
||||||
.upcast())
|
.upcast())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Default configuration for known encoders, can be disabled
|
||||||
|
/// by returning True from an encoder-setup handler
|
||||||
|
fn configure_encoder(codec: &Codec, enc: &gst::Element) {
|
||||||
|
match codec.encoder.name().as_str() {
|
||||||
|
"vp8enc" | "vp9enc" => {
|
||||||
|
enc.set_property("deadline", 1i64);
|
||||||
|
enc.set_property("threads", 12i32);
|
||||||
|
enc.set_property("target-bitrate", 2560000i32);
|
||||||
|
enc.set_property("cpu-used", -16i32);
|
||||||
|
enc.set_property("keyframe-max-dist", 2000i32);
|
||||||
|
enc.set_property_from_str("keyframe-mode", "disabled");
|
||||||
|
enc.set_property_from_str("end-usage", "cbr");
|
||||||
|
enc.set_property("buffer-initial-size", 100i32);
|
||||||
|
enc.set_property("buffer-optimal-size", 120i32);
|
||||||
|
enc.set_property("buffer-size", 150i32);
|
||||||
|
enc.set_property("resize-allowed", true);
|
||||||
|
enc.set_property("max-intra-bitrate", 250i32);
|
||||||
|
enc.set_property_from_str("error-resilient", "default");
|
||||||
|
enc.set_property("lag-in-frames", 0i32);
|
||||||
|
}
|
||||||
|
"x264enc" => {
|
||||||
|
enc.set_property("bitrate", 2048u32);
|
||||||
|
enc.set_property_from_str("tune", "zerolatency");
|
||||||
|
enc.set_property_from_str("speed-preset", "ultrafast");
|
||||||
|
enc.set_property("threads", 12u32);
|
||||||
|
enc.set_property("key-int-max", 2560u32);
|
||||||
|
enc.set_property("b-adapt", false);
|
||||||
|
enc.set_property("vbv-buf-capacity", 120u32);
|
||||||
|
}
|
||||||
|
"nvh264enc" => {
|
||||||
|
enc.set_property("bitrate", 2048u32);
|
||||||
|
enc.set_property("gop-size", 2560i32);
|
||||||
|
enc.set_property_from_str("rc-mode", "cbr-ld-hq");
|
||||||
|
enc.set_property("zerolatency", true);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Bit of an awkward function, but the goal here is to keep
|
/// Bit of an awkward function, but the goal here is to keep
|
||||||
/// most of the encoding code for consumers in line with
|
/// most of the encoding code for consumers in line with
|
||||||
/// the codec discovery code, and this gets the job done.
|
/// the codec discovery code, and this gets the job done.
|
||||||
|
@ -376,37 +415,8 @@ fn setup_encoding(
|
||||||
|
|
||||||
match codec.encoder.name().as_str() {
|
match codec.encoder.name().as_str() {
|
||||||
"vp8enc" | "vp9enc" => {
|
"vp8enc" | "vp9enc" => {
|
||||||
enc.set_property("deadline", 1i64);
|
|
||||||
enc.set_property("threads", 12i32);
|
|
||||||
enc.set_property("target-bitrate", 2560000i32);
|
|
||||||
enc.set_property("cpu-used", -16i32);
|
|
||||||
enc.set_property("keyframe-max-dist", 2000i32);
|
|
||||||
enc.set_property_from_str("keyframe-mode", "disabled");
|
|
||||||
enc.set_property_from_str("end-usage", "cbr");
|
|
||||||
enc.set_property("buffer-initial-size", 100i32);
|
|
||||||
enc.set_property("buffer-optimal-size", 120i32);
|
|
||||||
enc.set_property("buffer-size", 150i32);
|
|
||||||
enc.set_property("resize-allowed", true);
|
|
||||||
enc.set_property("max-intra-bitrate", 250i32);
|
|
||||||
enc.set_property_from_str("error-resilient", "default");
|
|
||||||
enc.set_property("lag-in-frames", 0i32);
|
|
||||||
pay.set_property_from_str("picture-id-mode", "15-bit");
|
pay.set_property_from_str("picture-id-mode", "15-bit");
|
||||||
}
|
}
|
||||||
"x264enc" => {
|
|
||||||
enc.set_property("bitrate", 2048u32);
|
|
||||||
enc.set_property_from_str("tune", "zerolatency");
|
|
||||||
enc.set_property_from_str("speed-preset", "ultrafast");
|
|
||||||
enc.set_property("threads", 12u32);
|
|
||||||
enc.set_property("key-int-max", 2560u32);
|
|
||||||
enc.set_property("b-adapt", false);
|
|
||||||
enc.set_property("vbv-buf-capacity", 120u32);
|
|
||||||
}
|
|
||||||
"nvh264enc" => {
|
|
||||||
enc.set_property("bitrate", 2048u32);
|
|
||||||
enc.set_property("gop-size", 2560i32);
|
|
||||||
enc.set_property_from_str("rc-mode", "cbr-ld-hq");
|
|
||||||
enc.set_property("zerolatency", true);
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1059,6 +1069,23 @@ impl Consumer {
|
||||||
false,
|
false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let encoder_was_configured: bool = element.emit_by_name(
|
||||||
|
"encoder-setup",
|
||||||
|
&[&self.peer_id, &webrtc_pad.stream_name, &enc],
|
||||||
|
);
|
||||||
|
|
||||||
|
if !encoder_was_configured {
|
||||||
|
gst_debug!(CAT, obj: element, "configuring encoder {:?}", enc);
|
||||||
|
configure_encoder(codec, &enc);
|
||||||
|
} else {
|
||||||
|
gst_debug!(
|
||||||
|
CAT,
|
||||||
|
obj: element,
|
||||||
|
"the encoder {:?} was configured through the signal handler",
|
||||||
|
enc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// At this point, the peer has provided its answer, and we want to
|
// At this point, the peer has provided its answer, and we want to
|
||||||
// let the payloader / encoder perform negotiation according to that.
|
// let the payloader / encoder perform negotiation according to that.
|
||||||
//
|
//
|
||||||
|
@ -2551,6 +2578,27 @@ impl ObjectImpl for WebRTCSink {
|
||||||
res
|
res
|
||||||
})
|
})
|
||||||
.build(),
|
.build(),
|
||||||
|
/*
|
||||||
|
* RsWebRTCSink::encoder-setup:
|
||||||
|
* @consumer_id: Identifier of the consumer
|
||||||
|
* @pad_name: The name of the corresponding input pad
|
||||||
|
* @encoder: The constructed encoder
|
||||||
|
*
|
||||||
|
* This signal can be used to tweak @encoder properties.
|
||||||
|
*
|
||||||
|
* Returns: True if the encoder is entirely configured,
|
||||||
|
* False if webrtcsink should apply a default configuration
|
||||||
|
*/
|
||||||
|
glib::subclass::Signal::builder(
|
||||||
|
"encoder-setup",
|
||||||
|
&[
|
||||||
|
String::static_type().into(),
|
||||||
|
String::static_type().into(),
|
||||||
|
gst::Element::static_type().into(),
|
||||||
|
],
|
||||||
|
bool::static_type().into(),
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue