mirror of
https://github.com/ossrs/srs.git
synced 2025-11-24 03:44:02 +08:00
358 lines
16 KiB
Plaintext
358 lines
16 KiB
Plaintext
# SRS Repository Guidelines
|
|
|
|
project:
|
|
name: "SRS (Simple Realtime Server)"
|
|
type: "C++ streaming media server (RTMP/WebRTC/WHIP/WHEP/SRT/HLS/HTTP-FLV)"
|
|
|
|
architecture:
|
|
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"
|
|
|
|
codebase_structure:
|
|
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"
|
|
|
|
code_patterns:
|
|
dependency_inversion:
|
|
principle: "MANDATORY - Depend on interfaces (ISrs*), not concrete classes (Srs*)"
|
|
rules:
|
|
- "Member variables use ISrs* types (e.g., ISrsMessageQueue *queue_)"
|
|
- "Concrete types only for instantiation"
|
|
- "Enables mocking for unit tests"
|
|
|
|
avoid_global_variables:
|
|
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"
|
|
|
|
cpp_compatibility:
|
|
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"
|
|
|
|
memory_management:
|
|
smart_pointers: "SrsUniquePtr<T> (unique ownership), SrsSharedPtr<T> (shared ownership)"
|
|
macros: "srs_freep (pointers), srs_freepa (arrays) - use only when smart pointers unsuitable"
|
|
|
|
naming_conventions:
|
|
- pattern: "field_naming"
|
|
description: "MANDATORY - All class and struct fields (member variables) must end with underscore (_), but NOT functions/methods"
|
|
usage: |
|
|
WRONG: Fields without underscore
|
|
class SrsBuffer {
|
|
private:
|
|
char *p;
|
|
int size;
|
|
public:
|
|
bool enabled;
|
|
};
|
|
|
|
CORRECT: Fields with underscore, functions without underscore
|
|
class SrsBuffer {
|
|
private:
|
|
char *p_;
|
|
int size_;
|
|
public:
|
|
bool enabled_;
|
|
public:
|
|
srs_error_t initialize();
|
|
};
|
|
scope: "Applies ONLY to fields (member variables) in classes and structs - NOT to functions, methods, comments, error messages, or parameters"
|
|
exceptions: "Only applies to SRS-defined classes/structs - do NOT change 3rd party code like llhttp"
|
|
rationale: "Consistent naming convention across SRS codebase for better code readability and maintenance - underscore distinguishes member variables from local variables and parameters"
|
|
|
|
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"
|
|
|
|
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"
|
|
|
|
error_handling:
|
|
- pattern: "srs_error_t"
|
|
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);"
|
|
|
|
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"
|
|
|
|
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"
|
|
|
|
conditional_compilation:
|
|
- pattern: "#ifdef SRS_VALGRIND"
|
|
description: "Valgrind support"
|
|
|
|
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"
|
|
|
|
build_and_development:
|
|
build:
|
|
command: "make -j"
|
|
description: "Build the SRS server using parallel make in the trunk directory"
|
|
working_directory: "trunk"
|
|
|
|
run_utests:
|
|
command: "make utest -j && ./objs/srs_utest"
|
|
description: "Run the unit tests for SRS"
|
|
working_directory: "trunk"
|
|
|
|
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"
|
|
|
|
testing:
|
|
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"
|
|
|
|
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());
|
|
}
|
|
|
|
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'"
|
|
|
|
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
|
|
|
|
documentation:
|
|
location: "3rdparty/srs-docs"
|
|
description: |
|
|
SRS documentation source files are located in the 3rdparty/srs-docs directory
|
|
usage: |
|
|
When looking for documentation or need to update docs, check this directory for markdown
|
|
files and documentation structure. Do not search ossrs.io or ossrs.net for documentation,
|
|
use the local files instead.
|