mirror of
https://github.com/xaionaro-go/streamctl.git
synced 2025-10-15 03:50:55 +08:00
Start adapting new streamforward to Android
This commit is contained in:
121
Makefile
121
Makefile
@@ -29,10 +29,10 @@ WINDOWS_CGO_FLAGS?=-I$(PWD)/3rdparty/amd64/windows/vlc-$(WINDOWS_VLC_VERSION)/sd
|
|||||||
WINDOWS_LINKER_FLAGS?=-L$(PWD)/3rdparty/amd64/windows/vlc-$(WINDOWS_VLC_VERSION)/sdk/lib -L$(PWD)/3rdparty/amd64/windows/ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0/lib
|
WINDOWS_LINKER_FLAGS?=-L$(PWD)/3rdparty/amd64/windows/vlc-$(WINDOWS_VLC_VERSION)/sdk/lib -L$(PWD)/3rdparty/amd64/windows/ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0/lib
|
||||||
WINDOWS_PKG_CONFIG_PATH?=$(PWD)/3rdparty/amd64/windows/vlc-$(WINDOWS_VLC_VERSION)/sdk/lib/pkgconfig
|
WINDOWS_PKG_CONFIG_PATH?=$(PWD)/3rdparty/amd64/windows/vlc-$(WINDOWS_VLC_VERSION)/sdk/lib/pkgconfig
|
||||||
|
|
||||||
all: streampanel-linux-amd64 streampanel-linux-arm64 streampanel-android streampanel-windows
|
all: streampanel-linux-amd64 streampanel-linux-arm64 streampanel-android-arm64 streampanel-windows
|
||||||
|
|
||||||
$(GOPATH)/bin/pkg-config-wrapper:
|
$(GOPATH)/bin/pkg-config-wrapper:
|
||||||
go install github.com/xaionaro-go/pkg-config-wrapper@4cf60a2b85ad0b917e217b13ebcf04b28cc24334
|
go install github.com/xaionaro-go/pkg-config-wrapper@5dd443e6c18336416c49047e2ba0002e26a85278
|
||||||
|
|
||||||
3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION):
|
3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION):
|
||||||
mkdir -p 3rdparty/arm64
|
mkdir -p 3rdparty/arm64
|
||||||
@@ -73,7 +73,7 @@ streampanel-macos-arm64: builddir
|
|||||||
|
|
||||||
3rdparty/arm64/termux-packages:
|
3rdparty/arm64/termux-packages:
|
||||||
mkdir -p 3rdparty/arm64/
|
mkdir -p 3rdparty/arm64/
|
||||||
cd 3rdparty/arm64 && git clone https://github.com/termux/termux-packages
|
cd 3rdparty/arm64 && git clone --depth=1 -b feat/static_libav https://github.com/xaionaro/termux-packages
|
||||||
|
|
||||||
3rdparty/arm64/termux-packages/environment-ready: 3rdparty/arm64/termux-packages
|
3rdparty/arm64/termux-packages/environment-ready: 3rdparty/arm64/termux-packages
|
||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
@@ -85,11 +85,43 @@ streampanel-macos-arm64: builddir
|
|||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh ./scripts/setup-android-sdk.sh
|
./scripts/run-docker.sh ./scripts/setup-android-sdk.sh
|
||||||
|
|
||||||
|
# downloading dependencies (e.g. we do not need ccls,
|
||||||
|
# but we need the most of the dependencies of ccls)
|
||||||
|
|
||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh ./build-package.sh -I gettext
|
./scripts/run-docker.sh ./build-package.sh -I gettext
|
||||||
|
|
||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh ./build-package.sh -I rust
|
./scripts/run-docker.sh ./build-package.sh -I ccls
|
||||||
|
|
||||||
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
|
./scripts/run-docker.sh ./build-package.sh -I termux-api
|
||||||
|
|
||||||
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
|
./scripts/run-docker.sh ./build-package.sh -I xdotool
|
||||||
|
|
||||||
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
|
./scripts/run-docker.sh ./build-package.sh -I xdg-utils
|
||||||
|
|
||||||
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
|
./scripts/run-docker.sh ./build-package.sh -I liblzma
|
||||||
|
|
||||||
|
if ! [ -f /data/data/com.termux/files/usr/lib/liblzma.a ]; then \
|
||||||
|
cd 3rdparty/arm64/termux-packages; \
|
||||||
|
./scripts/run-docker.sh rm -f /data/data/.built-packages/liblzma; \
|
||||||
|
./scripts/run-docker.sh ./build-package.sh -I liblzma; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -f /data/data/com.termux/files/usr/lib/libiconv.a ]; then \
|
||||||
|
cd 3rdparty/arm64/termux-packages; \
|
||||||
|
./scripts/run-docker.sh rm -f /data/data/.built-packages/libiconv; \
|
||||||
|
./scripts/run-docker.sh ./build-package.sh -I libiconv; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
|
./scripts/run-docker.sh ./build-package.sh -I libx11
|
||||||
|
|
||||||
|
# building what we need
|
||||||
|
|
||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh ./build-package.sh ffmpeg
|
./scripts/run-docker.sh ./build-package.sh ffmpeg
|
||||||
@@ -97,8 +129,10 @@ streampanel-macos-arm64: builddir
|
|||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh ./build-package.sh libxxf86vm
|
./scripts/run-docker.sh ./build-package.sh libxxf86vm
|
||||||
|
|
||||||
cd 3rdparty/arm64/termux-packages && \
|
#cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh ./build-package.sh vlc
|
#./scripts/run-docker.sh ./build-package.sh vlc
|
||||||
|
|
||||||
|
# installing fyne
|
||||||
|
|
||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh sudo apt update
|
./scripts/run-docker.sh sudo apt update
|
||||||
@@ -109,39 +143,90 @@ streampanel-macos-arm64: builddir
|
|||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh go install fyne.io/fyne/v2/cmd/fyne@latest
|
./scripts/run-docker.sh go install fyne.io/fyne/v2/cmd/fyne@latest
|
||||||
|
|
||||||
|
# avoiding fyne loading wrong GL libraries:
|
||||||
rm -f /data/data/com.termux/files/usr/lib/*lib*GL*
|
rm -f /data/data/com.termux/files/usr/lib/*lib*GL*
|
||||||
|
|
||||||
|
# marking to do not redo all the work above next time:
|
||||||
touch 3rdparty/arm64/termux-packages/environment-ready
|
touch 3rdparty/arm64/termux-packages/environment-ready
|
||||||
|
|
||||||
dockerbuild-streampanel-android: 3rdparty/arm64/termux-packages/environment-ready
|
dockerbuild-streampanel-android-arm64: 3rdparty/arm64/termux-packages/environment-ready
|
||||||
cd 3rdparty/arm64/termux-packages && \
|
cd 3rdparty/arm64/termux-packages && \
|
||||||
./scripts/run-docker.sh make ENABLE_VLC="$(ENABLE_VLC)" ENABLE_LIBAV="$(ENABLE_LIBAV)" FORCE_DEBUG="$(FORCE_DEBUG)" -C /project streampanel-android-in-docker
|
./scripts/run-docker.sh make ENABLE_VLC="$(ENABLE_VLC)" ENABLE_LIBAV="$(ENABLE_LIBAV)" FORCE_DEBUG="$(FORCE_DEBUG)" -C /project streampanel-android-arm64-in-docker
|
||||||
|
|
||||||
|
checkconfig-android-in-docker:
|
||||||
|
@if [ "$(ENABLE_VLC)" != 'false' ]; then \
|
||||||
|
echo "VLC is not supported for Android builds, yet, please disable it with ENABLE_VLC=false."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
checkconfig-android:
|
checkconfig-android:
|
||||||
@if [ "$(ENABLE_VLC)" != 'false' ]; then \
|
@if [ "$(ENABLE_VLC)" != 'false' ]; then \
|
||||||
echo "VLC is not supported for Android builds, yet, please disable it with ENABLE_VLC=false."; \
|
echo "VLC is not supported for Android builds, yet, please disable it with ENABLE_VLC=false."; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
@if [ "$(ENABLE_LIBAV)" != 'false' ]; then \
|
@if [ "$(ENABLE_LIBAV)" != 'false' ]; then \
|
||||||
echo "LibAV is not supported for Android builds, yet, please disable it with ENABLE_LIBAV=false."; \
|
echo "Building with LibAV support is not supported outside of the docker container yet. Please either disable LibAV with ENABLE_LIBAV=false or use `make dockerbuild-streampanel-android-arm64` instead."; \
|
||||||
exit 1; \
|
exit 1; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
streampanel-android-in-docker: checkconfig-android builddir $(GOPATH)/bin/pkg-config-wrapper
|
streampanel-android-arm64-in-docker: build-streampanel-android-arm64-in-docker check-streampanel-android-arm64-static-cgo
|
||||||
|
|
||||||
|
build-streampanel-android-arm64-in-docker: checkconfig-android-in-docker builddir $(GOPATH)/bin/pkg-config-wrapper
|
||||||
|
go mod tidy
|
||||||
$(eval ANDROID_NDK_HOME=$(shell ls -d /home/builder/lib/android-ndk-*))
|
$(eval ANDROID_NDK_HOME=$(shell ls -d /home/builder/lib/android-ndk-*))
|
||||||
cd cmd/streampanel && PKG_CONFIG_LIBS_FORCE_STATIC='libav*,libvlc' PKG_CONFIG_ERASE="-fopenmp=*,-landroid" PKG_CONFIG='$(GOPATH)/bin/pkg-config-wrapper' PKG_CONFIG_PATH='/data/data/com.termux/files/usr/lib/pkgconfig' CGO_CFLAGS='-I$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/ -I/data/data/com.termux/files/usr/include -Wno-incompatible-function-pointer-types -Wno-unused-result -Wno-xor-used-as-pow' CGO_LDFLAGS='-L$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/ -L/data/data/com.termux/files/usr/lib' ANDROID_NDK_HOME="$(ANDROID_NDK_HOME)" PATH="${PATH}:${HOME}/go/bin" fyne package $(FYNEBUILD_FLAGS) -release -os android/arm64 && mv streampanel.apk ../../build/
|
cd cmd/streampanel && \
|
||||||
|
PKG_CONFIG_WRAPPER_LOG='/tmp/pkg_config_wrapper.log' \
|
||||||
|
PKG_CONFIG_WRAPPER_LOG_LEVEL='trace' \
|
||||||
|
PKG_CONFIG_LIBS_FORCE_STATIC='libav*,libvlc' \
|
||||||
|
PKG_CONFIG_ERASE="-fopenmp=*,-landroid" \
|
||||||
|
PKG_CONFIG='$(GOPATH)/bin/pkg-config-wrapper' \
|
||||||
|
PKG_CONFIG_PATH='/data/data/com.termux/files/usr/lib/pkgconfig' \
|
||||||
|
CGO_CFLAGS='-I$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/ -I/data/data/com.termux/files/usr/include -Wno-incompatible-function-pointer-types -Wno-unused-result -Wno-xor-used-as-pow' \
|
||||||
|
CGO_LDFLAGS='-ldl -lc -L$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/ -L/data/data/com.termux/files/usr/lib' \
|
||||||
|
ANDROID_NDK_HOME="$(ANDROID_NDK_HOME)" \
|
||||||
|
PATH="${PATH}:${HOME}/go/bin" \
|
||||||
|
fyne package $(FYNEBUILD_FLAGS) -release -os android/arm64 && mv streampanel.apk ../../build/streampanel-arm64.apk
|
||||||
|
|
||||||
streampanel-android-static-cgo: checkconfig-android builddir $(GOPATH)/bin/pkg-config-wrapper 3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION) 3rdparty/arm64/termux
|
streampanel-android-arm64-static-cgo: build-streampanel-android-arm64-static-cgo check-streampanel-android-arm64-static-cgo
|
||||||
|
|
||||||
|
build-streampanel-android-arm64-static-cgo: builddir $(GOPATH)/bin/pkg-config-wrapper 3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION) 3rdparty/arm64/termux
|
||||||
$(eval ANDROID_NDK_HOME=$(PWD)/3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION))
|
$(eval ANDROID_NDK_HOME=$(PWD)/3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION))
|
||||||
cd cmd/streampanel && PKG_CONFIG_LIBS_FORCE_STATIC='libav*,libvlc' PKG_CONFIG_ERASE="-fopenmp=*,-landroid" PKG_CONFIG='$(GOPATH)/bin/pkg-config-wrapper' PKG_CONFIG_PATH='$(PWD)/3rdparty/arm64/termux/data/data/com.termux/files/usr/lib/pkgconfig' CGO_CFLAGS='-I$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/ -I$(PWD)/3rdparty/arm64/termux/data/data/com.termux/files/usr/include -Wno-incompatible-function-pointer-types -Wno-unused-result -Wno-xor-used-as-pow' CGO_LDFLAGS='-L$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/ -L$(PWD)/3rdparty/arm64/termux/data/data/com.termux/files/usr/lib' ANDROID_NDK_HOME="$(ANDROID_NDK_HOME)" fyne package $(FYNEBUILD_FLAGS) -release -os android/arm64 && mv streampanel.apk ../../build/
|
cd cmd/streampanel && \
|
||||||
|
PKG_CONFIG_LIBS_FORCE_STATIC='libav*,libvlc' \
|
||||||
|
PKG_CONFIG_ERASE="-fopenmp=*,-landroid" \
|
||||||
|
PKG_CONFIG='$(GOPATH)/bin/pkg-config-wrapper' \
|
||||||
|
PKG_CONFIG_PATH='$(PWD)/3rdparty/arm64/termux/data/data/com.termux/files/usr/lib/pkgconfig' \
|
||||||
|
CGO_CFLAGS='-I$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/ -I$(PWD)/3rdparty/arm64/termux/data/data/com.termux/files/usr/include -Wno-incompatible-function-pointer-types -Wno-unused-result -Wno-xor-used-as-pow' \
|
||||||
|
CGO_LDFLAGS='-ldl -lc -L$(ANDROID_NDK_HOME)/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/ -L$(PWD)/3rdparty/arm64/termux/data/data/com.termux/files/usr/lib' \
|
||||||
|
ANDROID_NDK_HOME="$(ANDROID_NDK_HOME)" \
|
||||||
|
fyne package $(FYNEBUILD_FLAGS) -release -os android/arm64 && mv streampanel.apk ../../build/streampanel-arm64.apk
|
||||||
|
|
||||||
streampanel-android: checkconfig-android builddir 3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION)
|
check-streampanel-android-arm64-static-cgo:
|
||||||
|
$(eval TEMP_DIR:=$(shell mktemp -d))
|
||||||
|
@echo "temp_dir:<$(TEMP_DIR)>"
|
||||||
|
@cp build/streampanel-arm64.apk "$(TEMP_DIR)/streampanel.zip"
|
||||||
|
@cd "$(TEMP_DIR)" && unzip streampanel.zip >/dev/null
|
||||||
|
@if readelf -d "$(TEMP_DIR)/lib/arm64-v8a/libstreampanel.so" | grep STATIC_TLS; then \
|
||||||
|
readelf -d "$(TEMP_DIR)/lib/arm64-v8a/libstreampanel.so"; \
|
||||||
|
echo "The resulting APK is linked in a wrong way, 'readlink -d' showed flag 'STATIC_TLS', so the application will crash when you will try to launch it."; \
|
||||||
|
rm -rf "$(TEMP_DIR)"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@if ! readelf -d "$(TEMP_DIR)/lib/arm64-v8a/libstreampanel.so" | grep libdl.so >/dev/null; then \
|
||||||
|
readelf -d "$(TEMP_DIR)/lib/arm64-v8a/libstreampanel.so"; \
|
||||||
|
echo "The resulting APK is linked in a wrong way, 'readlink -d' showed it does not use 'libdl.so', which likely means some wacky stuff happened and the application is not guaranteed to work."; \
|
||||||
|
rm -rf "$(TEMP_DIR)"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
rm -rf "$(TEMP_DIR)"
|
||||||
|
|
||||||
|
streampanel-android-arm64: checkconfig-android builddir 3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION)
|
||||||
$(eval ANDROID_NDK_HOME=$(PWD)/3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION))
|
$(eval ANDROID_NDK_HOME=$(PWD)/3rdparty/arm64/android-ndk-$(ANDROID_NDK_VERSION))
|
||||||
cd cmd/streampanel && ANDROID_NDK_HOME="$(ANDROID_NDK_HOME)" fyne package $(FYNEBUILD_FLAGS) -release -os android/arm64 && mv streampanel.apk ../../build/
|
cd cmd/streampanel && ANDROID_NDK_HOME="$(ANDROID_NDK_HOME)" fyne package $(FYNEBUILD_FLAGS) -release -os android/arm64 && mv streampanel-arm64.apk ../../build/
|
||||||
|
|
||||||
install-android:
|
install-android-arm64:
|
||||||
adb shell pm uninstall center.dx.streampanel >/dev/null 2>&1 || /bin/true
|
adb shell pm uninstall center.dx.streampanel >/dev/null 2>&1 || /bin/true
|
||||||
adb install build/streampanel.apk
|
adb install build/streampanel-arm64.apk
|
||||||
|
|
||||||
streampanel-ios: builddir
|
streampanel-ios: builddir
|
||||||
cd cmd/streampanel && fyne package $(GOBUILD_FLAGS) -release -os ios && mv streampanel.ipa ../../build/
|
cd cmd/streampanel && fyne package $(GOBUILD_FLAGS) -release -os ios && mv streampanel.ipa ../../build/
|
||||||
|
@@ -5,4 +5,4 @@ Website = "https://github.com/xaionaro/streamctl"
|
|||||||
Name = "streampanel"
|
Name = "streampanel"
|
||||||
ID = "center.dx.streampanel"
|
ID = "center.dx.streampanel"
|
||||||
Version = "0.1.0"
|
Version = "0.1.0"
|
||||||
Build = 188
|
Build = 300
|
||||||
|
8
go.mod
8
go.mod
@@ -7,14 +7,18 @@ toolchain go1.22.3
|
|||||||
// The original go-yaml is very slow, using the improved version instead
|
// The original go-yaml is very slow, using the improved version instead
|
||||||
replace github.com/goccy/go-yaml v1.11.3 => github.com/yoelsusanto/go-yaml v0.0.0-20240324162521-2018c1ab915b
|
replace github.com/goccy/go-yaml v1.11.3 => github.com/yoelsusanto/go-yaml v0.0.0-20240324162521-2018c1ab915b
|
||||||
|
|
||||||
replace code.cloudfoundry.org/bytefmt => github.com/cloudfoundry/bytefmt v0.0.0-20211005130812-5bb3c17173e5
|
|
||||||
|
|
||||||
replace github.com/andreykaipov/goobs v1.4.1 => github.com/xaionaro-go/goobs v0.0.0-20241009130652-ffb0e76ad260
|
replace github.com/andreykaipov/goobs v1.4.1 => github.com/xaionaro-go/goobs v0.0.0-20241009130652-ffb0e76ad260
|
||||||
|
|
||||||
replace github.com/adrg/libvlc-go/v3 v3.1.5 => github.com/xaionaro-go/libvlc-go/v3 v3.0.0-20241011194409-0fe4e2a9d901
|
replace github.com/adrg/libvlc-go/v3 v3.1.5 => github.com/xaionaro-go/libvlc-go/v3 v3.0.0-20241011194409-0fe4e2a9d901
|
||||||
|
|
||||||
replace fyne.io/fyne/v2 v2.5.0 => github.com/xaionaro-go/fyne/v2 v2.0.0-20241012203222-61bfd3b898c0
|
replace fyne.io/fyne/v2 v2.5.0 => github.com/xaionaro-go/fyne/v2 v2.0.0-20241012203222-61bfd3b898c0
|
||||||
|
|
||||||
|
replace code.cloudfoundry.org/bytefmt => github.com/cloudfoundry/bytefmt v0.0.0-20211005130812-5bb3c17173e5
|
||||||
|
|
||||||
|
replace github.com/pion/ice/v2 => github.com/aler9/ice/v2 v2.0.0-20241006110309-c973995af023
|
||||||
|
|
||||||
|
replace github.com/pion/webrtc/v3 => github.com/aler9/webrtc/v3 v3.0.0-20240610104456-eaec24056d06
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/facebookincubator/go-belt v0.0.0-20240804203001-846c4409d41c
|
github.com/facebookincubator/go-belt v0.0.0-20240804203001-846c4409d41c
|
||||||
github.com/go-git/go-billy/v5 v5.5.0
|
github.com/go-git/go-billy/v5 v5.5.0
|
||||||
|
28
go.sum
28
go.sum
@@ -73,6 +73,10 @@ github.com/abema/go-mp4 v1.2.0 h1:gi4X8xg/m179N/J15Fn5ugywN9vtI6PLk6iLldHGLAk=
|
|||||||
github.com/abema/go-mp4 v1.2.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
|
github.com/abema/go-mp4 v1.2.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/aler9/ice/v2 v2.0.0-20241006110309-c973995af023 h1:z/HjA8RgJDfvF2vca08z77PJvz0jDFK9QDJaZkaOXgk=
|
||||||
|
github.com/aler9/ice/v2 v2.0.0-20241006110309-c973995af023/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
|
||||||
|
github.com/aler9/webrtc/v3 v3.0.0-20240610104456-eaec24056d06 h1:WtKhXOpd8lgTeXF3RQVOzkNRuy83ygvWEpMYD2aoY3Q=
|
||||||
|
github.com/aler9/webrtc/v3 v3.0.0-20240610104456-eaec24056d06/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/anthonynsimon/bild v0.14.0 h1:IFRkmKdNdqmexXHfEU7rPlAmdUZ8BDZEGtGHDnGWync=
|
github.com/anthonynsimon/bild v0.14.0 h1:IFRkmKdNdqmexXHfEU7rPlAmdUZ8BDZEGtGHDnGWync=
|
||||||
@@ -518,7 +522,6 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
|||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
|
||||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
||||||
@@ -535,47 +538,34 @@ github.com/phuslu/goid v1.0.1 h1:74sob8Rch+WJCROvccSbvxn0Pz5RBvIf57MvesjbPNo=
|
|||||||
github.com/phuslu/goid v1.0.1/go.mod h1:txc2fUIdrdnn+v9Vq+QpiPQ3dnrXEchjoVDgic+r+L0=
|
github.com/phuslu/goid v1.0.1/go.mod h1:txc2fUIdrdnn+v9Vq+QpiPQ3dnrXEchjoVDgic+r+L0=
|
||||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||||
github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0=
|
|
||||||
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
|
github.com/pion/datachannel v1.5.6 h1:1IxKJntfSlYkpUj8LlYRSWpYiTTC02nUrOE8T3DqGeg=
|
||||||
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
|
github.com/pion/datachannel v1.5.6/go.mod h1:1eKT6Q85pRnr2mHiWHxJwO50SfZRtWHTsNIVb/NfGW4=
|
||||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||||
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
|
github.com/pion/dtls/v2 v2.2.11 h1:9U/dpCYl1ySttROPWJgqWKEylUdT0fXp/xst6JwY5Ks=
|
||||||
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
github.com/pion/dtls/v2 v2.2.11/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||||
github.com/pion/ice/v2 v2.3.11/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E=
|
|
||||||
github.com/pion/ice/v2 v2.3.24 h1:RYgzhH/u5lH0XO+ABatVKCtRd+4U1GEaCXSMjNr13tI=
|
|
||||||
github.com/pion/ice/v2 v2.3.24/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
|
|
||||||
github.com/pion/interceptor v0.1.25/go.mod h1:wkbPYAak5zKsfpVDYMtEfWEy8D4zL+rpxCxPImLOg3Y=
|
|
||||||
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
|
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
|
||||||
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
|
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
|
||||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
github.com/pion/mdns v0.0.8/go.mod h1:hYE72WX8WDveIhg7fmXgMKivD3Puklk0Ymzog0lSyaI=
|
|
||||||
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
||||||
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
||||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I=
|
|
||||||
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||||
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
|
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
|
||||||
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||||
github.com/pion/rtp v1.8.2/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
|
||||||
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||||
github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
|
github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
|
||||||
github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||||
github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0=
|
|
||||||
github.com/pion/sctp v1.8.8/go.mod h1:igF9nZBrjh5AtmKc7U30jXltsFHicFCXSmWA2GWRaWs=
|
|
||||||
github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA=
|
github.com/pion/sctp v1.8.13/go.mod h1:YKSgO/bO/6aOMP9LCie1DuD7m+GamiK2yIiPM6vH+GA=
|
||||||
github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=
|
github.com/pion/sctp v1.8.16 h1:PKrMs+o9EMLRvFfXq59WFsC+V8mN1wnKzqrv+3D/gYY=
|
||||||
github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
|
github.com/pion/sctp v1.8.16/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE=
|
||||||
github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw=
|
|
||||||
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
|
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
|
||||||
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
|
||||||
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
|
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
|
||||||
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
||||||
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||||
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||||
github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40=
|
|
||||||
github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI=
|
|
||||||
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||||
github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc=
|
github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc=
|
||||||
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||||
@@ -589,8 +579,6 @@ github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uP
|
|||||||
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||||
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
|
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
|
||||||
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||||
github.com/pion/webrtc/v3 v3.2.23 h1:GbqEuxBbVLFhXk0GwxKAoaIJYiEa9TyoZPEZC+2HZxM=
|
|
||||||
github.com/pion/webrtc/v3 v3.2.23/go.mod h1:1CaT2fcZzZ6VZA+O1i9yK2DU4EOcXVvSbWG9pr5jefs=
|
|
||||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
@@ -639,7 +627,6 @@ github.com/rymdport/portal v0.2.6 h1:HWmU3gORu7vWcpr7VSwUS2Xx1HtJXVcUuTqEZcMEsIg
|
|||||||
github.com/rymdport/portal v0.2.6/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
github.com/rymdport/portal v0.2.6/go.mod h1:kFF4jslnJ8pD5uCi17brj/ODlfIidOxlgUDTO5ncnC4=
|
||||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
@@ -813,7 +800,6 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
|||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
@@ -914,13 +900,11 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
|||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
|
||||||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
@@ -1020,7 +1004,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -1038,13 +1021,11 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
|||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
|
|
||||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
@@ -1065,7 +1046,6 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
27
pkg/recoder/.wip/livego/input.go
Normal file
27
pkg/recoder/.wip/livego/input.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package livego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Input struct {
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ recoder.Input = (*Input)(nil)
|
||||||
|
|
||||||
|
func (r *Recoder) NewInputFromURL(
|
||||||
|
ctx context.Context,
|
||||||
|
url string,
|
||||||
|
cfg recoder.InputConfig,
|
||||||
|
) (recoder.Input, error) {
|
||||||
|
return &Input{
|
||||||
|
URL: url,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Input) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
25
pkg/recoder/.wip/livego/output.go
Normal file
25
pkg/recoder/.wip/livego/output.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package livego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Output struct {
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) NewOutputFromURL(
|
||||||
|
ctx context.Context,
|
||||||
|
url string,
|
||||||
|
cfg recoder.OutputConfig,
|
||||||
|
) (recoder.Output, error) {
|
||||||
|
return &Output{
|
||||||
|
URL: url,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Output) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
72
pkg/recoder/.wip/livego/recoder.go
Normal file
72
pkg/recoder/.wip/livego/recoder.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package livego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gwuhaolin/livego/protocol/rtmp/rtmprelay"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Recoder struct {
|
||||||
|
Locker xsync.Mutex
|
||||||
|
Relay *rtmprelay.RtmpRelay
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ recoder.Recoder = (*Recoder)(nil)
|
||||||
|
|
||||||
|
func (r *Recoder) StartRecoding(
|
||||||
|
ctx context.Context,
|
||||||
|
inputIface recoder.Input,
|
||||||
|
outputIface recoder.Output,
|
||||||
|
) error {
|
||||||
|
input, ok := inputIface.(*Input)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected Input of type %T, but received %T", input, inputIface)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, ok := outputIface.(*Output)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected Input of type %T, but received %T", output, outputIface)
|
||||||
|
}
|
||||||
|
|
||||||
|
return xsync.DoR1(ctx, &r.Locker, func() error {
|
||||||
|
relay := rtmprelay.NewRtmpRelay(&input.URL, &output.URL)
|
||||||
|
if err := relay.Start(); err != nil {
|
||||||
|
return fmt.Errorf("unable to start RTMP relay from '%s' to '%s': %w", input.URL, output.URL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Relay = relay
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) WaitForRecordingEnd(
|
||||||
|
ctx context.Context,
|
||||||
|
) error {
|
||||||
|
return xsync.DoR1(ctx, &r.Locker, func() error {
|
||||||
|
if r.Relay != nil {
|
||||||
|
return fmt.Errorf("recoder is not started (or is closed)")
|
||||||
|
}
|
||||||
|
panic("do not know how to implement this, without changing the upstream library, yet")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) GetStats(
|
||||||
|
ctx context.Context,
|
||||||
|
) (*recoder.Stats, error) {
|
||||||
|
return &recoder.Stats{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) Close() error {
|
||||||
|
ctx := context.TODO()
|
||||||
|
return xsync.DoR1(ctx, &r.Locker, func() error {
|
||||||
|
if r.Relay != nil {
|
||||||
|
return fmt.Errorf("recoder is not started (or is closed)")
|
||||||
|
}
|
||||||
|
err := r.Relay.Start()
|
||||||
|
r.Relay = nil
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
19
pkg/recoder/.wip/livego/recoder_factory.go
Normal file
19
pkg/recoder/.wip/livego/recoder_factory.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package livego
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RecoderFactory struct{}
|
||||||
|
|
||||||
|
var _ recoder.Factory = (*RecoderFactory)(nil)
|
||||||
|
|
||||||
|
func NewRecoderFactory() *RecoderFactory {
|
||||||
|
return &RecoderFactory{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (RecoderFactory) New(ctx context.Context, cfg recoder.Config) (recoder.Recoder, error) {
|
||||||
|
return &Recoder{}, nil
|
||||||
|
}
|
9
pkg/recoder/input.go
Normal file
9
pkg/recoder/input.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package recoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Input interface {
|
||||||
|
io.Closer
|
||||||
|
}
|
@@ -1,11 +1,11 @@
|
|||||||
package streamforward
|
package libav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Recoder struct {
|
type Recoder struct {
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
|
"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
@@ -9,10 +9,10 @@ import (
|
|||||||
"github.com/asticode/go-astiav"
|
"github.com/asticode/go-astiav"
|
||||||
"github.com/asticode/go-astikit"
|
"github.com/asticode/go-astikit"
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/recoder/types"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OutputConfig = types.OutputConfig
|
type OutputConfig = recoder.OutputConfig
|
||||||
|
|
||||||
type Output struct {
|
type Output struct {
|
||||||
*astikit.Closer
|
*astikit.Closer
|
@@ -9,10 +9,11 @@ import (
|
|||||||
"github.com/asticode/go-astiav"
|
"github.com/asticode/go-astiav"
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/recoder/types"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RecoderConfig = types.RecoderConfig
|
type RecoderConfig = recoder.Config
|
||||||
type Packet = types.Packet
|
type Packet = types.Packet
|
||||||
|
|
||||||
type RecoderStats struct {
|
type RecoderStats struct {
|
@@ -1,11 +1,11 @@
|
|||||||
package streamforward
|
package libav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RecoderFactory struct{}
|
type RecoderFactory struct{}
|
@@ -703,6 +703,176 @@ func (x *NewRecoderReply) GetId() uint64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CloseInputRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
InputID uint64 `protobuf:"varint,1,opt,name=inputID,proto3" json:"inputID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseInputRequest) Reset() {
|
||||||
|
*x = CloseInputRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_recoder_proto_msgTypes[13]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseInputRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CloseInputRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CloseInputRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_recoder_proto_msgTypes[13]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CloseInputRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CloseInputRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_recoder_proto_rawDescGZIP(), []int{13}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseInputRequest) GetInputID() uint64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.InputID
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseInputReply struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseInputReply) Reset() {
|
||||||
|
*x = CloseInputReply{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_recoder_proto_msgTypes[14]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseInputReply) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CloseInputReply) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CloseInputReply) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_recoder_proto_msgTypes[14]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CloseInputReply.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CloseInputReply) Descriptor() ([]byte, []int) {
|
||||||
|
return file_recoder_proto_rawDescGZIP(), []int{14}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseOutputRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
OutputID uint64 `protobuf:"varint,2,opt,name=outputID,proto3" json:"outputID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseOutputRequest) Reset() {
|
||||||
|
*x = CloseOutputRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_recoder_proto_msgTypes[15]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseOutputRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CloseOutputRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CloseOutputRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_recoder_proto_msgTypes[15]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CloseOutputRequest.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CloseOutputRequest) Descriptor() ([]byte, []int) {
|
||||||
|
return file_recoder_proto_rawDescGZIP(), []int{15}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseOutputRequest) GetOutputID() uint64 {
|
||||||
|
if x != nil {
|
||||||
|
return x.OutputID
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseOutputReply struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseOutputReply) Reset() {
|
||||||
|
*x = CloseOutputReply{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_recoder_proto_msgTypes[16]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *CloseOutputReply) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*CloseOutputReply) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *CloseOutputReply) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_recoder_proto_msgTypes[16]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use CloseOutputReply.ProtoReflect.Descriptor instead.
|
||||||
|
func (*CloseOutputReply) Descriptor() ([]byte, []int) {
|
||||||
|
return file_recoder_proto_rawDescGZIP(), []int{16}
|
||||||
|
}
|
||||||
|
|
||||||
type GetRecoderStatsRequest struct {
|
type GetRecoderStatsRequest struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -714,7 +884,7 @@ type GetRecoderStatsRequest struct {
|
|||||||
func (x *GetRecoderStatsRequest) Reset() {
|
func (x *GetRecoderStatsRequest) Reset() {
|
||||||
*x = GetRecoderStatsRequest{}
|
*x = GetRecoderStatsRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_recoder_proto_msgTypes[13]
|
mi := &file_recoder_proto_msgTypes[17]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -727,7 +897,7 @@ func (x *GetRecoderStatsRequest) String() string {
|
|||||||
func (*GetRecoderStatsRequest) ProtoMessage() {}
|
func (*GetRecoderStatsRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *GetRecoderStatsRequest) ProtoReflect() protoreflect.Message {
|
func (x *GetRecoderStatsRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_recoder_proto_msgTypes[13]
|
mi := &file_recoder_proto_msgTypes[17]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -740,7 +910,7 @@ func (x *GetRecoderStatsRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use GetRecoderStatsRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use GetRecoderStatsRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*GetRecoderStatsRequest) Descriptor() ([]byte, []int) {
|
func (*GetRecoderStatsRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_recoder_proto_rawDescGZIP(), []int{13}
|
return file_recoder_proto_rawDescGZIP(), []int{17}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetRecoderStatsRequest) GetRecoderID() uint64 {
|
func (x *GetRecoderStatsRequest) GetRecoderID() uint64 {
|
||||||
@@ -762,7 +932,7 @@ type GetRecoderStatsReply struct {
|
|||||||
func (x *GetRecoderStatsReply) Reset() {
|
func (x *GetRecoderStatsReply) Reset() {
|
||||||
*x = GetRecoderStatsReply{}
|
*x = GetRecoderStatsReply{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_recoder_proto_msgTypes[14]
|
mi := &file_recoder_proto_msgTypes[18]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -775,7 +945,7 @@ func (x *GetRecoderStatsReply) String() string {
|
|||||||
func (*GetRecoderStatsReply) ProtoMessage() {}
|
func (*GetRecoderStatsReply) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *GetRecoderStatsReply) ProtoReflect() protoreflect.Message {
|
func (x *GetRecoderStatsReply) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_recoder_proto_msgTypes[14]
|
mi := &file_recoder_proto_msgTypes[18]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -788,7 +958,7 @@ func (x *GetRecoderStatsReply) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use GetRecoderStatsReply.ProtoReflect.Descriptor instead.
|
// Deprecated: Use GetRecoderStatsReply.ProtoReflect.Descriptor instead.
|
||||||
func (*GetRecoderStatsReply) Descriptor() ([]byte, []int) {
|
func (*GetRecoderStatsReply) Descriptor() ([]byte, []int) {
|
||||||
return file_recoder_proto_rawDescGZIP(), []int{14}
|
return file_recoder_proto_rawDescGZIP(), []int{18}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *GetRecoderStatsReply) GetBytesCountRead() uint64 {
|
func (x *GetRecoderStatsReply) GetBytesCountRead() uint64 {
|
||||||
@@ -818,7 +988,7 @@ type StartRecodingRequest struct {
|
|||||||
func (x *StartRecodingRequest) Reset() {
|
func (x *StartRecodingRequest) Reset() {
|
||||||
*x = StartRecodingRequest{}
|
*x = StartRecodingRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_recoder_proto_msgTypes[15]
|
mi := &file_recoder_proto_msgTypes[19]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -831,7 +1001,7 @@ func (x *StartRecodingRequest) String() string {
|
|||||||
func (*StartRecodingRequest) ProtoMessage() {}
|
func (*StartRecodingRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *StartRecodingRequest) ProtoReflect() protoreflect.Message {
|
func (x *StartRecodingRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_recoder_proto_msgTypes[15]
|
mi := &file_recoder_proto_msgTypes[19]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -844,7 +1014,7 @@ func (x *StartRecodingRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use StartRecodingRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use StartRecodingRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*StartRecodingRequest) Descriptor() ([]byte, []int) {
|
func (*StartRecodingRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_recoder_proto_rawDescGZIP(), []int{15}
|
return file_recoder_proto_rawDescGZIP(), []int{19}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *StartRecodingRequest) GetRecoderID() uint64 {
|
func (x *StartRecodingRequest) GetRecoderID() uint64 {
|
||||||
@@ -877,7 +1047,7 @@ type StartRecodingReply struct {
|
|||||||
func (x *StartRecodingReply) Reset() {
|
func (x *StartRecodingReply) Reset() {
|
||||||
*x = StartRecodingReply{}
|
*x = StartRecodingReply{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_recoder_proto_msgTypes[16]
|
mi := &file_recoder_proto_msgTypes[20]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -890,7 +1060,7 @@ func (x *StartRecodingReply) String() string {
|
|||||||
func (*StartRecodingReply) ProtoMessage() {}
|
func (*StartRecodingReply) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *StartRecodingReply) ProtoReflect() protoreflect.Message {
|
func (x *StartRecodingReply) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_recoder_proto_msgTypes[16]
|
mi := &file_recoder_proto_msgTypes[20]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -903,7 +1073,7 @@ func (x *StartRecodingReply) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use StartRecodingReply.ProtoReflect.Descriptor instead.
|
// Deprecated: Use StartRecodingReply.ProtoReflect.Descriptor instead.
|
||||||
func (*StartRecodingReply) Descriptor() ([]byte, []int) {
|
func (*StartRecodingReply) Descriptor() ([]byte, []int) {
|
||||||
return file_recoder_proto_rawDescGZIP(), []int{16}
|
return file_recoder_proto_rawDescGZIP(), []int{20}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RecodingEndedChanRequest struct {
|
type RecodingEndedChanRequest struct {
|
||||||
@@ -917,7 +1087,7 @@ type RecodingEndedChanRequest struct {
|
|||||||
func (x *RecodingEndedChanRequest) Reset() {
|
func (x *RecodingEndedChanRequest) Reset() {
|
||||||
*x = RecodingEndedChanRequest{}
|
*x = RecodingEndedChanRequest{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_recoder_proto_msgTypes[17]
|
mi := &file_recoder_proto_msgTypes[21]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -930,7 +1100,7 @@ func (x *RecodingEndedChanRequest) String() string {
|
|||||||
func (*RecodingEndedChanRequest) ProtoMessage() {}
|
func (*RecodingEndedChanRequest) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RecodingEndedChanRequest) ProtoReflect() protoreflect.Message {
|
func (x *RecodingEndedChanRequest) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_recoder_proto_msgTypes[17]
|
mi := &file_recoder_proto_msgTypes[21]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -943,7 +1113,7 @@ func (x *RecodingEndedChanRequest) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RecodingEndedChanRequest.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RecodingEndedChanRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*RecodingEndedChanRequest) Descriptor() ([]byte, []int) {
|
func (*RecodingEndedChanRequest) Descriptor() ([]byte, []int) {
|
||||||
return file_recoder_proto_rawDescGZIP(), []int{17}
|
return file_recoder_proto_rawDescGZIP(), []int{21}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RecodingEndedChanRequest) GetRecoderID() uint64 {
|
func (x *RecodingEndedChanRequest) GetRecoderID() uint64 {
|
||||||
@@ -962,7 +1132,7 @@ type RecodingEndedChanReply struct {
|
|||||||
func (x *RecodingEndedChanReply) Reset() {
|
func (x *RecodingEndedChanReply) Reset() {
|
||||||
*x = RecodingEndedChanReply{}
|
*x = RecodingEndedChanReply{}
|
||||||
if protoimpl.UnsafeEnabled {
|
if protoimpl.UnsafeEnabled {
|
||||||
mi := &file_recoder_proto_msgTypes[18]
|
mi := &file_recoder_proto_msgTypes[22]
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
@@ -975,7 +1145,7 @@ func (x *RecodingEndedChanReply) String() string {
|
|||||||
func (*RecodingEndedChanReply) ProtoMessage() {}
|
func (*RecodingEndedChanReply) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *RecodingEndedChanReply) ProtoReflect() protoreflect.Message {
|
func (x *RecodingEndedChanReply) ProtoReflect() protoreflect.Message {
|
||||||
mi := &file_recoder_proto_msgTypes[18]
|
mi := &file_recoder_proto_msgTypes[22]
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
@@ -988,7 +1158,7 @@ func (x *RecodingEndedChanReply) ProtoReflect() protoreflect.Message {
|
|||||||
|
|
||||||
// Deprecated: Use RecodingEndedChanReply.ProtoReflect.Descriptor instead.
|
// Deprecated: Use RecodingEndedChanReply.ProtoReflect.Descriptor instead.
|
||||||
func (*RecodingEndedChanReply) Descriptor() ([]byte, []int) {
|
func (*RecodingEndedChanReply) Descriptor() ([]byte, []int) {
|
||||||
return file_recoder_proto_rawDescGZIP(), []int{18}
|
return file_recoder_proto_rawDescGZIP(), []int{22}
|
||||||
}
|
}
|
||||||
|
|
||||||
var File_recoder_proto protoreflect.FileDescriptor
|
var File_recoder_proto protoreflect.FileDescriptor
|
||||||
@@ -1037,84 +1207,102 @@ var file_recoder_proto_rawDesc = []byte{
|
|||||||
0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
||||||
0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x21, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x52, 0x65,
|
0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x21, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x52, 0x65,
|
||||||
0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x36, 0x0a, 0x16, 0x47, 0x65,
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2d, 0x0a, 0x11, 0x43, 0x6c,
|
||||||
0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71,
|
0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49,
|
0x18, 0x0a, 0x07, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
|
||||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
0x52, 0x07, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x44, 0x22, 0x11, 0x0a, 0x0f, 0x43, 0x6c, 0x6f,
|
||||||
0x49, 0x44, 0x22, 0x68, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x30, 0x0a, 0x12,
|
||||||
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x62, 0x79,
|
0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01,
|
0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x02,
|
||||||
0x28, 0x04, 0x52, 0x0e, 0x62, 0x79, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65,
|
0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x44, 0x22, 0x12,
|
||||||
0x61, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,
|
0x0a, 0x10, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x70,
|
||||||
0x57, 0x72, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x62, 0x79, 0x74,
|
0x6c, 0x79, 0x22, 0x36, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
||||||
0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x22, 0x6a, 0x0a, 0x14,
|
0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09,
|
||||||
0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71,
|
0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49,
|
0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44, 0x22, 0x68, 0x0a, 0x14, 0x47, 0x65,
|
||||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x70,
|
||||||
0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20,
|
0x6c, 0x79, 0x12, 0x26, 0x0a, 0x0e, 0x62, 0x79, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74,
|
||||||
0x01, 0x28, 0x04, 0x52, 0x07, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08,
|
0x52, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x62, 0x79, 0x74, 0x65,
|
||||||
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08,
|
0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x61, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x62, 0x79,
|
||||||
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x44, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72,
|
0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x57, 0x72, 0x6f, 0x74, 0x65, 0x18, 0x02, 0x20,
|
||||||
0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x38,
|
0x01, 0x28, 0x04, 0x52, 0x0f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x57,
|
||||||
0x0a, 0x18, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x43,
|
0x72, 0x6f, 0x74, 0x65, 0x22, 0x6a, 0x0a, 0x14, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x63,
|
||||||
0x68, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65,
|
0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09,
|
||||||
0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72,
|
0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||||
0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44, 0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f,
|
0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e,
|
||||||
0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, 0x70,
|
0x70, 0x75, 0x74, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x69, 0x6e, 0x70,
|
||||||
0x6c, 0x79, 0x2a, 0xc3, 0x01, 0x0a, 0x0c, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65,
|
0x75, 0x74, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x44,
|
||||||
0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x49, 0x44,
|
||||||
0x76, 0x65, 0x6c, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67,
|
0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e,
|
||||||
0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x46, 0x61, 0x74, 0x61, 0x6c, 0x10, 0x01,
|
0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x38, 0x0a, 0x18, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69,
|
||||||
0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c,
|
0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x50, 0x61, 0x6e, 0x69, 0x63, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x67, 0x69,
|
0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18,
|
||||||
0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, 0x12, 0x14,
|
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x49, 0x44,
|
||||||
0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x57, 0x61,
|
0x22, 0x18, 0x0a, 0x16, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65,
|
||||||
0x72, 0x6e, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c,
|
0x64, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x2a, 0xc3, 0x01, 0x0a, 0x0c, 0x4c,
|
||||||
0x65, 0x76, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f,
|
0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x14, 0x0a, 0x10, 0x4c,
|
||||||
0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x44, 0x65, 0x62, 0x75, 0x67, 0x10,
|
0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x4e, 0x6f, 0x6e, 0x65, 0x10,
|
||||||
0x06, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65,
|
0x00, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65,
|
||||||
0x6c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x10, 0x07, 0x32, 0xee, 0x04, 0x0a, 0x07, 0x52, 0x65, 0x63,
|
0x6c, 0x46, 0x61, 0x74, 0x61, 0x6c, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x67,
|
||||||
0x6f, 0x64, 0x65, 0x72, 0x12, 0x5d, 0x0a, 0x0f, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x69,
|
0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x50, 0x61, 0x6e, 0x69, 0x63, 0x10, 0x02, 0x12,
|
||||||
0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x24, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65,
|
0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x45,
|
||||||
0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e,
|
0x72, 0x72, 0x6f, 0x72, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e,
|
||||||
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e,
|
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x57, 0x61, 0x72, 0x6e, 0x10, 0x04, 0x12, 0x14, 0x0a, 0x10,
|
||||||
0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74,
|
0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x49, 0x6e, 0x66, 0x6f,
|
||||||
0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x70, 0x6c,
|
0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76,
|
||||||
0x79, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12,
|
0x65, 0x6c, 0x44, 0x65, 0x62, 0x75, 0x67, 0x10, 0x06, 0x12, 0x15, 0x0a, 0x11, 0x4c, 0x6f, 0x67,
|
||||||
0x1d, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e,
|
0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x63, 0x65, 0x10, 0x07,
|
||||||
0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b,
|
0x32, 0x91, 0x06, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x5d, 0x0a, 0x0f,
|
||||||
0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65,
|
0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12,
|
||||||
0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4b, 0x0a,
|
0x24, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53,
|
||||||
0x09, 0x4e, 0x65, 0x77, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x63,
|
0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65,
|
||||||
0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x75, 0x74,
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f,
|
||||||
0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x72, 0x65, 0x63,
|
0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x4c,
|
||||||
0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x75, 0x74,
|
0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x08, 0x4e,
|
||||||
0x70, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0a, 0x4e, 0x65,
|
0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x1d, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65,
|
||||||
0x77, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64,
|
0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52,
|
||||||
0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x52, 0x65, 0x63, 0x6f, 0x64,
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
||||||
0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x63, 0x6f,
|
0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65,
|
||||||
0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x4e, 0x65, 0x77, 0x52, 0x65, 0x63, 0x6f,
|
0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x09, 0x4e, 0x65, 0x77, 0x4f, 0x75, 0x74, 0x70,
|
||||||
0x64, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0f, 0x47, 0x65,
|
0x75, 0x74, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70,
|
||||||
0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x24, 0x2e,
|
0x63, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74,
|
0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70,
|
||||||
0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
|
0x63, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||||
0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72,
|
0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0a, 0x4e, 0x65, 0x77, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72,
|
||||||
0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61,
|
0x12, 0x1f, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e,
|
||||||
0x74, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0d, 0x53, 0x74, 0x61,
|
0x4e, 0x65, 0x77, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x72, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x22, 0x2e, 0x72, 0x65, 0x63,
|
0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63,
|
||||||
0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52,
|
0x2e, 0x4e, 0x65, 0x77, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||||
0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20,
|
0x22, 0x00, 0x12, 0x4e, 0x0a, 0x0a, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74,
|
||||||
0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74,
|
0x12, 0x1f, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e,
|
||||||
0x61, 0x72, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
0x43, 0x6c, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||||
0x22, 0x00, 0x12, 0x65, 0x0a, 0x11, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e,
|
0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63,
|
||||||
0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x12, 0x26, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65,
|
0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||||
0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x45,
|
0x22, 0x00, 0x12, 0x51, 0x0a, 0x0b, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75,
|
||||||
0x6e, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
0x74, 0x12, 0x20, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63,
|
||||||
0x24, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52,
|
0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e,
|
0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72,
|
||||||
0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x42, 0x11, 0x5a, 0x0f, 0x67, 0x6f, 0x2f,
|
0x70, 0x63, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x65,
|
||||||
0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72,
|
0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f,
|
||||||
0x6f, 0x74, 0x6f, 0x33,
|
0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x24, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64,
|
||||||
|
0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64,
|
||||||
|
0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22,
|
||||||
|
0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65,
|
||||||
|
0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x70,
|
||||||
|
0x6c, 0x79, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x63,
|
||||||
|
0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x22, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f,
|
||||||
|
0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69,
|
||||||
|
0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x63, 0x6f,
|
||||||
|
0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65,
|
||||||
|
0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x65, 0x0a,
|
||||||
|
0x11, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x68,
|
||||||
|
0x61, 0x6e, 0x12, 0x26, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70,
|
||||||
|
0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x43,
|
||||||
|
0x68, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x72, 0x65, 0x63,
|
||||||
|
0x6f, 0x64, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x64, 0x69,
|
||||||
|
0x6e, 0x67, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x52, 0x65, 0x70, 0x6c, 0x79,
|
||||||
|
0x22, 0x00, 0x30, 0x01, 0x42, 0x11, 0x5a, 0x0f, 0x67, 0x6f, 0x2f, 0x72, 0x65, 0x63, 0x6f, 0x64,
|
||||||
|
0x65, 0x72, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -1130,7 +1318,7 @@ func file_recoder_proto_rawDescGZIP() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var file_recoder_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
var file_recoder_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var file_recoder_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
|
var file_recoder_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
|
||||||
var file_recoder_proto_goTypes = []interface{}{
|
var file_recoder_proto_goTypes = []interface{}{
|
||||||
(LoggingLevel)(0), // 0: recoder_grpc.LoggingLevel
|
(LoggingLevel)(0), // 0: recoder_grpc.LoggingLevel
|
||||||
(*SetLoggingLevelRequest)(nil), // 1: recoder_grpc.SetLoggingLevelRequest
|
(*SetLoggingLevelRequest)(nil), // 1: recoder_grpc.SetLoggingLevelRequest
|
||||||
@@ -1146,12 +1334,16 @@ var file_recoder_proto_goTypes = []interface{}{
|
|||||||
(*RecoderConfig)(nil), // 11: recoder_grpc.RecoderConfig
|
(*RecoderConfig)(nil), // 11: recoder_grpc.RecoderConfig
|
||||||
(*NewRecoderRequest)(nil), // 12: recoder_grpc.NewRecoderRequest
|
(*NewRecoderRequest)(nil), // 12: recoder_grpc.NewRecoderRequest
|
||||||
(*NewRecoderReply)(nil), // 13: recoder_grpc.NewRecoderReply
|
(*NewRecoderReply)(nil), // 13: recoder_grpc.NewRecoderReply
|
||||||
(*GetRecoderStatsRequest)(nil), // 14: recoder_grpc.GetRecoderStatsRequest
|
(*CloseInputRequest)(nil), // 14: recoder_grpc.CloseInputRequest
|
||||||
(*GetRecoderStatsReply)(nil), // 15: recoder_grpc.GetRecoderStatsReply
|
(*CloseInputReply)(nil), // 15: recoder_grpc.CloseInputReply
|
||||||
(*StartRecodingRequest)(nil), // 16: recoder_grpc.StartRecodingRequest
|
(*CloseOutputRequest)(nil), // 16: recoder_grpc.CloseOutputRequest
|
||||||
(*StartRecodingReply)(nil), // 17: recoder_grpc.StartRecodingReply
|
(*CloseOutputReply)(nil), // 17: recoder_grpc.CloseOutputReply
|
||||||
(*RecodingEndedChanRequest)(nil), // 18: recoder_grpc.RecodingEndedChanRequest
|
(*GetRecoderStatsRequest)(nil), // 18: recoder_grpc.GetRecoderStatsRequest
|
||||||
(*RecodingEndedChanReply)(nil), // 19: recoder_grpc.RecodingEndedChanReply
|
(*GetRecoderStatsReply)(nil), // 19: recoder_grpc.GetRecoderStatsReply
|
||||||
|
(*StartRecodingRequest)(nil), // 20: recoder_grpc.StartRecodingRequest
|
||||||
|
(*StartRecodingReply)(nil), // 21: recoder_grpc.StartRecodingReply
|
||||||
|
(*RecodingEndedChanRequest)(nil), // 22: recoder_grpc.RecodingEndedChanRequest
|
||||||
|
(*RecodingEndedChanReply)(nil), // 23: recoder_grpc.RecodingEndedChanReply
|
||||||
}
|
}
|
||||||
var file_recoder_proto_depIdxs = []int32{
|
var file_recoder_proto_depIdxs = []int32{
|
||||||
0, // 0: recoder_grpc.SetLoggingLevelRequest.level:type_name -> recoder_grpc.LoggingLevel
|
0, // 0: recoder_grpc.SetLoggingLevelRequest.level:type_name -> recoder_grpc.LoggingLevel
|
||||||
@@ -1164,18 +1356,22 @@ var file_recoder_proto_depIdxs = []int32{
|
|||||||
6, // 7: recoder_grpc.Recoder.NewInput:input_type -> recoder_grpc.NewInputRequest
|
6, // 7: recoder_grpc.Recoder.NewInput:input_type -> recoder_grpc.NewInputRequest
|
||||||
9, // 8: recoder_grpc.Recoder.NewOutput:input_type -> recoder_grpc.NewOutputRequest
|
9, // 8: recoder_grpc.Recoder.NewOutput:input_type -> recoder_grpc.NewOutputRequest
|
||||||
12, // 9: recoder_grpc.Recoder.NewRecoder:input_type -> recoder_grpc.NewRecoderRequest
|
12, // 9: recoder_grpc.Recoder.NewRecoder:input_type -> recoder_grpc.NewRecoderRequest
|
||||||
14, // 10: recoder_grpc.Recoder.GetRecoderStats:input_type -> recoder_grpc.GetRecoderStatsRequest
|
14, // 10: recoder_grpc.Recoder.CloseInput:input_type -> recoder_grpc.CloseInputRequest
|
||||||
16, // 11: recoder_grpc.Recoder.StartRecoding:input_type -> recoder_grpc.StartRecodingRequest
|
16, // 11: recoder_grpc.Recoder.CloseOutput:input_type -> recoder_grpc.CloseOutputRequest
|
||||||
18, // 12: recoder_grpc.Recoder.RecodingEndedChan:input_type -> recoder_grpc.RecodingEndedChanRequest
|
18, // 12: recoder_grpc.Recoder.GetRecoderStats:input_type -> recoder_grpc.GetRecoderStatsRequest
|
||||||
2, // 13: recoder_grpc.Recoder.SetLoggingLevel:output_type -> recoder_grpc.SetLoggingLevelReply
|
20, // 13: recoder_grpc.Recoder.StartRecoding:input_type -> recoder_grpc.StartRecodingRequest
|
||||||
7, // 14: recoder_grpc.Recoder.NewInput:output_type -> recoder_grpc.NewInputReply
|
22, // 14: recoder_grpc.Recoder.RecodingEndedChan:input_type -> recoder_grpc.RecodingEndedChanRequest
|
||||||
10, // 15: recoder_grpc.Recoder.NewOutput:output_type -> recoder_grpc.NewOutputReply
|
2, // 15: recoder_grpc.Recoder.SetLoggingLevel:output_type -> recoder_grpc.SetLoggingLevelReply
|
||||||
13, // 16: recoder_grpc.Recoder.NewRecoder:output_type -> recoder_grpc.NewRecoderReply
|
7, // 16: recoder_grpc.Recoder.NewInput:output_type -> recoder_grpc.NewInputReply
|
||||||
15, // 17: recoder_grpc.Recoder.GetRecoderStats:output_type -> recoder_grpc.GetRecoderStatsReply
|
10, // 17: recoder_grpc.Recoder.NewOutput:output_type -> recoder_grpc.NewOutputReply
|
||||||
17, // 18: recoder_grpc.Recoder.StartRecoding:output_type -> recoder_grpc.StartRecodingReply
|
13, // 18: recoder_grpc.Recoder.NewRecoder:output_type -> recoder_grpc.NewRecoderReply
|
||||||
19, // 19: recoder_grpc.Recoder.RecodingEndedChan:output_type -> recoder_grpc.RecodingEndedChanReply
|
15, // 19: recoder_grpc.Recoder.CloseInput:output_type -> recoder_grpc.CloseInputReply
|
||||||
13, // [13:20] is the sub-list for method output_type
|
17, // 20: recoder_grpc.Recoder.CloseOutput:output_type -> recoder_grpc.CloseOutputReply
|
||||||
6, // [6:13] is the sub-list for method input_type
|
19, // 21: recoder_grpc.Recoder.GetRecoderStats:output_type -> recoder_grpc.GetRecoderStatsReply
|
||||||
|
21, // 22: recoder_grpc.Recoder.StartRecoding:output_type -> recoder_grpc.StartRecodingReply
|
||||||
|
23, // 23: recoder_grpc.Recoder.RecodingEndedChan:output_type -> recoder_grpc.RecodingEndedChanReply
|
||||||
|
15, // [15:24] is the sub-list for method output_type
|
||||||
|
6, // [6:15] is the sub-list for method input_type
|
||||||
6, // [6:6] is the sub-list for extension type_name
|
6, // [6:6] is the sub-list for extension type_name
|
||||||
6, // [6:6] is the sub-list for extension extendee
|
6, // [6:6] is the sub-list for extension extendee
|
||||||
0, // [0:6] is the sub-list for field type_name
|
0, // [0:6] is the sub-list for field type_name
|
||||||
@@ -1344,7 +1540,7 @@ func file_recoder_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_recoder_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
file_recoder_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*GetRecoderStatsRequest); i {
|
switch v := v.(*CloseInputRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1356,7 +1552,7 @@ func file_recoder_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_recoder_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
file_recoder_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*GetRecoderStatsReply); i {
|
switch v := v.(*CloseInputReply); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1368,7 +1564,7 @@ func file_recoder_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_recoder_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
file_recoder_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*StartRecodingRequest); i {
|
switch v := v.(*CloseOutputRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1380,7 +1576,7 @@ func file_recoder_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_recoder_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
|
file_recoder_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*StartRecodingReply); i {
|
switch v := v.(*CloseOutputReply); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1392,7 +1588,7 @@ func file_recoder_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_recoder_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
|
file_recoder_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*RecodingEndedChanRequest); i {
|
switch v := v.(*GetRecoderStatsRequest); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
case 1:
|
case 1:
|
||||||
@@ -1404,6 +1600,54 @@ func file_recoder_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
file_recoder_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
|
file_recoder_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*GetRecoderStatsReply); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_recoder_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*StartRecodingRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_recoder_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*StartRecodingReply); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_recoder_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*RecodingEndedChanRequest); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_recoder_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
|
||||||
switch v := v.(*RecodingEndedChanReply); i {
|
switch v := v.(*RecodingEndedChanReply); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
@@ -1425,7 +1669,7 @@ func file_recoder_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_recoder_proto_rawDesc,
|
RawDescriptor: file_recoder_proto_rawDesc,
|
||||||
NumEnums: 1,
|
NumEnums: 1,
|
||||||
NumMessages: 19,
|
NumMessages: 23,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 1,
|
NumServices: 1,
|
||||||
},
|
},
|
@@ -26,6 +26,8 @@ type RecoderClient interface {
|
|||||||
NewInput(ctx context.Context, in *NewInputRequest, opts ...grpc.CallOption) (*NewInputReply, error)
|
NewInput(ctx context.Context, in *NewInputRequest, opts ...grpc.CallOption) (*NewInputReply, error)
|
||||||
NewOutput(ctx context.Context, in *NewOutputRequest, opts ...grpc.CallOption) (*NewOutputReply, error)
|
NewOutput(ctx context.Context, in *NewOutputRequest, opts ...grpc.CallOption) (*NewOutputReply, error)
|
||||||
NewRecoder(ctx context.Context, in *NewRecoderRequest, opts ...grpc.CallOption) (*NewRecoderReply, error)
|
NewRecoder(ctx context.Context, in *NewRecoderRequest, opts ...grpc.CallOption) (*NewRecoderReply, error)
|
||||||
|
CloseInput(ctx context.Context, in *CloseInputRequest, opts ...grpc.CallOption) (*CloseInputReply, error)
|
||||||
|
CloseOutput(ctx context.Context, in *CloseOutputRequest, opts ...grpc.CallOption) (*CloseOutputReply, error)
|
||||||
GetRecoderStats(ctx context.Context, in *GetRecoderStatsRequest, opts ...grpc.CallOption) (*GetRecoderStatsReply, error)
|
GetRecoderStats(ctx context.Context, in *GetRecoderStatsRequest, opts ...grpc.CallOption) (*GetRecoderStatsReply, error)
|
||||||
StartRecoding(ctx context.Context, in *StartRecodingRequest, opts ...grpc.CallOption) (*StartRecodingReply, error)
|
StartRecoding(ctx context.Context, in *StartRecodingRequest, opts ...grpc.CallOption) (*StartRecodingReply, error)
|
||||||
RecodingEndedChan(ctx context.Context, in *RecodingEndedChanRequest, opts ...grpc.CallOption) (Recoder_RecodingEndedChanClient, error)
|
RecodingEndedChan(ctx context.Context, in *RecodingEndedChanRequest, opts ...grpc.CallOption) (Recoder_RecodingEndedChanClient, error)
|
||||||
@@ -75,6 +77,24 @@ func (c *recoderClient) NewRecoder(ctx context.Context, in *NewRecoderRequest, o
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *recoderClient) CloseInput(ctx context.Context, in *CloseInputRequest, opts ...grpc.CallOption) (*CloseInputReply, error) {
|
||||||
|
out := new(CloseInputReply)
|
||||||
|
err := c.cc.Invoke(ctx, "/recoder_grpc.Recoder/CloseInput", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *recoderClient) CloseOutput(ctx context.Context, in *CloseOutputRequest, opts ...grpc.CallOption) (*CloseOutputReply, error) {
|
||||||
|
out := new(CloseOutputReply)
|
||||||
|
err := c.cc.Invoke(ctx, "/recoder_grpc.Recoder/CloseOutput", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *recoderClient) GetRecoderStats(ctx context.Context, in *GetRecoderStatsRequest, opts ...grpc.CallOption) (*GetRecoderStatsReply, error) {
|
func (c *recoderClient) GetRecoderStats(ctx context.Context, in *GetRecoderStatsRequest, opts ...grpc.CallOption) (*GetRecoderStatsReply, error) {
|
||||||
out := new(GetRecoderStatsReply)
|
out := new(GetRecoderStatsReply)
|
||||||
err := c.cc.Invoke(ctx, "/recoder_grpc.Recoder/GetRecoderStats", in, out, opts...)
|
err := c.cc.Invoke(ctx, "/recoder_grpc.Recoder/GetRecoderStats", in, out, opts...)
|
||||||
@@ -133,6 +153,8 @@ type RecoderServer interface {
|
|||||||
NewInput(context.Context, *NewInputRequest) (*NewInputReply, error)
|
NewInput(context.Context, *NewInputRequest) (*NewInputReply, error)
|
||||||
NewOutput(context.Context, *NewOutputRequest) (*NewOutputReply, error)
|
NewOutput(context.Context, *NewOutputRequest) (*NewOutputReply, error)
|
||||||
NewRecoder(context.Context, *NewRecoderRequest) (*NewRecoderReply, error)
|
NewRecoder(context.Context, *NewRecoderRequest) (*NewRecoderReply, error)
|
||||||
|
CloseInput(context.Context, *CloseInputRequest) (*CloseInputReply, error)
|
||||||
|
CloseOutput(context.Context, *CloseOutputRequest) (*CloseOutputReply, error)
|
||||||
GetRecoderStats(context.Context, *GetRecoderStatsRequest) (*GetRecoderStatsReply, error)
|
GetRecoderStats(context.Context, *GetRecoderStatsRequest) (*GetRecoderStatsReply, error)
|
||||||
StartRecoding(context.Context, *StartRecodingRequest) (*StartRecodingReply, error)
|
StartRecoding(context.Context, *StartRecodingRequest) (*StartRecodingReply, error)
|
||||||
RecodingEndedChan(*RecodingEndedChanRequest, Recoder_RecodingEndedChanServer) error
|
RecodingEndedChan(*RecodingEndedChanRequest, Recoder_RecodingEndedChanServer) error
|
||||||
@@ -155,6 +177,12 @@ func (UnimplementedRecoderServer) NewOutput(context.Context, *NewOutputRequest)
|
|||||||
func (UnimplementedRecoderServer) NewRecoder(context.Context, *NewRecoderRequest) (*NewRecoderReply, error) {
|
func (UnimplementedRecoderServer) NewRecoder(context.Context, *NewRecoderRequest) (*NewRecoderReply, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method NewRecoder not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method NewRecoder not implemented")
|
||||||
}
|
}
|
||||||
|
func (UnimplementedRecoderServer) CloseInput(context.Context, *CloseInputRequest) (*CloseInputReply, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CloseInput not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedRecoderServer) CloseOutput(context.Context, *CloseOutputRequest) (*CloseOutputReply, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method CloseOutput not implemented")
|
||||||
|
}
|
||||||
func (UnimplementedRecoderServer) GetRecoderStats(context.Context, *GetRecoderStatsRequest) (*GetRecoderStatsReply, error) {
|
func (UnimplementedRecoderServer) GetRecoderStats(context.Context, *GetRecoderStatsRequest) (*GetRecoderStatsReply, error) {
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetRecoderStats not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetRecoderStats not implemented")
|
||||||
}
|
}
|
||||||
@@ -249,6 +277,42 @@ func _Recoder_NewRecoder_Handler(srv interface{}, ctx context.Context, dec func(
|
|||||||
return interceptor(ctx, in, info, handler)
|
return interceptor(ctx, in, info, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _Recoder_CloseInput_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CloseInputRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RecoderServer).CloseInput(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/recoder_grpc.Recoder/CloseInput",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RecoderServer).CloseInput(ctx, req.(*CloseInputRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Recoder_CloseOutput_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(CloseOutputRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(RecoderServer).CloseOutput(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/recoder_grpc.Recoder/CloseOutput",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(RecoderServer).CloseOutput(ctx, req.(*CloseOutputRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
func _Recoder_GetRecoderStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
func _Recoder_GetRecoderStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
in := new(GetRecoderStatsRequest)
|
in := new(GetRecoderStatsRequest)
|
||||||
if err := dec(in); err != nil {
|
if err := dec(in); err != nil {
|
||||||
@@ -329,6 +393,14 @@ var Recoder_ServiceDesc = grpc.ServiceDesc{
|
|||||||
MethodName: "NewRecoder",
|
MethodName: "NewRecoder",
|
||||||
Handler: _Recoder_NewRecoder_Handler,
|
Handler: _Recoder_NewRecoder_Handler,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CloseInput",
|
||||||
|
Handler: _Recoder_CloseInput_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "CloseOutput",
|
||||||
|
Handler: _Recoder_CloseOutput_Handler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
MethodName: "GetRecoderStats",
|
MethodName: "GetRecoderStats",
|
||||||
Handler: _Recoder_GetRecoderStats_Handler,
|
Handler: _Recoder_GetRecoderStats_Handler,
|
@@ -7,6 +7,8 @@ service Recoder {
|
|||||||
rpc NewInput(NewInputRequest) returns (NewInputReply) {}
|
rpc NewInput(NewInputRequest) returns (NewInputReply) {}
|
||||||
rpc NewOutput(NewOutputRequest) returns (NewOutputReply) {}
|
rpc NewOutput(NewOutputRequest) returns (NewOutputReply) {}
|
||||||
rpc NewRecoder(NewRecoderRequest) returns (NewRecoderReply) {}
|
rpc NewRecoder(NewRecoderRequest) returns (NewRecoderReply) {}
|
||||||
|
rpc CloseInput(CloseInputRequest) returns (CloseInputReply) {}
|
||||||
|
rpc CloseOutput(CloseOutputRequest) returns (CloseOutputReply) {}
|
||||||
rpc GetRecoderStats(GetRecoderStatsRequest) returns (GetRecoderStatsReply) {}
|
rpc GetRecoderStats(GetRecoderStatsRequest) returns (GetRecoderStatsReply) {}
|
||||||
rpc StartRecoding(StartRecodingRequest) returns (StartRecodingReply) {}
|
rpc StartRecoding(StartRecodingRequest) returns (StartRecodingReply) {}
|
||||||
rpc RecodingEndedChan(RecodingEndedChanRequest) returns (stream RecodingEndedChanReply) {}
|
rpc RecodingEndedChan(RecodingEndedChanRequest) returns (stream RecodingEndedChanReply) {}
|
||||||
@@ -72,6 +74,15 @@ message NewRecoderReply {
|
|||||||
uint64 id = 1;
|
uint64 id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CloseInputRequest {
|
||||||
|
uint64 inputID = 1;
|
||||||
|
}
|
||||||
|
message CloseInputReply {}
|
||||||
|
message CloseOutputRequest {
|
||||||
|
uint64 outputID = 2;
|
||||||
|
}
|
||||||
|
message CloseOutputReply {}
|
||||||
|
|
||||||
message GetRecoderStatsRequest {
|
message GetRecoderStatsRequest {
|
||||||
uint64 recoderID = 1;
|
uint64 recoderID = 1;
|
||||||
}
|
}
|
@@ -3,7 +3,7 @@ package saferecoder
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/process"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InputID = process.InputID
|
type InputID = process.InputID
|
||||||
@@ -28,3 +28,7 @@ func (p *Process) NewInputFromURL(
|
|||||||
ID: inputID,
|
ID: inputID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (input *Input) Close() error {
|
||||||
|
return input.Process.Client.CloseInput(context.Background(), input.ID)
|
||||||
|
}
|
@@ -3,7 +3,7 @@ package saferecoder
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/process"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OutputID = process.OutputID
|
type OutputID = process.OutputID
|
||||||
@@ -28,3 +28,7 @@ func (p *Process) NewOutputFromURL(
|
|||||||
ID: outputID,
|
ID: outputID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (output *Output) Close() error {
|
||||||
|
return output.Process.Client.CloseOutput(context.Background(), output.ID)
|
||||||
|
}
|
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/process"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
type processBackend = process.Recoder
|
type processBackend = process.Recoder
|
@@ -7,9 +7,8 @@ import (
|
|||||||
|
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/recoder/types"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/grpc/go/recoder_grpc"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
)
|
)
|
||||||
@@ -54,7 +53,7 @@ func (c *Client) SetLoggingLevel(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type InputConfig = types.InputConfig
|
type InputConfig = recoder.InputConfig
|
||||||
type InputID uint64
|
type InputID uint64
|
||||||
|
|
||||||
func (c *Client) NewInputFromURL(
|
func (c *Client) NewInputFromURL(
|
||||||
@@ -83,6 +82,26 @@ func (c *Client) NewInputFromURL(
|
|||||||
return InputID(resp.GetId()), nil
|
return InputID(resp.GetId()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) CloseInput(
|
||||||
|
ctx context.Context,
|
||||||
|
inputID InputID,
|
||||||
|
) error {
|
||||||
|
client, conn, err := c.grpcClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
_, err = client.CloseInput(ctx, &recoder_grpc.CloseInputRequest{
|
||||||
|
InputID: uint64(inputID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("query error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type OutputID uint64
|
type OutputID uint64
|
||||||
type OutputConfig = recoder.OutputConfig
|
type OutputConfig = recoder.OutputConfig
|
||||||
|
|
||||||
@@ -112,8 +131,28 @@ func (c *Client) NewOutputFromURL(
|
|||||||
return OutputID(resp.GetId()), nil
|
return OutputID(resp.GetId()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) CloseOutput(
|
||||||
|
ctx context.Context,
|
||||||
|
outputID OutputID,
|
||||||
|
) error {
|
||||||
|
client, conn, err := c.grpcClient()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
_, err = client.CloseOutput(ctx, &recoder_grpc.CloseOutputRequest{
|
||||||
|
OutputID: uint64(outputID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("query error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type RecoderID uint64
|
type RecoderID uint64
|
||||||
type RecoderConfig = types.RecoderConfig
|
type RecoderConfig = recoder.Config
|
||||||
|
|
||||||
func (c *Client) NewRecoder(
|
func (c *Client) NewRecoder(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
@@ -2,7 +2,7 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/grpc/go/recoder_grpc"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func logLevelGo2Protobuf(logLevel logger.Level) recoder_grpc.LoggingLevel {
|
func logLevelGo2Protobuf(logLevel logger.Level) recoder_grpc.LoggingLevel {
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/facebookincubator/go-belt"
|
"github.com/facebookincubator/go-belt"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/process/server"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
@@ -14,8 +14,8 @@ import (
|
|||||||
"github.com/facebookincubator/go-belt/tool/experimental/errmon"
|
"github.com/facebookincubator/go-belt/tool/experimental/errmon"
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/process/client"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InputID = client.InputID
|
type InputID = client.InputID
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Recoder struct {
|
type Recoder struct {
|
@@ -2,7 +2,7 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/grpc/go/recoder_grpc"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func logLevelProtobuf2Go(logLevel recoder_grpc.LoggingLevel) logger.Level {
|
func logLevelProtobuf2Go(logLevel recoder_grpc.LoggingLevel) logger.Level {
|
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/facebookincubator/go-belt"
|
"github.com/facebookincubator/go-belt"
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/grpc/go/recoder_grpc"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/xcontext"
|
"github.com/xaionaro-go/streamctl/pkg/xcontext"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@@ -122,6 +122,26 @@ func (srv *GRPCServer) newInputByURL(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *GRPCServer) CloseInput(
|
||||||
|
ctx context.Context,
|
||||||
|
req *recoder_grpc.CloseInputRequest,
|
||||||
|
) (*recoder_grpc.CloseInputReply, error) {
|
||||||
|
inputID := InputID(req.GetInputID())
|
||||||
|
err := xsync.DoR1(ctx, &srv.InputLocker, func() error {
|
||||||
|
input := srv.Input[inputID]
|
||||||
|
if input == nil {
|
||||||
|
return fmt.Errorf("there is no open input with ID %d", inputID)
|
||||||
|
}
|
||||||
|
input.Close()
|
||||||
|
delete(srv.Input, inputID)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &recoder_grpc.CloseInputReply{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (srv *GRPCServer) NewOutput(
|
func (srv *GRPCServer) NewOutput(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *recoder_grpc.NewOutputRequest,
|
req *recoder_grpc.NewOutputRequest,
|
||||||
@@ -156,6 +176,26 @@ func (srv *GRPCServer) newOutputByURL(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *GRPCServer) CloseOutput(
|
||||||
|
ctx context.Context,
|
||||||
|
req *recoder_grpc.CloseOutputRequest,
|
||||||
|
) (*recoder_grpc.CloseOutputReply, error) {
|
||||||
|
outputID := OutputID(req.GetOutputID())
|
||||||
|
err := xsync.DoR1(ctx, &srv.InputLocker, func() error {
|
||||||
|
output := srv.Output[outputID]
|
||||||
|
if output == nil {
|
||||||
|
return fmt.Errorf("there is no open output with ID %d", outputID)
|
||||||
|
}
|
||||||
|
output.Close()
|
||||||
|
delete(srv.Output, outputID)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &recoder_grpc.CloseOutputReply{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (srv *GRPCServer) NewRecoder(
|
func (srv *GRPCServer) NewRecoder(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *recoder_grpc.NewRecoderRequest,
|
req *recoder_grpc.NewRecoderRequest,
|
@@ -4,9 +4,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/recoder/types"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/libav/saferecoder/process"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder/types"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Packet = types.Packet
|
type Packet = types.Packet
|
9
pkg/recoder/output.go
Normal file
9
pkg/recoder/output.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package recoder
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Output interface {
|
||||||
|
io.Closer
|
||||||
|
}
|
@@ -3,12 +3,9 @@ package recoder
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
)
|
|
||||||
|
|
||||||
type Stats struct {
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
||||||
BytesCountRead uint64
|
)
|
||||||
BytesCountWrote uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type Recoder interface {
|
type Recoder interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
@@ -20,6 +17,10 @@ type Recoder interface {
|
|||||||
GetStats(context.Context) (*Stats, error)
|
GetStats(context.Context) (*Stats, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NewInputFromPublisherer interface {
|
||||||
|
NewInputFromPublisher(context.Context, types.Publisher, InputConfig) (Input, error)
|
||||||
|
}
|
||||||
|
|
||||||
type Factory interface {
|
type Factory interface {
|
||||||
New(context.Context, Config) (Recoder, error)
|
New(context.Context, Config) (Recoder, error)
|
||||||
}
|
}
|
6
pkg/recoder/stats.go
Normal file
6
pkg/recoder/stats.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package recoder
|
||||||
|
|
||||||
|
type Stats struct {
|
||||||
|
BytesCountRead uint64
|
||||||
|
BytesCountWrote uint64
|
||||||
|
}
|
107
pkg/recoder/xaionaro-go-rtmp/input.go
Normal file
107
pkg/recoder/xaionaro-go-rtmp/input.go
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
package xaionarogortmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
|
"github.com/xaionaro-go/go-rtmp"
|
||||||
|
rtmpmsg "github.com/xaionaro-go/go-rtmp/message"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
xaionarogortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Input struct {
|
||||||
|
xsync.Mutex
|
||||||
|
Client *rtmp.ClientConn
|
||||||
|
Pubsub *xaionarogortmp.Pubsub
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ recoder.Input = (*Input)(nil)
|
||||||
|
|
||||||
|
func streamID2LocalAppName(
|
||||||
|
streamID types.StreamID,
|
||||||
|
) types.AppKey {
|
||||||
|
streamIDParts := strings.Split(string(streamID), "/")
|
||||||
|
localAppName := string(streamID)
|
||||||
|
if len(streamIDParts) == 2 {
|
||||||
|
localAppName = streamIDParts[1]
|
||||||
|
}
|
||||||
|
return types.AppKey(localAppName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) NewInputFromPublisher(
|
||||||
|
ctx context.Context,
|
||||||
|
publisherIface types.Publisher,
|
||||||
|
cfg recoder.InputConfig,
|
||||||
|
) (recoder.Input, error) {
|
||||||
|
publisher, ok := publisherIface.(*xaionarogortmp.Pubsub)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("expected a publisher or type %T, but received %T", publisherIface, publisher)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Input{
|
||||||
|
Pubsub: publisher,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) NewInputFromURL(
|
||||||
|
ctx context.Context,
|
||||||
|
urlString string,
|
||||||
|
cfg recoder.InputConfig,
|
||||||
|
) (_ recoder.Input, _err error) {
|
||||||
|
inClient, err := newRTMPClient(ctx, urlString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to connect to the input endpoint '%s': %w", urlString, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if _err != nil {
|
||||||
|
err := inClient.Close()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf(ctx, "unable to close the client for the input endpoint: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
url, err := url.Parse(urlString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse URL '%s': %w", urlString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteAppName, _ := getAppNameAndKey(url.Path)
|
||||||
|
tcURL := *url
|
||||||
|
tcURL.Path = "/" + remoteAppName
|
||||||
|
if tcURL.Port() == "1935" {
|
||||||
|
tcURL.Host = tcURL.Hostname()
|
||||||
|
}
|
||||||
|
err = inClient.Connect(ctx, &rtmpmsg.NetConnectionConnect{
|
||||||
|
Command: rtmpmsg.NetConnectionConnectCommand{
|
||||||
|
App: remoteAppName,
|
||||||
|
Type: "nonprivate",
|
||||||
|
FlashVer: "StreamPanel",
|
||||||
|
TCURL: tcURL.String(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("got an error on command 'Connect' to the input endpoint '%s': %w", urlString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("not implemented, yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (input *Input) Close() error {
|
||||||
|
var err error
|
||||||
|
ctx := context.TODO()
|
||||||
|
input.Do(ctx, func() {
|
||||||
|
if input.Client == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = input.Client.Close()
|
||||||
|
input.Client = nil
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
@@ -1,10 +1,11 @@
|
|||||||
package streamforward
|
package xaionarogortmp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"runtime/debug"
|
||||||
|
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/go-rtmp"
|
"github.com/xaionaro-go/go-rtmp"
|
||||||
@@ -13,8 +14,24 @@ import (
|
|||||||
|
|
||||||
func newRTMPClient(
|
func newRTMPClient(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
url url.URL,
|
urlString string,
|
||||||
) (*rtmp.ClientConn, error) {
|
) (_ *rtmp.ClientConn, _err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
_err = fmt.Errorf("got panic: %v", r)
|
||||||
|
}
|
||||||
|
if _err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.FromCtx(ctx).
|
||||||
|
WithField("error_event_exception_stack_trace", string(debug.Stack())).Errorf("%v", _err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
url, err := url.Parse(urlString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse URL '%s': %w", urlString, err)
|
||||||
|
}
|
||||||
|
|
||||||
logger.Debugf(ctx, "connecting to '%s'", url.String())
|
logger.Debugf(ctx, "connecting to '%s'", url.String())
|
||||||
if url.Port() == "" {
|
if url.Port() == "" {
|
||||||
switch url.Scheme {
|
switch url.Scheme {
|
||||||
@@ -44,5 +61,6 @@ func newRTMPClient(
|
|||||||
return nil, fmt.Errorf("unable to connect to '%s': %w", url.String(), err)
|
return nil, fmt.Errorf("unable to connect to '%s': %w", url.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Debugf(ctx, "connected to '%s'", url)
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
90
pkg/recoder/xaionaro-go-rtmp/output.go
Normal file
90
pkg/recoder/xaionaro-go-rtmp/output.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package xaionarogortmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"runtime/debug"
|
||||||
|
|
||||||
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/xaionaro-go/go-rtmp"
|
||||||
|
rtmpmsg "github.com/xaionaro-go/go-rtmp/message"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Output struct {
|
||||||
|
xsync.Mutex
|
||||||
|
Client *rtmp.ClientConn
|
||||||
|
AppKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ recoder.Output = (*Output)(nil)
|
||||||
|
|
||||||
|
func (r *Recoder) NewOutputFromURL(
|
||||||
|
ctx context.Context,
|
||||||
|
urlString string,
|
||||||
|
cfg recoder.OutputConfig,
|
||||||
|
) (_ recoder.Output, _err error) {
|
||||||
|
var output *Output
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
_err = fmt.Errorf("got panic: %v", r)
|
||||||
|
}
|
||||||
|
if _err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.FromCtx(ctx).
|
||||||
|
WithField("error_event_exception_stack_trace", string(debug.Stack())).Errorf("%v", _err)
|
||||||
|
output.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
client, err := newRTMPClient(ctx, urlString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to connect to the output endpoint '%s': %w", urlString, err)
|
||||||
|
}
|
||||||
|
output.Client = client
|
||||||
|
|
||||||
|
url, err := url.Parse(urlString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse URL '%s': %w", urlString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tcURL := *url
|
||||||
|
remoteAppName, appKey := getAppNameAndKey(tcURL.Path)
|
||||||
|
tcURL.Path = "/" + remoteAppName
|
||||||
|
if tcURL.Port() == "1935" {
|
||||||
|
tcURL.Host = tcURL.Hostname()
|
||||||
|
}
|
||||||
|
output.AppKey = appKey
|
||||||
|
|
||||||
|
if err := client.Connect(ctx, &rtmpmsg.NetConnectionConnect{
|
||||||
|
Command: rtmpmsg.NetConnectionConnectCommand{
|
||||||
|
App: remoteAppName,
|
||||||
|
Type: "nonprivate",
|
||||||
|
FlashVer: "StreamPanel",
|
||||||
|
TCURL: tcURL.String(),
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to connect the endpoint '%s': %w", urlString, err)
|
||||||
|
}
|
||||||
|
logger.Debugf(ctx, "connected the stream to '%s'", urlString)
|
||||||
|
|
||||||
|
return output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (output *Output) Close() error {
|
||||||
|
var err *multierror.Error
|
||||||
|
ctx := context.TODO()
|
||||||
|
output.Do(ctx, func() {
|
||||||
|
if output.Client != nil {
|
||||||
|
err = multierror.Append(
|
||||||
|
err,
|
||||||
|
output.Client.Close(),
|
||||||
|
)
|
||||||
|
output.Client = nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return err.ErrorOrNil()
|
||||||
|
}
|
@@ -1,20 +1,12 @@
|
|||||||
package streamforward
|
package xaionarogortmp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
|
||||||
flvtag "github.com/yutopp/go-flv/tag"
|
flvtag "github.com/yutopp/go-flv/tag"
|
||||||
)
|
)
|
||||||
|
|
||||||
type StreamServer interface {
|
|
||||||
types.WithConfiger
|
|
||||||
types.WaitPublisherChaner
|
|
||||||
types.PubsubNameser
|
|
||||||
types.GetPortServerser
|
|
||||||
}
|
|
||||||
|
|
||||||
type Sub interface {
|
type Sub interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
ClosedChan() <-chan struct{}
|
ClosedChan() <-chan struct{}
|
176
pkg/recoder/xaionaro-go-rtmp/recoder.go
Normal file
176
pkg/recoder/xaionaro-go-rtmp/recoder.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package xaionarogortmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/xaionaro-go/go-rtmp"
|
||||||
|
rtmpmsg "github.com/xaionaro-go/go-rtmp/message"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
yutoppgortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||||
|
flvtag "github.com/yutopp/go-flv/tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
chunkSize = 128
|
||||||
|
)
|
||||||
|
|
||||||
|
type Recoder struct {
|
||||||
|
Locker xsync.Mutex
|
||||||
|
Stream *rtmp.Stream
|
||||||
|
CancelFunc context.CancelFunc
|
||||||
|
ReadCount atomic.Uint64
|
||||||
|
WriteCount atomic.Uint64
|
||||||
|
Sub *yutoppgortmp.Sub
|
||||||
|
eventChan chan *flvtag.FlvTag
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ recoder.Recoder = (*Recoder)(nil)
|
||||||
|
var _ recoder.NewInputFromPublisherer = (*Recoder)(nil)
|
||||||
|
|
||||||
|
func (RecoderFactory) New(
|
||||||
|
ctx context.Context,
|
||||||
|
cfg recoder.Config,
|
||||||
|
) (recoder.Recoder, error) {
|
||||||
|
return &Recoder{
|
||||||
|
eventChan: make(chan *flvtag.FlvTag),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) StartRecoding(
|
||||||
|
ctx context.Context,
|
||||||
|
inputIface recoder.Input,
|
||||||
|
outputIface recoder.Output,
|
||||||
|
) (_err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
_err = fmt.Errorf("got panic: %v", r)
|
||||||
|
}
|
||||||
|
if _err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.FromCtx(ctx).
|
||||||
|
WithField("error_event_exception_stack_trace", string(debug.Stack())).Errorf("%v", _err)
|
||||||
|
r.Close()
|
||||||
|
}()
|
||||||
|
input, ok := inputIface.(*Input)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected Input of type %T, but received %T", input, inputIface)
|
||||||
|
}
|
||||||
|
|
||||||
|
output, ok := outputIface.(*Output)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expected Input of type %T, but received %T", output, outputIface)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := xsync.DoR1(ctx, &r.Locker, func() error {
|
||||||
|
if r.CancelFunc != nil {
|
||||||
|
return fmt.Errorf("recoding is already running")
|
||||||
|
}
|
||||||
|
|
||||||
|
stream, err := output.Client.CreateStream(ctx, &rtmpmsg.NetConnectionCreateStream{}, chunkSize)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create a stream on the remote side: %w", err)
|
||||||
|
}
|
||||||
|
r.Stream = stream
|
||||||
|
|
||||||
|
logger.Debugf(ctx, "calling Publish")
|
||||||
|
if err := r.Stream.Publish(ctx, &rtmpmsg.NetStreamPublish{
|
||||||
|
PublishingName: output.AppKey,
|
||||||
|
PublishingType: "live",
|
||||||
|
}); err != nil {
|
||||||
|
return fmt.Errorf("unable to send the Publish to the remote endpoint: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf(ctx, "starting publishing")
|
||||||
|
switch {
|
||||||
|
case input.Pubsub != nil:
|
||||||
|
r.Sub = input.Pubsub.Sub(output.Client, r.subCallback(r.Stream))
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("this case is not implemented, yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf(ctx, "started publishing")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debugf(ctx, "the source stopped, so stopped also publishing")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) WaitForRecordingEnd(
|
||||||
|
ctx context.Context,
|
||||||
|
) error {
|
||||||
|
var closeChan <-chan struct{}
|
||||||
|
err := xsync.DoR1(ctx, &r.Locker, func() error {
|
||||||
|
if r.CancelFunc != nil {
|
||||||
|
return fmt.Errorf("recoding is not started (or was already closed)")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case r.Sub != nil:
|
||||||
|
closeChan = r.Sub.ClosedChan()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("this case is not implemented, yet")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
<-closeChan
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) GetStats(context.Context) (*recoder.Stats, error) {
|
||||||
|
return &recoder.Stats{
|
||||||
|
BytesCountRead: r.ReadCount.Load(),
|
||||||
|
BytesCountWrote: r.WriteCount.Load(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Recoder) Close() (_err error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
logger.Debug(ctx, "closing the Recoder")
|
||||||
|
defer func() { logger.Debugf(ctx, "closed the Recoder: %v", _err) }()
|
||||||
|
return xsync.DoR1(ctx, &r.Locker, func() error {
|
||||||
|
var result *multierror.Error
|
||||||
|
|
||||||
|
if r.CancelFunc == nil {
|
||||||
|
return fmt.Errorf("the stream was not started yet")
|
||||||
|
}
|
||||||
|
r.CancelFunc()
|
||||||
|
r.CancelFunc = nil
|
||||||
|
if r.Stream != nil {
|
||||||
|
result = multierror.Append(result, r.Stream.Close())
|
||||||
|
r.Stream = nil
|
||||||
|
}
|
||||||
|
if r.Sub != nil {
|
||||||
|
result = multierror.Append(result, r.Sub.Close())
|
||||||
|
r.Sub = nil
|
||||||
|
}
|
||||||
|
return result.ErrorOrNil()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAppNameAndKey(
|
||||||
|
remotePath string,
|
||||||
|
) (string, string) {
|
||||||
|
remoteAppName := "live"
|
||||||
|
pathParts := strings.SplitN(remotePath, "/", -2)
|
||||||
|
apiKey := pathParts[len(pathParts)-1]
|
||||||
|
if len(pathParts) >= 2 {
|
||||||
|
remoteAppName = strings.Trim(strings.Join(pathParts[:len(pathParts)-1], "/"), "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
return remoteAppName, apiKey
|
||||||
|
}
|
11
pkg/recoder/xaionaro-go-rtmp/recoder_factory.go
Normal file
11
pkg/recoder/xaionaro-go-rtmp/recoder_factory.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package xaionarogortmp
|
||||||
|
|
||||||
|
import "github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
|
|
||||||
|
type RecoderFactory struct{}
|
||||||
|
|
||||||
|
var _ recoder.Factory = (*RecoderFactory)(nil)
|
||||||
|
|
||||||
|
func NewRecoderFactory() *RecoderFactory {
|
||||||
|
return &RecoderFactory{}
|
||||||
|
}
|
15
pkg/recoder/xaionaro-go-rtmp/stream_forwards.go
Normal file
15
pkg/recoder/xaionaro-go-rtmp/stream_forwards.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package xaionarogortmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/streamforward"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamForwards = streamforward.StreamForwards
|
||||||
|
|
||||||
|
func NewStreamForwards(
|
||||||
|
s StreamServer,
|
||||||
|
platformsController types.PlatformsController,
|
||||||
|
) *StreamForwards {
|
||||||
|
return streamforward.NewStreamForwards(s, NewRecoderFactory(), platformsController)
|
||||||
|
}
|
105
pkg/recoder/xaionaro-go-rtmp/sub_callback.go
Normal file
105
pkg/recoder/xaionaro-go-rtmp/sub_callback.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package xaionarogortmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
|
"github.com/xaionaro-go/go-rtmp"
|
||||||
|
rtmpmsg "github.com/xaionaro-go/go-rtmp/message"
|
||||||
|
flvtag "github.com/yutopp/go-flv/tag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *Recoder) subCallback(
|
||||||
|
stream *rtmp.Stream,
|
||||||
|
) func(
|
||||||
|
ctx context.Context,
|
||||||
|
flv *flvtag.FlvTag,
|
||||||
|
) error {
|
||||||
|
return func(
|
||||||
|
ctx context.Context,
|
||||||
|
flv *flvtag.FlvTag,
|
||||||
|
) error {
|
||||||
|
logger.Tracef(ctx, "flvtag == %#+v", *flv)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
switch d := flv.Data.(type) {
|
||||||
|
case *flvtag.AudioData:
|
||||||
|
// Consume flv payloads (d)
|
||||||
|
if err := flvtag.EncodeAudioData(&buf, d); err != nil {
|
||||||
|
err = fmt.Errorf("flvtag.Data == %#+v; err == %w", *d, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLen := uint64(buf.Len())
|
||||||
|
r.WriteCount.Add(payloadLen)
|
||||||
|
logger.Tracef(ctx, "flvtag.Data == %#+v; payload len == %d", *d, payloadLen)
|
||||||
|
|
||||||
|
// TODO: Fix these values
|
||||||
|
chunkStreamID := 5
|
||||||
|
if err := stream.Write(ctx, chunkStreamID, flv.Timestamp, &rtmpmsg.AudioMessage{
|
||||||
|
Payload: &buf,
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("stream.Write (%T) return an error: %w", d, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *flvtag.VideoData:
|
||||||
|
// Consume flv payloads (d)
|
||||||
|
if err := flvtag.EncodeVideoData(&buf, d); err != nil {
|
||||||
|
err = fmt.Errorf("flvtag.Data == %#+v; err == %w", *d, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLen := uint64(buf.Len())
|
||||||
|
r.WriteCount.Add(payloadLen)
|
||||||
|
logger.Tracef(ctx, "flvtag.Data == %#+v; payload len == %d", *d, payloadLen)
|
||||||
|
|
||||||
|
// TODO: Fix these values
|
||||||
|
chunkStreamID := 6
|
||||||
|
if err := stream.Write(ctx, chunkStreamID, flv.Timestamp, &rtmpmsg.VideoMessage{
|
||||||
|
Payload: &buf,
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("stream.Write (%T) return an error: %w", d, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case *flvtag.ScriptData:
|
||||||
|
// Consume flv payloads (d)
|
||||||
|
if err := flvtag.EncodeScriptData(&buf, d); err != nil {
|
||||||
|
err = fmt.Errorf("flvtag.Data == %#+v; err == %v", *d, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadLen := uint64(buf.Len())
|
||||||
|
r.WriteCount.Add(payloadLen)
|
||||||
|
logger.Tracef(ctx, "flvtag.Data == %#+v; payload len == %d", *d, payloadLen)
|
||||||
|
|
||||||
|
// TODO: hide these implementation
|
||||||
|
amdBuf := new(bytes.Buffer)
|
||||||
|
amfEnc := rtmpmsg.NewAMFEncoder(amdBuf, rtmpmsg.EncodingTypeAMF0)
|
||||||
|
if err := rtmpmsg.EncodeBodyAnyValues(amfEnc, &rtmpmsg.NetStreamSetDataFrame{
|
||||||
|
Payload: buf.Bytes(),
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("flvtag.Data == %#+v; payload len == %d; err == %v", *d, payloadLen, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Fix these values
|
||||||
|
chunkStreamID := 8
|
||||||
|
if err := stream.Write(ctx, chunkStreamID, flv.Timestamp, &rtmpmsg.DataMessage{
|
||||||
|
Name: "@setDataFrame", // TODO: fix
|
||||||
|
Encoding: rtmpmsg.EncodingTypeAMF0,
|
||||||
|
Body: amdBuf,
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("stream.Write (%T) return an error: %w", d, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
logger.Errorf(ctx, "unexpected data type: %T", flv.Data)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
8
pkg/recoder/xaionaro-go-rtmp/type_alias.go
Normal file
8
pkg/recoder/xaionaro-go-rtmp/type_alias.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package xaionarogortmp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/streamforward"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamServer = streamforward.StreamServer
|
||||||
|
type ActiveStreamForwarding = streamforward.ActiveStreamForwarding
|
@@ -1,4 +1,4 @@
|
|||||||
package streamforward
|
package xaionarogortmp
|
||||||
|
|
||||||
func ptr[T any](in T) *T {
|
func ptr[T any](in T) *T {
|
||||||
return &in
|
return &in
|
@@ -1,6 +1,7 @@
|
|||||||
package streamforward
|
package streamforward
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder/libav"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/streamforward"
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/streamforward"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
||||||
)
|
)
|
||||||
@@ -11,5 +12,5 @@ func NewStreamForwards(
|
|||||||
s StreamServer,
|
s StreamServer,
|
||||||
platformsController types.PlatformsController,
|
platformsController types.PlatformsController,
|
||||||
) *StreamForwards {
|
) *StreamForwards {
|
||||||
return streamforward.NewStreamForwards(s, NewRecoderFactory(), platformsController)
|
return streamforward.NewStreamForwards(s, libav.NewRecoderFactory(), platformsController)
|
||||||
}
|
}
|
||||||
|
@@ -1,38 +0,0 @@
|
|||||||
package streamserver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/gwuhaolin/livego/protocol/rtmp/rtmprelay"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ActiveStreamForwarding struct {
|
|
||||||
StreamID types.StreamID
|
|
||||||
DestinationID types.DestinationID
|
|
||||||
RtmpRelay *rtmprelay.RtmpRelay
|
|
||||||
}
|
|
||||||
|
|
||||||
func newActiveStreamForward(
|
|
||||||
_ context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
dstID types.DestinationID,
|
|
||||||
urlSrc string,
|
|
||||||
urlDst string,
|
|
||||||
) (*ActiveStreamForwarding, error) {
|
|
||||||
relay := rtmprelay.NewRtmpRelay(&urlSrc, &urlDst)
|
|
||||||
if err := relay.Start(); err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to start RTMP relay from '%s' to '%s': %w", urlSrc, urlDst, err)
|
|
||||||
}
|
|
||||||
return &ActiveStreamForwarding{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: dstID,
|
|
||||||
RtmpRelay: relay,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) Close() error {
|
|
||||||
fwd.RtmpRelay.Stop()
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,435 +0,0 @@
|
|||||||
package streamforward
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"runtime/debug"
|
|
||||||
"strings"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
"github.com/xaionaro-go/go-rtmp"
|
|
||||||
rtmpmsg "github.com/xaionaro-go/go-rtmp/message"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
|
||||||
yutoppgortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
|
||||||
flvtag "github.com/yutopp/go-flv/tag"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
chunkSize = 128
|
|
||||||
)
|
|
||||||
|
|
||||||
type Unlocker interface {
|
|
||||||
Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
type StreamForward = types.StreamForward[*ActiveStreamForwarding]
|
|
||||||
|
|
||||||
type ActiveStreamForwarding struct {
|
|
||||||
*StreamForwards
|
|
||||||
Locker xsync.Mutex
|
|
||||||
StreamID types.StreamID
|
|
||||||
DestinationID types.DestinationID
|
|
||||||
URL *url.URL
|
|
||||||
InClient *rtmp.ClientConn
|
|
||||||
OutClient *rtmp.ClientConn
|
|
||||||
OutStream *rtmp.Stream
|
|
||||||
Sub Sub
|
|
||||||
CancelFunc context.CancelFunc
|
|
||||||
ReadCount atomic.Uint64
|
|
||||||
WriteCount atomic.Uint64
|
|
||||||
PauseFunc func(ctx context.Context, fwd *ActiveStreamForwarding)
|
|
||||||
eventChan chan *flvtag.FlvTag
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwds *StreamForwards) NewActiveStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
dstID types.DestinationID,
|
|
||||||
urlString string,
|
|
||||||
pauseFunc func(ctx context.Context, fwd *ActiveStreamForwarding),
|
|
||||||
) (_ret *ActiveStreamForwarding, _err error) {
|
|
||||||
logger.Debugf(ctx, "NewActiveStreamForward(ctx, '%s', '%s', '%s', relayService, pauseFunc)", streamID, dstID, urlString)
|
|
||||||
defer func() {
|
|
||||||
logger.Debugf(ctx, "/NewActiveStreamForward(ctx, '%s', '%s', '%s', relayService, pauseFunc): %#+v %v", streamID, dstID, urlString, _ret, _err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
urlParsed, err := url.Parse(urlString)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to parse URL '%s': %w", urlString, err)
|
|
||||||
}
|
|
||||||
fwd := &ActiveStreamForwarding{
|
|
||||||
StreamForwards: fwds,
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: dstID,
|
|
||||||
URL: urlParsed,
|
|
||||||
PauseFunc: pauseFunc,
|
|
||||||
eventChan: make(chan *flvtag.FlvTag),
|
|
||||||
}
|
|
||||||
if err := fwd.Start(ctx); err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to start the forwarder: %w", err)
|
|
||||||
}
|
|
||||||
return fwd, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) Start(ctx context.Context) (_err error) {
|
|
||||||
logger.Debugf(ctx, "Start")
|
|
||||||
defer func() { logger.Debugf(ctx, "/Start: %v", _err) }()
|
|
||||||
|
|
||||||
return xsync.DoA1R1(ctx, &fwd.Locker, fwd.start, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) start(ctx context.Context) (_err error) {
|
|
||||||
if fwd.CancelFunc != nil {
|
|
||||||
return fmt.Errorf("the stream forwarder is already running")
|
|
||||||
}
|
|
||||||
ctx, cancelFn := context.WithCancel(ctx)
|
|
||||||
fwd.CancelFunc = cancelFn
|
|
||||||
observability.Go(ctx, func() {
|
|
||||||
for {
|
|
||||||
err := fwd.waitForPublisherAndStart(
|
|
||||||
ctx,
|
|
||||||
)
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
fwd.Close()
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf(ctx, "%s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) Stop() error {
|
|
||||||
return fwd.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) getAppNameAndKey() (types.AppKey, string, string) {
|
|
||||||
remoteAppName := "live"
|
|
||||||
pathParts := strings.SplitN(fwd.URL.Path, "/", -2)
|
|
||||||
apiKey := pathParts[len(pathParts)-1]
|
|
||||||
if len(pathParts) >= 2 {
|
|
||||||
remoteAppName = strings.Trim(strings.Join(pathParts[:len(pathParts)-1], "/"), "/")
|
|
||||||
}
|
|
||||||
streamID := fwd.StreamID
|
|
||||||
streamIDParts := strings.Split(string(streamID), "/")
|
|
||||||
localAppName := string(streamID)
|
|
||||||
if len(streamIDParts) == 2 {
|
|
||||||
localAppName = streamIDParts[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return types.AppKey(localAppName), remoteAppName, apiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) WaitForPublisher(
|
|
||||||
ctx context.Context,
|
|
||||||
) (types.Publisher, error) {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return nil, ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
logger.Debugf(ctx, "wait for stream '%s'", fwd.StreamID)
|
|
||||||
ch, err := fwd.StreamServer.WaitPublisherChan(ctx, fwd.StreamID, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get the publisher wait chan: %w", err)
|
|
||||||
}
|
|
||||||
logger.Debugf(ctx, "wait for stream '%s' result: %#+v", fwd.StreamID, ch)
|
|
||||||
fwd.PauseFunc(ctx, fwd)
|
|
||||||
logger.Debugf(ctx, "no pauses or pauses ended")
|
|
||||||
return <-ch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) waitForPublisherAndStart(
|
|
||||||
ctx context.Context,
|
|
||||||
) (_ret error) {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
_ret = fmt.Errorf("got panic: %v", r)
|
|
||||||
}
|
|
||||||
if _ret == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.FromCtx(ctx).
|
|
||||||
WithField("error_event_exception_stack_trace", string(debug.Stack())).Errorf("%v", _ret)
|
|
||||||
}()
|
|
||||||
|
|
||||||
publisher, err := fwd.WaitForPublisher(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get publisher: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf(ctx, "DestinationStreamingLocker.Lock(ctx, '%s')", fwd.DestinationID)
|
|
||||||
destinationUnlocker := fwd.StreamForwards.DestinationStreamingLocker.Lock(ctx, fwd.DestinationID)
|
|
||||||
defer func() {
|
|
||||||
if destinationUnlocker != nil { // if ctx was cancelled before we locked then the unlocker is nil
|
|
||||||
destinationUnlocker.Unlock()
|
|
||||||
}
|
|
||||||
logger.Debugf(ctx, "DestinationStreamingLocker.Unlock(ctx, '%s')", fwd.DestinationID)
|
|
||||||
}()
|
|
||||||
logger.Debugf(ctx, "/DestinationStreamingLocker.Lock(ctx, '%s')", fwd.DestinationID)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
_, remoteAppName, apiKey := fwd.getAppNameAndKey()
|
|
||||||
|
|
||||||
outClient, err := newRTMPClient(ctx, *fwd.URL)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to connect to the output endpoint '%s': %w", fwd.URL, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf(ctx, "connected to '%s'", fwd.URL.String())
|
|
||||||
|
|
||||||
fwd.Locker.Do(ctx, func() {
|
|
||||||
fwd.OutClient = outClient
|
|
||||||
})
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
fwd.Locker.Do(ctx, func() {
|
|
||||||
if fwd.OutClient == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err := fwd.OutClient.Close()
|
|
||||||
if err != nil {
|
|
||||||
logger.Warnf(ctx, "unable to close fwd.Client: %v", err)
|
|
||||||
}
|
|
||||||
fwd.OutClient = nil
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
tcURL := *fwd.URL
|
|
||||||
tcURL.Path = "/" + remoteAppName
|
|
||||||
if tcURL.Port() == "1935" {
|
|
||||||
tcURL.Host = tcURL.Hostname()
|
|
||||||
}
|
|
||||||
|
|
||||||
var closedChan <-chan struct{}
|
|
||||||
err = xsync.DoR1(ctx, &fwd.Locker, func() error {
|
|
||||||
if err := outClient.Connect(ctx, &rtmpmsg.NetConnectionConnect{
|
|
||||||
Command: rtmpmsg.NetConnectionConnectCommand{
|
|
||||||
App: remoteAppName,
|
|
||||||
Type: "nonprivate",
|
|
||||||
FlashVer: "StreamPanel",
|
|
||||||
TCURL: tcURL.String(),
|
|
||||||
},
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("unable to connect the stream to '%s': %w", fwd.URL.String(), err)
|
|
||||||
}
|
|
||||||
logger.Debugf(ctx, "connected the stream to '%s'", fwd.URL.String())
|
|
||||||
|
|
||||||
fwd.OutStream, err = outClient.CreateStream(ctx, &rtmpmsg.NetConnectionCreateStream{}, chunkSize)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create a stream to '%s': %w", fwd.URL.String(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf(ctx, "calling Publish at '%s'", fwd.URL.String())
|
|
||||||
if err := fwd.OutStream.Publish(ctx, &rtmpmsg.NetStreamPublish{
|
|
||||||
PublishingName: apiKey,
|
|
||||||
PublishingType: "live",
|
|
||||||
}); err != nil {
|
|
||||||
return fmt.Errorf("unable to send the Publish message to '%s': %w", fwd.URL.String(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf(ctx, "starting publishing to '%s'", fwd.URL.String())
|
|
||||||
switch publisher := publisher.(type) {
|
|
||||||
case *yutoppgortmp.Pubsub:
|
|
||||||
fwd.Sub = publisher.Sub(outClient, fwd.subCallback)
|
|
||||||
closedChan = fwd.Sub.ClosedChan()
|
|
||||||
default:
|
|
||||||
closedChan, err = fwd.connectToLocalhostAndStartReadingStream(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to connect to the input endpoint (publisher type: %T): %w", publisher, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf(ctx, "started publishing to '%s'", fwd.URL.String())
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
<-closedChan
|
|
||||||
logger.Debugf(ctx, "the source stopped, so stopped also publishing to '%s'", fwd.URL.String())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) connectToLocalhostAndStartReadingStream(
|
|
||||||
ctx context.Context,
|
|
||||||
) (_ret <-chan struct{}, _err error) {
|
|
||||||
urlParsed, err := fwd.StreamForwards.getLocalhostRTMP(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get the input endpoint URL: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
inClient, err := newRTMPClient(ctx, *urlParsed)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to connect to the input endpoint '%s': %w", fwd.URL, err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if _err == nil {
|
|
||||||
fwd.Locker.Do(ctx, func() {
|
|
||||||
fwd.InClient = inClient
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
err := inClient.Close()
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf(ctx, "unable to close the client for the input endpoint: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
_, remoteAppName, _ := fwd.getAppNameAndKey()
|
|
||||||
tcURL := *urlParsed
|
|
||||||
tcURL.Path = "/" + remoteAppName
|
|
||||||
if tcURL.Port() == "1935" {
|
|
||||||
tcURL.Host = tcURL.Hostname()
|
|
||||||
}
|
|
||||||
err = inClient.Connect(ctx, &rtmpmsg.NetConnectionConnect{
|
|
||||||
Command: rtmpmsg.NetConnectionConnectCommand{
|
|
||||||
App: remoteAppName,
|
|
||||||
Type: "nonprivate",
|
|
||||||
FlashVer: "StreamPanel",
|
|
||||||
TCURL: tcURL.String(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("got an error on command 'Connect' to the input endpoint '%s': %w", urlParsed, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("not implemented, yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) subCallback(ctx context.Context, flv *flvtag.FlvTag) error {
|
|
||||||
logger.Tracef(ctx, "flvtag == %#+v", *flv)
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
switch d := flv.Data.(type) {
|
|
||||||
case *flvtag.AudioData:
|
|
||||||
// Consume flv payloads (d)
|
|
||||||
if err := flvtag.EncodeAudioData(&buf, d); err != nil {
|
|
||||||
err = fmt.Errorf("flvtag.Data == %#+v; err == %w", *d, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
payloadLen := uint64(buf.Len())
|
|
||||||
fwd.WriteCount.Add(payloadLen)
|
|
||||||
logger.Tracef(ctx, "flvtag.Data == %#+v; payload len == %d", *d, payloadLen)
|
|
||||||
|
|
||||||
// TODO: Fix these values
|
|
||||||
chunkStreamID := 5
|
|
||||||
if err := fwd.OutStream.Write(ctx, chunkStreamID, flv.Timestamp, &rtmpmsg.AudioMessage{
|
|
||||||
Payload: &buf,
|
|
||||||
}); err != nil {
|
|
||||||
err = fmt.Errorf("fwd.OutStream.Write (%T) return an error: %w", d, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
case *flvtag.VideoData:
|
|
||||||
// Consume flv payloads (d)
|
|
||||||
if err := flvtag.EncodeVideoData(&buf, d); err != nil {
|
|
||||||
err = fmt.Errorf("flvtag.Data == %#+v; err == %w", *d, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
payloadLen := uint64(buf.Len())
|
|
||||||
fwd.WriteCount.Add(payloadLen)
|
|
||||||
logger.Tracef(ctx, "flvtag.Data == %#+v; payload len == %d", *d, payloadLen)
|
|
||||||
|
|
||||||
// TODO: Fix these values
|
|
||||||
chunkStreamID := 6
|
|
||||||
if err := fwd.OutStream.Write(ctx, chunkStreamID, flv.Timestamp, &rtmpmsg.VideoMessage{
|
|
||||||
Payload: &buf,
|
|
||||||
}); err != nil {
|
|
||||||
err = fmt.Errorf("fwd.OutStream.Write (%T) return an error: %w", d, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
case *flvtag.ScriptData:
|
|
||||||
// Consume flv payloads (d)
|
|
||||||
if err := flvtag.EncodeScriptData(&buf, d); err != nil {
|
|
||||||
err = fmt.Errorf("flvtag.Data == %#+v; err == %v", *d, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
payloadLen := uint64(buf.Len())
|
|
||||||
fwd.WriteCount.Add(payloadLen)
|
|
||||||
logger.Tracef(ctx, "flvtag.Data == %#+v; payload len == %d", *d, payloadLen)
|
|
||||||
|
|
||||||
// TODO: hide these implementation
|
|
||||||
amdBuf := new(bytes.Buffer)
|
|
||||||
amfEnc := rtmpmsg.NewAMFEncoder(amdBuf, rtmpmsg.EncodingTypeAMF0)
|
|
||||||
if err := rtmpmsg.EncodeBodyAnyValues(amfEnc, &rtmpmsg.NetStreamSetDataFrame{
|
|
||||||
Payload: buf.Bytes(),
|
|
||||||
}); err != nil {
|
|
||||||
err = fmt.Errorf("flvtag.Data == %#+v; payload len == %d; err == %v", *d, payloadLen, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Fix these values
|
|
||||||
chunkStreamID := 8
|
|
||||||
if err := fwd.OutStream.Write(ctx, chunkStreamID, flv.Timestamp, &rtmpmsg.DataMessage{
|
|
||||||
Name: "@setDataFrame", // TODO: fix
|
|
||||||
Encoding: rtmpmsg.EncodingTypeAMF0,
|
|
||||||
Body: amdBuf,
|
|
||||||
}); err != nil {
|
|
||||||
err = fmt.Errorf("fwd.OutStream.Write (%T) return an error: %w", d, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
logger.Errorf(ctx, "unexpected data type: %T", flv.Data)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) Close() error {
|
|
||||||
ctx := context.TODO()
|
|
||||||
return xsync.DoR1(ctx, &fwd.Locker, func() error {
|
|
||||||
if fwd.CancelFunc == nil {
|
|
||||||
return fmt.Errorf("the stream was not started yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
var result *multierror.Error
|
|
||||||
if fwd.CancelFunc != nil {
|
|
||||||
fwd.CancelFunc()
|
|
||||||
fwd.CancelFunc = nil
|
|
||||||
}
|
|
||||||
if fwd.Sub != nil {
|
|
||||||
result = multierror.Append(result, fwd.Sub.Close())
|
|
||||||
fwd.Sub = nil
|
|
||||||
}
|
|
||||||
if fwd.OutClient != nil {
|
|
||||||
result = multierror.Append(result, fwd.OutClient.Close())
|
|
||||||
fwd.OutClient = nil
|
|
||||||
}
|
|
||||||
if fwd.InClient != nil {
|
|
||||||
result = multierror.Append(result, fwd.InClient.Close())
|
|
||||||
fwd.InClient = nil
|
|
||||||
}
|
|
||||||
return result.ErrorOrNil()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fwd *ActiveStreamForwarding) String() string {
|
|
||||||
return fmt.Sprintf("%s->%s", fwd.StreamID, fwd.DestinationID)
|
|
||||||
}
|
|
@@ -1,682 +1,16 @@
|
|||||||
package streamforward
|
package streamforward
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"github.com/xaionaro-go/streamctl/pkg/recoder/xaionaro-go-rtmp"
|
||||||
"fmt"
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/streamforward"
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/facebookincubator/go-belt"
|
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
|
||||||
"github.com/xaionaro-go/lockmap"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamd/memoize"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
|
||||||
"github.com/xaionaro-go/typing/ordered"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ForwardingKey struct {
|
type StreamForwards = streamforward.StreamForwards
|
||||||
StreamID types.StreamID
|
|
||||||
DestinationID types.DestinationID
|
|
||||||
}
|
|
||||||
|
|
||||||
type StreamForwards struct {
|
|
||||||
StreamServer
|
|
||||||
types.PlatformsController
|
|
||||||
Mutex xsync.RWMutex
|
|
||||||
ActiveStreamForwardings map[ForwardingKey]*ActiveStreamForwarding
|
|
||||||
DestinationStreamingLocker *lockmap.LockMap
|
|
||||||
StreamDestinations []types.StreamDestination
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStreamForwards(
|
func NewStreamForwards(
|
||||||
s StreamServer,
|
s StreamServer,
|
||||||
pc types.PlatformsController,
|
platformsController types.PlatformsController,
|
||||||
) *StreamForwards {
|
) *StreamForwards {
|
||||||
return &StreamForwards{
|
return streamforward.NewStreamForwards(s, xaionarogortmp.NewRecoderFactory(), platformsController)
|
||||||
StreamServer: s,
|
|
||||||
PlatformsController: pc,
|
|
||||||
DestinationStreamingLocker: lockmap.NewLockMap(),
|
|
||||||
ActiveStreamForwardings: map[ForwardingKey]*ActiveStreamForwarding{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) Init(
|
|
||||||
ctx context.Context,
|
|
||||||
opts ...types.InitOption,
|
|
||||||
) error {
|
|
||||||
return xsync.DoR1(ctx, &s.Mutex, func() error {
|
|
||||||
return s.init(ctx, opts...)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) init(
|
|
||||||
ctx context.Context,
|
|
||||||
_ ...types.InitOption,
|
|
||||||
) (_ret error) {
|
|
||||||
s.WithConfig(ctx, func(ctx context.Context, cfg *types.Config) {
|
|
||||||
for dstID, dstCfg := range cfg.Destinations {
|
|
||||||
err := s.addActiveStreamDestination(ctx, dstID, dstCfg.URL)
|
|
||||||
if err != nil {
|
|
||||||
_ret = fmt.Errorf("unable to initialize stream destination '%s' to %#+v: %w", dstID, dstCfg, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for streamID, streamCfg := range cfg.Streams {
|
|
||||||
for dstID, fwd := range streamCfg.Forwardings {
|
|
||||||
if !fwd.Disabled {
|
|
||||||
_, err := s.newActiveStreamForward(ctx, streamID, dstID, fwd.Quirks)
|
|
||||||
if err != nil {
|
|
||||||
_ret = fmt.Errorf("unable to launch stream forward from '%s' to '%s': %w", streamID, dstID, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) AddStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
enabled bool,
|
|
||||||
quirks types.ForwardingQuirks,
|
|
||||||
) (*StreamForward, error) {
|
|
||||||
return xsync.DoR2(ctx, &s.Mutex, func() (*StreamForward, error) {
|
|
||||||
return s.addStreamForward(ctx, streamID, destinationID, enabled, quirks)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) addStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
enabled bool,
|
|
||||||
quirks types.ForwardingQuirks,
|
|
||||||
) (*StreamForward, error) {
|
|
||||||
ctx = belt.WithField(ctx, "module", "StreamServer")
|
|
||||||
|
|
||||||
var (
|
|
||||||
streamConfig *types.StreamConfig
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
s.WithConfig(ctx, func(ctx context.Context, cfg *types.Config) {
|
|
||||||
streamConfig = cfg.Streams[streamID]
|
|
||||||
if _, ok := streamConfig.Forwardings[destinationID]; ok {
|
|
||||||
err = fmt.Errorf("the forwarding %s->%s already exists", streamID, destinationID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
streamConfig.Forwardings[destinationID] = types.ForwardingConfig{
|
|
||||||
Disabled: !enabled,
|
|
||||||
Quirks: quirks,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if enabled {
|
|
||||||
fwd, err := s.newActiveStreamForward(ctx, streamID, destinationID, quirks)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return fwd, nil
|
|
||||||
}
|
|
||||||
return &StreamForward{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: destinationID,
|
|
||||||
Enabled: enabled,
|
|
||||||
Quirks: quirks,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) getLocalhostRTMP(ctx context.Context) (*url.URL, error) {
|
|
||||||
portSrvs, err := s.StreamServer.GetPortServers(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get port servers info: %w", err)
|
|
||||||
}
|
|
||||||
portSrv := portSrvs[0]
|
|
||||||
|
|
||||||
urlString := fmt.Sprintf("%s://%s", portSrv.Type, portSrv.Addr)
|
|
||||||
urlParsed, err := url.Parse(urlString)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to parse '%s': %w", urlString, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return urlParsed, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) newActiveStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
quirks types.ForwardingQuirks,
|
|
||||||
) (*StreamForward, error) {
|
|
||||||
ctx = belt.WithField(ctx, "stream_forward", fmt.Sprintf("%s->%s", streamID, destinationID))
|
|
||||||
key := ForwardingKey{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: destinationID,
|
|
||||||
}
|
|
||||||
if _, ok := s.ActiveStreamForwardings[key]; ok {
|
|
||||||
return nil, fmt.Errorf("there is already an active stream forwarding to '%s'", destinationID)
|
|
||||||
}
|
|
||||||
|
|
||||||
dst, err := s.findStreamDestinationByID(ctx, destinationID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to find stream destination '%s': %w", destinationID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
urlParsed, err := url.Parse(dst.URL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to parse URL '%s': %w", dst.URL, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if urlParsed.Host == "" {
|
|
||||||
urlParsed, err = s.getLocalhostRTMP(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get the URL of the output endpoint: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &StreamForward{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: destinationID,
|
|
||||||
Enabled: true,
|
|
||||||
Quirks: quirks,
|
|
||||||
NumBytesWrote: 0,
|
|
||||||
NumBytesRead: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
fwd, err := s.NewActiveStreamForward(
|
|
||||||
ctx,
|
|
||||||
streamID,
|
|
||||||
destinationID,
|
|
||||||
urlParsed.String(),
|
|
||||||
func(
|
|
||||||
ctx context.Context,
|
|
||||||
fwd *ActiveStreamForwarding,
|
|
||||||
) {
|
|
||||||
if quirks.StartAfterYoutubeRecognizedStream.Enabled {
|
|
||||||
if quirks.RestartUntilYoutubeRecognizesStream.Enabled {
|
|
||||||
logger.Errorf(ctx, "StartAfterYoutubeRecognizedStream should not be used together with RestartUntilYoutubeRecognizesStream")
|
|
||||||
} else {
|
|
||||||
logger.Debugf(ctx, "fwd %s->%s is waiting for YouTube to recognize the stream", streamID, destinationID)
|
|
||||||
started, err := s.PlatformsController.CheckStreamStartedByPlatformID(
|
|
||||||
memoize.SetNoCache(ctx, true),
|
|
||||||
youtube.ID,
|
|
||||||
)
|
|
||||||
logger.Debugf(ctx, "youtube status check: %v %v", started, err)
|
|
||||||
if started {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := time.NewTicker(time.Second)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-t.C:
|
|
||||||
}
|
|
||||||
started, err := s.PlatformsController.CheckStreamStartedByPlatformID(
|
|
||||||
ctx,
|
|
||||||
youtube.ID,
|
|
||||||
)
|
|
||||||
logger.Debugf(ctx, "youtube status check: %v %v", started, err)
|
|
||||||
if started {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to run the stream forwarding: %w", err)
|
|
||||||
}
|
|
||||||
s.ActiveStreamForwardings[key] = fwd
|
|
||||||
result.ActiveForwarding = fwd
|
|
||||||
|
|
||||||
if quirks.RestartUntilYoutubeRecognizesStream.Enabled {
|
|
||||||
observability.Go(ctx, func() {
|
|
||||||
s.restartUntilYoutubeRecognizesStream(
|
|
||||||
ctx,
|
|
||||||
result,
|
|
||||||
quirks.RestartUntilYoutubeRecognizesStream,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) restartUntilYoutubeRecognizesStream(
|
|
||||||
ctx context.Context,
|
|
||||||
fwd *StreamForward,
|
|
||||||
cfg types.RestartUntilYoutubeRecognizesStream,
|
|
||||||
) {
|
|
||||||
ctx = belt.WithField(ctx, "module", "restartUntilYoutubeRecognizesStream")
|
|
||||||
ctx = belt.WithField(ctx, "stream_forward", fmt.Sprintf("%s->%s", fwd.StreamID, fwd.DestinationID))
|
|
||||||
|
|
||||||
logger.Debugf(ctx, "restartUntilYoutubeRecognizesStream(ctx, %#+v, %#+v)", fwd, cfg)
|
|
||||||
defer func() { logger.Debugf(ctx, "restartUntilYoutubeRecognizesStream(ctx, %#+v, %#+v)", fwd, cfg) }()
|
|
||||||
|
|
||||||
if !cfg.Enabled {
|
|
||||||
logger.Errorf(ctx, "an attempt to start restartUntilYoutubeRecognizesStream when the hack is disabled for this stream forwarder: %#+v", cfg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.PlatformsController == nil {
|
|
||||||
logger.Errorf(ctx, "PlatformsController is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if fwd.ActiveForwarding == nil {
|
|
||||||
logger.Error(ctx, "ActiveForwarding is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := fwd.ActiveForwarding.WaitForPublisher(ctx)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(ctx, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-time.After(cfg.StartTimeout):
|
|
||||||
}
|
|
||||||
logger.Debugf(ctx, "waited %v, checking if the remote platform accepted the stream", cfg.StartTimeout)
|
|
||||||
|
|
||||||
for {
|
|
||||||
streamOK, err := s.PlatformsController.CheckStreamStartedByPlatformID(
|
|
||||||
memoize.SetNoCache(ctx, true),
|
|
||||||
youtube.ID,
|
|
||||||
)
|
|
||||||
logger.Debugf(ctx, "the result of checking the stream on the remote platform: %v %v", streamOK, err)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf(ctx, "unable to check if the stream with URL '%s' is started: %v", fwd.ActiveForwarding.URL, err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if streamOK {
|
|
||||||
logger.Debugf(ctx, "waiting %v to recheck if the stream will be still OK", cfg.StopStartDelay)
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-time.After(cfg.StopStartDelay):
|
|
||||||
}
|
|
||||||
streamOK, err := s.PlatformsController.CheckStreamStartedByPlatformID(
|
|
||||||
memoize.SetNoCache(ctx, true),
|
|
||||||
youtube.ID,
|
|
||||||
)
|
|
||||||
logger.Debugf(ctx, "the result of checking the stream on the remote platform: %v %v", streamOK, err)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf(ctx, "unable to check if the stream with URL '%s' is started: %v", fwd.ActiveForwarding.URL, err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if streamOK {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof(ctx, "the remote platform still does not see the stream, restarting the stream forwarding: stopping...")
|
|
||||||
|
|
||||||
err := fwd.ActiveForwarding.Stop()
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf(ctx, "unable to stop stream forwarding: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-time.After(cfg.StopStartDelay):
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof(ctx, "the remote platform still does not see the stream, restarting the stream forwarding: starting...")
|
|
||||||
|
|
||||||
err = fwd.ActiveForwarding.Start(ctx)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf(ctx, "unable to start stream forwarding: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) UpdateStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
enabled bool,
|
|
||||||
quirks types.ForwardingQuirks,
|
|
||||||
) (*StreamForward, error) {
|
|
||||||
return xsync.DoR2(ctx, &s.Mutex, func() (*StreamForward, error) {
|
|
||||||
return s.updateStreamForward(ctx, streamID, destinationID, enabled, quirks)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) updateStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
enabled bool,
|
|
||||||
quirks types.ForwardingQuirks,
|
|
||||||
) (_ret *StreamForward, _err error) {
|
|
||||||
s.WithConfig(ctx, func(ctx context.Context, cfg *types.Config) {
|
|
||||||
streamConfig := cfg.Streams[streamID]
|
|
||||||
fwdCfg, ok := streamConfig.Forwardings[destinationID]
|
|
||||||
if !ok {
|
|
||||||
_err = fmt.Errorf("the forwarding %s->%s does not exist", streamID, destinationID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var fwd *StreamForward
|
|
||||||
if fwdCfg.Disabled && enabled {
|
|
||||||
var err error
|
|
||||||
fwd, err = s.newActiveStreamForward(ctx, streamID, destinationID, quirks)
|
|
||||||
if err != nil {
|
|
||||||
_err = fmt.Errorf("unable to active the stream: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !fwdCfg.Disabled && !enabled {
|
|
||||||
err := s.removeActiveStreamForward(ctx, streamID, destinationID)
|
|
||||||
if err != nil {
|
|
||||||
_err = fmt.Errorf("unable to deactivate the stream: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
streamConfig.Forwardings[destinationID] = types.ForwardingConfig{
|
|
||||||
Disabled: !enabled,
|
|
||||||
Quirks: quirks,
|
|
||||||
}
|
|
||||||
|
|
||||||
r := &StreamForward{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: destinationID,
|
|
||||||
Enabled: enabled,
|
|
||||||
Quirks: quirks,
|
|
||||||
NumBytesWrote: 0,
|
|
||||||
NumBytesRead: 0,
|
|
||||||
}
|
|
||||||
if fwd != nil {
|
|
||||||
r.ActiveForwarding = fwd.ActiveForwarding
|
|
||||||
}
|
|
||||||
_ret = r
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) ListStreamForwards(
|
|
||||||
ctx context.Context,
|
|
||||||
) (_ret []StreamForward, _err error) {
|
|
||||||
defer func() {
|
|
||||||
logger.Tracef(ctx, "/ListStreamForwards(): %#+v %v", _ret, _err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return xsync.DoR2(ctx, &s.Mutex, func() ([]StreamForward, error) {
|
|
||||||
return s.getStreamForwards(ctx, func(si types.StreamID, di ordered.Optional[types.DestinationID]) bool {
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) getStreamForwards(
|
|
||||||
ctx context.Context,
|
|
||||||
filterFunc func(types.StreamID, ordered.Optional[types.DestinationID]) bool,
|
|
||||||
) (_ret []StreamForward, _err error) {
|
|
||||||
activeStreamForwards, err := s.listActiveStreamForwards(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to get the list of active stream forwardings: %w", err)
|
|
||||||
}
|
|
||||||
logger.Tracef(ctx, "len(activeStreamForwards) == %d", len(activeStreamForwards))
|
|
||||||
|
|
||||||
type fwdID struct {
|
|
||||||
StreamID types.StreamID
|
|
||||||
DestID types.DestinationID
|
|
||||||
}
|
|
||||||
m := map[fwdID]*StreamForward{}
|
|
||||||
for idx := range activeStreamForwards {
|
|
||||||
fwd := &activeStreamForwards[idx]
|
|
||||||
if !filterFunc(fwd.StreamID, ordered.Opt(fwd.DestinationID)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m[fwdID{
|
|
||||||
StreamID: fwd.StreamID,
|
|
||||||
DestID: fwd.DestinationID,
|
|
||||||
}] = fwd
|
|
||||||
}
|
|
||||||
|
|
||||||
var result []StreamForward
|
|
||||||
s.WithConfig(ctx, func(ctx context.Context, cfg *types.Config) {
|
|
||||||
logger.Tracef(ctx, "len(s.Config.Streams) == %d", len(cfg.Streams))
|
|
||||||
for streamID, stream := range cfg.Streams {
|
|
||||||
if !filterFunc(streamID, ordered.Optional[types.DestinationID]{}) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
logger.Tracef(ctx, "len(s.Config.Streams[%s].Forwardings) == %d", streamID, len(stream.Forwardings))
|
|
||||||
for dstID, cfg := range stream.Forwardings {
|
|
||||||
if !filterFunc(streamID, ordered.Opt(dstID)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
item := StreamForward{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: dstID,
|
|
||||||
Enabled: !cfg.Disabled,
|
|
||||||
Quirks: cfg.Quirks,
|
|
||||||
}
|
|
||||||
if activeFwd, ok := m[fwdID{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestID: dstID,
|
|
||||||
}]; ok {
|
|
||||||
item.NumBytesWrote = activeFwd.NumBytesWrote
|
|
||||||
item.NumBytesRead = activeFwd.NumBytesRead
|
|
||||||
}
|
|
||||||
logger.Tracef(ctx, "stream forwarding '%s->%s': %#+v", streamID, dstID, cfg)
|
|
||||||
result = append(result, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) listActiveStreamForwards(
|
|
||||||
_ context.Context,
|
|
||||||
) ([]StreamForward, error) {
|
|
||||||
var result []StreamForward
|
|
||||||
for _, fwd := range s.ActiveStreamForwardings {
|
|
||||||
result = append(result, StreamForward{
|
|
||||||
StreamID: fwd.StreamID,
|
|
||||||
DestinationID: fwd.DestinationID,
|
|
||||||
Enabled: true,
|
|
||||||
NumBytesWrote: fwd.WriteCount.Load(),
|
|
||||||
NumBytesRead: fwd.ReadCount.Load(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) RemoveStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
dstID types.DestinationID,
|
|
||||||
) error {
|
|
||||||
ctx = belt.WithField(ctx, "module", "StreamServer")
|
|
||||||
return xsync.DoA3R1(ctx, &s.Mutex, s.removeStreamForward, ctx, streamID, dstID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) removeStreamForward(
|
|
||||||
ctx context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
dstID types.DestinationID,
|
|
||||||
) (err error) {
|
|
||||||
s.WithConfig(ctx, func(ctx context.Context, cfg *types.Config) {
|
|
||||||
streamCfg := cfg.Streams[streamID]
|
|
||||||
if _, ok := streamCfg.Forwardings[dstID]; !ok {
|
|
||||||
err = fmt.Errorf("the forwarding %s->%s does not exist", streamID, dstID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
delete(streamCfg.Forwardings, dstID)
|
|
||||||
err = s.removeActiveStreamForward(ctx, streamID, dstID)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) removeActiveStreamForward(
|
|
||||||
_ context.Context,
|
|
||||||
streamID types.StreamID,
|
|
||||||
dstID types.DestinationID,
|
|
||||||
) error {
|
|
||||||
key := ForwardingKey{
|
|
||||||
StreamID: streamID,
|
|
||||||
DestinationID: dstID,
|
|
||||||
}
|
|
||||||
|
|
||||||
fwd := s.ActiveStreamForwardings[key]
|
|
||||||
if fwd == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(s.ActiveStreamForwardings, key)
|
|
||||||
err := fwd.Close()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to close stream forwarding: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) GetStreamForwardsByDestination(
|
|
||||||
ctx context.Context,
|
|
||||||
destID types.DestinationID,
|
|
||||||
) (_ret []StreamForward, _err error) {
|
|
||||||
ctx = belt.WithField(ctx, "module", "StreamServer")
|
|
||||||
logger.Debugf(ctx, "GetStreamForwardsByDestination()")
|
|
||||||
defer func() {
|
|
||||||
logger.Debugf(ctx, "/GetStreamForwardsByDestination(): %#+v %v", _ret, _err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
return xsync.DoR2(ctx, &s.Mutex, func() ([]StreamForward, error) {
|
|
||||||
return s.getStreamForwards(ctx, func(streamID types.StreamID, dstID ordered.Optional[types.DestinationID]) bool {
|
|
||||||
return !dstID.IsSet() || dstID.Get() == destID
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) ListStreamDestinations(
|
|
||||||
ctx context.Context,
|
|
||||||
) ([]types.StreamDestination, error) {
|
|
||||||
ctx = belt.WithField(ctx, "module", "StreamServer")
|
|
||||||
return xsync.DoA1R2(ctx, &s.Mutex, s.listStreamDestinations, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) listStreamDestinations(
|
|
||||||
_ context.Context,
|
|
||||||
) ([]types.StreamDestination, error) {
|
|
||||||
c := make([]types.StreamDestination, len(s.StreamDestinations))
|
|
||||||
copy(c, s.StreamDestinations)
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) AddStreamDestination(
|
|
||||||
ctx context.Context,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
url string,
|
|
||||||
) error {
|
|
||||||
ctx = belt.WithField(ctx, "module", "StreamServer")
|
|
||||||
return xsync.DoA3R1(ctx, &s.Mutex, s.addStreamDestination, ctx, destinationID, url)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) addStreamDestination(
|
|
||||||
ctx context.Context,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
url string,
|
|
||||||
) (_ret error) {
|
|
||||||
s.WithConfig(ctx, func(ctx context.Context, cfg *types.Config) {
|
|
||||||
err := s.addActiveStreamDestination(ctx, destinationID, url)
|
|
||||||
if err != nil {
|
|
||||||
_ret = fmt.Errorf("unable to add an active stream destination: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.Destinations[destinationID] = &types.DestinationConfig{URL: url}
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) addActiveStreamDestination(
|
|
||||||
_ context.Context,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
url string,
|
|
||||||
) error {
|
|
||||||
s.StreamDestinations = append(s.StreamDestinations, types.StreamDestination{
|
|
||||||
ID: destinationID,
|
|
||||||
URL: url,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) RemoveStreamDestination(
|
|
||||||
ctx context.Context,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
) error {
|
|
||||||
ctx = belt.WithField(ctx, "module", "StreamServer")
|
|
||||||
return xsync.DoA2R1(ctx, &s.Mutex, s.removeStreamDestination, ctx, destinationID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) removeStreamDestination(
|
|
||||||
ctx context.Context,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
) (err error) {
|
|
||||||
s.WithConfig(ctx, func(ctx context.Context, cfg *types.Config) {
|
|
||||||
for _, streamCfg := range cfg.Streams {
|
|
||||||
delete(streamCfg.Forwardings, destinationID)
|
|
||||||
}
|
|
||||||
delete(cfg.Destinations, destinationID)
|
|
||||||
err = s.removeActiveStreamDestination(ctx, destinationID)
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) removeActiveStreamDestination(
|
|
||||||
ctx context.Context,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
) error {
|
|
||||||
streamForwards, err := s.ListStreamForwards(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to list stream forwardings: %w", err)
|
|
||||||
}
|
|
||||||
for _, fwd := range streamForwards {
|
|
||||||
if fwd.DestinationID == destinationID {
|
|
||||||
s.RemoveStreamForward(ctx, fwd.StreamID, fwd.DestinationID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range s.StreamDestinations {
|
|
||||||
if s.StreamDestinations[i].ID == destinationID {
|
|
||||||
s.StreamDestinations = append(s.StreamDestinations[:i], s.StreamDestinations[i+1:]...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("have not found stream destination with id %s", destinationID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StreamForwards) findStreamDestinationByID(
|
|
||||||
_ context.Context,
|
|
||||||
destinationID types.DestinationID,
|
|
||||||
) (types.StreamDestination, error) {
|
|
||||||
for _, dst := range s.StreamDestinations {
|
|
||||||
if dst.ID == destinationID {
|
|
||||||
return dst, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return types.StreamDestination{}, fmt.Errorf("unable to find a stream destination by StreamID '%s'", destinationID)
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
package streamforward
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/streamforward"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StreamServer = streamforward.StreamServer
|
||||||
|
type ActiveStreamForwarding = streamforward.ActiveStreamForwarding
|
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/player"
|
"github.com/xaionaro-go/streamctl/pkg/player"
|
||||||
playertypes "github.com/xaionaro-go/streamctl/pkg/player/types"
|
playertypes "github.com/xaionaro-go/streamctl/pkg/player/types"
|
||||||
|
xaionarogortmp "github.com/xaionaro-go/streamctl/pkg/recoder/xaionaro-go-rtmp"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamplayer"
|
"github.com/xaionaro-go/streamctl/pkg/streamplayer"
|
||||||
yutoppgortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp"
|
yutoppgortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp/streamforward"
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp/streamforward"
|
||||||
@@ -123,7 +124,7 @@ func (s *StreamServer) WithConfig(
|
|||||||
func (s *StreamServer) WaitPubsub(
|
func (s *StreamServer) WaitPubsub(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
appKey types.AppKey,
|
appKey types.AppKey,
|
||||||
) streamforward.Pubsub {
|
) xaionarogortmp.Pubsub {
|
||||||
return &pubsubAdapter{s.RelayService.WaitPubsub(ctx, appKey, false)}
|
return &pubsubAdapter{s.RelayService.WaitPubsub(ctx, appKey, false)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,12 +132,12 @@ type pubsubAdapter struct {
|
|||||||
*yutoppgortmp.Pubsub
|
*yutoppgortmp.Pubsub
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ streamforward.Pubsub = (*pubsubAdapter)(nil)
|
var _ xaionarogortmp.Pubsub = (*pubsubAdapter)(nil)
|
||||||
|
|
||||||
func (pubsub *pubsubAdapter) Sub(
|
func (pubsub *pubsubAdapter) Sub(
|
||||||
conn io.Closer,
|
conn io.Closer,
|
||||||
callback func(ctx context.Context, flv *flvtag.FlvTag) error,
|
callback func(ctx context.Context, flv *flvtag.FlvTag) error,
|
||||||
) streamforward.Sub {
|
) xaionarogortmp.Sub {
|
||||||
return pubsub.Pubsub.Sub(conn, callback)
|
return pubsub.Pubsub.Sub(conn, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
package recoder
|
|
||||||
|
|
||||||
type Input interface{}
|
|
@@ -1,3 +0,0 @@
|
|||||||
package recoder
|
|
||||||
|
|
||||||
type Output interface{}
|
|
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||||
)
|
)
|
||||||
@@ -205,17 +205,47 @@ func (fwd *ActiveStreamForwarding) waitForPublisherAndStart(
|
|||||||
return fmt.Errorf("unable to initialize a recoder: %w", err)
|
return fmt.Errorf("unable to initialize a recoder: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
input, err := fwd.openInputFor(ctx, recoderInstance)
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
input, err := fwd.openInputFor(ctx, recoderInstance, publisher)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errmon.ObserveErrorCtx(ctx, recoderInstance.Close())
|
errmon.ObserveErrorCtx(ctx, recoderInstance.Close())
|
||||||
return fmt.Errorf("unable to open the input: %w", err)
|
return fmt.Errorf("unable to open the input: %w", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
err := input.Close()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf(ctx, "unable to close the input: %w", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
output, err := fwd.openOutputFor(ctx, recoderInstance)
|
output, err := fwd.openOutputFor(ctx, recoderInstance)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errmon.ObserveErrorCtx(ctx, recoderInstance.Close())
|
errmon.ObserveErrorCtx(ctx, recoderInstance.Close())
|
||||||
return fmt.Errorf("unable to open the output: %w", err)
|
return fmt.Errorf("unable to open the output: %w", err)
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
err := output.Close()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf(ctx, "unable to close the output: %w", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
recodingFinished := make(chan struct{})
|
recodingFinished := make(chan struct{})
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -280,6 +310,7 @@ func (fwd *ActiveStreamForwarding) waitForPublisherAndStart(
|
|||||||
func (fwd *ActiveStreamForwarding) openInputFor(
|
func (fwd *ActiveStreamForwarding) openInputFor(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
recoderInstance recoder.Recoder,
|
recoderInstance recoder.Recoder,
|
||||||
|
publisher types.Publisher,
|
||||||
) (recoder.Input, error) {
|
) (recoder.Input, error) {
|
||||||
inputURL, err := fwd.getLocalhostEndpoint(ctx)
|
inputURL, err := fwd.getLocalhostEndpoint(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -288,7 +319,13 @@ func (fwd *ActiveStreamForwarding) openInputFor(
|
|||||||
|
|
||||||
inputURL.Path = "/" + string(fwd.StreamID)
|
inputURL.Path = "/" + string(fwd.StreamID)
|
||||||
|
|
||||||
input, err := recoderInstance.NewInputFromURL(ctx, inputURL.String(), recoder.InputConfig{})
|
var input recoder.Input
|
||||||
|
inputCfg := recoder.InputConfig{}
|
||||||
|
if newInputFromStreamIDer, ok := recoderInstance.(recoder.NewInputFromPublisherer); ok {
|
||||||
|
input, err = newInputFromStreamIDer.NewInputFromPublisher(ctx, publisher, inputCfg)
|
||||||
|
} else {
|
||||||
|
input, err = recoderInstance.NewInputFromURL(ctx, inputURL.String(), inputCfg)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to open '%s' as the input: %w", inputURL, err)
|
return nil, fmt.Errorf("unable to open '%s' as the input: %w", inputURL, err)
|
||||||
}
|
}
|
||||||
|
@@ -10,9 +10,9 @@ import (
|
|||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/xaionaro-go/lockmap"
|
"github.com/xaionaro-go/lockmap"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/recoder"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamd/memoize"
|
"github.com/xaionaro-go/streamctl/pkg/streamd/memoize"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/recoder"
|
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||||
"github.com/xaionaro-go/typing/ordered"
|
"github.com/xaionaro-go/typing/ordered"
|
||||||
|
@@ -1,2 +1 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user