# 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 (unique ownership), SrsSharedPtr (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()); } 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.