mirror of
https://github.com/Palakis/obs-websocket.git
synced 2024-08-30 18:12:16 +00:00
Compare commits
363 Commits
Author | SHA1 | Date | |
---|---|---|---|
e3ad148c15 | |||
acffacd67d | |||
54a16f4d2f | |||
35cb506d6e | |||
7675a1ee58 | |||
863f5e28b3 | |||
1a6f6096e4 | |||
b57982b6cb | |||
4132356141 | |||
e560e95310 | |||
65f4ff6a30 | |||
ab38f33530 | |||
1f04ee8252 | |||
739bd6f696 | |||
066145ab31 | |||
54d0f764d4 | |||
1398689ebf | |||
647625628d | |||
e17df69b80 | |||
f001d18eea | |||
f49980350a | |||
d0a90ecea4 | |||
4506b46ba0 | |||
a8c36d7366 | |||
781eaec683 | |||
bdee8f318a | |||
82dac4d208 | |||
386e1f3b46 | |||
a3cbbf3ea9 | |||
e647debcfb | |||
fba9bd2b76 | |||
56217e0176 | |||
77c5801c4c | |||
7db879cca9 | |||
8d0cb2e875 | |||
dc7b386295 | |||
03c8c6385c | |||
841de2f752 | |||
9de6e229d9 | |||
8dfe471ef2 | |||
da05f315be | |||
d014a7ab25 | |||
d0118c63c0 | |||
9ddfad99ea | |||
82d74fcb2f | |||
0f4f029a76 | |||
2c581e9998 | |||
f2028c506a | |||
200db77140 | |||
007604cc21 | |||
89486e9172 | |||
a60ca96fd1 | |||
0ade2c869d | |||
9986382850 | |||
89b9165c25 | |||
5d290165a2 | |||
d267171cc7 | |||
cebe325e81 | |||
4e6178881b | |||
6bca8194cb | |||
a4885f332d | |||
cf97fb2051 | |||
20a8853854 | |||
dd487a5055 | |||
72ca07f571 | |||
c7305889c3 | |||
bc24497760 | |||
200e65c730 | |||
b32d8ef1e7 | |||
8cfc613a3d | |||
5fbc2cbac7 | |||
9d7a32aa1f | |||
bcc9ef82d1 | |||
d6b28191e8 | |||
acd8a9ea69 | |||
d4b8a8ff9e | |||
a698f7bdf5 | |||
16f07ff0c3 | |||
ed4526751e | |||
e241518f8d | |||
a6677edbf5 | |||
5748e163f8 | |||
85fa6b60e2 | |||
f0bb941c47 | |||
b7df1e8596 | |||
ff8eda3682 | |||
8c4bd91c78 | |||
07c868edcd | |||
69061869a7 | |||
98b2ac9bdc | |||
ef2bbff4e5 | |||
7bb8e56072 | |||
df0926f6fd | |||
75572279a9 | |||
328c6a0f7c | |||
ecd5062975 | |||
c01ae5610d | |||
564e7f31c3 | |||
efa61952b3 | |||
ae27a26ebe | |||
d81076f720 | |||
528f16c5e1 | |||
13ac8bfa90 | |||
935c58b17b | |||
31e133bf06 | |||
eb7fb6694c | |||
a298577da1 | |||
8cd6d43ec4 | |||
b8fd143cc1 | |||
045ae058a3 | |||
52c9816db2 | |||
32440580f6 | |||
906d986f4b | |||
0198651ca0 | |||
d911c40897 | |||
ca8a117335 | |||
f75e2f0ada | |||
ea12b62235 | |||
0af4af7e50 | |||
f017491f44 | |||
ed433d3312 | |||
9ba2e83857 | |||
af0ec74704 | |||
251402e844 | |||
1a5318ebe0 | |||
15aac3f436 | |||
5f373632e6 | |||
0d518bf8df | |||
16b735c3a4 | |||
1239f094fd | |||
6986fa51eb | |||
f4bc88bf73 | |||
ad915536a3 | |||
f48b31664e | |||
d6c0eb1998 | |||
d3dcfba463 | |||
3619dcd777 | |||
86c002e318 | |||
42a807ce9c | |||
9bfff9539e | |||
edf4d919fe | |||
b0fb3d8c61 | |||
5143090142 | |||
bc1555ca5b | |||
146a392f1a | |||
e2fc205a35 | |||
d4bdb216d2 | |||
bffc1d6a1f | |||
df0d890c11 | |||
dceee4dede | |||
dedbbf12bb | |||
bdb53d07f5 | |||
547991df72 | |||
fd17557a96 | |||
a9e928a799 | |||
84b3e086e6 | |||
5e3c794aec | |||
98acbb2b2b | |||
318f0753b0 | |||
8908adbdee | |||
fad7a26550 | |||
38005fc0ef | |||
e3d3181ad6 | |||
83d069e4d9 | |||
f2db5a8229 | |||
6ebb777256 | |||
db50c531d9 | |||
218f7af2e3 | |||
a19d2956c1 | |||
7a6b5b965d | |||
8a871aaddd | |||
bec0a0df10 | |||
1068a2dfbe | |||
b62be5d584 | |||
9686019693 | |||
32a066b4d4 | |||
d5a415e01e | |||
33f83b8486 | |||
adedf8d34f | |||
9243071f03 | |||
36050850d4 | |||
c6d5cc555a | |||
b271aa2aaa | |||
d24961b3b9 | |||
e52543efe7 | |||
0b0e27ad3b | |||
9f460f6c99 | |||
c4529bb9a3 | |||
5a069d2ffc | |||
4fc78e455a | |||
75e2198315 | |||
0d4bb4ed2d | |||
577738ad0a | |||
a8dfdb03fb | |||
aebd470d49 | |||
ce3dfd9678 | |||
b82801b145 | |||
a6ab35f1fb | |||
a265cea4fb | |||
166760651e | |||
41a9191223 | |||
1465e7760e | |||
1a043f1dc0 | |||
70c5b00c90 | |||
bb5177dc79 | |||
f60a9a632b | |||
024c47132b | |||
73ce9cb4cc | |||
f45d439094 | |||
2d5749a78c | |||
f078a10028 | |||
701f88e532 | |||
1524a1997d | |||
b16f812f91 | |||
cde307d644 | |||
c47f6d093c | |||
f5336938c9 | |||
4985548edf | |||
8bc4841e84 | |||
634f9833a1 | |||
80052e62ee | |||
0672b6a157 | |||
1e71bfa151 | |||
d4c2c8197a | |||
c9baed2df9 | |||
1e2065c84a | |||
537d683dfb | |||
e6f1b9f8c8 | |||
5815d2bfce | |||
7692e93306 | |||
8768b83251 | |||
35d18810fc | |||
ccd40a1834 | |||
5c1f0c3541 | |||
e237e52ae4 | |||
effec90528 | |||
de4a2247c0 | |||
b20b7cbc98 | |||
3c347b9b77 | |||
794c4066d8 | |||
8c8e3072a7 | |||
228708eec4 | |||
dcafaedaa8 | |||
a85297f2c8 | |||
91f7450cbd | |||
e7b074991d | |||
5f83ce2a28 | |||
44af896dee | |||
9eaa9a98ee | |||
629880cd58 | |||
42266ed14f | |||
9dd7a197e4 | |||
6f39da20a9 | |||
1ebb6f9257 | |||
e30e982ef0 | |||
42a80c6185 | |||
0d495f4d65 | |||
71f5e66bd1 | |||
a6f71b68f3 | |||
a527f343cd | |||
f8e1c454d9 | |||
da6e55f09f | |||
d277e2788a | |||
53ba747b78 | |||
b9862acd1d | |||
d1c19382a1 | |||
4141983ccd | |||
2b8b5001e0 | |||
532126561e | |||
3c026c4eef | |||
ae6c15158f | |||
6aa57247f9 | |||
50862ac945 | |||
01ce6faa20 | |||
6e571aef95 | |||
d6091c83e2 | |||
5ca55fc13e | |||
2b60da02e8 | |||
f02297152c | |||
af7c0bbc72 | |||
40727d5a6c | |||
9bdd73dbc6 | |||
3e1ed09f12 | |||
3682c625d7 | |||
70cd52ac95 | |||
3264da4b2f | |||
06c1648f55 | |||
94c3e5d41d | |||
7ae20d8c3b | |||
0ff4411abf | |||
3d68b7c9e5 | |||
f8b1cae0c9 | |||
5bee0fc453 | |||
7162765824 | |||
99aa6be887 | |||
afaaff298f | |||
68cf9af6a3 | |||
8d5752d6b5 | |||
98dbcc4c69 | |||
687d8fd120 | |||
114ace23f7 | |||
3fbc221db0 | |||
f2e6e137a6 | |||
bb232f1b3e | |||
9dc153bc22 | |||
b9bbdf5978 | |||
e0db0e394d | |||
3e9001721e | |||
bbf3b0f86f | |||
7f3eb9f11b | |||
c783c51915 | |||
0816d222c6 | |||
af16c70143 | |||
748b6f6e2e | |||
3bd600ed52 | |||
78e6ad0f59 | |||
2d71dc68f1 | |||
7dc2a00d47 | |||
7ece78a05b | |||
ff2bace1bf | |||
e63ce01bdc | |||
4439ce71d0 | |||
27ec094775 | |||
1bed53e07b | |||
8ae06c0c4f | |||
372db7865b | |||
cfeffc551e | |||
d519815a7e | |||
32932eacf5 | |||
1a057cf5a3 | |||
e3936dad9b | |||
4ec9b85506 | |||
fbae081c33 | |||
cfb490b7e3 | |||
1947a3cfd5 | |||
fca6774d4a | |||
5fd1e978ec | |||
4985c549dd | |||
e6c378aef0 | |||
0657835ba3 | |||
36934de04a | |||
e735ab80b1 | |||
9904ca89d4 | |||
554ab54690 | |||
5abcd18ba0 | |||
03bf41b250 | |||
0df6bde5cb | |||
e3a28b640f | |||
94576fae44 | |||
d091d9c3e4 | |||
01c9e48f4f | |||
5639c18c2f | |||
25b835f4d8 | |||
78fb5b3093 | |||
73b85ef9df | |||
93c604a639 | |||
053676a320 | |||
d1a142cefd | |||
36cf2a4126 | |||
a3bc9ae8d0 | |||
6d77ae24b3 | |||
d9bbed5f69 | |||
d6ce405e71 |
22
.github/CONTRIBUTING.md
vendored
Normal file
22
.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
## Contributing to obs-websocket
|
||||||
|
|
||||||
|
### Translating obs-websocket to your language
|
||||||
|
Localization happens on Crowdin: https://crowdin.com/project/obs-websocket
|
||||||
|
|
||||||
|
### Writing code for obs-websocket
|
||||||
|
#### Coding Guidelines
|
||||||
|
- Function and variable names: snake_case for C names, CamelCase for C++ names
|
||||||
|
- Tabs are 8 columns wide
|
||||||
|
- 80 columns max.
|
||||||
|
|
||||||
|
#### Commit Guidelines
|
||||||
|
- Commits follow the 50/72 standard:
|
||||||
|
- 50 characters max for the title
|
||||||
|
- One empty line after the title
|
||||||
|
- Description wrapped to 72 columns max per line.
|
||||||
|
- Commit titles:
|
||||||
|
- Use present tense
|
||||||
|
- Prefix the title with a "scope" name
|
||||||
|
- e.g: "CI: fix wrong behaviour when packaging for OS X"
|
||||||
|
- Typical scopes: CI, General, Request, Event, Server
|
||||||
|
- Look at existing commits for more examples
|
18
.github/ISSUE_TEMPLATE.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#### Issue type
|
||||||
|
- [ ] Bug
|
||||||
|
- [ ] Feature request
|
||||||
|
- [ ] Other
|
||||||
|
|
||||||
|
#### Description
|
||||||
|
*Replace this with a description of the bug encountered or feature requested.*
|
||||||
|
|
||||||
|
#### Steps to reproduce
|
||||||
|
*If it's a bug, please describe the steps to reproduce it. Otherwise, remove this section.*
|
||||||
|
|
||||||
|
#### Technical information
|
||||||
|
- **Operating System** :
|
||||||
|
- **OBS Studio version** :
|
||||||
|
|
||||||
|
#### Development Environment
|
||||||
|
*If you're trying to compile obs-websocket, please describe your compiler type and version (e.g: GCC 4.7, VC2013, ...), and the CMake settings used.
|
||||||
|
Remove this section if it doesn't apply to your case.*
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,3 +1,7 @@
|
|||||||
|
*~
|
||||||
|
.DS_Store
|
||||||
/build/
|
/build/
|
||||||
/build32/
|
/build32/
|
||||||
/build64/
|
/build64/
|
||||||
|
/release/
|
||||||
|
/installer/Output/
|
||||||
|
55
.travis.yml
Normal file
55
.travis.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
language: cpp
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
# AWS key ID
|
||||||
|
- secure: pAiNUGVbjP12BfnWPk0FFTkbnk4Tocvv88XiT3rzRqkQaD7/iyEogLBfHM4nOEgFiIMHbC41aE83w5JgRNPwn6mTgoQBOglzqq1tGuXfqPyV2VStk8beji1evubGoVjjPaoPTFyIdQc5GGxdHyogI/ed9Hb3ccyykYvjyolj9XoCiW42QHx60AHGwl+So+dEa8xydj9SLRPlZ/AitmI/cPVN3YotA7s37BLFiab54enxk7T4rwpR1nU0HVfoCpn5F4wZYxRq+LlSVFzC8vVE9cpDSLS5kjrZIZaT18tYG1/untCj+wqMIZbghaJXLtPSRW2YPHcJTz8q1YSXnJ19+0uiAIMAqaVv0kD5BAM97byYDBW+b9H6SYFkb/Pw/qcK9amMzMBjDPFpYFkl9Q2kzhsNs3HsZf/flSZjtrkQJiP3SOi/KvKzVK9X4Wym6hYZWHgmMTTYFrvr6BYnf2GkpfKNjm1d2kc0NNrq4d5H4NOEQB8MP+QH+o+BPeM6d9dthrUc1Pw+BXzOAr85CN4qtpPGoAl/Dbfgd6eu/88E2LpUufW2VFAOPWjykSOqzSN3orh7AaWuE34VFEnQ+2y3uIE8AKoyXzJv6zYkyNnNewKZeGe2kKYNwLn5UxQA9JEj7a+tvVevk4xBSkkjFAvjSG2z8/F1FXNbEfoLX1Hz/bU=
|
||||||
|
# AWS key secret
|
||||||
|
- secure: bGwljoP3E1OVBXLXox0O6p8kwQXLcNQ8YDKVa4H8u9Y+Ic7uqE4iV3rYS3ynNWSBMVRWY3ZbyClnhrCNwRhBAlcd8qWSJdpjVzs6HdQyzhuKa1P3V4FJPb7upGP/5R/DECGwex8Mun9dmXpYDak75LxfKIJUidPis5VDCYqul7k/xVVCou6Ctjpj7vQhWXDj2G/py+mdB8DERhymnQCtyK1Ziu8c4QlFKByZmnD72GFm/h3JPI1Pq1V2mz3x6x6GaYjb9Rdbd0UNwqjGQX4q2M/c3GEJa6B2JBCoTncawNZBNnPUF9qtv+zh0TNaNHMRWX13AJ/qYB+nVDub0C9b/6Mc48mt0Tv4ze15MproVrylZdV6qHYEG8yGPBqpTVbRP6gv6Y2TXIHWoTzqA+F/Gv2IDChyHXsld/MQQS2MSo5iaYktIrZKtX8Z0qAmTzPwIVBromaSI3vrE7UH0fRSQ6fAM8+Tn+MRthOBdqu23kS1dnG+X2CPbUhBfsJp0OSwVQD5jQtA51/sREVeGFiJvzQIkvwQDjb5MYilsRnwmoBXemkLmqaviXVY4rz1o5AIvz2pgZS2YggK1xHZCuI5tSjcNEkb77VwZTfsqrdDo9EJh6VgfdnGlHQhR2/A5hUJ4ANpJ/LgZlgfVp71Xg2GWQW6M4Znc5uj6A6xLBkO6FA=
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: linux
|
||||||
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
before_install:
|
||||||
|
- docker run -d --name xenial -v $(dirname $(pwd)):/root -v /home/travis/package:/package
|
||||||
|
-e TRAVIS_BRANCH="$TRAVIS_BRANCH" -e TRAVIS_TAG="$TRAVIS_TAG" -w /root nimmis/ubuntu:16.04
|
||||||
|
- docker exec -it xenial /root/obs-websocket/CI/install-dependencies-xenial.sh
|
||||||
|
script:
|
||||||
|
- docker exec -it xenial /root/obs-websocket/CI/build-xenial.sh
|
||||||
|
after_success:
|
||||||
|
- docker exec -it xenial /root/obs-websocket/CI/package-xenial.sh
|
||||||
|
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
before_install: "./CI/install-dependencies-osx.sh"
|
||||||
|
script: "./CI/build-osx.sh"
|
||||||
|
after_success: "./CI/package-osx.sh"
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
- provider: s3
|
||||||
|
region: eu-central-1
|
||||||
|
bucket: obs-websocket-linux-builds
|
||||||
|
access_key_id: "$AWS_ID"
|
||||||
|
secret_access_key: "$AWS_SECRET"
|
||||||
|
local_dir: /home/travis/package
|
||||||
|
skip_cleanup: true
|
||||||
|
acl: public_read
|
||||||
|
on:
|
||||||
|
repo: Palakis/obs-websocket
|
||||||
|
condition: "$TRAVIS_OS_NAME = linux"
|
||||||
|
all_branches: true
|
||||||
|
- provider: s3
|
||||||
|
region: eu-central-1
|
||||||
|
bucket: obs-websocket-osx-builds
|
||||||
|
access_key_id: "$AWS_ID"
|
||||||
|
secret_access_key: "$AWS_SECRET"
|
||||||
|
local_dir: release
|
||||||
|
skip_cleanup: true
|
||||||
|
acl: public_read
|
||||||
|
on:
|
||||||
|
repo: Palakis/obs-websocket
|
||||||
|
condition: "$TRAVIS_OS_NAME = osx"
|
||||||
|
all_branches: true
|
29
BUILDING.md
Normal file
29
BUILDING.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Compiling obs-websocket
|
||||||
|
## Prerequisites
|
||||||
|
You'll need [QT 5.7.0](https://download.qt.io/official_releases/qt/5.7/5.7.0/), CMake, and a working development environment for OBS Studio installed on your computer.
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
In cmake-gui, you'll have to set the following variables :
|
||||||
|
- **QTDIR** (path) : location of the Qt environment suited for your compiler and architecture
|
||||||
|
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source code of OBS Studio
|
||||||
|
- **LIBOBS_LIB** (filepath) : location of the obs.lib file
|
||||||
|
- **OBS_FRONTEND_LIB** (filepath) : location of the obs-frontend-api.lib file
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
On Debian/Ubuntu :
|
||||||
|
```
|
||||||
|
sudo apt-get install libqt5websockets5-dev
|
||||||
|
git clone --recursive https://github.com/Palakis/obs-websocket.git
|
||||||
|
cd obs-websocket
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -DLIBOBS_INCLUDE_DIR="<path to the libobs sub-folder in obs-studio's source code>" -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
make -j4
|
||||||
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
|
## OS X
|
||||||
|
*To do*
|
||||||
|
|
||||||
|
## Automated Builds
|
||||||
|
- Windows : [](https://ci.appveyor.com/project/Palakis/obs-websocket/history)
|
||||||
|
- Linux & OS X : [](https://travis-ci.org/Palakis/obs-websocket)
|
11
CI/build-osx.sh
Executable file
11
CI/build-osx.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
mkdir build && cd build
|
||||||
|
|
||||||
|
cmake .. \
|
||||||
|
-DQTDIR=/usr/local/opt/qt5 \
|
||||||
|
-DLIBOBS_INCLUDE_DIR=../../obs-studio/libobs \
|
||||||
|
-DOBS_FRONTEND_LIB="$(pwd)/../../obs-studio/build/UI/obs-frontend-api/libobs-frontend-api.dylib" \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
&& make -j4
|
8
CI/build-xenial.sh
Executable file
8
CI/build-xenial.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
cd /root/obs-websocket
|
||||||
|
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -DLIBOBS_INCLUDE_DIR="../../obs-studio/libobs" -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
make -j4
|
27
CI/install-dependencies-osx.sh
Executable file
27
CI/install-dependencies-osx.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
# OBS Studio deps
|
||||||
|
brew update
|
||||||
|
brew install ffmpeg
|
||||||
|
brew install libav
|
||||||
|
|
||||||
|
# qtwebsockets deps
|
||||||
|
brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/fdb7c6e960e830b3bf630850c0002c5df9f68ed8/Formula/qt5.rb
|
||||||
|
|
||||||
|
# Build obs-studio
|
||||||
|
cd ..
|
||||||
|
git clone --recursive https://github.com/jp9000/obs-studio
|
||||||
|
cd obs-studio
|
||||||
|
git checkout 19.0.2
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake .. \
|
||||||
|
-DCMAKE_PREFIX_PATH=/usr/local/opt/qt5/lib/cmake \
|
||||||
|
&& make -j4
|
||||||
|
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
# Packages app
|
||||||
|
cd ..
|
||||||
|
curl -L -O https://www.slepin.fr/obs-websocket/ci/Packages.pkg -f --retry 5 -C -
|
||||||
|
sudo installer -pkg ./Packages.pkg -target /
|
57
CI/install-dependencies-xenial.sh
Executable file
57
CI/install-dependencies-xenial.sh
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
# OBS Studio deps
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get install -y \
|
||||||
|
libc-dev-bin libc6-dev \
|
||||||
|
git \
|
||||||
|
build-essential
|
||||||
|
|
||||||
|
apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
checkinstall \
|
||||||
|
cmake \
|
||||||
|
libasound2-dev \
|
||||||
|
libavcodec-dev \
|
||||||
|
libavdevice-dev \
|
||||||
|
libavfilter-dev \
|
||||||
|
libavformat-dev \
|
||||||
|
libavutil-dev \
|
||||||
|
libcurl4-openssl-dev \
|
||||||
|
libfontconfig-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libgl1-mesa-dev \
|
||||||
|
libjack-jackd2-dev \
|
||||||
|
libjansson-dev \
|
||||||
|
libpulse-dev \
|
||||||
|
libqt5x11extras5-dev \
|
||||||
|
libspeexdsp-dev \
|
||||||
|
libswresample-dev \
|
||||||
|
libswscale-dev \
|
||||||
|
libudev-dev \
|
||||||
|
libv4l-dev \
|
||||||
|
libvlc-dev \
|
||||||
|
libx11-dev \
|
||||||
|
libx264-dev \
|
||||||
|
libxcb-shm0-dev \
|
||||||
|
libxcb-xinerama0-dev \
|
||||||
|
libxcomposite-dev \
|
||||||
|
libxinerama-dev \
|
||||||
|
pkg-config \
|
||||||
|
qtbase5-dev
|
||||||
|
|
||||||
|
# obs-websocket deps
|
||||||
|
apt-get install -y libqt5websockets5-dev
|
||||||
|
|
||||||
|
# Build obs-studio
|
||||||
|
cd /root
|
||||||
|
git clone https://github.com/jp9000/obs-studio ./obs-studio
|
||||||
|
cd obs-studio
|
||||||
|
git checkout 19.0.2
|
||||||
|
mkdir build && cd build
|
||||||
|
cmake -DUNIX_STRUCTURE=1 -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||||
|
make -j4
|
||||||
|
make install
|
||||||
|
|
||||||
|
ldconfig
|
843
CI/osx/obs-websocket.pkgproj
Normal file
843
CI/osx/obs-websocket.pkgproj
Normal file
@ -0,0 +1,843 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>PROJECT</key>
|
||||||
|
<dict>
|
||||||
|
<key>PACKAGE_FILES</key>
|
||||||
|
<dict>
|
||||||
|
<key>DEFAULT_INSTALL_LOCATION</key>
|
||||||
|
<string>/</string>
|
||||||
|
<key>HIERARCHY</key>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>../../build/QtNetwork</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>292</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>../../build/QtWebSockets</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>292</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>bin</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>509</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Resources</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>509</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Contents</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>509</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>OBS.app</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>509</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Applications</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>509</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>../../build/obs-websocket.so</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>bin</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>../../data</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>obs-websocket</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>plugins</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>obs-studio</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Application Support</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Automator</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Documentation</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Extensions</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Filesystems</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Frameworks</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Input Methods</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Internet Plug-Ins</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>LaunchAgents</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>LaunchDaemons</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>PreferencePanes</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Preferences</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Printers</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>PrivilegedHelperTools</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>QuickLook</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>QuickTime</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Screen Savers</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Scripts</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Services</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Widgets</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Library</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CHILDREN</key>
|
||||||
|
<array/>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Shared</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>1023</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>80</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>Users</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>GID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>/</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>PERMISSIONS</key>
|
||||||
|
<integer>493</integer>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>UID</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<key>PAYLOAD_TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>VERSION</key>
|
||||||
|
<integer>4</integer>
|
||||||
|
</dict>
|
||||||
|
<key>PACKAGE_SCRIPTS</key>
|
||||||
|
<dict>
|
||||||
|
<key>RESOURCES</key>
|
||||||
|
<array/>
|
||||||
|
</dict>
|
||||||
|
<key>PACKAGE_SETTINGS</key>
|
||||||
|
<dict>
|
||||||
|
<key>AUTHENTICATION</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>CONCLUSION_ACTION</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>IDENTIFIER</key>
|
||||||
|
<string>fr.palakis.obswebsocket</string>
|
||||||
|
<key>OVERWRITE_PERMISSIONS</key>
|
||||||
|
<false/>
|
||||||
|
<key>VERSION</key>
|
||||||
|
<string>4.0.0</string>
|
||||||
|
</dict>
|
||||||
|
<key>PROJECT_COMMENTS</key>
|
||||||
|
<dict>
|
||||||
|
<key>NOTES</key>
|
||||||
|
<data>
|
||||||
|
PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1M
|
||||||
|
IDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQv
|
||||||
|
c3RyaWN0LmR0ZCI+CjxodG1sPgo8aGVhZD4KPG1ldGEgaHR0cC1l
|
||||||
|
cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7
|
||||||
|
IGNoYXJzZXQ9VVRGLTgiPgo8bWV0YSBodHRwLWVxdWl2PSJDb250
|
||||||
|
ZW50LVN0eWxlLVR5cGUiIGNvbnRlbnQ9InRleHQvY3NzIj4KPHRp
|
||||||
|
dGxlPjwvdGl0bGU+CjxtZXRhIG5hbWU9IkdlbmVyYXRvciIgY29u
|
||||||
|
dGVudD0iQ29jb2EgSFRNTCBXcml0ZXIiPgo8bWV0YSBuYW1lPSJD
|
||||||
|
b2NvYVZlcnNpb24iIGNvbnRlbnQ9IjE0MDQuMTMiPgo8c3R5bGUg
|
||||||
|
dHlwZT0idGV4dC9jc3MiPgo8L3N0eWxlPgo8L2hlYWQ+Cjxib2R5
|
||||||
|
Pgo8L2JvZHk+CjwvaHRtbD4K
|
||||||
|
</data>
|
||||||
|
</dict>
|
||||||
|
<key>PROJECT_SETTINGS</key>
|
||||||
|
<dict>
|
||||||
|
<key>BUILD_PATH</key>
|
||||||
|
<dict>
|
||||||
|
<key>PATH</key>
|
||||||
|
<string>../../release</string>
|
||||||
|
<key>PATH_TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<key>EXCLUDED_FILES</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.DS_Store</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove .DS_Store files</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove ".DS_Store" files created by the Finder.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.pbdevelopment</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove .pbdevelopment files</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove ".pbdevelopment" files created by ProjectBuilder or Xcode.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>CVS</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.cvsignore</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.cvspass</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.svn</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.git</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>.gitignore</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove SCM metadata</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>classes.nib</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>designable.db</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>info.nib</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Optimize nib files</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>PATTERNS_ARRAY</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>REGULAR_EXPRESSION</key>
|
||||||
|
<false/>
|
||||||
|
<key>STRING</key>
|
||||||
|
<string>Resources Disabled</string>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>PROTECTED</key>
|
||||||
|
<true/>
|
||||||
|
<key>PROXY_NAME</key>
|
||||||
|
<string>Remove Resources Disabled folders</string>
|
||||||
|
<key>PROXY_TOOLTIP</key>
|
||||||
|
<string>Remove "Resources Disabled" folders.</string>
|
||||||
|
<key>STATE</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>SEPARATOR</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>NAME</key>
|
||||||
|
<string>obs-websocket</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>TYPE</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>VERSION</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
59
CI/package-osx.sh
Executable file
59
CI/package-osx.sh
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "-- Preparing package build"
|
||||||
|
export QT_PREFIX="/usr/local/opt/qt5"
|
||||||
|
|
||||||
|
export WS_LIB="$QT_PREFIX/lib/QtWebSockets.framework/QtWebSockets"
|
||||||
|
export NET_LIB="$QT_PREFIX/lib/QtNetwork.framework/QtNetwork"
|
||||||
|
|
||||||
|
export GIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
|
||||||
|
export VERSION="$GIT_HASH-$TRAVIS_BRANCH"
|
||||||
|
export LATEST_VERSION="$TRAVIS_BRANCH"
|
||||||
|
if [ -n "${TRAVIS_TAG}" ]; then
|
||||||
|
export VERSION="$TRAVIS_TAG"
|
||||||
|
export LATEST_VERSION="$TRAVIS_TAG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export FILENAME="obs-websocket-$VERSION.pkg"
|
||||||
|
export LATEST_FILENAME="obs-websocket-latest-$LATEST_VERSION.pkg"
|
||||||
|
|
||||||
|
echo "-- Copying Qt dependencies"
|
||||||
|
cp $WS_LIB ./build
|
||||||
|
cp $NET_LIB ./build
|
||||||
|
|
||||||
|
chmod +rw ./build/QtWebSockets ./build/QtNetwork
|
||||||
|
|
||||||
|
echo "-- Modifying QtNetwork"
|
||||||
|
# TODO : put a loop in there
|
||||||
|
install_name_tool \
|
||||||
|
-change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
|
||||||
|
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
||||||
|
./build/QtNetwork
|
||||||
|
|
||||||
|
echo "-- Modifying QtWebSockets"
|
||||||
|
install_name_tool \
|
||||||
|
-change /usr/local/opt/qt/lib/QtWebSockets.framework/Versions/5/QtWebSockets @rpath/QtWebSockets \
|
||||||
|
-change /usr/local/opt/qt/lib/QtNetwork.framework/Versions/5/QtNetwork @rpath/QtNetwork \
|
||||||
|
-change /usr/local/opt/qt/lib/QtCore.framework/Versions/5/QtCore @rpath/QtCore \
|
||||||
|
./build/QtWebSockets
|
||||||
|
|
||||||
|
echo "-- Modifying obs-websocket.so"
|
||||||
|
install_name_tool \
|
||||||
|
-change "$QT_PREFIX/lib/QtWebSockets.framework/Versions/5/QtWebSockets" @rpath/QtWebSockets \
|
||||||
|
-change "$QT_PREFIX/lib/QtWidgets.framework/Versions/5/QtWidgets" @rpath/QtWidgets \
|
||||||
|
-change "$QT_PREFIX/lib/QtNetwork.framework/Versions/5/QtNetwork" @rpath/QtNetwork \
|
||||||
|
-change "$QT_PREFIX/lib/QtGui.framework/Versions/5/QtGui" @rpath/QtGui \
|
||||||
|
-change "$QT_PREFIX/lib/QtCore.framework/Versions/5/QtCore" @rpath/QtCore \
|
||||||
|
./build/obs-websocket.so
|
||||||
|
|
||||||
|
chmod -w ./build/QtWebSockets ./build/QtNetwork
|
||||||
|
|
||||||
|
echo "-- Actual package build"
|
||||||
|
packagesbuild ./CI/osx/obs-websocket.pkgproj
|
||||||
|
|
||||||
|
echo "-- Renaming obs-websocket.pkg to $FILENAME"
|
||||||
|
mv ./release/obs-websocket.pkg ./release/$FILENAME
|
||||||
|
cp ./release/$FILENAME ./release/$LATEST_FILENAME
|
24
CI/package-xenial.sh
Executable file
24
CI/package-xenial.sh
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd /root/obs-websocket
|
||||||
|
|
||||||
|
export GIT_HASH=$(git rev-parse --short HEAD)
|
||||||
|
export PKG_VERSION="1-$GIT_HASH-$TRAVIS_BRANCH-git"
|
||||||
|
|
||||||
|
if [ -n "${TRAVIS_TAG}" ]; then
|
||||||
|
export PKG_VERSION="$TRAVIS_TAG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd /root/obs-websocket/build
|
||||||
|
|
||||||
|
PAGER=cat checkinstall -y --type=debian --fstrans=no --nodoc \
|
||||||
|
--backup=no --deldoc=yes --install=no \
|
||||||
|
--pkgname=obs-websocket --pkgversion="$PKG_VERSION" \
|
||||||
|
--pkglicense="GPLv2.0" --maintainer="contact@slepin.fr" \
|
||||||
|
--requires="libqt5websockets5" --pkggroup="video" \
|
||||||
|
--pkgsource="https://github.com/Palakis/obs-websocket" \
|
||||||
|
--pakdir="/package"
|
||||||
|
|
||||||
|
chmod ao+r /package/*
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.5)
|
cmake_minimum_required(VERSION 3.2)
|
||||||
project(obs-websocket)
|
project(obs-websocket)
|
||||||
|
|
||||||
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
set(CMAKE_PREFIX_PATH "${QTDIR}")
|
||||||
@ -7,18 +7,14 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
include(external/FindLibObs.cmake)
|
include(external/FindLibObs.cmake)
|
||||||
|
|
||||||
find_package(LibObs REQUIRED)
|
find_package(LibObs REQUIRED)
|
||||||
find_package(Qt5Core REQUIRED)
|
find_package(Qt5Core REQUIRED)
|
||||||
find_package(Qt5WebSockets REQUIRED)
|
find_package(Qt5WebSockets REQUIRED)
|
||||||
find_package(Qt5Widgets REQUIRED)
|
find_package(Qt5Widgets REQUIRED)
|
||||||
|
|
||||||
add_subdirectory(deps/mbedtls EXCLUDE_FROM_ALL)
|
add_subdirectory(deps/mbedtls EXCLUDE_FROM_ALL)
|
||||||
set(ENABLED_PROGRAMS false)
|
set(ENABLE_PROGRAMS false)
|
||||||
|
|
||||||
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
|
||||||
if(OBS_FRONTEND_LIB EQUAL "OBS_FRONTEND_LIB-NOTFOUND")
|
|
||||||
message(FATAL_ERROR "Could not find OBS Frontend API\'s library !")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(obs-websocket_SOURCES
|
set(obs-websocket_SOURCES
|
||||||
obs-websocket.cpp
|
obs-websocket.cpp
|
||||||
@ -38,10 +34,13 @@ set(obs-websocket_HEADERS
|
|||||||
Utils.h
|
Utils.h
|
||||||
forms/settings-dialog.h)
|
forms/settings-dialog.h)
|
||||||
|
|
||||||
|
# --- Platform-independent build settings ---
|
||||||
add_library(obs-websocket MODULE
|
add_library(obs-websocket MODULE
|
||||||
${obs-websocket_SOURCES}
|
${obs-websocket_SOURCES}
|
||||||
${obs-websocket_HEADERS})
|
${obs-websocket_HEADERS})
|
||||||
|
|
||||||
add_dependencies(obs-websocket mbedcrypto)
|
add_dependencies(obs-websocket mbedcrypto)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
"${LIBOBS_INCLUDE_DIR}/../UI/obs-frontend-api"
|
||||||
${Qt5Core_INCLUDES}
|
${Qt5Core_INCLUDES}
|
||||||
@ -49,10 +48,92 @@ include_directories(
|
|||||||
${Qt5Widgets_INCLUDES}
|
${Qt5Widgets_INCLUDES}
|
||||||
${mbedcrypto_INCLUDES}
|
${mbedcrypto_INCLUDES}
|
||||||
"${CMAKE_SOURCE_DIR}/deps/mbedtls/include")
|
"${CMAKE_SOURCE_DIR}/deps/mbedtls/include")
|
||||||
|
|
||||||
target_link_libraries(obs-websocket
|
target_link_libraries(obs-websocket
|
||||||
libobs
|
libobs
|
||||||
${OBS_FRONTEND_LIB}
|
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::WebSockets
|
Qt5::WebSockets
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
mbedcrypto)
|
mbedcrypto)
|
||||||
|
|
||||||
|
# --- End of section ---
|
||||||
|
|
||||||
|
# --- Windows-specific build settings and tasks ---
|
||||||
|
if(WIN32)
|
||||||
|
if(NOT DEFINED OBS_FRONTEND_LIB)
|
||||||
|
set(OBS_FRONTEND_LIB "OBS_FRONTEND_LIB-NOTFOUND" CACHE FILEPATH "OBS frontend library")
|
||||||
|
message(FATAL_ERROR "Could not find OBS Frontend API\'s library !")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(obs-websocket
|
||||||
|
"${OBS_FRONTEND_LIB}")
|
||||||
|
|
||||||
|
add_custom_command(TARGET obs-websocket POST_BUILD
|
||||||
|
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy
|
||||||
|
"${QTDIR}/bin/Qt5WebSockets.dll"
|
||||||
|
"${QTDIR}/bin/Qt5Network.dll"
|
||||||
|
"${CMAKE_BINARY_DIR}/$<CONFIG>")
|
||||||
|
|
||||||
|
COMMAND if $<CONFIG:Debug>==1 ("${CMAKE_COMMAND}" -E copy
|
||||||
|
"${QTDIR}/bin/Qt5WebSocketsd.dll"
|
||||||
|
"${QTDIR}/bin/Qt5Networkd.dll"
|
||||||
|
"${CMAKE_BINARY_DIR}/$<CONFIG>")
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Release package helper ---
|
||||||
|
# The "release" folder has a structure similar OBS' one on Windows
|
||||||
|
set(RELEASE_DIR "${PROJECT_SOURCE_DIR}/release")
|
||||||
|
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(ARCH_NAME "64bit")
|
||||||
|
else()
|
||||||
|
set(ARCH_NAME "32bit")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_command(TARGET obs-websocket POST_BUILD
|
||||||
|
COMMAND if $<CONFIG:Release>==1 (
|
||||||
|
"${CMAKE_COMMAND}" -E make_directory
|
||||||
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket"
|
||||||
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
|
||||||
|
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy_directory
|
||||||
|
"${PROJECT_SOURCE_DIR}/data"
|
||||||
|
"${RELEASE_DIR}/data/obs-plugins/obs-websocket")
|
||||||
|
|
||||||
|
COMMAND if $<CONFIG:Release>==1 ("${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_FILE:obs-websocket>"
|
||||||
|
"${QTDIR}/bin/Qt5WebSockets.dll"
|
||||||
|
"${QTDIR}/bin/Qt5Network.dll"
|
||||||
|
"${RELEASE_DIR}/obs-plugins/${ARCH_NAME}")
|
||||||
|
)
|
||||||
|
# --- End of sub-section ---
|
||||||
|
|
||||||
|
endif()
|
||||||
|
# --- End of section ---
|
||||||
|
|
||||||
|
# --- Linux-specific build settings and tasks ---
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||||
|
|
||||||
|
target_compile_options(mbedcrypto PRIVATE -fPIC)
|
||||||
|
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
||||||
|
target_link_libraries(obs-websocket
|
||||||
|
obs-frontend-api)
|
||||||
|
|
||||||
|
file(GLOB locale_files data/locale/*.ini)
|
||||||
|
|
||||||
|
install(TARGETS obs-websocket
|
||||||
|
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/obs-plugins")
|
||||||
|
install(FILES ${locale_files}
|
||||||
|
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/obs/obs-plugins/obs-websocket/locale")
|
||||||
|
endif()
|
||||||
|
# --- End of section ---
|
||||||
|
|
||||||
|
# -- OS X specific build settings and tasks --
|
||||||
|
if(APPLE)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -fvisibility=default")
|
||||||
|
|
||||||
|
set_target_properties(obs-websocket PROPERTIES PREFIX "")
|
||||||
|
target_link_libraries(obs-websocket "${OBS_FRONTEND_LIB}")
|
||||||
|
endif()
|
||||||
|
# -- End of section --
|
||||||
|
188
Config.cpp
188
Config.cpp
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,22 +19,54 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <mbedtls/base64.h>
|
#include <mbedtls/base64.h>
|
||||||
#include <mbedtls/sha256.h>
|
#include <mbedtls/sha256.h>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
#include <util/config-file.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
#define CONFIG_SECTION_NAME "obs-websocket"
|
#define SECTION_NAME "WebsocketAPI"
|
||||||
#define CONFIG_PARAM_SECRET "auth_hash"
|
#define PARAM_ENABLE "ServerEnabled"
|
||||||
#define CONFIG_PARAM_SALT "auth_salt"
|
#define PARAM_PORT "ServerPort"
|
||||||
#define CONFIG_PARAM_AUTHREQUIRED "auth_required"
|
#define PARAM_DEBUG "DebugEnabled"
|
||||||
|
#define PARAM_AUTHREQUIRED "AuthRequired"
|
||||||
|
#define PARAM_SECRET "AuthSecret"
|
||||||
|
#define PARAM_SALT "AuthSalt"
|
||||||
|
|
||||||
Config *Config::_instance = new Config();
|
Config *Config::_instance = new Config();
|
||||||
|
|
||||||
Config::Config() {
|
Config::Config()
|
||||||
|
{
|
||||||
// Default settings
|
// Default settings
|
||||||
|
ServerEnabled = true;
|
||||||
|
ServerPort = 4444;
|
||||||
|
|
||||||
|
DebugEnabled = false;
|
||||||
|
|
||||||
AuthRequired = false;
|
AuthRequired = false;
|
||||||
Secret = "";
|
Secret = "";
|
||||||
Salt = "";
|
Salt = "";
|
||||||
SettingsLoaded = false;
|
SettingsLoaded = false;
|
||||||
|
|
||||||
|
// OBS Config defaults
|
||||||
|
config_t* obs_config = obs_frontend_get_global_config();
|
||||||
|
if (obs_config)
|
||||||
|
{
|
||||||
|
config_set_default_bool(obs_config,
|
||||||
|
SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
|
config_set_default_uint(obs_config,
|
||||||
|
SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
|
||||||
|
config_set_default_bool(obs_config,
|
||||||
|
SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
|
|
||||||
|
config_set_default_bool(obs_config,
|
||||||
|
SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
|
config_set_default_string(obs_config,
|
||||||
|
SECTION_NAME, PARAM_SECRET, Secret);
|
||||||
|
config_set_default_string(obs_config,
|
||||||
|
SECTION_NAME, PARAM_SALT, Salt);
|
||||||
|
}
|
||||||
|
|
||||||
mbedtls_entropy_init(&entropy);
|
mbedtls_entropy_init(&entropy);
|
||||||
mbedtls_ctr_drbg_init(&rng);
|
mbedtls_ctr_drbg_init(&rng);
|
||||||
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
|
mbedtls_ctr_drbg_seed(&rng, mbedtls_entropy_func, &entropy, nullptr, 0);
|
||||||
@ -43,52 +75,85 @@ Config::Config() {
|
|||||||
SessionChallenge = GenerateSalt();
|
SessionChallenge = GenerateSalt();
|
||||||
}
|
}
|
||||||
|
|
||||||
Config::~Config() {
|
Config::~Config()
|
||||||
|
{
|
||||||
mbedtls_ctr_drbg_free(&rng);
|
mbedtls_ctr_drbg_free(&rng);
|
||||||
mbedtls_entropy_free(&entropy);
|
mbedtls_entropy_free(&entropy);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Config::GenerateSalt() {
|
void Config::Load()
|
||||||
|
{
|
||||||
|
config_t* obs_config = obs_frontend_get_global_config();
|
||||||
|
|
||||||
|
ServerEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_ENABLE);
|
||||||
|
ServerPort = config_get_uint(obs_config, SECTION_NAME, PARAM_PORT);
|
||||||
|
|
||||||
|
DebugEnabled = config_get_bool(obs_config, SECTION_NAME, PARAM_DEBUG);
|
||||||
|
|
||||||
|
AuthRequired = config_get_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED);
|
||||||
|
Secret = config_get_string(obs_config, SECTION_NAME, PARAM_SECRET);
|
||||||
|
Salt = config_get_string(obs_config, SECTION_NAME, PARAM_SALT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Config::Save()
|
||||||
|
{
|
||||||
|
config_t* obs_config = obs_frontend_get_global_config();
|
||||||
|
|
||||||
|
config_set_bool(obs_config, SECTION_NAME, PARAM_ENABLE, ServerEnabled);
|
||||||
|
config_set_uint(obs_config, SECTION_NAME, PARAM_PORT, ServerPort);
|
||||||
|
|
||||||
|
config_set_bool(obs_config, SECTION_NAME, PARAM_DEBUG, DebugEnabled);
|
||||||
|
|
||||||
|
config_set_bool(obs_config, SECTION_NAME, PARAM_AUTHREQUIRED, AuthRequired);
|
||||||
|
config_set_string(obs_config, SECTION_NAME, PARAM_SECRET, Secret);
|
||||||
|
config_set_string(obs_config, SECTION_NAME, PARAM_SALT, Salt);
|
||||||
|
|
||||||
|
config_save(obs_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Config::GenerateSalt()
|
||||||
|
{
|
||||||
// Generate 32 random chars
|
// Generate 32 random chars
|
||||||
unsigned char *random_chars = (unsigned char *)bzalloc(32);
|
unsigned char* random_chars = (unsigned char*)bzalloc(32);
|
||||||
mbedtls_ctr_drbg_random(&rng, random_chars, 32);
|
mbedtls_ctr_drbg_random(&rng, random_chars, 32);
|
||||||
|
|
||||||
// Convert the 32 random chars to a base64 string
|
// Convert the 32 random chars to a base64 string
|
||||||
unsigned char *salt = (unsigned char*)bzalloc(64);
|
char* salt = (char*)bzalloc(64);
|
||||||
size_t salt_bytes;
|
size_t salt_bytes;
|
||||||
mbedtls_base64_encode(salt, 64, &salt_bytes, random_chars, 32);
|
mbedtls_base64_encode(
|
||||||
salt[salt_bytes] = 0; // Null-terminate the string
|
(unsigned char*)salt, 64, &salt_bytes,
|
||||||
|
random_chars, 32);
|
||||||
|
|
||||||
bfree(random_chars);
|
bfree(random_chars);
|
||||||
return (char *)salt;
|
return salt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Config::GenerateSecret(const char *password, const char *salt) {
|
const char* Config::GenerateSecret(const char *password, const char *salt)
|
||||||
size_t passwordLength = strlen(password);
|
{
|
||||||
size_t saltLength = strlen(salt);
|
|
||||||
|
|
||||||
// Concatenate the password and the salt
|
// Concatenate the password and the salt
|
||||||
unsigned char *passAndSalt = (unsigned char*)bzalloc(passwordLength + saltLength);
|
std::string passAndSalt = "";
|
||||||
memcpy(passAndSalt, password, passwordLength);
|
passAndSalt += password;
|
||||||
memcpy(passAndSalt + passwordLength, salt, saltLength);
|
passAndSalt += salt;
|
||||||
passAndSalt[passwordLength + saltLength] = 0; // Null-terminate the string
|
|
||||||
|
|
||||||
// Generate a SHA256 hash of the password
|
// Generate a SHA256 hash of the password
|
||||||
unsigned char *challengeHash = (unsigned char *)bzalloc(32);
|
unsigned char* challengeHash = (unsigned char*)bzalloc(32);
|
||||||
mbedtls_sha256(passAndSalt, passwordLength + saltLength, challengeHash, 0);
|
mbedtls_sha256(
|
||||||
|
(unsigned char*)passAndSalt.c_str(), passAndSalt.length(),
|
||||||
|
challengeHash, 0);
|
||||||
|
|
||||||
// Encode SHA256 hash to Base64
|
// Encode SHA256 hash to Base64
|
||||||
unsigned char *challenge = (unsigned char*)bzalloc(64);
|
char* challenge = (char*)bzalloc(64);
|
||||||
size_t challenge_bytes = 0;
|
size_t challenge_bytes = 0;
|
||||||
mbedtls_base64_encode(challenge, 64, &challenge_bytes, challengeHash, 32);
|
mbedtls_base64_encode(
|
||||||
challenge[64] = 0; // Null-terminate the string
|
(unsigned char*)challenge, 64, &challenge_bytes,
|
||||||
|
challengeHash, 32);
|
||||||
|
|
||||||
bfree(passAndSalt);
|
|
||||||
bfree(challengeHash);
|
bfree(challengeHash);
|
||||||
return (char*)challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::SetPassword(const char *password) {
|
void Config::SetPassword(const char *password)
|
||||||
|
{
|
||||||
const char *new_salt = GenerateSalt();
|
const char *new_salt = GenerateSalt();
|
||||||
const char *new_challenge = GenerateSecret(password, new_salt);
|
const char *new_challenge = GenerateSecret(password, new_salt);
|
||||||
|
|
||||||
@ -96,58 +161,39 @@ void Config::SetPassword(const char *password) {
|
|||||||
this->Secret = new_challenge;
|
this->Secret = new_challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Config::CheckAuth(const char *response) {
|
bool Config::CheckAuth(const char *response)
|
||||||
size_t secretLength = strlen(this->Secret);
|
{
|
||||||
size_t sessChallengeLength = strlen(this->SessionChallenge);
|
|
||||||
|
|
||||||
// Concatenate auth secret with the challenge sent to the user
|
// Concatenate auth secret with the challenge sent to the user
|
||||||
char *challengeAndResponse = (char*)bzalloc(secretLength + sessChallengeLength);
|
std::string challengeAndResponse = "";
|
||||||
memcpy(challengeAndResponse, this->Secret, secretLength);
|
challengeAndResponse += this->Secret;
|
||||||
memcpy(challengeAndResponse + secretLength, this->SessionChallenge, sessChallengeLength);
|
challengeAndResponse += this->SessionChallenge;
|
||||||
challengeAndResponse[secretLength + sessChallengeLength] = 0; // Null-terminate the string
|
|
||||||
|
|
||||||
// Generate a SHA256 hash of challengeAndResponse
|
// Generate a SHA256 hash of challengeAndResponse
|
||||||
unsigned char *hash = (unsigned char*)bzalloc(32);
|
unsigned char* hash = (unsigned char*)bzalloc(32);
|
||||||
mbedtls_sha256((unsigned char*)challengeAndResponse, secretLength + sessChallengeLength, hash, 0);
|
mbedtls_sha256(
|
||||||
|
(unsigned char*)challengeAndResponse.c_str(),
|
||||||
|
challengeAndResponse.length(),
|
||||||
|
hash, 0);
|
||||||
|
|
||||||
// Encode the SHA256 hash to Base64
|
// Encode the SHA256 hash to Base64
|
||||||
unsigned char *expected_response = (unsigned char*)bzalloc(64);
|
char* expected_response = (char*)bzalloc(64);
|
||||||
size_t base64_size = 0;
|
size_t base64_size = 0;
|
||||||
mbedtls_base64_encode(expected_response, 64, &base64_size, hash, 32);
|
mbedtls_base64_encode(
|
||||||
expected_response[64] = 0; // Null-terminate the string
|
(unsigned char*)expected_response, 64, &base64_size,
|
||||||
|
hash, 32);
|
||||||
|
|
||||||
if (strcmp((char*)expected_response, response) == 0) {
|
bool authSuccess = false;
|
||||||
|
if (strcmp(expected_response, response) == 0) {
|
||||||
SessionChallenge = GenerateSalt();
|
SessionChallenge = GenerateSalt();
|
||||||
return true;
|
authSuccess = true;
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bfree(hash);
|
||||||
|
bfree(expected_response);
|
||||||
|
return authSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_data) {
|
Config* Config::Current()
|
||||||
Config *conf = static_cast<Config *>(private_data);
|
{
|
||||||
|
|
||||||
if (saving) {
|
|
||||||
obs_data_t *settings = obs_data_create();
|
|
||||||
obs_data_set_bool(settings, CONFIG_PARAM_AUTHREQUIRED, conf->AuthRequired);
|
|
||||||
obs_data_set_string(settings, CONFIG_PARAM_SECRET, conf->Secret);
|
|
||||||
obs_data_set_string(settings, CONFIG_PARAM_SALT, conf->Salt);
|
|
||||||
|
|
||||||
obs_data_set_obj(save_data, CONFIG_SECTION_NAME, settings);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
obs_data_t *settings = obs_data_get_obj(save_data, CONFIG_SECTION_NAME);
|
|
||||||
if (settings) {
|
|
||||||
conf->AuthRequired = obs_data_get_bool(settings, CONFIG_PARAM_AUTHREQUIRED);
|
|
||||||
conf->Secret = obs_data_get_string(settings, CONFIG_PARAM_SECRET);
|
|
||||||
conf->Salt = obs_data_get_string(settings, CONFIG_PARAM_SALT);
|
|
||||||
|
|
||||||
conf->SettingsLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Config* Config::Current() {
|
|
||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
18
Config.h
18
Config.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,19 +19,27 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#ifndef CONFIG_H
|
#ifndef CONFIG_H
|
||||||
#define CONFIG_H
|
#define CONFIG_H
|
||||||
|
|
||||||
#include <obs-module.h>
|
|
||||||
#include <mbedtls/entropy.h>
|
#include <mbedtls/entropy.h>
|
||||||
#include <mbedtls/ctr_drbg.h>
|
#include <mbedtls/ctr_drbg.h>
|
||||||
|
|
||||||
class Config {
|
class Config
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
|
void Load();
|
||||||
|
void Save();
|
||||||
|
|
||||||
void SetPassword(const char *password);
|
void SetPassword(const char *password);
|
||||||
bool CheckAuth(const char *userChallenge);
|
bool CheckAuth(const char *userChallenge);
|
||||||
const char* GenerateSalt();
|
const char* GenerateSalt();
|
||||||
static const char* GenerateSecret(const char *password, const char *salt);
|
static const char* GenerateSecret(
|
||||||
static void OBSSaveCallback(obs_data_t *save_data, bool saving, void *);
|
const char *password, const char *salt);
|
||||||
|
|
||||||
|
bool ServerEnabled;
|
||||||
|
uint64_t ServerPort;
|
||||||
|
|
||||||
|
bool DebugEnabled;
|
||||||
|
|
||||||
bool AuthRequired;
|
bool AuthRequired;
|
||||||
const char *Secret;
|
const char *Secret;
|
||||||
|
983
PROTOCOL.md
983
PROTOCOL.md
File diff suppressed because it is too large
Load Diff
72
README.md
72
README.md
@ -1,19 +1,65 @@
|
|||||||
obs-websocket
|
obs-websocket
|
||||||
==============
|
==============
|
||||||
Websocket API for OBS Studio.
|
Remote control of OBS Studio made easy.
|
||||||
|
|
||||||
## Build prerequisites
|
Follow the project on Twitter for news & updates : [@obswebsocket](https://twitter.com/obswebsocket)
|
||||||
You need QT 5.7 (with QtWebSockets), CMake, and a working development environment for OBS Studio installed on your computer.
|
|
||||||
|
|
||||||
## How to build
|
[](https://gitter.im/obs-websocket/obs-websocket) [](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [](https://travis-ci.org/Palakis/obs-websocket)
|
||||||
In CMake, you'll need to fill these CMake variables :
|
|
||||||
- **QTDIR** (path) : location of the Qt environment suited for your compiler and architecture
|
|
||||||
- **LIBOBS_INCLUDE_DIR** (path) : location of the libobs subfolder in the source code of OBS Studio
|
|
||||||
- **LIBOBS_LIB** (filepath) : location of the obs.lib file
|
|
||||||
- **OBS_FRONTEND_LIB** (filepath) : location of the obs-frontend-api.lib file
|
|
||||||
|
|
||||||
After building the obs-websocket plugin's binary, copy its Qt dependencies (QtCore, QtNetwork and QtWebSockets library binaries) in the same folder.
|
## Downloads
|
||||||
|
Binaries for Windows and Linux are available in the [Releases](https://github.com/Palakis/obs-websocket/releases) section.
|
||||||
|
|
||||||
## How to use
|
## Using obs-websocket
|
||||||
There's currently no frontend or language API available for obs-websocket. However, the full protocol reference is documented in the [PROTOCOL.md](PROTOCOL.md) file.
|
A web client and frontend made by [t2t2](https://github.com/t2t2/obs-tablet-remote) (compatible with tablets and other touch interfaces) is available here : http://t2t2.github.io/obs-tablet-remote/
|
||||||
A simple websocket client can connect to the plugin's embedded server.
|
|
||||||
|
It is **highly recommended** to protect obs-websocket with a password against unauthorized control. To do this, open the "Websocket server settings" dialog under OBS' "Tools" menu. In the settings dialogs, you can enable or disable authentication and set a password for it.
|
||||||
|
|
||||||
|
### Possible use cases
|
||||||
|
- Remote control OBS from a phone or tablet on the same local network
|
||||||
|
- Change your stream overlay/graphics based on the current scene (like the AGDQ overlay does)
|
||||||
|
- Automate scene switching with a third-party program (e.g. : auto-pilot, foot pedal, ...)
|
||||||
|
|
||||||
|
### For developers
|
||||||
|
The server is a typical Websockets server running by default on port 4444 (the port number can be changed in the Settings dialog).
|
||||||
|
The protocol understood by the server is documented in [PROTOCOL.md](PROTOCOL.md).
|
||||||
|
|
||||||
|
Here's a list of available language APIs for obs-websocket :
|
||||||
|
- Javascript (browser & nodejs) : [obs-websocket-js](https://github.com/haganbmj/obs-websocket-js) by Brendan Hagan
|
||||||
|
- C#/VB.NET : [obs-websocket-dotnet](https://github.com/Palakis/obs-websocket-dotnet)
|
||||||
|
- Python : [obs-websocket-py](https://github.com/Elektordi/obs-websocket-py) by Guillaume Genty a.k.a Elektordi
|
||||||
|
|
||||||
|
I'd like to know what you're building with or for obs-websocket. If you do something in this fashion, feel free to drop me an email at `contact at slepin dot fr` !
|
||||||
|
|
||||||
|
## Compiling obs-websocket
|
||||||
|
See the [build instructions](BUILDING.md).
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
**We need your help on translations**. Please join the localization project on Crowdin: https://crowdin.com/project/obs-websocket
|
||||||
|
|
||||||
|
## Special thanks
|
||||||
|
In order of appearance:
|
||||||
|
- [Brendan H.](https://github.com/haganbmj) : Code contributions and better English in the Protocol specification
|
||||||
|
- [Mikhail Swift](https://github.com/mikhailswift) : Code contributions
|
||||||
|
- [Tobias Frahmer](https://github.com/Frahmer) : German translation
|
||||||
|
- [Genture](https://github.com/Genteure) : Simplified Chinese and Traditional Chinese translations
|
||||||
|
- [Larissa Gabilan](https://github.com/laris151) : Portuguese translation
|
||||||
|
- [Andy Asquelt](https://github.com/asquelt) : Polish translation
|
||||||
|
- [Marcel Haazen](https://github.com/inpothet) : Dutch translation
|
||||||
|
- [Peter Antonvich](https://github.com/pantonvich) : Code contributions
|
||||||
|
|
||||||
|
And also: special thanks to supporters of the project!
|
||||||
|
|
||||||
|
## Supporters
|
||||||
|
They have contributed financially to the project and made possible the addition of several features into obs-websocket. Many thanks to them!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[Support Class](http://supportclass.net) designs and develops professional livestreams, with services ranging from broadcast graphics design and integration to event organization, along many other skills.
|
||||||
|
|
||||||
|
[](http://supportclass.net)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[MediaUnit](http://www.mediaunit.no) is a Norwegian media company developing products and services for the media industry, primarly focused on web and events.
|
||||||
|
|
||||||
|
[](http://www.mediaunit.no/)
|
||||||
|
491
Utils.cpp
491
Utils.cpp
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,51 +16,97 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <obs-frontend-api.h>
|
||||||
|
#include <obs.hpp>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QUrl>
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include "obs-websocket.h"
|
||||||
|
|
||||||
obs_data_array_t* Utils::GetSceneItems(obs_source_t *source) {
|
Q_DECLARE_METATYPE(OBSScene);
|
||||||
obs_data_array_t *items = obs_data_array_create();
|
|
||||||
obs_scene_t *scene = obs_scene_from_source(source);
|
obs_data_array_t* string_list_to_array(char** strings, char* key)
|
||||||
if (scene == NULL) {
|
{
|
||||||
return NULL;
|
if (!strings)
|
||||||
|
return obs_data_array_create();
|
||||||
|
|
||||||
|
obs_data_array_t* list = obs_data_array_create();
|
||||||
|
|
||||||
|
char* value = "";
|
||||||
|
for (int i = 0; value != nullptr; i++)
|
||||||
|
{
|
||||||
|
value = strings[i];
|
||||||
|
|
||||||
|
obs_data_t* item = obs_data_create();
|
||||||
|
obs_data_set_string(item, key, value);
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
obs_data_array_push_back(list, item);
|
||||||
|
|
||||||
|
obs_data_release(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t *scene, obs_sceneitem_t *currentItem, void *param) {
|
return list;
|
||||||
obs_data_array_t *data = static_cast<obs_data_array_t *>(param);
|
}
|
||||||
obs_data_array_push_back(data, GetSceneItemData(currentItem));
|
|
||||||
|
obs_data_array_t* Utils::GetSceneItems(obs_source_t* source)
|
||||||
|
{
|
||||||
|
obs_data_array_t* items = obs_data_array_create();
|
||||||
|
obs_scene_t* scene = obs_scene_from_source(source);
|
||||||
|
|
||||||
|
if (!scene)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
obs_scene_enum_items(scene, [](obs_scene_t* scene, obs_sceneitem_t* currentItem, void* param)
|
||||||
|
{
|
||||||
|
obs_data_array_t* data = static_cast<obs_data_array_t* >(param);
|
||||||
|
|
||||||
|
obs_data_t* item_data = GetSceneItemData(currentItem);
|
||||||
|
obs_data_array_insert(data, 0, item_data);
|
||||||
|
|
||||||
|
obs_data_release(item_data);
|
||||||
return true;
|
return true;
|
||||||
}, items);
|
}, items);
|
||||||
|
|
||||||
obs_scene_release(scene);
|
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t *item) {
|
obs_data_t* Utils::GetSceneItemData(obs_sceneitem_t* item)
|
||||||
if (!item) {
|
{
|
||||||
return NULL;
|
if (!item)
|
||||||
}
|
return nullptr;
|
||||||
|
|
||||||
vec2 pos;
|
vec2 pos;
|
||||||
obs_sceneitem_get_pos(item, &pos);
|
obs_sceneitem_get_pos(item, &pos);
|
||||||
|
|
||||||
vec2 bounds;
|
vec2 scale;
|
||||||
obs_sceneitem_get_bounds(item, &bounds);
|
obs_sceneitem_get_scale(item, &scale);
|
||||||
|
|
||||||
obs_data_t *data = obs_data_create();
|
obs_source_t* item_source = obs_sceneitem_get_source(item);
|
||||||
obs_data_set_string(data, "name", obs_source_get_name(obs_sceneitem_get_source(item)));
|
float item_width = float(obs_source_get_width(item_source));
|
||||||
obs_data_set_string(data, "type", obs_source_get_id(obs_sceneitem_get_source(item)));
|
float item_height = float(obs_source_get_height(item_source));
|
||||||
obs_data_set_double(data, "volume", obs_source_get_volume(obs_sceneitem_get_source(item)));
|
|
||||||
|
obs_data_t* data = obs_data_create();
|
||||||
|
obs_data_set_string(data, "name",
|
||||||
|
obs_source_get_name(obs_sceneitem_get_source(item)));
|
||||||
|
obs_data_set_string(data, "type",
|
||||||
|
obs_source_get_id(obs_sceneitem_get_source(item)));
|
||||||
|
obs_data_set_double(data, "volume",
|
||||||
|
obs_source_get_volume(obs_sceneitem_get_source(item)));
|
||||||
obs_data_set_double(data, "x", pos.x);
|
obs_data_set_double(data, "x", pos.x);
|
||||||
obs_data_set_double(data, "y", pos.y);
|
obs_data_set_double(data, "y", pos.y);
|
||||||
obs_data_set_double(data, "cx", bounds.x);
|
obs_data_set_int(data, "source_cx", (int)item_width);
|
||||||
obs_data_set_double(data, "cy", bounds.y);
|
obs_data_set_int(data, "source_cy", (int)item_height);
|
||||||
|
obs_data_set_double(data, "cx", item_width* scale.x);
|
||||||
|
obs_data_set_double(data, "cy", item_height* scale.y);
|
||||||
obs_data_set_bool(data, "render", obs_sceneitem_visible(item));
|
obs_data_set_bool(data, "render", obs_sceneitem_visible(item));
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* name)
|
||||||
obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* name) {
|
{
|
||||||
struct current_search {
|
struct current_search {
|
||||||
const char* query;
|
const char* query;
|
||||||
obs_sceneitem_t* result;
|
obs_sceneitem_t* result;
|
||||||
@ -68,18 +114,21 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* n
|
|||||||
|
|
||||||
current_search search;
|
current_search search;
|
||||||
search.query = name;
|
search.query = name;
|
||||||
search.result = NULL;
|
search.result = nullptr;
|
||||||
|
|
||||||
obs_scene_t *scene = obs_scene_from_source(source);
|
obs_scene_t* scene = obs_scene_from_source(source);
|
||||||
if (scene == NULL) {
|
if (scene == nullptr)
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
obs_scene_enum_items(scene, [](obs_scene_t *scene, obs_sceneitem_t *currentItem, void *param) {
|
obs_scene_enum_items(scene, [](obs_scene_t* scene, obs_sceneitem_t* currentItem, void* param)
|
||||||
current_search *search = static_cast<current_search *>(param);
|
{
|
||||||
|
current_search* search = static_cast<current_search* >(param);
|
||||||
const char* currentItemName = obs_source_get_name(obs_sceneitem_get_source(currentItem));
|
|
||||||
if (strcmp(currentItemName, search->query) == 0) {
|
const char* currentItemName =
|
||||||
|
obs_source_get_name(obs_sceneitem_get_source(currentItem));
|
||||||
|
|
||||||
|
if (strcmp(currentItemName, search->query) == 0)
|
||||||
|
{
|
||||||
search->result = currentItem;
|
search->result = currentItem;
|
||||||
obs_sceneitem_addref(search->result);
|
obs_sceneitem_addref(search->result);
|
||||||
return false;
|
return false;
|
||||||
@ -91,14 +140,57 @@ obs_sceneitem_t* Utils::GetSceneItemFromName(obs_source_t* source, const char* n
|
|||||||
return search.result;
|
return search.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_array_t* Utils::GetScenes() {
|
obs_source_t* Utils::GetTransitionFromName(const char* search_name)
|
||||||
|
{
|
||||||
|
obs_source_t* found_transition = NULL;
|
||||||
|
|
||||||
|
obs_frontend_source_list transition_list = {};
|
||||||
|
obs_frontend_get_transitions(&transition_list);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < transition_list.sources.num; i++)
|
||||||
|
{
|
||||||
|
obs_source_t* transition = transition_list.sources.array[i];
|
||||||
|
|
||||||
|
const char* transition_name = obs_source_get_name(transition);
|
||||||
|
if (strcmp(transition_name, search_name) == 0)
|
||||||
|
{
|
||||||
|
found_transition = transition;
|
||||||
|
obs_source_addref(found_transition);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_frontend_source_list_free(&transition_list);
|
||||||
|
|
||||||
|
return found_transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_source_t* Utils::GetSceneFromNameOrCurrent(const char* scene_name)
|
||||||
|
{
|
||||||
|
obs_source_t* scene = nullptr;
|
||||||
|
|
||||||
|
if (!scene_name || !strlen(scene_name))
|
||||||
|
scene = obs_frontend_get_current_scene();
|
||||||
|
else
|
||||||
|
scene = obs_get_source_by_name(scene_name);
|
||||||
|
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_array_t* Utils::GetScenes()
|
||||||
|
{
|
||||||
obs_frontend_source_list sceneList = {};
|
obs_frontend_source_list sceneList = {};
|
||||||
obs_frontend_get_scenes(&sceneList);
|
obs_frontend_get_scenes(&sceneList);
|
||||||
|
|
||||||
obs_data_array_t* scenes = obs_data_array_create();
|
obs_data_array_t* scenes = obs_data_array_create();
|
||||||
for (size_t i = 0; i < (&sceneList)->sources.num; i++) {
|
for (size_t i = 0; i < sceneList.sources.num; i++)
|
||||||
obs_source_t* scene = (&sceneList)->sources.array[i];
|
{
|
||||||
obs_data_array_push_back(scenes, GetSceneData(scene));
|
obs_source_t* scene = sceneList.sources.array[i];
|
||||||
|
|
||||||
|
obs_data_t* scene_data = GetSceneData(scene);
|
||||||
|
obs_data_array_push_back(scenes, scene_data);
|
||||||
|
|
||||||
|
obs_data_release(scene_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_source_list_free(&sceneList);
|
obs_frontend_source_list_free(&sceneList);
|
||||||
@ -106,10 +198,325 @@ obs_data_array_t* Utils::GetScenes() {
|
|||||||
return scenes;
|
return scenes;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_data_t* Utils::GetSceneData(obs_source *source) {
|
obs_data_t* Utils::GetSceneData(obs_source* source)
|
||||||
|
{
|
||||||
|
obs_data_array_t* scene_items = GetSceneItems(source);
|
||||||
|
|
||||||
obs_data_t* sceneData = obs_data_create();
|
obs_data_t* sceneData = obs_data_create();
|
||||||
obs_data_set_string(sceneData, "name", obs_source_get_name(source));
|
obs_data_set_string(sceneData, "name", obs_source_get_name(source));
|
||||||
obs_data_set_array(sceneData, "sources", GetSceneItems(source));
|
obs_data_set_array(sceneData, "sources", scene_items);
|
||||||
|
|
||||||
|
obs_data_array_release(scene_items);
|
||||||
return sceneData;
|
return sceneData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
obs_data_array_t* Utils::GetSceneCollections()
|
||||||
|
{
|
||||||
|
char** scene_collections = obs_frontend_get_scene_collections();
|
||||||
|
obs_data_array_t* list = string_list_to_array(scene_collections, "sc-name");
|
||||||
|
|
||||||
|
bfree(scene_collections);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_data_array_t* Utils::GetProfiles()
|
||||||
|
{
|
||||||
|
char** profiles = obs_frontend_get_profiles();
|
||||||
|
obs_data_array_t* list = string_list_to_array(profiles, "profile-name");
|
||||||
|
|
||||||
|
bfree(profiles);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSpinBox* Utils::GetTransitionDurationControl()
|
||||||
|
{
|
||||||
|
QMainWindow* window = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
return window->findChild<QSpinBox*>("transitionDuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
int Utils::GetTransitionDuration()
|
||||||
|
{
|
||||||
|
QSpinBox* control = GetTransitionDurationControl();
|
||||||
|
if (control)
|
||||||
|
return control->value();
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::SetTransitionDuration(int ms)
|
||||||
|
{
|
||||||
|
QSpinBox* control = GetTransitionDurationControl();
|
||||||
|
|
||||||
|
if (control && ms >= 0)
|
||||||
|
control->setValue(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utils::SetTransitionByName(const char* transition_name)
|
||||||
|
{
|
||||||
|
obs_source_t* transition = GetTransitionFromName(transition_name);
|
||||||
|
|
||||||
|
if (transition)
|
||||||
|
{
|
||||||
|
obs_frontend_set_current_transition(transition);
|
||||||
|
obs_source_release(transition);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton* Utils::GetPreviewModeButtonControl()
|
||||||
|
{
|
||||||
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
return main->findChild<QPushButton*>("modeSwitch");
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidget* Utils::GetSceneListControl()
|
||||||
|
{
|
||||||
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
return main->findChild<QListWidget*>("scenes");
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_scene_t* Utils::SceneListItemToScene(QListWidgetItem* item)
|
||||||
|
{
|
||||||
|
if (!item)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
QVariant item_data = item->data(static_cast<int>(Qt::UserRole));
|
||||||
|
return item_data.value<OBSScene>();
|
||||||
|
}
|
||||||
|
|
||||||
|
QLayout* Utils::GetPreviewLayout()
|
||||||
|
{
|
||||||
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
return main->findChild<QLayout*>("previewLayout");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utils::IsPreviewModeActive()
|
||||||
|
{
|
||||||
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
|
||||||
|
// Clue 1 : "Studio Mode" button is toggled on
|
||||||
|
bool buttonToggledOn = GetPreviewModeButtonControl()->isChecked();
|
||||||
|
|
||||||
|
// Clue 2 : Preview layout has more than one item
|
||||||
|
int previewChildCount = GetPreviewLayout()->count();
|
||||||
|
blog(LOG_INFO, "preview layout children count : %d", previewChildCount);
|
||||||
|
|
||||||
|
return buttonToggledOn || (previewChildCount >= 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::EnablePreviewMode()
|
||||||
|
{
|
||||||
|
if (!IsPreviewModeActive())
|
||||||
|
GetPreviewModeButtonControl()->click();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::DisablePreviewMode()
|
||||||
|
{
|
||||||
|
if (IsPreviewModeActive())
|
||||||
|
GetPreviewModeButtonControl()->click();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::TogglePreviewMode()
|
||||||
|
{
|
||||||
|
GetPreviewModeButtonControl()->click();
|
||||||
|
}
|
||||||
|
|
||||||
|
obs_scene_t* Utils::GetPreviewScene()
|
||||||
|
{
|
||||||
|
if (IsPreviewModeActive())
|
||||||
|
{
|
||||||
|
QListWidget* sceneList = GetSceneListControl();
|
||||||
|
|
||||||
|
QList<QListWidgetItem*> selected = sceneList->selectedItems();
|
||||||
|
|
||||||
|
// Qt::UserRole == QtUserRole::OBSRef
|
||||||
|
obs_scene_t* scene = Utils::SceneListItemToScene(selected.first());
|
||||||
|
|
||||||
|
obs_scene_addref(scene);
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utils::SetPreviewScene(const char* name)
|
||||||
|
{
|
||||||
|
if (IsPreviewModeActive())
|
||||||
|
{
|
||||||
|
QListWidget* sceneList = GetSceneListControl();
|
||||||
|
QList<QListWidgetItem*> matchingItems =
|
||||||
|
sceneList->findItems(name, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (matchingItems.count() > 0)
|
||||||
|
{
|
||||||
|
sceneList->setCurrentItem(matchingItems.first());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::TransitionToProgram()
|
||||||
|
{
|
||||||
|
if (!IsPreviewModeActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// WARNING : if the layout created in OBS' CreateProgramOptions() changes
|
||||||
|
// then this won't work as expected
|
||||||
|
|
||||||
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
|
||||||
|
// The program options widget is the second item in the left-to-right layout
|
||||||
|
QWidget* programOptions = GetPreviewLayout()->itemAt(1)->widget();
|
||||||
|
|
||||||
|
// The "Transition" button lies in the mainButtonLayout
|
||||||
|
// which is the first itemin the program options' layout
|
||||||
|
QLayout* mainButtonLayout = programOptions->layout()->itemAt(1)->layout();
|
||||||
|
QWidget* transitionBtnWidget = mainButtonLayout->itemAt(0)->widget();
|
||||||
|
|
||||||
|
// Try to cast that widget into a button
|
||||||
|
QPushButton* transitionBtn = qobject_cast<QPushButton*>(transitionBtnWidget);
|
||||||
|
|
||||||
|
// Perform a click on that button
|
||||||
|
transitionBtn->click();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Utils::OBSVersionString() {
|
||||||
|
uint32_t version = obs_get_version();
|
||||||
|
|
||||||
|
uint8_t major, minor, patch;
|
||||||
|
major = (version >> 24) & 0xFF;
|
||||||
|
minor = (version >> 16) & 0xFF;
|
||||||
|
patch = version & 0xFF;
|
||||||
|
|
||||||
|
char* result = (char*)bmalloc(sizeof(char) * 12);
|
||||||
|
sprintf(result, "%d.%d.%d", major, minor, patch);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSystemTrayIcon* Utils::GetTrayIcon()
|
||||||
|
{
|
||||||
|
QMainWindow* main = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
return main->findChildren<QSystemTrayIcon*>().first();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Utils::SysTrayNotify(QString &text, QSystemTrayIcon::MessageIcon icon, QString title)
|
||||||
|
{
|
||||||
|
if (!QSystemTrayIcon::supportsMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QSystemTrayIcon* trayIcon = GetTrayIcon();
|
||||||
|
if (trayIcon)
|
||||||
|
trayIcon->showMessage(title, text, icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Utils::FormatIPAddress(QHostAddress &addr)
|
||||||
|
{
|
||||||
|
if (addr.protocol() == QAbstractSocket::IPv4Protocol)
|
||||||
|
QString v4addr = addr.toString().replace("::fff:", "");
|
||||||
|
|
||||||
|
return addr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Utils::GetRecordingFolder()
|
||||||
|
{
|
||||||
|
config_t* profile = obs_frontend_get_profile_config();
|
||||||
|
const char* outputMode = config_get_string(profile, "Output", "Mode");
|
||||||
|
|
||||||
|
if (strcmp(outputMode, "Advanced") == 0)
|
||||||
|
{
|
||||||
|
// Advanced mode
|
||||||
|
return config_get_string(profile, "AdvOut", "RecFilePath");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Simple mode
|
||||||
|
return config_get_string(profile, "SimpleOutput", "FilePath");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Utils::SetRecordingFolder(const char* path)
|
||||||
|
{
|
||||||
|
if (!QDir(path).exists())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
config_t* profile = obs_frontend_get_profile_config();
|
||||||
|
const char* outputMode = config_get_string(profile, "Output", "Mode");
|
||||||
|
|
||||||
|
if (strcmp(outputMode, "Advanced") == 0)
|
||||||
|
{
|
||||||
|
config_set_string(profile, "AdvOut", "RecFilePath", path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config_set_string(profile, "SimpleOutput", "FilePath", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
config_save(profile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString* Utils::ParseDataToQueryString(obs_data_t * data)
|
||||||
|
{
|
||||||
|
QString* query = nullptr;
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
obs_data_item_t* item = obs_data_first(data);
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
query = new QString();
|
||||||
|
bool isFirst = true;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!obs_data_item_has_user_value(item))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!isFirst)
|
||||||
|
query->append('&');
|
||||||
|
else
|
||||||
|
isFirst = false;
|
||||||
|
|
||||||
|
const char* attrName = obs_data_item_get_name(item);
|
||||||
|
query->append(attrName).append("=");
|
||||||
|
switch (obs_data_item_gettype(item))
|
||||||
|
{
|
||||||
|
case OBS_DATA_BOOLEAN:
|
||||||
|
query->append(obs_data_item_get_bool(item)?"true":"false");
|
||||||
|
break;
|
||||||
|
case OBS_DATA_NUMBER:
|
||||||
|
switch (obs_data_item_numtype(item))
|
||||||
|
{
|
||||||
|
case OBS_DATA_NUM_DOUBLE:
|
||||||
|
query->append(QString::number(obs_data_item_get_double(item)));
|
||||||
|
break;
|
||||||
|
case OBS_DATA_NUM_INT:
|
||||||
|
query->append(QString::number(obs_data_item_get_int(item)));
|
||||||
|
break;
|
||||||
|
case OBS_DATA_NUM_INVALID:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OBS_DATA_STRING:
|
||||||
|
query->append(QUrl::toPercentEncoding(QString(obs_data_item_get_string(item))));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//other types are not supported
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ( obs_data_item_next( &item ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
61
Utils.h
61
Utils.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,18 +19,65 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <QSpinBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QLayout>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <stdio.h>
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <obs-frontend-api.h>
|
#include <util/config-file.h>
|
||||||
|
|
||||||
class Utils
|
class Utils
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static obs_data_array_t* GetSceneItems(obs_source_t *source);
|
static obs_data_array_t* GetSceneItems(obs_source_t* source);
|
||||||
static obs_data_t* GetSceneItemData(obs_scene_item *item);
|
static obs_data_t* GetSceneItemData(obs_scene_item* item);
|
||||||
static obs_sceneitem_t* GetSceneItemFromName(obs_source_t *source, const char* name);
|
static obs_sceneitem_t* GetSceneItemFromName(
|
||||||
|
obs_source_t* source, const char* name);
|
||||||
|
static obs_source_t* GetTransitionFromName(const char* search_name);
|
||||||
|
static obs_source_t* GetSceneFromNameOrCurrent(const char* scene_name);
|
||||||
|
|
||||||
static obs_data_array_t* GetScenes();
|
static obs_data_array_t* GetScenes();
|
||||||
static obs_data_t* GetSceneData(obs_source *source);
|
static obs_data_t* GetSceneData(obs_source* source);
|
||||||
|
|
||||||
|
static obs_data_array_t* GetSceneCollections();
|
||||||
|
static obs_data_array_t* GetProfiles();
|
||||||
|
|
||||||
|
static QSpinBox* GetTransitionDurationControl();
|
||||||
|
static int GetTransitionDuration();
|
||||||
|
static void SetTransitionDuration(int ms);
|
||||||
|
|
||||||
|
static bool SetTransitionByName(const char* transition_name);
|
||||||
|
|
||||||
|
static QPushButton* GetPreviewModeButtonControl();
|
||||||
|
static QLayout* GetPreviewLayout();
|
||||||
|
static QListWidget* GetSceneListControl();
|
||||||
|
static obs_scene_t* SceneListItemToScene(QListWidgetItem* item);
|
||||||
|
|
||||||
|
static bool IsPreviewModeActive();
|
||||||
|
static void EnablePreviewMode();
|
||||||
|
static void DisablePreviewMode();
|
||||||
|
static void TogglePreviewMode();
|
||||||
|
|
||||||
|
static obs_scene_t* GetPreviewScene();
|
||||||
|
static bool SetPreviewScene(const char* name);
|
||||||
|
static void TransitionToProgram();
|
||||||
|
|
||||||
|
static const char* OBSVersionString();
|
||||||
|
|
||||||
|
static QSystemTrayIcon* GetTrayIcon();
|
||||||
|
static void SysTrayNotify(
|
||||||
|
QString &text,
|
||||||
|
QSystemTrayIcon::MessageIcon n,
|
||||||
|
QString title = QString("obs-websocket"));
|
||||||
|
|
||||||
|
static QString FormatIPAddress(QHostAddress &addr);
|
||||||
|
static const char* GetRecordingFolder();
|
||||||
|
static bool SetRecordingFolder(const char* path);
|
||||||
|
|
||||||
|
static QString* ParseDataToQueryString(obs_data_t * data);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // UTILS_H
|
#endif // UTILS_H
|
||||||
|
602
WSEvents.cpp
602
WSEvents.cpp
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
Copyright (C) 2017 Brendan Hagan <https://github.com/haganbmj>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,101 +17,368 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <util/platform.h>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Utils.h"
|
||||||
#include "WSEvents.h"
|
#include "WSEvents.h"
|
||||||
|
#include "obs-websocket.h"
|
||||||
|
|
||||||
WSEvents::WSEvents(WSServer *server) {
|
bool transition_is_cut(obs_source_t* transition)
|
||||||
_srv = server;
|
{
|
||||||
obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this);
|
if (!transition)
|
||||||
|
return false;
|
||||||
|
|
||||||
QTimer *statusTimer = new QTimer();
|
if (obs_source_get_type(transition) == OBS_SOURCE_TYPE_TRANSITION
|
||||||
connect(statusTimer, SIGNAL(timeout()), this, SLOT(StreamStatus()));
|
&& strcmp(obs_source_get_id(transition), "cut_transition") == 0)
|
||||||
statusTimer->start(1000);
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
WSEvents::~WSEvents() {
|
const char* ns_to_timestamp(uint64_t ns)
|
||||||
|
{
|
||||||
|
uint64_t ms = ns / (1000 * 1000);
|
||||||
|
uint64_t secs = ms / 1000;
|
||||||
|
uint64_t minutes = secs / 60;
|
||||||
|
|
||||||
|
uint64_t hours_part = minutes / 60;
|
||||||
|
uint64_t minutes_part = minutes % 60;
|
||||||
|
uint64_t secs_part = secs % 60;
|
||||||
|
uint64_t ms_part = ms % 1000;
|
||||||
|
|
||||||
|
char* ts = (char*)bmalloc(64);
|
||||||
|
sprintf(ts, "%02d:%02d:%02d.%03d",
|
||||||
|
hours_part, minutes_part, secs_part, ms_part);
|
||||||
|
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
WSEvents* WSEvents::Instance = nullptr;
|
||||||
|
|
||||||
|
WSEvents::WSEvents(WSServer* srv)
|
||||||
|
{
|
||||||
|
_srv = srv;
|
||||||
|
obs_frontend_add_event_callback(WSEvents::FrontendEventHandler, this);
|
||||||
|
|
||||||
|
QSpinBox* duration_control = Utils::GetTransitionDurationControl();
|
||||||
|
connect(duration_control, SIGNAL(valueChanged(int)),
|
||||||
|
this, SLOT(TransitionDurationChanged(int)));
|
||||||
|
|
||||||
|
QTimer* statusTimer = new QTimer();
|
||||||
|
connect(statusTimer, SIGNAL(timeout()),
|
||||||
|
this, SLOT(StreamStatus()));
|
||||||
|
statusTimer->start(2000); // equal to frontend's constant BITRATE_UPDATE_SECONDS
|
||||||
|
|
||||||
|
QListWidget* sceneList = Utils::GetSceneListControl();
|
||||||
|
connect(sceneList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
|
||||||
|
this, SLOT(SelectedSceneChanged(QListWidgetItem*, QListWidgetItem*)));
|
||||||
|
|
||||||
|
QPushButton* modeSwitch = Utils::GetPreviewModeButtonControl();
|
||||||
|
connect(modeSwitch, SIGNAL(clicked(bool)), this, SLOT(ModeSwitchClicked(bool)));
|
||||||
|
|
||||||
|
transition_handler = nullptr;
|
||||||
|
scene_handler = nullptr;
|
||||||
|
|
||||||
|
QTimer::singleShot(1000, this, SLOT(deferredInitOperations()));
|
||||||
|
|
||||||
|
_streaming_active = false;
|
||||||
|
_recording_active = false;
|
||||||
|
|
||||||
|
_stream_starttime = 0;
|
||||||
|
_rec_starttime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
WSEvents::~WSEvents()
|
||||||
|
{
|
||||||
obs_frontend_remove_event_callback(WSEvents::FrontendEventHandler, this);
|
obs_frontend_remove_event_callback(WSEvents::FrontendEventHandler, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void *private_data)
|
void WSEvents::deferredInitOperations()
|
||||||
{
|
{
|
||||||
WSEvents *owner = static_cast<WSEvents *>(private_data);
|
obs_source_t* transition = obs_frontend_get_current_transition();
|
||||||
|
connectTransitionSignals(transition);
|
||||||
|
obs_source_release(transition);
|
||||||
|
|
||||||
// TODO : implement SourceChanged, SourceOrderChanged and RepopulateSources
|
obs_source_t* scene = obs_frontend_get_current_scene();
|
||||||
|
connectSceneSignals(scene);
|
||||||
|
obs_source_release(scene);
|
||||||
|
}
|
||||||
|
|
||||||
if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED) {
|
void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private_data)
|
||||||
|
{
|
||||||
|
WSEvents* owner = static_cast<WSEvents*>(private_data);
|
||||||
|
|
||||||
|
if (!owner->_srv)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO : implement SourceOrderChanged and RepopulateSources
|
||||||
|
|
||||||
|
if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED)
|
||||||
|
{
|
||||||
owner->OnSceneChange();
|
owner->OnSceneChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED) {
|
else if (event == OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED)
|
||||||
|
{
|
||||||
owner->OnSceneListChange();
|
owner->OnSceneListChange();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTING) {
|
else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED)
|
||||||
|
{
|
||||||
|
owner->OnSceneCollectionChange();
|
||||||
|
}
|
||||||
|
else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED)
|
||||||
|
{
|
||||||
|
owner->OnSceneCollectionListChange();
|
||||||
|
}
|
||||||
|
else if (event == OBS_FRONTEND_EVENT_TRANSITION_CHANGED)
|
||||||
|
{
|
||||||
|
owner->OnTransitionChange();
|
||||||
|
}
|
||||||
|
else if (event == OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED)
|
||||||
|
{
|
||||||
|
owner->OnTransitionListChange();
|
||||||
|
}
|
||||||
|
else if (event == OBS_FRONTEND_EVENT_PROFILE_CHANGED)
|
||||||
|
{
|
||||||
|
owner->OnProfileChange();
|
||||||
|
}
|
||||||
|
else if (event == OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED)
|
||||||
|
{
|
||||||
|
owner->OnProfileListChange();
|
||||||
|
}
|
||||||
|
else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTING)
|
||||||
|
{
|
||||||
owner->OnStreamStarting();
|
owner->OnStreamStarting();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTED) {
|
else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTED)
|
||||||
|
{
|
||||||
|
owner->_streaming_active = true;
|
||||||
owner->OnStreamStarted();
|
owner->OnStreamStarted();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPING) {
|
else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPING)
|
||||||
|
{
|
||||||
owner->OnStreamStopping();
|
owner->OnStreamStopping();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPED) {
|
else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPED)
|
||||||
|
{
|
||||||
|
owner->_streaming_active = false;
|
||||||
owner->OnStreamStopped();
|
owner->OnStreamStopped();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTING) {
|
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTING)
|
||||||
|
{
|
||||||
owner->OnRecordingStarting();
|
owner->OnRecordingStarting();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) {
|
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED)
|
||||||
|
{
|
||||||
|
owner->_recording_active = true;
|
||||||
owner->OnRecordingStarted();
|
owner->OnRecordingStarted();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTING) {
|
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPING)
|
||||||
owner->OnRecordingStarting();
|
{
|
||||||
|
owner->OnRecordingStopping();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPED) {
|
else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPED)
|
||||||
|
{
|
||||||
|
owner->_recording_active = false;
|
||||||
owner->OnRecordingStopped();
|
owner->OnRecordingStopped();
|
||||||
}
|
}
|
||||||
else if (event == OBS_FRONTEND_EVENT_EXIT) {
|
else if (event == OBS_FRONTEND_EVENT_EXIT)
|
||||||
obs_frontend_save();
|
{
|
||||||
owner->OnExit();
|
owner->OnExit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::broadcastUpdate(const char *updateType, obs_data_t *additionalFields = NULL) {
|
void WSEvents::broadcastUpdate(const char* updateType, obs_data_t* additionalFields = NULL)
|
||||||
obs_source_t *source = obs_frontend_get_current_scene();
|
{
|
||||||
const char *name = obs_source_get_name(source);
|
obs_data_t* update = obs_data_create();
|
||||||
|
|
||||||
obs_data_t *update = obs_data_create();
|
|
||||||
|
|
||||||
obs_data_set_string(update, "update-type", updateType);
|
obs_data_set_string(update, "update-type", updateType);
|
||||||
if (additionalFields != NULL) {
|
|
||||||
obs_data_apply(update, additionalFields);
|
const char* ts = nullptr;
|
||||||
|
if (_streaming_active)
|
||||||
|
{
|
||||||
|
ts = ns_to_timestamp(os_gettime_ns() - _stream_starttime);
|
||||||
|
obs_data_set_string(update, "stream-timecode", ts);
|
||||||
|
bfree((void*)ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
_srv->broadcast(obs_data_get_json(update));
|
if (_recording_active)
|
||||||
|
{
|
||||||
|
ts = ns_to_timestamp(os_gettime_ns() - _rec_starttime);
|
||||||
|
obs_data_set_string(update, "rec-timecode", ts);
|
||||||
|
bfree((void*)ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (additionalFields != NULL)
|
||||||
|
obs_data_apply(update, additionalFields);
|
||||||
|
|
||||||
|
const char *json = obs_data_get_json(update);
|
||||||
|
_srv->broadcast(json);
|
||||||
|
if (Config::Current()->DebugEnabled)
|
||||||
|
blog(LOG_DEBUG, "Update << '%s'", json);
|
||||||
|
|
||||||
obs_data_release(update);
|
obs_data_release(update);
|
||||||
obs_source_release(source);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneChange() {
|
void WSEvents::connectTransitionSignals(obs_source_t* transition)
|
||||||
// Implements an existing update type from bilhamil's OBS Remote
|
{
|
||||||
obs_source_t *source = obs_frontend_get_current_scene();
|
if (transition_handler)
|
||||||
const char *name = obs_source_get_name(source);
|
{
|
||||||
|
signal_handler_disconnect(transition_handler,
|
||||||
|
"transition_start", OnTransitionBegin, this);
|
||||||
|
}
|
||||||
|
|
||||||
obs_data_t *data = obs_data_create();
|
if (!transition_is_cut(transition))
|
||||||
obs_data_set_string(data, "scene-name", name);
|
{
|
||||||
|
transition_handler = obs_source_get_signal_handler(transition);
|
||||||
|
signal_handler_connect(transition_handler,
|
||||||
|
"transition_start", OnTransitionBegin, this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transition_handler = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::connectSceneSignals(obs_source_t* scene)
|
||||||
|
{
|
||||||
|
if (scene_handler)
|
||||||
|
{
|
||||||
|
signal_handler_disconnect(scene_handler,
|
||||||
|
"reorder", OnSceneReordered, this);
|
||||||
|
signal_handler_disconnect(scene_handler,
|
||||||
|
"item_add", OnSceneItemAdd, this);
|
||||||
|
signal_handler_disconnect(scene_handler,
|
||||||
|
"item_remove", OnSceneItemDelete, this);
|
||||||
|
signal_handler_disconnect(scene_handler,
|
||||||
|
"item_visible", OnSceneItemVisibilityChanged, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : connect to all scenes, not just the current one.
|
||||||
|
scene_handler = obs_source_get_signal_handler(scene);
|
||||||
|
signal_handler_connect(scene_handler,
|
||||||
|
"reorder", OnSceneReordered, this);
|
||||||
|
signal_handler_connect(scene_handler,
|
||||||
|
"item_add", OnSceneItemAdd, this);
|
||||||
|
signal_handler_connect(scene_handler,
|
||||||
|
"item_remove", OnSceneItemDelete, this);
|
||||||
|
signal_handler_connect(scene_handler,
|
||||||
|
"item_visible", OnSceneItemVisibilityChanged, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t WSEvents::GetStreamingTime()
|
||||||
|
{
|
||||||
|
if (_streaming_active)
|
||||||
|
return (os_gettime_ns() - _stream_starttime);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* WSEvents::GetStreamingTimecode()
|
||||||
|
{
|
||||||
|
return ns_to_timestamp(GetStreamingTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t WSEvents::GetRecordingTime()
|
||||||
|
{
|
||||||
|
if (_recording_active)
|
||||||
|
return (os_gettime_ns() - _rec_starttime);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* WSEvents::GetRecordingTimecode()
|
||||||
|
{
|
||||||
|
return ns_to_timestamp(GetRecordingTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnSceneChange()
|
||||||
|
{
|
||||||
|
// Implements an existing update type from bilhamil's OBS Remote
|
||||||
|
obs_data_t* data = obs_data_create();
|
||||||
|
|
||||||
|
obs_source_t* current_scene = obs_frontend_get_current_scene();
|
||||||
|
obs_data_array_t* scene_items = Utils::GetSceneItems(current_scene);
|
||||||
|
connectSceneSignals(current_scene);
|
||||||
|
|
||||||
|
obs_data_set_string(data, "scene-name", obs_source_get_name(current_scene));
|
||||||
|
obs_data_set_array(data, "sources", scene_items);
|
||||||
|
|
||||||
broadcastUpdate("SwitchScenes", data);
|
broadcastUpdate("SwitchScenes", data);
|
||||||
|
|
||||||
|
obs_data_array_release(scene_items);
|
||||||
|
obs_source_release(current_scene);
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
obs_source_release(source);
|
|
||||||
|
// Dirty fix : OBS blocks signals when swapping scenes in Studio Mode
|
||||||
|
// after transition end, so SelectedSceneChanged is never called...
|
||||||
|
if (Utils::IsPreviewModeActive())
|
||||||
|
{
|
||||||
|
QListWidget* list = Utils::GetSceneListControl();
|
||||||
|
SelectedSceneChanged(list->currentItem(), nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnSceneListChange() {
|
void WSEvents::OnSceneListChange()
|
||||||
|
{
|
||||||
broadcastUpdate("ScenesChanged");
|
broadcastUpdate("ScenesChanged");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStarting() {
|
void WSEvents::OnSceneCollectionChange()
|
||||||
|
{
|
||||||
|
broadcastUpdate("SceneCollectionChanged");
|
||||||
|
|
||||||
|
scene_handler = nullptr;
|
||||||
|
transition_handler = nullptr;
|
||||||
|
|
||||||
|
OnTransitionListChange();
|
||||||
|
OnTransitionChange();
|
||||||
|
|
||||||
|
OnSceneListChange();
|
||||||
|
OnSceneChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnSceneCollectionListChange()
|
||||||
|
{
|
||||||
|
broadcastUpdate("SceneCollectionListChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnTransitionChange()
|
||||||
|
{
|
||||||
|
obs_source_t* current_transition = obs_frontend_get_current_transition();
|
||||||
|
connectTransitionSignals(current_transition);
|
||||||
|
|
||||||
|
obs_data_t* data = obs_data_create();
|
||||||
|
obs_data_set_string(data, "transition-name",
|
||||||
|
obs_source_get_name(current_transition));
|
||||||
|
|
||||||
|
broadcastUpdate("SwitchTransition", data);
|
||||||
|
|
||||||
|
obs_data_release(data);
|
||||||
|
obs_source_release(current_transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnTransitionListChange()
|
||||||
|
{
|
||||||
|
broadcastUpdate("TransitionListChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnProfileChange()
|
||||||
|
{
|
||||||
|
broadcastUpdate("ProfileChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnProfileListChange()
|
||||||
|
{
|
||||||
|
broadcastUpdate("ProfileListChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnStreamStarting()
|
||||||
|
{
|
||||||
// Implements an existing update type from bilhamil's OBS Remote
|
// Implements an existing update type from bilhamil's OBS Remote
|
||||||
obs_data_t *data = obs_data_create();
|
obs_data_t* data = obs_data_create();
|
||||||
obs_data_set_bool(data, "preview-only", false);
|
obs_data_set_bool(data, "preview-only", false);
|
||||||
|
|
||||||
broadcastUpdate("StreamStarting", data);
|
broadcastUpdate("StreamStarting", data);
|
||||||
@ -118,16 +386,18 @@ void WSEvents::OnStreamStarting() {
|
|||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStarted() {
|
void WSEvents::OnStreamStarted()
|
||||||
|
{
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
_streamStartTime = os_gettime_ns();
|
_stream_starttime = os_gettime_ns();
|
||||||
_lastBytesSent = 0;
|
_lastBytesSent = 0;
|
||||||
broadcastUpdate("StreamStarted");
|
broadcastUpdate("StreamStarted");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStopping() {
|
void WSEvents::OnStreamStopping()
|
||||||
|
{
|
||||||
// Implements an existing update type from bilhamil's OBS Remote
|
// Implements an existing update type from bilhamil's OBS Remote
|
||||||
obs_data_t *data = obs_data_create();
|
obs_data_t* data = obs_data_create();
|
||||||
obs_data_set_bool(data, "preview-only", false);
|
obs_data_set_bool(data, "preview-only", false);
|
||||||
|
|
||||||
broadcastUpdate("StreamStopping", data);
|
broadcastUpdate("StreamStopping", data);
|
||||||
@ -135,81 +405,243 @@ void WSEvents::OnStreamStopping() {
|
|||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnStreamStopped() {
|
void WSEvents::OnStreamStopped()
|
||||||
|
{
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
_streamStartTime = 0;
|
_stream_starttime = 0;
|
||||||
broadcastUpdate("StreamStopped");
|
broadcastUpdate("StreamStopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStarting() {
|
void WSEvents::OnRecordingStarting()
|
||||||
|
{
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
broadcastUpdate("RecordingStarting");
|
broadcastUpdate("RecordingStarting");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStarted() {
|
void WSEvents::OnRecordingStarted()
|
||||||
|
{
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
|
_rec_starttime = os_gettime_ns();
|
||||||
broadcastUpdate("RecordingStarted");
|
broadcastUpdate("RecordingStarted");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStopping() {
|
void WSEvents::OnRecordingStopping()
|
||||||
|
{
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
broadcastUpdate("RecordingStopping");
|
broadcastUpdate("RecordingStopping");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnRecordingStopped() {
|
void WSEvents::OnRecordingStopped()
|
||||||
|
{
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
|
_rec_starttime = 0;
|
||||||
broadcastUpdate("RecordingStopped");
|
broadcastUpdate("RecordingStopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::OnExit() {
|
void WSEvents::OnExit()
|
||||||
|
{
|
||||||
// New update type specific to OBS Studio
|
// New update type specific to OBS Studio
|
||||||
broadcastUpdate("Exiting");
|
broadcastUpdate("Exiting");
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSEvents::StreamStatus() {
|
void WSEvents::StreamStatus()
|
||||||
bool streamingActive = obs_frontend_streaming_active();
|
{
|
||||||
bool recordingActive = obs_frontend_recording_active();
|
bool streaming_active = obs_frontend_streaming_active();
|
||||||
|
bool recording_active = obs_frontend_recording_active();
|
||||||
|
|
||||||
obs_output_t *streamOutput = obs_frontend_get_streaming_output();
|
obs_output_t* stream_output = obs_frontend_get_streaming_output();
|
||||||
|
|
||||||
|
if (!stream_output || !streaming_active)
|
||||||
|
{
|
||||||
|
if (stream_output)
|
||||||
|
obs_output_release(stream_output);
|
||||||
|
|
||||||
if (!streamOutput || !streamingActive || !recordingActive) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
|
uint64_t bytes_sent = obs_output_get_total_bytes(stream_output);
|
||||||
uint64_t bytesSentTime = os_gettime_ns();
|
uint64_t bytes_sent_time = os_gettime_ns();
|
||||||
|
|
||||||
if (bytesSent < _lastBytesSent) {
|
if (bytes_sent < _lastBytesSent)
|
||||||
bytesSent = 0;
|
bytes_sent = 0;
|
||||||
}
|
|
||||||
if (bytesSent == 0) {
|
if (bytes_sent == 0)
|
||||||
_lastBytesSent = 0;
|
_lastBytesSent = 0;
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t bitsBetween = (bytesSent - _lastBytesSent) * 8;
|
|
||||||
double timePassed = double(bytesSentTime - _lastBytesSentTime) / 1000000000.0;
|
|
||||||
|
|
||||||
uint64_t bitsPerSec = bitsBetween / timePassed;
|
uint64_t bytes_between = bytes_sent - _lastBytesSent;
|
||||||
uint64_t bytesPerSec = bitsPerSec / 8;
|
double time_passed =
|
||||||
|
double(bytes_sent_time - _lastBytesSentTime) / 1000000000.0;
|
||||||
|
|
||||||
_lastBytesSent = bytesSent;
|
uint64_t bytes_per_sec = bytes_between / time_passed;
|
||||||
_lastBytesSentTime = bytesSentTime;
|
|
||||||
|
|
||||||
uint64_t totalStreamTime = (os_gettime_ns() - _streamStartTime) / 1000000000;
|
_lastBytesSent = bytes_sent;
|
||||||
|
_lastBytesSentTime = bytes_sent_time;
|
||||||
|
|
||||||
obs_data_t *data = obs_data_create();
|
uint64_t totalStreamTime =
|
||||||
obs_data_set_bool(data, "streaming", streamingActive);
|
(os_gettime_ns() - _stream_starttime) / 1000000000;
|
||||||
obs_data_set_bool(data, "recording", recordingActive);
|
|
||||||
obs_data_set_bool(data, "preview-only", false); // Retrocompat with OBSRemote
|
int total_frames = obs_output_get_total_frames(stream_output);
|
||||||
obs_data_set_int(data, "bytes-per-sec", bytesPerSec); // BUG : Computation seems buggy
|
int dropped_frames = obs_output_get_frames_dropped(stream_output);
|
||||||
obs_data_set_double(data, "strain", 0.0); // dafuq is strain
|
|
||||||
|
float strain = obs_output_get_congestion(stream_output);
|
||||||
|
|
||||||
|
obs_data_t* data = obs_data_create();
|
||||||
|
obs_data_set_bool(data, "streaming", streaming_active);
|
||||||
|
obs_data_set_bool(data, "recording", recording_active);
|
||||||
|
obs_data_set_int(data, "bytes-per-sec", bytes_per_sec);
|
||||||
|
obs_data_set_int(data, "kbits-per-sec", (bytes_per_sec * 8) / 1024);
|
||||||
obs_data_set_int(data, "total-stream-time", totalStreamTime);
|
obs_data_set_int(data, "total-stream-time", totalStreamTime);
|
||||||
obs_data_set_int(data, "num-total-frames", obs_output_get_total_frames(streamOutput));
|
obs_data_set_int(data, "num-total-frames", total_frames);
|
||||||
obs_data_set_int(data, "num-dropped-frames", obs_output_get_frames_dropped(streamOutput));
|
obs_data_set_int(data, "num-dropped-frames", dropped_frames);
|
||||||
obs_data_set_double(data, "fps", obs_get_active_fps());
|
obs_data_set_double(data, "fps", obs_get_active_fps());
|
||||||
|
obs_data_set_double(data, "strain", strain);
|
||||||
|
obs_data_set_bool(data, "preview-only", false); // Retrocompat with OBSRemote
|
||||||
|
|
||||||
broadcastUpdate("StreamStatus", data);
|
broadcastUpdate("StreamStatus", data);
|
||||||
|
|
||||||
obs_data_release(data);
|
obs_data_release(data);
|
||||||
obs_output_release(streamOutput);
|
obs_output_release(stream_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WSEvents::TransitionDurationChanged(int ms)
|
||||||
|
{
|
||||||
|
obs_data_t* fields = obs_data_create();
|
||||||
|
obs_data_set_int(fields, "new-duration", ms);
|
||||||
|
|
||||||
|
broadcastUpdate("TransitionDurationChanged", fields);
|
||||||
|
|
||||||
|
obs_data_release(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnTransitionBegin(void* param, calldata_t* data)
|
||||||
|
{
|
||||||
|
UNUSED_PARAMETER(data);
|
||||||
|
|
||||||
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
instance->broadcastUpdate("TransitionBegin");
|
||||||
|
|
||||||
|
blog(LOG_INFO, "transition begin");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnSceneReordered(void* param, calldata_t* data)
|
||||||
|
{
|
||||||
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
obs_scene_t* scene = nullptr;
|
||||||
|
calldata_get_ptr(data, "scene", &scene);
|
||||||
|
|
||||||
|
obs_data_t* fields = obs_data_create();
|
||||||
|
obs_data_set_string(fields, "scene-name",
|
||||||
|
obs_source_get_name(obs_scene_get_source(scene)));
|
||||||
|
|
||||||
|
instance->broadcastUpdate("SourceOrderChanged", fields);
|
||||||
|
|
||||||
|
obs_data_release(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnSceneItemAdd(void* param, calldata_t* data)
|
||||||
|
{
|
||||||
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
obs_scene_t* scene = nullptr;
|
||||||
|
calldata_get_ptr(data, "scene", &scene);
|
||||||
|
|
||||||
|
obs_sceneitem_t* scene_item = nullptr;
|
||||||
|
calldata_get_ptr(data, "item", &scene_item);
|
||||||
|
|
||||||
|
const char* scene_name =
|
||||||
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
|
const char* sceneitem_name =
|
||||||
|
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
||||||
|
|
||||||
|
obs_data_t* fields = obs_data_create();
|
||||||
|
obs_data_set_string(fields, "scene-name", scene_name);
|
||||||
|
obs_data_set_string(fields, "item-name", sceneitem_name);
|
||||||
|
|
||||||
|
instance->broadcastUpdate("SceneItemAdded", fields);
|
||||||
|
|
||||||
|
obs_data_release(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnSceneItemDelete(void* param, calldata_t* data)
|
||||||
|
{
|
||||||
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
obs_scene_t* scene = nullptr;
|
||||||
|
calldata_get_ptr(data, "scene", &scene);
|
||||||
|
|
||||||
|
obs_sceneitem_t* scene_item = nullptr;
|
||||||
|
calldata_get_ptr(data, "item", &scene_item);
|
||||||
|
|
||||||
|
const char* scene_name =
|
||||||
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
|
const char* sceneitem_name =
|
||||||
|
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
||||||
|
|
||||||
|
obs_data_t* fields = obs_data_create();
|
||||||
|
obs_data_set_string(fields, "scene-name", scene_name);
|
||||||
|
obs_data_set_string(fields, "item-name", sceneitem_name);
|
||||||
|
|
||||||
|
instance->broadcastUpdate("SceneItemRemoved", fields);
|
||||||
|
|
||||||
|
obs_data_release(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::OnSceneItemVisibilityChanged(void* param, calldata_t* data)
|
||||||
|
{
|
||||||
|
WSEvents* instance = static_cast<WSEvents*>(param);
|
||||||
|
|
||||||
|
obs_scene_t* scene = nullptr;
|
||||||
|
calldata_get_ptr(data, "scene", &scene);
|
||||||
|
|
||||||
|
obs_sceneitem_t* scene_item = nullptr;
|
||||||
|
calldata_get_ptr(data, "item", &scene_item);
|
||||||
|
|
||||||
|
bool visible = false;
|
||||||
|
calldata_get_bool(data, "visible", &visible);
|
||||||
|
|
||||||
|
const char* scene_name =
|
||||||
|
obs_source_get_name(obs_scene_get_source(scene));
|
||||||
|
const char* sceneitem_name =
|
||||||
|
obs_source_get_name(obs_sceneitem_get_source(scene_item));
|
||||||
|
|
||||||
|
obs_data_t* fields = obs_data_create();
|
||||||
|
obs_data_set_string(fields, "scene-name", scene_name);
|
||||||
|
obs_data_set_string(fields, "item-name", sceneitem_name);
|
||||||
|
obs_data_set_bool(fields, "item-visible", visible);
|
||||||
|
|
||||||
|
instance->broadcastUpdate("SceneItemVisibilityChanged", fields);
|
||||||
|
|
||||||
|
obs_data_release(fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::SelectedSceneChanged(QListWidgetItem* current, QListWidgetItem* prev)
|
||||||
|
{
|
||||||
|
if (Utils::IsPreviewModeActive())
|
||||||
|
{
|
||||||
|
obs_scene_t* scene = Utils::SceneListItemToScene(current);
|
||||||
|
if (!scene) return;
|
||||||
|
|
||||||
|
obs_source_t* scene_source = obs_scene_get_source(scene);
|
||||||
|
obs_data_array_t* scene_items = Utils::GetSceneItems(scene_source);
|
||||||
|
|
||||||
|
obs_data_t* data = obs_data_create();
|
||||||
|
obs_data_set_string(data, "scene-name", obs_source_get_name(scene_source));
|
||||||
|
obs_data_set_array(data, "sources", scene_items);
|
||||||
|
|
||||||
|
broadcastUpdate("PreviewSceneChanged", data);
|
||||||
|
|
||||||
|
obs_data_array_release(scene_items);
|
||||||
|
obs_data_release(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSEvents::ModeSwitchClicked(bool checked)
|
||||||
|
{
|
||||||
|
obs_data_t* data = obs_data_create();
|
||||||
|
obs_data_set_bool(data, "new-state", checked);
|
||||||
|
|
||||||
|
broadcastUpdate("StudioModeSwitched", data);
|
||||||
|
|
||||||
|
obs_data_release(data);
|
||||||
|
}
|
||||||
|
58
WSEvents.h
58
WSEvents.h
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
Copyright (C) 2017 Brendan Hagan <https://github.com/haganbmj>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,33 +20,63 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#ifndef WSEVENTS_H
|
#ifndef WSEVENTS_H
|
||||||
#define WSEVENTS_H
|
#define WSEVENTS_H
|
||||||
|
|
||||||
#include <QtWebSockets/QWebSocket>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <util/platform.h>
|
#include <QListWidgetItem>
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
|
|
||||||
class WSEvents : public QObject
|
class WSEvents : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WSEvents(WSServer *server);
|
explicit WSEvents(WSServer* srv);
|
||||||
~WSEvents();
|
~WSEvents();
|
||||||
static void FrontendEventHandler(enum obs_frontend_event event, void *private_data);
|
static void FrontendEventHandler(
|
||||||
|
enum obs_frontend_event event, void* private_data);
|
||||||
|
void connectTransitionSignals(obs_source_t* transition);
|
||||||
|
void connectSceneSignals(obs_source_t* scene);
|
||||||
|
static WSEvents* Instance;
|
||||||
|
|
||||||
|
uint64_t GetStreamingTime();
|
||||||
|
const char* GetStreamingTimecode();
|
||||||
|
uint64_t GetRecordingTime();
|
||||||
|
const char* GetRecordingTimecode();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
void deferredInitOperations();
|
||||||
void StreamStatus();
|
void StreamStatus();
|
||||||
|
void TransitionDurationChanged(int ms);
|
||||||
|
void SelectedSceneChanged(
|
||||||
|
QListWidgetItem* current, QListWidgetItem* prev);
|
||||||
|
void ModeSwitchClicked(bool checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WSServer *_srv;
|
WSServer* _srv;
|
||||||
uint64_t _streamStartTime;
|
signal_handler_t* transition_handler;
|
||||||
|
signal_handler_t* scene_handler;
|
||||||
|
|
||||||
|
bool _streaming_active;
|
||||||
|
bool _recording_active;
|
||||||
|
|
||||||
|
uint64_t _stream_starttime;
|
||||||
|
uint64_t _rec_starttime;
|
||||||
|
|
||||||
uint64_t _lastBytesSent;
|
uint64_t _lastBytesSent;
|
||||||
uint64_t _lastBytesSentTime;
|
uint64_t _lastBytesSentTime;
|
||||||
void broadcastUpdate(const char *updateType, obs_data_t *additionalFields);
|
|
||||||
|
void broadcastUpdate(const char* updateType,
|
||||||
|
obs_data_t* additionalFields);
|
||||||
|
|
||||||
void OnSceneChange();
|
void OnSceneChange();
|
||||||
void OnSceneListChange();
|
void OnSceneListChange();
|
||||||
|
void OnSceneCollectionChange();
|
||||||
|
void OnSceneCollectionListChange();
|
||||||
|
|
||||||
|
void OnTransitionChange();
|
||||||
|
void OnTransitionListChange();
|
||||||
|
|
||||||
|
void OnProfileChange();
|
||||||
|
void OnProfileListChange();
|
||||||
|
|
||||||
void OnStreamStarting();
|
void OnStreamStarting();
|
||||||
void OnStreamStarted();
|
void OnStreamStarted();
|
||||||
@ -58,6 +89,13 @@ class WSEvents : public QObject
|
|||||||
void OnRecordingStopped();
|
void OnRecordingStopped();
|
||||||
|
|
||||||
void OnExit();
|
void OnExit();
|
||||||
|
|
||||||
|
static void OnTransitionBegin(void* param, calldata_t* data);
|
||||||
|
|
||||||
|
static void OnSceneReordered(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemAdd(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemDelete(void* param, calldata_t* data);
|
||||||
|
static void OnSceneItemVisibilityChanged(void* param, calldata_t* data);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WSEVENTS_H
|
#endif // WSEVENTS_H
|
1695
WSRequestHandler.cpp
1695
WSRequestHandler.cpp
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
Copyright (C) 2017 Mikhail Swift <https://github.com/mikhailswift>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,57 +20,97 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#ifndef WSREQUESTHANDLER_H
|
#ifndef WSREQUESTHANDLER_H
|
||||||
#define WSREQUESTHANDLER_H
|
#define WSREQUESTHANDLER_H
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <QtWebSockets/QWebSocket>
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
|
#include <QtWebSockets/QWebSocket>
|
||||||
|
#include <QtWebSockets/QWebSocketServer>
|
||||||
|
|
||||||
class WSRequestHandler : public QObject
|
class WSRequestHandler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WSRequestHandler(QWebSocket *client);
|
explicit WSRequestHandler(QWebSocket* client);
|
||||||
~WSRequestHandler();
|
~WSRequestHandler();
|
||||||
void sendTextMessage(QString textMessage);
|
void processIncomingMessage(QString textMessage);
|
||||||
|
bool hasField(const char* name);
|
||||||
private Q_SLOTS:
|
|
||||||
void processTextMessage(QString textMessage);
|
|
||||||
void socketDisconnected();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void disconnected();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWebSocket *_client;
|
static obs_service_t* _service;
|
||||||
bool _authenticated;
|
QWebSocket* _client;
|
||||||
const char *_messageId;
|
const char* _messageId;
|
||||||
const char *_requestType;
|
const char* _requestType;
|
||||||
obs_data_t *_requestData;
|
obs_data_t* data;
|
||||||
|
|
||||||
std::map<std::string, void(*)(WSRequestHandler*)> messageMap;
|
QMap<QString, void(*)(WSRequestHandler*)> messageMap;
|
||||||
std::set<std::string> authNotRequired;
|
QSet<QString> authNotRequired;
|
||||||
|
|
||||||
void SendOKResponse(obs_data_t *additionalFields = NULL);
|
void SendOKResponse(obs_data_t* additionalFields = NULL);
|
||||||
void SendErrorResponse(const char *errorMessage);
|
void SendErrorResponse(const char* errorMessage);
|
||||||
static void ErrNotImplemented(WSRequestHandler *owner);
|
void SendResponse(obs_data_t* response);
|
||||||
|
|
||||||
|
static void HandleGetVersion(WSRequestHandler* req);
|
||||||
|
static void HandleGetAuthRequired(WSRequestHandler* req);
|
||||||
|
static void HandleAuthenticate(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetCurrentScene(WSRequestHandler* req);
|
||||||
|
static void HandleGetCurrentScene(WSRequestHandler* req);
|
||||||
|
static void HandleGetSceneList(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetSceneItemRender(WSRequestHandler* req);
|
||||||
|
static void HandleSetSceneItemPosition(WSRequestHandler* req);
|
||||||
|
static void HandleSetSceneItemTransform(WSRequestHandler* req);
|
||||||
|
static void HandleSetSceneItemCrop(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleGetStreamingStatus(WSRequestHandler* req);
|
||||||
|
static void HandleStartStopStreaming(WSRequestHandler* req);
|
||||||
|
static void HandleStartStopRecording(WSRequestHandler* req);
|
||||||
|
static void HandleStartStreaming(WSRequestHandler* req);
|
||||||
|
static void HandleStopStreaming(WSRequestHandler* req);
|
||||||
|
static void HandleStartRecording(WSRequestHandler* req);
|
||||||
|
static void HandleStopRecording(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetRecordingFolder(WSRequestHandler* req);
|
||||||
|
static void HandleGetRecordingFolder(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleGetTransitionList(WSRequestHandler* req);
|
||||||
|
static void HandleGetCurrentTransition(WSRequestHandler* req);
|
||||||
|
static void HandleSetCurrentTransition(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetVolume(WSRequestHandler* req);
|
||||||
|
static void HandleGetVolume(WSRequestHandler* req);
|
||||||
|
static void HandleToggleMute(WSRequestHandler* req);
|
||||||
|
static void HandleSetMute(WSRequestHandler* req);
|
||||||
|
static void HandleGetMute(WSRequestHandler* req);
|
||||||
|
static void HandleGetSpecialSources(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetCurrentSceneCollection(WSRequestHandler* req);
|
||||||
|
static void HandleGetCurrentSceneCollection(WSRequestHandler* req);
|
||||||
|
static void HandleListSceneCollections(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetCurrentProfile(WSRequestHandler* req);
|
||||||
|
static void HandleGetCurrentProfile(WSRequestHandler* req);
|
||||||
|
static void HandleListProfiles(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetStreamSettings(WSRequestHandler* req);
|
||||||
|
static void HandleGetStreamSettings(WSRequestHandler* req);
|
||||||
|
static void HandleSaveStreamSettings(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleSetTransitionDuration(WSRequestHandler* req);
|
||||||
|
static void HandleGetTransitionDuration(WSRequestHandler* req);
|
||||||
|
|
||||||
|
static void HandleGetStudioModeStatus(WSRequestHandler* req);
|
||||||
|
static void HandleGetPreviewScene(WSRequestHandler* req);
|
||||||
|
static void HandleSetPreviewScene(WSRequestHandler* req);
|
||||||
|
static void HandleTransitionToProgram(WSRequestHandler* req);
|
||||||
|
static void HandleEnableStudioMode(WSRequestHandler* req);
|
||||||
|
static void HandleDisableStudioMode(WSRequestHandler* req);
|
||||||
|
static void HandleToggleStudioMode(WSRequestHandler* req);
|
||||||
|
|
||||||
static void HandleGetVersion(WSRequestHandler *owner);
|
static void HandleSetTextGDIPlusProperties(WSRequestHandler* req);
|
||||||
static void HandleGetAuthRequired(WSRequestHandler *owner);
|
static void HandleGetTextGDIPlusProperties(WSRequestHandler* req);
|
||||||
static void HandleAuthenticate(WSRequestHandler *owner);
|
static void HandleSetBrowserSourceProperties(WSRequestHandler* req);
|
||||||
|
static void HandleGetBrowserSourceProperties(WSRequestHandler* req);
|
||||||
static void HandleSetCurrentScene(WSRequestHandler *owner);
|
|
||||||
static void HandleGetCurrentScene(WSRequestHandler *owner);
|
|
||||||
static void HandleGetSceneList(WSRequestHandler *owner);
|
|
||||||
static void HandleSetSourceRender(WSRequestHandler *owner);
|
|
||||||
|
|
||||||
static void HandleGetStreamingStatus(WSRequestHandler *owner);
|
|
||||||
static void HandleStartStopStreaming(WSRequestHandler *owner);
|
|
||||||
static void HandleStartStopRecording(WSRequestHandler *owner);
|
|
||||||
|
|
||||||
static void HandleGetTransitionList(WSRequestHandler *owner);
|
|
||||||
static void HandleGetCurrentTransition(WSRequestHandler *owner);
|
|
||||||
static void HandleSetCurrentTransition(WSRequestHandler *owner);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WSPROTOCOL_H
|
#endif // WSPROTOCOL_H
|
||||||
|
148
WSServer.cpp
148
WSServer.cpp
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,66 +16,156 @@ You should have received a copy of the GNU General Public License along
|
|||||||
with this program. If not, see <https://www.gnu.org/licenses/>
|
with this program. If not, see <https://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "WSServer.h"
|
|
||||||
#include "WSRequestHandler.h"
|
|
||||||
#include <QtWebSockets/QWebSocketServer>
|
|
||||||
#include <QtWebSockets/QWebSocket>
|
#include <QtWebSockets/QWebSocket>
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
#include <QtCore/QByteArray>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
|
#include "WSServer.h"
|
||||||
|
#include "obs-websocket.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
QT_USE_NAMESPACE
|
QT_USE_NAMESPACE
|
||||||
|
|
||||||
WSServer::WSServer(quint16 port, QObject *parent) :
|
WSServer* WSServer::Instance = nullptr;
|
||||||
|
|
||||||
|
WSServer::WSServer(QObject* parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
_wsServer(Q_NULLPTR),
|
_wsServer(Q_NULLPTR),
|
||||||
_clients()
|
_clients(),
|
||||||
|
_clMutex(QMutex::Recursive)
|
||||||
{
|
{
|
||||||
_serverThread = new QThread();
|
_serverThread = new QThread();
|
||||||
_wsServer = new QWebSocketServer(
|
|
||||||
QStringLiteral("OBS Websocket API"),
|
|
||||||
QWebSocketServer::NonSecureMode,
|
|
||||||
this);
|
|
||||||
_wsServer->moveToThread(_serverThread);
|
|
||||||
_serverThread->start();
|
|
||||||
|
|
||||||
bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
|
_wsServer = new QWebSocketServer(
|
||||||
if (serverStarted) {
|
QStringLiteral("obs-websocket"),
|
||||||
connect(_wsServer, &QWebSocketServer::newConnection, this, &WSServer::onNewConnection);
|
QWebSocketServer::NonSecureMode,
|
||||||
}
|
_serverThread);
|
||||||
|
|
||||||
|
_serverThread->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
WSServer::~WSServer()
|
WSServer::~WSServer()
|
||||||
{
|
{
|
||||||
|
Stop();
|
||||||
|
delete _serverThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::Start(quint16 port)
|
||||||
|
{
|
||||||
|
if (port == _wsServer->serverPort())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(_wsServer->isListening())
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
bool serverStarted = _wsServer->listen(QHostAddress::Any, port);
|
||||||
|
if (serverStarted)
|
||||||
|
{
|
||||||
|
connect(_wsServer, &QWebSocketServer::newConnection,
|
||||||
|
this, &WSServer::onNewConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::Stop()
|
||||||
|
{
|
||||||
|
_clMutex.lock();
|
||||||
|
for(QWebSocket* pClient : _clients) {
|
||||||
|
pClient->close();
|
||||||
|
}
|
||||||
|
_clMutex.unlock();
|
||||||
|
|
||||||
_wsServer->close();
|
_wsServer->close();
|
||||||
qDeleteAll(_clients.begin(), _clients.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::broadcast(QString message)
|
void WSServer::broadcast(QString message)
|
||||||
{
|
{
|
||||||
Q_FOREACH(WSRequestHandler *pClient, _clients) {
|
_clMutex.lock();
|
||||||
|
|
||||||
|
for(QWebSocket* pClient : _clients) {
|
||||||
|
if (Config::Current()->AuthRequired
|
||||||
|
&& (pClient->property(PROP_AUTHENTICATED).toBool() == false))
|
||||||
|
{
|
||||||
|
// Skip this client if unauthenticated
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
pClient->sendTextMessage(message);
|
pClient->sendTextMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_clMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::onNewConnection()
|
void WSServer::onNewConnection()
|
||||||
{
|
{
|
||||||
QWebSocket *pSocket = _wsServer->nextPendingConnection();
|
QWebSocket* pSocket = _wsServer->nextPendingConnection();
|
||||||
|
|
||||||
if (pSocket) {
|
if (pSocket)
|
||||||
WSRequestHandler *pHandler = new WSRequestHandler(pSocket);
|
{
|
||||||
|
connect(pSocket, &QWebSocket::textMessageReceived,
|
||||||
|
this, &WSServer::textMessageReceived);
|
||||||
|
connect(pSocket, &QWebSocket::disconnected,
|
||||||
|
this, &WSServer::socketDisconnected);
|
||||||
|
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
||||||
|
|
||||||
connect(pHandler, &WSRequestHandler::disconnected, this, &WSServer::socketDisconnected);
|
_clMutex.lock();
|
||||||
_clients << pHandler;
|
_clients << pSocket;
|
||||||
|
_clMutex.unlock();
|
||||||
|
|
||||||
|
QHostAddress clientAddr = pSocket->peerAddress();
|
||||||
|
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
||||||
|
|
||||||
|
blog(LOG_INFO, "new client connection from %s:%d",
|
||||||
|
clientIp.toUtf8().constData(), pSocket->peerPort());
|
||||||
|
|
||||||
|
QString msg = QString(obs_module_text("OBSWebsocket.ConnectNotify.ClientIP"))
|
||||||
|
+ QString(" ")
|
||||||
|
+ clientAddr.toString();
|
||||||
|
|
||||||
|
Utils::SysTrayNotify(msg,
|
||||||
|
QSystemTrayIcon::Information,
|
||||||
|
QString(obs_module_text("OBSWebsocket.ConnectNotify.Connected")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WSServer::textMessageReceived(QString message)
|
||||||
|
{
|
||||||
|
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
||||||
|
|
||||||
|
if (pSocket)
|
||||||
|
{
|
||||||
|
WSRequestHandler handler(pSocket);
|
||||||
|
handler.processIncomingMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSServer::socketDisconnected()
|
void WSServer::socketDisconnected()
|
||||||
{
|
{
|
||||||
WSRequestHandler *pClient = qobject_cast<WSRequestHandler *>(sender());
|
QWebSocket* pSocket = qobject_cast<QWebSocket*>(sender());
|
||||||
|
|
||||||
if (pClient) {
|
if (pSocket)
|
||||||
_clients.removeAll(pClient);
|
{
|
||||||
pClient->deleteLater();
|
pSocket->setProperty(PROP_AUTHENTICATED, false);
|
||||||
|
|
||||||
|
_clMutex.lock();
|
||||||
|
_clients.removeAll(pSocket);
|
||||||
|
_clMutex.unlock();
|
||||||
|
|
||||||
|
pSocket->deleteLater();
|
||||||
|
|
||||||
|
QHostAddress clientAddr = pSocket->peerAddress();
|
||||||
|
QString clientIp = Utils::FormatIPAddress(clientAddr);
|
||||||
|
|
||||||
|
blog(LOG_INFO, "client %s:%d disconnected",
|
||||||
|
clientIp.toUtf8().constData(), pSocket->peerPort());
|
||||||
|
|
||||||
|
QString msg = QString(obs_module_text("OBSWebsocket.ConnectNotify.ClientIP"))
|
||||||
|
+ QString(" ")
|
||||||
|
+ clientAddr.toString();
|
||||||
|
|
||||||
|
Utils::SysTrayNotify(msg,
|
||||||
|
QSystemTrayIcon::Information,
|
||||||
|
QString(obs_module_text("OBSWebsocket.ConnectNotify.Disconnected")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
20
WSServer.h
20
WSServer.h
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -21,29 +21,35 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QList>
|
#include <QtCore/QList>
|
||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QMutex>
|
||||||
#include "WSRequestHandler.h"
|
|
||||||
|
|
||||||
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
|
QT_FORWARD_DECLARE_CLASS(QWebSocketServer)
|
||||||
QT_FORWARD_DECLARE_CLASS(QWebSocket)
|
QT_FORWARD_DECLARE_CLASS(QWebSocket)
|
||||||
|
|
||||||
|
#include "WSRequestHandler.h"
|
||||||
|
|
||||||
class WSServer : public QObject
|
class WSServer : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WSServer(quint16 port, QObject *parent = Q_NULLPTR);
|
explicit WSServer(QObject* parent = Q_NULLPTR);
|
||||||
virtual ~WSServer();
|
virtual ~WSServer();
|
||||||
|
void Start(quint16 port);
|
||||||
|
void Stop();
|
||||||
void broadcast(QString message);
|
void broadcast(QString message);
|
||||||
|
static WSServer* Instance;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void onNewConnection();
|
void onNewConnection();
|
||||||
|
void textMessageReceived(QString message);
|
||||||
void socketDisconnected();
|
void socketDisconnected();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWebSocketServer *_wsServer;
|
QWebSocketServer* _wsServer;
|
||||||
QList<WSRequestHandler *> _clients;
|
QList<QWebSocket*> _clients;
|
||||||
QThread *_serverThread;
|
QMutex _clMutex;
|
||||||
|
QThread* _serverThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WSSERVER_H
|
#endif // WSSERVER_H
|
50
appveyor.yml
Normal file
50
appveyor.yml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
environment:
|
||||||
|
CURL_VERSION: 7.39.0
|
||||||
|
|
||||||
|
install:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
- cd C:\projects\
|
||||||
|
- if not exist dependencies2013.zip curl -kLO https://obsproject.com/downloads/dependencies2013.zip -f --retry 5 -C -
|
||||||
|
- 7z x dependencies2013.zip -odependencies2013
|
||||||
|
- if not exist qt570.zip curl -kLO https://www.slepin.fr/obs-websocket/ci/qt570.zip -f --retry 5 -C -
|
||||||
|
- 7z x qt570.zip -o"Qt5.7.0"
|
||||||
|
- set DepsPath32=%CD%\dependencies2013\win32
|
||||||
|
- set DepsPath64=%CD%\dependencies2013\win64
|
||||||
|
- set QTDIR32=%CD%\Qt5.7.0\msvc2013
|
||||||
|
- set QTDIR64=%CD%\Qt5.7.0\msvc2013_64
|
||||||
|
- set build_config=Release
|
||||||
|
- git clone --recursive https://github.com/jp9000/obs-studio
|
||||||
|
- cd C:\projects\obs-studio\
|
||||||
|
- git checkout 19.0.2
|
||||||
|
- mkdir build
|
||||||
|
- mkdir build32
|
||||||
|
- mkdir build64
|
||||||
|
- cd ./build32
|
||||||
|
- cmake -G "Visual Studio 12 2013" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
|
- cd ../build64
|
||||||
|
- cmake -G "Visual Studio 12 2013 Win64" -DCOPIED_DEPENDENCIES=false -DCOPY_DEPENDENCIES=true ..
|
||||||
|
- call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build32\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
- call msbuild /m /p:Configuration=%build_config% C:\projects\obs-studio\build64\obs-studio.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
- cd C:\projects\obs-websocket\
|
||||||
|
- mkdir build32
|
||||||
|
- mkdir build64
|
||||||
|
- cd ./build32
|
||||||
|
- cmake -G "Visual Studio 12 2013" -DQTDIR="%QTDIR32%" -DLibObs_DIR="C:\projects\obs-studio\build32\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build32\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build32\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
||||||
|
- cd ../build64
|
||||||
|
- cmake -G "Visual Studio 12 2013 Win64" -DQTDIR="%QTDIR64%" -DLibObs_DIR="C:\projects\obs-studio\build64\libobs" -DLIBOBS_INCLUDE_DIR="C:\projects\obs-studio\libobs" -DLIBOBS_LIB="C:\projects\obs-studio\build64\libobs\%build_config%\obs.lib" -DOBS_FRONTEND_LIB="C:\projects\obs-studio\build64\UI\obs-frontend-api\%build_config%\obs-frontend-api.lib" ..
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- call msbuild /m /p:Configuration=%build_config% C:\projects\obs-websocket\build32\obs-websocket.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
- call msbuild /m /p:Configuration=%build_config% C:\projects\obs-websocket\build64\obs-websocket.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
|
||||||
|
before_deploy:
|
||||||
|
- 7z a "C:\projects\obs-websocket\build.zip" C:\projects\obs-websocket\release\*
|
||||||
|
|
||||||
|
deploy_script:
|
||||||
|
- ps: Push-AppveyorArtifact "C:\projects\obs-websocket\build.zip" -FileName "obs-websocket-$(git log --pretty=format:'%h' -n 1).zip"
|
||||||
|
|
||||||
|
test: off
|
||||||
|
|
||||||
|
cache:
|
||||||
|
- C:\projects\dependencies2013.zip
|
||||||
|
- C:\projects\qt570.zip
|
6
data/locale/de-DE.ini
Normal file
6
data/locale/de-DE.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
OBSWebsocket.Menu.SettingsItem="Websocket-Server Einstellungen"
|
||||||
|
OBSWebsocket.Settings.DialogTitle="Websocket-Server Einstellungen"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="Websocket-Server aktivieren"
|
||||||
|
OBSWebsocket.Settings.ServerPort="Server Port"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="Authentifizierung erforderlich"
|
||||||
|
OBSWebsocket.Settings.Password="Passwort"
|
@ -1,4 +1,10 @@
|
|||||||
Menu.SettingsItem="Websocket server settings"
|
OBSWebsocket.Menu.SettingsItem="Websocket server settings"
|
||||||
Settings.DialogTitle="obs-websocket"
|
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
||||||
Settings.AuthRequired="Enable authentication"
|
OBSWebsocket.Settings.ServerEnable="Enable Websocket server"
|
||||||
Settings.Password="Password"
|
OBSWebsocket.Settings.ServerPort="Server Port"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="Enable authentication"
|
||||||
|
OBSWebsocket.Settings.Password="Password"
|
||||||
|
OBSWebsocket.Settings.DebugEnable="Enable debug logging"
|
||||||
|
OBSWebsocket.ConnectNotify.Connected="New WebSocket connection"
|
||||||
|
OBSWebsocket.ConnectNotify.Disconnected="WebSocket client disconnected"
|
||||||
|
OBSWebsocket.ConnectNotify.ClientIP="Client Address:"
|
@ -1,4 +1,9 @@
|
|||||||
Menu.SettingsItem="Paramètres du serveur Websocket"
|
OBSWebsocket.Menu.SettingsItem="Paramètres du serveur Websocket"
|
||||||
Settings.DialogTitle="obs-websocket"
|
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
||||||
Settings.AuthRequired="Activer l'authentification"
|
OBSWebsocket.Settings.ServerEnable="Activer le serveur Websockets"
|
||||||
Settings.Password="Mot de passe"
|
OBSWebsocket.Settings.ServerPort="Port du serveur"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="Activer l'authentification"
|
||||||
|
OBSWebsocket.Settings.Password="Mot de passe"
|
||||||
|
OBSWebsocket.ConnectNotify.Connected="Nouvelle connexion WebSocket"
|
||||||
|
OBSWebsocket.ConnectNotify.Disconnected="Déconnexion WebSocket"
|
||||||
|
OBSWebsocket.ConnectNotify.ClientIP="Adresse du client :"
|
6
data/locale/nl-NL.ini
Normal file
6
data/locale/nl-NL.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
OBSWebsocket.Menu.SettingsItem="Websocket server instellingen"
|
||||||
|
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="Activeer Websocket server"
|
||||||
|
OBSWebsocket.Settings.ServerPort="Server Poort"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="Activeer authenticatie"
|
||||||
|
OBSWebsocket.Settings.Password="Wachtwoord"
|
6
data/locale/pl-PL.ini
Normal file
6
data/locale/pl-PL.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
OBSWebsocket.Menu.SettingsItem="Ustawienia serwera zdalnego sterowania"
|
||||||
|
OBSWebsocket.Settings.DialogTitle="Serwer zdalnego sterowania"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="Włącz serwer zdalnego sterowania (Websocket)"
|
||||||
|
OBSWebsocket.Settings.ServerPort="Port serwera"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="Wymagaj hasła"
|
||||||
|
OBSWebsocket.Settings.Password="Hasło"
|
6
data/locale/pt-BR.ini
Normal file
6
data/locale/pt-BR.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
OBSWebsocket.Menu.SettingsItem="Configuraçes do Servidor Websocket"
|
||||||
|
OBSWebsocket.Settings.DialogTitle="obs-websocket"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="Habilitar o Servidor Websocket"
|
||||||
|
OBSWebsocket.Settings.ServerPort="Porta do Servidor"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="Autenticação Requerida"
|
||||||
|
OBSWebsocket.Settings.Password="Senha"
|
6
data/locale/zh-CN.ini
Normal file
6
data/locale/zh-CN.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
OBSWebsocket.Menu.SettingsItem="Websocket 服务器设置"
|
||||||
|
OBSWebsocket.Settings.DialogTitle="obs-websocket 设置"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="启用 Websocket 服务器"
|
||||||
|
OBSWebsocket.Settings.ServerPort="服务器端口"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="启用密码认证"
|
||||||
|
OBSWebsocket.Settings.Password="密码"
|
6
data/locale/zh-TW.ini
Normal file
6
data/locale/zh-TW.ini
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
OBSWebsocket.Menu.SettingsItem="Websocket 伺服器設定"
|
||||||
|
OBSWebsocket.Settings.DialogTitle="obs-websocket 設定"
|
||||||
|
OBSWebsocket.Settings.ServerEnable="啟用 Websocket 伺服器"
|
||||||
|
OBSWebsocket.Settings.ServerPort="伺服器端口"
|
||||||
|
OBSWebsocket.Settings.AuthRequired="啟用密碼認證"
|
||||||
|
OBSWebsocket.Settings.Password="密碼"
|
BIN
doc/mediaunit_logo_black.png
Normal file
BIN
doc/mediaunit_logo_black.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
BIN
doc/supportclass_logo_blacktext.png
Normal file
BIN
doc/supportclass_logo_blacktext.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -17,72 +17,97 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
|
|
||||||
|
#include "obs-websocket.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "WSServer.h"
|
||||||
#include "settings-dialog.h"
|
#include "settings-dialog.h"
|
||||||
#include "ui_settings-dialog.h"
|
#include "ui_settings-dialog.h"
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
#define CHANGE_ME "changeme"
|
#define CHANGE_ME "changeme"
|
||||||
|
|
||||||
SettingsDialog::SettingsDialog(QWidget *parent) :
|
SettingsDialog::SettingsDialog(QWidget* parent) :
|
||||||
QDialog(parent),
|
QDialog(parent, Qt::Dialog),
|
||||||
ui(new Ui::SettingsDialog)
|
ui(new Ui::SettingsDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
connect(ui->authRequired, &QCheckBox::stateChanged, this, &SettingsDialog::AuthCheckboxChanged);
|
connect(ui->authRequired, &QCheckBox::stateChanged,
|
||||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::FormAccepted);
|
this, &SettingsDialog::AuthCheckboxChanged);
|
||||||
|
connect(ui->buttonBox, &QDialogButtonBox::accepted,
|
||||||
|
this, &SettingsDialog::FormAccepted);
|
||||||
|
|
||||||
|
|
||||||
AuthCheckboxChanged();
|
AuthCheckboxChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::showEvent(QShowEvent *event) {
|
void SettingsDialog::showEvent(QShowEvent* event)
|
||||||
ui->authRequired->setChecked(Config::Current()->AuthRequired);
|
{
|
||||||
|
Config* conf = Config::Current();
|
||||||
|
|
||||||
|
ui->serverEnabled->setChecked(conf->ServerEnabled);
|
||||||
|
ui->serverPort->setValue(conf->ServerPort);
|
||||||
|
|
||||||
|
ui->debugEnabled->setChecked(conf->DebugEnabled);
|
||||||
|
|
||||||
|
ui->authRequired->setChecked(conf->AuthRequired);
|
||||||
ui->password->setText(CHANGE_ME);
|
ui->password->setText(CHANGE_ME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::ToggleShowHide() {
|
void SettingsDialog::ToggleShowHide()
|
||||||
if (!isVisible()) {
|
{
|
||||||
|
if (!isVisible())
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::AuthCheckboxChanged() {
|
void SettingsDialog::AuthCheckboxChanged()
|
||||||
if (ui->authRequired->isChecked()) {
|
{
|
||||||
|
if (ui->authRequired->isChecked())
|
||||||
ui->password->setEnabled(true);
|
ui->password->setEnabled(true);
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
ui->password->setEnabled(false);
|
ui->password->setEnabled(false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::FormAccepted() {
|
void SettingsDialog::FormAccepted()
|
||||||
if (ui->authRequired->isChecked()) {
|
{
|
||||||
if (ui->password->text() != CHANGE_ME) {
|
Config* conf = Config::Current();
|
||||||
QByteArray pwd = ui->password->text().toLocal8Bit();
|
|
||||||
|
conf->ServerEnabled = ui->serverEnabled->isChecked();
|
||||||
|
conf->ServerPort = ui->serverPort->value();
|
||||||
|
|
||||||
|
conf->DebugEnabled = ui->debugEnabled->isChecked();
|
||||||
|
|
||||||
|
if (ui->authRequired->isChecked())
|
||||||
|
{
|
||||||
|
if (ui->password->text() != CHANGE_ME)
|
||||||
|
{
|
||||||
|
QByteArray pwd = ui->password->text().toUtf8();
|
||||||
const char *new_password = pwd;
|
const char *new_password = pwd;
|
||||||
|
|
||||||
blog(LOG_INFO, "new password : %s", new_password);
|
conf->SetPassword(new_password);
|
||||||
Config::Current()->SetPassword(new_password);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(Config::Current()->Secret, "") != 0) {
|
|
||||||
Config::Current()->AuthRequired = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Config::Current()->AuthRequired = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(Config::Current()->Secret, "") != 0)
|
||||||
|
conf->AuthRequired = true;
|
||||||
|
else
|
||||||
|
conf->AuthRequired = false;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
Config::Current()->AuthRequired = false;
|
{
|
||||||
|
conf->AuthRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_frontend_save();
|
conf->Save();
|
||||||
|
|
||||||
|
if (conf->ServerEnabled)
|
||||||
|
WSServer::Instance->Start(conf->ServerPort);
|
||||||
|
else
|
||||||
|
WSServer::Instance->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsDialog::~SettingsDialog()
|
SettingsDialog::~SettingsDialog()
|
||||||
{
|
{
|
||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -27,12 +27,12 @@ class SettingsDialog;
|
|||||||
|
|
||||||
class SettingsDialog : public QDialog
|
class SettingsDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SettingsDialog(QWidget *parent = 0);
|
explicit SettingsDialog(QWidget* parent = 0);
|
||||||
~SettingsDialog();
|
~SettingsDialog();
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent* event);
|
||||||
void ToggleShowHide();
|
void ToggleShowHide();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
@ -40,7 +40,7 @@ private Q_SLOTS:
|
|||||||
void FormAccepted();
|
void FormAccepted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::SettingsDialog *ui;
|
Ui::SettingsDialog* ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SETTINGSDIALOG_H
|
#endif // SETTINGSDIALOG_H
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>354</width>
|
<width>407</width>
|
||||||
<height>110</height>
|
<height>175</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -17,7 +17,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Settings.DialogTitle</string>
|
<string>OBSWebsocket.Settings.DialogTitle</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeGripEnabled">
|
<property name="sizeGripEnabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
@ -28,27 +28,67 @@
|
|||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
<item row="3" column="0">
|
<item row="3" column="1">
|
||||||
<widget class="QLabel" name="lbl_password">
|
<widget class="QCheckBox" name="authRequired">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Settings.Password</string>
|
<string>OBSWebsocket.Settings.AuthRequired</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1">
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_password">
|
||||||
|
<property name="text">
|
||||||
|
<string>OBSWebsocket.Settings.Password</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
<widget class="QLineEdit" name="password">
|
<widget class="QLineEdit" name="password">
|
||||||
<property name="echoMode">
|
<property name="echoMode">
|
||||||
<enum>QLineEdit::Password</enum>
|
<enum>QLineEdit::Password</enum>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="authRequired">
|
<widget class="QCheckBox" name="serverEnabled">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Settings.AuthRequired</string>
|
<string>OBSWebsocket.Settings.ServerEnable</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="lbl_serverPort">
|
||||||
|
<property name="text">
|
||||||
|
<string>OBSWebsocket.Settings.ServerPort</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QSpinBox" name="serverPort">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1024</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>4444</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="QCheckBox" name="debugEnabled">
|
||||||
|
<property name="text">
|
||||||
|
<string>OBSWebsocket.Settings.DebugEnable</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
@ -73,11 +113,11 @@
|
|||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>248</x>
|
<x>248</x>
|
||||||
<y>254</y>
|
<y>274</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>157</x>
|
<x>157</x>
|
||||||
<y>274</y>
|
<y>294</y>
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
@ -89,11 +129,11 @@
|
|||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
<x>316</x>
|
<x>316</x>
|
||||||
<y>260</y>
|
<y>280</y>
|
||||||
</hint>
|
</hint>
|
||||||
<hint type="destinationlabel">
|
<hint type="destinationlabel">
|
||||||
<x>286</x>
|
<x>286</x>
|
||||||
<y>274</y>
|
<y>294</y>
|
||||||
</hint>
|
</hint>
|
||||||
</hints>
|
</hints>
|
||||||
</connection>
|
</connection>
|
||||||
|
54
installer/installer.iss
Normal file
54
installer/installer.iss
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
; Script generated by the Inno Setup Script Wizard.
|
||||||
|
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||||
|
|
||||||
|
#define MyAppName "obs-websocket"
|
||||||
|
#define MyAppVersion "4.0.0"
|
||||||
|
#define MyAppPublisher "St<53>phane Lepin"
|
||||||
|
#define MyAppURL "http://github.com/Palakis/obs-websocket"
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
; NOTE: The value of AppId uniquely identifies this application.
|
||||||
|
; Do not use the same AppId value in installers for other applications.
|
||||||
|
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
|
||||||
|
AppId={{117EE44F-48E1-49E5-A381-CC8D9195CF35}
|
||||||
|
AppName={#MyAppName}
|
||||||
|
AppVersion={#MyAppVersion}
|
||||||
|
;AppVerName={#MyAppName} {#MyAppVersion}
|
||||||
|
AppPublisher={#MyAppPublisher}
|
||||||
|
AppPublisherURL={#MyAppURL}
|
||||||
|
AppSupportURL={#MyAppURL}
|
||||||
|
AppUpdatesURL={#MyAppURL}
|
||||||
|
DefaultDirName={code:GetDirName}
|
||||||
|
DefaultGroupName={#MyAppName}
|
||||||
|
OutputBaseFilename=obs-websocket-{#MyAppVersion}-Windows-Installer
|
||||||
|
Compression=lzma
|
||||||
|
SolidCompression=yes
|
||||||
|
LicenseFile=..\LICENSE
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
Name: "english"; MessagesFile: "compiler:Default.isl"
|
||||||
|
Name: "french"; MessagesFile: "compiler:Languages\French.isl"
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
Source: "..\release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
|
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
|
||||||
|
|
||||||
|
[Icons]
|
||||||
|
Name: "{group}\{cm:ProgramOnTheWeb,{#MyAppName}}"; Filename: "{#MyAppURL}"
|
||||||
|
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
|
||||||
|
|
||||||
|
[Code]
|
||||||
|
// credit where it's due :
|
||||||
|
// following function come from https://github.com/Xaymar/obs-studio_amf-encoder-plugin/blob/master/%23Resources/Installer.in.iss#L45
|
||||||
|
function GetDirName(Value: string): string;
|
||||||
|
var
|
||||||
|
InstallPath: string;
|
||||||
|
begin
|
||||||
|
// initialize default path, which will be returned when the following registry
|
||||||
|
// key queries fail due to missing keys or for some different reason
|
||||||
|
Result := '{pf}\obs-studio';
|
||||||
|
// query the first registry value; if this succeeds, return the obtained value
|
||||||
|
if RegQueryStringValue(HKLM32, 'SOFTWARE\OBS Studio', '', InstallPath) then
|
||||||
|
Result := InstallPath
|
||||||
|
end;
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,33 +19,41 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
#include <obs-frontend-api.h>
|
#include <obs-frontend-api.h>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "obs-websocket.h"
|
#include "obs-websocket.h"
|
||||||
#include "WSEvents.h"
|
|
||||||
#include "WSServer.h"
|
#include "WSServer.h"
|
||||||
|
#include "WSEvents.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "forms/settings-dialog.h"
|
#include "forms/settings-dialog.h"
|
||||||
|
|
||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE("obs-websocket", "en-US")
|
||||||
|
|
||||||
WSEvents *eventHandler;
|
|
||||||
WSServer *server;
|
|
||||||
SettingsDialog *settings_dialog;
|
SettingsDialog *settings_dialog;
|
||||||
|
|
||||||
bool obs_module_load(void)
|
bool obs_module_load(void)
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs-websockets] you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
|
blog(LOG_INFO, "you can haz websockets (version %s)", OBS_WEBSOCKET_VERSION);
|
||||||
|
|
||||||
server = new WSServer(4444);
|
|
||||||
eventHandler = new WSEvents(server);
|
|
||||||
|
|
||||||
obs_frontend_add_save_callback(Config::OBSSaveCallback, Config::Current());
|
|
||||||
|
|
||||||
QAction *menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(obs_module_text("Menu.SettingsItem"));
|
// Core setup
|
||||||
|
Config* config = Config::Current();
|
||||||
|
config->Load();
|
||||||
|
|
||||||
|
WSServer::Instance = new WSServer();
|
||||||
|
WSEvents::Instance = new WSEvents(WSServer::Instance);
|
||||||
|
|
||||||
|
if (config->ServerEnabled)
|
||||||
|
WSServer::Instance->Start(config->ServerPort);
|
||||||
|
|
||||||
|
// UI setup
|
||||||
|
QAction *menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
||||||
|
obs_module_text("OBSWebsocket.Menu.SettingsItem"));
|
||||||
|
|
||||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||||
settings_dialog = new SettingsDialog();
|
QMainWindow* main_window = (QMainWindow*)obs_frontend_get_main_window();
|
||||||
|
settings_dialog = new SettingsDialog(main_window);
|
||||||
obs_frontend_pop_ui_translation();
|
obs_frontend_pop_ui_translation();
|
||||||
|
|
||||||
auto menu_cb = [] {
|
auto menu_cb = [] {
|
||||||
@ -53,11 +61,14 @@ bool obs_module_load(void)
|
|||||||
};
|
};
|
||||||
menu_action->connect(menu_action, &QAction::triggered, menu_cb);
|
menu_action->connect(menu_action, &QAction::triggered, menu_cb);
|
||||||
|
|
||||||
|
// Loading finished
|
||||||
|
blog(LOG_INFO, "module loaded!");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void obs_module_unload()
|
void obs_module_unload()
|
||||||
{
|
{
|
||||||
blog(LOG_INFO, "[obs-websockets] goodbye !");
|
blog(LOG_INFO, "goodbye!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
obs-websocket
|
obs-websocket
|
||||||
Copyright (C) 2016 Stéphane Lepin <stephane.lepin@gmail.com>
|
Copyright (C) 2016-2017 Stéphane Lepin <stephane.lepin@gmail.com>
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,6 +19,10 @@ with this program. If not, see <https://www.gnu.org/licenses/>
|
|||||||
#ifndef OBSWEBSOCKET_H
|
#ifndef OBSWEBSOCKET_H
|
||||||
#define OBSWEBSOCKET_H
|
#define OBSWEBSOCKET_H
|
||||||
|
|
||||||
#define OBS_WEBSOCKET_VERSION "0.3-alpha"
|
#define PROP_AUTHENTICATED "wsclient_authenticated"
|
||||||
|
#define OBS_WEBSOCKET_VERSION "4.1.0"
|
||||||
|
#define API_VERSION 1.3
|
||||||
|
|
||||||
|
#define blog(level, msg, ...) blog(level, "[obs-websocket] " msg, ##__VA_ARGS__)
|
||||||
|
|
||||||
#endif // OBSWEBSOCKET_H
|
#endif // OBSWEBSOCKET_H
|
Reference in New Issue
Block a user