Skip to content

Commit 144eb30

Browse files
committed
wip
1 parent a2a0413 commit 144eb30

File tree

8 files changed

+139
-162
lines changed

8 files changed

+139
-162
lines changed

smelter-core/src/graphics_context.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,58 @@ impl GraphicsContext {
8080
pub fn has_vulkan_encoder_support(&self) -> bool {
8181
false
8282
}
83+
84+
#[cfg(feature = "vk-video")]
85+
pub fn vulkan_h264_encode_profile_level_support(&self) -> Option<H264ProfileLevelSupport> {
86+
let vulkan_ctx = self.vulkan_ctx.as_ref()?;
87+
let caps = vulkan_ctx.device.encode_capabilities().h264?;
88+
89+
Some(H264ProfileLevelSupport {
90+
baseline_max_level_idc: caps.baseline_profile.map(|p| p.max_level_idc),
91+
main_max_level_idc: caps.main_profile.map(|p| p.max_level_idc),
92+
high_max_level_idc: caps.high_profile.map(|p| p.max_level_idc),
93+
})
94+
}
95+
96+
#[cfg(not(feature = "vk-video"))]
97+
pub fn vulkan_h264_encode_profile_level_support(&self) -> Option<H264ProfileLevelSupport> {
98+
None
99+
}
100+
101+
#[cfg(feature = "vk-video")]
102+
pub fn vulkan_h264_decode_profile_level_support(&self) -> Option<H264ProfileLevelSupport> {
103+
let vulkan_ctx = self.vulkan_ctx.as_ref()?;
104+
let caps = vulkan_ctx.device.decode_capabilities().h264?;
105+
106+
Some(H264ProfileLevelSupport {
107+
baseline_max_level_idc: caps.baseline_profile.map(|p| p.max_level_idc),
108+
main_max_level_idc: caps.main_profile.map(|p| p.max_level_idc),
109+
high_max_level_idc: caps.high_profile.map(|p| p.max_level_idc),
110+
})
111+
}
112+
113+
#[cfg(not(feature = "vk-video"))]
114+
pub fn vulkan_h264_decode_profile_level_support(&self) -> Option<H264ProfileLevelSupport> {
115+
None
116+
}
117+
}
118+
119+
#[derive(Debug, Clone, Copy)]
120+
pub struct H264ProfileLevelSupport {
121+
pub baseline_max_level_idc: Option<u8>,
122+
pub main_max_level_idc: Option<u8>,
123+
pub high_max_level_idc: Option<u8>,
124+
}
125+
126+
impl H264ProfileLevelSupport {
127+
pub fn max_level_for_profile(self, profile_idc: u8) -> Option<u8> {
128+
match profile_idc {
129+
0x42 => self.baseline_max_level_idc,
130+
0x4d => self.main_max_level_idc,
131+
0x64 => self.high_max_level_idc,
132+
_ => None,
133+
}
134+
}
83135
}
84136

85137
#[derive(Debug, thiserror::Error)]

smelter-core/src/pipeline/webrtc/h264_offer_filter.rs

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ use tracing::warn;
44
use webrtc::{
55
api::media_engine::MIME_TYPE_H264,
66
peer_connection::sdp::session_description::RTCSessionDescription,
7-
rtp_transceiver::rtp_codec::RTCRtpCodecParameters,
7+
rtp_transceiver::rtp_codec::{RTCRtpCodecCapability, RTCRtpCodecParameters},
88
};
99

10+
use super::supported_codec_parameters::get_video_rtcp_feedback;
11+
1012
#[derive(Debug, Clone)]
1113
struct OfferH264Params {
1214
payload_type: u8,
@@ -20,40 +22,24 @@ struct ParsedH264Fmtp {
2022
packetization_mode: String,
2123
}
2224

23-
/// Mirrors H264 variants from the SDP offer.
25+
/// Builds H264 codec parameters by copying every H264 variant from the SDP offer.
2426
///
2527
/// For each offered H264 payload type, we emit a local codec entry with the same
26-
/// payload type and negotiated fmtp constraints, so WHIP answers can include every
27-
/// offered H264 profile/level and WHEP can expose as many H264 subtypes as possible.
28-
pub(crate) fn filter_h264_codecs_by_offer(
29-
offer: &RTCSessionDescription,
30-
codecs: Vec<RTCRtpCodecParameters>,
31-
) -> Vec<RTCRtpCodecParameters> {
28+
/// payload type and fmtp, so negotiation produces an exact match. This allows
29+
/// accepting any H264 profile/level the peer supports.
30+
///
31+
/// Returns an empty list if no H264 codecs are found in the offer.
32+
pub(crate) fn h264_codecs_from_offer(offer: &RTCSessionDescription) -> Vec<RTCRtpCodecParameters> {
3233
let offer_h264_params = match extract_h264_params_from_offer(offer) {
3334
Some(params) if !params.is_empty() => params,
34-
Some(_) => {
35-
// Offer has no explicit H264 fmtp lines.
36-
return codecs;
37-
}
35+
Some(_) => return Vec::new(),
3836
None => {
39-
warn!("Failed to parse SDP offer for H264 codec filtering, using all codecs");
40-
return codecs;
37+
warn!("Failed to parse SDP offer for H264 codecs");
38+
return Vec::new();
4139
}
4240
};
4341

44-
let (h264_templates, mut passthrough): (Vec<_>, Vec<_>) = codecs
45-
.into_iter()
46-
.partition(|codec| codec.capability.mime_type == MIME_TYPE_H264);
47-
48-
if h264_templates.is_empty() {
49-
return passthrough;
50-
}
51-
52-
// Any H264 template works: payload type and fmtp are overwritten from the offer.
53-
let Some(template) = h264_templates.into_iter().next() else {
54-
return passthrough;
55-
};
56-
42+
let mut codecs = Vec::new();
5743
let mut seen = HashSet::new();
5844
for offer in offer_h264_params {
5945
if !seen.insert((
@@ -64,17 +50,23 @@ pub(crate) fn filter_h264_codecs_by_offer(
6450
continue;
6551
}
6652

67-
let mut codec = template.clone();
68-
69-
codec.payload_type = offer.payload_type;
70-
codec.capability.sdp_fmtp_line = build_h264_fmtp(
71-
offer.profile_level_id.as_deref(),
72-
offer.packetization_mode.as_str(),
73-
);
74-
passthrough.push(codec);
53+
codecs.push(RTCRtpCodecParameters {
54+
capability: RTCRtpCodecCapability {
55+
mime_type: MIME_TYPE_H264.to_owned(),
56+
clock_rate: 90000,
57+
channels: 0,
58+
sdp_fmtp_line: build_h264_fmtp(
59+
offer.profile_level_id.as_deref(),
60+
offer.packetization_mode.as_str(),
61+
),
62+
rtcp_feedback: get_video_rtcp_feedback(),
63+
},
64+
payload_type: offer.payload_type,
65+
..Default::default()
66+
});
7567
}
7668

77-
passthrough
69+
codecs
7870
}
7971

8072
/// Extracts H264 payload parameters from offer fmtp/rtpmap lines.

smelter-core/src/pipeline/webrtc/h264_vulkan_capability_filter.rs

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,14 @@ use webrtc::rtp_transceiver::rtp_codec::RTCRtpCodecParameters;
44

55
use crate::pipeline::PipelineCtx;
66

7-
#[derive(Debug, Clone, Copy)]
8-
struct H264ProfileLevelSupport {
9-
baseline_max_level_idc: Option<u8>,
10-
main_max_level_idc: Option<u8>,
11-
high_max_level_idc: Option<u8>,
12-
}
13-
14-
impl H264ProfileLevelSupport {
15-
fn max_level_for_profile(self, profile_idc: u8) -> Option<u8> {
16-
match profile_idc {
17-
0x42 => self.baseline_max_level_idc,
18-
0x4d => self.main_max_level_idc,
19-
0x64 => self.high_max_level_idc,
20-
_ => None,
21-
}
22-
}
23-
}
24-
257
pub(crate) fn filter_h264_codecs_for_vulkan_encoder(
268
ctx: &Arc<PipelineCtx>,
279
codecs: Vec<RTCRtpCodecParameters>,
2810
) -> Vec<RTCRtpCodecParameters> {
29-
let Some(support) = vulkan_h264_encode_profile_level_support(ctx) else {
11+
let Some(support) = ctx
12+
.graphics_context
13+
.vulkan_h264_encode_profile_level_support()
14+
else {
3015
return codecs;
3116
};
3217

@@ -37,7 +22,10 @@ pub(crate) fn filter_h264_codecs_for_vulkan_decoder(
3722
ctx: &Arc<PipelineCtx>,
3823
codecs: Vec<RTCRtpCodecParameters>,
3924
) -> Vec<RTCRtpCodecParameters> {
40-
let Some(support) = vulkan_h264_decode_profile_level_support(ctx) else {
25+
let Some(support) = ctx
26+
.graphics_context
27+
.vulkan_h264_decode_profile_level_support()
28+
else {
4129
return codecs;
4230
};
4331

@@ -46,7 +34,7 @@ pub(crate) fn filter_h264_codecs_for_vulkan_decoder(
4634

4735
fn filter_h264_codecs_by_profile_level_support(
4836
codecs: Vec<RTCRtpCodecParameters>,
49-
support: H264ProfileLevelSupport,
37+
support: crate::graphics_context::H264ProfileLevelSupport,
5038
) -> Vec<RTCRtpCodecParameters> {
5139
codecs
5240
.into_iter()
@@ -81,45 +69,3 @@ fn h264_profile_level_idc_from_fmtp(fmtp: &str) -> Option<(u8, u8)> {
8169

8270
None
8371
}
84-
85-
#[cfg(feature = "vk-video")]
86-
fn vulkan_h264_encode_profile_level_support(
87-
ctx: &Arc<PipelineCtx>,
88-
) -> Option<H264ProfileLevelSupport> {
89-
let vulkan_ctx = ctx.graphics_context.vulkan_ctx.as_ref()?;
90-
let caps = vulkan_ctx.device.encode_capabilities().h264?;
91-
92-
Some(H264ProfileLevelSupport {
93-
baseline_max_level_idc: caps.baseline_profile.map(|p| p.max_level_idc),
94-
main_max_level_idc: caps.main_profile.map(|p| p.max_level_idc),
95-
high_max_level_idc: caps.high_profile.map(|p| p.max_level_idc),
96-
})
97-
}
98-
99-
#[cfg(not(feature = "vk-video"))]
100-
fn vulkan_h264_encode_profile_level_support(
101-
_ctx: &Arc<PipelineCtx>,
102-
) -> Option<H264ProfileLevelSupport> {
103-
None
104-
}
105-
106-
#[cfg(feature = "vk-video")]
107-
fn vulkan_h264_decode_profile_level_support(
108-
ctx: &Arc<PipelineCtx>,
109-
) -> Option<H264ProfileLevelSupport> {
110-
let vulkan_ctx = ctx.graphics_context.vulkan_ctx.as_ref()?;
111-
let caps = vulkan_ctx.device.decode_capabilities().h264?;
112-
113-
Some(H264ProfileLevelSupport {
114-
baseline_max_level_idc: caps.baseline_profile.map(|p| p.max_level_idc),
115-
main_max_level_idc: caps.main_profile.map(|p| p.max_level_idc),
116-
high_max_level_idc: caps.high_profile.map(|p| p.max_level_idc),
117-
})
118-
}
119-
120-
#[cfg(not(feature = "vk-video"))]
121-
fn vulkan_h264_decode_profile_level_support(
122-
_ctx: &Arc<PipelineCtx>,
123-
) -> Option<H264ProfileLevelSupport> {
124-
None
125-
}

smelter-core/src/pipeline/webrtc/supported_codec_parameters.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -50,33 +50,30 @@ pub fn h264_codec_params() -> Vec<RTCRtpCodecParameters> {
5050
"640033", // high, 5.1
5151
];
5252

53-
let mut params = Vec::with_capacity(profile_level_ids.len() * 2);
54-
let mut payload_type = 100u8;
53+
let opus_payload_types: [u8; 2] = [110, 111];
54+
let payload_types = (100u8..).filter(|pt| !opus_payload_types.contains(pt));
5555

56-
for plid in profile_level_ids {
57-
for pmode in [1, 0] {
58-
params.push(RTCRtpCodecParameters {
59-
capability: RTCRtpCodecCapability {
60-
mime_type: MIME_TYPE_H264.to_owned(),
61-
clock_rate: 90000,
62-
channels: 0,
63-
sdp_fmtp_line: format!(
64-
"level-asymmetry-allowed=1;packetization-mode={pmode};profile-level-id={plid}"
65-
),
66-
rtcp_feedback: get_video_rtcp_feedback(),
67-
},
68-
payload_type,
69-
..Default::default()
70-
});
71-
72-
payload_type += 1;
73-
}
74-
}
75-
76-
params
56+
profile_level_ids
57+
.iter()
58+
.flat_map(|plid| [1, 0].map(|pmode| (plid, pmode)))
59+
.zip(payload_types)
60+
.map(|((plid, pmode), payload_type)| RTCRtpCodecParameters {
61+
capability: RTCRtpCodecCapability {
62+
mime_type: MIME_TYPE_H264.to_owned(),
63+
clock_rate: 90000,
64+
channels: 0,
65+
sdp_fmtp_line: format!(
66+
"level-asymmetry-allowed=1;packetization-mode={pmode};profile-level-id={plid}"
67+
),
68+
rtcp_feedback: get_video_rtcp_feedback(),
69+
},
70+
payload_type,
71+
..Default::default()
72+
})
73+
.collect()
7774
}
7875

79-
fn get_video_rtcp_feedback() -> Vec<RTCPFeedback> {
76+
pub(crate) fn get_video_rtcp_feedback() -> Vec<RTCPFeedback> {
8077
vec![
8178
RTCPFeedback {
8279
typ: "goog-remb".to_owned(),

smelter-core/src/pipeline/webrtc/whep_input/input.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub(crate) struct WhepInput {
3434
ctx: Arc<PipelineCtx>,
3535
session_url: Url,
3636
client: Arc<WhipWhepHttpClient>,
37+
_peer_connection: RecvonlyPeerConnection,
3738
}
3839

3940
impl WhepInput {
@@ -157,6 +158,7 @@ async fn init_whep_client(
157158
ctx,
158159
session_url,
159160
client,
161+
_peer_connection: pc,
160162
}),
161163
InputInitInfo::Other,
162164
QueueDataReceiver {

smelter-core/src/pipeline/webrtc/whep_output/peer_connection.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ use webrtc::{
3131

3232
use crate::pipeline::webrtc::{
3333
error::WhipWhepServerError,
34-
h264_offer_filter::filter_h264_codecs_by_offer,
34+
h264_offer_filter::h264_codecs_from_offer,
3535
h264_vulkan_capability_filter::filter_h264_codecs_for_vulkan_encoder,
36-
supported_codec_parameters::{h264_codec_params, vp8_codec_params, vp9_codec_params},
36+
supported_codec_parameters::{vp8_codec_params, vp9_codec_params},
3737
};
3838

3939
use crate::prelude::*;
@@ -291,13 +291,12 @@ fn register_codecs(
291291
if let Some(encoder) = video_encoder {
292292
match encoder {
293293
VideoEncoderOptions::FfmpegH264(_) => {
294-
let codecs = filter_h264_codecs_by_offer(offer, h264_codec_params());
295-
for codec in codecs {
294+
for codec in h264_codecs_from_offer(offer) {
296295
media_engine.register_codec(codec, RTPCodecType::Video)?;
297296
}
298297
}
299298
VideoEncoderOptions::VulkanH264(_) => {
300-
let codecs = filter_h264_codecs_by_offer(offer, h264_codec_params());
299+
let codecs = h264_codecs_from_offer(offer);
301300
for codec in filter_h264_codecs_for_vulkan_encoder(ctx, codecs) {
302301
media_engine.register_codec(codec, RTPCodecType::Video)?;
303302
}

0 commit comments

Comments
 (0)