Skip to content

Commit bfb91f9

Browse files
authored
AI: WebRTC: Support G.711 (PCMU/PCMA) audio codec for WebRTC. v7.0.124 (#4075) (#4568)
This PR adds G.711 (PCMU/PCMA) audio codec support for WebRTC in SRS, enabling relay-only streaming of G.711 audio between WebRTC clients via WHIP/WHEP. G.711 is a widely-used, royalty-free audio codec with excellent compatibility across VoIP systems, IP cameras, and legacy telephony equipment. Fixes #4075 Many IP cameras, VoIP systems, and IoT devices use G.711 (PCMU/PCMA) as their default audio codec. Previously, SRS only supported Opus for WebRTC audio, requiring transcoding or rejecting G.711 streams entirely. This PR enables direct relay of G.711 audio streams in WebRTC, similar to how VP9/AV1 video codecs are supported. Enhanced WHIP/WHEP players with URL-based codec selection: ``` # Audio codec only http://localhost:8080/players/whip.html?acodec=pcmu http://localhost:8080/players/whip.html?acodec=pcma # Video + audio codecs http://localhost:8080/players/whip.html?vcodec=vp9&acodec=pcmu http://localhost:8080/players/whep.html?vcodec=h264&acodec=pcma # Backward compatible (codec = vcodec) http://localhost:8080/players/whip.html?codec=vp9 ``` Testing ```bash # Build and run unit tests cd trunk make utest -j && ./objs/srs_utest # Test with WHIP player # 1. Start SRS server ./objs/srs -c conf/rtc.conf # 2. Open WHIP publisher with PCMU audio http://localhost:8080/players/whip.html?acodec=pcmu # 3. Open WHEP player to receive stream http://localhost:8080/players/whep.html ``` ## Related Issues - Fixes #4075 - WebRTC G.711A Audio Codec Support - Related to #4548 - VP9 codec support (similar relay-only pattern)
1 parent 7fcd406 commit bfb91f9

20 files changed

+463
-43
lines changed

trunk/3rdparty/srs-docs/doc/webrtc.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ DVR recording, or maximum compatibility.
456456

457457
## VP9 Codec Support
458458

459-
SRS supports VP9 codec for WebRTC-to-WebRTC streaming since v7.0.0 ([#4548](https://github.com/ossrs/srs/issues/4548)).
459+
SRS supports VP9 codec for WebRTC-to-WebRTC streaming since v7.0.123 ([#4548](https://github.com/ossrs/srs/issues/4548)).
460460
VP9 is a royalty-free codec that saves 20-40% bandwidth compared to H.264. VP9 works better than H.264/H.265 with congestion control
461461
in WebRTC, making it ideal for keeping streams live under network fluctuations. SRS implements VP9 as relay-only (SFU mode),
462462
accepting VP9 streams via WHIP and forwarding to WHEP players without transcoding. VP9 streams cannot be converted to

trunk/doc/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The changelog for SRS.
77
<a name="v7-changes"></a>
88

99
## SRS 7.0 Changelog
10+
* v7.0, 2025-11-09, AI: WebRTC: Support G.711 (PCMU/PCMA) audio codec for WebRTC. v7.0.124 (#4075)
1011
* v7.0, 2025-11-08, AI: WebRTC: Support VP9 codec for WebRTC-to-WebRTC streaming. v7.0.123 (#4548)
1112
* v7.0, 2025-11-08, AI: API: Add audio_frames and video_frames to HTTP API. v7.0.122 (#4559)
1213
* v7.0, 2025-11-07, AI: WHIP: Return detailed HTTP error responses with proper status codes. v7.0.121 (#4502)

trunk/research/players/js/srs.page.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function update_nav() {
2424
$("#nav_vlc").attr("href", "vlc.html" + window.location.search);
2525
}
2626

27-
// Special extra params, such as auth_key.
27+
// Special extra params, such as auth_key, codec, vcodec, acodec.
2828
function user_extra_params(query, params, rtc) {
2929
var queries = params || [];
3030

@@ -124,6 +124,9 @@ function build_default_whip_whep_url(query, apiPath) {
124124
console.log('?api=x to overwrite WebRTC API(1985).');
125125
console.log('?schema=http|https to overwrite WebRTC API protocol.');
126126
console.log(`?path=xxx to overwrite default ${apiPath}`);
127+
console.log('?codec=xxx to specify video codec (alias for vcodec, e.g., h264, vp9, av1)');
128+
console.log('?vcodec=xxx to specify video codec (e.g., h264, vp9, av1)');
129+
console.log('?acodec=xxx to specify audio codec (e.g., opus, pcmu, pcma)');
127130

128131
var server = (!query.server)? window.location.hostname:query.server;
129132
var vhost = (!query.vhost)? window.location.hostname:query.vhost;

trunk/research/players/js/srs.sdk.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ function SrsRtcWhipWhepAsync() {
4141
// camera: boolean, whether capture video from camera, default to true.
4242
// screen: boolean, whether capture video from screen, default to false.
4343
// audio: boolean, whether play audio, default to true.
44+
// vcodec: string, video codec to use (e.g., 'h264', 'vp9', 'av1'), default to undefined.
45+
// acodec: string, audio codec to use (e.g., 'opus', 'pcmu', 'pcma'), default to undefined.
4446
self.publish = async function (url, options) {
4547
if (url.indexOf('/whip/') === -1) throw new Error(`invalid WHIP url ${url}`);
4648
const hasAudio = options?.audio ?? true;
4749
const useCamera = options?.camera ?? true;
4850
const useScreen = options?.screen ?? false;
51+
const vcodec = options?.vcodec;
52+
const acodec = options?.acodec;
4953

5054
if (!hasAudio && !useCamera && !useScreen) throw new Error(`The camera, screen and audio can't be false at the same time`);
5155

@@ -91,6 +95,13 @@ function SrsRtcWhipWhepAsync() {
9195

9296
var offer = await self.pc.createOffer();
9397
await self.pc.setLocalDescription(offer);
98+
99+
// Filter codecs if specified
100+
if (vcodec || acodec) {
101+
offer.sdp = self.__internal.filterCodec(offer.sdp, vcodec, acodec);
102+
console.log(`Filtered codecs (vcodec=${vcodec}, acodec=${acodec}): ${offer.sdp}`);
103+
}
104+
94105
const answer = await new Promise(function (resolve, reject) {
95106
console.log(`Generated offer: ${offer.sdp}`);
96107

@@ -119,15 +130,26 @@ function SrsRtcWhipWhepAsync() {
119130
// @options The options to control playing, supports:
120131
// videoOnly: boolean, whether only play video, default to false.
121132
// audioOnly: boolean, whether only play audio, default to false.
133+
// vcodec: string, video codec to use (e.g., 'h264', 'vp9', 'av1'), default to undefined.
134+
// acodec: string, audio codec to use (e.g., 'opus', 'pcmu', 'pcma'), default to undefined.
122135
self.play = async function(url, options) {
123136
if (url.indexOf('/whip-play/') === -1 && url.indexOf('/whep/') === -1) throw new Error(`invalid WHEP url ${url}`);
124137
if (options?.videoOnly && options?.audioOnly) throw new Error(`The videoOnly and audioOnly in options can't be true at the same time`);
138+
const vcodec = options?.vcodec;
139+
const acodec = options?.acodec;
125140

126141
if (!options?.videoOnly) self.pc.addTransceiver("audio", {direction: "recvonly"});
127142
if (!options?.audioOnly) self.pc.addTransceiver("video", {direction: "recvonly"});
128143

129144
var offer = await self.pc.createOffer();
130145
await self.pc.setLocalDescription(offer);
146+
147+
// Filter codecs if specified
148+
if (vcodec || acodec) {
149+
offer.sdp = self.__internal.filterCodec(offer.sdp, vcodec, acodec);
150+
console.log(`Filtered codecs (vcodec=${vcodec}, acodec=${acodec}): ${offer.sdp}`);
151+
}
152+
131153
const answer = await new Promise(function(resolve, reject) {
132154
console.log(`Generated offer: ${offer.sdp}`);
133155

@@ -199,6 +221,43 @@ function SrsRtcWhipWhepAsync() {
199221
simulator: a.protocol + '//' + a.host + '/rtc/v1/nack/',
200222
};
201223
},
224+
filterCodec: (sdp, vcodec, acodec) => {
225+
// Filter video codec if specified
226+
if (vcodec) {
227+
const vcodecUpper = vcodec.toUpperCase();
228+
sdp = sdp.split('\n').filter(line => {
229+
// Keep all non-video lines
230+
if (!line.startsWith('a=rtpmap:') && !line.startsWith('a=rtcp-fb:') &&
231+
!line.startsWith('a=fmtp:')) {
232+
return true;
233+
}
234+
// For video codec lines, only keep the specified codec
235+
if (line.includes('video/')) {
236+
return line.toUpperCase().includes(vcodecUpper);
237+
}
238+
return true;
239+
}).join('\n');
240+
}
241+
242+
// Filter audio codec if specified
243+
if (acodec) {
244+
const acodecUpper = acodec.toUpperCase();
245+
sdp = sdp.split('\n').filter(line => {
246+
// Keep all non-audio lines
247+
if (!line.startsWith('a=rtpmap:') && !line.startsWith('a=rtcp-fb:') &&
248+
!line.startsWith('a=fmtp:')) {
249+
return true;
250+
}
251+
// For audio codec lines, only keep the specified codec
252+
if (line.includes('audio/')) {
253+
return line.toUpperCase().includes(acodecUpper);
254+
}
255+
return true;
256+
}).join('\n');
257+
}
258+
259+
return sdp;
260+
},
202261
};
203262

204263
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/ontrack

trunk/research/players/whep.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,17 @@
125125

126126
// For example: webrtc://r.ossrs.net/live/livestream
127127
var url = $("#txt_url").val();
128+
var query = parse_query_string();
129+
130+
// Support codec parameters: codec (alias for vcodec), vcodec, acodec
131+
var vcodec = query.vcodec || query.codec;
132+
var acodec = query.acodec;
133+
128134
sdk.play(url, {
129135
videoOnly: $('#ch_videoonly').prop('checked'),
130136
audioOnly: $('#ch_audioonly').prop('checked'),
137+
vcodec: vcodec,
138+
acodec: acodec
131139
}).then(function(session){
132140
$('#sessionid').html(session.sessionid);
133141
$('#simulator-drop').attr('href', session.simulator + '?drop=1&username=' + session.sessionid);

trunk/research/players/whip.html

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,18 @@
132132

133133
// For example: webrtc://r.ossrs.net/live/livestream
134134
var url = $("#txt_url").val();
135+
var query = parse_query_string();
136+
137+
// Support codec parameters: codec (alias for vcodec), vcodec, acodec
138+
var vcodec = query.vcodec || query.codec;
139+
var acodec = query.acodec;
140+
135141
sdk.publish(url, {
136142
camera: $('#ra_camera').prop('checked'),
137143
screen: $('#ra_screen').prop('checked'),
138-
audio: $('#ch_audio').prop('checked')
144+
audio: $('#ch_audio').prop('checked'),
145+
vcodec: vcodec,
146+
acodec: acodec
139147
}).then(function(session){
140148
$('#sessionid').html(session.sessionid);
141149
$('#simulator-drop').attr('href', session.simulator + '?drop=1&username=' + session.sessionid);

trunk/src/app/srs_app_log.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include <srs_kernel_utility.hpp>
2121

2222
// the max size of a line of log.
23-
#define LOG_MAX_SIZE 8192
23+
#define LOG_MAX_SIZE 65536 // 64 KB
2424

2525
// the tail append to each log.
2626
#define LOG_TAIL '\n'

trunk/src/app/srs_app_rtc_api.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,25 @@ srs_error_t SrsGoApiRtcPlay::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
158158
if (eip.empty()) {
159159
eip = r->query_get("candidate");
160160
}
161-
string codec = r->query_get("codec");
161+
// Support vcodec/codec (alias for vcodec) and acodec parameters
162+
string vcodec = r->query_get("vcodec");
163+
if (vcodec.empty()) {
164+
vcodec = r->query_get("codec");
165+
}
166+
string acodec = r->query_get("acodec");
162167
// For client to specifies whether encrypt by SRTP.
163168
string srtp = r->query_get("encrypt");
164169
string dtls = r->query_get("dtls");
165170

166171
srs_trace(
167-
"RTC play %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s",
172+
"RTC play %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, vcodec=%s, acodec=%s, srtp=%s, dtls=%s",
168173
streamurl.c_str(), api.c_str(), tid.c_str(), clientip.c_str(), ruc.req_->app_.c_str(),
169174
ruc.req_->stream_.c_str(), remote_sdp_str.length(),
170-
eip.c_str(), codec.c_str(), srtp.c_str(), dtls.c_str());
175+
eip.c_str(), vcodec.c_str(), acodec.c_str(), srtp.c_str(), dtls.c_str());
171176

172177
ruc.eip_ = eip;
173-
ruc.codec_ = codec;
178+
ruc.vcodec_ = vcodec;
179+
ruc.acodec_ = acodec;
174180
ruc.publish_ = false;
175181
ruc.dtls_ = (dtls != "false");
176182

@@ -479,14 +485,20 @@ srs_error_t SrsGoApiRtcPublish::do_serve_http(ISrsHttpResponseWriter *w, ISrsHtt
479485
if (eip.empty()) {
480486
eip = r->query_get("candidate");
481487
}
482-
string codec = r->query_get("codec");
488+
// Support vcodec/codec (alias for vcodec) and acodec parameters
489+
string vcodec = r->query_get("vcodec");
490+
if (vcodec.empty()) {
491+
vcodec = r->query_get("codec");
492+
}
493+
string acodec = r->query_get("acodec");
483494

484-
srs_trace("RTC publish %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s",
495+
srs_trace("RTC publish %s, api=%s, tid=%s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, vcodec=%s, acodec=%s",
485496
streamurl.c_str(), api.c_str(), tid.c_str(), clientip.c_str(), ruc.req_->app_.c_str(), ruc.req_->stream_.c_str(),
486-
remote_sdp_str.length(), eip.c_str(), codec.c_str());
497+
remote_sdp_str.length(), eip.c_str(), vcodec.c_str(), acodec.c_str());
487498

488499
ruc.eip_ = eip;
489-
ruc.codec_ = codec;
500+
ruc.vcodec_ = vcodec;
501+
ruc.acodec_ = acodec;
490502
ruc.publish_ = true;
491503
ruc.dtls_ = ruc.srtp_ = true;
492504

@@ -776,7 +788,12 @@ srs_error_t SrsGoApiRtcWhip::do_serve_http_with(ISrsHttpResponseWriter *w, ISrsH
776788
if (eip.empty()) {
777789
eip = r->query_get("candidate");
778790
}
779-
string codec = r->query_get("codec");
791+
// Support vcodec/codec (alias for vcodec) and acodec parameters
792+
string vcodec = r->query_get("vcodec");
793+
if (vcodec.empty()) {
794+
vcodec = r->query_get("codec");
795+
}
796+
string acodec = r->query_get("acodec");
780797
string app = r->query_get("app");
781798
string stream = r->query_get("stream");
782799
string action = r->query_get("action");
@@ -815,13 +832,14 @@ srs_error_t SrsGoApiRtcWhip::do_serve_http_with(ISrsHttpResponseWriter *w, ISrsH
815832
string srtp = r->query_get("encrypt");
816833
string dtls = r->query_get("dtls");
817834

818-
srs_trace("RTC whip %s %s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, codec=%s, srtp=%s, dtls=%s, ufrag=%s, pwd=%s, param=%s",
835+
srs_trace("RTC whip %s %s, clientip=%s, app=%s, stream=%s, offer=%dB, eip=%s, vcodec=%s, acodec=%s, srtp=%s, dtls=%s, ufrag=%s, pwd=%s, param=%s",
819836
action.c_str(), ruc->req_->get_stream_url().c_str(), clientip.c_str(), ruc->req_->app_.c_str(), ruc->req_->stream_.c_str(),
820-
remote_sdp_str.length(), eip.c_str(), codec.c_str(), srtp.c_str(), dtls.c_str(), ruc->req_->ice_ufrag_.c_str(),
837+
remote_sdp_str.length(), eip.c_str(), vcodec.c_str(), acodec.c_str(), srtp.c_str(), dtls.c_str(), ruc->req_->ice_ufrag_.c_str(),
821838
ruc->req_->ice_pwd_.c_str(), ruc->req_->param_.c_str());
822839

823840
ruc->eip_ = eip;
824-
ruc->codec_ = codec;
841+
ruc->vcodec_ = vcodec;
842+
ruc->acodec_ = acodec;
825843
ruc->publish_ = (action == "publish");
826844

827845
// For client to specifies whether encrypt by SRTP.

trunk/src/app/srs_app_rtc_conn.cpp

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3335,10 +3335,30 @@ srs_error_t SrsRtcPublisherNegotiator::negotiate_publish_capability(SrsRtcUserCo
33353335
// Update the ruc, which is about user specified configuration.
33363336
ruc->audio_before_video_ = !nn_any_video_parsed;
33373337

3338-
// TODO: check opus format specific param
3339-
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
3338+
// Try to find audio codec based on user preference or default order
3339+
std::vector<SrsMediaPayloadType> payloads;
3340+
3341+
// If user specified audio codec, try that first
3342+
if (!ruc->acodec_.empty()) {
3343+
payloads = remote_media_desc.find_media_with_encoding_name(ruc->acodec_);
3344+
if (payloads.empty()) {
3345+
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found %s audio payload type", ruc->acodec_.c_str());
3346+
}
3347+
} else {
3348+
// Default order: Opus, PCMU (G.711 μ-law), PCMA (G.711 A-law)
3349+
// Prioritize PCMU over PCMA as per Chrome SDP order
3350+
payloads = remote_media_desc.find_media_with_encoding_name("opus");
3351+
if (payloads.empty()) {
3352+
// Then try PCMU (G.711 μ-law)
3353+
payloads = remote_media_desc.find_media_with_encoding_name("PCMU");
3354+
}
3355+
if (payloads.empty()) {
3356+
// Finally try PCMA (G.711 A-law)
3357+
payloads = remote_media_desc.find_media_with_encoding_name("PCMA");
3358+
}
3359+
}
33403360
if (payloads.empty()) {
3341-
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found opus payload type");
3361+
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found audio payload type (opus/PCMU/PCMA)");
33423362
}
33433363

33443364
for (int j = 0; j < (int)payloads.size(); j++) {
@@ -3366,10 +3386,10 @@ srs_error_t SrsRtcPublisherNegotiator::negotiate_publish_capability(SrsRtcUserCo
33663386

33673387
track_desc->type_ = "audio";
33683388
track_desc->set_codec_payload((SrsCodecPayload *)audio_payload);
3369-
// Only choose one match opus codec.
3389+
// Only choose one match audio codec.
33703390
break;
33713391
}
3372-
} else if (remote_media_desc.is_video() && srs_video_codec_str2id(ruc->codec_) == SrsVideoCodecIdAV1) {
3392+
} else if (remote_media_desc.is_video() && srs_video_codec_str2id(ruc->vcodec_) == SrsVideoCodecIdAV1) {
33733393
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("AV1");
33743394
if (payloads.empty()) {
33753395
// Be compatible with the Chrome M96, still check the AV1X encoding name
@@ -3406,7 +3426,7 @@ srs_error_t SrsRtcPublisherNegotiator::negotiate_publish_capability(SrsRtcUserCo
34063426
track_desc->set_codec_payload((SrsCodecPayload *)video_payload);
34073427
break;
34083428
}
3409-
} else if (remote_media_desc.is_video() && srs_video_codec_str2id(ruc->codec_) == SrsVideoCodecIdVP9) {
3429+
} else if (remote_media_desc.is_video() && srs_video_codec_str2id(ruc->vcodec_) == SrsVideoCodecIdVP9) {
34103430
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("VP9");
34113431
if (payloads.empty()) {
34123432
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid VP9 payload type");
@@ -3438,7 +3458,7 @@ srs_error_t SrsRtcPublisherNegotiator::negotiate_publish_capability(SrsRtcUserCo
34383458
track_desc->set_codec_payload((SrsCodecPayload *)video_payload);
34393459
break;
34403460
}
3441-
} else if (remote_media_desc.is_video() && srs_video_codec_str2id(ruc->codec_) == SrsVideoCodecIdHEVC) {
3461+
} else if (remote_media_desc.is_video() && srs_video_codec_str2id(ruc->vcodec_) == SrsVideoCodecIdHEVC) {
34423462
std::vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("H265");
34433463
if (payloads.empty()) {
34443464
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no found valid H.265 payload type");
@@ -3797,16 +3817,33 @@ srs_error_t SrsRtcPlayerNegotiator::negotiate_play_capability(SrsRtcUserConfig *
37973817
// Update the ruc, which is about user specified configuration.
37983818
ruc->audio_before_video_ = !nn_any_video_parsed;
37993819

3800-
// TODO: check opus format specific param
3801-
vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name("opus");
3820+
// Try to find audio tracks in source with different codec names
3821+
// Try Opus first (most common), then PCMU, then PCMA
3822+
std::vector<SrsRtcTrackDescription *> source_audio_tracks = source->get_track_desc("audio", "opus");
3823+
std::string source_audio_codec = "opus";
3824+
3825+
if (source_audio_tracks.empty()) {
3826+
source_audio_tracks = source->get_track_desc("audio", "PCMU");
3827+
source_audio_codec = "PCMU";
3828+
}
3829+
if (source_audio_tracks.empty()) {
3830+
source_audio_tracks = source->get_track_desc("audio", "PCMA");
3831+
source_audio_codec = "PCMA";
3832+
}
3833+
if (source_audio_tracks.empty()) {
3834+
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no audio track in source (tried opus/PCMU/PCMA)");
3835+
}
3836+
3837+
// Try to find matching codec in remote SDP
3838+
vector<SrsMediaPayloadType> payloads = remote_media_desc.find_media_with_encoding_name(source_audio_codec);
38023839
if (payloads.empty()) {
3803-
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found opus payload type");
3840+
return srs_error_new(ERROR_RTC_SDP_EXCHANGE, "no valid found %s payload type", source_audio_codec.c_str());
38043841
}
38053842

38063843
remote_payload = payloads.at(0);
3807-
track_descs = source->get_track_desc("audio", "opus");
3844+
track_descs = source_audio_tracks;
38083845
} else if (remote_media_desc.is_video()) {
3809-
SrsVideoCodecId prefer_codec = srs_video_codec_str2id(ruc->codec_);
3846+
SrsVideoCodecId prefer_codec = srs_video_codec_str2id(ruc->vcodec_);
38103847
if (prefer_codec == SrsVideoCodecIdReserved) {
38113848
// Get the source codec if not specified.
38123849
std::vector<SrsRtcTrackDescription *> source_track_descs = source->get_track_desc("video", "");

trunk/src/app/srs_app_rtc_server.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ class SrsRtcUserConfig
7070
std::string remote_sdp_str_;
7171
SrsSdp remote_sdp_;
7272
std::string eip_;
73-
std::string codec_;
73+
std::string vcodec_; // Video codec
74+
std::string acodec_; // Audio codec
7475
std::string api_;
7576

7677
// Session data.

0 commit comments

Comments
 (0)