Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webrtc: support reading, publishing, proxying LPCM tracks #3437

Merged
merged 1 commit into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/formatprocessor/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
generateRTPPackets bool,
) (*formatProcessorGeneric, error) {
if generateRTPPackets {
return nil, fmt.Errorf("we don't know how to generate RTP packets of format %+v", forma)
return nil, fmt.Errorf("we don't know how to generate RTP packets of format %T", forma)

Check warning on line 23 in internal/formatprocessor/generic.go

View check run for this annotation

Codecov / codecov/patch

internal/formatprocessor/generic.go#L23

Added line #L23 was not covered by tests
}

return &formatProcessorGeneric{
Expand Down
51 changes: 44 additions & 7 deletions internal/protocols/webrtc/incoming_track.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const (
keyFrameInterval = 2 * time.Second
)

const (
mimeTypeMultiopus = "audio/multiopus"
mimeTypeL16 = "audio/L16"
)

var incomingVideoCodecs = []webrtc.RTPCodecParameters{
{
RTPCodecCapability: webrtc.RTPCodecCapability{
Expand Down Expand Up @@ -95,7 +100,7 @@ var incomingVideoCodecs = []webrtc.RTPCodecParameters{
var incomingAudioCodecs = []webrtc.RTPCodecParameters{
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeMultiopus,
MimeType: mimeTypeMultiopus,
ClockRate: 48000,
Channels: 3,
SDPFmtpLine: "channel_mapping=0,2,1;num_streams=2;coupled_streams=1",
Expand All @@ -104,7 +109,7 @@ var incomingAudioCodecs = []webrtc.RTPCodecParameters{
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeMultiopus,
MimeType: mimeTypeMultiopus,
ClockRate: 48000,
Channels: 4,
SDPFmtpLine: "channel_mapping=0,1,2,3;num_streams=2;coupled_streams=2",
Expand All @@ -113,7 +118,7 @@ var incomingAudioCodecs = []webrtc.RTPCodecParameters{
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeMultiopus,
MimeType: mimeTypeMultiopus,
ClockRate: 48000,
Channels: 5,
SDPFmtpLine: "channel_mapping=0,4,1,2,3;num_streams=3;coupled_streams=2",
Expand All @@ -122,7 +127,7 @@ var incomingAudioCodecs = []webrtc.RTPCodecParameters{
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeMultiopus,
MimeType: mimeTypeMultiopus,
ClockRate: 48000,
Channels: 6,
SDPFmtpLine: "channel_mapping=0,4,1,2,3,5;num_streams=4;coupled_streams=2",
Expand All @@ -131,7 +136,7 @@ var incomingAudioCodecs = []webrtc.RTPCodecParameters{
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeMultiopus,
MimeType: mimeTypeMultiopus,
ClockRate: 48000,
Channels: 7,
SDPFmtpLine: "channel_mapping=0,4,1,2,3,5,6;num_streams=4;coupled_streams=4",
Expand All @@ -140,7 +145,7 @@ var incomingAudioCodecs = []webrtc.RTPCodecParameters{
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeMultiopus,
MimeType: mimeTypeMultiopus,
ClockRate: 48000,
Channels: 8,
SDPFmtpLine: "channel_mapping=0,6,1,4,5,2,3,7;num_streams=5;coupled_streams=4",
Expand Down Expand Up @@ -193,6 +198,30 @@ var incomingAudioCodecs = []webrtc.RTPCodecParameters{
},
PayloadType: 8,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeTypeL16,
ClockRate: 8000,
Channels: 2,
},
PayloadType: 120,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeTypeL16,
ClockRate: 16000,
Channels: 2,
},
PayloadType: 121,
},
{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeTypeL16,
ClockRate: 48000,
Channels: 2,
},
PayloadType: 122,
},
}

// IncomingTrack is an incoming track.
Expand Down Expand Up @@ -245,7 +274,7 @@ func newIncomingTrack(
PacketizationMode: 1,
}

case strings.ToLower(mimeMultiopus):
case strings.ToLower(mimeTypeMultiopus):
t.format = &format.Opus{
PayloadTyp: uint8(track.PayloadType()),
ChannelCount: int(track.Codec().Channels),
Expand Down Expand Up @@ -301,6 +330,14 @@ func newIncomingTrack(
ChannelCount: int(channels),
}

case strings.ToLower(mimeTypeL16):
t.format = &format.LPCM{
PayloadTyp: uint8(track.PayloadType()),
BitDepth: 16,
SampleRate: int(track.Codec().ClockRate),
ChannelCount: int(track.Codec().Channels),
}

default:
return nil, fmt.Errorf("unsupported codec: %+v", track.Codec())
}
Expand Down
28 changes: 23 additions & 5 deletions internal/protocols/webrtc/outgoing_track.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
"github.com/pion/webrtc/v3"
)

const (
mimeMultiopus = "audio/multiopus"
)

// OutgoingTrack is a WebRTC outgoing track
type OutgoingTrack struct {
Format format.Format
Expand Down Expand Up @@ -62,7 +58,7 @@
if forma.ChannelCount > 2 {
return webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeMultiopus,
MimeType: mimeTypeMultiopus,
ClockRate: 48000,
Channels: uint16(forma.ChannelCount),
},
Expand Down Expand Up @@ -140,6 +136,28 @@
PayloadType: 8,
}, nil

case *format.LPCM:
if forma.BitDepth != 16 {
return webrtc.RTPCodecParameters{}, fmt.Errorf("unsupported LPCM bit depth: %d", forma.BitDepth)
}

Check warning on line 142 in internal/protocols/webrtc/outgoing_track.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/webrtc/outgoing_track.go#L141-L142

Added lines #L141 - L142 were not covered by tests

if forma.ClockRate() != 8000 && forma.ClockRate() != 16000 && forma.ClockRate() != 48000 {
return webrtc.RTPCodecParameters{}, fmt.Errorf("unsupported clock rate: %d", forma.ClockRate())
}

Check warning on line 146 in internal/protocols/webrtc/outgoing_track.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/webrtc/outgoing_track.go#L145-L146

Added lines #L145 - L146 were not covered by tests

if forma.ChannelCount != 1 && forma.ChannelCount != 2 {
return webrtc.RTPCodecParameters{}, fmt.Errorf("unsupported channel count: %d", forma.ChannelCount)
}

Check warning on line 150 in internal/protocols/webrtc/outgoing_track.go

View check run for this annotation

Codecov / codecov/patch

internal/protocols/webrtc/outgoing_track.go#L149-L150

Added lines #L149 - L150 were not covered by tests

return webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: mimeTypeL16,
ClockRate: uint32(forma.ClockRate()),
Channels: uint16(forma.ChannelCount),
},
PayloadType: 96,
}, nil

default:
return webrtc.RTPCodecParameters{}, fmt.Errorf("unsupported track type: %T", forma)
}
Expand Down
45 changes: 45 additions & 0 deletions internal/protocols/webrtc/peer_connection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,51 @@ func TestPeerConnectionPublishRead(t *testing.T) {
ChannelCount: 1,
},
},
{
"l16 8000 stereo",
&format.LPCM{
PayloadTyp: 96,
BitDepth: 16,
SampleRate: 8000,
ChannelCount: 2,
},
&format.LPCM{
PayloadTyp: 96,
BitDepth: 16,
SampleRate: 8000,
ChannelCount: 2,
},
},
{
"l16 16000 stereo",
&format.LPCM{
PayloadTyp: 96,
BitDepth: 16,
SampleRate: 16000,
ChannelCount: 2,
},
&format.LPCM{
PayloadTyp: 96,
BitDepth: 16,
SampleRate: 16000,
ChannelCount: 2,
},
},
{
"l16 48khz stereo",
&format.LPCM{
PayloadTyp: 96,
BitDepth: 16,
SampleRate: 48000,
ChannelCount: 2,
},
&format.LPCM{
PayloadTyp: 96,
BitDepth: 16,
SampleRate: 48000,
ChannelCount: 2,
},
},
} {
t.Run(ca.name, func(t *testing.T) {
pc1 := &PeerConnection{
Expand Down
22 changes: 22 additions & 0 deletions internal/servers/webrtc/read_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,24 @@
return lines.join('\r\n');
};

const enableL16 = (section) => {
let lines = section.split('\r\n');

lines[0] += " 120";
lines.splice(lines.length - 1, 0, "a=rtpmap:120 L16/8000/2");
lines.splice(lines.length - 1, 0, "a=rtcp-fb:120 transport-cc");

lines[0] += " 121";
lines.splice(lines.length - 1, 0, "a=rtpmap:121 L16/16000/2");
lines.splice(lines.length - 1, 0, "a=rtcp-fb:121 transport-cc");

lines[0] += " 122";
lines.splice(lines.length - 1, 0, "a=rtpmap:122 L16/48000/2");
lines.splice(lines.length - 1, 0, "a=rtcp-fb:122 transport-cc");

return lines.join('\r\n');
};

const enableStereoOpus = (section) => {
let opusPayloadFormat = '';
let lines = section.split('\r\n');
Expand Down Expand Up @@ -202,6 +220,10 @@
sections[i] = enableMultichannelOpus(sections[i]);
}

if (nonAdvertisedCodecs.includes('L16/48000/2')) {
sections[i] = enableL16(sections[i]);
}

break;
}
}
Expand Down
Loading
Loading