Skip to content

Commit 5e20c3d

Browse files
authored
Look for SPS instead of keyframe with H264 packetization_mode=0 (#220)
1 parent 9cdd430 commit 5e20c3d

File tree

3 files changed

+53
-35
lines changed

3 files changed

+53
-35
lines changed

lib/ex_webrtc/peer_connection/configuration.ex

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
2727
clock_rate: 90_000
2828
}
2929

30-
# Ensure we are using H264 with packetization_mode=1 by default
31-
# (packetization_mode=0 has issues when switching layers)
32-
@default_codec_params_h264 %RTPCodecParameters{
33-
payload_type: 99,
34-
mime_type: "video/H264",
35-
clock_rate: 90_000,
36-
sdp_fmtp_line: %FMTP{
37-
pt: 99,
38-
level_asymmetry_allowed: true,
39-
packetization_mode: 1,
40-
profile_level_id: 0x42E01F
41-
}
42-
}
43-
44-
@default_codec_params_av1 %RTPCodecParameters{
45-
payload_type: 45,
46-
mime_type: "video/AV1",
47-
clock_rate: 90_000,
48-
sdp_fmtp_line: %FMTP{pt: 45, level_idx: 5, profile: 0, tier: 0}
49-
}
50-
51-
@default_audio_codecs [@default_codec_params_opus]
52-
53-
@default_video_codecs [
54-
@default_codec_params_vp8,
30+
@default_codec_params_h264 [
5531
%RTPCodecParameters{
5632
payload_type: 98,
5733
mime_type: "video/H264",
@@ -63,10 +39,34 @@ defmodule ExWebRTC.PeerConnection.Configuration do
6339
profile_level_id: 0x42E01F
6440
}
6541
},
66-
@default_codec_params_h264,
67-
@default_codec_params_av1
42+
%RTPCodecParameters{
43+
payload_type: 99,
44+
mime_type: "video/H264",
45+
clock_rate: 90_000,
46+
sdp_fmtp_line: %FMTP{
47+
pt: 99,
48+
level_asymmetry_allowed: true,
49+
packetization_mode: 1,
50+
profile_level_id: 0x42E01F
51+
}
52+
}
6853
]
6954

55+
@default_codec_params_av1 %RTPCodecParameters{
56+
payload_type: 45,
57+
mime_type: "video/AV1",
58+
clock_rate: 90_000,
59+
sdp_fmtp_line: %FMTP{pt: 45, level_idx: 5, profile: 0, tier: 0}
60+
}
61+
62+
@default_audio_codecs [@default_codec_params_opus]
63+
@default_video_codecs [
64+
@default_codec_params_vp8,
65+
@default_codec_params_h264,
66+
@default_codec_params_av1
67+
]
68+
|> List.flatten()
69+
7070
@typedoc """
7171
Allowed audio codec names which will get expanded to the relevant default `t:ExWebRTC.RTPCodecParameters.t/0`
7272
"""
@@ -718,7 +718,7 @@ defmodule ExWebRTC.PeerConnection.Configuration do
718718
options
719719

720720
Enum.all?(codecs, &is_atom/1) ->
721-
expanded_codecs = Enum.map(codecs, &expand_default_codec/1)
721+
expanded_codecs = codecs |> Enum.map(&expand_default_codec/1) |> List.flatten()
722722
Keyword.put(options, key, expanded_codecs)
723723

724724
true ->

lib/ex_webrtc/rtp/h264.ex

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,19 @@ defmodule ExWebRTC.RTP.H264 do
77

88
# Copied nearly 1-to-1 from https://github.com/membraneframework/membrane_rtp_h264_plugin/blob/master/lib/rtp_h264/utils.ex
99
# originally based on galene's implementation https://github.com/jech/galene/blob/6fbdf0eab2c9640e673d9f9ec0331da24cbf2c4c/codecs/codecs.go#L119
10-
# but only looks for SPS
11-
# it is also unclear why we sometimes check against nalu type == 7
12-
# and sometimes against nalu type == 5 but galene does it this way
13-
# and it works
10+
# but only looks for SPS, in packetization_mode=0 as well as packetization_mode=1.
11+
#
12+
# It's been empirically tested with simulated packet loss that for packetization_mode=0 (`nalu_type in 1..23` clause):
13+
# * if we're checking against `nalu_type == 5`, the stream breaks regularly when switching layers,
14+
# * if we're checking against `nalu_type == 5 or nalu_type == 7`, the stream breaks occasionally when switching layers,
15+
# this happens when we've lost the packet containing SPS, but received the following one containing the keyframe,
16+
# * if we're checking against `nalu_type == 7`, no issues were encountered.
17+
#
18+
# Janus also does it this way.
19+
# https://github.com/meetecho/janus-gateway/blob/3367f41de9225daed812ca0991c259f1458fe49f/src/utils.h#L352
20+
#
21+
# For more info, refer to the H264 spec and RFC 6184, sections 5.4 and 6
22+
# https://datatracker.ietf.org/doc/html/rfc6184#section-5.4
1423

1524
@doc """
1625
Returns a boolean telling if the packets contains a beginning of a H264 intra-frame.
@@ -21,14 +30,21 @@ defmodule ExWebRTC.RTP.H264 do
2130

2231
def keyframe?(%Packet{}), do: false
2332

33+
# Reserved
2434
defp do_keyframe?(0, _), do: false
25-
defp do_keyframe?(nalu_type, _) when nalu_type in 1..23, do: nalu_type == 5
35+
36+
# Single NAL Unit packets: check if NALU contains SPS (type 7)
37+
defp do_keyframe?(nalu_type, _) when nalu_type in 1..23, do: nalu_type == 7
38+
39+
# STAP-A
2640
defp do_keyframe?(24, aus), do: check_aggr_units(24, aus)
2741

42+
# STAP-B, MTAP16, MTAP24
2843
defp do_keyframe?(nalu_type, <<_don::16, aus::binary>>)
2944
when nalu_type in 25..27,
3045
do: check_aggr_units(nalu_type, aus)
3146

47+
# FU-A, FU-B
3248
defp do_keyframe?(nalu_type, <<s::1, _e::1, _r::1, type::5, _fu_payload::binary>>)
3349
when nalu_type in 28..29,
3450
do: s == 1 and type == 7

test/ex_webrtc/peer_connection/configuration_test.exs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,11 +515,13 @@ defmodule ExWebRTC.PeerConnection.ConfigurationTest do
515515

516516
assert Configuration.expand_default_codecs(og_options) == og_options
517517

518-
[video_codecs: [vp8_params, h264_params]] =
518+
# 2 packetization_modes for H264
519+
[video_codecs: [vp8_params, h264_params_0, h264_params_1]] =
519520
Configuration.expand_default_codecs(video_codecs: [:vp8, :h264])
520521

521522
assert vp8_params.mime_type == "video/VP8"
522-
assert h264_params.mime_type == "video/H264"
523+
assert h264_params_0.mime_type == "video/H264"
524+
assert h264_params_1.mime_type == "video/H264"
523525

524526
assert_raise RuntimeError, fn -> Configuration.expand_default_codecs(video_codecs: [:av2]) end
525527

0 commit comments

Comments
 (0)