2025-10-22 23:00:44 -04:00
# SRS Repository Guidelines
2025-06-25 10:22:04 -04:00
project:
name: "SRS (Simple Realtime Server)"
2025-10-22 23:00:44 -04:00
type: "C++ streaming media server (RTMP/WebRTC/WHIP/WHEP/SRT/HLS/HTTP-FLV)"
2025-06-25 10:22:04 -04:00
architecture:
2025-10-22 23:00:44 -04:00
overview: "C++ streaming server using State Threads (ST) for coroutine-based networking"
threading: "Single-threaded coroutine architecture - no multi-threading, context switches on async I/O"
key_classes: "SrsServer (main), SrsLiveSource (source management)"
webrtc: "SFU (Selective Forwarding Unit) - media relay without transcoding. Supports WHIP/WHEP. NO external TURN needed (SRS provides ICE/relay)"
origin_cluster:
v6_mesh: "DEPRECATED - C++ MESH mode, RTMP only"
v7_proxy: "RECOMMENDED - Go-based proxy (github.com/ossrs/proxy-go), all protocols, better scaling"
2025-08-12 10:35:04 -04:00
2025-07-01 09:00:56 -04:00
codebase_structure:
2025-10-22 23:00:44 -04:00
dirs: "trunk/src/{main,core,kernel,protocol,app}"
key_files: "srs_main_server.cpp, srs_app_server.hpp, srs_core.hpp, srs_core_autofree.hpp"
2025-06-25 10:22:04 -04:00
code_patterns:
2025-10-04 22:41:09 -04:00
dependency_inversion:
2025-10-22 23:00:44 -04:00
principle: "MANDATORY - Depend on interfaces (ISrs*), not concrete classes (Srs*)"
2025-10-04 22:41:09 -04:00
rules:
2025-10-22 23:00:44 -04:00
- "Member variables use ISrs* types (e.g., ISrsMessageQueue *queue_)"
- "Concrete types only for instantiation"
- "Enables mocking for unit tests"
2025-10-04 22:41:09 -04:00
avoid_global_variables:
2025-10-22 23:00:44 -04:00
principle: "MANDATORY - Store globals (_srs_*) as member fields for mockability"
pattern: |
Constructor: config_ = _srs_config; // Store reference
Destructor: config_ = NULL; // Set NULL, don't free
Methods: Use config_->method() instead of _srs_config->method()
common_globals: "_srs_config, _srs_sources, _srs_stat, _srs_hooks, _srs_context, _srs_log, _srs_server"
2025-10-04 22:41:09 -04:00
2025-07-11 10:27:57 -04:00
cpp_compatibility:
2025-10-22 23:00:44 -04:00
standard: "C++98 only - NO C++11+ features"
forbidden: "auto, nullptr, range-for, lambda, std::unique_ptr, enum class, override, constexpr"
use: "NULL, traditional for-loops, typedef, SrsUniquePtr/SrsSharedPtr"
2025-07-11 10:27:57 -04:00
2025-06-25 10:22:04 -04:00
memory_management:
2025-10-22 23:00:44 -04:00
smart_pointers: "SrsUniquePtr<T> (unique ownership), SrsSharedPtr<T> (shared ownership)"
macros: "srs_freep (pointers), srs_freepa (arrays) - use only when smart pointers unsuitable"
2025-07-01 09:00:56 -04:00
2025-09-06 12:39:46 -04:00
naming_conventions:
- pattern: "field_naming"
2025-09-07 21:09:08 -04:00
description: "MANDATORY - All class and struct fields (member variables) must end with underscore (_), but NOT functions/methods"
2025-09-06 12:39:46 -04:00
usage: |
WRONG: Fields without underscore
class SrsBuffer {
private:
char *p;
int size;
public:
bool enabled;
};
2025-09-07 21:09:08 -04:00
CORRECT: Fields with underscore, functions without underscore
2025-09-06 12:39:46 -04:00
class SrsBuffer {
private:
char *p_;
int size_;
public:
bool enabled_;
2025-09-07 21:09:08 -04:00
public:
srs_error_t initialize();
2025-09-06 12:39:46 -04:00
};
2025-09-09 21:06:45 -04:00
scope: "Applies ONLY to fields (member variables) in classes and structs - NOT to functions, methods, comments, error messages, or parameters"
2025-09-06 12:39:46 -04:00
exceptions: "Only applies to SRS-defined classes/structs - do NOT change 3rd party code like llhttp"
2025-09-07 21:09:08 -04:00
rationale: "Consistent naming convention across SRS codebase for better code readability and maintenance - underscore distinguishes member variables from local variables and parameters"
2025-09-06 12:39:46 -04:00
2025-10-13 22:26:38 -04:00
access_specifiers_for_testing:
- pattern: "SRS_DECLARE_PRIVATE and SRS_DECLARE_PROTECTED macros"
description: "MANDATORY - Always use SRS_DECLARE_PRIVATE instead of 'private' and SRS_DECLARE_PROTECTED instead of 'protected' in class definitions"
purpose: "Enables unit tests to access private and protected members for dependency injection and mocking"
usage: |
CORRECT: Using SRS access specifier macros
class SrsBufferCache {
SRS_DECLARE_PRIVATE:
ISrsAppConfig *config_;
ISrsRequest *req_;
SRS_DECLARE_PROTECTED:
virtual srs_error_t do_start();
public:
srs_error_t start();
};
rules:
- "ALWAYS use SRS_DECLARE_PRIVATE instead of 'private' keyword"
- "ALWAYS use SRS_DECLARE_PROTECTED instead of 'protected' keyword"
- "Use standard 'public' keyword for public members (no macro needed)"
- "This applies to ALL production code classes in trunk/src/"
- "When SRS_FORCE_PUBLIC4UTEST is defined, all private/protected members become public for testing"
2025-09-06 12:39:46 -04:00
commenting_style:
- pattern: "C++ style comments"
description: "MANDATORY - Use C++ style comments (//) instead of C style comments (/* */)"
usage: |
WRONG: C style comments
/* This is a comment */
/**
* Multi-line comment
* with multiple lines
*/
CORRECT: C++ style comments
// This is a comment
// Multi-line comment
// with multiple lines
rationale: "Consistent with SRS codebase style and better for single-line documentation"
- pattern: "No thread-safety comments"
description: "Do NOT describe thread-safety in comments since SRS is a single-threaded application"
usage: |
WRONG: Mentioning thread-safety
// This implementation is thread-safe for single-threaded usage but may
// require external synchronization in multi-threaded environments.
CORRECT: Focus on functionality without thread-safety mentions
// This implementation maintains state between calls for consistent
// round-robin behavior across multiple selections.
rationale: "SRS uses single-threaded coroutine-based architecture, so thread-safety discussions are irrelevant and confusing"
2025-06-25 10:22:04 -04:00
error_handling:
- pattern: "srs_error_t"
2025-07-18 07:59:44 -04:00
description: "Custom error handling system - MANDATORY for all functions that return srs_error_t"
- pattern: "error_checking_and_wrapping"
description: "MANDATORY pattern for calling functions that return srs_error_t - ALWAYS check and wrap errors"
usage: |
WRONG: Not checking error return value (causes error object leak)
muxer_->write_audio(shared_audio, format);
reader.read(start, filesize, &nread);
CORRECT: Always check error and wrap with context
if ((err = muxer_->write_audio(shared_audio, format)) != srs_success) {
return srs_error_wrap(err, "write audio");
}
ssize_t nread = 0;
if ((err = reader.read(start, filesize, &nread)) != srs_success) {
return srs_error_wrap(err, "read %d only %d bytes", filesize, (int)nread);
}
rationale: |
- Prevents error object memory leaks
- Provides proper error context propagation
- Includes relevant local variables in error messages for debugging
- Maintains error chain for better troubleshooting
- pattern: "error_wrapping_with_context"
description: "When wrapping errors, include relevant local variables and context information"
rules:
- "Always include newly created local variables in error messages"
- "Include function parameters that provide context"
- "Use descriptive error messages that explain what operation failed"
- "Format numeric values appropriately (cast to int for display if needed)"
examples:
- "return srs_error_wrap(err, \"read %d only %d bytes\", filesize, (int)nread);"
- "return srs_error_wrap(err, \"write audio format=%d\", format->codec);"
- "return srs_error_wrap(err, \"connect to %s:%d\", host.c_str(), port);"
2025-07-08 08:33:09 -04:00
binary_data_handling:
- pattern: "SrsBuffer"
description: "MANDATORY utility for marshaling and unmarshaling structs with bytes - ALWAYS use this instead of manual byte manipulation"
usage: |
WRONG: Manual byte manipulation
char* buf = new char[4];
buf[0] = 0xff;
buf[1] = (value >> 8) & 0xFF;
buf[2] = value & 0xFF;
CORRECT: Use SrsBuffer
char* buf = new char[4];
SrsBuffer buffer(buf, 4);
buffer.write_1bytes(0xff);
buffer.write_2bytes(value);
key_methods:
read: "read_1bytes(), read_2bytes(), read_4bytes(), read_8bytes(), read_string(len), read_bytes(data, size)"
write: "write_1bytes(), write_2bytes(), write_4bytes(), write_8bytes(), write_string(), write_bytes()"
utility: "pos(), left(), empty(), require(size), skip(size)"
rationale: "Provides consistent byte-order handling, bounds checking, and position tracking for binary data operations"
2025-07-12 21:36:47 -04:00
time_handling:
- pattern: "srs_utime_t"
description: "MANDATORY type for all time variables - ALWAYS use srs_utime_t instead of int64_t for time values"
usage: |
WRONG: Using int64_t for time
int64_t now = srs_get_system_time();
int64_t duration = end_time - start_time;
int64_t timeout_ms = timeout / 1000;
CORRECT: Use srs_utime_t for time
srs_utime_t now = srs_get_system_time();
srs_utime_t duration = now - start_time;
int timeout_ms = srsu2msi(timeout);
rationale: "srs_utime_t provides consistent time handling across platforms and ensures proper microsecond precision"
- pattern: "srs_get_system_time()"
description: "Standard function to get current system time in microseconds"
usage: "srs_utime_t now = srs_get_system_time();"
- pattern: "duration calculation"
description: "Calculate time duration using simple subtraction"
usage: |
srs_utime_t starttime = srs_get_system_time();
// ... some operations ...
srs_utime_t duration = srs_get_system_time() - starttime;
int duration_ms = srsu2ms(duration);
note: "For simple cases, use direct subtraction. srs_duration() function is available for special cases with zero checks."
- pattern: "srsu2ms(us)"
description: "MANDATORY macro to convert microseconds to milliseconds - ALWAYS use instead of division by 1000"
usage: |
WRONG: Manual division
int ms = now / 1000;
int timeout_ms = timeout / 1000;
CORRECT: Use srsu2ms macro
int ms = srsu2ms(now);
int timeout_ms = srsu2ms(timeout);
rationale: "Provides consistent conversion and avoids magic numbers"
- pattern: "srsu2msi(us)"
description: "Convert microseconds to milliseconds as integer"
usage: "int ms = srsu2msi(timeout);"
- pattern: "srsu2s(us) / srsu2si(us)"
description: "Convert microseconds to seconds"
usage: "int seconds = srsu2si(duration);"
- pattern: "time_constants"
description: "Standard time constants for SRS"
constants:
- "SRS_UTIME_MILLISECONDS (1000) - microseconds in one millisecond"
- "SRS_UTIME_SECONDS (1000000LL) - microseconds in one second"
- "SRS_UTIME_MINUTES (60000000LL) - microseconds in one minute"
- "SRS_UTIME_HOURS (3600000000LL) - microseconds in one hour"
- "SRS_UTIME_NO_TIMEOUT - special value for no timeout"
- pattern: "best_practices"
description: "Time handling best practices"
practices:
- "Always declare time variables as srs_utime_t, never int64_t"
- "Use srs_get_system_time() to get current time"
- "Use simple subtraction (end - start) for calculating time differences"
- "Use srsu2ms() family macros for time unit conversions"
- "Use time constants (SRS_UTIME_SECONDS, etc.) for time arithmetic"
- "Check for SRS_UTIME_NO_TIMEOUT when handling timeout values"
2025-06-25 10:22:04 -04:00
conditional_compilation:
- pattern: "#ifdef SRS_VALGRIND"
description: "Valgrind support"
2025-07-04 08:39:23 -04:00
codec_handling:
enhanced_rtmp:
description: |
For HEVC and other new codecs in RTMP/FLV protocols, always use enhanced-RTMP codec fourcc instead of legacy RTMP codec IDs.
Enhanced-RTMP provides better support for modern codecs and is the preferred approach.
NOTE: This guidance applies ONLY to RTMP/FLV protocols, not to other protocols like SRT/WebRTC/WHIP/WHEP/HLS/DASH/GB28181.
hevc_codec:
- pattern: "fourcc: hvc1"
description: "Use enhanced-RTMP fourcc 'hvc1' for HEVC codec identification in RTMP/FLV"
usage: "Always use fourcc 'hvc1' for HEVC instead of legacy RTMP codecid(12)"
rationale: "Enhanced-RTMP fourcc provides better codec identification and compatibility"
general_rule:
- "Do NOT use legacy RTMP codecid(12) for HEVC"
- "Always prefer enhanced-RTMP codec fourcc for new codecs"
- "Use fourcc format for codec identification in enhanced-RTMP contexts"
- "This applies ONLY to RTMP/FLV protocols - other protocols use their own codec identification methods"
2025-07-01 09:00:56 -04:00
build_and_development:
build:
command: "make -j"
description: "Build the SRS server using parallel make in the trunk directory"
working_directory: "trunk"
2025-06-25 10:22:04 -04:00
2025-07-01 09:00:56 -04:00
run_utests:
command: "make utest -j && ./objs/srs_utest"
description: "Run the unit tests for SRS"
working_directory: "trunk"
2025-06-30 08:10:21 -04:00
2025-10-10 12:08:04 -04:00
run_blackbox_tests:
description: "Blackbox tests are integration tests that test SRS as a complete system using FFmpeg as client"
location: "trunk/3rdparty/srs-bench/blackbox/"
prerequisites:
- "SRS server binary must be built first (make -j in trunk/)"
- "FFmpeg and FFprobe must be available in PATH"
- "Test media files (avatar.flv, etc.) must be present in srs-bench directory"
build_blackbox_tests:
command: "cd trunk/3rdparty/srs-bench && make"
description: "Build the blackbox test binary"
output: "./objs/srs_blackbox_test"
run_all_tests:
command: "./objs/srs_blackbox_test -test.v -srs-log -srs-stdout"
description: "Run all blackbox tests with verbose output and detailed SRS logs"
working_directory: "trunk/3rdparty/srs-bench"
2025-07-01 09:00:56 -04:00
testing:
2025-10-23 09:44:24 -04:00
utest_file_naming:
description: "Unit test file naming indicates ownership and maintenance responsibility"
patterns:
- "srs_utest_ai*.cpp - AI-managed, AI has full autonomy"
- "srs_utest_workflow_*.cpp - Human-dominated with AI help, human maintains"
- "srs_utest_manual_*.cpp - Human-written, AI should NOT modify without permission"
2025-07-01 09:00:56 -04:00
error_handling_macros:
- Use HELPER_EXPECT_SUCCESS(x) when expecting a function to succeed (returns srs_success)
- Use HELPER_EXPECT_FAILED(x) when expecting a function to fail (returns non-srs_success error)
- These macros automatically handle error cleanup and provide better error messages
- Always declare "srs_error_t err;" at the beginning of test functions that use these macros
- |
Example:
VOID TEST(MyTest, TestFunction) {
srs_error_t err;
HELPER_EXPECT_SUCCESS(some_function_that_should_succeed());
HELPER_EXPECT_FAILED(some_function_that_should_fail());
}
2025-07-02 08:16:52 -04:00
2025-10-26 12:05:27 -04:00
debugging:
memory_and_pointer_issues:
description: "For memory corruption, crashes, pointer issues, or undefined behavior, proper debugging information is MANDATORY"
required_information: "User MUST provide either coredump stack trace OR sanitizer output - without it, memory issues cannot be diagnosed"
sanitizer:
description: "AddressSanitizer (ASAN) - Google's memory error detection tool for catching buffer overflows, use-after-free, memory leaks, etc."
when_enabled: "Automatically enabled for unit tests (--utest=on), manually enabled with ./configure --sanitizer=on, disabled by default for production builds (causes memory leak and increasing forever)"
how_to_enable: "./configure --sanitizer=on && make"
coredump:
description: "When sanitizer cannot be used, coredump with stack trace is required"
how_to_get: "ulimit -c unlimited, run SRS until crash, gdb ./objs/srs core.xxx -ex 'bt' -ex 'quit'"
2025-07-02 08:16:52 -04:00
code_review:
github_pull_requests:
- When reviewing or understanding GitHub pull requests, use the diff URL to get the code changes
- Format: https://patch-diff.githubusercontent.com/raw/ossrs/srs/pull/{PR_NUMBER}.diff
- Example: For PR #4289, access https://patch-diff.githubusercontent.com/raw/ossrs/srs/pull/4289.diff
- This provides the raw diff showing all changes made in the pull request
- Use web-fetch tool to retrieve and analyze the diff content
2025-07-02 20:53:19 -04:00
documentation:
location: "3rdparty/srs-docs"
description: |
SRS documentation source files are located in the 3rdparty/srs-docs directory
usage: |
2025-10-20 08:03:07 -04:00
When looking for documentation or need to update docs, check this directory for markdown
2025-07-02 20:53:19 -04:00
files and documentation structure. Do not search ossrs.io or ossrs.net for documentation,
use the local files instead.