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)
175 lines
4.1 KiB
C++
175 lines
4.1 KiB
C++
//
|
|
// Copyright (c) 2013-2025 The SRS Authors
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
|
|
#include <srs_app_log.hpp>
|
|
|
|
#include <stdarg.h>
|
|
#include <sys/time.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <srs_app_config.hpp>
|
|
#include <srs_app_utility.hpp>
|
|
#include <srs_kernel_error.hpp>
|
|
#include <srs_kernel_utility.hpp>
|
|
|
|
// the max size of a line of log.
|
|
#define LOG_MAX_SIZE 65536 // 64 KB
|
|
|
|
// the tail append to each log.
|
|
#define LOG_TAIL '\n'
|
|
// reserved for the end of log data, it must be strlen(LOG_TAIL)
|
|
#define LOG_TAIL_SIZE 1
|
|
|
|
SrsFileLog::SrsFileLog()
|
|
{
|
|
level_ = SrsLogLevelTrace;
|
|
log_data_ = new char[LOG_MAX_SIZE];
|
|
|
|
fd_ = -1;
|
|
log_to_file_tank_ = false;
|
|
utc_ = false;
|
|
}
|
|
|
|
SrsFileLog::~SrsFileLog()
|
|
{
|
|
srs_freepa(log_data_);
|
|
|
|
if (fd_ > 0) {
|
|
::close(fd_);
|
|
fd_ = -1;
|
|
}
|
|
|
|
if (_srs_config) {
|
|
_srs_config->unsubscribe(this);
|
|
}
|
|
}
|
|
|
|
// LCOV_EXCL_START
|
|
srs_error_t SrsFileLog::initialize()
|
|
{
|
|
if (_srs_config) {
|
|
_srs_config->subscribe(this);
|
|
|
|
log_to_file_tank_ = _srs_config->get_log_tank_file();
|
|
utc_ = _srs_config->get_utc_time();
|
|
|
|
std::string level = _srs_config->get_log_level();
|
|
std::string level_v2 = _srs_config->get_log_level_v2();
|
|
level_ = level_v2.empty() ? srs_get_log_level(level) : srs_get_log_level_v2(level_v2);
|
|
}
|
|
|
|
return srs_success;
|
|
}
|
|
|
|
void SrsFileLog::reopen()
|
|
{
|
|
if (fd_ > 0) {
|
|
::close(fd_);
|
|
}
|
|
|
|
if (!log_to_file_tank_) {
|
|
return;
|
|
}
|
|
|
|
open_log_file();
|
|
}
|
|
|
|
void SrsFileLog::log(SrsLogLevel level, const char *tag, const SrsContextId &context_id, const char *fmt, va_list args)
|
|
{
|
|
if (level < level_ || level >= SrsLogLevelDisabled) {
|
|
return;
|
|
}
|
|
|
|
int size = 0;
|
|
bool header_ok = srs_log_header(
|
|
log_data_, LOG_MAX_SIZE, utc_, level >= SrsLogLevelWarn, tag, context_id, srs_log_level_strings[level], &size);
|
|
if (!header_ok) {
|
|
return;
|
|
}
|
|
|
|
// Something not expected, drop the log.
|
|
int r0 = vsnprintf(log_data_ + size, LOG_MAX_SIZE - size, fmt, args);
|
|
if (r0 <= 0 || r0 >= LOG_MAX_SIZE - size) {
|
|
return;
|
|
}
|
|
size += r0;
|
|
|
|
// Add errno and strerror() if error. Check size to avoid security issue https://github.com/ossrs/srs/issues/1229
|
|
if (level == SrsLogLevelError && errno != 0 && size < LOG_MAX_SIZE) {
|
|
r0 = snprintf(log_data_ + size, LOG_MAX_SIZE - size, "(%s)", strerror(errno));
|
|
|
|
// Something not expected, drop the log.
|
|
if (r0 <= 0 || r0 >= LOG_MAX_SIZE - size) {
|
|
return;
|
|
}
|
|
size += r0;
|
|
}
|
|
|
|
write_log(fd_, log_data_, size, level);
|
|
}
|
|
|
|
void SrsFileLog::write_log(int &fd, char *str_log, int size, int level)
|
|
{
|
|
// ensure the tail and EOF of string
|
|
// LOG_TAIL_SIZE for the TAIL char.
|
|
// 1 for the last char(0).
|
|
size = srs_min(LOG_MAX_SIZE - 1 - LOG_TAIL_SIZE, size);
|
|
|
|
// add some to the end of char.
|
|
str_log[size++] = LOG_TAIL;
|
|
|
|
// if not to file, to console and return.
|
|
if (!log_to_file_tank_) {
|
|
// if is error msg, then print color msg.
|
|
// \033[31m : red text code in shell
|
|
// \033[32m : green text code in shell
|
|
// \033[33m : yellow text code in shell
|
|
// \033[0m : normal text code
|
|
if (level <= SrsLogLevelTrace) {
|
|
printf("%.*s", size, str_log);
|
|
} else if (level == SrsLogLevelWarn) {
|
|
printf("\033[33m%.*s\033[0m", size, str_log);
|
|
} else {
|
|
printf("\033[31m%.*s\033[0m", size, str_log);
|
|
}
|
|
fflush(stdout);
|
|
|
|
return;
|
|
}
|
|
|
|
// open log file. if specified
|
|
if (fd < 0) {
|
|
open_log_file();
|
|
}
|
|
|
|
// write log to file.
|
|
if (fd > 0) {
|
|
::write(fd, str_log, size);
|
|
}
|
|
}
|
|
|
|
void SrsFileLog::open_log_file()
|
|
{
|
|
if (!_srs_config) {
|
|
return;
|
|
}
|
|
|
|
std::string filename = _srs_config->get_log_file();
|
|
|
|
if (filename.empty()) {
|
|
return;
|
|
}
|
|
|
|
fd_ = ::open(filename.c_str(),
|
|
O_RDWR | O_CREAT | O_APPEND,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
|
|
}
|
|
// LCOV_EXCL_STOP
|