├── .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