Skip to content

Commit

Permalink
webrtc: support reading, publishing, proxying LPCM tracks
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Jun 9, 2024
1 parent bf8b68d commit 37a4790
Show file tree
Hide file tree
Showing 7 changed files with 375 additions and 104 deletions.
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 @@ func newGeneric(
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)
}

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 @@ import (
"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 @@ func (t *OutgoingTrack) codecParameters() (webrtc.RTPCodecParameters, error) {
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 @@ func (t *OutgoingTrack) codecParameters() (webrtc.RTPCodecParameters, error) {
PayloadType: 8,
}, nil

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

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

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

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: 44100,
ChannelCount: 2,
},
&format.LPCM{
PayloadTyp: 96,
BitDepth: 16,
SampleRate: 44100,
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

0 comments on commit 37a4790

Please sign in to comment.