fix(webrtc): force IDR on KeyframeRequest via forced-idr=1 + pict_type (closes #16)

WebRTC KeyframeRequest 响应延迟 2.1s(152 P 帧丢弃)→ 端到端 5-20ms。

- BitrateCommand::ForceKeyframe variant over existing bitrate_tx channel
- SwEncEncode.force_keyframe_pending: bypass dedup, set AV_PICTURE_TYPE_I,
  clear only after avcodec_send_frame succeeds
- libx264 forced-idr=1 via av_opt_set (FFmpeg-level option, not x264-native)
- pict_type reset to NONE per frame (reused AVFrame leak guard)
- WebRtcInner.force_keyframe_to_encode + take_force_keyframe() drain API
- recreate_encoder sets force_keyframe_pending (resolution change → IDR)

Verification: skipping non-IDR frame 152→0, ForceKeyframe→IDR 4-5ms stable.
This commit is contained in:
dailz
2026-06-14 08:20:06 +08:00
parent 0e91c793c7
commit 36cee9d9dd
3 changed files with 58 additions and 1 deletions
+16
View File
@@ -200,6 +200,7 @@ struct WebRtcInner {
video_pt: Option<Pt>,
connected: bool,
need_keyframe: bool,
force_keyframe_to_encode: bool,
current_bwe_estimate: Option<Bitrate>,
rtp_clock: u32,
buf: Vec<u8>,
@@ -353,6 +354,17 @@ impl WebRtcState {
pub fn set_need_keyframe(&mut self) {
if let Some(inner) = self.inner.as_mut() {
inner.need_keyframe = true;
inner.force_keyframe_to_encode = true;
}
}
pub fn take_force_keyframe(&mut self) -> bool {
if let Some(inner) = self.inner.as_mut() {
let v = inner.force_keyframe_to_encode;
inner.force_keyframe_to_encode = false;
v
} else {
false
}
}
}
@@ -429,6 +441,7 @@ impl WebRtcInner {
video_pt: None,
connected: false,
need_keyframe: false,
force_keyframe_to_encode: false,
current_bwe_estimate: None,
rtp_clock: 0,
buf: vec![0u8; 65535],
@@ -446,6 +459,7 @@ impl WebRtcInner {
.map_err(|e| anyhow::anyhow!("accept_offer: {e}"))?;
self.need_keyframe = true;
self.force_keyframe_to_encode = true;
tracing::info!("SDP exchange complete, waiting for ICE/DTLS...");
self.discover_video_params();
@@ -503,6 +517,7 @@ impl WebRtcInner {
tracing::info!("WebRTC connected!");
self.connected = true;
self.need_keyframe = true;
self.force_keyframe_to_encode = true;
self.discover_video_params();
}
Event::IceConnectionStateChange(IceConnectionState::Disconnected) => {
@@ -525,6 +540,7 @@ impl WebRtcInner {
Event::KeyframeRequest(_) => {
tracing::info!("received keyframe request from viewer");
self.need_keyframe = true;
self.force_keyframe_to_encode = true;
}
Event::EgressBitrateEstimate(est) => {
let bitrate = match est {