├── .github └── workflows │ ├── changelogConfig.json │ ├── main.yml │ ├── main.yml.bak │ ├── test.yml │ └── text.yml.bak ├── .gitignore ├── .gitmodules ├── CI ├── build-linux.sh ├── build-macos.sh ├── build-windows.ps1 ├── include │ ├── Brewfile │ ├── Xcnotary │ ├── build_environment.ps1 │ ├── build_environment.ps1.in │ ├── build_environment.sh │ ├── build_environment.sh.in │ ├── build_support.sh │ ├── build_support_linux.sh │ ├── build_support_macos.sh │ └── build_support_windows.ps1 ├── linux │ ├── 01_install_dependencies.sh │ ├── 02_build_obs_libs.sh │ ├── 03_build_plugin.sh │ └── 04_package_plugin.sh ├── macos │ ├── 01_install_dependencies.sh │ ├── 02_build_obs_libs.sh │ ├── 03_build_plugin.sh │ └── 04_package_plugin.sh ├── utility │ ├── check-format.sh │ └── formatcode.sh └── windows │ ├── 01_install_dependencies.ps1 │ ├── 02_build_obs_libs.ps1 │ ├── 03_build_plugin.ps1 │ └── 04_package_plugin.ps1 ├── CMakeLists.txt ├── LICENSE ├── README.md ├── README_de-DE.md ├── README_es-ES.md ├── README_fr-FR.md ├── README_it-IT.md ├── README_ja-JP.md ├── README_ko-KR.md ├── README_nl-NL.md ├── README_ru-RU.md ├── README_zh-CN.md ├── README_zh-TW.md ├── bundle ├── installer-macos.pkgproj.in └── macOS │ ├── Plugin-Info.plist.in │ └── entitlements.plist ├── data └── locale │ ├── de-DE.ini │ ├── en-US.ini │ ├── es-ES.ini │ ├── fr-FR.ini │ ├── it-IT.ini │ ├── ja-JP.ini │ ├── ko-KR.ini │ ├── nl-NL.ini │ ├── ru-RU.ini │ ├── zh-CN.ini │ └── zh-TW.ini ├── external ├── BuildHelper.cmake ├── Findlibobs.cmake ├── Findobs-frontend-api.cmake ├── GitInfoHelper.cmake ├── ObsPluginHelpers.cmake ├── ObsPluginHelpers.cmake.bak └── ObsPluginHelpers.cmake.bak.bak ├── helper.h ├── installer ├── function │ └── unprevious.nsi ├── installer.nsi ├── locale.nsi ├── locale │ ├── de-DE.nsi │ ├── en-US.nsi │ ├── es-ES.nsi │ ├── fr-FR.nsi │ ├── it-IT.nsi │ ├── ja-JP.nsi │ ├── ko-KR.nsi │ ├── nl-NL.nsi │ ├── zh-CN.nsi │ └── zh-TW.nsi └── obs.ico ├── rtsp-server ├── 3rdpart │ ├── libb64 │ │ └── CMakeLists.txt │ └── md5 │ │ ├── COPYING │ │ └── md5.hpp ├── CMakeLists.txt ├── net │ ├── Acceptor.cpp │ ├── Acceptor.h │ ├── BufferReader.cpp │ ├── BufferReader.h │ ├── BufferWriter.cpp │ ├── BufferWriter.h │ ├── Channel.h │ ├── EpollTaskScheduler.cpp │ ├── EpollTaskScheduler.h │ ├── EventLoop.cpp │ ├── EventLoop.h │ ├── KqueueTaskScheduler.cpp │ ├── KqueueTaskScheduler.h │ ├── Logger.cpp │ ├── Logger.h │ ├── MemoryManager.cpp │ ├── MemoryManager.h │ ├── Pipe.cpp │ ├── Pipe.h │ ├── RingBuffer.h │ ├── SelectTaskScheduler.cpp │ ├── SelectTaskScheduler.h │ ├── Socket.h │ ├── SocketUtil.cpp │ ├── SocketUtil.h │ ├── TaskScheduler.cpp │ ├── TaskScheduler.h │ ├── TcpConnection.cpp │ ├── TcpConnection.h │ ├── TcpServer.cpp │ ├── TcpServer.h │ ├── TcpSocket.cpp │ ├── TcpSocket.h │ ├── ThreadSafeQueue.h.bak │ ├── Timer.cpp │ ├── Timer.h │ ├── Timestamp.cpp │ └── Timestamp.h └── xop │ ├── AACSource.cpp │ ├── AACSource.h │ ├── Base64Encode.cpp │ ├── Base64Encode.h │ ├── BaseMd5.cpp │ ├── BaseMd5.h │ ├── CngMd5.cpp │ ├── CngMd5.h │ ├── DigestAuthentication.cpp │ ├── DigestAuthentication.h │ ├── G711ASource.cpp │ ├── G711ASource.h │ ├── H264NalUnit.cpp │ ├── H264NalUnit.h │ ├── H264Source.cpp │ ├── H264Source.h │ ├── H265NalUnit.cpp │ ├── H265NalUnit.h │ ├── H265Source.cpp │ ├── H265Source.h │ ├── MacMd5.cpp │ ├── MacMd5.h │ ├── Md5.cpp │ ├── Md5.h │ ├── MediaSession.cpp │ ├── MediaSession.h │ ├── MediaSource.h │ ├── Nal.cpp │ ├── Nal.h │ ├── NalUnit.cpp │ ├── NalUnit.h │ ├── RtpConnection.cpp │ ├── RtpConnection.h │ ├── RtspConnection.cpp │ ├── RtspConnection.h │ ├── RtspMessage.cpp │ ├── RtspMessage.h │ ├── RtspPusher.cpp │ ├── RtspPusher.h │ ├── RtspServer.cpp │ ├── RtspServer.h │ ├── VP8Source.cpp │ ├── VP8Source.h │ ├── media.h │ ├── rtp.h │ └── rtsp.h ├── rtsp_main.cpp ├── rtsp_output.cpp ├── rtsp_output.h ├── rtsp_output_helper.cpp ├── rtsp_output_helper.h ├── rtspoutput.rc.in ├── threadsafe_queue.h └── ui ├── rtsp_properties.cpp ├── rtsp_properties.hpp └── rtsp_properties.ui /.github/workflows/changelogConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "typeLabels": [ 3 | { "types": ["breaking"], "title": "⚡️ Breaking Changes" }, 4 | { "types": ["feat", "feature"], "title": "🚀 New Features" }, 5 | { "types": ["fix", "bugfix"], "title": "💊 Bugfixes" }, 6 | { "types": ["improvements", "enhancement"], "title": "🔨 Improvements" }, 7 | { "types": ["perf"], "title": "🏎️ Performance Improvements" }, 8 | { "types": ["build", "ci"], "title": "🏗️ Build System" }, 9 | { "types": ["refactor"], "title": "🪚 Refactors" }, 10 | { "types": ["doc", "docs"], "title": "📚 Documentation Changes" }, 11 | { "types": ["test", "tests"], "title": "🔍 Tests" }, 12 | { "types": ["style"], "title": "💅 Code Style Changes" }, 13 | { "types": ["chore"], "title": "🧹 Chores" }, 14 | { "types": ["other"], "title": "Other Changes" } 15 | ], 16 | "bumpLabels": [ 17 | { 18 | "title": "major", 19 | "types": ["breaking"] 20 | }, 21 | { 22 | "title": "minor", 23 | "types": ["feat", "feature"] 24 | }, 25 | { 26 | "title": "patch", 27 | "types": [] 28 | } 29 | ], 30 | "issuesUrl": "", 31 | "sortOrder": "desc", 32 | "emptyMessage": "No changes" 33 | } -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: 'test' 2 | 3 | on: 4 | #release: 5 | # types: [published] 6 | push: 7 | tags: 8 | - test[0-9]+ 9 | jobs: 10 | create-release: 11 | name: '05 - Create release' 12 | runs-on: [ubuntu-latest] 13 | 14 | steps: 15 | - name: 'Checkout plugin' 16 | uses: actions/checkout@v2.3.3 17 | 18 | - name: 'Create release ${{ needs.get_plugin_info.outputs.git_tag_name }}' 19 | uses: ncipollo/release-action@v1 20 | id: create_release 21 | with: 22 | #bodyFile: "body.md" 23 | #omitBody: true 24 | #omitBodyDuringUpdate: true 25 | draft: true 26 | prerelease: true 27 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/text.yml.bak: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: [published] 4 | 5 | jobs: 6 | one: 7 | runs-on: ubuntu-16.04 8 | steps: 9 | - name: Dump GitHub context 10 | env: 11 | GITHUB_CONTEXT: ${{ toJson(github) }} 12 | run: echo "$GITHUB_CONTEXT" 13 | - name: Dump job context 14 | env: 15 | JOB_CONTEXT: ${{ toJson(job) }} 16 | run: echo "$JOB_CONTEXT" 17 | - name: Dump steps context 18 | env: 19 | STEPS_CONTEXT: ${{ toJson(steps) }} 20 | run: echo "$STEPS_CONTEXT" 21 | - name: Dump runner context 22 | env: 23 | RUNNER_CONTEXT: ${{ toJson(runner) }} 24 | run: echo "$RUNNER_CONTEXT" 25 | - name: Dump strategy context 26 | env: 27 | STRATEGY_CONTEXT: ${{ toJson(strategy) }} 28 | run: echo "$STRATEGY_CONTEXT" 29 | - name: Dump matrix context 30 | env: 31 | MATRIX_CONTEXT: ${{ toJson(matrix) }} 32 | run: echo "$MATRIX_CONTEXT" 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.aps 2 | rtspoutput.rc 3 | resource.h 4 | bundle/installer-macos.generated.pkgproj 5 | bundle/installer-macos.pkgproj 6 | bundle/LICENSE.txt 7 | bundle/macOS/Plugin-Info.plist 8 | installer/LICENSE 9 | build*/ 10 | release/ 11 | .vs/ 12 | .vscode/ 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rtsp-server/3rdpart/libb64/libb64"] 2 | path = rtsp-server/3rdpart/libb64/libb64 3 | url = https://github.com/libb64/libb64 4 | -------------------------------------------------------------------------------- /CI/build-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | # Linux plugin build script 5 | ############################################################################## 6 | # 7 | # This script contains all steps necessary to: 8 | # 9 | # * Build libobs and obs-frontend-api with all required dependencies 10 | # * Build your plugin 11 | # * Create debian package 12 | # 13 | # Parameters: 14 | # -h, --help : Print usage help 15 | # -q, --quiet : Suppress most build process output 16 | # -v, --verbose : Enable more verbose build process output 17 | # -p, --package : Create installer for plugin 18 | # -b, --build-dir : Specify alternative build directory 19 | # (default: build) 20 | # 21 | # Environment Variables (optional): 22 | # OBS_VERSION : OBS Version 23 | # 24 | ############################################################################## 25 | 26 | # Halt on errors 27 | set -eE 28 | 29 | ## SET UP ENVIRONMENT ## 30 | _RUN_OBS_BUILD_SCRIPT=TRUE 31 | 32 | CHECKOUT_DIR="$(git rev-parse --show-toplevel)" 33 | if [ -f "${CHECKOUT_DIR}/CI/include/build_environment.sh" ]; then 34 | source "${CHECKOUT_DIR}/CI/include/build_environment.sh" 35 | fi 36 | PRODUCT_NAME="${PRODUCT_NAME:-obs-plugin}" 37 | DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies" 38 | OBS_BUILD_DIR="${CHECKOUT_DIR}/../obs-studio" 39 | source "${CHECKOUT_DIR}/CI/include/build_support.sh" 40 | source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh" 41 | 42 | ## DEPENDENCY INSTALLATION ## 43 | source "${CHECKOUT_DIR}/CI/linux/01_install_dependencies.sh" 44 | 45 | ## OBS LIBRARY BUILD ## 46 | source "${CHECKOUT_DIR}/CI/linux/02_build_obs_libs.sh" 47 | 48 | ## PLUGIN BUILD ## 49 | source "${CHECKOUT_DIR}/CI/linux/03_build_plugin.sh" 50 | 51 | ## PLUGIN PACKAGE AND NOTARIZE ## 52 | source "${CHECKOUT_DIR}/CI/linux/04_package_plugin.sh" 53 | 54 | ## MAIN SCRIPT FUNCTIONS ## 55 | print_usage() { 56 | echo -e "build_linux.sh - Build script for ${PRODUCT_NAME}\n" 57 | echo -e "Usage: ${0}\n" \ 58 | "-h, --help : Print this help\n" \ 59 | "-q, --quiet : Suppress most build process output\n" \ 60 | "-v, --verbose : Enable more verbose build process output\n" \ 61 | "-d, --skip-dependency-checks : Skip dependency checks\n" \ 62 | "-p, --package : Create installer for plugin\n" \ 63 | "-b, --build-dir : Specify alternative build directory (default: build)\n" 64 | 65 | } 66 | 67 | obs-build-main() { 68 | while true; do 69 | case "${1}" in 70 | -h | --help ) print_usage; exit 0 ;; 71 | -d | --skip-dependency-checks ) SKIP_DEP_CHECKS=TRUE; shift ;; 72 | -q | --quiet ) export QUIET=TRUE; shift ;; 73 | -v | --verbose ) export VERBOSE=TRUE; shift ;; 74 | -p | --package ) PACKAGE=TRUE; shift ;; 75 | -b | --build-dir ) BUILD_DIR="${2}"; shift 2 ;; 76 | -- ) shift; break ;; 77 | * ) break ;; 78 | esac 79 | done 80 | 81 | ensure_dir "${CHECKOUT_DIR}" 82 | step "Fetching version tags..." 83 | git fetch origin --tags 84 | GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) 85 | GIT_HASH=$(git rev-parse --short HEAD) 86 | GIT_TAG=$(git describe --tags --abbrev=0 2&>/dev/null || true) 87 | FILE_NAME="${PRODUCT_NAME}-${GIT_TAG:-${PRODUCT_VERSION}}-${GIT_HASH}-linux" 88 | 89 | if [ -z "${SKIP_DEP_CHECKS}" ]; then 90 | install_dependencies 91 | fi 92 | 93 | build_obs_libs 94 | build_obs_plugin 95 | 96 | if [ -n "${PACKAGE}" ]; then 97 | package_obs_plugin 98 | fi 99 | 100 | cleanup 101 | } 102 | 103 | obs-build-main $* 104 | -------------------------------------------------------------------------------- /CI/include/Brewfile: -------------------------------------------------------------------------------- 1 | brew "cmake" 2 | brew "ninja" 3 | brew "coreutils" 4 | -------------------------------------------------------------------------------- /CI/include/Xcnotary: -------------------------------------------------------------------------------- 1 | brew "akeru-inc/tap/xcnotary" 2 | -------------------------------------------------------------------------------- /CI/include/build_environment.ps1: -------------------------------------------------------------------------------- 1 | $ProductName = "obs-rtspserver" 2 | #$ProductVersion = "@CMAKE_PROJECT_VERSION@" 3 | -------------------------------------------------------------------------------- /CI/include/build_environment.ps1.in: -------------------------------------------------------------------------------- 1 | $ProductName = "@CMAKE_PROJECT_NAME@" 2 | $ProductVersion = "@CMAKE_PROJECT_VERSION@" 3 | -------------------------------------------------------------------------------- /CI/include/build_environment.sh: -------------------------------------------------------------------------------- 1 | PRODUCT_NAME="obs-rtspserver" 2 | #PRODUCT_VERSION="@CMAKE_PROJECT_VERSION@" 3 | #LINUX_MAINTAINER_EMAIL="@LINUX_MAINTAINER_EMAIL@" 4 | -------------------------------------------------------------------------------- /CI/include/build_environment.sh.in: -------------------------------------------------------------------------------- 1 | PRODUCT_NAME="@CMAKE_PROJECT_NAME@" 2 | PRODUCT_VERSION="@CMAKE_PROJECT_VERSION@" 3 | LINUX_MAINTAINER_EMAIL="@LINUX_MAINTAINER_EMAIL@" 4 | -------------------------------------------------------------------------------- /CI/include/build_support_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | # Linux support functions 5 | ############################################################################## 6 | # 7 | # This script file can be included in build scripts for Linux. 8 | # 9 | ############################################################################## 10 | 11 | # Setup build environment 12 | 13 | # CI_OBS_VERSION=$(cat "${CI_WORKFLOW}" | sed -En "s/[ ]+OBS_VERSION: '([0-9\.]+)'/\1/p") 14 | 15 | if [ "${TERM-}" -a -z "${CI}" ]; then 16 | COLOR_RED=$(tput setaf 1) 17 | COLOR_GREEN=$(tput setaf 2) 18 | COLOR_BLUE=$(tput setaf 4) 19 | COLOR_ORANGE=$(tput setaf 3) 20 | COLOR_RESET=$(tput sgr0) 21 | else 22 | COLOR_RED=$(echo -e '\033[31m') 23 | COLOR_GREEN=$(echo -e '\033[32m') 24 | COLOR_BLUE=$(echo -e '\033[34m') 25 | COLOR_ORANGE=$(echo -e '\033[33m') 26 | COLOR_RESET=$(echo -e '\033[0m') 27 | fi 28 | 29 | if [ "${CI}" -o "${QUIET}" ]; then 30 | export CURLCMD="curl --silent --show-error --location -O" 31 | else 32 | export CURLCMD="curl --progress-bar --location --continue-at - -O" 33 | fi 34 | -------------------------------------------------------------------------------- /CI/linux/02_build_obs_libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | # Linux libobs library build function 5 | ############################################################################## 6 | # 7 | # This script file can be included in build scripts for Linux or run directly 8 | # 9 | ############################################################################## 10 | 11 | # Halt on errors 12 | set -eE 13 | 14 | build_obs_libs() { 15 | status "Build libobs and obs-frontend-api" 16 | trap "caught_error 'build_obs_libs'" ERR 17 | check_ccache 18 | 19 | ensure_dir "${OBS_BUILD_DIR}" 20 | 21 | step "Configuring OBS build system" 22 | check_ccache 23 | cmake -S . -B plugin_${BUILD_DIR} -G Ninja ${CMAKE_CCACHE_OPTIONS} \ 24 | -DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \ 25 | -DENABLE_PLUGINS=OFF \ 26 | -DENABLE_UI=ON \ 27 | -DENABLE_SCRIPTING=OFF \ 28 | -DENABLE_PIPEWIRE=OFF \ 29 | -DBUILD_BROWSER=OFF \ 30 | ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR} 31 | 32 | step "Building libobs and obs-frontend-api" 33 | cmake --build plugin_${BUILD_DIR} -t obs-frontend-api 34 | } 35 | 36 | build-obs-libs-standalone() { 37 | CHECKOUT_DIR="$(git rev-parse --show-toplevel)" 38 | if [ -f "${CHECKOUT_DIR}/CI/include/build_environment.sh" ]; then 39 | source "${CHECKOUT_DIR}/CI/include/build_environment.sh" 40 | fi 41 | PRODUCT_NAME="${PRODUCT_NAME:-obs-plugin}" 42 | OBS_BUILD_DIR="${CHECKOUT_DIR}/../obs-studio" 43 | source "${CHECKOUT_DIR}/CI/include/build_support.sh" 44 | source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh" 45 | 46 | build_obs_libs 47 | } 48 | 49 | print_usage() { 50 | echo -e "Usage: ${0}\n" \ 51 | "-h, --help : Print this help\n" \ 52 | "-q, --quiet : Suppress most build process output\n" \ 53 | "-v, --verbose : Enable more verbose build process output\n" \ 54 | "--build-dir : Specify alternative build directory (default: build)\n" 55 | } 56 | 57 | build-obs-libs-main() { 58 | if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then 59 | while true; do 60 | case "${1}" in 61 | -h | --help ) print_usage; exit 0 ;; 62 | -q | --quiet ) export QUIET=TRUE; shift ;; 63 | -v | --verbose ) export VERBOSE=TRUE; shift ;; 64 | --build-dir ) BUILD_DIR="${2}"; shift 2 ;; 65 | -- ) shift; break ;; 66 | * ) break ;; 67 | esac 68 | done 69 | 70 | build-obs-libs-standalone 71 | fi 72 | } 73 | 74 | build-obs-libs-main $* 75 | -------------------------------------------------------------------------------- /CI/linux/03_build_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | # Linux libobs plugin build function 5 | ############################################################################## 6 | # 7 | # This script file can be included in build scripts for Linux or run directly 8 | # 9 | ############################################################################## 10 | 11 | # Halt on errors 12 | set -eE 13 | 14 | build_obs_plugin() { 15 | status "Build plugin ${PRODUCT_NAME}" 16 | trap "caught_error 'builds_obs_plugin'" ERR 17 | 18 | ensure_dir "${CHECKOUT_DIR}" 19 | 20 | step "Configuring OBS plugin build system" 21 | check_ccache 22 | 23 | cmake -S . -B ${BUILD_DIR} -G Ninja ${CMAKE_CCACHE_OPTIONS} \ 24 | -DOBS_SOURCE_DIR="${OBS_BUILD_DIR}" \ 25 | ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR} 26 | 27 | step "Building OBS plugin" 28 | cmake --build ${BUILD_DIR} 29 | } 30 | 31 | build-plugin-standalone() { 32 | CHECKOUT_DIR="$(git rev-parse --show-toplevel)" 33 | if [ -f "${CHECKOUT_DIR}/CI/include/build_environment.sh" ]; then 34 | source "${CHECKOUT_DIR}/CI/include/build_environment.sh" 35 | fi 36 | PRODUCT_NAME="${PRODUCT_NAME:-obs-plugin}" 37 | OBS_BUILD_DIR="${CHECKOUT_DIR}/../obs-studio" 38 | source "${CHECKOUT_DIR}/CI/include/build_support.sh" 39 | source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh" 40 | 41 | build_obs_plugin 42 | } 43 | 44 | print_usage() { 45 | echo -e "Usage: ${0}\n" \ 46 | "-h, --help : Print this help\n" \ 47 | "-q, --quiet : Suppress most build process output\n" \ 48 | "-v, --verbose : Enable more verbose build process output\n" \ 49 | "--build-dir : Specify alternative build directory (default: build)\n" 50 | } 51 | 52 | build-plugin-main() { 53 | if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then 54 | while true; do 55 | case "${1}" in 56 | -h | --help ) print_usage; exit 0 ;; 57 | -q | --quiet ) export QUIET=TRUE; shift ;; 58 | -v | --verbose ) export VERBOSE=TRUE; shift ;; 59 | --build-dir ) BUILD_DIR="${2}"; shift 2 ;; 60 | -- ) shift; break ;; 61 | * ) break ;; 62 | esac 63 | done 64 | 65 | build-plugin-standalone 66 | fi 67 | } 68 | 69 | build-plugin-main $* 70 | -------------------------------------------------------------------------------- /CI/linux/04_package_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | # Linux libobs plugin build function 5 | ############################################################################## 6 | # 7 | # This script file can be included in build scripts for Linux or run directly 8 | # 9 | ############################################################################## 10 | 11 | # Halt on errors 12 | set -eE 13 | 14 | package_obs_plugin() { 15 | status "Package OBS plugin ${PRODUCT_NAME}" 16 | trap "caught_error 'package_obs_plugin'" ERR 17 | 18 | ensure_dir "${CHECKOUT_DIR}" 19 | 20 | step "Package ${PRODUCT_NAME}..." 21 | 22 | cmake --build "${BUILD_DIR}" -t package 23 | } 24 | 25 | package-plugin-standalone() { 26 | CHECKOUT_DIR="$(git rev-parse --show-toplevel)" 27 | if [ -f "${CHECKOUT_DIR}/CI/include/build_environment.sh" ]; then 28 | source "${CHECKOUT_DIR}/CI/include/build_environment.sh" 29 | fi 30 | PRODUCT_NAME="${PRODUCT_NAME:-obs-plugin}" 31 | source "${CHECKOUT_DIR}/CI/include/build_support.sh" 32 | source "${CHECKOUT_DIR}/CI/include/build_support_linux.sh" 33 | 34 | #GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD) 35 | #GIT_HASH=$(git rev-parse --short HEAD) 36 | #GIT_TAG=$(git describe --tags --always --dirty='-dev') 37 | #GIT_VERSION=$(echo ${GIT_TAG} | grep -Eos '[0-9]+.[0-9]+.[0-9]+(-[a-z0-9]+)*$') 38 | #FILE_NAME="${PRODUCT_NAME}-${GIT_TAG}-linux" 39 | 40 | package_obs_plugin 41 | } 42 | 43 | print_usage() { 44 | echo -e "Usage: ${0}\n" \ 45 | "-h, --help : Print this help\n" \ 46 | "-q, --quiet : Suppress most build process output\n" \ 47 | "-v, --verbose : Enable more verbose build process output\n" \ 48 | "--build-dir : Specify alternative build directory (default: build)\n" 49 | } 50 | 51 | package-plugin-main() { 52 | if [ ! -n "${_RUN_OBS_BUILD_SCRIPT}" ]; then 53 | while true; do 54 | case "${1}" in 55 | -h | --help ) print_usage; exit 0 ;; 56 | -q | --quiet ) export QUIET=TRUE; shift ;; 57 | -v | --verbose ) export VERBOSE=TRUE; shift ;; 58 | --build-dir ) BUILD_DIR="${2}"; shift 2 ;; 59 | -- ) shift; break ;; 60 | * ) break ;; 61 | esac 62 | done 63 | 64 | package-plugin-standalone 65 | fi 66 | } 67 | 68 | package-plugin-main $* 69 | -------------------------------------------------------------------------------- /CI/macos/02_build_obs_libs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | # macOS libobs library build function 5 | ############################################################################## 6 | # 7 | # This script file can be included in build scripts for macOS or run directly 8 | # 9 | ############################################################################## 10 | 11 | # Halt on errors 12 | set -eE 13 | 14 | build_obs_libs() { 15 | status "Build libobs and obs-frontend-api" 16 | trap "caught_error 'build_obs_libs'" ERR 17 | 18 | ensure_dir "${OBS_BUILD_DIR}" 19 | 20 | step "Configuring OBS build system" 21 | check_ccache 22 | #-DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-${CI_MACOSX_DEPLOYMENT_TARGET}} \ 23 | cmake -S . -B plugin_${BUILD_DIR} -G Ninja ${CMAKE_CCACHE_OPTIONS} \ 24 | -DCMAKE_OSX_ARCHITECTURES="${CMAKE_ARCHS}" \ 25 | -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ 26 | -DOBS_CODESIGN_LINKER=${CODESIGN_LINKER:-OFF} \ 27 | -DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \ 28 | -DENABLE_PLUGINS=OFF \ 29 | -DENABLE_UI=ON \ 30 | -DENABLE_SCRIPTING=OFF \ 31 | -DENABLE_SPARKLE_UPDATER=OFF \ 32 | -DSPARKLE=OFF \ 33 | -DBUILD_BROWSER=OFF \ 34 | -DCMAKE_PREFIX_PATH="${DEPS_BUILD_DIR}/obs-deps" \ 35 | ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR} 36 | 37 | step "Building libobs and obs-frontend-api" 38 | cmake --build plugin_${BUILD_DIR} -t obs-frontend-api 39 | } 40 | 41 | build-obs-libs-standalone() { 42 | CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)" 43 | if [ -f "${CHECKOUT_DIR}/CI/include/build_environment.sh" ]; then 44 | source "${CHECKOUT_DIR}/CI/include/build_environment.sh" 45 | fi 46 | PRODUCT_NAME="${PRODUCT_NAME:-obs-plugin}" 47 | DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies" 48 | OBS_BUILD_DIR="${CHECKOUT_DIR}/../obs-studio" 49 | source "${CHECKOUT_DIR}/CI/include/build_support.sh" 50 | source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh" 51 | 52 | check_macos_version 53 | check_archs 54 | build_obs_libs 55 | } 56 | 57 | print_usage() { 58 | echo -e "Usage: ${0}\n" \ 59 | "-h, --help : Print this help\n" \ 60 | "-q, --quiet : Suppress most build process output\n" \ 61 | "-v, --verbose : Enable more verbose build process output\n" \ 62 | "-a, --architecture : Specify build architecture (default: universal, alternative: x86_64, arm64)\n" \ 63 | "--build-dir : Specify alternative build directory (default: build)\n" 64 | } 65 | 66 | build-obs-libs-main() { 67 | if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then 68 | while true; do 69 | case "${1}" in 70 | -h | --help ) print_usage; exit 0 ;; 71 | -q | --quiet ) export QUIET=TRUE; shift ;; 72 | -v | --verbose ) export VERBOSE=TRUE; shift ;; 73 | -a | --architecture ) ARCH="${2}"; shift 2 ;; 74 | --build-dir ) BUILD_DIR="${2}"; shift 2 ;; 75 | -- ) shift; break ;; 76 | * ) break ;; 77 | esac 78 | done 79 | 80 | build-obs-libs-standalone 81 | fi 82 | } 83 | 84 | build-obs-libs-main $* 85 | -------------------------------------------------------------------------------- /CI/macos/03_build_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ############################################################################## 4 | # macOS libobs plugin build function 5 | ############################################################################## 6 | # 7 | # This script file can be included in build scripts for macOS or run directly 8 | # 9 | ############################################################################## 10 | 11 | # Halt on errors 12 | set -eE 13 | 14 | build_obs_plugin() { 15 | status "Build plugin ${PRODUCT_NAME}" 16 | trap "caught_error 'builds_obs_plugin'" ERR 17 | 18 | if [ "${CODESIGN}" ]; then 19 | read_codesign_ident 20 | fi 21 | 22 | ensure_dir "${CHECKOUT_DIR}" 23 | step "Configuring OBS plugin build system" 24 | check_ccache 25 | 26 | #-DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-${CI_MACOSX_DEPLOYMENT_TARGET}} \ 27 | cmake -S . -B ${BUILD_DIR} -G Ninja ${CMAKE_CCACHE_OPTIONS} \ 28 | -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ 29 | -DCMAKE_OSX_ARCHITECTURES="${CMAKE_ARCHS}" \ 30 | -DOBS_CODESIGN_LINKER=${CODESIGN_LINKER:-OFF} \ 31 | -DCMAKE_BUILD_TYPE=${BUILD_CONFIG} \ 32 | -DOBS_BUNDLE_CODESIGN_IDENTITY="${CODESIGN_IDENT:--}" \ 33 | -DCMAKE_PREFIX_PATH="${DEPS_BUILD_DIR}/obs-deps" \ 34 | -DOBS_SOURCE_DIR="${OBS_BUILD_DIR}" \ 35 | ${QUIET:+-Wno-deprecated -Wno-dev --log-level=ERROR} 36 | 37 | step "Building OBS plugin" 38 | cmake --build ${BUILD_DIR} 39 | 40 | } 41 | 42 | build-plugin-standalone() { 43 | CHECKOUT_DIR="$(/usr/bin/git rev-parse --show-toplevel)" 44 | if [ -f "${CHECKOUT_DIR}/CI/include/build_environment.sh" ]; then 45 | source "${CHECKOUT_DIR}/CI/include/build_environment.sh" 46 | fi 47 | PRODUCT_NAME="${PRODUCT_NAME:-obs-plugin}" 48 | DEPS_BUILD_DIR="${CHECKOUT_DIR}/../obs-build-dependencies" 49 | OBS_BUILD_DIR="${CHECKOUT_DIR}/../obs-studio" 50 | source "${CHECKOUT_DIR}/CI/include/build_support.sh" 51 | source "${CHECKOUT_DIR}/CI/include/build_support_macos.sh" 52 | 53 | check_macos_version 54 | check_archs 55 | 56 | build_obs_plugin 57 | } 58 | 59 | print_usage() { 60 | echo -e "Usage: ${0}\n" \ 61 | "-h, --help : Print this help\n" \ 62 | "-q, --quiet : Suppress most build process output\n" \ 63 | "-v, --verbose : Enable more verbose build process output\n" \ 64 | "-a, --architecture : Specify build architecture (default: x86_64, alternative: arm64)\n" \ 65 | "-c, --codesign : Codesign OBS and all libraries (default: ad-hoc only)\n" \ 66 | "--build-dir : Specify alternative build directory (default: build)\n" 67 | } 68 | 69 | build-plugin-main() { 70 | if [ -z "${_RUN_OBS_BUILD_SCRIPT}" ]; then 71 | while true; do 72 | case "${1}" in 73 | -h | --help ) print_usage; exit 0 ;; 74 | -q | --quiet ) export QUIET=TRUE; shift ;; 75 | -v | --verbose ) export VERBOSE=TRUE; shift ;; 76 | -c | --codesign ) CODESIGN=TRUE; shift ;; 77 | -a | --architecture ) ARCH="${2}"; shift 2 ;; 78 | --build-dir ) BUILD_DIR="${2}"; shift 2 ;; 79 | -- ) shift; break ;; 80 | * ) break ;; 81 | esac 82 | done 83 | 84 | build-plugin-standalone 85 | fi 86 | } 87 | 88 | build-plugin-main $* 89 | -------------------------------------------------------------------------------- /CI/utility/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dirty=$(git ls-files --modified) 3 | 4 | set +x 5 | if [[ $dirty ]]; then 6 | echo "=================================" 7 | echo "Files were not formatted properly" 8 | echo "$dirty" 9 | echo "=================================" 10 | exit 1 11 | fi 12 | -------------------------------------------------------------------------------- /CI/utility/formatcode.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Original source https://github.com/Project-OSRM/osrm-backend/blob/master/scripts/format.sh 3 | 4 | set -o errexit 5 | set -o pipefail 6 | set -o nounset 7 | 8 | if [ ${#} -eq 1 ]; then 9 | VERBOSITY="--verbose" 10 | else 11 | VERBOSITY="" 12 | fi 13 | 14 | # Runs the Clang Formatter in parallel on the code base. 15 | # Return codes: 16 | # - 1 there are files to be formatted 17 | # - 0 everything looks fine 18 | 19 | # Get CPU count 20 | OS=$(uname) 21 | NPROC=1 22 | if [[ ${OS} = "Linux" ]] ; then 23 | NPROC=$(nproc) 24 | elif [[ ${OS} = "Darwin" ]] ; then 25 | NPROC=$(sysctl -n hw.physicalcpu) 26 | fi 27 | 28 | # Discover clang-format 29 | if type clang-format-12 2> /dev/null ; then 30 | CLANG_FORMAT=clang-format-12 31 | elif type clang-format 2> /dev/null ; then 32 | # Clang format found, but need to check version 33 | CLANG_FORMAT=clang-format 34 | V=$(clang-format --version) 35 | if [[ $V != *"version 12.0"* ]]; then 36 | echo "clang-format is not 12.0 (returned ${V})" 37 | exit 1 38 | fi 39 | else 40 | echo "No appropriate clang-format found (expected clang-format-12.0.0, or clang-format)" 41 | exit 1 42 | fi 43 | 44 | find . -type d \( \ 45 | -path ./\*build -o \ 46 | -path ./cmake -o \ 47 | -path ./deps -o \ 48 | -path ./plugins/decklink/\*/decklink-sdk -o \ 49 | -path ./plugins/enc-amf -o \ 50 | -path ./plugins/mac-syphon/syphon-framework -o \ 51 | -path ./plugins/obs-outputs/ftl-sdk -o \ 52 | -path ./plugins/obs-vst \ 53 | \) -prune -false -type f -o \ 54 | -name '*.h' -or \ 55 | -name '*.hpp' -or \ 56 | -name '*.m' -or \ 57 | -name '*.m,' -or \ 58 | -name '*.c' -or \ 59 | -name '*.cpp' \ 60 | | xargs -L100 -P ${NPROC} ${CLANG_FORMAT} ${VERBOSITY} -i -style=file -fallback-style=none 61 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | include(external/GitInfoHelper.cmake) 4 | get_git_version(OBS_PLUGUN_GIT_TAG OBS_PLUGUIN_VERSION OBS_PLUGUIN_SHORT_VERSION OBS_PLUGUIN_LONG_VERSION) 5 | project(obs-rtspserver VERSION ${OBS_PLUGUIN_LONG_VERSION} 6 | HOMEPAGE_URL "https://obsproject.com/forum/resources/obs-rtspserver.1037" 7 | DESCRIPTION "RTSP server plugin for obs-studio") 8 | set(LINUX_MAINTAINER_EMAIL "scottxu@scottxublog.com") 9 | set(LINUX_MAINTAINER "Scott Xu") 10 | set(MACOS_BUNDLEID "com.scottxu.obs-rtspserver") 11 | 12 | set(OBS_PLUGIN_OBS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) 13 | set(OBS_FRONTEND_API_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/UI/obs-frontend-api") 14 | 15 | add_library(${CMAKE_PROJECT_NAME} MODULE) 16 | 17 | if (NOT COMMAND setup_plugin_target) 18 | include(external/BuildHelper.cmake) 19 | endif() 20 | 21 | add_subdirectory(rtsp-server) 22 | 23 | if(CMAKE_VERSION VERSION_LESS "3.7.0") 24 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 25 | endif() 26 | #set(CMAKE_AUTOMOC ON) 27 | #set(CMAKE_AUTOUIC ON) 28 | 29 | set(OBS_RTSPSERVER_SOURCES 30 | rtsp_main.cpp 31 | rtsp_output.cpp 32 | rtsp_properties.cpp 33 | rtsp_output_helper.cpp 34 | ) 35 | 36 | set(OBS_RTSPSERVER_HEADERS 37 | rtsp_output.h 38 | rtsp_properties.h 39 | rtsp_output_helper.h 40 | ) 41 | 42 | file(GLOB OBS_RTSPSERVER_UI_SOURCES 43 | ui/*.cpp) 44 | 45 | file(GLOB OBS_RTSPSERVER_MAIN_SOURCES 46 | *.cpp) 47 | 48 | set(OBS_RTSPSERVER_SOURCES 49 | ${OBS_RTSPSERVER_UI_SOURCES} 50 | ${OBS_RTSPSERVER_MAIN_SOURCES} 51 | ) 52 | 53 | file(GLOB OBS_RTSPSERVER_UI_HEADERS 54 | ui/*.hpp) 55 | 56 | file(GLOB OBS_RTSPSERVER_MAIN_HEADERS 57 | *.h) 58 | 59 | set(OBS_RTSPSERVER_HEADERS 60 | ${OBS_RTSPSERVER_UI_HEADERS} 61 | ${OBS_RTSPSERVER_MAIN_HEADERS} 62 | ) 63 | 64 | set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY CXX_STANDARD 17) 65 | set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY C_STANDARD 17) 66 | 67 | find_qt(COMPONENTS Widgets Core) 68 | 69 | set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES AUTOMOC ON AUTOUIC ON AUTORCC ON) 70 | 71 | target_sources(${CMAKE_PROJECT_NAME} PRIVATE 72 | ${OBS_RTSPSERVER_SOURCES} 73 | ${OBS_RTSPSERVER_HEADERS}) 74 | 75 | if(WIN32) 76 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/rtspoutput.rc.in ${CMAKE_CURRENT_SOURCE_DIR}/rtspoutput.rc) 77 | target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/rtspoutput.rc) 78 | endif() 79 | 80 | target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE 81 | ${OBS_FRONTEND_API_INCLUDE_DIRS} 82 | ${LIBOBS_INCLUDE_DIRS} 83 | "rtsp-server") 84 | 85 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE 86 | rtsp-server 87 | obs-frontend-api 88 | libobs 89 | Qt::Core 90 | Qt::Widgets) 91 | 92 | add_definitions(-DVERSION_STRING="${OBS_PLUGUIN_VERSION}") 93 | 94 | if(APPLE) 95 | set_target_properties(${CMAKE_PROJECT_NAME} 96 | PROPERTIES 97 | FOLDER "plugins" 98 | PRODUCTNAME "OBS RTSP Server Plugin") 99 | else() 100 | set_target_properties(${CMAKE_PROJECT_NAME} 101 | PROPERTIES 102 | FOLDER "plugins" 103 | VERSION "${OBS_PLUGUIN_VERSION}" 104 | PRODUCTNAME "OBS RTSP Server Plugin") 105 | endif() 106 | 107 | setup_plugin_target(${CMAKE_PROJECT_NAME}) 108 | 109 | -------------------------------------------------------------------------------- /bundle/macOS/Plugin-Info.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleName 6 | ${MACOSX_PLUGIN_BUNDLE_NAME} 7 | CFBundleIdentifier 8 | ${MACOSX_PLUGIN_GUI_IDENTIFIER} 9 | CFBundleVersion 10 | ${MACOSX_PLUGIN_BUNDLE_VERSION} 11 | CFBundleShortVersionString 12 | ${MACOSX_PLUGIN_SHORT_VERSION_STRING} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleExecutable 16 | ${MACOSX_PLUGIN_EXECUTABLE_NAME} 17 | CFBundlePackageType 18 | ${MACOSX_PLUGIN_BUNDLE_TYPE} 19 | CFBundleSupportedPlatforms 20 | 21 | MacOSX 22 | 23 | LSMinimumSystemVersion 24 | 10.13 25 | 26 | -------------------------------------------------------------------------------- /bundle/macOS/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-unsigned-executable-memory 6 | 7 | com.apple.security.device.camera 8 | 9 | com.apple.security.device.audio-input 10 | 11 | com.apple.security.cs.disable-library-validation 12 | 13 | 14 | com.apple.security.cs.allow-dyld-environment-variables 15 | 16 | 17 | -------------------------------------------------------------------------------- /data/locale/de-DE.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP Server" 2 | RstpServer.Description="OBS RTSP Server Plugin" 3 | RtspServer.Properties="RTSP Server" 4 | RtspServer.Properties.StartOutput="Starten" 5 | RtspServer.Properties.StopOutput="Stoppen" 6 | RtspServer.Properties.Options="Optionen" 7 | RtspServer.Properties.Options.AutoStart="AutoStart" 8 | RtspServer.Properties.Options.EnabledAudioTracks="Audiospuren: " 9 | RtspServer.Properties.Target="Ziel" 10 | RtspServer.Properties.Target.Multicast="Aktiviertes Multicast" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="Kopieren" 13 | RtspServer.Properties.Options.Output="Ausgabe-Optionen: " 14 | RtspServer.Properties.Options.Output.Tip="Wenn Sie die Ausgabe-Optionen ändern wollen öffnen Sie: Datei->Einstellungen->Ausgabe->Streaming." 15 | RtspServer.Properties.Authentication="Authentifizierung" 16 | RtspServer.Properties.Authentication.Enabled="Aktiviert" 17 | RtspServer.Properties.Authentication.Realm="Realm: " 18 | RtspServer.Properties.Authentication.Username="Benutzername: " 19 | RtspServer.Properties.Authentication.Password="Passwort: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(Optional)" 21 | RtspServer.Properties.Status="Status" 22 | RtspServer.Properties.Status.TotalDataSent="Gesamtdatenausgabe: " 23 | RtspServer.Properties.Status.Bitrate="Bitrate: " 24 | RtspServer.Properties.Status.DroppedFrames="Dropped Frames:" 25 | RtspServer.Properties.Version="Version: " 26 | 27 | RtspOutput="RTSP Ausgabe" 28 | RtspOutput.Error.BeginDataCapture="Datenerfassung konnte nicht gestartet werden" 29 | RtspOutput.Error.InitEncoders="Kodierungsinitialisierungsfehler" 30 | RtspOutput.Error.StartRtspServer="RTSP Server konnte nicht auf Port '%d' gestartet werden" 31 | RtspOutput.Error.StartMulticast="Multicast-Start fehlgeschlagen." 32 | RtspOutput.Error.Encode="Kodierungsfehler" 33 | RtspOutput.Error.Unknown="Unbekannter Fehler" 34 | RtspOutput.Hotkey.StartOutput="Starten" 35 | RtspOutput.Hotkey.StopOutput="Stoppen" 36 | RtspOutput.Properties.Multicast="Aktiviertes Multicast" 37 | RtspOutput.Properties.Port="Port" 38 | RtspOutput.Properties.UrlSuffix="URL-Suffix" 39 | RtspOutput.Properties.OutputAudio="Audioausgabe aktivieren" 40 | RtspOutput.Properties.Authentication="Authentifizierung" 41 | RtspOutput.Properties.Authentication.Realm="Realm" 42 | RtspOutput.Properties.Authentication.Username="Benutzername" 43 | RtspOutput.Properties.Authentication.Password="Passwort" 44 | 45 | Reset="Zurücksetzen" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/en-US.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP Server" 2 | RstpServer.Description="OBS RTSP Server Plugin" 3 | RtspServer.Properties="RTSP Server" 4 | RtspServer.Properties.StartOutput="Start" 5 | RtspServer.Properties.StopOutput="Stop" 6 | RtspServer.Properties.Options="Options" 7 | RtspServer.Properties.Options.AutoStart="Auto Start" 8 | RtspServer.Properties.Options.EnabledAudioTracks="Audio Tracks: " 9 | RtspServer.Properties.Target="Target" 10 | RtspServer.Properties.Target.Multicast="Enabled Multicast" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="Copy" 13 | RtspServer.Properties.Options.Output="Output Options: " 14 | RtspServer.Properties.Options.Output.Tip="Open File->Settings->Output->Streaming to change output options." 15 | RtspServer.Properties.Authentication="Authentication" 16 | RtspServer.Properties.Authentication.Enabled="Enabled" 17 | RtspServer.Properties.Authentication.Realm="Realm: " 18 | RtspServer.Properties.Authentication.Username="Username: " 19 | RtspServer.Properties.Authentication.Password="Password: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(Optional)" 21 | RtspServer.Properties.Status="Status" 22 | RtspServer.Properties.Status.TotalDataSent="Total Data Output: " 23 | RtspServer.Properties.Status.Bitrate="Bitrate: " 24 | RtspServer.Properties.Status.DroppedFrames="Dropped Frames: " 25 | RtspServer.Properties.Version="Version: " 26 | 27 | RtspOutput="RTSP Output" 28 | RtspOutput.Error.BeginDataCapture="can't begin data capture" 29 | RtspOutput.Error.InitEncoders="initialize encoders error" 30 | RtspOutput.Error.StartRtspServer="starting RTSP server failed on port '%d'" 31 | RtspOutput.Error.StartMulticast="starting multicast failed" 32 | RtspOutput.Error.Encode="encode error" 33 | RtspOutput.Error.Unknown="unknown error" 34 | RtspOutput.Hotkey.StartOutput="Start Output" 35 | RtspOutput.Hotkey.StopOutput="Stop Output" 36 | RtspOutput.Properties.Multicast="Enabled Multicast" 37 | RtspOutput.Properties.Port="Port" 38 | RtspOutput.Properties.UrlSuffix="URL Suffix" 39 | RtspOutput.Properties.OutputAudio="Enable Audio Output" 40 | RtspOutput.Properties.Authentication="Authentication" 41 | RtspOutput.Properties.Authentication.Realm="Realm" 42 | RtspOutput.Properties.Authentication.Username="Username" 43 | RtspOutput.Properties.Authentication.Password="Password" 44 | 45 | Reset="Reset" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/es-ES.ini: -------------------------------------------------------------------------------- 1 | RtspServer="Servidor RTSP" 2 | RstpServer.Description="Complemento servidor RTSP para OBS" 3 | RtspServer.Properties="Servidor RTSP" 4 | RtspServer.Properties.StartOutput="Iniciar" 5 | RtspServer.Properties.StopOutput="Parar" 6 | RtspServer.Properties.Options="Opciones" 7 | RtspServer.Properties.Options.AutoStart="Autoencendido" 8 | RtspServer.Properties.Options.EnabledAudioTracks="Pistas de audio: " 9 | RtspServer.Properties.Target="Objetivo" 10 | RtspServer.Properties.Target.Multicast="Habilitado Multicast" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="Copiar" 13 | RtspServer.Properties.Options.Output="Opciones de salida: " 14 | RtspServer.Properties.Options.Output.Tip="Si desea cambiar las opciones de salida, abra: Archivo->Configuración->Salida->Emisión." 15 | RtspServer.Properties.Authentication="Autenticación" 16 | RtspServer.Properties.Authentication.Enabled="Activada" 17 | RtspServer.Properties.Authentication.Realm="Realm: " 18 | RtspServer.Properties.Authentication.Username="Nombre de usuario: " 19 | RtspServer.Properties.Authentication.Password="Contraseña: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(Opcional)" 21 | RtspServer.Properties.Status="Estatus" 22 | RtspServer.Properties.Status.TotalDataSent="Salida de datos total: " 23 | RtspServer.Properties.Status.Bitrate="Tasa de bits: " 24 | RtspServer.Properties.Status.DroppedFrames="Frames caídos:" 25 | RtspServer.Properties.Version="Versión: " 26 | 27 | RtspOutput="Salida RTSP" 28 | RtspOutput.Error.BeginDataCapture="No se pudo iniciar la adquisición de datos" 29 | RtspOutput.Error.InitEncoders="Error de inicialización de codificación" 30 | RtspOutput.Error.StartRtspServer="El servidor RTSP no se pudo iniciar en el puerto '%d'" 31 | RtspOutput.Error.StartMulticast="error al iniciar la multidifusión" 32 | RtspOutput.Error.Encode="Error de codificación" 33 | RtspOutput.Error.Unknown="Error desconocido" 34 | RtspOutput.Hotkey.StartOutput="Iniciar" 35 | RtspOutput.Hotkey.StopOutput="Parar" 36 | RtspOutput.Properties.Multicast="Habilitado Multicast" 37 | RtspOutput.Properties.Port="Puerto de red" 38 | RtspOutput.Properties.UrlSuffix="Sufijo de URL" 39 | RtspOutput.Properties.OutputAudio="Habilitar salida de audio" 40 | RtspOutput.Properties.Authentication="Autenticación" 41 | RtspOutput.Properties.Authentication.Realm="Realm" 42 | RtspOutput.Properties.Authentication.Username="Nombre de usuario" 43 | RtspOutput.Properties.Authentication.Password="Contraseña" 44 | 45 | Reset="Reiniciar" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/fr-FR.ini: -------------------------------------------------------------------------------- 1 | RtspServer="Serveur RTSP" 2 | RstpServer.Description="Module d'extension de serveur RTSP OBS" 3 | RtspServer.Properties="Serveur RTSP" 4 | RtspServer.Properties.StartOutput="Démarrer" 5 | RtspServer.Properties.StopOutput="Arrêter" 6 | RtspServer.Properties.Options="Options" 7 | RtspServer.Properties.Options.AutoStart="Démarrage automatique" 8 | RtspServer.Properties.Options.EnabledAudioTracks="Pistes audio: " 9 | RtspServer.Properties.Target="Cible" 10 | RtspServer.Properties.Target.Multicast="Activé Multicast" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="Copier" 13 | RtspServer.Properties.Options.Output="Options de sortie: " 14 | RtspServer.Properties.Options.Output.Tip="Ouvrir le Fichier->Paramètres->Sortie->Streaming pour modifier les options de sortie." 15 | RtspServer.Properties.Authentication="Authentification" 16 | RtspServer.Properties.Authentication.Enabled="Activée" 17 | RtspServer.Properties.Authentication.Realm="Realm: " 18 | RtspServer.Properties.Authentication.Username="Nom d'utilisateur: " 19 | RtspServer.Properties.Authentication.Password="Mot de passe: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(Facultatif)" 21 | RtspServer.Properties.Status="Statut" 22 | RtspServer.Properties.Status.TotalDataSent="Sortie totale des données: " 23 | RtspServer.Properties.Status.Bitrate="Débit binaire: " 24 | RtspServer.Properties.Status.DroppedFrames="Images perdues :" 25 | RtspServer.Properties.Version="Version: " 26 | 27 | RtspOutput="Sortie RTSP" 28 | RtspOutput.Error.BeginDataCapture="ne peut pas commencer la capture de données" 29 | RtspOutput.Error.InitEncoders="Erreur d'initialisation des encodeurs" 30 | RtspOutput.Error.StartRtspServer="le démarrage du serveur RTSP a échoué sur le port '%d'" 31 | RtspOutput.Error.StartMulticast="échec du démarrage de la multidiffusion" 32 | RtspOutput.Error.Encode="erreur d'encodage" 33 | RtspOutput.Error.Unknown="erreur inconnue" 34 | RtspOutput.Hotkey.StartOutput="Démarrer la sortie" 35 | RtspOutput.Hotkey.StopOutput="Arrêter la sortie" 36 | RtspOutput.Properties.Multicast="Activé Multicast" 37 | RtspOutput.Properties.Port="Port" 38 | RtspOutput.Properties.UrlSuffix="Suffixe d'URL" 39 | RtspOutput.Properties.OutputAudio="Activer la sortie audio" 40 | RtspOutput.Properties.Authentication="Authentification" 41 | RtspOutput.Properties.Authentication.Realm="Realm" 42 | RtspOutput.Properties.Authentication.Username="Nom d'utilisateur" 43 | RtspOutput.Properties.Authentication.Password="Mot de passe" 44 | 45 | Reset="Réinitialiser" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/it-IT.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP Server" 2 | RstpServer.Description="OBS RTSP Server Plugin" 3 | RtspServer.Properties="RTSP Server" 4 | RtspServer.Properties.StartOutput="Avvia" 5 | RtspServer.Properties.StopOutput="Ferma" 6 | RtspServer.Properties.Options="Opzioni" 7 | RtspServer.Properties.Options.AutoStart="Avvio Automatico" 8 | RtspServer.Properties.Options.EnabledAudioTracks="Tracce Audio: " 9 | RtspServer.Properties.Target="Target" 10 | RtspServer.Properties.Target.Multicast="Abilitato Multicast" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="Copia" 13 | RtspServer.Properties.Options.Output="Output Opzioni: " 14 | RtspServer.Properties.Options.Output.Tip="Apri File->Impostazioni->Uscita->Dirette per cambiare le opzioni di uscita" 15 | RtspServer.Properties.Authentication="Autenticazione" 16 | RtspServer.Properties.Authentication.Enabled="Abilitato" 17 | RtspServer.Properties.Authentication.Realm="Realm: " 18 | RtspServer.Properties.Authentication.Username="Nome utente: " 19 | RtspServer.Properties.Authentication.Password="Parola d'ordine: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(Opzionale)" 21 | RtspServer.Properties.Status="Stato" 22 | RtspServer.Properties.Status.TotalDataSent="Totale Dati in uscita: " 23 | RtspServer.Properties.Status.Bitrate="Bitrate: " 24 | RtspServer.Properties.Status.DroppedFrames="Dropped Frames:" 25 | RtspServer.Properties.Version="Versione: " 26 | 27 | RtspOutput="Uscita RTSP" 28 | RtspOutput.Error.BeginDataCapture="impossibile iniziare l'acquisizione dei dati" 29 | RtspOutput.Error.InitEncoders="inizializzare l'errore degli encoder" 30 | RtspOutput.Error.StartRtspServer="avvio del server RTSP non riuscito sulla porta '%d'" 31 | RtspOutput.Error.StartMulticast="l'avvio della trasmissione multicast è fallito" 32 | RtspOutput.Error.Encode="errore di codifica" 33 | RtspOutput.Error.Unknown="errore sconosciuto" 34 | RtspOutput.Hotkey.StartOutput="Avvia Uscita" 35 | RtspOutput.Hotkey.StopOutput="Ferma Uscita" 36 | RtspOutput.Properties.Multicast="Abilitato Multicast" 37 | RtspOutput.Properties.Port="Porta" 38 | RtspOutput.Properties.UrlSuffix="Suffisso URL" 39 | RtspOutput.Properties.OutputAudio="Abilita l'uscita audio" 40 | RtspOutput.Properties.Authentication="Autenticazione" 41 | RtspOutput.Properties.Authentication.Realm="Realm" 42 | RtspOutput.Properties.Authentication.Username="Nome utente" 43 | RtspOutput.Properties.Authentication.Password="Parola d'ordine" 44 | 45 | Reset="Ripristina" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/ja-JP.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSPサーバー" 2 | RstpServer.Description="OBS RTSPサーバープラグイン" 3 | RtspServer.Properties="RTSPサーバー" 4 | RtspServer.Properties.StartOutput="出力開始" 5 | RtspServer.Properties.StopOutput="出力終了" 6 | RtspServer.Properties.Options="オプション" 7 | RtspServer.Properties.Options.AutoStart="自動起動" 8 | RtspServer.Properties.Options.EnabledAudioTracks="音声トラック: " 9 | RtspServer.Properties.Target="サーバー" 10 | RtspServer.Properties.Target.Multicast="マルチキャストを有効にする" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="コピー" 13 | RtspServer.Properties.Options.Output="出力オプション: " 14 | RtspServer.Properties.Options.Output.Tip="出力オプションを変更するには [ファイル]->[設定]->[出力]->[配信] を開きます。" 15 | RtspServer.Properties.Authentication="認証" 16 | RtspServer.Properties.Authentication.Enabled="認証を有効にする" 17 | RtspServer.Properties.Authentication.Realm="レルム: " 18 | RtspServer.Properties.Authentication.Username="ユーザー名: " 19 | RtspServer.Properties.Authentication.Password="パスワード: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(オプション)" 21 | RtspServer.Properties.Status="統計" 22 | RtspServer.Properties.Status.TotalDataSent="出力データの合計: " 23 | RtspServer.Properties.Status.Bitrate="ビットレート: " 24 | RtspServer.Properties.Status.DroppedFrames="ドロップされたフレーム:" 25 | RtspServer.Properties.Version="Version: " 26 | 27 | RtspOutput="RTSP出力" 28 | RtspOutput.Error.BeginDataCapture="データキャプチャを開始できません" 29 | RtspOutput.Error.InitEncoders="エンコーダ初期化エラー" 30 | RtspOutput.Error.StartRtspServer="ポート%dでのRTSPサーバーの起動に失敗しました" 31 | RtspOutput.Error.StartMulticast="マルチキャストの開始に失敗しました" 32 | RtspOutput.Error.Encode="エンコードエラー" 33 | RtspOutput.Error.Unknown="不明なエラー" 34 | RtspOutput.Hotkey.StartOutput="出力開始" 35 | RtspOutput.Hotkey.StopOutput="出力終了" 36 | RtspOutput.Properties.Multicast="マルチキャスト" 37 | RtspOutput.Properties.Port="ポート" 38 | RtspOutput.Properties.UrlSuffix="URLのサフィックス" 39 | RtspOutput.Properties.OutputAudio="オーディオ出力を有効にする" 40 | RtspOutput.Properties.Authentication="認証" 41 | RtspOutput.Properties.Authentication.Realm="レルム" 42 | RtspOutput.Properties.Authentication.Username="ユーザー名" 43 | RtspOutput.Properties.Authentication.Password="パスワード" 44 | 45 | Reset="リセット" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/ko-KR.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP 서버" 2 | RstpServer.Description="OBS RTSP 서버 플러그인" 3 | RtspServer.Properties="RTSP 서버" 4 | RtspServer.Properties.StartOutput="스타트" 5 | RtspServer.Properties.StopOutput="중지" 6 | RtspServer.Properties.Options="옵션" 7 | RtspServer.Properties.Options.AutoStart="자동 실행" 8 | RtspServer.Properties.Options.EnabledAudioTracks="오디오 트랙: " 9 | RtspServer.Properties.Target="표적" 10 | RtspServer.Properties.Target.Multicast="활성화된 멀티캐스트" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="복사" 13 | RtspServer.Properties.Options.Output="출력 옵션: " 14 | RtspServer.Properties.Options.Output.Tip="[파일]->[설정]->[출력]->[방송] 을 열어 출력 옵션을 변경합니다." 15 | RtspServer.Properties.Authentication="인증" 16 | RtspServer.Properties.Authentication.Enabled="활성화 됨" 17 | RtspServer.Properties.Authentication.Realm="Realm: " 18 | RtspServer.Properties.Authentication.Username="사용자 이름: " 19 | RtspServer.Properties.Authentication.Password="비밀번호: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(옵션)" 21 | RtspServer.Properties.Status="상태" 22 | RtspServer.Properties.Status.TotalDataSent="총 데이터 출력: " 23 | RtspServer.Properties.Status.Bitrate="비트 레이트: " 24 | RtspServer.Properties.Status.DroppedFrames="드롭된 프레임:" 25 | RtspServer.Properties.Version="버전: " 26 | 27 | RtspOutput="RTSP 출력" 28 | RtspOutput.Error.BeginDataCapture="데이터 캡처를 시작할 수 없습니다." 29 | RtspOutput.Error.InitEncoders="인코더 초기화 오류" 30 | RtspOutput.Error.StartRtspServer="포트에서 RTSP 서버 시작 실패 '%d'" 31 | RtspOutput.Error.StartMulticast="멀티캐스트 시작 실패" 32 | RtspOutput.Error.Encode="인코딩 오류" 33 | RtspOutput.Error.Unknown="알수없는 오류" 34 | RtspOutput.Hotkey.StartOutput="시작 출력" 35 | RtspOutput.Hotkey.StopOutput="출력 중지" 36 | RtspOutput.Properties.Multicast="활성화된 멀티캐스트" 37 | RtspOutput.Properties.Port="포트" 38 | RtspOutput.Properties.UrlSuffix="URL 접미사" 39 | RtspOutput.Properties.OutputAudio="오디오 출력 활성화" 40 | RtspOutput.Properties.Authentication="인증" 41 | RtspOutput.Properties.Authentication.Realm="Realm" 42 | RtspOutput.Properties.Authentication.Username="사용자 이름" 43 | RtspOutput.Properties.Authentication.Password="비밀번호" 44 | 45 | Reset="초기화" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/nl-NL.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP Server" 2 | RstpServer.Description="OBS RTSP Server Plugin" 3 | RtspServer.Properties="RTSP Server" 4 | RtspServer.Properties.StartOutput="Starten" 5 | RtspServer.Properties.StopOutput="Stoppen" 6 | RtspServer.Properties.Options="Opties" 7 | RtspServer.Properties.Options.AutoStart="AutoStart" 8 | RtspServer.Properties.Options.EnabledAudioTracks="Audiotracks: " 9 | RtspServer.Properties.Target="Doelwit" 10 | RtspServer.Properties.Target.Multicast="Ingeschakelde Multicast" 11 | RtspServer.Properties.Target.Address="URL: " 12 | RtspServer.Properties.Target.Address.Copy="Kopiëren" 13 | RtspServer.Properties.Options.Output="Uitvoeropties: " 14 | RtspServer.Properties.Options.Output.Tip="Als u de uitvoeropties wilt wijzigen, opent u: Bestand->Instellingen->Uitvoer->Streamen." 15 | RtspServer.Properties.Authentication="Authenticatie" 16 | RtspServer.Properties.Authentication.Enabled="Enabled" 17 | RtspServer.Properties.Authentication.Realm="Realm: " 18 | RtspServer.Properties.Authentication.Username="Gebruikersnaam: " 19 | RtspServer.Properties.Authentication.Password="Wachtwoord: " 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(Optioneel)" 21 | RtspServer.Properties.Status="Toestand" 22 | RtspServer.Properties.Status.TotalDataSent="Totale gegevensoutput: " 23 | RtspServer.Properties.Status.Bitrate="Bitsnelheid: " 24 | RtspServer.Properties.Status.DroppedFrames="Dropped Frames:" 25 | RtspServer.Properties.Version="Versie: " 26 | 27 | RtspOutput="RTSP Uitvoer" 28 | RtspOutput.Error.BeginDataCapture="Data-acquisitie kan niet worden gestart" 29 | RtspOutput.Error.InitEncoders="initialiseer encoderfout" 30 | RtspOutput.Error.StartRtspServer="RTSP-server kan niet worden gestart op poort '%d'" 31 | RtspOutput.Error.StartMulticast="starten van multicast mislukt" 32 | RtspOutput.Error.Encode="Coderingsfout" 33 | RtspOutput.Error.Unknown="Onbekende fout" 34 | RtspOutput.Hotkey.StartOutput="Starten" 35 | RtspOutput.Hotkey.StopOutput="Stoppen" 36 | RtspOutput.Properties.Multicast="Ingeschakelde Multicast" 37 | RtspOutput.Properties.Port="Poort" 38 | RtspOutput.Properties.UrlSuffix="Url-achtervoegsel" 39 | RtspOutput.Properties.OutputAudio="Audio-uitvoer inschakelen" 40 | RtspOutput.Properties.Authentication="Authenticatie" 41 | RtspOutput.Properties.Authentication.Realm="Realm" 42 | RtspOutput.Properties.Authentication.Username="Gebruikersnaam" 43 | RtspOutput.Properties.Authentication.Password="Wachtwoord" 44 | 45 | Reset="Terugzetten" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/ru-RU.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP Сервер" 2 | RstpServer.Description="OBS RTSP Сервер Плагин" 3 | RtspServer.Properties="RTSP Сервер" 4 | RtspServer.Properties.StartOutput="Начать" 5 | RtspServer.Properties.StopOutput="Остановить" 6 | RtspServer.Properties.Options="Опции" 7 | RtspServer.Properties.Options.AutoStart="Автоматический запуск" 8 | RtspServer.Properties.Options.EnabledAudioTracks="Аудио дорожки:" 9 | RtspServer.Properties.Target="Цель" 10 | RtspServer.Properties.Target.Multicast="Включен многоадресный режим" 11 | RtspServer.Properties.Target.Address="URL:" 12 | RtspServer.Properties.Target.Address.Copy="Копировать" 13 | RtspServer.Properties.Options.Output="Варианты вывода:" 14 | RtspServer.Properties.Options.Output.Tip="Откройте Файл->Настройки->Вывод->Потоковая передача, чтобы изменить параметры вывода." 15 | RtspServer.Properties.Authentication="Аутентификация" 16 | RtspServer.Properties.Authentication.Enabled="Включено" 17 | RtspServer.Properties.Authentication.Realm="Царство:" 18 | RtspServer.Properties.Authentication.Username="Имя пользователя:" 19 | RtspServer.Properties.Authentication.Password="Пароль:" 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(Опционально)" 21 | RtspServer.Properties.Status="Статус" 22 | RtspServer.Properties.Status.TotalDataSent="Общий объем данных:" 23 | RtspServer.Properties.Status.Bitrate="Битрейт:" 24 | RtspServer.Properties.Status.DroppedFrames="Сброшенные кадры:" 25 | RtspServer.Properties.Version="Версия:" 26 | 27 | RtspOutput="RTSP Выход" 28 | RtspOutput.Error.BeginDataCapture="не могу начать захват данных" 29 | RtspOutput.Error.InitEncoders="Ошибка инициализации кодировщиков" 30 | RtspOutput.Error.StartRtspServer="запуск сервера RTSP не удался на порту '%d'" 31 | RtspOutput.Error.StartMulticast="начало многоадресной передачи не удалось" 32 | RtspOutput.Error.Encode="ошибка кодирования" 33 | RtspOutput.Error.Unknown="неизвестная ошибка" 34 | RtspOutput.Hotkey.StartOutput="Начало вывода" 35 | RtspOutput.Hotkey.StopOutput="Остановить вывод" 36 | RtspOutput.Properties.Multicast="Включен многоадресный режим" 37 | RtspOutput.Properties.Port="Порт" 38 | RtspOutput.Properties.UrlSuffix="URL суффикс" 39 | RtspOutput.Properties.OutputAudio="Включить аудиовыход" 40 | RtspOutput.Properties.Authentication="Аутентификация" 41 | RtspOutput.Properties.Authentication.Realm="Царство" 42 | RtspOutput.Properties.Authentication.Username="Имя пользователя" 43 | RtspOutput.Properties.Authentication.Password="Пароль" 44 | 45 | Reset="Сбросить" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/zh-CN.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP 服务器" 2 | RstpServer.Description="OBS RTSP 服务器插件" 3 | RtspServer.Properties="RTSP 服务器" 4 | RtspServer.Properties.StartOutput="启动" 5 | RtspServer.Properties.StopOutput="停止" 6 | RtspServer.Properties.Options="选项" 7 | RtspServer.Properties.Options.AutoStart="自动启动" 8 | RtspServer.Properties.Options.EnabledAudioTracks="音轨:" 9 | RtspServer.Properties.Target="目标" 10 | RtspServer.Properties.Target.Multicast="启用组播" 11 | RtspServer.Properties.Target.Address="URL:" 12 | RtspServer.Properties.Target.Address.Copy="复制" 13 | RtspServer.Properties.Options.Output="输出选项:" 14 | RtspServer.Properties.Options.Output.Tip="打开 文件->设置->输出->串流 以更改输出选项。" 15 | RtspServer.Properties.Authentication="身份认证" 16 | RtspServer.Properties.Authentication.Enabled="启用" 17 | RtspServer.Properties.Authentication.Realm="领域:" 18 | RtspServer.Properties.Authentication.Username="用户名:" 19 | RtspServer.Properties.Authentication.Password="密码:" 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(可选)" 21 | RtspServer.Properties.Status="状态" 22 | RtspServer.Properties.Status.TotalDataSent="总数据输出:" 23 | RtspServer.Properties.Status.Bitrate="比特率:" 24 | RtspServer.Properties.Status.DroppedFrames="丢弃的帧:" 25 | RtspServer.Properties.Version="版本:" 26 | 27 | RtspOutput="RTSP 输出" 28 | RtspOutput.Error.BeginDataCapture="无法开始数据捕获" 29 | RtspOutput.Error.InitEncoders="初始化编码器时发生错误" 30 | RtspOutput.Error.StartRtspServer="在端口“%d”上启动RTSP服务器失败" 31 | RtspOutput.Error.StartMulticast="组播启动失败" 32 | RtspOutput.Error.Encode="编码错误" 33 | RtspOutput.Error.Unknown="未知错误" 34 | RtspOutput.Hotkey.StartOutput="启动输出" 35 | RtspOutput.Hotkey.StopOutput="停止输出" 36 | RtspOutput.Properties.Multicast="启用组播" 37 | RtspOutput.Properties.Port="端口" 38 | RtspOutput.Properties.UrlSuffix="URL 后缀" 39 | RtspOutput.Properties.OutputAudio="启用音频输出" 40 | RtspOutput.Properties.Authentication="身份认证" 41 | RtspOutput.Properties.Authentication.Realm="领域" 42 | RtspOutput.Properties.Authentication.Username="用户名" 43 | RtspOutput.Properties.Authentication.Password="密码" 44 | 45 | Reset="重置" 46 | 47 | -------------------------------------------------------------------------------- /data/locale/zh-TW.ini: -------------------------------------------------------------------------------- 1 | RtspServer="RTSP 伺服器" 2 | RstpServer.Description="OBS RTSP 伺服器外掛程式" 3 | RtspServer.Properties="RTSP 伺服器" 4 | RtspServer.Properties.StartOutput="開始" 5 | RtspServer.Properties.StopOutput="停止" 6 | RtspServer.Properties.Options="選項" 7 | RtspServer.Properties.Options.AutoStart="自動開始" 8 | RtspServer.Properties.Options.EnabledAudioTracks="音軌:" 9 | RtspServer.Properties.Target="目標" 10 | RtspServer.Properties.Target.Multicast="啟用組播" 11 | RtspServer.Properties.Target.Address="URL:" 12 | RtspServer.Properties.Target.Address.Copy="拷貝" 13 | RtspServer.Properties.Options.Output="輸出選項:" 14 | RtspServer.Properties.Options.Output.Tip="打開 檔案->設定->輸出->串流 以更改輸出選項。" 15 | RtspServer.Properties.Authentication="身份驗證" 16 | RtspServer.Properties.Authentication.Enabled="啟用" 17 | RtspServer.Properties.Authentication.Realm="領域:" 18 | RtspServer.Properties.Authentication.Username="用戶名:" 19 | RtspServer.Properties.Authentication.Password="密碼:" 20 | RtspServer.Properties.Authentication.PasswordPlaceholder="(可選)" 21 | RtspServer.Properties.Status="狀態" 22 | RtspServer.Properties.Status.TotalDataSent="總數據輸出:" 23 | RtspServer.Properties.Status.Bitrate="位元速率:" 24 | RtspServer.Properties.Status.DroppedFrames="落幀數:" 25 | RtspServer.Properties.Version="版本:" 26 | 27 | RtspOutput="RTSP 輸出" 28 | RtspOutput.Error.BeginDataCapture="無法開始數據捕獲" 29 | RtspOutput.Error.InitEncoders="初始化編碼器錯誤" 30 | RtspOutput.Error.StartRtspServer="連接埠 %d 上啟動RTSP伺服器失敗" 31 | RtspOutput.Error.StartMulticast="組播啟動失敗" 32 | RtspOutput.Error.Encode="編碼錯誤" 33 | RtspOutput.Error.Unknown="未知錯誤" 34 | RtspOutput.Hotkey.StartOutput="開始輸出" 35 | RtspOutput.Hotkey.StopOutput="停止輸出" 36 | RtspOutput.Properties.Multicast="啟用組播" 37 | RtspOutput.Properties.Port="連接埠" 38 | RtspOutput.Properties.UrlSuffix="URL 後綴" 39 | RtspOutput.Properties.OutputAudio="啟用音訊輸出" 40 | RtspOutput.Properties.Authentication="身份驗證" 41 | RtspOutput.Properties.Authentication.Realm="領域" 42 | RtspOutput.Properties.Authentication.Username="用戶名" 43 | RtspOutput.Properties.Authentication.Password="密碼" 44 | 45 | Reset="重置" 46 | 47 | -------------------------------------------------------------------------------- /external/BuildHelper.cmake: -------------------------------------------------------------------------------- 1 | set(OBS_PLUGIN_OBS_SOURCE_DIR ${OBS_SOURCE_DIR}) 2 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/external") 3 | 4 | set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/release") 5 | set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${QTDIR};${DepsPath}") 6 | 7 | set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${OBS_PLUGUN_GIT_TAG}-linux") 8 | set(CPACK_PACKAGING_INSTALL_PREFIX "/usr") 9 | #set(CPACK_SOURCE_PACKAGE_FILE_NAME "${OBS_PLUGIN_PACKAGE_FILE_NAME}") 10 | set(MACOSX_PLUGIN_GUI_IDENTIFIER "${MACOS_BUNDLEID}") 11 | set(MACOSX_PLUGIN_BUNDLE_VERSION "${OBS_PLUGUN_LONG_VERSION}") 12 | set(MACOSX_PLUGIN_SHORT_VERSION_STRING "${OBS_PLUGUN_VERSION}") 13 | 14 | find_package(libobs REQUIRED) 15 | #add_library(OBS::libobs STATIC IMPORTED GLOBAL) 16 | #set_target_properties(OBS::libobs PROPERTIES 17 | # IMPORTED_LOCATION "${LIBOBS_LIB}" 18 | # ) 19 | add_library(OBS::libobs STATIC IMPORTED GLOBAL) 20 | if (LIBOBS_LIB MATCHES "/([^/]+)\\.framework$") 21 | set(_libobs_fw "${LIBOBS_LIB}/${CMAKE_MATCH_1}") 22 | if(EXISTS "${_libobs_fw}.tbd") 23 | string(APPEND _libobs_fw ".tbd") 24 | endif() 25 | message("${_libobs_fw}") 26 | set_target_properties(OBS::libobs PROPERTIES 27 | IMPORTED_LOCATION "${_libobs_fw}" 28 | ) 29 | else() 30 | set_target_properties(OBS::libobs PROPERTIES 31 | IMPORTED_LOCATION "${LIBOBS_LIB}" 32 | ) 33 | endif() 34 | add_library(libobs ALIAS OBS::libobs) 35 | 36 | find_package(obs-frontend-api REQUIRED) 37 | add_library(OBS::obs-frontend-api STATIC IMPORTED GLOBAL) 38 | set_target_properties(OBS::obs-frontend-api PROPERTIES 39 | IMPORTED_LOCATION "${OBS_FRONTEND_API_LIB}" 40 | ) 41 | add_library(obs-frontend-api ALIAS OBS::obs-frontend-api) 42 | 43 | include("${CMAKE_CURRENT_SOURCE_DIR}/external/ObsPluginHelpers.cmake") 44 | 45 | if(OS_WINDOWS) 46 | if(MSVC) 47 | target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE /W3) 48 | endif() 49 | elseif(OS_MACOS) 50 | configure_file( 51 | ${CMAKE_SOURCE_DIR}/bundle/installer-macos.pkgproj.in 52 | ${CMAKE_SOURCE_DIR}/bundle/installer-macos.generated.pkgproj) 53 | 54 | target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -Wall) 55 | else() 56 | target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -Wall) 57 | endif() 58 | -------------------------------------------------------------------------------- /external/GitInfoHelper.cmake: -------------------------------------------------------------------------------- 1 | 2 | 3 | function(get_git_version git_tag_name git_tag_version_name git_tag_short_version_name git_tag_long_version_name) 4 | execute_process(COMMAND git describe --tags --always --dirty=-dev 5 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 6 | TIMEOUT 10 7 | OUTPUT_VARIABLE git_tag 8 | OUTPUT_STRIP_TRAILING_WHITESPACE) 9 | 10 | execute_process(COMMAND git rev-list HEAD --count 11 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 12 | TIMEOUT 10 13 | OUTPUT_VARIABLE git_rev_list_count 14 | OUTPUT_STRIP_TRAILING_WHITESPACE) 15 | 16 | string(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+(-[a-z0-9]+)*$" git_tag_version "${git_tag}") 17 | string(REGEX MATCH "^[0-9]+.[0-9]+.[0-9]+" git_tag_short_version "${git_tag_version}") 18 | 19 | #if("${git_tag_version}" MATCHES "-[0-9]+-g") 20 | # string(REGEX MATCH "-[0-9]+-g" _git_tag_tweak_version_temp "${git_tag_version}") 21 | # string(REGEX MATCH "[0-9]+" _git_tag_tweak_version "${_git_tag_tweak_version_temp}") 22 | #else() 23 | # set(_git_tag_tweak_version "0") 24 | #endif() 25 | 26 | if("${git_rev_list_count}" MATCHES "^[0-9]+$") 27 | set(_git_tag_tweak_version "${git_rev_list_count}") 28 | else() 29 | set(_git_tag_tweak_version "0") 30 | endif() 31 | 32 | if("${git_tag_short_version}" STREQUAL "") 33 | set(git_tag_long_version "0.0.1.${_git_tag_tweak_version}") 34 | else() 35 | set(git_tag_long_version "${git_tag_short_version}.${_git_tag_tweak_version}") 36 | endif() 37 | 38 | set(${git_tag_name} "${git_tag}" PARENT_SCOPE) 39 | set(${git_tag_version_name} "${git_tag_version}" PARENT_SCOPE) 40 | set(${git_tag_short_version_name} "${git_tag_short_version}" PARENT_SCOPE) 41 | set(${git_tag_long_version_name} "${git_tag_long_version}" PARENT_SCOPE) 42 | 43 | message("Git Tag:\t${git_tag}") 44 | message("Git Tag Version:\t${git_tag_version}") 45 | message("Git Tag Short Version:\t${git_tag_short_version}") 46 | message("Git Tag Long Version:\t${git_tag_long_version}") 47 | endfunction(get_git_version) 48 | -------------------------------------------------------------------------------- /helper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #pragma once 9 | 10 | #define CONFIG_SECTIION "RstpOutput" 11 | #define HOTKEY_CONFIG_SECTIION "Hotkeys" 12 | 13 | enum encoder_codec { UNKNOW = 0, H264 = 1, HEVC = 2, AV1 = 3, AAC = 4 }; 14 | 15 | [[maybe_unused]] static bool make_config_dir() 16 | { 17 | auto path = obs_module_config_path(""); 18 | auto ret = os_mkdirs(path); 19 | bfree(path); 20 | return ret == MKDIR_SUCCESS || ret == MKDIR_EXISTS; 21 | } 22 | 23 | [[maybe_unused]] static obs_data_t *rtsp_output_read_data() 24 | { 25 | obs_data_t *data; 26 | auto path = obs_module_config_path("rtsp_output.json"); 27 | data = obs_data_create_from_json_file_safe(path, "bak"); 28 | bfree(path); 29 | return data; 30 | } 31 | 32 | [[maybe_unused]] static bool rtsp_output_save_data(obs_data_t *data) 33 | { 34 | if (!make_config_dir()) 35 | return false; 36 | auto path = obs_module_config_path("rtsp_output.json"); 37 | auto ret = obs_data_save_json_safe(data, path, "tmp", "bak"); 38 | bfree(path); 39 | return ret; 40 | } 41 | 42 | [[maybe_unused]] static config_t *rtsp_properties_open_config() 43 | { 44 | if (!make_config_dir()) 45 | return nullptr; 46 | auto path = obs_module_config_path("config.ini"); 47 | config_t *config; 48 | auto ret = config_open(&config, path, CONFIG_OPEN_ALWAYS); 49 | bfree(path); 50 | if (ret) 51 | return nullptr; 52 | config_set_default_bool(config, CONFIG_SECTIION, "AutoStart", false); 53 | config_set_default_bool(config, CONFIG_SECTIION, "AudioTrack1", true); 54 | config_set_default_bool(config, CONFIG_SECTIION, "AudioTrack2", false); 55 | config_set_default_bool(config, CONFIG_SECTIION, "AudioTrack3", false); 56 | config_set_default_bool(config, CONFIG_SECTIION, "AudioTrack4", false); 57 | config_set_default_bool(config, CONFIG_SECTIION, "AudioTrack5", false); 58 | config_set_default_bool(config, CONFIG_SECTIION, "AudioTrack6", false); 59 | return config; 60 | } 61 | 62 | [[maybe_unused]] static std::string string_format(char const *format, ...) 63 | { 64 | va_list argp; 65 | va_start(argp, format); 66 | auto size = (size_t)vsnprintf(nullptr, 0, format, argp) + 1; 67 | va_end(argp); 68 | auto buf = std::vector(size); 69 | va_start(argp, format); 70 | vsnprintf(buf.data(), size, format, argp); 71 | va_end(argp); 72 | return std::string(buf.data(), buf.data() + size - 1); 73 | } 74 | 75 | [[maybe_unused]] static std::string rtsp_properties_get_data_volume_display(uint64_t total_bytes) 76 | { 77 | const uint64_t kb = 1024; 78 | const uint64_t mb = kb * 1024; 79 | const uint64_t gb = mb * 1024; 80 | const uint64_t tb = gb * 1024; 81 | if (total_bytes == 0) 82 | return "0.0 MB"; 83 | if (total_bytes < kb) { 84 | return string_format("%lu bytes", total_bytes); 85 | } 86 | if (total_bytes < mb) { 87 | return string_format("%.1f KB", double(total_bytes) / kb); 88 | } 89 | if (total_bytes < gb) { 90 | return string_format("%.1f MB", double(total_bytes) / mb); 91 | } 92 | if (total_bytes < tb) { 93 | return string_format("%.1f GB", double(total_bytes) / gb); 94 | } 95 | return string_format("%.1f TB", double(total_bytes) / tb); 96 | } 97 | 98 | [[maybe_unused]] static encoder_codec get_encoder_codec(const obs_encoder_t *encoder) 99 | { 100 | const char *const codec = obs_encoder_get_codec(encoder); 101 | if (strcmp(codec, "h264") == 0) { 102 | return encoder_codec::H264; 103 | } 104 | if (strcmp(codec, "hevc") == 0) { 105 | return encoder_codec::HEVC; 106 | } 107 | if (strcmp(codec, "av1") == 0) { 108 | return encoder_codec::AV1; 109 | } 110 | if (strcmp(codec, "aac") == 0) { 111 | return encoder_codec::AAC; 112 | } 113 | return UNKNOW; 114 | } 115 | -------------------------------------------------------------------------------- /installer/function/unprevious.nsi: -------------------------------------------------------------------------------- 1 | ;Uninstall Previous 2 | Function UninstallPrevious 3 | 4 | ; Check for uninstaller. 5 | ReadRegStr $R0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" 6 | StrCpy $R1 $INSTDIR 7 | ReadRegStr $R1 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "InstallLocation" 8 | 9 | ${If} $R0 == "" 10 | Return 11 | ${EndIf} 12 | 13 | DetailPrint $(LANGTEXT_REMOVING_PREV) 14 | 15 | ; Run the uninstaller silently. 16 | ExecWait '"$R0" /S _?=$R1' $0 17 | 18 | ${If} $0 != 0 19 | Abort $(LANGTEXT_REMOVING_PREV_FAILED) 20 | ${EndIf} 21 | 22 | FunctionEnd 23 | 24 | ; eof 25 | -------------------------------------------------------------------------------- /installer/locale.nsi: -------------------------------------------------------------------------------- 1 | ; Set languages (first is default language) 2 | !insertmacro MUI_LANGUAGE "English" 3 | !insertmacro MUI_LANGUAGE "SimpChinese" 4 | !insertmacro MUI_LANGUAGE "TradChinese" 5 | !insertmacro MUI_LANGUAGE "German" 6 | !insertmacro MUI_LANGUAGE "Spanish" 7 | !insertmacro MUI_LANGUAGE "Dutch" 8 | !insertmacro MUI_LANGUAGE "French" 9 | !insertmacro MUI_LANGUAGE "Japanese" 10 | !insertmacro MUI_LANGUAGE "Italian" 11 | 12 | ; Locale List 13 | !include .\locale\en-US.nsi 14 | !include .\locale\zh-CN.nsi 15 | !include .\locale\zh-TW.nsi 16 | !include .\locale\de-DE.nsi 17 | !include .\locale\es-ES.nsi 18 | !include .\locale\nl-NL.nsi 19 | !include .\locale\fr-FR.nsi 20 | !include .\locale\ja-JP.nsi 21 | !include .\locale\it-IT.nsi 22 | -------------------------------------------------------------------------------- /installer/locale/de-DE.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_GERMAN} "OBS RTSP Server Plugin" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_GERMAN} "Lizenzinformationen" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_GERMAN} "Bitte überprüfen Sie die Lizenzbedingungen, bevor Sie ${DISPLAYNAME} installieren." 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_GERMAN} "Drücken Sie auf die Bild Unten-Taste oder scrollen Sie, um den Rest der Lizenz anzuzeigen." 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_GERMAN} "&Weiter >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_GERMAN} "Vorherige Installation wird entfernt..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_GERMAN} "Fehler beim Entfernen der vorherigen Installation." 8 | -------------------------------------------------------------------------------- /installer/locale/en-US.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_ENGLISH} "OBS RTSP Server Plugin" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_ENGLISH} "License Information" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_ENGLISH} "Please review the license terms before installing ${DISPLAYNAME}." 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_ENGLISH} "Press Page Down or scroll to see the rest of the license." 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_ENGLISH} "&Next >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_ENGLISH} "Removing previous installation..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_ENGLISH} "Failed to remove previous installation." 8 | -------------------------------------------------------------------------------- /installer/locale/es-ES.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_SPANISH} "OBS RTSP Server Plugin" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_SPANISH} "Información de licencia" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_SPANISH} "Revise los términos de la licencia antes de instalar ${DISPLAYNAME}." 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_SPANISH} "Presione Av Pág o desplácese para ver el resto de la licencia." 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_SPANISH} "&Siguiente >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_SPANISH} "Eliminando la instalación anterior ..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_SPANISH} "No se pudo eliminar la instalación anterior." 8 | -------------------------------------------------------------------------------- /installer/locale/fr-FR.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_FRENCH} "Module d'extension de serveur RTSP OBS" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_FRENCH} "Informations de licence" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_FRENCH} "Veuillez vérifier les termes de la licence avant d'installer ${DISPLAYNAME}." 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_FRENCH} "Appuyez sur le bouton page bas ou faites défiler pour afficher le reste de la licence." 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_FRENCH} "&Suivant >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_FRENCH} "Suppression de l'installation précédente..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_FRENCH} "Échec de la suppression de l'installation précédente." 8 | -------------------------------------------------------------------------------- /installer/locale/it-IT.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_ITALIAN} "OBS RTSP Server Plugin" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_ITALIAN} "Informazioni sulla licenza" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_ITALIAN} "Si prega di rivedere i termini della licenza prima di installare ${DISPLAYNAME}." 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_ITALIAN} "Premi [Pagina Giù] o scorri per vedere il resto della licenza." 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_ITALIAN} "&Seguente >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_ITALIAN} "Rimozione dell'installazione precedente..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_ITALIAN} "Impossibile rimuovere l'installazione precedente." 8 | -------------------------------------------------------------------------------- /installer/locale/ja-JP.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_JAPANESE} "OBS RTSPサーバープラグイン" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_JAPANESE} "ライセンス情報" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_JAPANESE} "${DISPLAYNAME} をインストールする前にライセンス条項を確認してください。" 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_JAPANESE} "「PageDown」を押すか、スクロールして残りのライセンスを表示します。" 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_JAPANESE} "&次 >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_JAPANESE} "以前のインストールを削除します..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_JAPANESE} "以前のインストールを削除できませんでした。" 8 | -------------------------------------------------------------------------------- /installer/locale/ko-KR.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_KOREAN} "OBS RTSP 서버 플러그인" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_KOREAN} "라이선스 정보" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_KOREAN} "${DISPLAYNAME}를 설치하기 전에 사용 조건을 검토하십시오." 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_KOREAN} "[Page Down]을 누르거나 스크롤하여 나머지 라이선스를 확인합니다." 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_KOREAN} "&다음 >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_KOREAN} "이전 설치 제거 중 ..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_KOREAN} "이전 설치를 제거하지 못했습니다." 8 | -------------------------------------------------------------------------------- /installer/locale/nl-NL.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_DUTCH} "OBS RTSP Server Plugin" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_DUTCH} "Licentie-informatie" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_DUTCH} "Lees de licentievoorwaarden voordat u gaat installeren ${DISPLAYNAME}." 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_DUTCH} "Druk op Page Down of scroll om de rest van de licentie te zien." 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_DUTCH} "&Volgende >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_DUTCH} "Vorige installatie verwijderen..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_DUTCH} "Vorige installatie kan niet worden verwijderd." 8 | -------------------------------------------------------------------------------- /installer/locale/zh-CN.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_SIMPCHINESE} "OBS RTSP 服务器插件" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_SIMPCHINESE} "许可证信息" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_SIMPCHINESE} "请在安装 ${DISPLAYNAME} 之前查看许可条款。" 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_SIMPCHINESE} "按 Page Down 或滚动查看许可证的其余部分。" 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_SIMPCHINESE} "下一步(&N) >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_SIMPCHINESE} "正在删除以前的安装..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_SIMPCHINESE} "无法删除以前的安装。" 8 | -------------------------------------------------------------------------------- /installer/locale/zh-TW.nsi: -------------------------------------------------------------------------------- 1 | LangString LANGTEXT_DISPLAYNAME ${LANG_TRADCHINESE} "OBS RTSP 伺服器外掛程式" 2 | LangString LANGTEXT_HEADER_TEXT ${LANG_TRADCHINESE} "許可證資訊" 3 | LangString LANGTEXT_HEADER_SUBTEXT ${LANG_TRADCHINESE} "請在安裝 ${DISPLAYNAME} 之前查看許可條款。" 4 | LangString LANGTEXT_LICENSEPAGE_TEXT_TOP ${LANG_TRADCHINESE} "按 Page Down 或滾動查看許可證的其餘部分。" 5 | LangString LANGTEXT_LICENSEPAGE_BUTTON ${LANG_TRADCHINESE} "下一步(&N) >" 6 | LangString LANGTEXT_REMOVING_PREV ${LANG_TRADCHINESE} "正在删除以前的安裝..." 7 | LangString LANGTEXT_REMOVING_PREV_FAILED ${LANG_TRADCHINESE} "無法刪除以前的安裝。" 8 | -------------------------------------------------------------------------------- /installer/obs.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iamscottxu/obs-rtspserver/1a1924a6ce487ea5a3b0a23719254c54bc590928/installer/obs.ico -------------------------------------------------------------------------------- /rtsp-server/3rdpart/libb64/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(libb64) 3 | 4 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 5 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 6 | include_directories("libb64/include") 7 | 8 | file(GLOB libb64_SOURCES 9 | libb64/src/*.c) 10 | 11 | file(GLOB libb64_HEADERS 12 | libb64/include/b64/*.h) 13 | 14 | add_library(libb64 STATIC 15 | ${libb64_SOURCES} 16 | ${libb64_HEADERS}) 17 | -------------------------------------------------------------------------------- /rtsp-server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(rtsp-server) 3 | 4 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 5 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 6 | include_directories("3rdpart/md5") 7 | include_directories("3rdpart/libb64/libb64/include") 8 | add_subdirectory(3rdpart/libb64) 9 | 10 | file(GLOB rtsp-server_net_SOURCES 11 | net/*.cpp) 12 | 13 | file(GLOB rtsp-server_xop_SOURCES 14 | xop/*.cpp) 15 | 16 | set(rtsp-server_SOURCES 17 | ${rtsp-server_net_SOURCES} 18 | ${rtsp-server_xop_SOURCES} 19 | ) 20 | 21 | file(GLOB rtsp-server_net_HEADERS 22 | net/*.h) 23 | 24 | file(GLOB rtsp-server_xop_HEADERS 25 | xop/*.h) 26 | 27 | set(rtsp-server_HEADERS 28 | ${rtsp-server_net_HEADERS} 29 | ${rtsp-server_xop_HEADERS} 30 | ) 31 | 32 | add_library(rtsp-server STATIC 33 | ${rtsp-server_SOURCES} 34 | ${rtsp-server_HEADERS} 35 | ) 36 | 37 | target_link_libraries(rtsp-server 38 | libb64) 39 | -------------------------------------------------------------------------------- /rtsp-server/net/Acceptor.cpp: -------------------------------------------------------------------------------- 1 | //Scott Xu 2 | //2020-12-6 Add IPv6 support. 3 | #include "Acceptor.h" 4 | #include "EventLoop.h" 5 | #include "SocketUtil.h" 6 | #include "Logger.h" 7 | 8 | using namespace xop; 9 | 10 | Acceptor::Acceptor(EventLoop *eventLoop) 11 | : event_loop_(eventLoop), tcp_socket_(new TcpSocket()) 12 | { 13 | } 14 | 15 | Acceptor::~Acceptor() = default; 16 | 17 | int Acceptor::Listen(const std::string &ip, const uint16_t port) 18 | { 19 | std::lock_guard locker(mutex_); 20 | 21 | if (tcp_socket_->GetSocket() > 0) { 22 | tcp_socket_->Close(); 23 | } 24 | const SOCKET sockfd = 25 | tcp_socket_->Create(SocketUtil::IsIpv6Address(ip)); 26 | channel_ptr_.reset(new Channel(sockfd)); 27 | SocketUtil::SetReuseAddr(sockfd); 28 | SocketUtil::SetReusePort(sockfd); 29 | SocketUtil::SetNonBlock(sockfd); 30 | 31 | if (!tcp_socket_->Bind(ip, port)) { 32 | return -1; 33 | } 34 | 35 | if (!tcp_socket_->Listen(1024)) { 36 | return -1; 37 | } 38 | 39 | channel_ptr_->SetReadCallback([this] { this->OnAccept(); }); 40 | channel_ptr_->EnableReading(); 41 | event_loop_->UpdateChannel(channel_ptr_); 42 | return 0; 43 | } 44 | 45 | void Acceptor::Close() 46 | { 47 | std::lock_guard locker(mutex_); 48 | 49 | if (tcp_socket_->GetSocket() > 0) { 50 | event_loop_->RemoveChannel(channel_ptr_); 51 | tcp_socket_->Close(); 52 | } 53 | } 54 | 55 | void Acceptor::OnAccept() 56 | { 57 | std::lock_guard locker(mutex_); 58 | if (const auto sockfd = tcp_socket_->Accept(); sockfd > 0) { 59 | if (new_connection_callback_) { 60 | new_connection_callback_(sockfd); 61 | } else { 62 | SocketUtil::Close(sockfd); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rtsp-server/net/Acceptor.h: -------------------------------------------------------------------------------- 1 | //Scott Xu 2 | //2020-12-6 Add IPv6 support. 3 | #ifndef XOP_ACCEPTOR_H 4 | #define XOP_ACCEPTOR_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include "Channel.h" 10 | #include "TcpSocket.h" 11 | 12 | namespace xop { 13 | 14 | typedef std::function NewConnectionCallback; 15 | 16 | class EventLoop; 17 | 18 | class Acceptor { 19 | public: 20 | explicit Acceptor(EventLoop *eventLoop); 21 | virtual ~Acceptor(); 22 | 23 | void SetNewConnectionCallback(const NewConnectionCallback &cb) 24 | { 25 | new_connection_callback_ = cb; 26 | } 27 | 28 | int Listen(const std::string &ip, uint16_t port); 29 | void Close(); 30 | 31 | private: 32 | void OnAccept(); 33 | 34 | EventLoop *event_loop_ = nullptr; 35 | std::mutex mutex_; 36 | std::unique_ptr tcp_socket_; 37 | ChannelPtr channel_ptr_; 38 | NewConnectionCallback new_connection_callback_; 39 | }; 40 | 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /rtsp-server/net/BufferReader.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #include "BufferReader.h" 5 | #include "Socket.h" 6 | 7 | using namespace xop; 8 | uint32_t xop::ReadUint32BE(char *data) 9 | { 10 | const auto p = reinterpret_cast(data); 11 | const uint32_t value = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 12 | return value; 13 | } 14 | 15 | uint32_t xop::ReadUint32LE(char *data) 16 | { 17 | const auto p = reinterpret_cast(data); 18 | const uint32_t value = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; 19 | return value; 20 | } 21 | 22 | uint32_t xop::ReadUint24BE(char *data) 23 | { 24 | const auto p = reinterpret_cast(data); 25 | const uint32_t value = (p[0] << 16) | (p[1] << 8) | p[2]; 26 | return value; 27 | } 28 | 29 | uint32_t xop::ReadUint24LE(char *data) 30 | { 31 | const auto p = reinterpret_cast(data); 32 | const uint32_t value = (p[2] << 16) | (p[1] << 8) | p[0]; 33 | return value; 34 | } 35 | 36 | uint16_t xop::ReadUint16BE(char *data) 37 | { 38 | const auto p = reinterpret_cast(data); 39 | const uint16_t value = (p[0] << 8) | p[1]; 40 | return value; 41 | } 42 | 43 | uint16_t xop::ReadUint16LE(char *data) 44 | { 45 | const auto *p = reinterpret_cast(data); 46 | const uint16_t value = (p[1] << 8) | p[0]; 47 | return value; 48 | } 49 | 50 | constexpr char BufferReader::kCRLF[] = "\r\n"; 51 | 52 | BufferReader::BufferReader(const uint32_t initialSize) : buffer_(initialSize) 53 | { 54 | buffer_.resize(initialSize); 55 | } 56 | 57 | BufferReader::~BufferReader() = default; 58 | 59 | int BufferReader::Read(const SOCKET sockfd) 60 | { 61 | if (const size_t size = WritableBytes(); size < MAX_BYTES_PER_READ) { 62 | const auto bufferReaderSize = buffer_.size(); 63 | if (bufferReaderSize > MAX_BUFFER_SIZE) { 64 | return 0; 65 | } 66 | 67 | buffer_.resize(bufferReaderSize + MAX_BYTES_PER_READ); 68 | } 69 | 70 | const int bytes_read = 71 | recv(sockfd, beginWrite(), MAX_BYTES_PER_READ, 0); 72 | if (bytes_read > 0) { 73 | writer_index_ += bytes_read; 74 | } 75 | 76 | return bytes_read; 77 | } 78 | 79 | size_t BufferReader::ReadAll(std::string &data) 80 | { 81 | const size_t size = ReadableBytes(); 82 | if (size > 0) { 83 | data.assign(Peek(), size); 84 | writer_index_ = 0; 85 | reader_index_ = 0; 86 | } 87 | 88 | return size; 89 | } 90 | 91 | size_t BufferReader::ReadUntilCrlf(std::string &data) 92 | { 93 | const char *crlf = FindLastCrlf(); 94 | if (crlf == nullptr) { 95 | return 0; 96 | } 97 | 98 | const auto size = static_cast(crlf - Peek() + 2); 99 | data.assign(Peek(), size); 100 | Retrieve(size); 101 | return size; 102 | } 103 | -------------------------------------------------------------------------------- /rtsp-server/net/BufferReader.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_BUFFER_READER_H 5 | #define XOP_BUFFER_READER_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "Socket.h" 12 | 13 | namespace xop { 14 | 15 | uint32_t ReadUint32BE(char *data); 16 | uint32_t ReadUint32LE(char *data); 17 | uint32_t ReadUint24BE(char *data); 18 | uint32_t ReadUint24LE(char *data); 19 | uint16_t ReadUint16BE(char *data); 20 | uint16_t ReadUint16LE(char *data); 21 | 22 | class BufferReader { 23 | public: 24 | static constexpr uint32_t kInitialSize = 2048; 25 | explicit BufferReader(uint32_t initialSize = kInitialSize); 26 | virtual ~BufferReader(); 27 | 28 | size_t ReadableBytes() const { return writer_index_ - reader_index_; } 29 | 30 | size_t WritableBytes() const { return buffer_.size() - writer_index_; } 31 | 32 | char *Peek() { return Begin() + reader_index_; } 33 | 34 | const char *Peek() const { return Begin() + reader_index_; } 35 | 36 | const char *FindFirstCrlf() const 37 | { 38 | const char *crlf = 39 | std::search(Peek(), BeginWrite(), kCRLF, kCRLF + 2); 40 | return crlf == BeginWrite() ? nullptr : crlf; 41 | } 42 | 43 | const char *FindLastCrlf() const 44 | { 45 | const char *crlf = 46 | std::find_end(Peek(), BeginWrite(), kCRLF, kCRLF + 2); 47 | return crlf == BeginWrite() ? nullptr : crlf; 48 | } 49 | 50 | const char *FindLastCrlfCrlf() const 51 | { 52 | char crlfCrlf[] = "\r\n\r\n"; 53 | const char *crlf = std::find_end(Peek(), BeginWrite(), crlfCrlf, 54 | crlfCrlf + 4); 55 | return crlf == BeginWrite() ? nullptr : crlf; 56 | } 57 | 58 | void RetrieveAll() 59 | { 60 | writer_index_ = 0; 61 | reader_index_ = 0; 62 | } 63 | 64 | void Retrieve(const size_t len) 65 | { 66 | if (len <= ReadableBytes()) { 67 | reader_index_ += len; 68 | if (reader_index_ == writer_index_) { 69 | reader_index_ = 0; 70 | writer_index_ = 0; 71 | } 72 | } else { 73 | RetrieveAll(); 74 | } 75 | } 76 | 77 | void RetrieveUntil(const char *end) { Retrieve(end - Peek()); } 78 | 79 | int Read(SOCKET sockfd); 80 | size_t ReadAll(std::string &data); 81 | size_t ReadUntilCrlf(std::string &data); 82 | 83 | size_t Size() const { return buffer_.size(); } 84 | 85 | private: 86 | char *Begin() { return &*buffer_.begin(); } 87 | 88 | const char *Begin() const { return &*buffer_.begin(); } 89 | 90 | char *beginWrite() { return Begin() + writer_index_; } 91 | 92 | const char *BeginWrite() const { return Begin() + writer_index_; } 93 | 94 | std::vector buffer_; 95 | size_t reader_index_ = 0; 96 | size_t writer_index_ = 0; 97 | 98 | static const char kCRLF[]; 99 | static constexpr uint32_t MAX_BYTES_PER_READ = 4096; 100 | static constexpr uint32_t MAX_BUFFER_SIZE = 1024 * 100000; 101 | }; 102 | 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /rtsp-server/net/BufferWriter.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #include "BufferWriter.h" 5 | #include "Socket.h" 6 | #include "SocketUtil.h" 7 | 8 | using namespace xop; 9 | 10 | void xop::WriteUint32BE(char *p, uint32_t value) 11 | { 12 | p[0] = value >> 24; 13 | p[1] = value >> 16; 14 | p[2] = value >> 8; 15 | p[3] = value & 0xff; 16 | } 17 | 18 | void xop::WriteUint32LE(char *p, uint32_t value) 19 | { 20 | p[0] = value & 0xff; 21 | p[1] = value >> 8; 22 | p[2] = value >> 16; 23 | p[3] = value >> 24; 24 | } 25 | 26 | void xop::WriteUint24BE(char *p, uint32_t value) 27 | { 28 | p[0] = value >> 16; 29 | p[1] = value >> 8; 30 | p[2] = value & 0xff; 31 | } 32 | 33 | void xop::WriteUint24LE(char *p, uint32_t value) 34 | { 35 | p[0] = value & 0xff; 36 | p[1] = value >> 8; 37 | p[2] = value >> 16; 38 | } 39 | 40 | void xop::WriteUint16BE(char *p, uint16_t value) 41 | { 42 | p[0] = value >> 8; 43 | p[1] = value & 0xff; 44 | } 45 | 46 | void xop::WriteUint16LE(char *p, uint16_t value) 47 | { 48 | p[0] = value & 0xff; 49 | p[1] = value >> 8; 50 | } 51 | 52 | BufferWriter::BufferWriter(const int capacity) : max_queue_length_(capacity) {} 53 | 54 | bool BufferWriter::Append(const std::shared_ptr &data, const size_t size, 55 | const uint32_t index) 56 | { 57 | if (size <= index) { 58 | return false; 59 | } 60 | 61 | if (static_cast(buffer_.size()) >= max_queue_length_) { 62 | return false; 63 | } 64 | 65 | Packet pkt = {data, size, index}; 66 | buffer_.emplace(std::move(pkt)); 67 | return true; 68 | } 69 | 70 | bool BufferWriter::Append(const char *data, const size_t size, 71 | const uint32_t index) 72 | { 73 | if (size <= index) { 74 | return false; 75 | } 76 | 77 | if (static_cast(buffer_.size()) >= max_queue_length_) { 78 | return false; 79 | } 80 | 81 | Packet pkt; 82 | pkt.data.reset(new char[size + 512], std::default_delete()); 83 | memcpy(pkt.data.get(), data, size); 84 | pkt.size = size; 85 | pkt.writeIndex = index; 86 | buffer_.emplace(std::move(pkt)); 87 | return true; 88 | } 89 | 90 | int BufferWriter::Send(const SOCKET sockfd, const int timeout) 91 | { 92 | if (timeout > 0) { 93 | SocketUtil::SetBlock(sockfd, timeout); 94 | } 95 | 96 | int ret; 97 | int count = 1; 98 | 99 | do { 100 | if (buffer_.empty()) { 101 | return 0; 102 | } 103 | 104 | count -= 1; 105 | Packet &pkt = buffer_.front(); 106 | ret = send(sockfd, pkt.data.get() + pkt.writeIndex, 107 | static_cast(pkt.size) - pkt.writeIndex, 0); 108 | if (ret > 0) { 109 | pkt.writeIndex += ret; 110 | if (pkt.size == pkt.writeIndex) { 111 | count += 1; 112 | buffer_.pop(); 113 | } 114 | } else if (ret < 0) { 115 | #if defined(WIN32) || defined(_WIN32) 116 | if (const int error = WSAGetLastError(); 117 | error == WSAEWOULDBLOCK || 118 | error == WSAEINPROGRESS || error == 0) 119 | #else 120 | if (errno == EINTR || errno == EAGAIN) 121 | #endif 122 | { 123 | ret = 0; 124 | } 125 | } 126 | } while (count > 0); 127 | 128 | if (timeout > 0) { 129 | SocketUtil::SetNonBlock(sockfd); 130 | } 131 | 132 | return ret; 133 | } 134 | -------------------------------------------------------------------------------- /rtsp-server/net/BufferWriter.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_BUFFER_WRITER_H 5 | #define XOP_BUFFER_WRITER_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include "Socket.h" 11 | 12 | namespace xop { 13 | 14 | void WriteUint32BE(char *p, uint32_t value); 15 | void WriteUint32LE(char *p, uint32_t value); 16 | void WriteUint24BE(char *p, uint32_t value); 17 | void WriteUint24LE(char *p, uint32_t value); 18 | void WriteUint16BE(char *p, uint16_t value); 19 | void WriteUint16LE(char *p, uint16_t value); 20 | 21 | class BufferWriter { 22 | public: 23 | explicit BufferWriter(int capacity = kMaxQueueLength); 24 | virtual ~BufferWriter() = default; 25 | 26 | bool Append(const std::shared_ptr &data, size_t size, 27 | uint32_t index = 0); 28 | bool Append(const char *data, size_t size, uint32_t index = 0); 29 | int Send(SOCKET sockfd, int timeout = 0); 30 | 31 | bool IsEmpty() const { return buffer_.empty(); } 32 | 33 | bool IsFull() const 34 | { 35 | return static_cast(buffer_.size()) >= max_queue_length_ 36 | ? true 37 | : false; 38 | } 39 | 40 | size_t Size() const { return buffer_.size(); } 41 | 42 | private: 43 | typedef struct Packet { 44 | std::shared_ptr data; 45 | size_t size{}; 46 | uint32_t writeIndex{}; 47 | } Packet; 48 | 49 | std::queue buffer_; 50 | int max_queue_length_ = 0; 51 | 52 | static constexpr int kMaxQueueLength = 10000; 53 | }; 54 | 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /rtsp-server/net/Channel.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_CHANNEL_H 5 | #define XOP_CHANNEL_H 6 | 7 | #include 8 | #include 9 | #include "Socket.h" 10 | 11 | namespace xop { 12 | 13 | enum EventType: uint32_t { 14 | EVENT_NONE = 0, 15 | EVENT_IN = 1, 16 | EVENT_PRI = 2, 17 | EVENT_OUT = 4, 18 | EVENT_ERR = 8, 19 | EVENT_HUP = 16, 20 | EVENT_RDHUP = 8192 21 | }; 22 | 23 | class Channel { 24 | public: 25 | typedef std::function EventCallback; 26 | 27 | Channel() = delete; 28 | 29 | explicit Channel(const SOCKET sockfd) : sockfd_(sockfd) {} 30 | 31 | virtual ~Channel() = default; 32 | 33 | void SetReadCallback(const EventCallback &cb) { read_callback_ = cb; } 34 | 35 | void SetWriteCallback(const EventCallback &cb) { write_callback_ = cb; } 36 | 37 | void SetCloseCallback(const EventCallback &cb) { close_callback_ = cb; } 38 | 39 | void SetErrorCallback(const EventCallback &cb) { error_callback_ = cb; } 40 | 41 | SOCKET GetSocket() const { return sockfd_; } 42 | 43 | uint32_t GetEvents() const { return events_; } 44 | void SetEvents(const int events) { events_ = events; } 45 | 46 | void EnableReading() { events_ |= EVENT_IN; } 47 | 48 | void EnableWriting() { events_ |= EVENT_OUT; } 49 | 50 | void DisableReading() { events_ &= ~EVENT_IN; } 51 | 52 | void DisableWriting() { events_ &= ~EVENT_OUT; } 53 | 54 | bool IsNoneEvent() const { return events_ == EVENT_NONE; } 55 | bool IsWriting() const { return (events_ & EVENT_OUT) != 0; } 56 | bool IsReading() const { return (events_ & EVENT_IN) != 0; } 57 | 58 | void HandleEvent(const uint32_t events) const 59 | { 60 | if (events & (EVENT_PRI | EVENT_IN)) { 61 | read_callback_(); 62 | } 63 | 64 | if (events & EVENT_OUT) { 65 | write_callback_(); 66 | } 67 | 68 | if (events & EVENT_HUP) { 69 | close_callback_(); 70 | return; 71 | } 72 | 73 | if (events & EVENT_ERR) { 74 | error_callback_(); 75 | } 76 | } 77 | 78 | private: 79 | EventCallback read_callback_ = [] {}; 80 | EventCallback write_callback_ = [] {}; 81 | EventCallback close_callback_ = [] {}; 82 | EventCallback error_callback_ = [] {}; 83 | 84 | SOCKET sockfd_ = 0; 85 | uint32_t events_ = 0; 86 | }; 87 | 88 | typedef std::shared_ptr ChannelPtr; 89 | 90 | } 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /rtsp-server/net/EpollTaskScheduler.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #include "EpollTaskScheduler.h" 5 | 6 | #if defined(__linux) || defined(__linux__) 7 | #include 8 | #include 9 | #endif 10 | #include "Logger.h" 11 | 12 | using namespace xop; 13 | 14 | EpollTaskScheduler::EpollTaskScheduler(const int id) : TaskScheduler(id) 15 | { 16 | #if defined(__linux) || defined(__linux__) 17 | epollfd_ = epoll_create1(0); 18 | if (epollfd_ < 0) { 19 | LOG_ERROR("epoll_create1 errno: %d", errno); 20 | } 21 | #endif 22 | this->EpollTaskScheduler::UpdateChannel(wakeup_channel_); 23 | } 24 | 25 | EpollTaskScheduler::~EpollTaskScheduler() 26 | { 27 | #if defined(__linux) || defined(__linux__) 28 | if (epollfd_ >= 0) { 29 | close(epollfd_); 30 | epollfd_ = -1; 31 | } 32 | #endif 33 | } 34 | 35 | void EpollTaskScheduler::UpdateChannel(const ChannelPtr &channel) 36 | { 37 | std::lock_guard lock(mutex_); 38 | #if defined(__linux) || defined(__linux__) 39 | int fd = channel->GetSocket(); 40 | if (channels_.find(fd) != channels_.end()) { 41 | if (channel->IsNoneEvent()) { 42 | Update(EPOLL_CTL_DEL, channel); 43 | channels_.erase(fd); 44 | } else { 45 | Update(EPOLL_CTL_MOD, channel); 46 | } 47 | } else { 48 | if (!channel->IsNoneEvent()) { 49 | channels_.emplace(fd, channel); 50 | Update(EPOLL_CTL_ADD, channel); 51 | } 52 | } 53 | #endif 54 | } 55 | 56 | void EpollTaskScheduler::Update(int operation, const ChannelPtr &channel) 57 | { 58 | #if defined(__linux) || defined(__linux__) 59 | struct epoll_event event = {0}; 60 | 61 | if (operation != EPOLL_CTL_DEL) { 62 | event.data.ptr = channel.get(); 63 | event.events = channel->GetEvents(); 64 | } 65 | 66 | if (::epoll_ctl(epollfd_, operation, channel->GetSocket(), &event) < 67 | 0) { 68 | LOG_ERROR("epoll_ctl errno: %d", errno); 69 | } 70 | #endif 71 | } 72 | 73 | void EpollTaskScheduler::RemoveChannel(const ChannelPtr &channel) 74 | { 75 | std::lock_guard lock(mutex_); 76 | #if defined(__linux) || defined(__linux__) 77 | int fd = channel->GetSocket(); 78 | 79 | if (channels_.find(fd) != channels_.end()) { 80 | Update(EPOLL_CTL_DEL, channel); 81 | channels_.erase(fd); 82 | } 83 | #endif 84 | } 85 | 86 | bool EpollTaskScheduler::HandleEvent(int timeout) 87 | { 88 | #if defined(__linux) || defined(__linux__) 89 | struct epoll_event events[512] = {0}; 90 | int num_events = -1; 91 | 92 | num_events = epoll_wait(epollfd_, events, 512, timeout); 93 | if (num_events < 0) { 94 | if (errno != EINTR) { 95 | LOG_ERROR("epoll_wait errno: %d", errno); 96 | return false; 97 | } 98 | } 99 | 100 | for (int n = 0; n < num_events; n++) { 101 | if (events[n].data.ptr) { 102 | static_cast(events[n].data.ptr) 103 | ->HandleEvent(events[n].events); 104 | } 105 | } 106 | return true; 107 | #else 108 | return false; 109 | #endif 110 | } 111 | -------------------------------------------------------------------------------- /rtsp-server/net/EpollTaskScheduler.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_EPOLL_TASK_SCHEDULER_H 5 | #define XOP_EPOLL_TASK_SCHEDULER_H 6 | 7 | #include "TaskScheduler.h" 8 | #include 9 | #include 10 | 11 | namespace xop { 12 | class EpollTaskScheduler : public TaskScheduler { 13 | public: 14 | explicit EpollTaskScheduler(int id = 0); 15 | ~EpollTaskScheduler() override; 16 | 17 | void UpdateChannel(const ChannelPtr &channel) override; 18 | void RemoveChannel(const ChannelPtr &channel) override; 19 | 20 | // timeout: ms 21 | bool HandleEvent(int timeout) override; 22 | 23 | private: 24 | void Update(int operation, const ChannelPtr &channel); 25 | 26 | int epollfd_ = -1; 27 | std::mutex mutex_; 28 | std::unordered_map channels_; 29 | }; 30 | 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /rtsp-server/net/EventLoop.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_EVENT_LOOP_H 5 | #define XOP_EVENT_LOOP_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "TaskScheduler.h" 12 | #include "Timer.h" 13 | 14 | #define TASK_SCHEDULER_PRIORITY_LOW 0 15 | #define TASK_SCHEDULER_PRIORITY_NORMAL 1 16 | #define TASK_SCHEDULER_PRIORITYO_HIGH 2 17 | #define TASK_SCHEDULER_PRIORITY_HIGHEST 3 18 | #define TASK_SCHEDULER_PRIORITY_REALTIME 4 19 | 20 | namespace xop { 21 | 22 | class EventLoop { 23 | public: 24 | EventLoop(const EventLoop &) = delete; 25 | EventLoop &operator=(const EventLoop &) = delete; 26 | explicit EventLoop( 27 | uint32_t num_threads = 1); //std::thread::hardware_concurrency() 28 | virtual ~EventLoop(); 29 | 30 | std::shared_ptr GetTaskScheduler(); 31 | 32 | bool AddTriggerEvent(const TriggerEvent &callback); 33 | TimerId AddTimer(TimerEvent timerEvent, uint32_t msec); 34 | void RemoveTimer(TimerId timerId); 35 | void UpdateChannel(const ChannelPtr &channel); 36 | void RemoveChannel(ChannelPtr &channel); 37 | 38 | void Loop(); 39 | void Quit(); 40 | 41 | private: 42 | std::mutex mutex_; 43 | uint32_t num_threads_ = 1; 44 | uint32_t index_ = 1; 45 | std::vector> task_schedulers_; 46 | std::vector> threads_; 47 | }; 48 | 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /rtsp-server/net/KqueueTaskScheduler.cpp: -------------------------------------------------------------------------------- 1 | // Scott Xu 2 | // 2021-1-19 3 | 4 | #include "KqueueTaskScheduler.h" 5 | 6 | #if defined(__APPLE__) || defined(__MACH__) 7 | #include 8 | #include 9 | #endif 10 | 11 | using namespace xop; 12 | 13 | KqueueTaskScheduler::KqueueTaskScheduler(int id) : TaskScheduler(id) 14 | { 15 | #if defined(__APPLE__) || defined(__MACH__) 16 | kqueuefd_ = kqueue(); 17 | #endif 18 | this->KqueueTaskScheduler::UpdateChannel(wakeup_channel_); 19 | } 20 | 21 | KqueueTaskScheduler::~KqueueTaskScheduler() {} 22 | 23 | void KqueueTaskScheduler::UpdateChannel(const ChannelPtr &channel) 24 | { 25 | std::lock_guard lock(mutex_); 26 | #if defined(__APPLE__) || defined(__MACH__) 27 | int fd = channel->GetSocket(); 28 | if (channels_.find(fd) != channels_.end()) { 29 | if (channel->IsNoneEvent()) { 30 | Update(EV_DELETE, channel); 31 | channels_.erase(fd); 32 | } else { 33 | Update(EV_ADD | EV_ENABLE, channel); 34 | } 35 | } else { 36 | if (!channel->IsNoneEvent()) { 37 | channels_.emplace(fd, channel); 38 | Update(EV_ADD | EV_ENABLE, channel); 39 | } 40 | } 41 | #endif 42 | } 43 | 44 | void KqueueTaskScheduler::Update(int operation, const ChannelPtr &channel) 45 | { 46 | #if defined(__APPLE__) || defined(__MACH__) 47 | struct kevent events[2] = {0}; 48 | int num_events = 0; 49 | 50 | if (channel->IsReading()) 51 | EV_SET(&events[num_events++], channel->GetSocket(), EVFILT_READ, 52 | operation, NULL, NULL, channel.get()); 53 | if (channel->IsWriting()) 54 | EV_SET(&events[num_events++], channel->GetSocket(), 55 | EVFILT_WRITE, operation, NULL, NULL, channel.get()); 56 | 57 | if (kevent(kqueuefd_, events, num_events, nullptr, 0, nullptr) < 0) { 58 | } 59 | #endif 60 | } 61 | 62 | void KqueueTaskScheduler::RemoveChannel(const ChannelPtr &channel) 63 | { 64 | std::lock_guard lock(mutex_); 65 | #if defined(__APPLE__) || defined(__MACH__) 66 | int fd = channel->GetSocket(); 67 | 68 | if (channels_.find(fd) != channels_.end()) { 69 | Update(EV_DELETE, channel); 70 | channels_.erase(fd); 71 | } 72 | #endif 73 | } 74 | 75 | bool KqueueTaskScheduler::HandleEvent(int timeout) 76 | { 77 | #if defined(__APPLE__) || defined(__MACH__) 78 | struct kevent events[512] = {0}; 79 | int num_events = -1; 80 | 81 | if (timeout > 0) { 82 | struct timespec _timeout = {0}; 83 | _timeout.tv_sec = timeout / 1000; 84 | _timeout.tv_nsec = (timeout % 1000) * 1000 * 1000; 85 | num_events = 86 | kevent(kqueuefd_, nullptr, 0, events, 512, &_timeout); 87 | } else 88 | num_events = 89 | kevent(kqueuefd_, nullptr, 0, events, 512, nullptr); 90 | if (num_events < 0) { 91 | if (errno != EINTR) { 92 | return false; 93 | } 94 | } 95 | 96 | for (int n = 0; n < num_events; n++) { 97 | auto filter = events[n].filter; 98 | auto flags = events[n].flags; 99 | auto channel = (Channel *)events[n].udata; 100 | 101 | if (!channel) 102 | continue; 103 | 104 | int handleEventEvents = EVENT_NONE; 105 | if (filter == EVFILT_READ) 106 | handleEventEvents = EVENT_IN; 107 | else if (filter == EVFILT_WRITE) 108 | handleEventEvents = EVENT_OUT; 109 | 110 | if (flags & EV_ERROR) 111 | handleEventEvents = handleEventEvents | EVENT_ERR; 112 | if (flags & EV_EOF) 113 | handleEventEvents = handleEventEvents | EVENT_HUP; 114 | 115 | channel->HandleEvent(handleEventEvents); 116 | } 117 | return true; 118 | #else 119 | return false; 120 | #endif 121 | } 122 | -------------------------------------------------------------------------------- /rtsp-server/net/KqueueTaskScheduler.h: -------------------------------------------------------------------------------- 1 | // Scott Xu 2 | // 2021-1-19 3 | 4 | #ifndef XOP_KQUEUE_TASK_SCHEDULER_H 5 | #define XOP_KQUEUE_TASK_SCHEDULER_H 6 | 7 | #include "TaskScheduler.h" 8 | #include 9 | #include 10 | 11 | namespace xop { 12 | class KqueueTaskScheduler : public TaskScheduler { 13 | public: 14 | explicit KqueueTaskScheduler(int id = 0); 15 | ~KqueueTaskScheduler() override; 16 | 17 | void UpdateChannel(const ChannelPtr &channel) override; 18 | void RemoveChannel(const ChannelPtr &channel) override; 19 | 20 | // timeout: ms 21 | bool HandleEvent(int timeout) override; 22 | 23 | private: 24 | void Update(int operation, const ChannelPtr &channel); 25 | 26 | int kqueuefd_ = -1; 27 | std::mutex mutex_; 28 | std::unordered_map channels_; 29 | }; 30 | 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /rtsp-server/net/Logger.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #if defined(WIN32) || defined(_WIN32) 5 | #ifndef _CRT_SECURE_NO_WARNINGS 6 | #define _CRT_SECURE_NO_WARNINGS 7 | #endif 8 | #endif 9 | 10 | #include "Logger.h" 11 | #include "Timestamp.h" 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace xop; 17 | 18 | const char *Priority_To_String[] = {"DEBUG", "CONFIG", "INFO", "WARNING", 19 | "ERROR"}; 20 | 21 | Logger::Logger() = default; 22 | 23 | Logger &Logger::Instance() 24 | { 25 | static Logger s_logger; 26 | return s_logger; 27 | } 28 | 29 | Logger::~Logger() = default; 30 | 31 | void Logger::Init(char *pathname) 32 | { 33 | std::unique_lock lock(mutex_); 34 | 35 | if (pathname != nullptr) { 36 | ofs_.open(pathname, std::ios::out | std::ios::binary); 37 | if (ofs_.fail()) { 38 | std::cerr << "Failed to open logfile." << std::endl; 39 | } 40 | } 41 | } 42 | 43 | void Logger::Exit() 44 | { 45 | std::unique_lock lock(mutex_); 46 | 47 | if (ofs_.is_open()) { 48 | ofs_.close(); 49 | } 50 | } 51 | 52 | void Logger::SetWriteCallback(const LogWriteCallbackFun writeCallback) 53 | { 54 | _writeCallback = writeCallback; 55 | } 56 | 57 | void Logger::Log(const Priority priority, const char *__file, 58 | const char *__func, const int __line, const char *fmt, ...) 59 | { 60 | std::unique_lock lock(mutex_); 61 | 62 | char buf[2048]; 63 | auto buf_ptr = buf; 64 | auto buf_end = buf + sizeof(buf); 65 | buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "[%s][%s:%s:%d] ", Priority_To_String[priority], __file, 66 | __func, __line); 67 | va_list args; 68 | va_start(args, fmt); 69 | vsnprintf(buf_ptr, buf_end - buf_ptr, fmt, args); 70 | va_end(args); 71 | 72 | this->Write(std::string(buf)); 73 | _writeCallback(priority, std::string(buf)); 74 | } 75 | 76 | void Logger::Log2(const Priority priority, const char *fmt, ...) 77 | { 78 | std::unique_lock lock(mutex_); 79 | 80 | char buf[4096]; 81 | auto buf_ptr = buf; 82 | auto buf_end = buf + sizeof(buf); 83 | buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "[%s] ", Priority_To_String[priority]); 84 | va_list args; 85 | va_start(args, fmt); 86 | vsnprintf(buf_ptr, buf_end - buf_ptr, fmt, args); 87 | va_end(args); 88 | 89 | this->Write(std::string(buf)); 90 | _writeCallback(priority, std::string(buf)); 91 | } 92 | 93 | void Logger::Write(const std::string &info) 94 | { 95 | if (ofs_.is_open()) { 96 | ofs_ << "[" << Timestamp::Localtime() << "]" << info 97 | << std::endl; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /rtsp-server/net/Logger.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2020-5-15 3 | // Scott Xu 4 | // 2020-12-2 5 | // Add LogWriteCallbackFun. 6 | 7 | #ifndef XOP_LOGGER_H 8 | #define XOP_LOGGER_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace xop { 16 | 17 | enum Priority { 18 | LOG_DEBUG, 19 | LOG_STATE, 20 | LOG_INFO, 21 | LOG_WARNING, 22 | LOG_ERROR, 23 | }; 24 | 25 | typedef void (*LogWriteCallbackFun)(Priority priority, std::string info); 26 | 27 | class Logger { 28 | public: 29 | Logger &operator=(const Logger &) = delete; 30 | Logger(const Logger &) = delete; 31 | static Logger &Instance(); 32 | virtual ~Logger(); 33 | 34 | void Init(char *pathname = nullptr); 35 | void Exit(); 36 | void SetWriteCallback(LogWriteCallbackFun writeCallback); 37 | 38 | void Log(Priority priority, const char *__file, const char *__func, 39 | int __line, const char *fmt, ...); 40 | void Log2(Priority priority, const char *fmt, ...); 41 | 42 | private: 43 | void Write(const std::string &buf); 44 | Logger(); 45 | 46 | std::mutex mutex_; 47 | std::ofstream ofs_; 48 | LogWriteCallbackFun _writeCallback{}; 49 | }; 50 | 51 | } 52 | 53 | //#ifdef _DEBUG 54 | #define LOG_DEBUG(fmt, ...) \ 55 | xop::Logger::Instance().Log(LOG_DEBUG, __FILE__, __FUNCTION__, \ 56 | __LINE__, fmt, ##__VA_ARGS__) 57 | //#else 58 | //#define LOG_DEBUG(fmt, ...) 59 | //#endif 60 | #define LOG_INFO(fmt, ...) \ 61 | xop::Logger::Instance().Log2(LOG_INFO, fmt, ##__VA_ARGS__) 62 | #define LOG_ERROR(fmt, ...) \ 63 | xop::Logger::Instance().Log(LOG_ERROR, __FILE__, __FUNCTION__, \ 64 | __LINE__, fmt, ##__VA_ARGS__) 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /rtsp-server/net/MemoryManager.cpp: -------------------------------------------------------------------------------- 1 | #include "MemoryManager.h" 2 | 3 | using namespace xop; 4 | 5 | void *xop::Alloc(const uint32_t size) 6 | { 7 | return MemoryManager::Instance().Alloc(size); 8 | } 9 | 10 | void xop::Free(void *ptr) 11 | { 12 | return MemoryManager::Instance().Free(ptr); 13 | } 14 | 15 | MemoryPool::MemoryPool() = default; 16 | 17 | MemoryPool::~MemoryPool() 18 | { 19 | if (memory_) { 20 | free(memory_); 21 | } 22 | } 23 | 24 | void MemoryPool::Init(const uint32_t size, const uint32_t n) 25 | { 26 | if (memory_) { 27 | return; 28 | } 29 | 30 | block_size_ = size; 31 | num_blocks_ = n; 32 | memory_ = static_cast( 33 | malloc(num_blocks_ * (block_size_ + sizeof(MemoryBlock)))); 34 | head_ = reinterpret_cast(memory_); 35 | head_->block_id = 1; 36 | head_->pool = this; 37 | head_->next = nullptr; 38 | 39 | MemoryBlock *current = head_; 40 | for (uint32_t n = 1; n < num_blocks_; n++) { 41 | auto *next = reinterpret_cast( 42 | memory_ + n * (block_size_ + sizeof(MemoryBlock))); 43 | next->block_id = n + 1; 44 | next->pool = this; 45 | next->next = nullptr; 46 | 47 | current->next = next; 48 | current = next; 49 | } 50 | } 51 | 52 | void *MemoryPool::Alloc(const uint32_t size) 53 | { 54 | std::lock_guard locker(mutex_); 55 | if (head_ != nullptr) { 56 | MemoryBlock *block = head_; 57 | head_ = head_->next; 58 | return reinterpret_cast(block) + sizeof(MemoryBlock); 59 | } 60 | 61 | return nullptr; 62 | } 63 | 64 | void MemoryPool::Free(void *ptr) 65 | { 66 | if (const auto block = reinterpret_cast( 67 | static_cast(ptr) - sizeof(MemoryBlock)); 68 | block->block_id != 0) { 69 | std::lock_guard locker(mutex_); 70 | block->next = head_; 71 | head_ = block; 72 | } 73 | } 74 | 75 | MemoryManager::MemoryManager() 76 | { 77 | memory_pools_[0].Init(4096, 50); 78 | memory_pools_[1].Init(40960, 10); 79 | memory_pools_[2].Init(102400, 5); 80 | //memory_pools_[3].Init(204800, 2); 81 | } 82 | 83 | MemoryManager::~MemoryManager() = default; 84 | 85 | MemoryManager &MemoryManager::Instance() 86 | { 87 | static MemoryManager s_mgr; 88 | return s_mgr; 89 | } 90 | 91 | void *MemoryManager::Alloc(const uint32_t size) 92 | { 93 | for (auto &memory_pool : memory_pools_) { 94 | if (size <= memory_pool.BolckSize()) { 95 | if (void *ptr = memory_pool.Alloc(size); 96 | ptr != nullptr) { 97 | return ptr; 98 | } 99 | break; 100 | } 101 | } 102 | 103 | const auto block = 104 | static_cast(malloc(size + sizeof(MemoryBlock))); 105 | block->block_id = 0; 106 | block->pool = nullptr; 107 | block->next = nullptr; 108 | return reinterpret_cast(block) + sizeof(MemoryBlock); 109 | } 110 | 111 | void MemoryManager::Free(void *ptr) 112 | { 113 | const auto block = reinterpret_cast( 114 | static_cast(ptr) - sizeof(MemoryBlock)); 115 | 116 | if (MemoryPool *pool = block->pool; 117 | pool != nullptr && block->block_id > 0) { 118 | pool->Free(ptr); 119 | } else { 120 | free(block); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /rtsp-server/net/MemoryManager.h: -------------------------------------------------------------------------------- 1 | #ifndef XOP_MEMMORY_MANAGER_H 2 | #define XOP_MEMMORY_MANAGER_H 3 | 4 | #include 5 | #include 6 | 7 | namespace xop { 8 | 9 | void *Alloc(uint32_t size); 10 | void Free(void *ptr); 11 | 12 | class MemoryPool; 13 | 14 | struct MemoryBlock { 15 | uint32_t block_id = 0; 16 | MemoryPool *pool = nullptr; 17 | MemoryBlock *next = nullptr; 18 | }; 19 | 20 | class MemoryPool { 21 | public: 22 | MemoryPool(); 23 | virtual ~MemoryPool(); 24 | 25 | void Init(uint32_t size, uint32_t n); 26 | void *Alloc(uint32_t size); 27 | void Free(void *ptr); 28 | 29 | size_t BolckSize() const { return block_size_; } 30 | 31 | //private: 32 | char *memory_ = nullptr; 33 | uint32_t block_size_ = 0; 34 | uint32_t num_blocks_ = 0; 35 | MemoryBlock *head_ = nullptr; 36 | std::mutex mutex_; 37 | }; 38 | 39 | class MemoryManager { 40 | public: 41 | static MemoryManager &Instance(); 42 | ~MemoryManager(); 43 | 44 | void *Alloc(uint32_t size); 45 | void Free(void *ptr); 46 | 47 | private: 48 | MemoryManager(); 49 | 50 | static constexpr int kMaxMemoryPool = 3; 51 | MemoryPool memory_pools_[kMaxMemoryPool]; 52 | }; 53 | 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /rtsp-server/net/Pipe.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #include "Pipe.h" 5 | #include "SocketUtil.h" 6 | #include 7 | 8 | using namespace xop; 9 | 10 | Pipe::Pipe() 11 | { 12 | memset(pipe_fd_, 0, 2); 13 | } 14 | 15 | Pipe::~Pipe() 16 | { 17 | Close(); 18 | } 19 | 20 | bool Pipe::Create() 21 | { 22 | #if defined(__linux) || defined(__linux__) 23 | if (pipe2(pipe_fd_, O_NONBLOCK | O_CLOEXEC) < 0) { 24 | return false; 25 | } 26 | #else 27 | const TcpSocket rp(socket(AF_INET, SOCK_STREAM, 0)); 28 | const TcpSocket wp(socket(AF_INET, SOCK_STREAM, 0)); 29 | std::random_device rd; 30 | 31 | pipe_fd_[0] = rp.GetSocket(); 32 | pipe_fd_[1] = wp.GetSocket(); 33 | uint16_t port = 0; 34 | int again = 5; 35 | 36 | while (again--) { 37 | port = static_cast(rd()); 38 | if (rp.Bind("127.0.0.1", port)) { 39 | break; 40 | } 41 | } 42 | 43 | if (again == 0) { 44 | return false; 45 | } 46 | 47 | if (!rp.Listen(1)) { 48 | return false; 49 | } 50 | 51 | if (!wp.Connect("127.0.0.1", port)) { 52 | return false; 53 | } 54 | 55 | pipe_fd_[0] = rp.Accept(); //TODO 56 | if (pipe_fd_[0] < 0) { 57 | return false; 58 | } 59 | 60 | SocketUtil::SetNonBlock(pipe_fd_[0]); 61 | SocketUtil::SetNonBlock(pipe_fd_[1]); 62 | #endif 63 | return true; 64 | } 65 | 66 | int Pipe::Write(void *buf, const int len) const 67 | { 68 | #if defined(WIN32) || defined(_WIN32) 69 | return ::send(pipe_fd_[1], static_cast(buf), len, 0); 70 | #else 71 | return ::write(pipe_fd_[1], buf, len); 72 | #endif 73 | } 74 | 75 | int Pipe::Read(void *buf, const int len) const 76 | { 77 | #if defined(WIN32) || defined(_WIN32) 78 | return recv(pipe_fd_[0], static_cast(buf), len, 0); 79 | #else 80 | return ::read(pipe_fd_[0], buf, len); 81 | #endif 82 | } 83 | 84 | void Pipe::Close() const 85 | { 86 | #if defined(WIN32) || defined(_WIN32) 87 | closesocket(pipe_fd_[0]); 88 | closesocket(pipe_fd_[1]); 89 | #else 90 | ::close(pipe_fd_[0]); 91 | ::close(pipe_fd_[1]); 92 | #endif 93 | } 94 | -------------------------------------------------------------------------------- /rtsp-server/net/Pipe.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_PIPE_H 5 | #define XOP_PIPE_H 6 | 7 | #include "TcpSocket.h" 8 | 9 | namespace xop { 10 | 11 | class Pipe { 12 | public: 13 | Pipe(); 14 | virtual ~Pipe(); 15 | bool Create(); 16 | int Write(void *buf, int len) const; 17 | int Read(void *buf, int len) const; 18 | void Close() const; 19 | 20 | SOCKET Read() const { return pipe_fd_[0]; } 21 | SOCKET Write() const { return pipe_fd_[1]; } 22 | 23 | private: 24 | SOCKET pipe_fd_[2]{}; 25 | }; 26 | 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /rtsp-server/net/RingBuffer.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_RING_BUFFER_H 5 | #define XOP_RING_BUFFER_H 6 | 7 | #include 8 | #include 9 | 10 | namespace xop { 11 | 12 | template class RingBuffer { 13 | public: 14 | explicit RingBuffer(unsigned capacity = 60) 15 | : capacity_(capacity), num_datas_(0), buffer_(capacity) 16 | { 17 | } 18 | 19 | virtual ~RingBuffer() {} 20 | 21 | bool Push(const T &data) { return PushData(std::forward(data)); } 22 | 23 | bool Push(T &&data) { return PushData(data); } 24 | 25 | bool Pop(T &data) 26 | { 27 | if (num_datas_ > 0) { 28 | data = std::move(buffer_[get_pos_]); 29 | Add(get_pos_); 30 | --num_datas_; 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | bool IsFull() const { return num_datas_ == capacity_ ? true : false; } 38 | 39 | bool IsEmpty() const { return num_datas_ == 0 ? true : false; } 40 | 41 | int Size() const { return num_datas_; } 42 | 43 | private: 44 | template bool PushData(F &&data) 45 | { 46 | if (num_datas_ < capacity_) { 47 | buffer_[put_pos_] = std::forward(data); 48 | Add(put_pos_); 49 | ++num_datas_; 50 | return true; 51 | } 52 | 53 | return false; 54 | } 55 | 56 | void Add(int &pos) const { pos = pos + 1 == capacity_ ? 0 : pos + 1; } 57 | 58 | int capacity_ = 0; 59 | int put_pos_ = 0; 60 | int get_pos_ = 0; 61 | 62 | std::atomic_int num_datas_; 63 | std::vector buffer_; 64 | }; 65 | 66 | } 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /rtsp-server/net/SelectTaskScheduler.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_SELECT_TASK_SCHEDULER_H 5 | #define XOP_SELECT_TASK_SCHEDULER_H 6 | 7 | #include "TaskScheduler.h" 8 | #include "Socket.h" 9 | #include 10 | #include 11 | 12 | #if defined(WIN32) || defined(_WIN32) 13 | #else 14 | #include 15 | #include 16 | #include 17 | #include 18 | #endif 19 | 20 | namespace xop { 21 | 22 | class SelectTaskScheduler : public TaskScheduler { 23 | public: 24 | explicit SelectTaskScheduler(int id = 0); 25 | ~SelectTaskScheduler() override; 26 | 27 | void UpdateChannel(const ChannelPtr &channel) override; 28 | void RemoveChannel(const ChannelPtr &channel) override; 29 | bool HandleEvent(int timeout) override; 30 | 31 | private: 32 | fd_set fd_read_backup_{}; 33 | fd_set fd_write_backup_{}; 34 | fd_set fd_exp_backup_{}; 35 | SOCKET maxfd_ = 0; 36 | 37 | bool is_fd_read_reset_ = false; 38 | bool is_fd_write_reset_ = false; 39 | bool is_fd_exp_reset_ = false; 40 | 41 | std::mutex mutex_; 42 | std::unordered_map channels_; 43 | }; 44 | 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /rtsp-server/net/Socket.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | // Scott Xu 4 | // 2020-12-2 Add IPv6 Support. 5 | 6 | #ifndef XOP_SOCKET_H 7 | #define XOP_SOCKET_H 8 | 9 | #if defined(WIN32) || defined(_WIN32) 10 | #define FD_SETSIZE 1024 11 | #define WIN32_LEAN_AND_MEAN 12 | #define _WINSOCK_DEPRECATED_NO_WARNINGS 13 | #include 14 | #include 15 | #include 16 | #include 17 | #define SHUT_RD 0 18 | #define SHUT_WR 1 19 | #define SHUT_RDWR 2 20 | #else 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #if defined(__linux) || defined(__linux__) 36 | #include 37 | #include 38 | #endif 39 | #define SOCKET int 40 | #define INVALID_SOCKET (-1) 41 | #define SOCKET_ERROR (-1) 42 | 43 | #define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */ 44 | #define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */ 45 | #endif 46 | 47 | #include 48 | #include 49 | 50 | #endif // _XOP_SOCKET_H 51 | -------------------------------------------------------------------------------- /rtsp-server/net/SocketUtil.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | // Scott Xu 4 | // 2020-12-2 Add IPv6 Support. 5 | 6 | #ifndef XOP_SOCKET_UTIL_H 7 | #define XOP_SOCKET_UTIL_H 8 | 9 | #include "Socket.h" 10 | #include 11 | 12 | namespace xop { 13 | 14 | class SocketUtil { 15 | public: 16 | static bool Bind(SOCKET sockfd, const std::string &ip, uint16_t port, 17 | bool ipv6 = false); 18 | static void SetNonBlock(SOCKET fd); 19 | static void SetBlock(SOCKET fd, int write_timeout = 0); 20 | static void SetReuseAddr(SOCKET fd); 21 | static void SetReusePort(SOCKET sockfd); 22 | static void SetNoDelay(SOCKET sockfd); 23 | static void SetKeepAlive(SOCKET sockfd); 24 | static void SetNoSigpipe(SOCKET sockfd); 25 | static void SetSendBufSize(SOCKET sockfd, int size); 26 | static void SetRecvBufSize(SOCKET sockfd, int size); 27 | static std::string GetPeerIp(SOCKET sockfd, bool ipv6 = false); 28 | static std::string GetSocketIp(SOCKET sockfd, bool ipv6 = false); 29 | static uint16_t GetPeerPort(SOCKET sockfd, bool ipv6 = false); 30 | static int GetPeerAddr(SOCKET sockfd, sockaddr_in *addr); 31 | static int GetPeerAddr6(SOCKET sockfd, sockaddr_in6 *addr); 32 | static int GetSocketAddr(SOCKET sockfd, sockaddr_in *addr); 33 | static int GetSocketAddr6(SOCKET sockfd, sockaddr_in6 *addr); 34 | static void Close(SOCKET sockfd); 35 | static bool Connect(SOCKET sockfd, const std::string &ip, uint16_t port, 36 | int timeout = 0, bool ipv6 = false); 37 | static bool IsIpv6Address(const std::string &ip); 38 | static bool IsIpv6Socket(SOCKET sockfd); 39 | }; 40 | 41 | } 42 | 43 | #endif // _SOCKET_UTIL_H 44 | -------------------------------------------------------------------------------- /rtsp-server/net/TaskScheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "TaskScheduler.h" 2 | #if defined(WIN32) || defined(_WIN32) 3 | 4 | #else 5 | #include 6 | #endif 7 | 8 | using namespace xop; 9 | 10 | TaskScheduler::TaskScheduler(const int id) 11 | : id_(id), 12 | is_shutdown_(false), 13 | wakeup_pipe_(new Pipe()), 14 | trigger_events_(new xop::RingBuffer(kMaxTriggetEvents)) 15 | { 16 | static std::once_flag flag; 17 | std::call_once(flag, [] { 18 | #if defined(WIN32) || defined(_WIN32) 19 | WSADATA wsa_data; 20 | if (WSAStartup(MAKEWORD(2, 2), &wsa_data)) { 21 | WSACleanup(); 22 | } 23 | #endif 24 | }); 25 | 26 | if (wakeup_pipe_->Create()) { 27 | wakeup_channel_.reset(new Channel(wakeup_pipe_->Read())); 28 | wakeup_channel_->EnableReading(); 29 | wakeup_channel_->SetReadCallback([this]() { this->Wake(); }); 30 | } 31 | } 32 | 33 | TaskScheduler::~TaskScheduler() = default; 34 | 35 | void TaskScheduler::Start() 36 | { 37 | #if defined(WIN32) || defined(_WIN32) 38 | 39 | #else 40 | signal(SIGPIPE, SIG_IGN); 41 | signal(SIGQUIT, SIG_IGN); 42 | signal(SIGUSR1, SIG_IGN); 43 | signal(SIGTERM, SIG_IGN); 44 | signal(SIGKILL, SIG_IGN); 45 | #endif 46 | is_shutdown_ = false; 47 | while (!is_shutdown_) { 48 | this->HandleTriggerEvent(); 49 | this->timer_queue_.HandleTimerEvent(); 50 | const int64_t timeout = this->timer_queue_.GetTimeRemaining(); 51 | this->HandleEvent(static_cast(timeout)); 52 | } 53 | } 54 | 55 | void TaskScheduler::Stop() 56 | { 57 | is_shutdown_ = true; 58 | char event = kTriggetEvent; 59 | wakeup_pipe_->Write(&event, 1); 60 | } 61 | 62 | TimerId TaskScheduler::AddTimer(const TimerEvent &timerEvent, 63 | const uint32_t msec) 64 | { 65 | const TimerId id = timer_queue_.AddTimer(timerEvent, msec); 66 | return id; 67 | } 68 | 69 | void TaskScheduler::RemoveTimer(const TimerId timerId) 70 | { 71 | timer_queue_.RemoveTimer(timerId); 72 | } 73 | 74 | bool TaskScheduler::AddTriggerEvent(TriggerEvent callback) 75 | { 76 | if (trigger_events_->Size() < kMaxTriggetEvents) { 77 | std::lock_guard lock(mutex_); 78 | char event = kTriggetEvent; 79 | trigger_events_->Push(std::move(callback)); 80 | wakeup_pipe_->Write(&event, 1); 81 | return true; 82 | } 83 | 84 | return false; 85 | } 86 | 87 | void TaskScheduler::Wake() const 88 | { 89 | char event[10] = {0}; 90 | while (wakeup_pipe_->Read(event, 10) > 0) 91 | ; 92 | } 93 | 94 | void TaskScheduler::HandleTriggerEvent() const 95 | { 96 | do { 97 | if (TriggerEvent callback; trigger_events_->Pop(callback)) { 98 | callback(); 99 | } 100 | } while (trigger_events_->Size() > 0); 101 | } 102 | -------------------------------------------------------------------------------- /rtsp-server/net/TaskScheduler.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_TASK_SCHEDULER_H 5 | #define XOP_TASK_SCHEDULER_H 6 | 7 | #include "Channel.h" 8 | #include "Pipe.h" 9 | #include "Timer.h" 10 | #include "RingBuffer.h" 11 | 12 | namespace xop { 13 | 14 | typedef std::function TriggerEvent; 15 | 16 | class TaskScheduler { 17 | public: 18 | explicit TaskScheduler(int id = 1); 19 | virtual ~TaskScheduler(); 20 | 21 | void Start(); 22 | void Stop(); 23 | TimerId AddTimer(const TimerEvent &timerEvent, uint32_t msec); 24 | void RemoveTimer(TimerId timerId); 25 | bool AddTriggerEvent(TriggerEvent callback); 26 | 27 | virtual void UpdateChannel(const ChannelPtr &channel) = 0; 28 | virtual void RemoveChannel(const ChannelPtr &channel) = 0; 29 | virtual bool HandleEvent(int timeout) = 0; 30 | 31 | int GetId() const { return id_; } 32 | 33 | protected: 34 | void Wake() const; 35 | void HandleTriggerEvent() const; 36 | 37 | int id_ = 0; 38 | std::atomic_bool is_shutdown_; 39 | std::unique_ptr wakeup_pipe_; 40 | std::shared_ptr wakeup_channel_; 41 | std::unique_ptr> trigger_events_; 42 | 43 | std::mutex mutex_; 44 | TimerQueue timer_queue_; 45 | 46 | static constexpr char kTriggetEvent = 1; 47 | static constexpr char kTimerEvent = 2; 48 | static constexpr int kMaxTriggetEvents = 50000; 49 | }; 50 | 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /rtsp-server/net/TcpConnection.cpp: -------------------------------------------------------------------------------- 1 | //Scott Xu 2 | //2020-12-6 Add IPv6 support. 3 | #include "TcpConnection.h" 4 | #include "SocketUtil.h" 5 | 6 | using namespace xop; 7 | 8 | TcpConnection::TcpConnection( 9 | const SOCKET sockfd, std::shared_ptr task_scheduler) 10 | : read_buffer_(new BufferReader), 11 | write_buffer_(new BufferWriter(500)), 12 | is_closed_(false), 13 | task_scheduler_(std::move(task_scheduler)), 14 | channel_(new Channel(sockfd)), 15 | ipv6_(SocketUtil::IsIpv6Socket(sockfd)) 16 | { 17 | channel_->SetReadCallback([this] { this->HandleRead(); }); 18 | channel_->SetWriteCallback([this] { this->HandleWrite(); }); 19 | channel_->SetCloseCallback([this] { this->HandleClose(); }); 20 | channel_->SetErrorCallback([this] { this->HandleError(); }); 21 | 22 | SocketUtil::SetNonBlock(sockfd); 23 | SocketUtil::SetSendBufSize(sockfd, 100 * 1024); 24 | SocketUtil::SetKeepAlive(sockfd); 25 | 26 | channel_->EnableReading(); 27 | task_scheduler_->UpdateChannel(channel_); 28 | } 29 | 30 | TcpConnection::~TcpConnection() 31 | { 32 | if (const SOCKET fd = channel_->GetSocket(); fd > 0) { 33 | SocketUtil::Close(fd); 34 | } 35 | } 36 | 37 | void TcpConnection::Send(const std::shared_ptr &data, const size_t size) 38 | { 39 | if (!is_closed_) { 40 | mutex_.lock(); 41 | write_buffer_->Append(data, size); 42 | mutex_.unlock(); 43 | 44 | this->HandleWrite(); 45 | } 46 | } 47 | 48 | void TcpConnection::Send(const char *data, const size_t size) 49 | { 50 | if (!is_closed_) { 51 | mutex_.lock(); 52 | write_buffer_->Append(data, size); 53 | mutex_.unlock(); 54 | 55 | this->HandleWrite(); 56 | } 57 | } 58 | 59 | void TcpConnection::Disconnect() 60 | { 61 | std::lock_guard lock(mutex_); 62 | task_scheduler_->AddTriggerEvent([this] { this->Close(); }); 63 | } 64 | 65 | void TcpConnection::HandleRead() 66 | { 67 | { 68 | std::lock_guard lock(mutex_); 69 | 70 | if (is_closed_) { 71 | return; 72 | } 73 | 74 | if (const int ret = read_buffer_->Read(channel_->GetSocket()); 75 | ret <= 0) { 76 | this->Close(); 77 | return; 78 | } 79 | } 80 | 81 | if (read_cb_) { 82 | if (const bool ret = 83 | read_cb_(weak_from_this(), *read_buffer_); 84 | !ret) { 85 | std::lock_guard lock(mutex_); 86 | this->Close(); 87 | } 88 | } 89 | } 90 | 91 | void TcpConnection::HandleWrite() 92 | { 93 | if (is_closed_) { 94 | return; 95 | } 96 | 97 | //std::lock_guard lock(mutex_); 98 | if (!mutex_.try_lock()) { 99 | return; 100 | } 101 | 102 | bool empty; 103 | //do 104 | //{ 105 | if (const int ret = write_buffer_->Send(channel_->GetSocket()); 106 | ret < 0) { 107 | this->Close(); 108 | mutex_.unlock(); 109 | return; 110 | } 111 | empty = write_buffer_->IsEmpty(); 112 | //} while (false); 113 | 114 | if (empty) { 115 | if (channel_->IsWriting()) { 116 | channel_->DisableWriting(); 117 | task_scheduler_->UpdateChannel(channel_); 118 | } 119 | } else if (!channel_->IsWriting()) { 120 | channel_->EnableWriting(); 121 | task_scheduler_->UpdateChannel(channel_); 122 | } 123 | 124 | mutex_.unlock(); 125 | } 126 | 127 | void TcpConnection::Close() 128 | { 129 | if (!is_closed_) { 130 | is_closed_ = true; 131 | task_scheduler_->RemoveChannel(channel_); 132 | 133 | if (close_cb_) { 134 | close_cb_(weak_from_this()); 135 | } 136 | 137 | if (disconnect_cb_) { 138 | disconnect_cb_(weak_from_this()); 139 | } 140 | } 141 | } 142 | 143 | void TcpConnection::HandleClose() 144 | { 145 | std::lock_guard lock(mutex_); 146 | this->Close(); 147 | } 148 | 149 | void TcpConnection::HandleError() 150 | { 151 | std::lock_guard lock(mutex_); 152 | this->Close(); 153 | } 154 | -------------------------------------------------------------------------------- /rtsp-server/net/TcpConnection.h: -------------------------------------------------------------------------------- 1 | //Scott Xu 2 | //2020-12-6 Add IPv6 support. 3 | #ifndef XOP_TCP_CONNECTION_H 4 | #define XOP_TCP_CONNECTION_H 5 | 6 | #include 7 | #include 8 | #include "TaskScheduler.h" 9 | #include "BufferReader.h" 10 | #include "BufferWriter.h" 11 | #include "Channel.h" 12 | #include "SocketUtil.h" 13 | 14 | namespace xop { 15 | 16 | class TcpConnection : public std::enable_shared_from_this { 17 | public: 18 | using Ptr = std::shared_ptr; 19 | using Weak = std::weak_ptr; 20 | using DisconnectCallback = std::function; 21 | using CloseCallback = std::function; 22 | using ReadCallback = 23 | std::function; 24 | 25 | TcpConnection(SOCKET sockfd, 26 | std::shared_ptr task_scheduler); 27 | virtual ~TcpConnection(); 28 | 29 | std::shared_ptr GetTaskScheduler() const 30 | { 31 | return task_scheduler_; 32 | } 33 | 34 | void SetReadCallback(const ReadCallback &cb) { read_cb_ = cb; } 35 | 36 | void SetCloseCallback(const CloseCallback &cb) { close_cb_ = cb; } 37 | 38 | void Send(const std::shared_ptr &data, size_t size); 39 | void Send(const char *data, size_t size); 40 | 41 | void Disconnect(); 42 | 43 | bool IsClosed() const { return is_closed_; } 44 | 45 | bool IsIpv6() const { return ipv6_; } 46 | 47 | SOCKET GetSocket() const { return channel_->GetSocket(); } 48 | 49 | uint16_t GetPort() const 50 | { 51 | return SocketUtil::GetPeerPort(channel_->GetSocket(), ipv6_); 52 | } 53 | 54 | std::string GetIp() const 55 | { 56 | return SocketUtil::GetPeerIp(channel_->GetSocket(), ipv6_); 57 | } 58 | 59 | protected: 60 | friend class TcpServer; 61 | 62 | virtual void HandleRead(); 63 | virtual void HandleWrite(); 64 | virtual void HandleClose(); 65 | virtual void HandleError(); 66 | 67 | void SetDisconnectCallback(const DisconnectCallback &cb) 68 | { 69 | disconnect_cb_ = cb; 70 | } 71 | 72 | std::unique_ptr read_buffer_; 73 | std::unique_ptr write_buffer_; 74 | std::atomic_bool is_closed_; 75 | 76 | private: 77 | void Close(); 78 | 79 | std::shared_ptr task_scheduler_; 80 | std::shared_ptr channel_; 81 | std::mutex mutex_; 82 | DisconnectCallback disconnect_cb_; 83 | CloseCallback close_cb_; 84 | ReadCallback read_cb_; 85 | bool ipv6_; 86 | }; 87 | 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /rtsp-server/net/TcpServer.cpp: -------------------------------------------------------------------------------- 1 | // Scott Xu 2 | // 2020-12-04 Add multiple socket support. 3 | #include "TcpServer.h" 4 | #include "Acceptor.h" 5 | #include "EventLoop.h" 6 | #include 7 | 8 | using namespace xop; 9 | using namespace std; 10 | 11 | TcpServer::TcpServer(EventLoop *event_loop) : event_loop_(event_loop) 12 | //, port_(0) 13 | //, acceptor_(new Acceptor(event_loop_)) 14 | //, is_started_(false) 15 | { 16 | } 17 | 18 | TcpServer::~TcpServer() 19 | { 20 | TcpServer::Stop(); 21 | } 22 | 23 | bool TcpServer::Start(const std::string &ip, const uint16_t port) 24 | { 25 | /*Stop(); 26 | 27 | if (!is_started_) { 28 | if (acceptor_->Listen(ip, port) < 0) { 29 | return false; 30 | } 31 | 32 | port_ = port; 33 | ip_ = ip; 34 | is_started_ = true; 35 | return true; 36 | }*/ 37 | //return false; 38 | 39 | auto acceptor = std::make_unique(event_loop_); 40 | acceptor->SetNewConnectionCallback([this](SOCKET sockfd) { 41 | if (const auto conn = this->OnConnect(sockfd)) { 42 | this->AddConnection(sockfd, conn); 43 | conn->SetDisconnectCallback([this](const TcpConnection:: 44 | Weak &conn) { 45 | const auto scheduler = 46 | conn.lock()->GetTaskScheduler(); 47 | if (SOCKET sockfd = conn.lock()->GetSocket(); 48 | !scheduler->AddTriggerEvent([this, sockfd] { 49 | this->RemoveConnection(sockfd); 50 | })) { 51 | scheduler->AddTimer( 52 | [this, sockfd]() { 53 | this->RemoveConnection( 54 | sockfd); 55 | return false; 56 | }, 57 | 100); 58 | } 59 | }); 60 | } 61 | }); 62 | if (acceptor->Listen(ip, port) < 0) 63 | return false; 64 | acceptors_.push_back(std::move(acceptor)); 65 | return true; 66 | } 67 | 68 | void TcpServer::Stop() 69 | { 70 | /*if (is_started_) { 71 | 72 | mutex_.lock(); 73 | for (auto iter : connections_) { 74 | iter.second->Disconnect(); 75 | } 76 | mutex_.unlock(); 77 | 78 | acceptor_->Close(); 79 | is_started_ = false; 80 | 81 | while (1) { 82 | Timer::Sleep(1); 83 | if (connections_.empty()) { 84 | break; 85 | } 86 | } 87 | }*/ 88 | if (acceptors_.empty()) 89 | return; 90 | 91 | mutex_.lock(); 92 | for (const auto iter : connections_) 93 | iter.second->Disconnect(); 94 | mutex_.unlock(); 95 | 96 | for (auto it = acceptors_.begin(); it != acceptors_.end(); ++it) 97 | (*it)->Close(); 98 | 99 | while (!connections_.empty()) 100 | Timer::Sleep(10); 101 | 102 | acceptors_.clear(); 103 | 104 | return; 105 | } 106 | 107 | TcpConnection::Ptr TcpServer::OnConnect(const SOCKET sockfd) 108 | { 109 | return std::make_shared(sockfd, 110 | event_loop_->GetTaskScheduler()); 111 | } 112 | 113 | void TcpServer::AddConnection(const SOCKET sockfd, TcpConnection::Ptr tcpConn) 114 | { 115 | std::lock_guard locker(mutex_); 116 | connections_.emplace(sockfd, tcpConn); 117 | } 118 | 119 | void TcpServer::RemoveConnection(const SOCKET sockfd) 120 | { 121 | std::lock_guard locker(mutex_); 122 | connections_.erase(sockfd); 123 | } 124 | -------------------------------------------------------------------------------- /rtsp-server/net/TcpServer.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-11-10 3 | // Scott Xu 4 | // 2020-12-04 Add multiple socket support. 5 | 6 | #ifndef XOP_TCPSERVER_H 7 | #define XOP_TCPSERVER_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "EventLoop.h" 14 | #include "TcpConnection.h" 15 | 16 | namespace xop { 17 | 18 | class Acceptor; 19 | class EventLoop; 20 | 21 | class TcpServer { 22 | public: 23 | explicit TcpServer(EventLoop *event_loop); 24 | virtual ~TcpServer(); 25 | 26 | virtual bool Start(const std::string &ip, uint16_t port); 27 | virtual void Stop(); 28 | 29 | /*std::string GetIPAddress() const 30 | { return ip_; } 31 | 32 | uint16_t GetPort() const 33 | { return port_; }*/ 34 | 35 | protected: 36 | virtual TcpConnection::Ptr OnConnect(SOCKET sockfd); 37 | virtual void AddConnection(SOCKET sockfd, TcpConnection::Ptr tcpConn); 38 | virtual void RemoveConnection(SOCKET sockfd); 39 | 40 | EventLoop *event_loop_; 41 | //uint16_t port_; 42 | //std::string ip_; 43 | std::vector> acceptors_; 44 | //bool is_started_; 45 | std::mutex mutex_; 46 | std::unordered_map> connections_; 47 | }; 48 | 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /rtsp-server/net/TcpSocket.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | // Scott Xu 4 | // 2020-12-2 Add IPv6 Support. 5 | 6 | #include "TcpSocket.h" 7 | #include "Socket.h" 8 | #include "SocketUtil.h" 9 | #include "Logger.h" 10 | 11 | using namespace xop; 12 | 13 | TcpSocket::TcpSocket(const SOCKET sockfd, const bool ipv6) 14 | : sockfd_(sockfd), ipv6_(ipv6) 15 | { 16 | } 17 | 18 | TcpSocket::~TcpSocket() = default; 19 | 20 | SOCKET TcpSocket::Create(const bool ipv6) 21 | { 22 | ipv6_ = ipv6; 23 | sockfd_ = ::socket(ipv6_ ? AF_INET6 : AF_INET, SOCK_STREAM, 0); //TODO 24 | return sockfd_; 25 | } 26 | 27 | bool TcpSocket::Bind(const std::string &ip, const uint16_t port) const 28 | { 29 | if (!SocketUtil::Bind(sockfd_, ip, port, ipv6_)) { 30 | LOG_ERROR(" bind <%s:%u> failed.\n", sockfd_, 31 | ip.c_str(), port); 32 | return false; 33 | } 34 | 35 | return true; 36 | } 37 | 38 | bool TcpSocket::Listen(const int backlog) const 39 | { 40 | if (::listen(sockfd_, backlog) == SOCKET_ERROR) { 41 | LOG_ERROR(" listen failed.\n", sockfd_); 42 | return false; 43 | } 44 | 45 | return true; 46 | } 47 | 48 | SOCKET TcpSocket::Accept() const 49 | { 50 | sockaddr *psockaddr; 51 | socklen_t addrlen; 52 | if (ipv6_) { 53 | sockaddr_in6 addr = {0}; 54 | addrlen = sizeof addr; 55 | psockaddr = reinterpret_cast(&addr); 56 | } else { 57 | sockaddr_in addr = {0}; 58 | addrlen = sizeof addr; 59 | psockaddr = reinterpret_cast(&addr); 60 | } 61 | 62 | const SOCKET socket_fd = accept(sockfd_, psockaddr, &addrlen); 63 | 64 | return socket_fd; 65 | } 66 | 67 | bool TcpSocket::Connect(const std::string &ip, const uint16_t port, 68 | const int timeout) const 69 | { 70 | if (!SocketUtil::Connect(sockfd_, ip, port, timeout, ipv6_)) { 71 | LOG_ERROR(" connect failed.\n", sockfd_); 72 | return false; 73 | } 74 | 75 | return true; 76 | } 77 | 78 | void TcpSocket::Close() 79 | { 80 | #if defined(WIN32) || defined(_WIN32) 81 | closesocket(sockfd_); 82 | #else 83 | ::close(sockfd_); 84 | #endif 85 | sockfd_ = 0; 86 | } 87 | 88 | void TcpSocket::ShutdownWrite() 89 | { 90 | shutdown(sockfd_, SHUT_WR); 91 | sockfd_ = 0; 92 | } 93 | -------------------------------------------------------------------------------- /rtsp-server/net/TcpSocket.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | // Scott Xu 4 | // 2020-12-2 Add IPv6 Support. 5 | 6 | #ifndef XOP_TCP_SOCKET_H 7 | #define XOP_TCP_SOCKET_H 8 | 9 | #include 10 | #include 11 | #include "Socket.h" 12 | 13 | namespace xop { 14 | 15 | class TcpSocket { 16 | public: 17 | TcpSocket(SOCKET sockfd = -1, bool ipv6 = false); 18 | virtual ~TcpSocket(); 19 | 20 | SOCKET Create(bool ipv6 = false); 21 | bool Bind(const std::string &ip, uint16_t port) const; 22 | bool Listen(int backlog) const; 23 | SOCKET Accept() const; 24 | bool Connect(const std::string &ip, uint16_t port, 25 | int timeout = 0) const; 26 | void Close(); 27 | void ShutdownWrite(); 28 | 29 | SOCKET GetSocket() const { return sockfd_; } 30 | bool IsIpv6Socket() const { return ipv6_; } 31 | 32 | private: 33 | SOCKET sockfd_ = 0; 34 | bool ipv6_; 35 | }; 36 | 37 | } 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /rtsp-server/net/ThreadSafeQueue.h.bak: -------------------------------------------------------------------------------- 1 | #ifndef THREAD_SAFE_QUEUE_H 2 | #define THREAD_SAFE_QUEUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace xop 9 | { 10 | 11 | template 12 | class ThreadSafeQueue 13 | { 14 | public: 15 | ThreadSafeQueue() 16 | { 17 | 18 | } 19 | 20 | ThreadSafeQueue(ThreadSafeQueue const& other) 21 | { 22 | std::lock_guard lock(other._mutex); 23 | _dataQueue = other._dataQueue; 24 | } 25 | 26 | ~ThreadSafeQueue() 27 | { 28 | 29 | } 30 | 31 | void push(T value) 32 | { 33 | std::lock_guard lock(_mutex); 34 | _dataQueue.push(value); 35 | _dataCond.notify_one(); 36 | } 37 | 38 | bool waitAndPop(T& value) 39 | { 40 | std::unique_lock lock(_mutex); 41 | _dataCond.wait(lock); 42 | if (_dataQueue.empty()) 43 | { 44 | return false; 45 | } 46 | value = _dataQueue.front(); 47 | _dataQueue.pop(); 48 | return true; 49 | } 50 | 51 | std::shared_ptr waitAndPop() 52 | { 53 | std::unique_lock lock(_mutex); 54 | _dataCond.wait(lock); 55 | if (_dataQueue.empty()) 56 | { 57 | return nullptr; 58 | } 59 | std::shared_ptr res(std::make_shared(_dataQueue.front())); 60 | _dataQueue.pop(); 61 | return res; 62 | } 63 | 64 | bool tryPop(T& value) 65 | { 66 | std::lock_guard lock(_mutex); 67 | if(_dataQueue.empty()) 68 | return false; 69 | 70 | value = _dataQueue.front(); 71 | _dataQueue.pop(); 72 | 73 | return true; 74 | } 75 | 76 | std::shared_ptr tryPop() 77 | { 78 | std::lock_guard lock(_mutex); 79 | if(_dataQueue.empty()) 80 | return std::shared_ptr(); 81 | std::shared_ptr res(std::make_shared(_dataQueue.front())); 82 | _dataQueue.pop(); 83 | return res; 84 | } 85 | 86 | size_t size() const 87 | { 88 | std::lock_guard lock(_mutex); 89 | return _dataQueue.size(); 90 | } 91 | 92 | bool empty() const 93 | { 94 | std::lock_guard lock(_mutex); 95 | return _dataQueue.empty(); 96 | } 97 | 98 | void clear() 99 | { 100 | std::lock_guard lock(_mutex); 101 | std::queue empty; 102 | _dataQueue.swap(empty); 103 | } 104 | 105 | void wake() 106 | { 107 | _dataCond.notify_one(); 108 | } 109 | 110 | private: 111 | mutable std::mutex _mutex; 112 | std::queue _dataQueue; 113 | std::condition_variable _dataCond; 114 | }; 115 | 116 | } 117 | 118 | #endif 119 | 120 | -------------------------------------------------------------------------------- /rtsp-server/net/Timer.cpp: -------------------------------------------------------------------------------- 1 | #include "Timer.h" 2 | 3 | using namespace xop; 4 | using namespace std; 5 | using namespace std::chrono; 6 | 7 | TimerId TimerQueue::AddTimer(const TimerEvent &event, uint32_t ms) 8 | { 9 | std::lock_guard locker(mutex_); 10 | const int64_t timeout = GetTimeNow(); 11 | TimerId timer_id = ++last_timer_id_; 12 | 13 | auto timer = make_shared(event, ms); 14 | timer->SetNextTimeout(timeout); 15 | timers_.emplace(timer_id, timer); 16 | events_.emplace(std::pair(timeout + ms, timer_id), std::move(timer)); 17 | return timer_id; 18 | } 19 | 20 | void TimerQueue::RemoveTimer(TimerId timerId) 21 | { 22 | std::lock_guard locker(mutex_); 23 | if (const auto iter = timers_.find(timerId); iter != timers_.end()) { 24 | int64_t timeout = iter->second->getNextTimeout(); 25 | events_.erase(std::pair(timeout, timerId)); 26 | timers_.erase(timerId); 27 | } 28 | } 29 | 30 | int64_t TimerQueue::GetTimeNow() const 31 | { 32 | const auto time_point = steady_clock::now(); 33 | return duration_cast(time_point.time_since_epoch()) 34 | .count(); 35 | } 36 | 37 | int64_t TimerQueue::GetTimeRemaining() 38 | { 39 | std::lock_guard locker(mutex_); 40 | 41 | if (timers_.empty()) { 42 | return -1; 43 | } 44 | 45 | int64_t msec = events_.begin()->first.first - GetTimeNow(); 46 | if (msec < 0) { 47 | msec = 0; 48 | } 49 | 50 | return msec; 51 | } 52 | 53 | void TimerQueue::HandleTimerEvent() 54 | { 55 | if (!timers_.empty()) { 56 | std::lock_guard locker(mutex_); 57 | const int64_t timePoint = GetTimeNow(); 58 | while (!timers_.empty() && 59 | events_.begin()->first.first <= timePoint) { 60 | TimerId timerId = events_.begin()->first.second; 61 | if (const bool flag = 62 | events_.begin()->second->event_callback_(); 63 | flag) { 64 | events_.begin()->second->SetNextTimeout( 65 | timePoint); 66 | auto timerPtr = events_.begin()->second; 67 | events_.erase(events_.begin()); 68 | events_.emplace( 69 | std::pair(timerPtr->getNextTimeout(), 70 | timerId), 71 | timerPtr); 72 | } else { 73 | events_.erase(events_.begin()); 74 | timers_.erase(timerId); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /rtsp-server/net/Timer.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef _XOP_TIMER_H 5 | #define _XOP_TIMER_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace xop { 18 | 19 | typedef std::function TimerEvent; 20 | typedef uint32_t TimerId; 21 | 22 | class Timer { 23 | public: 24 | Timer(TimerEvent event, const uint32_t msec) 25 | : event_callback_(std::move(event)), interval_(msec) 26 | { 27 | if (interval_ == 0) { 28 | interval_ = 1; 29 | } 30 | } 31 | 32 | static void Sleep(const uint32_t msec) 33 | { 34 | std::this_thread::sleep_for(std::chrono::milliseconds(msec)); 35 | } 36 | 37 | void SetEventCallback(const TimerEvent &event) 38 | { 39 | event_callback_ = event; 40 | } 41 | 42 | void Start(const int64_t microseconds, const bool repeat = false) 43 | { 44 | is_repeat_ = repeat; 45 | auto time_begin = std::chrono::high_resolution_clock::now(); 46 | int64_t elapsed = 0; 47 | 48 | do { 49 | std::this_thread::sleep_for(std::chrono::microseconds( 50 | microseconds - elapsed)); 51 | time_begin = std::chrono::high_resolution_clock::now(); 52 | if (event_callback_) { 53 | event_callback_(); 54 | } 55 | elapsed = 56 | std::chrono::duration_cast< 57 | std::chrono::microseconds>( 58 | std::chrono::high_resolution_clock::now() - 59 | time_begin) 60 | .count(); 61 | if (elapsed < 0) { 62 | elapsed = 0; 63 | } 64 | 65 | } while (is_repeat_); 66 | } 67 | 68 | void Stop() { is_repeat_ = false; } 69 | 70 | private: 71 | friend class TimerQueue; 72 | 73 | void SetNextTimeout(const int64_t time_point) 74 | { 75 | next_timeout_ = time_point + interval_; 76 | } 77 | 78 | int64_t getNextTimeout() const { return next_timeout_; } 79 | 80 | bool is_repeat_ = false; 81 | TimerEvent event_callback_ = [] { return false; }; 82 | uint32_t interval_ = 0; 83 | int64_t next_timeout_ = 0; 84 | }; 85 | 86 | class TimerQueue { 87 | public: 88 | TimerId AddTimer(const TimerEvent &event, uint32_t msec); 89 | void RemoveTimer(TimerId timerId); 90 | 91 | int64_t GetTimeRemaining(); 92 | void HandleTimerEvent(); 93 | 94 | private: 95 | int64_t GetTimeNow() const; 96 | 97 | std::mutex mutex_; 98 | std::unordered_map> timers_; 99 | std::map, std::shared_ptr> events_; 100 | uint32_t last_timer_id_ = 0; 101 | }; 102 | 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /rtsp-server/net/Timestamp.cpp: -------------------------------------------------------------------------------- 1 | #include "Timestamp.h" 2 | #include 3 | #include 4 | 5 | using namespace xop; 6 | using namespace std; 7 | using namespace std::chrono; 8 | 9 | std::string Timestamp::Localtime() 10 | { 11 | std::ostringstream stream; 12 | const auto now = system_clock::now(); 13 | const time_t tt = system_clock::to_time_t(now); 14 | 15 | #if defined(WIN32) || defined(_WIN32) 16 | tm tm{}; 17 | localtime_s(&tm, &tt); 18 | stream << std::put_time(&tm, "%F %T"); 19 | #else 20 | char buffer[200] = {0}; 21 | std::string timeString; 22 | std::strftime(buffer, 200, "%F %T", std::localtime(&tt)); 23 | stream << buffer; 24 | #endif 25 | return stream.str(); 26 | } 27 | -------------------------------------------------------------------------------- /rtsp-server/net/Timestamp.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-15 3 | 4 | #ifndef XOP_TIMESTAMP_H 5 | #define XOP_TIMESTAMP_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace xop { 14 | 15 | class Timestamp { 16 | public: 17 | Timestamp() 18 | : begin_time_point_(std::chrono::high_resolution_clock::now()) 19 | { 20 | } 21 | 22 | void reset() 23 | { 24 | begin_time_point_ = std::chrono::high_resolution_clock::now(); 25 | } 26 | 27 | int64_t Elapsed() const 28 | { 29 | return std::chrono::duration_cast( 30 | std::chrono::high_resolution_clock::now() - 31 | begin_time_point_) 32 | .count(); 33 | } 34 | 35 | static std::string Localtime(); 36 | 37 | private: 38 | std::chrono::time_point 39 | begin_time_point_; 40 | }; 41 | 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /rtsp-server/xop/AACSource.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-16 3 | 4 | #ifndef XOP_AAC_SOURCE_H 5 | #define XOP_AAC_SOURCE_H 6 | 7 | #include "MediaSource.h" 8 | #include "rtp.h" 9 | 10 | namespace xop { 11 | 12 | class AACSource : public MediaSource { 13 | public: 14 | static AACSource *CreateNew(uint32_t samplerate = 44100, 15 | uint8_t channels = 2, bool has_adts = true); 16 | ~AACSource() override; 17 | 18 | uint32_t GetSamplerate() const { return samplerate_; } 19 | 20 | uint32_t GetChannels() const { return channels_; } 21 | 22 | std::string GetMediaDescription(uint16_t port = 0) override; 23 | 24 | std::string GetAttribute() override; 25 | 26 | bool HandleFrame(MediaChannelId channel_id, AVFrame frame) override; 27 | 28 | static uint32_t GetTimestamp(uint32_t samplerate = 44100); 29 | 30 | private: 31 | AACSource(uint32_t samplerate, uint8_t channels, bool has_adts); 32 | 33 | uint32_t samplerate_ = 44100; 34 | uint8_t channels_ = 2; 35 | bool has_adts_ = true; 36 | 37 | static constexpr size_t ADTS_SIZE = 7; 38 | static constexpr size_t AU_SIZE = 4; 39 | }; 40 | 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /rtsp-server/xop/Base64Encode.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Base64Encode.h" 3 | 4 | #include 5 | 6 | extern "C" { 7 | #include "b64/cencode.h" 8 | } 9 | 10 | std::string xop::Base64Encode(const void *input, const size_t size) 11 | { 12 | std::vector buffer(size / 3 * 4 + (size % 3 > 0 ? 4 : 0) + 1); 13 | base64_encodestate b64encoder; 14 | base64_init_encodestate(&b64encoder); 15 | 16 | const auto length = base64_encode_block( 17 | static_cast(input), static_cast(size), 18 | buffer.data(), &b64encoder); 19 | base64_encode_blockend(buffer.data() + length, &b64encoder); 20 | 21 | return std::string(buffer.cbegin(), buffer.cend() - 1); //TODO 22 | } 23 | -------------------------------------------------------------------------------- /rtsp-server/xop/Base64Encode.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOP_BASE64ENCODE_H 2 | #define _XOP_BASE64ENCODE_H 3 | #include 4 | 5 | namespace xop { 6 | std::string Base64Encode(const void *input, const size_t size); 7 | } 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /rtsp-server/xop/BaseMd5.cpp: -------------------------------------------------------------------------------- 1 | #include "BaseMd5.h" 2 | #include "md5.hpp" 3 | 4 | using namespace xop; 5 | 6 | BaseMd5::BaseMd5() : Md5() {} 7 | 8 | BaseMd5::~BaseMd5() = default; 9 | 10 | void BaseMd5::GetMd5Hash(const unsigned char *data, const size_t dataSize, 11 | unsigned char *outHash) 12 | { 13 | md5::md5_state_t state; 14 | 15 | md5_init(&state); 16 | md5_append(&state, data, dataSize); 17 | md5_finish(&state, outHash); 18 | } 19 | -------------------------------------------------------------------------------- /rtsp-server/xop/BaseMd5.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOP_BASEMD5_H 2 | #define _XOP_BASEMD5_H 3 | 4 | #include "Md5.h" 5 | 6 | namespace xop { 7 | class BaseMd5 : public Md5 { 8 | public: 9 | BaseMd5(); 10 | ~BaseMd5() override; 11 | 12 | void GetMd5Hash(const unsigned char *data, size_t dataSize, 13 | unsigned char *outHash) override; 14 | }; 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /rtsp-server/xop/CngMd5.cpp: -------------------------------------------------------------------------------- 1 | #include "CngMd5.h" 2 | #include "net/Logger.h" 3 | 4 | #if defined(WIN32) || defined(_WIN32) 5 | #pragma comment(lib, "Bcrypt.lib") 6 | #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) 7 | #define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) 8 | #endif 9 | 10 | using namespace xop; 11 | 12 | CngMd5::CngMd5() : Md5() 13 | { 14 | 15 | #if defined(WIN32) || defined(_WIN32) 16 | DWORD cbData = 0; 17 | NTSTATUS status; 18 | if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider( 19 | &hAlgorithm_, BCRYPT_MD5_ALGORITHM, nullptr, 0))) { 20 | LOG_ERROR( 21 | "**** Error 0x%x returned by BCryptOpenAlgorithmProvider", 22 | status); 23 | return; 24 | } 25 | if (!NT_SUCCESS(status = BCryptGetProperty( 26 | hAlgorithm_, BCRYPT_OBJECT_LENGTH, 27 | (PBYTE)&cbHashObject_, sizeof(DWORD), &cbData, 28 | 0))) { 29 | LOG_ERROR("**** Error 0x%x returned by BCryptGetProperty", 30 | status); 31 | CngMd5::~CngMd5(); 32 | return; 33 | } 34 | if (!NT_SUCCESS(status = BCryptGetProperty( 35 | hAlgorithm_, BCRYPT_HASH_LENGTH, 36 | (PBYTE)&cbHash_, sizeof(DWORD), &cbData, 0))) { 37 | LOG_ERROR("**** Error 0x%x returned by BCryptGetProperty", 38 | status); 39 | CngMd5::~CngMd5(); 40 | return; 41 | } 42 | if (cbHash_ > MD5_HASH_LENGTH) { 43 | LOG_ERROR("**** The generated hash value is too long"); 44 | CngMd5::~CngMd5(); 45 | } 46 | #endif 47 | } 48 | 49 | CngMd5::~CngMd5() 50 | { 51 | #if defined(WIN32) || defined(_WIN32) 52 | if (hAlgorithm_) { 53 | BCryptCloseAlgorithmProvider(hAlgorithm_, 0); 54 | hAlgorithm_ = nullptr; 55 | } 56 | #endif 57 | } 58 | 59 | void CngMd5::GetMd5Hash(const unsigned char *data, const size_t dataSize, 60 | unsigned char *outHash) 61 | { 62 | #if defined(WIN32) || defined(_WIN32) 63 | if (hAlgorithm_ == nullptr) return; 64 | 65 | const auto pbHashObject = static_cast( 66 | HeapAlloc(GetProcessHeap(), 0, cbHashObject_)); 67 | //create a hash 68 | BCRYPT_HASH_HANDLE hHash = nullptr; 69 | 70 | auto cleanup = [&] () { 71 | if (hHash) 72 | BCryptDestroyHash(hHash); 73 | if (pbHashObject) 74 | HeapFree(GetProcessHeap(), 0, pbHashObject); 75 | }; 76 | 77 | if (nullptr == pbHashObject) { 78 | LOG_ERROR("**** memory allocation failed"); 79 | cleanup(); 80 | return; 81 | } 82 | NTSTATUS status; 83 | if (!NT_SUCCESS(status = BCryptCreateHash(hAlgorithm_, &hHash, 84 | pbHashObject, cbHashObject_, 85 | nullptr, 0, 0))) { 86 | LOG_ERROR("**** Error 0x%x returned by BCryptCreateHash", 87 | status); 88 | cleanup(); 89 | return; 90 | } 91 | 92 | if (!NT_SUCCESS(status = BCryptHashData(hHash, const_cast(data), 93 | static_cast(dataSize), 0))) { 94 | LOG_ERROR("**** Error 0x%x returned by BCryptHashData", status); 95 | cleanup(); 96 | return; 97 | } 98 | 99 | //close the hash 100 | if (!NT_SUCCESS(status = 101 | BCryptFinishHash(hHash, outHash, cbHash_, 0))) { 102 | LOG_ERROR("**** Error 0x%x returned by BCryptFinishHash", 103 | status); 104 | } 105 | cleanup(); 106 | #endif 107 | } 108 | -------------------------------------------------------------------------------- /rtsp-server/xop/CngMd5.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOP_CNGMD5_H 2 | #define _XOP_CNGMD5_H 3 | 4 | #include "Md5.h" 5 | 6 | #if defined(WIN32) || defined(_WIN32) 7 | #include 8 | #include 9 | #endif 10 | 11 | namespace xop { 12 | class CngMd5 : public Md5 { 13 | public: 14 | CngMd5(); 15 | ~CngMd5() override; 16 | 17 | void GetMd5Hash(const unsigned char *data, size_t dataSize, 18 | unsigned char *outHash) override; 19 | 20 | private: 21 | #if defined(WIN32) || defined(_WIN32) 22 | BCRYPT_ALG_HANDLE hAlgorithm_ = nullptr; 23 | DWORD cbHash_ = 0, cbHashObject_ = 0; 24 | #endif 25 | }; 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /rtsp-server/xop/DigestAuthentication.cpp: -------------------------------------------------------------------------------- 1 | #include "DigestAuthentication.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #if defined(WIN32) || defined(_WIN32) 7 | #include "CngMd5.h" 8 | #elif defined(__linux) || defined(__linux__) 9 | #include "BaseMd5.h" 10 | #elif defined(__APPLE__) || defined(__MACH__) 11 | #include "MacMd5.h" 12 | #else 13 | #include "BaseMd5.h" 14 | #endif 15 | 16 | using namespace xop; 17 | 18 | DigestAuthentication::DigestAuthentication(std::string realm, 19 | std::string username, 20 | std::string password) 21 | : realm_(std::move(realm)), 22 | username_(std::move(username)), 23 | password_(std::move(password)) 24 | { 25 | #if defined(WIN32) || defined(_WIN32) 26 | md5_ = new CngMd5(); 27 | #elif defined(__linux) || defined(__linux__) 28 | md5_ = new BaseMd5(); 29 | #elif defined(__APPLE__) || defined(__MACH__) 30 | md5_ = new MacMd5(); 31 | #else 32 | md5_ = new BaseMd5(); 33 | #endif 34 | } 35 | 36 | DigestAuthentication::~DigestAuthentication() 37 | { 38 | delete md5_; 39 | } 40 | 41 | std::string DigestAuthentication::GetNonce() const 42 | { 43 | std::random_device rd; 44 | 45 | const auto timePoint = 46 | std::chrono::time_point_cast( 47 | std::chrono::steady_clock::now()); 48 | const uint32_t timestamp = 49 | static_cast(timePoint.time_since_epoch().count()); 50 | 51 | return md5_->GetMd5HashString(std::to_string(timestamp + rd())); 52 | } 53 | 54 | std::string DigestAuthentication::GetResponse(const std::string &nonce, 55 | const std::string &cmd, 56 | const std::string &url) const 57 | { 58 | //md5(md5(: : ) : : md5(:)) 59 | const auto hex1 = md5_->GetMd5HashString(username_ + ":" + realm_ + 60 | ":" + password_); 61 | const auto hex2 = md5_->GetMd5HashString(cmd + ":" + url); 62 | auto response = md5_->GetMd5HashString(hex1 + ":" + nonce + ":" + hex2); 63 | return response; 64 | } 65 | -------------------------------------------------------------------------------- /rtsp-server/xop/DigestAuthentication.h: -------------------------------------------------------------------------------- 1 | //PHZ 2 | //2019-10-6 3 | 4 | #ifndef RTSP_DIGEST_AUTHENTICATION_H 5 | #define RTSP_DIGEST_AUTHENTICATION_H 6 | 7 | #include 8 | #include "Md5.h" 9 | 10 | namespace xop { 11 | 12 | class DigestAuthentication { 13 | public: 14 | DigestAuthentication(std::string realm, std::string 15 | username, 16 | std::string password); 17 | virtual ~DigestAuthentication(); 18 | 19 | std::string GetRealm() const { return realm_; } 20 | 21 | std::string GetUsername() const { return username_; } 22 | 23 | std::string GetPassword() const { return password_; } 24 | 25 | std::string GetNonce() const; 26 | std::string GetResponse(const std::string &nonce, const std::string &cmd, 27 | const std::string &url) const; 28 | 29 | private: 30 | std::string realm_; 31 | std::string username_; 32 | std::string password_; 33 | Md5 *md5_; 34 | }; 35 | 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /rtsp-server/xop/G711ASource.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-16 3 | 4 | #if defined(WIN32) || defined(_WIN32) 5 | #ifndef _CRT_SECURE_NO_WARNINGS 6 | #define _CRT_SECURE_NO_WARNINGS 7 | #endif 8 | #endif 9 | #include "G711ASource.h" 10 | #include 11 | #include 12 | #include 13 | #if defined(WIN32) || defined(_WIN32) 14 | 15 | #else 16 | #include 17 | #endif 18 | 19 | using namespace xop; 20 | using namespace std; 21 | 22 | G711ASource::G711ASource() 23 | { 24 | payload_ = 8; 25 | media_type_ = MediaType::PCMA; 26 | clock_rate_ = 8000; 27 | } 28 | 29 | G711ASource *G711ASource::CreateNew() 30 | { 31 | return new G711ASource(); 32 | } 33 | 34 | G711ASource::~G711ASource() = default; 35 | 36 | string G711ASource::GetMediaDescription(const uint16_t port) 37 | { 38 | char buf[100]; 39 | snprintf(buf, sizeof(buf), "m=audio %hu RTP/AVP 8", port); 40 | 41 | return buf; 42 | } 43 | 44 | string G711ASource::GetAttribute() 45 | { 46 | return "a=rtpmap:8 PCMA/8000/1"; 47 | } 48 | 49 | bool G711ASource::HandleFrame(const MediaChannelId channel_id, 50 | const AVFrame frame) 51 | { 52 | if (frame.size > MAX_RTP_PAYLOAD_SIZE) { 53 | return false; 54 | } 55 | 56 | const uint8_t *frame_buf = frame.buffer.get(); 57 | const size_t frame_size = frame.size; 58 | 59 | RtpPacket rtp_pkt; 60 | rtp_pkt.type = FrameType::AUDIO_FRAME; 61 | rtp_pkt.timestamp = frame.timestamp; 62 | rtp_pkt.size = static_cast(frame_size) + RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE; 63 | rtp_pkt.last = 1; 64 | 65 | memcpy(rtp_pkt.data.get() + RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE, 66 | frame_buf, frame_size); 67 | 68 | if (send_frame_callback_) { 69 | return send_frame_callback_(channel_id, rtp_pkt); //TODO 70 | } 71 | 72 | return true; 73 | } 74 | 75 | uint32_t G711ASource::GetTimestamp() 76 | { 77 | const auto time_point = chrono::time_point_cast( 78 | chrono::steady_clock::now()); 79 | return static_cast( 80 | (time_point.time_since_epoch().count() + 500) / 1000 * 8); 81 | } 82 | -------------------------------------------------------------------------------- /rtsp-server/xop/G711ASource.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-16 3 | 4 | #ifndef XOP_G711A_SOURCE_H 5 | #define XOP_G711A_SOURCE_H 6 | 7 | #include "MediaSource.h" 8 | #include "rtp.h" 9 | 10 | namespace xop { 11 | 12 | class G711ASource : public MediaSource { 13 | public: 14 | static G711ASource *CreateNew(); 15 | ~G711ASource() override; 16 | 17 | uint32_t GetSampleRate() const { return samplerate_; } 18 | 19 | uint32_t GetChannels() const { return channels_; } 20 | 21 | std::string GetMediaDescription(uint16_t port = 0) override; 22 | 23 | std::string GetAttribute() override; 24 | 25 | bool HandleFrame(MediaChannelId channel_id, AVFrame frame) override; 26 | 27 | static uint32_t GetTimestamp(); 28 | 29 | private: 30 | G711ASource(); 31 | 32 | uint32_t samplerate_ = 8000; 33 | uint32_t channels_ = 1; 34 | }; 35 | 36 | } 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /rtsp-server/xop/H264NalUnit.cpp: -------------------------------------------------------------------------------- 1 | #include "H264NalUnit.h" 2 | 3 | using namespace xop; 4 | 5 | H264NalUnit::H264NalUnit(const uint8_t *data, size_t dataSize) 6 | : NalUnit(data, dataSize) 7 | { 8 | } 9 | 10 | uint8_t H264NalUnit::GetType() 11 | { 12 | uint8_t *data; 13 | if (GetHeader(&data) == H264_NALU_HEADER_SIZE) { 14 | return data[0] & 0x1f; 15 | } 16 | return 0; 17 | } 18 | 19 | uint8_t H264NalUnit::GetRefIdc() 20 | { 21 | uint8_t *data; 22 | if (GetHeader(&data) == H264_NALU_HEADER_SIZE) { 23 | return (data[0] & 0x60) >> 5; 24 | } 25 | return 0; 26 | } 27 | 28 | H264NalType H264NalUnit::GetH264Type() 29 | { 30 | return static_cast(GetType()); 31 | } 32 | 33 | size_t H264NalUnit::GetHeader(uint8_t **data) 34 | { 35 | if (GetData(data) >= H264_NALU_HEADER_SIZE) 36 | return H264_NALU_HEADER_SIZE; 37 | return 0; 38 | } 39 | 40 | size_t H264NalUnit::CopyHeader(uint8_t *start, size_t size) 41 | { 42 | if (size > H264_NALU_HEADER_SIZE) { 43 | size = H264_NALU_HEADER_SIZE; 44 | } 45 | return CopyData(start, size); 46 | } 47 | 48 | size_t H264NalUnit::GetHeaderSize() 49 | { 50 | const auto size = GetSize(); 51 | if (size > H264_NALU_HEADER_SIZE) 52 | return H264_NALU_HEADER_SIZE; 53 | return 0; 54 | } 55 | 56 | size_t H264NalUnit::GetBody(uint8_t **data) 57 | { 58 | const auto size = GetData(data); 59 | if (size > H264_NALU_HEADER_SIZE) { 60 | *data += H264_NALU_HEADER_SIZE; 61 | return size - H264_NALU_HEADER_SIZE; 62 | } 63 | return 0; 64 | } 65 | 66 | size_t H264NalUnit::CopyBody(uint8_t *start, size_t size, size_t skip) 67 | { 68 | skip += H264_NALU_HEADER_SIZE; 69 | return CopyData(start, size, skip); 70 | } 71 | 72 | size_t H264NalUnit::GetBodySize() 73 | { 74 | const auto size = GetSize(); 75 | if (size > H264_NALU_HEADER_SIZE) 76 | return size - H264_NALU_HEADER_SIZE; 77 | return 0; 78 | } 79 | 80 | bool H264NalUnit::IsIdrFrame() 81 | { 82 | const auto type = GetH264Type(); 83 | return type == H264NalType::H264_NAL_SLICE_IDR; 84 | } 85 | 86 | bool H264NalUnit::IsFrame() 87 | { 88 | const auto type = GetH264Type(); 89 | return type >= H264NalType::H264_NAL_SLICE && type <= H264NalType::H264_NAL_SLICE_IDR; 90 | } 91 | 92 | NalUnit * H264NalUnit::GetNalUnit(const uint8_t *data, size_t dataSize) 93 | { 94 | return new H264NalUnit(data, dataSize); 95 | } 96 | -------------------------------------------------------------------------------- /rtsp-server/xop/H264NalUnit.h: -------------------------------------------------------------------------------- 1 | #ifndef XOP_H264_NALUNIT_H 2 | #define XOP_H264_NALUNIT_H 3 | 4 | #include 5 | #include "NalUnit.h" 6 | 7 | #define H264_NALU_HEADER_SIZE 1 8 | 9 | namespace xop { 10 | 11 | enum class H264NalType: uint8_t { 12 | H264_NAL_UNKNOWN = 0, 13 | H264_NAL_SLICE = 1, 14 | H264_NAL_SLICE_DPA = 2, 15 | H264_NAL_SLICE_DPB = 3, 16 | H264_NAL_SLICE_DPC = 4, 17 | H264_NAL_SLICE_IDR = 5, 18 | H264_NAL_SEI = 6, 19 | H264_NAL_SPS = 7, 20 | H264_NAL_PPS = 8, 21 | H264_NAL_AUD = 9, 22 | H264_NAL_EOSEQ = 10, 23 | H264_NAL_EOSTREAM = 11, 24 | H264_NAL_FILLER = 12, 25 | H264_NAL_RSV13 = 13, 26 | H264_NAL_RSV14 = 14, 27 | H264_NAL_RSV15 = 15, 28 | H264_NAL_RSV16 = 16, 29 | H264_NAL_RSV17 = 17, 30 | H264_NAL_RSV18 = 18, 31 | H264_NAL_RSV19 = 19, 32 | H264_NAL_RSV20 = 20, 33 | H264_NAL_RSV21 = 21, 34 | H264_NAL_RSV22 = 22, 35 | H264_NAL_RSV23 = 23, 36 | H264_NAL_UNSPEC24 = 24, 37 | H264_NAL_UNSPEC25 = 25, 38 | H264_NAL_UNSPEC26 = 26, 39 | H264_NAL_UNSPEC27 = 27, 40 | H264_NAL_UNSPEC28 = 28, 41 | H264_NAL_UNSPEC29 = 29, 42 | H264_NAL_UNSPEC30 = 30, 43 | H264_NAL_UNSPEC31 = 31 44 | }; 45 | 46 | class H264NalUnit : public NalUnit { 47 | public: 48 | uint8_t GetType() override; 49 | uint8_t GetRefIdc(); 50 | H264NalType GetH264Type(); 51 | size_t GetHeader(uint8_t **data) override; 52 | size_t CopyHeader(uint8_t *start, size_t size) override; 53 | size_t GetHeaderSize() override; 54 | size_t GetBody(uint8_t **data) override; 55 | size_t CopyBody(uint8_t *start, size_t size, size_t skip = 0) override; 56 | size_t GetBodySize() override; 57 | bool IsIdrFrame() override; 58 | bool IsFrame() override; 59 | static NalUnit *GetNalUnit(const uint8_t *data, size_t dataSize); 60 | 61 | private: 62 | H264NalUnit(const uint8_t *data, size_t dataSize); 63 | }; 64 | 65 | } 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /rtsp-server/xop/H264Source.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-16 3 | 4 | #ifndef XOP_H264_SOURCE_H 5 | #define XOP_H264_SOURCE_H 6 | 7 | #include "MediaSource.h" 8 | #include "rtp.h" 9 | #include "NalUnit.h" 10 | 11 | namespace xop { 12 | 13 | class H264Source : public MediaSource { 14 | public: 15 | static H264Source *CreateNew(std::vector extraData, 16 | uint32_t framerate = 25); 17 | 18 | static H264Source *CreateNew(std::vector sps, 19 | std::vector pps, 20 | uint32_t framerate = 25); 21 | ~H264Source() override; 22 | 23 | void SetFramerate(const uint32_t framerate) { framerate_ = framerate; } 24 | 25 | uint32_t GetFramerate() const { return framerate_; } 26 | 27 | std::string GetMediaDescription(uint16_t port) override; 28 | 29 | std::string GetAttribute() override; 30 | 31 | bool HandleFrame(MediaChannelId channel_id, AVFrame frame) override; 32 | 33 | static uint32_t GetTimestamp(); 34 | 35 | private: 36 | H264Source(std::vector sps, std::vector pps, 37 | uint32_t framerate); 38 | 39 | static FrameType GetRtpFrameType(std::shared_ptr nalUnit); 40 | 41 | uint32_t framerate_ = 25; 42 | 43 | //uint32_t profileLevelId_; 44 | 45 | std::vector sps_; 46 | 47 | std::vector pps_; 48 | }; 49 | 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /rtsp-server/xop/H265NalUnit.cpp: -------------------------------------------------------------------------------- 1 | #include "H265NalUnit.h" 2 | 3 | using namespace xop; 4 | 5 | H265NalUnit::H265NalUnit(const uint8_t *data, size_t dataSize) : NalUnit(data, dataSize) 6 | { 7 | } 8 | 9 | uint8_t H265NalUnit::GetType() 10 | { 11 | uint8_t *data; 12 | if (GetHeader(&data) == H265_NALU_HEADER_SIZE) { 13 | return (data[0] & 0x7e) >> 1; 14 | } 15 | return 0; 16 | } 17 | 18 | uint8_t H265NalUnit::GetLayerId() 19 | { 20 | uint8_t *data; 21 | if (GetHeader(&data) == H265_NALU_HEADER_SIZE) { 22 | return (data[0] << 5 & 0x20) | (data[1] >> 3 & 0x1f); 23 | } 24 | return 0; 25 | } 26 | 27 | uint8_t H265NalUnit::GetTemporalId() 28 | { 29 | uint8_t *data; 30 | if (GetHeader(&data) == H265_NALU_HEADER_SIZE) { 31 | return data[1] & 0x07; 32 | } 33 | return 0; 34 | } 35 | 36 | H265NalType H265NalUnit::GetH265Type() 37 | { 38 | return static_cast(GetType()); 39 | } 40 | 41 | size_t H265NalUnit::GetHeader(uint8_t **data) 42 | { 43 | if (GetData(data) >= H265_NALU_HEADER_SIZE) 44 | return H265_NALU_HEADER_SIZE; 45 | return 0; 46 | } 47 | 48 | size_t H265NalUnit::CopyHeader(uint8_t *start, size_t size) 49 | { 50 | if (size > H265_NALU_HEADER_SIZE) { 51 | size = H265_NALU_HEADER_SIZE; 52 | } 53 | return CopyData(start, size); 54 | } 55 | 56 | size_t H265NalUnit::GetHeaderSize() 57 | { 58 | const auto size = GetSize(); 59 | if (size > H265_NALU_HEADER_SIZE) 60 | return H265_NALU_HEADER_SIZE; 61 | return 0; 62 | } 63 | 64 | size_t H265NalUnit::GetBody(uint8_t **data) 65 | { 66 | const auto size = GetData(data); 67 | if (size > H265_NALU_HEADER_SIZE) { 68 | *data += H265_NALU_HEADER_SIZE; 69 | return size - H265_NALU_HEADER_SIZE; 70 | } 71 | return 0; 72 | } 73 | 74 | size_t H265NalUnit::CopyBody(uint8_t *start, size_t size, size_t skip) 75 | { 76 | skip += H265_NALU_HEADER_SIZE; 77 | return CopyData(start, size, skip); 78 | } 79 | 80 | size_t H265NalUnit::GetBodySize() 81 | { 82 | const auto size = GetSize(); 83 | if (size > H265_NALU_HEADER_SIZE) 84 | return size - H265_NALU_HEADER_SIZE; 85 | return 0; 86 | } 87 | 88 | bool H265NalUnit::IsIdrFrame() 89 | { 90 | const auto type = GetH265Type(); 91 | return type == H265NalType::H265_NAL_IDR_N_LP || type == H265NalType::H265_NAL_IDR_W_RADL; 92 | } 93 | 94 | bool H265NalUnit::IsFrame() 95 | { 96 | const auto type = GetH265Type(); 97 | return type <= H265NalType::H265_NAL_CRA_NUT; 98 | } 99 | 100 | NalUnit* H265NalUnit::GetNalUnit(const uint8_t *data, size_t dataSize) 101 | { 102 | return new H265NalUnit(data, dataSize); 103 | } 104 | 105 | -------------------------------------------------------------------------------- /rtsp-server/xop/H265NalUnit.h: -------------------------------------------------------------------------------- 1 | #ifndef XOP_H265_NALUNIT_H 2 | #define XOP_H265_NALUNIT_H 3 | 4 | #include 5 | #include "NalUnit.h" 6 | 7 | #define H265_NALU_HEADER_SIZE 2 8 | 9 | namespace xop { 10 | 11 | enum class H265NalType: uint8_t { 12 | H265_NAL_TRAIL_N = 0, 13 | H265_NAL_TRAIL_R = 1, 14 | H265_NAL_TSA_N = 2, 15 | H265_NAL_TSA_R = 3, 16 | H265_NAL_STSA_N = 4, 17 | H265_NAL_STSA_R = 5, 18 | H265_NAL_RADL_N = 6, 19 | H265_NAL_RADL_R = 7, 20 | H265_NAL_RASL_N = 8, 21 | H265_NAL_RASL_R = 9, 22 | H265_NAL_VCL_N10 = 10, 23 | H265_NAL_VCL_R11 = 11, 24 | H265_NAL_VCL_N12 = 12, 25 | H265_NAL_VCL_R13 = 13, 26 | H265_NAL_VCL_N14 = 14, 27 | H265_NAL_VCL_R15 = 15, 28 | H265_NAL_BLA_W_LP = 16, 29 | H265_NAL_BLA_W_RADL = 17, 30 | H265_NAL_BLA_N_LP = 18, 31 | H265_NAL_IDR_W_RADL = 19, 32 | H265_NAL_IDR_N_LP = 20, 33 | H265_NAL_CRA_NUT = 21, 34 | H265_NAL_RSV_IRAP_VCL22 = 22, 35 | H265_NAL_RSV_IRAP_VCL23 = 23, 36 | H265_NAL_RSV_VCL24 = 24, 37 | H265_NAL_RSV_VCL25 = 25, 38 | H265_NAL_RSV_VCL26 = 26, 39 | H265_NAL_RSV_VCL27 = 27, 40 | H265_NAL_RSV_VCL28 = 28, 41 | H265_NAL_RSV_VCL29 = 29, 42 | H265_NAL_RSV_VCL30 = 30, 43 | H265_NAL_RSV_VCL31 = 31, 44 | H265_NAL_VPS = 32, 45 | H265_NAL_SPS = 33, 46 | H265_NAL_PPS = 34, 47 | H265_NAL_AUD = 35, 48 | H265_NAL_EOS_NUT = 36, 49 | H265_NAL_EOB_NUT = 37, 50 | H265_NAL_FD_NUT = 38, 51 | H265_NAL_SEI_PREFIX = 39, 52 | H265_NAL_SEI_SUFFIX = 40, 53 | H265_NAL_RSV_NVCL41 = 41, 54 | H265_NAL_RSV_NVCL42 = 42, 55 | H265_NAL_RSV_NVCL43 = 43, 56 | H265_NAL_RSV_NVCL44 = 44, 57 | H265_NAL_RSV_NVCL45 = 45, 58 | H265_NAL_RSV_NVCL46 = 46, 59 | H265_NAL_RSV_NVCL47 = 47, 60 | H265_NAL_UNSPEC48 = 48, 61 | H265_NAL_UNSPEC49 = 49, 62 | H265_NAL_UNSPEC50 = 50, 63 | H265_NAL_UNSPEC51 = 51, 64 | H265_NAL_UNSPEC52 = 52, 65 | H265_NAL_UNSPEC53 = 53, 66 | H265_NAL_UNSPEC54 = 54, 67 | H265_NAL_UNSPEC55 = 55, 68 | H265_NAL_UNSPEC56 = 56, 69 | H265_NAL_UNSPEC57 = 57, 70 | H265_NAL_UNSPEC58 = 58, 71 | H265_NAL_UNSPEC59 = 59, 72 | H265_NAL_UNSPEC60 = 60, 73 | H265_NAL_UNSPEC61 = 61, 74 | H265_NAL_UNSPEC62 = 62, 75 | H265_NAL_UNSPEC63 = 63, 76 | }; 77 | 78 | class H265NalUnit : public NalUnit { 79 | public: 80 | uint8_t GetType() override; 81 | uint8_t GetLayerId(); 82 | uint8_t GetTemporalId(); 83 | H265NalType GetH265Type(); 84 | size_t GetHeader(uint8_t **data) override; 85 | size_t CopyHeader(uint8_t *start, size_t size) override; 86 | size_t GetHeaderSize() override; 87 | size_t GetBody(uint8_t **data) override; 88 | size_t CopyBody(uint8_t *start, size_t size, size_t skip = 0) override; 89 | size_t GetBodySize() override; 90 | bool IsIdrFrame() override; 91 | bool IsFrame() override; 92 | static NalUnit *GetNalUnit(const uint8_t *data, size_t dataSize); 93 | 94 | private: 95 | H265NalUnit(const uint8_t *data, size_t dataSize); 96 | 97 | }; 98 | 99 | } 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /rtsp-server/xop/H265Source.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-16 3 | 4 | #ifndef XOP_H265_SOURCE_H 5 | #define XOP_H265_SOURCE_H 6 | 7 | #include "MediaSource.h" 8 | #include "rtp.h" 9 | #include "NalUnit.h" 10 | 11 | namespace xop { 12 | 13 | class H265Source : public MediaSource { 14 | public: 15 | static H265Source * 16 | CreateNew(std::vector extraData, 17 | std::vector sei = std::vector(), 18 | uint32_t framerate = 25); 19 | 20 | static H265Source * 21 | CreateNew(std::vector vps, std::vector sps, 22 | std::vector pps, 23 | std::vector sei = std::vector(), 24 | uint32_t framerate = 25); 25 | 26 | ~H265Source() override; 27 | 28 | void SetFramerate(const uint32_t framerate) { framerate_ = framerate; } 29 | 30 | uint32_t GetFramerate() const { return framerate_; } 31 | 32 | std::string GetMediaDescription(uint16_t port = 0) override; 33 | 34 | std::string GetAttribute() override; 35 | 36 | bool HandleFrame(MediaChannelId channelId, AVFrame frame) override; 37 | 38 | static uint32_t GetTimestamp(); 39 | 40 | private: 41 | H265Source(std::vector vps, std::vector sps, 42 | std::vector pps, std::vector sei, 43 | uint32_t framerate); 44 | 45 | static FrameType GetRtpFrameType(std::shared_ptr nalUnit); 46 | 47 | uint32_t framerate_ = 25; 48 | 49 | std::vector vps_; 50 | 51 | std::vector sps_; 52 | 53 | std::vector pps_; 54 | 55 | std::vector sei_; 56 | }; 57 | 58 | } 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /rtsp-server/xop/MacMd5.cpp: -------------------------------------------------------------------------------- 1 | #include "MacMd5.h" 2 | 3 | #if defined(__APPLE__) || defined(__MACH__) 4 | #include 5 | #endif 6 | 7 | using namespace xop; 8 | 9 | MacMd5::MacMd5() : Md5() {} 10 | 11 | MacMd5::~MacMd5() = default; 12 | 13 | void MacMd5::GetMd5Hash(const unsigned char *data, size_t dataSize, 14 | unsigned char *outHash) 15 | { 16 | #if defined(__APPLE__) || defined(__MACH__) 17 | CC_MD5(data, (CC_LONG)dataSize, outHash); 18 | #endif 19 | } 20 | -------------------------------------------------------------------------------- /rtsp-server/xop/MacMd5.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOP_MACMD5_H 2 | #define _XOP_MACMD5_H 3 | 4 | #include "Md5.h" 5 | 6 | namespace xop { 7 | class MacMd5 : public Md5 { 8 | public: 9 | MacMd5(); 10 | ~MacMd5() override; 11 | 12 | void GetMd5Hash(const unsigned char *data, size_t dataSize, 13 | unsigned char *outHash) override; 14 | }; 15 | } 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /rtsp-server/xop/Md5.cpp: -------------------------------------------------------------------------------- 1 | #include "Md5.h" 2 | 3 | using namespace xop; 4 | 5 | Md5::Md5() = default; 6 | 7 | Md5::~Md5() = default; 8 | 9 | void Md5::GetMd5Hash(const unsigned char *data, size_t dataSize, 10 | unsigned char *outHash) 11 | { 12 | } 13 | 14 | void Md5::GetMd5Hash(const std::string &str, unsigned char *outHash) 15 | { 16 | GetMd5Hash(reinterpret_cast(str.c_str()), 17 | str.length(), outHash); 18 | } 19 | 20 | std::string Md5::GetMd5HashString(const unsigned char *data, 21 | const size_t dataSize) 22 | { 23 | unsigned char hash[MD5_HASH_LENGTH]; 24 | GetMd5Hash(data, dataSize, hash); 25 | char hashStr[2 * MD5_HASH_LENGTH + 1]{}; 26 | for (size_t i = 0; i < MD5_HASH_LENGTH; i++) { 27 | hashStr[2 * i] = hex_value_[((hash[i] >> 4) & 0xF)]; 28 | hashStr[2 * i + 1] = hex_value_[(hash[i]) & 0x0F]; 29 | } 30 | hashStr[2 * MD5_HASH_LENGTH] = '\0'; 31 | return hashStr; 32 | } 33 | 34 | std::string Md5::GetMd5HashString(const std::string &str) 35 | { 36 | return GetMd5HashString( 37 | reinterpret_cast(str.c_str()), 38 | str.length()); 39 | } 40 | -------------------------------------------------------------------------------- /rtsp-server/xop/Md5.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOP_MD5_H 2 | #define _XOP_MD5_H 3 | 4 | #include 5 | 6 | #define MD5_HASH_LENGTH 16 7 | 8 | namespace xop { 9 | class Md5 { 10 | public: 11 | Md5(); 12 | virtual ~Md5(); 13 | 14 | virtual void GetMd5Hash(const unsigned char *data, size_t dataSize, 15 | unsigned char *outHash); 16 | void GetMd5Hash(const std::string &str, unsigned char *outHash); 17 | std::string GetMd5HashString(const unsigned char *data, 18 | size_t dataSize); 19 | std::string GetMd5HashString(const std::string &str); 20 | 21 | private: 22 | const char hex_value_[16] = {'0', '1', '2', '3', '4', '5', '6', '7', 23 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 24 | }; 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /rtsp-server/xop/MediaSource.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-6-8 3 | 4 | #ifndef XOP_MEDIA_SOURCE_H 5 | #define XOP_MEDIA_SOURCE_H 6 | 7 | #include "media.h" 8 | #include "rtp.h" 9 | #include 10 | #include 11 | #include 12 | 13 | namespace xop { 14 | 15 | class MediaSource { 16 | public: 17 | using SendFrameCallback = 18 | std::function; 19 | 20 | MediaSource() = default; 21 | virtual ~MediaSource() = default; 22 | 23 | virtual MediaType GetMediaType() const { return media_type_; } 24 | 25 | virtual std::string GetMediaDescription(uint16_t port = 0) = 0; 26 | 27 | virtual std::string GetAttribute() = 0; 28 | 29 | virtual bool HandleFrame(MediaChannelId channelId, AVFrame frame) = 0; 30 | virtual void SetSendFrameCallback(const SendFrameCallback callback) 31 | { 32 | send_frame_callback_ = callback; 33 | } 34 | 35 | virtual uint32_t GetPayloadType() const { return payload_; } 36 | 37 | virtual uint32_t GetClockRate() const { return clock_rate_; } 38 | 39 | protected: 40 | MediaType media_type_ = MediaType::NONE; 41 | uint32_t payload_ = 0; 42 | uint32_t clock_rate_ = 0; 43 | SendFrameCallback send_frame_callback_; 44 | }; 45 | 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /rtsp-server/xop/Nal.cpp: -------------------------------------------------------------------------------- 1 | #include "Nal.h" 2 | 3 | using namespace std; 4 | using namespace xop; 5 | 6 | bool NalHelper::NalUnitWhile(const uint8_t *data, size_t dataSize, 7 | NalUnitWhileCallback callback) 8 | { 9 | if (dataSize == 0) 10 | return true; 11 | const uint8_t *end = data + dataSize; 12 | const uint8_t *nal_start = FindStartCode(data, end); 13 | 14 | while (true) { 15 | while (nal_start < end && !*nal_start++) 16 | ; 17 | 18 | if (nal_start == end) 19 | break; 20 | 21 | const uint8_t *nalEnd = FindStartCode(nal_start, end); 22 | 23 | if (!callback(const_cast(nal_start), 24 | nalEnd - nal_start)) 25 | return false; 26 | 27 | nal_start = nalEnd; 28 | } 29 | return true; 30 | } 31 | 32 | uint32_t NalHelper::GetNalUnitCount(const uint8_t *data, size_t dataSize) 33 | { 34 | uint32_t count = 0; 35 | NalUnitWhile(data, dataSize, [&count](const uint8_t *, size_t) { 36 | count++; 37 | return true; 38 | }); 39 | return count; 40 | } 41 | 42 | const uint8_t *NalHelper::FindStartCode(const uint8_t *p, const uint8_t *end) 43 | { 44 | const uint8_t *out = FFmpegFindStartcodeInternal(p, end); 45 | if (p < out && out < end && !out[-1]) 46 | --out; 47 | return out; 48 | } 49 | 50 | /* NOTE: I noticed that FFmpeg does some unusual special handling of certain 51 | * scenarios that I was unaware of, so instead of just searching for {0, 0, 1} 52 | * we'll just use the code from FFmpeg - http://www.ffmpeg.org/ */ 53 | const uint8_t *NalHelper::FFmpegFindStartcodeInternal(const uint8_t *p, 54 | const uint8_t *end) 55 | { 56 | const uint8_t *a = p + 4 - (reinterpret_cast(p) & 3); 57 | 58 | for (end -= 3; p < a && p < end; p++) { 59 | if (p[0] == 0 && p[1] == 0 && p[2] == 1) 60 | return p; 61 | } 62 | 63 | for (end -= 3; p < end; p += 4) { 64 | const uint32_t x = *reinterpret_cast(p); 65 | 66 | if (x - 0x01010101 & ~x & 0x80808080) { 67 | if (p[1] == 0) { 68 | if (p[0] == 0 && p[2] == 1) 69 | return p; 70 | if (p[2] == 0 && p[3] == 1) 71 | return p + 1; 72 | } 73 | 74 | if (p[3] == 0) { 75 | if (p[2] == 0 && p[4] == 1) 76 | return p + 2; 77 | if (p[4] == 0 && p[5] == 1) 78 | return p + 3; 79 | } 80 | } 81 | } 82 | 83 | for (end += 3; p < end; p++) { 84 | if (p[0] == 0 && p[1] == 0 && p[2] == 1) 85 | return p; 86 | } 87 | 88 | return end + 3; 89 | } 90 | -------------------------------------------------------------------------------- /rtsp-server/xop/Nal.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOP_NAL_H 2 | #define _XOP_NAL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "NalUnit.h" 13 | 14 | namespace xop { 15 | 16 | template>> 17 | class Nal { 18 | public: 19 | Nal(const std::vector &data); 20 | Nal(const uint8_t *data, size_t dataSize); 21 | size_t GetSize() const; 22 | size_t GetData(uint8_t **data) const; 23 | std::vector GetData() const; 24 | std::shared_ptr operator[](size_t index); 25 | size_t GetCount(); 26 | size_t CopyData(uint8_t *start, size_t size) const; 27 | std::shared_ptr GetNalUnitByType(uint8_t nalUnitType); 28 | 29 | private: 30 | const uint8_t *data_; 31 | size_t data_size_; 32 | std::vector> nal_unit_list_; 33 | }; 34 | 35 | class NalHelper { 36 | public: 37 | using NalUnitWhileCallback = 38 | std::function; 39 | static bool NalUnitWhile(const uint8_t *data, size_t dataSize, 40 | NalUnitWhileCallback callback); 41 | static uint32_t GetNalUnitCount(const uint8_t *data, size_t dataSize); 42 | static const uint8_t *FindStartCode(const uint8_t *p, 43 | const uint8_t *end); 44 | 45 | private: 46 | NalHelper() = default; 47 | static const uint8_t *FFmpegFindStartcodeInternal(const uint8_t *p, 48 | const uint8_t *end); 49 | }; 50 | 51 | template 52 | Nal::Nal(const std::vector &data) : Nal(data.data(), data.size()) 53 | { 54 | } 55 | 56 | template 57 | Nal::Nal(const uint8_t *data, size_t dataSize) 58 | : data_(data), data_size_(dataSize) 59 | { 60 | nal_unit_list_.resize(NalHelper::GetNalUnitCount(data, dataSize)); 61 | auto it = nal_unit_list_.begin(); 62 | auto end = nal_unit_list_.end(); 63 | NalHelper::NalUnitWhile( 64 | data, dataSize, 65 | [&it, end](const uint8_t *unitData, size_t unitDataSize) { 66 | *it = std::shared_ptr(static_cast( 67 | T::GetNalUnit(unitData, unitDataSize))); 68 | if (it++ == end) 69 | return false; 70 | return true; 71 | }); 72 | } 73 | 74 | template size_t Nal::GetSize() const 75 | { 76 | return data_size_; 77 | } 78 | 79 | template size_t Nal::GetData(uint8_t **data) const 80 | { 81 | *data = const_cast(data_); 82 | return data_size_; 83 | } 84 | 85 | template std::vector Nal::GetData() const 86 | { 87 | uint8_t *data = nullptr; 88 | const auto size = GetData(&data); 89 | return std::vector(data, data + size); 90 | } 91 | 92 | template 93 | std::shared_ptr Nal::operator[](size_t index) 94 | { 95 | return nal_unit_list_[index]; 96 | } 97 | 98 | template size_t Nal::GetCount() 99 | { 100 | return nal_unit_list_.size(); 101 | } 102 | 103 | template 104 | size_t Nal::CopyData(uint8_t *start, size_t size) const 105 | { 106 | if (size > data_size_) 107 | size = data_size_; 108 | memcpy(start, data_, size); 109 | return size; 110 | } 111 | 112 | template 113 | std::shared_ptr Nal::GetNalUnitByType(uint8_t nalUnitType) 114 | { 115 | for (auto iter = nal_unit_list_.begin(); iter != nal_unit_list_.end(); 116 | ++iter) { 117 | if (iter->get()->GetType() == nalUnitType) 118 | return *iter; 119 | } 120 | return nullptr; 121 | } 122 | 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /rtsp-server/xop/NalUnit.cpp: -------------------------------------------------------------------------------- 1 | #include "NalUnit.h" 2 | #include 3 | 4 | using namespace xop; 5 | using namespace std; 6 | 7 | NalUnit::NalUnit(const uint8_t *data, size_t dataSize) 8 | : data_(data), data_size_(dataSize) 9 | { 10 | } 11 | 12 | size_t NalUnit::GetData(uint8_t **data) const 13 | { 14 | *data = const_cast(data_); 15 | return data_size_; 16 | } 17 | 18 | vector NalUnit::GetData() const 19 | { 20 | uint8_t *data = nullptr; 21 | const auto size = GetData(&data); 22 | return vector(data, data + size); 23 | } 24 | 25 | size_t NalUnit::CopyData(uint8_t *start, size_t size, size_t skip) const 26 | { 27 | if (skip > data_size_) 28 | return 0; 29 | if (skip + size > data_size_) 30 | size = data_size_ - skip; 31 | memcpy(start, data_ + skip, size); 32 | return size; 33 | } 34 | 35 | size_t NalUnit::GetSize() const 36 | { 37 | return data_size_; 38 | } 39 | 40 | std::vector NalUnit::GetHeader() 41 | { 42 | uint8_t *data = nullptr; 43 | const auto size = GetHeader(&data); 44 | return vector(data, data + size); 45 | } 46 | 47 | std::vector NalUnit::GetBody() 48 | { 49 | uint8_t *data = nullptr; 50 | const auto size = GetBody(&data); 51 | return vector(data, data + size); 52 | } 53 | 54 | NalUnit *NalUnit::GetNalUnit(const uint8_t *, size_t) 55 | { 56 | return nullptr; 57 | } 58 | -------------------------------------------------------------------------------- /rtsp-server/xop/NalUnit.h: -------------------------------------------------------------------------------- 1 | #ifndef _XOP_NALUNIT_H 2 | #define _XOP_NALUNIT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace xop { 9 | class NalUnit { 10 | public: 11 | size_t GetData(uint8_t **data) const; 12 | std::vector GetData() const; 13 | size_t CopyData(uint8_t *start, size_t size, size_t skip = 0) const; 14 | size_t GetSize() const; 15 | virtual uint8_t GetType() = 0; 16 | virtual size_t GetHeader(uint8_t **data) = 0; 17 | std::vector GetHeader(); 18 | virtual size_t CopyHeader(uint8_t *start, size_t size) = 0; 19 | virtual size_t GetHeaderSize() = 0; 20 | virtual size_t GetBody(uint8_t **data) = 0; 21 | std::vector GetBody(); 22 | virtual size_t CopyBody(uint8_t *start, size_t size, size_t skip = 0) = 0; 23 | virtual size_t GetBodySize() = 0; 24 | virtual bool IsIdrFrame() = 0; 25 | virtual bool IsFrame() = 0; 26 | static NalUnit* GetNalUnit(const uint8_t *data, size_t dataSize); 27 | 28 | protected: 29 | NalUnit(const uint8_t *data, size_t dataSize); 30 | 31 | private: 32 | const uint8_t *data_; 33 | size_t data_size_; 34 | }; 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /rtsp-server/xop/RtpConnection.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-6-8 3 | // Scott Xu 4 | // 2020-12-5 Add IPv6 Support. 5 | 6 | #ifndef XOP_RTP_CONNECTION_H 7 | #define XOP_RTP_CONNECTION_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "rtp.h" 15 | #include "media.h" 16 | #include "net/Socket.h" 17 | 18 | namespace xop { 19 | 20 | class RtspConnection; 21 | class TaskScheduler; 22 | 23 | class RtpConnection { 24 | public: 25 | RtpConnection(std::weak_ptr rtsp_connection, 26 | uint8_t max_channel_count); 27 | 28 | RtpConnection(uint8_t max_channel_count, 29 | std::weak_ptr task_scheduler, 30 | bool ipv6 = false); 31 | 32 | virtual ~RtpConnection(); 33 | 34 | void SetClockRate(MediaChannelId channel_id, const uint32_t clock_rate) 35 | { 36 | media_channel_info_[static_cast(channel_id)] 37 | .clock_rate = clock_rate; 38 | } 39 | 40 | void SetPayloadType(MediaChannelId channel_id, const uint32_t payload) 41 | { 42 | media_channel_info_[static_cast(channel_id)] 43 | .rtp_header.payload = payload; 44 | } 45 | 46 | bool SetupRtpOverTcp(MediaChannelId channel_id, uint8_t rtp_channel, 47 | uint8_t rtcp_channel); 48 | bool SetupRtpOverUdp(MediaChannelId channel_id, uint16_t rtp_port, 49 | uint16_t rtcp_port); 50 | bool SetupRtpOverMulticast(MediaChannelId channel_id, 51 | const std::string &ip, uint16_t port); 52 | 53 | uint16_t GetRtpSessionId() const 54 | { 55 | return static_cast(reinterpret_cast(this)); 56 | } 57 | 58 | uint16_t GetRtpPort(MediaChannelId channel_id) const 59 | { 60 | return local_rtp_port_[static_cast(channel_id)]; 61 | } 62 | 63 | uint16_t GetRtcpPort(MediaChannelId channel_id) const 64 | { 65 | return local_rtcp_port_[static_cast(channel_id)]; 66 | } 67 | 68 | SOCKET GetRtcpfd(MediaChannelId channel_id) const 69 | { 70 | return rtcpfd_[static_cast(channel_id)]; 71 | } 72 | 73 | bool IsMulticast() const { return is_multicast_; } 74 | 75 | bool IsSetup(MediaChannelId channel_id) const 76 | { 77 | return media_channel_info_[static_cast(channel_id)] 78 | .is_setup; 79 | } 80 | 81 | std::string GetMulticastIp(MediaChannelId channel_id); 82 | 83 | void Play(); 84 | void Record(); 85 | void Teardown(); 86 | 87 | std::string GetRtpInfo(const std::string &rtsp_url) const; 88 | int SendRtpPacket(MediaChannelId channel_id, const RtpPacket &pkt); 89 | 90 | bool IsClosed() const { return is_closed_; } 91 | 92 | int GetId() const; 93 | 94 | bool HasKeyFrame() const { return has_key_frame_; } 95 | 96 | private: 97 | friend class RtspConnection; 98 | friend class MediaSession; 99 | void SetFrameType(FrameType frameType = FrameType::NONE); 100 | void SetRtpHeader(MediaChannelId channel_id, const RtpPacket &pkt); 101 | int SendRtpOverTcp(MediaChannelId channel_id, 102 | const RtpPacket &pkt) const; 103 | int SendRtpOverUdp(MediaChannelId channel_id, const RtpPacket &pkt); 104 | 105 | uint8_t max_channel_count_ = 0; 106 | 107 | std::weak_ptr rtsp_connection_; 108 | std::weak_ptr task_scheduler_; 109 | 110 | TransportMode transport_mode_; 111 | bool is_multicast_ = false; 112 | 113 | bool is_closed_ = false; 114 | bool has_key_frame_ = false; 115 | 116 | FrameType frame_type_ = FrameType::NONE; 117 | std::vector local_rtp_port_; 118 | std::vector local_rtcp_port_; 119 | std::vector rtpfd_; 120 | std::vector rtcpfd_; 121 | 122 | sockaddr_in6 peer_addr_{}; 123 | std::vector peer_rtp_addr_; 124 | std::vector peer_rtcp_sddr_; 125 | std::vector media_channel_info_; 126 | 127 | bool ipv6_; 128 | }; 129 | 130 | } 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /rtsp-server/xop/RtspConnection.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-6-8 3 | // Scott Xu 4 | // 2020-12-5 Add IPv6 Support. 5 | 6 | #ifndef _RTSP_CONNECTION_H 7 | #define _RTSP_CONNECTION_H 8 | 9 | #include "net/TcpConnection.h" 10 | #include "RtpConnection.h" 11 | #include "RtspMessage.h" 12 | #include "DigestAuthentication.h" 13 | #include "rtsp.h" 14 | #include 15 | #include 16 | #include 17 | 18 | namespace xop { 19 | 20 | class RtspConnection : public TcpConnection { 21 | public: 22 | using CloseCallback = std::function; 23 | 24 | enum class ConnectionMode { 25 | RTSP_SERVER, 26 | RTSP_PUSHER, 27 | //RTSP_CLIENT, 28 | }; 29 | 30 | enum class ConnectionState { START_CONNECT, START_PLAY, START_PUSH }; 31 | 32 | RtspConnection() = delete; 33 | RtspConnection(SOCKET sockfd, 34 | std::shared_ptr task_scheduler, 35 | const std::shared_ptr &rtsp); 36 | ~RtspConnection() override; 37 | 38 | MediaSessionId GetMediaSessionId() const { return session_id_; } 39 | 40 | void KeepAlive() { ++alive_count_; } 41 | 42 | bool IsAlive() const 43 | { 44 | if (IsClosed()) { 45 | return false; 46 | } 47 | 48 | if (rtp_conn_ != nullptr) { 49 | if (rtp_conn_->IsMulticast()) { 50 | return true; 51 | } 52 | } 53 | 54 | return (alive_count_ > 0); 55 | } 56 | 57 | void ResetAliveCount() { alive_count_ = 0; } 58 | 59 | int GetId() const { return GetTaskScheduler()->GetId(); } 60 | 61 | bool IsPlay() const 62 | { 63 | return conn_state_ == ConnectionState::START_PLAY; 64 | } 65 | 66 | bool IsRecord() const 67 | { 68 | return conn_state_ == ConnectionState::START_PUSH; 69 | } 70 | 71 | private: 72 | friend class RtpConnection; 73 | friend class MediaSession; 74 | friend class RtspServer; 75 | friend class RtspPusher; 76 | 77 | bool OnRead(BufferReader &buffer); 78 | void OnClose(); 79 | void HandleRtcp(SOCKET sockfd); 80 | static void HandleRtcp(BufferReader &buffer); 81 | bool HandleRtspRequest(BufferReader &buffer); 82 | bool HandleRtspResponse(BufferReader &buffer); 83 | 84 | void SendRtspMessage(std::shared_ptr buf, uint32_t size); 85 | 86 | void HandleCmdOption(); 87 | void HandleCmdDescribe(); 88 | void HandleCmdSetup(); 89 | void HandleCmdPlay(); 90 | void HandleCmdTeardown(); 91 | void HandleCmdGetParamter(); 92 | bool HandleAuthentication(); 93 | 94 | void SendOptions(ConnectionMode mode = ConnectionMode::RTSP_SERVER); 95 | void SendDescribe(); 96 | void SendAnnounce(); 97 | void SendSetup(); 98 | void HandleRecord(); 99 | 100 | std::atomic_int alive_count_; 101 | std::weak_ptr rtsp_; 102 | 103 | ConnectionMode conn_mode_ = ConnectionMode::RTSP_SERVER; 104 | ConnectionState conn_state_ = ConnectionState::START_CONNECT; 105 | MediaSessionId session_id_ = 0; 106 | 107 | bool has_auth_ = true; 108 | std::string nonce_; 109 | std::unique_ptr auth_info_; 110 | 111 | //std::shared_ptr rtp_channel_; 112 | std::map> rtcp_channels_; 113 | std::unique_ptr rtsp_request_; 114 | std::unique_ptr rtsp_response_; 115 | std::shared_ptr rtp_conn_; 116 | }; 117 | 118 | } 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /rtsp-server/xop/RtspPusher.cpp: -------------------------------------------------------------------------------- 1 | #include "RtspPusher.h" 2 | #include "RtspConnection.h" 3 | #include "net/Logger.h" 4 | #include "net/TcpSocket.h" 5 | #include "net/Timestamp.h" 6 | #include 7 | 8 | using namespace xop; 9 | 10 | RtspPusher::RtspPusher(EventLoop *event_loop) : event_loop_(event_loop) {} 11 | 12 | RtspPusher::~RtspPusher() 13 | { 14 | this->Close(); 15 | } 16 | 17 | std::shared_ptr RtspPusher::Create(EventLoop *loop) 18 | { 19 | std::shared_ptr pusher(new RtspPusher(loop)); 20 | return pusher; 21 | } 22 | 23 | void RtspPusher::AddSession(MediaSession *session) 24 | { 25 | std::lock_guard locker(mutex_); 26 | media_session_.reset(session); 27 | } 28 | 29 | void RtspPusher::RemoveSession(MediaSessionId session_id) 30 | { 31 | std::lock_guard locker(mutex_); 32 | media_session_ = nullptr; //TODO 33 | } 34 | 35 | MediaSession::Ptr RtspPusher::LookMediaSession(MediaSessionId session_id) 36 | { 37 | return media_session_; //TODO 38 | } 39 | 40 | int RtspPusher::OpenUrl(const std::string &url, const int msec) 41 | { 42 | std::lock_guard lock(mutex_); 43 | 44 | static Timestamp timestamp; 45 | int timeout = msec; 46 | if (timeout <= 0) { 47 | timeout = 10000; 48 | } 49 | 50 | timestamp.reset(); 51 | 52 | if (!this->ParseRtspUrl(url)) { 53 | LOG_ERROR("rtsp url(%s) was illegal.\n", url.c_str()); 54 | return -1; 55 | } 56 | 57 | if (rtsp_conn_ != nullptr) { 58 | std::shared_ptr rtspConn = rtsp_conn_; 59 | SOCKET sockfd = rtspConn->GetSocket(); 60 | task_scheduler_->AddTriggerEvent( 61 | [sockfd, rtspConn]() { rtspConn->Disconnect(); }); 62 | rtsp_conn_ = nullptr; 63 | } 64 | 65 | TcpSocket tcpSocket; 66 | tcpSocket.Create(); 67 | if (!tcpSocket.Connect(rtsp_url_info_.ip, rtsp_url_info_.port, 68 | timeout)) { 69 | tcpSocket.Close(); 70 | return -1; 71 | } 72 | 73 | task_scheduler_ = event_loop_->GetTaskScheduler(); 74 | rtsp_conn_ = std::make_shared( 75 | tcpSocket.GetSocket(), task_scheduler_, shared_from_this()); 76 | event_loop_->AddTriggerEvent([this]() { 77 | rtsp_conn_->SendOptions( 78 | RtspConnection::ConnectionMode::RTSP_PUSHER); 79 | }); 80 | 81 | timeout -= static_cast(timestamp.Elapsed()); 82 | if (timeout < 0) { 83 | timeout = 1000; 84 | } 85 | 86 | do { 87 | Timer::Sleep(100); 88 | timeout -= 100; 89 | } while (!rtsp_conn_->IsRecord() && timeout > 0); 90 | 91 | if (!rtsp_conn_->IsRecord()) { 92 | std::shared_ptr rtspConn = rtsp_conn_; 93 | SOCKET sockfd = rtspConn->GetSocket(); 94 | task_scheduler_->AddTriggerEvent( 95 | [sockfd, rtspConn]() { rtspConn->Disconnect(); }); 96 | rtsp_conn_ = nullptr; 97 | return -1; 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | void RtspPusher::Close() 104 | { 105 | std::lock_guard lock(mutex_); 106 | 107 | if (rtsp_conn_ != nullptr) { 108 | std::shared_ptr rtsp_conn = rtsp_conn_; 109 | SOCKET sockfd = rtsp_conn->GetSocket(); 110 | task_scheduler_->AddTriggerEvent( 111 | [sockfd, rtsp_conn]() { rtsp_conn->Disconnect(); }); 112 | rtsp_conn_ = nullptr; 113 | } 114 | } 115 | 116 | bool RtspPusher::IsConnected() 117 | { 118 | std::lock_guard lock(mutex_); 119 | 120 | if (rtsp_conn_ != nullptr) { 121 | return (!rtsp_conn_->IsClosed()); 122 | } 123 | return false; 124 | } 125 | 126 | bool RtspPusher::PushFrame(const MediaChannelId channelId, const AVFrame &frame) 127 | { 128 | std::lock_guard locker(mutex_); 129 | if (!media_session_ || !rtsp_conn_) { 130 | return false; 131 | } 132 | 133 | return media_session_->HandleFrame(channelId, frame); 134 | } 135 | -------------------------------------------------------------------------------- /rtsp-server/xop/RtspPusher.h: -------------------------------------------------------------------------------- 1 | #ifndef XOP_RTSP_PUSHER_H 2 | #define XOP_RTSP_PUSHER_H 3 | 4 | #include 5 | #include "rtsp.h" 6 | #include "net/EventLoop.h" 7 | 8 | namespace xop { 9 | 10 | class RtspConnection; 11 | 12 | class RtspPusher : public Rtsp { 13 | public: 14 | static std::shared_ptr Create(EventLoop *loop); 15 | ~RtspPusher() override; 16 | 17 | void AddSession(MediaSession *session); 18 | void RemoveSession(MediaSessionId session_id); 19 | 20 | int OpenUrl(const std::string &url, int msec = 3000); 21 | void Close(); 22 | bool IsConnected(); 23 | 24 | bool PushFrame(MediaChannelId channelId, const AVFrame &frame); 25 | 26 | private: 27 | friend class RtspConnection; 28 | 29 | explicit RtspPusher(EventLoop *event_loop); 30 | MediaSession::Ptr LookMediaSession(MediaSessionId session_id) override; 31 | 32 | EventLoop *event_loop_ = nullptr; 33 | std::shared_ptr task_scheduler_ = nullptr; 34 | std::mutex mutex_; 35 | std::shared_ptr rtsp_conn_; 36 | std::shared_ptr media_session_; 37 | }; 38 | 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /rtsp-server/xop/RtspServer.cpp: -------------------------------------------------------------------------------- 1 | #include "RtspServer.h" 2 | #include "RtspConnection.h" 3 | 4 | using namespace xop; 5 | using namespace std; 6 | 7 | RtspServer::RtspServer(EventLoop *loop) : TcpServer(loop) {} 8 | 9 | RtspServer::~RtspServer() = default; 10 | 11 | std::shared_ptr RtspServer::Create(EventLoop *loop) 12 | { 13 | std::shared_ptr server(new RtspServer(loop)); 14 | return server; 15 | } 16 | 17 | MediaSessionId RtspServer::AddSession(MediaSession *session) 18 | { 19 | std::lock_guard locker(mutex_); 20 | 21 | if (rtsp_suffix_map_.find(session->GetRtspUrlSuffix()) != 22 | rtsp_suffix_map_.end()) 23 | return 0; 24 | 25 | if (session->task_scheduler_.lock() != nullptr) 26 | return 0; 27 | 28 | session->task_scheduler_ = event_loop_->GetTaskScheduler(); 29 | std::shared_ptr media_session(session); 30 | MediaSessionId sessionId = media_session->GetMediaSessionId(); 31 | rtsp_suffix_map_.emplace(media_session->GetRtspUrlSuffix(), sessionId); 32 | media_sessions_.emplace(sessionId, std::move(media_session)); 33 | 34 | return sessionId; 35 | } 36 | 37 | void RtspServer::RemoveSession(const MediaSessionId sessionId) 38 | { 39 | std::lock_guard locker(mutex_); 40 | 41 | if (const auto iter = media_sessions_.find(sessionId); 42 | iter != media_sessions_.end()) { 43 | rtsp_suffix_map_.erase(iter->second->GetRtspUrlSuffix()); 44 | media_sessions_.erase(sessionId); 45 | } 46 | } 47 | 48 | MediaSession::Ptr RtspServer::LookMediaSession(const std::string &suffix) 49 | { 50 | std::lock_guard locker(mutex_); 51 | 52 | if (const auto iter = rtsp_suffix_map_.find(suffix); 53 | iter != rtsp_suffix_map_.end()) { 54 | const MediaSessionId id = iter->second; 55 | return media_sessions_[id]; 56 | } 57 | 58 | return nullptr; 59 | } 60 | 61 | MediaSession::Ptr RtspServer::LookMediaSession(const MediaSessionId session_id) 62 | { 63 | std::lock_guard locker(mutex_); 64 | 65 | if (const auto iter = media_sessions_.find(session_id); 66 | iter != media_sessions_.end()) { 67 | return iter->second; 68 | } 69 | 70 | return nullptr; 71 | } 72 | 73 | bool RtspServer::PushFrame(const MediaSessionId session_id, 74 | const MediaChannelId channel_id, 75 | const AVFrame &frame) 76 | { 77 | std::shared_ptr sessionPtr; 78 | 79 | { 80 | std::lock_guard locker(mutex_); 81 | if (const auto iter = media_sessions_.find(session_id); 82 | iter != media_sessions_.end()) { 83 | sessionPtr = iter->second; 84 | } else { 85 | return false; 86 | } 87 | } 88 | 89 | if (sessionPtr != nullptr && sessionPtr->GetNumClient() != 0) { 90 | return sessionPtr->HandleFrame(channel_id, frame); 91 | } 92 | 93 | return false; 94 | } 95 | 96 | TcpConnection::Ptr RtspServer::OnConnect(SOCKET sockfd) 97 | { 98 | return std::make_shared( 99 | sockfd, event_loop_->GetTaskScheduler(), shared_from_this()); 100 | } 101 | -------------------------------------------------------------------------------- /rtsp-server/xop/RtspServer.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2020-4-2 3 | 4 | #ifndef XOP_RTSP_SERVER_H 5 | #define XOP_RTSP_SERVER_H 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "net/TcpServer.h" 12 | #include "rtsp.h" 13 | 14 | namespace xop { 15 | 16 | class RtspConnection; 17 | 18 | class RtspServer : public Rtsp, public TcpServer { 19 | public: 20 | static std::shared_ptr Create(EventLoop *loop); 21 | ~RtspServer() override; 22 | 23 | MediaSessionId AddSession(MediaSession *session); 24 | void RemoveSession(MediaSessionId sessionId); 25 | 26 | bool PushFrame(MediaSessionId session_id, MediaChannelId channel_id, 27 | const AVFrame &frame); 28 | 29 | private: 30 | friend class RtspConnection; 31 | 32 | explicit RtspServer(EventLoop *loop); 33 | MediaSession::Ptr LookMediaSession(const std::string &suffix) override; 34 | MediaSession::Ptr LookMediaSession(MediaSessionId session_id) override; 35 | TcpConnection::Ptr OnConnect(SOCKET sockfd) override; 36 | 37 | std::mutex mutex_; 38 | std::unordered_map> 39 | media_sessions_; 40 | std::unordered_map rtsp_suffix_map_; 41 | }; 42 | 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /rtsp-server/xop/VP8Source.cpp: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2021-8-26 3 | 4 | #if defined(WIN32) || defined(_WIN32) 5 | #ifndef _CRT_SECURE_NO_WARNINGS 6 | #define _CRT_SECURE_NO_WARNINGS 7 | #endif 8 | #endif 9 | 10 | #include "VP8Source.h" 11 | #include 12 | #include 13 | #include 14 | #if defined(WIN32) || defined(_WIN32) 15 | 16 | #else 17 | #include 18 | #endif 19 | 20 | using namespace xop; 21 | using namespace std; 22 | 23 | VP8Source::VP8Source(const uint32_t framerate) : framerate_(framerate) 24 | { 25 | payload_ = 96; 26 | clock_rate_ = 90000; 27 | } 28 | 29 | VP8Source *VP8Source::CreateNew(const uint32_t framerate) 30 | { 31 | return new VP8Source(framerate); 32 | } 33 | 34 | VP8Source::~VP8Source() = default; 35 | 36 | string VP8Source::GetMediaDescription(const uint16_t port) 37 | { 38 | char buf[100]; 39 | snprintf(buf, sizeof(buf), "m=video %hu RTP/AVP 96", port); 40 | return buf; 41 | } 42 | 43 | string VP8Source::GetAttribute() 44 | { 45 | return "a=rtpmap:96 VP8/90000"; 46 | } 47 | 48 | bool VP8Source::HandleFrame(const MediaChannelId channel_id, AVFrame frame) 49 | { 50 | uint8_t *frame_buf = frame.buffer.get(); 51 | size_t frame_size = frame.size; 52 | 53 | if (frame.timestamp == 0) { 54 | frame.timestamp = GetTimestamp(); 55 | } 56 | 57 | // X = R = N = 0; PartID = 0; 58 | // S = 1 if this is the first (or only) fragment of the frame 59 | uint8_t vp8_payload_descriptor = 0x10; 60 | 61 | while (frame_size > 0) { 62 | size_t payload_size = MAX_RTP_PAYLOAD_SIZE; 63 | 64 | RtpPacket rtp_pkt; 65 | rtp_pkt.timestamp = frame.timestamp; 66 | rtp_pkt.size = RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE + 67 | RTP_VPX_HEAD_SIZE + MAX_RTP_PAYLOAD_SIZE; 68 | rtp_pkt.last = 0; 69 | 70 | if (frame_size < MAX_RTP_PAYLOAD_SIZE) { 71 | payload_size = frame_size; 72 | rtp_pkt.size = RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE + 73 | RTP_VPX_HEAD_SIZE + 74 | static_cast(frame_size); 75 | rtp_pkt.last = 1; 76 | } 77 | 78 | rtp_pkt.data.get()[RTP_TCP_HEAD_SIZE + RTP_HEADER_SIZE + 0] = 79 | vp8_payload_descriptor; 80 | memcpy(rtp_pkt.data.get() + RTP_TCP_HEAD_SIZE + 81 | RTP_HEADER_SIZE + RTP_VPX_HEAD_SIZE, 82 | frame_buf, payload_size); 83 | 84 | if (send_frame_callback_) { 85 | if (!send_frame_callback_(channel_id, rtp_pkt)) 86 | return false; 87 | } 88 | 89 | frame_buf += payload_size; 90 | frame_size -= payload_size; 91 | vp8_payload_descriptor = 0x00; 92 | } 93 | 94 | return true; 95 | } 96 | 97 | uint32_t VP8Source::GetTimestamp() 98 | { 99 | const auto time_point = chrono::time_point_cast( 100 | chrono::steady_clock::now()); 101 | return static_cast( 102 | (time_point.time_since_epoch().count() + 500) / 1000 * 90); 103 | } 104 | -------------------------------------------------------------------------------- /rtsp-server/xop/VP8Source.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2021-8-26 3 | 4 | #ifndef XOP_VP8_SOURCE_H 5 | #define XOP_VP8_SOURCE_H 6 | 7 | #include "MediaSource.h" 8 | #include "rtp.h" 9 | 10 | namespace xop { 11 | 12 | class VP8Source : public MediaSource { 13 | public: 14 | static VP8Source *CreateNew(uint32_t framerate = 25); 15 | ~VP8Source() override; 16 | 17 | void Setframerate(const uint32_t framerate) { framerate_ = framerate; } 18 | 19 | uint32_t GetFramerate() const { return framerate_; } 20 | 21 | std::string GetMediaDescription(uint16_t port = 0) override; 22 | 23 | std::string GetAttribute() override; 24 | 25 | bool HandleFrame(MediaChannelId channelId, AVFrame frame) override; 26 | 27 | static uint32_t GetTimestamp(); 28 | 29 | private: 30 | explicit VP8Source(uint32_t framerate); 31 | 32 | uint32_t framerate_ = 25; 33 | }; 34 | 35 | } 36 | #endif 37 | -------------------------------------------------------------------------------- /rtsp-server/xop/media.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-5-16 3 | 4 | #ifndef XOP_MEDIA_H 5 | #define XOP_MEDIA_H 6 | 7 | #include 8 | 9 | namespace xop { 10 | 11 | /* RTSP服务支持的媒体类型 */ 12 | enum class MediaType { 13 | //PCMU = 0, 14 | PCMA = 8, 15 | H264 = 96, 16 | AAC = 37, 17 | H265 = 265, 18 | NONE 19 | }; 20 | 21 | enum class FrameType : uint8_t { 22 | VIDEO_FRAME_IDR = 0x01, 23 | VIDEO_FRAME_NOTIDR = 0x02, 24 | AUDIO_FRAME = 0x11, 25 | NONE = 0x00 26 | }; 27 | 28 | struct AVFrame { 29 | explicit AVFrame(const size_t size = 0) 30 | : buffer(new uint8_t[size], std::default_delete()), 31 | size(size), 32 | timestamp(0) 33 | { 34 | } 35 | 36 | std::shared_ptr buffer; /* 帧数据 */ 37 | size_t size; /* 帧大小 */ 38 | uint32_t timestamp; /* 时间戳 */ 39 | }; 40 | 41 | enum class MediaChannelId : uint8_t { 42 | channel_0 = 0, 43 | channel_1 = 1, 44 | channel_2 = 2, 45 | channel_3 = 3, 46 | channel_4 = 4, 47 | channel_5 = 5, 48 | channel_6 = 6, 49 | channel_7 = 7, 50 | channel_8 = 8, 51 | channel_9 = 9 52 | }; 53 | 54 | typedef uint32_t MediaSessionId; 55 | 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /rtsp-server/xop/rtp.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2021-9-2 3 | 4 | #ifndef XOP_RTP_H 5 | #define XOP_RTP_H 6 | 7 | #include 8 | #include 9 | #include "media.h" 10 | 11 | #define RTP_HEADER_SIZE 12 12 | #define MAX_RTP_PAYLOAD_SIZE 1420 //1460 1500-20-12-8 13 | #define RTP_VERSION 2 14 | #define RTP_TCP_HEAD_SIZE 4 15 | #define RTP_VPX_HEAD_SIZE 1 16 | 17 | #define RTP_HEADER_BIG_ENDIAN 0 18 | namespace xop { 19 | 20 | enum class TransportMode { 21 | NONE = 0, 22 | RTP_OVER_TCP = 1, 23 | RTP_OVER_UDP = 2, 24 | RTP_OVER_MULTICAST = 3, 25 | }; 26 | 27 | typedef struct _RTP_header { 28 | #if RTP_HEADER_BIG_ENDIAN 29 | /* 大端序 */ 30 | unsigned char version : 2; 31 | unsigned char padding : 1; 32 | unsigned char extension : 1; 33 | unsigned char csrc : 4; 34 | unsigned char marker : 1; 35 | unsigned char payload : 7; 36 | #else 37 | /* 小端序 */ 38 | unsigned char csrc : 4; 39 | unsigned char extension : 1; 40 | unsigned char padding : 1; 41 | unsigned char version : 2; 42 | unsigned char payload : 7; 43 | unsigned char marker : 1; 44 | #endif 45 | 46 | unsigned short seq; 47 | unsigned int ts; 48 | unsigned int ssrc; 49 | } RtpHeader; 50 | 51 | struct MediaChannelInfo { 52 | RtpHeader rtp_header; 53 | 54 | // tcp 55 | uint8_t rtp_channel; 56 | uint8_t rtcp_channel; 57 | 58 | // udp 59 | uint16_t rtp_port; 60 | uint16_t rtcp_port; 61 | uint16_t packet_seq; 62 | uint32_t clock_rate; 63 | 64 | // rtcp 65 | uint64_t packet_count; 66 | uint64_t octet_count; 67 | uint64_t last_rtcp_ntp_time; 68 | 69 | bool is_setup; 70 | bool is_play; 71 | bool is_record; 72 | }; 73 | 74 | struct RtpPacket { 75 | RtpPacket() 76 | : data(new uint8_t[1600], std::default_delete()), 77 | size(0), 78 | timestamp(0), 79 | type(FrameType::NONE), 80 | last(0) 81 | { 82 | } 83 | 84 | std::shared_ptr data; 85 | uint16_t size; 86 | uint32_t timestamp; 87 | FrameType type; 88 | uint8_t last; 89 | }; 90 | 91 | } 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /rtsp-server/xop/rtsp.h: -------------------------------------------------------------------------------- 1 | // PHZ 2 | // 2018-6-8 3 | // Scott Xu 4 | // 2020-12-5 Add IPv6 Support. 5 | 6 | #ifndef XOP_RTSP_H 7 | #define XOP_RTSP_H 8 | 9 | #include 10 | #include 11 | #include "MediaSession.h" 12 | #include "net/Logger.h" 13 | 14 | namespace xop { 15 | 16 | struct RtspUrlInfo { 17 | std::string url; 18 | std::string ip; 19 | uint16_t port; 20 | std::string suffix; 21 | }; 22 | 23 | class Rtsp : public std::enable_shared_from_this { 24 | public: 25 | Rtsp() = default; 26 | virtual ~Rtsp() = default; 27 | 28 | virtual void SetAuthConfig(const std::string realm, 29 | const std::string username, 30 | const std::string password) 31 | { 32 | realm_ = realm; 33 | username_ = username; 34 | password_ = password; 35 | has_auth_info_ = true; 36 | 37 | if (realm_.empty() || username.empty()) { 38 | has_auth_info_ = false; 39 | } 40 | } 41 | 42 | virtual void SetVersion(std::string version) // SDP Session Name 43 | { 44 | version_ = std::move(version); 45 | } 46 | 47 | virtual std::string GetVersion() { return version_; } 48 | 49 | virtual std::string GetRtspUrl() { return rtsp_url_info_.url; } 50 | 51 | bool ParseRtspUrl(const std::string &url) 52 | { 53 | char ip[100] = {0}; 54 | char suffix[100] = {0}; 55 | uint16_t port = 0; 56 | #if defined(WIN32) || defined(_WIN32) 57 | if (sscanf_s(url.c_str() + 7, "[%[^]]]:%hu/%s", ip, 100, &port, 58 | suffix, 100) == 3) //IPv6 59 | #else 60 | if (sscanf(url.c_str() + 7, "[%[^]]]:%hu/%s", ip, &port, 61 | suffix) == 3) 62 | #endif 63 | { 64 | rtsp_url_info_.port = port; 65 | } 66 | #if defined(WIN32) || defined(_WIN32) 67 | else if (sscanf_s(url.c_str() + 7, "[%[^]]]/%s", ip, 100, 68 | suffix, 100) == 2) 69 | #else 70 | else if (sscanf(url.c_str() + 7, "[%[^]]]/%s", ip, suffix) == 2) 71 | #endif 72 | { 73 | rtsp_url_info_.port = 554; 74 | } 75 | #if defined(WIN32) || defined(_WIN32) 76 | else if (sscanf_s(url.c_str() + 7, "%[^:]:%hu/%s", ip, 100, 77 | &port, suffix, 100) == 3) //IPv4, domain 78 | #else 79 | else if (sscanf(url.c_str() + 7, "%[^:]:%hu/%s", ip, &port, 80 | suffix) == 3) 81 | #endif 82 | { 83 | rtsp_url_info_.port = port; 84 | } 85 | #if defined(WIN32) || defined(_WIN32) 86 | else if (sscanf_s(url.c_str() + 7, "%[^/]/%s", ip, 100, suffix, 87 | 100) == 2) 88 | #else 89 | else if (sscanf(url.c_str() + 7, "%[^/]/%s", ip, suffix) == 2) 90 | #endif 91 | { 92 | rtsp_url_info_.port = 554; 93 | } else { 94 | LOG_ERROR("%s was illegal.\n", url.c_str()); 95 | return false; 96 | } 97 | 98 | rtsp_url_info_.ip = ip; 99 | rtsp_url_info_.suffix = suffix; 100 | rtsp_url_info_.url = url; 101 | return true; 102 | } 103 | 104 | protected: 105 | friend class RtspConnection; 106 | virtual MediaSession::Ptr LookMediaSession([[maybe_unused]] const std::string &suffix) 107 | { 108 | return nullptr; 109 | } 110 | 111 | virtual MediaSession::Ptr LookMediaSession([[maybe_unused]] MediaSessionId sessionId) 112 | { 113 | return nullptr; 114 | } 115 | 116 | bool has_auth_info_ = false; 117 | std::string realm_; 118 | std::string username_; 119 | std::string password_; 120 | std::string version_; 121 | RtspUrlInfo rtsp_url_info_; 122 | }; 123 | 124 | } 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /rtsp_output.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | [[maybe_unused]] static const char *rtsp_output_getname(void *unused); 4 | void rtsp_output_register(); 5 | -------------------------------------------------------------------------------- /rtsp_output_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef RTSP_OUTPUT_HELPER_H 2 | #define RTSP_OUTPUT_HELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct rtsp_output_settings { 9 | bool adv_out = false; 10 | uint32_t rescale_cx = 0; 11 | uint32_t rescale_cy = 0; 12 | }; 13 | 14 | class RtspOutputHelper { 15 | public: 16 | RtspOutputHelper(std::string outputName); 17 | ~RtspOutputHelper(); 18 | static RtspOutputHelper *CreateRtspOutput(obs_data_t *settings, obs_data_t *hotkey); 19 | void UpdateSettings(obs_data_t *settings) const; 20 | obs_data_t *GetSettings() const; 21 | void UpdateEncoder(); 22 | bool Start() const; 23 | void Stop() const; 24 | std::string GetLastError() const; 25 | obs_data_t *HotkeysSave() const; 26 | void SignalConnect(const char *signal, signal_callback_t callback, 27 | void *data) const; 28 | void SignalDisconnect(const char *signal, signal_callback_t callback, 29 | void *data) const; 30 | std::string GetOutputName() const; 31 | uint64_t GetTotalBytes() const; 32 | int GetTotalFrames() const; 33 | int GetFramesDropped() const; 34 | bool IsActive() const; 35 | 36 | private: 37 | RtspOutputHelper(obs_output_t *obsOutput); 38 | void CreateVideoEncoder(); 39 | void CreateAudioEncoder(); 40 | void GetBaseConfig(); 41 | static void OnPreStartSignal(void *data, calldata_t *cd); 42 | obs_output_t *obsOutput; 43 | struct rtsp_output_settings outputSettings; 44 | obs_encoder_t *videoEncoder = nullptr; 45 | std::vector audioEncoders; 46 | }; 47 | 48 | #endif // RTSP_OUTPUT_HELPER_H 49 | -------------------------------------------------------------------------------- /rtspoutput.rc.in: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION ${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH},${PROJECT_VERSION_TWEAK} 3 | BEGIN 4 | BLOCK "StringFileInfo" 5 | BEGIN 6 | BLOCK "040904B0" 7 | BEGIN 8 | VALUE "CompanyName", "OBS" 9 | VALUE "FileDescription", "OBS RTSP Server Plugin" 10 | VALUE "FileVersion", "${PROJECT_VERSION}" 11 | VALUE "InternalName", "obs-rtspserver" 12 | VALUE "OriginalFilename", "obs-rtspserver.dll" 13 | VALUE "ProductName", "OBS Studio" 14 | VALUE "ProductVersion", "${OBS_PLUGUIN_VERSION}" 15 | VALUE "Comments", "RTSP server for OBS" 16 | END 17 | END 18 | 19 | BLOCK "VarFileInfo" 20 | BEGIN 21 | VALUE "Translation", 0x0409, 0x04B0 22 | END 23 | END 24 | -------------------------------------------------------------------------------- /threadsafe_queue.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __THREADSAFE_QUEUE_H_ 3 | #define __THREADSAFE_QUEUE_H_ 4 | 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace std; 13 | template class threadsafe_queue 14 | { 15 | public: 16 | threadsafe_queue(size_t size_limit) 17 | : size_limit(size_limit), m_termination(false) 18 | { 19 | } 20 | ~threadsafe_queue() = default; 21 | 22 | /** 23 | * 1. When termination is not called, one element is dequeued every time the 24 | * queue is called until the queue is empty. This method blocks the thread. 25 | * 2. After termination is called, this method will never block. If it is 26 | * already in a blocked state, contact the blocked state. 27 | * 3. When true is returned, the value is valid. When false is returned, value 28 | * is invalid. Returns false when termination is called and the queue is empty. 29 | **/ 30 | 31 | //return nullptr if the queue is empty 32 | std::shared_ptr wait_and_pop() 33 | { 34 | unique_lock lk(mut); 35 | data_cond.wait(lk, [this] { 36 | return !data_queue.empty() || 37 | m_termination.load(memory_order_acquire); 38 | }); 39 | 40 | //dequeue if not empty 41 | if (!data_queue.empty()) 42 | { 43 | shared_ptr res = data_queue.front(); 44 | data_queue.pop(); 45 | return res; 46 | } 47 | 48 | //If the queue is empty, return nullptr 49 | return nullptr; 50 | } 51 | 52 | //return false if the queue is empty 53 | bool wait_and_pop(T &&value) 54 | { 55 | shared_ptr res = wait_and_pop(); 56 | if (res == nullptr) 57 | return false; 58 | value = std::move(res); 59 | return true; 60 | } 61 | 62 | //return nullptr if the queue is empty 63 | std::shared_ptr try_pop() 64 | { 65 | lock_guard lk(mut); 66 | 67 | //dequeue if not empty 68 | if (!data_queue.empty()) 69 | { 70 | shared_ptr res = data_queue.front(); 71 | data_queue.pop(); 72 | return res; 73 | } 74 | 75 | //If the queue is empty, return nullptr 76 | return nullptr; 77 | } 78 | 79 | //return false if the queue is empty 80 | bool try_pop(T &&value) 81 | { 82 | shared_ptr res = try_pop(); 83 | if (res == nullptr) 84 | return false; 85 | value = std::move(res); 86 | return true; 87 | } 88 | 89 | //insert queue, move 90 | void move_push(T &&new_value) 91 | { 92 | if (m_termination.load(memory_order_acquire)) 93 | return; 94 | shared_ptr data(make_shared(std::move(new_value))); 95 | unique_lock lk(mut); 96 | data_queue.push(data); 97 | if (data_queue.size() > size_limit) { 98 | data_queue.pop(); 99 | m_dropped_count.fetch_add(1, memory_order_relaxed); 100 | } 101 | data_cond.notify_one(); 102 | } 103 | 104 | //insert queue 105 | void push(T new_value) 106 | { 107 | move_push(new_value); 108 | } 109 | 110 | bool empty() 111 | { 112 | unique_lock lk(mut); 113 | return data_queue.empty(); 114 | } 115 | 116 | size_t size() 117 | { 118 | unique_lock lk(mut); 119 | return data_queue.size(); 120 | } 121 | 122 | size_t dropped_count() const 123 | { 124 | return m_dropped_count.load(memory_order_relaxed); 125 | } 126 | 127 | //Set this queue to terminated state. 128 | //In the exit state, the enqueue is ignored, and the dequeue can be performed. 129 | //When the queue is empty, wait_and_pop will not block. 130 | void termination() 131 | { 132 | unique_lock lk(mut); 133 | m_termination.store(true, memory_order_release); 134 | data_cond.notify_all(); 135 | } 136 | 137 | //Get whether this queue is terminated 138 | bool is_termination() const 139 | { 140 | return m_termination.load(memory_order_acquire); 141 | } 142 | 143 | private: 144 | mutex mut; 145 | queue> data_queue; 146 | const size_t size_limit; 147 | condition_variable data_cond; 148 | atomic_bool m_termination; 149 | atomic_size_t m_dropped_count; 150 | }; 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /ui/rtsp_properties.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RTSP_PROPERTIES_H 2 | #define RTSP_PROPERTIES_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../rtsp_output_helper.h" 8 | 9 | namespace Ui { 10 | class RtspProperties; 11 | } 12 | 13 | class RtspProperties : public QDialog { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit RtspProperties(std::string rtspOutputName, QWidget *parent = 0); 18 | ~RtspProperties(); 19 | 20 | public Q_SLOTS: 21 | virtual int exec(); 22 | 23 | private Q_SLOTS: 24 | void onPushButtonStartClicked(); 25 | void onPushButtonStopClicked(); 26 | void onPushButtonAddressCopyClicked() const; 27 | void onCheckBoxEnableMulticastClicked(int checked) const; 28 | void onSpinBoxPortValueChanged(int value) const; 29 | void onLineEditUrlSuffixValueChanged(const QString &value) const; 30 | void onCheckBoxEnableAuthenticationClicked(bool checked) const; 31 | void onLineEditRealmTextChanged(const QString &value) const; 32 | void onLineEditUsernameTextChanged(const QString &value) const; 33 | void onLineEditPasswordTextChanged(const QString &value) const; 34 | 35 | void onStatusTimerTimeout(); 36 | 37 | void onButtonStatusChanging(bool outputStarted, bool outputStopped) const; 38 | void onStatusTimerStatusChanging(bool start); 39 | void onLabelMessageStatusChanging(bool showError) const; 40 | 41 | Q_SIGNALS: 42 | void setButtonStatus(bool outputStarted, bool outputStopped); 43 | void setStatusTimerStatus(bool start); 44 | void setLabelMessageStatus(bool showError); 45 | 46 | private: 47 | Ui::RtspProperties *ui; 48 | QTimer *statusTimer; 49 | 50 | signal_handler_t *signalHandler; 51 | RtspOutputHelper *rtspOutputHelper; 52 | 53 | uint64_t lastTotalBytes; 54 | 55 | obs_data_t *settings; 56 | 57 | void showEvent(QShowEvent *event); 58 | void closeEvent(QCloseEvent *event); 59 | 60 | static void OnOutputStart(void *data, calldata_t *cd); 61 | static void OnOutputStop(void *data, calldata_t *cd); 62 | 63 | void LoadConfig(config_t *config) const; 64 | void SaveConfig(config_t *config) const; 65 | }; 66 | 67 | #endif // RTSP_PROPERTIES_H 68 | --------------------------------------------------------------------------------