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)
151 lines
4.1 KiB
C++
151 lines
4.1 KiB
C++
//
|
|
// Copyright (c) 2013-2025 The SRS Authors
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
|
|
#ifndef SRS_APP_RTC_SERVER_HPP
|
|
#define SRS_APP_RTC_SERVER_HPP
|
|
|
|
#include <srs_core.hpp>
|
|
|
|
#include <srs_app_async_call.hpp>
|
|
#include <srs_app_listener.hpp>
|
|
#include <srs_app_reload.hpp>
|
|
#include <srs_app_rtc_conn.hpp>
|
|
#include <srs_app_st.hpp>
|
|
#include <srs_kernel_hourglass.hpp>
|
|
#include <srs_protocol_sdp.hpp>
|
|
|
|
#include <set>
|
|
#include <string>
|
|
|
|
class SrsRtcServer;
|
|
class SrsHourGlass;
|
|
class SrsRtcConnection;
|
|
class ISrsRtcConnection;
|
|
class ISrsRequest;
|
|
class SrsSdp;
|
|
class SrsRtcSource;
|
|
class SrsResourceManager;
|
|
class SrsAsyncCallWorker;
|
|
class ISrsUdpMuxSocket;
|
|
class ISrsResourceManager;
|
|
class ISrsStreamPublishTokenManager;
|
|
class ISrsRtcSourceManager;
|
|
class ISrsDtlsCertificate;
|
|
class ISrsAppConfig;
|
|
class ISrsAppFactory;
|
|
class ISrsProtocolUtility;
|
|
|
|
// The UDP black hole, for developer to use wireshark to catch plaintext packets.
|
|
// For example, server receive UDP packets at udp://8000, and forward the plaintext packet to black hole,
|
|
// we can use wireshark to capture the plaintext.
|
|
class SrsRtcBlackhole
|
|
{
|
|
public:
|
|
bool blackhole_;
|
|
|
|
// clang-format off
|
|
SRS_DECLARE_PRIVATE: // clang-format on
|
|
sockaddr_in *blackhole_addr_;
|
|
srs_netfd_t blackhole_stfd_;
|
|
|
|
public:
|
|
SrsRtcBlackhole();
|
|
virtual ~SrsRtcBlackhole();
|
|
|
|
public:
|
|
srs_error_t initialize();
|
|
void sendto(void *data, int len);
|
|
};
|
|
|
|
extern SrsRtcBlackhole *_srs_blackhole;
|
|
|
|
// The user config for RTC publish or play.
|
|
class SrsRtcUserConfig
|
|
{
|
|
public:
|
|
// Original variables from API.
|
|
std::string remote_sdp_str_;
|
|
SrsSdp remote_sdp_;
|
|
std::string eip_;
|
|
std::string vcodec_; // Video codec
|
|
std::string acodec_; // Audio codec
|
|
std::string api_;
|
|
|
|
// Session data.
|
|
std::string local_sdp_str_;
|
|
std::string session_id_;
|
|
std::string token_;
|
|
|
|
// Generated data.
|
|
ISrsRequest *req_;
|
|
bool publish_;
|
|
bool dtls_;
|
|
bool srtp_;
|
|
|
|
// The order of audio and video, or whether audio is before video. Please make sure the order is match for offer and
|
|
// answer, or client might fail at setRemoveDescription(answer). See https://github.com/ossrs/srs/issues/3179
|
|
bool audio_before_video_;
|
|
|
|
public:
|
|
SrsRtcUserConfig();
|
|
virtual ~SrsRtcUserConfig();
|
|
};
|
|
|
|
// Discover the candidates for RTC server.
|
|
extern std::set<std::string> discover_candidates(ISrsProtocolUtility *utility, ISrsAppConfig *config, SrsRtcUserConfig *ruc);
|
|
|
|
// The dns resolve utility, return the resolved ip address.
|
|
extern std::string srs_dns_resolve(std::string host, int &family);
|
|
|
|
// RTC session manager to handle WebRTC session lifecycle and management.
|
|
class SrsRtcSessionManager : public ISrsExecRtcAsyncTask
|
|
{
|
|
// clang-format off
|
|
SRS_DECLARE_PRIVATE: // clang-format on
|
|
ISrsResourceManager *conn_manager_;
|
|
ISrsStreamPublishTokenManager *stream_publish_tokens_;
|
|
ISrsRtcSourceManager *rtc_sources_;
|
|
ISrsDtlsCertificate *dtls_certificate_;
|
|
ISrsAppConfig *config_;
|
|
ISrsAppFactory *app_factory_;
|
|
|
|
// clang-format off
|
|
SRS_DECLARE_PRIVATE: // clang-format on
|
|
// WebRTC async call worker for non-blocking operations.
|
|
SrsAsyncCallWorker *rtc_async_;
|
|
|
|
public:
|
|
SrsRtcSessionManager();
|
|
virtual ~SrsRtcSessionManager();
|
|
|
|
public:
|
|
virtual srs_error_t initialize();
|
|
|
|
public:
|
|
virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
|
|
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession);
|
|
|
|
// clang-format off
|
|
SRS_DECLARE_PRIVATE: // clang-format on
|
|
virtual srs_error_t do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection *session);
|
|
|
|
public:
|
|
virtual void srs_update_rtc_sessions();
|
|
|
|
// interface ISrsExecRtcAsyncTask
|
|
public:
|
|
virtual srs_error_t exec_rtc_async_work(ISrsAsyncCallTask *t);
|
|
|
|
public:
|
|
virtual srs_error_t on_udp_packet(ISrsUdpMuxSocket *skt);
|
|
};
|
|
|
|
// Helper function to discover candidate IPs from API server hostname
|
|
// Used by WebRTC ICE candidate discovery
|
|
extern srs_error_t api_server_as_candidates(ISrsAppConfig *config, std::string api, std::set<std::string> &candidate_ips);
|
|
|
|
#endif
|