Compare commits

...

363 Commits

Author SHA1 Message Date
e3ad148c15 Request types: get/set/save streaming settings (PR #100)
* Adding support for changing streaming server settings

* Updates after initial code review for customized rtmp settings

* Updating PROTOCOL.MD documentment for streaming service settings

* Changes based on code review
2017-07-06 13:07:06 +02:00
acffacd67d Request/Response: refactor logging code 2017-07-04 21:20:34 +02:00
54a16f4d2f Events: refactor logging code 2017-07-04 21:18:42 +02:00
35cb506d6e Merge pull request #98 from haganbmj/request-logging
General: Toggle to log incoming requests
2017-07-04 20:20:33 +02:00
7675a1ee58 General: Toggle to log outgoing response and events 2017-07-04 14:17:51 -04:00
863f5e28b3 Merge pull request #99 from Rosuav/patch-1
Fix formatting (trivial)
2017-07-03 15:54:19 +02:00
1a6f6096e4 Fix formatting (trivial) 2017-07-03 23:39:42 +10:00
b57982b6cb General: Toggle to log incoming requests 2017-06-29 22:11:51 -05:00
4132356141 Protocol: Add Get and Set Browser Source Properties (#95)
* Protocol: Add Get and Set Browser Source Properties

* address white space - I don't see the indentation issue at my end for protocol.md but I'll try
2017-06-22 18:51:19 +02:00
e560e95310 Update README.md 2017-06-22 17:11:57 +02:00
65f4ff6a30 Protocol: add request types to get/set Text GDI Plus sources (#94)
* Protocol: add request types to get/set Text GDI Plus sources

* address comments
2017-06-20 16:07:47 +02:00
ab38f33530 docs: Update README to add a Python library 2017-06-20 10:54:30 +02:00
1f04ee8252 CI: Fix wrong OS X deps paths 2017-06-20 09:41:07 +02:00
739bd6f696 CI: Fix wrong Qt paths 2017-06-08 12:25:44 +02:00
066145ab31 Server: replace Q_FOREACH with ranged for-loops 2017-06-08 11:18:50 +02:00
54d0f764d4 CI: Fix OS X non-working @rpath replace 2017-06-06 11:57:04 +02:00
1398689ebf CI: Fix OS X Qt 5.8 fetch 2017-06-06 11:09:53 +02:00
647625628d CI: Fix dependency versions 2017-06-06 10:35:36 +02:00
e17df69b80 Merge remote-tracking branch 'origin/master' 2017-06-06 09:50:12 +02:00
f001d18eea Protocol: add request types to get/set current recording folder 2017-06-06 09:50:02 +02:00
f49980350a General: code style refresh 2017-06-06 09:30:07 +02:00
d0a90ecea4 Update PROTOCOL.md 2017-06-06 00:54:32 +02:00
4506b46ba0 Docs: update Contributing Guidelines 2017-05-31 15:29:23 +02:00
a8c36d7366 Docs: add issue template 2017-05-31 15:24:58 +02:00
781eaec683 Docs: add Contributing Guidelines 2017-05-31 15:08:30 +02:00
bdee8f318a Update package-osx.sh 2017-05-30 22:29:06 +02:00
82dac4d208 CI: Create "latest" copy of OS X artifact 2017-05-30 22:06:23 +02:00
386e1f3b46 General: 80 columns refactor in C/C++ headers 2017-04-26 10:07:42 +02:00
a3cbbf3ea9 General: refactor to limit linesize to 80 columns 2017-04-26 10:02:02 +02:00
e647debcfb Handler: fix possible duplicate data release 2017-04-25 08:53:24 +02:00
fba9bd2b76 Auth: refactor CheckAuth and GenerateSecret to avoid hazardous operations 2017-04-24 22:45:08 +02:00
56217e0176 Revert "CMake: work towards more auto-detect on Linux"
This reverts commit 77c5801c4c.
2017-04-24 13:33:50 +02:00
77c5801c4c CMake: work towards more auto-detect on Linux 2017-04-24 13:14:51 +02:00
7db879cca9 Protocol spec: Specify plugin version number 2017-04-24 12:38:53 +02:00
8d0cb2e875 Auth: free string after use 2017-04-24 11:01:11 +02:00
dc7b386295 Create README.md 2017-04-24 10:20:46 +02:00
03c8c6385c Bugfix: wrong behaviour in TransitionToProgram with a specif. transition 2017-04-23 23:28:06 +02:00
841de2f752 Systray: notify disconnects 2017-04-23 19:12:53 +02:00
9de6e229d9 Preliminary work on Recording folder get/set 2017-04-23 18:54:24 +02:00
8dfe471ef2 Server: small refactor 2017-04-23 15:41:39 +02:00
da05f315be Systray: Localizable notification 2017-04-23 15:30:09 +02:00
d014a7ab25 Requests: Start/Stop Streaming/Recording request now have errors 2017-04-23 15:22:10 +02:00
d0118c63c0 Bugfix: PreviewSceneChanged not emitted when swapping scenes 2017-04-23 12:28:51 +02:00
9ddfad99ea Bugfix: calling SetPreviewScene with an unknown source doesn't errors 2017-04-23 12:09:58 +02:00
82d74fcb2f CI: Build plugin with latest known stable release of OBS 2017-04-21 21:20:17 +02:00
0f4f029a76 Tray icon: remove debug message 2017-04-21 17:34:33 +02:00
2c581e9998 Merge remote-tracking branch 'origin/master' 2017-04-21 16:19:59 +02:00
f2028c506a UI: System tray notification on new connections 2017-04-21 16:16:11 +02:00
200db77140 UI: System tray notification on new connections 2017-04-21 15:59:32 +02:00
007604cc21 Readme: changed G-monitor's username 2017-04-21 14:21:07 +02:00
89486e9172 Readme: fix layout again 2017-04-21 14:17:55 +02:00
a60ca96fd1 Readme: update layout 2017-04-21 14:06:14 +02:00
0ade2c869d Readme: fix layout 2017-04-21 14:03:16 +02:00
9986382850 Supporters: add MediaUnit 2017-04-21 14:02:22 +02:00
89b9165c25 Protocol spec: update ToC 2017-04-21 13:42:50 +02:00
5d290165a2 Add request type GetSpecialSources 2017-04-21 13:39:28 +02:00
d267171cc7 Merge remote-tracking branch 'origin/master' 2017-04-21 11:14:56 +02:00
cebe325e81 New request type GetMute + request handler refactor 2017-04-21 11:14:47 +02:00
4e6178881b Update README.md 2017-04-21 08:10:43 +02:00
6bca8194cb Update README.md 2017-04-21 00:28:51 +02:00
a4885f332d README: Add sponsor mention for Support Class 2017-04-21 00:28:20 +02:00
cf97fb2051 Misc: add Support Class logo for sponsor mention 2017-04-20 23:38:43 +02:00
20a8853854 Protocol spec: better readability 2017-04-20 23:26:41 +02:00
dd487a5055 Version bump 2017-04-20 22:58:52 +02:00
72ca07f571 Merge branch 'preview-api' 2017-04-20 22:27:46 +02:00
c7305889c3 Events: add scene description to SwitchScenes and PreviewSceneChanged 2017-04-20 19:41:58 +02:00
bc24497760 Merge branch 'master' of github.com:Palakis/obs-websocket 2017-04-20 17:22:19 +02:00
200e65c730 OS X: Fix locales packaging 2017-04-20 17:22:01 +02:00
b32d8ef1e7 Merge remote-tracking branch 'origin/master' 2017-04-20 17:09:22 +02:00
8cfc613a3d Protocol spec: add missing scene visibility field in spec 2017-04-20 17:09:13 +02:00
5fbc2cbac7 OS X: fix linked libraries paths 2017-04-20 16:44:00 +02:00
9d7a32aa1f OS X: Fixed server threading issue 2017-04-20 16:24:41 +02:00
bcc9ef82d1 Oops, forgot to uncomment something 2017-04-20 16:16:36 +02:00
d6b28191e8 Fix another linker issue in OS X 2017-04-20 15:58:21 +02:00
acd8a9ea69 Travis: don't include unwanted artifacts 2017-04-20 14:35:48 +02:00
d4b8a8ff9e OS X: Fix linker issue 2017-04-20 14:25:56 +02:00
a698f7bdf5 Merge branch 'master' of github.com:Palakis/obs-websocket 2017-04-20 14:00:35 +02:00
16f07ff0c3 Travis OS X Packaging: include QtWebSockets in pkg 2017-04-20 13:58:31 +02:00
ed4526751e Better param checks for Studio Mode request types 2017-04-20 10:06:43 +02:00
e241518f8d Add StudioModeSwitched event 2017-04-20 09:53:31 +02:00
a6677edbf5 Add ability to specify a transition when calling TransitionToProgram 2017-04-19 18:53:09 +02:00
5748e163f8 Added GetPreviewScene and modified GetStudioModeStatus 2017-04-19 15:21:21 +02:00
85fa6b60e2 Update PROTOCOL.md 2017-04-19 15:00:47 +02:00
f0bb941c47 New request type : TransitionToProgram 2017-04-19 14:36:53 +02:00
b7df1e8596 Add SetPreviewScene request type and PreviewSceneChanged event 2017-04-19 13:43:58 +02:00
ff8eda3682 Fixed detection and control behaviour for some functions used by Preview API 2017-04-19 11:50:49 +02:00
8c4bd91c78 New request types to get and set the status of Studio Mode 2017-04-19 11:34:37 +02:00
07c868edcd Preliminary work on the Preview request types 2017-04-19 11:21:10 +02:00
69061869a7 Update README.md 2017-04-17 02:17:45 +02:00
98b2ac9bdc Update BUILDING.md 2017-04-17 02:14:55 +02:00
ef2bbff4e5 Travis: add support for tag package naming 2017-04-17 01:32:22 +02:00
7bb8e56072 Travis: fix OS X Packages project path 2017-04-17 00:56:29 +02:00
df0926f6fd Travis: OS X deploy 2017-04-17 00:52:07 +02:00
75572279a9 Travis: OS X packaging 2017-04-17 00:40:50 +02:00
328c6a0f7c Travis: woops, wrong section 2017-04-16 05:12:00 +02:00
ecd5062975 Travis: allow all branches for Linux deploy 2017-04-16 05:03:44 +02:00
c01ae5610d Travis: pass tag env var to docker container 2017-04-16 04:55:03 +02:00
564e7f31c3 Travis: better idea 2017-04-16 04:42:25 +02:00
efa61952b3 Travis: better file glob 2017-04-16 04:35:08 +02:00
ae27a26ebe Travis: fix checkinstall issue with version numbers 2017-04-16 04:19:22 +02:00
d81076f720 Fix Travis deploy behaviour 2017-04-16 03:54:19 +02:00
528f16c5e1 Travis: different approach for Linux builds upload 2017-04-16 03:43:25 +02:00
13ac8bfa90 Travis: testing the contents of the folder but outside of the container 2017-04-16 03:11:53 +02:00
935c58b17b Travis: trying packaging in another dir for upload 2017-04-16 01:32:32 +02:00
31e133bf06 Travis: gosh 2017-04-16 01:22:26 +02:00
eb7fb6694c Travis: okay, trying another strategy to get rid of build failures 2017-04-16 00:58:46 +02:00
a298577da1 Travis: wrong path for artifacts 2017-04-16 00:43:20 +02:00
8cd6d43ec4 Travis: wth happens with checkinstall builds 2017-04-16 00:27:02 +02:00
b8fd143cc1 Travis Linux: fix possible permissions issue with the built package 2017-04-15 23:15:04 +02:00
045ae058a3 Travis: Linux upload still not working 2017-04-15 22:56:38 +02:00
52c9816db2 Travis: trying again 2017-04-15 22:42:58 +02:00
32440580f6 Travis: dynamically find artifacts to upload 2017-04-15 22:30:41 +02:00
906d986f4b Travis: trying to make artifact upload working 2017-04-15 22:20:03 +02:00
0198651ca0 Travis: trying to resolve the issue with failing Linux packaging 2017-04-15 22:09:24 +02:00
d911c40897 Travis: fix file permissions (again) 2017-04-15 21:58:58 +02:00
ca8a117335 Travis: renamed some scripts 2017-04-15 21:49:41 +02:00
f75e2f0ada Travis: push build artifacts to S3 2017-04-15 20:11:45 +02:00
ea12b62235 Fix typo in Protocol spec 2017-04-15 19:27:37 +02:00
0af4af7e50 Merge remote-tracking branch 'origin/master' 2017-04-15 17:15:28 +02:00
f017491f44 Travis: fix Debian package version 2017-04-15 17:08:34 +02:00
ed433d3312 Travis: fix script permission 2017-04-15 16:45:21 +02:00
9ba2e83857 Fixed typos in CMakeLists.txt 2017-04-15 15:52:15 +02:00
af0ec74704 Travis: Linux packaging (again) 2017-04-15 15:40:34 +02:00
251402e844 Merge remote-tracking branch 'origin/master' 2017-04-15 15:24:56 +02:00
1a5318ebe0 Travis: Linux packaging 2017-04-15 15:24:46 +02:00
15aac3f436 Update README.md 2017-04-15 13:59:41 +02:00
5f373632e6 Merge pull request #67 from inpothet/master
Dutch Locale
2017-04-15 13:58:19 +02:00
0d518bf8df Dutch Locale 2017-04-15 13:45:44 +02:00
16b735c3a4 Travis: Some preliminary work on OS X packaging 2017-04-15 13:28:18 +02:00
1239f094fd Trying something 2017-04-15 13:00:16 +02:00
6986fa51eb Travis: keep only the Docker method for Linux builds
This avoids hacks and allows us to use a recent Ubuntu release even of Travis' Ubuntu infrastructure
2017-04-15 01:26:27 +02:00
f4bc88bf73 Travis: remove possibly useless line 2017-04-15 01:24:31 +02:00
ad915536a3 Travis: Removed debug ls command 2017-04-15 01:21:23 +02:00
f48b31664e Trying with absolute paths 2017-04-15 01:08:48 +02:00
d6c0eb1998 Trying something else 2017-04-15 00:50:31 +02:00
d3dcfba463 Trying something 2017-04-15 00:42:47 +02:00
3619dcd777 Merge remote-tracking branch 'origin/master' 2017-04-14 23:41:59 +02:00
86c002e318 Okay, rolling back some changes 2017-04-14 23:41:25 +02:00
42a807ce9c Okay, rolling back some changes 2017-04-14 23:40:16 +02:00
9bfff9539e Meh, not working as expected 2017-04-14 23:27:39 +02:00
edf4d919fe Encore a fix for Travis OS X 2017-04-14 23:12:36 +02:00
b0fb3d8c61 Fix for Travis OS X 2017-04-14 22:47:37 +02:00
5143090142 Work on OS X Travis builds 2017-04-14 22:38:57 +02:00
bc1555ca5b Update before-script-osx.sh 2017-04-14 22:19:48 +02:00
146a392f1a Merge remote-tracking branch 'asquelt/master' 2017-04-14 22:11:24 +02:00
e2fc205a35 Update README.md 2017-04-14 21:25:41 +02:00
d4bdb216d2 verifying libobs location 2017-04-14 15:59:47 +02:00
bffc1d6a1f verifying libobs location 2017-04-14 15:51:11 +02:00
df0d890c11 Fixed typo 2017-04-14 15:46:10 +02:00
dceee4dede verifying libobs location 2017-04-14 15:39:14 +02:00
dedbbf12bb Implement #63 2017-04-14 15:36:05 +02:00
bdb53d07f5 upstream proposed fix for #59 2017-04-14 15:21:33 +02:00
547991df72 Try to do something for #59 2017-04-14 15:11:53 +02:00
fd17557a96 try to build osx version 2017-04-12 15:20:22 +02:00
a9e928a799 try to build osx version 2017-04-12 15:08:49 +02:00
84b3e086e6 try to build osx version 2017-04-12 14:38:12 +02:00
5e3c794aec try to build osx version 2017-04-11 14:16:10 +02:00
98acbb2b2b try to build osx version 2017-04-11 13:45:07 +02:00
318f0753b0 try to build osx version 2017-04-11 13:25:08 +02:00
8908adbdee try to build osx version 2017-04-11 13:07:44 +02:00
fad7a26550 try to build osx version 2017-04-11 12:49:48 +02:00
38005fc0ef Merge pull request #60 from asquelt/locale
Polish locale
2017-04-10 12:59:43 +02:00
e3d3181ad6 try to build osx version 2017-04-06 17:43:55 +02:00
83d069e4d9 try to build osx version 2017-04-06 17:42:51 +02:00
f2db5a8229 try to build osx version 2017-04-06 17:07:14 +02:00
6ebb777256 Polish locale 2017-04-06 16:27:57 +02:00
db50c531d9 try docker 2017-04-06 13:26:40 +02:00
218f7af2e3 try docker 2017-04-06 13:19:33 +02:00
a19d2956c1 try docker 2017-04-06 13:07:42 +02:00
7a6b5b965d try docker 2017-04-06 13:01:09 +02:00
8a871aaddd try docker 2017-04-06 12:56:59 +02:00
bec0a0df10 try docker 2017-04-06 12:52:57 +02:00
1068a2dfbe try docker 2017-04-06 12:47:57 +02:00
b62be5d584 try docker 2017-04-06 12:44:57 +02:00
9686019693 try docker 2017-04-06 12:38:59 +02:00
32a066b4d4 try docker 2017-04-06 12:31:49 +02:00
d5a415e01e try to build osx version 2017-04-06 11:46:39 +02:00
33f83b8486 try to build osx version 2017-04-06 11:34:10 +02:00
adedf8d34f try to build osx version 2017-04-06 11:23:30 +02:00
9243071f03 try to build osx version 2017-04-06 11:12:05 +02:00
36050850d4 try to build osx version 2017-04-06 11:01:03 +02:00
c6d5cc555a try to build osx version 2017-04-05 13:33:36 +02:00
b271aa2aaa try to build osx version 2017-04-05 13:19:57 +02:00
d24961b3b9 try to build osx version 2017-04-05 12:59:07 +02:00
e52543efe7 try to build osx version 2017-04-05 09:34:15 +02:00
0b0e27ad3b try to build osx version 2017-04-05 00:41:35 +02:00
9f460f6c99 try to build osx version 2017-04-05 00:36:07 +02:00
c4529bb9a3 try to build osx version 2017-04-05 00:01:53 +02:00
5a069d2ffc try to build osx version 2017-04-04 23:24:07 +02:00
4fc78e455a Merge branch 'master' of github.com:asquelt/obs-websocket 2017-04-04 22:57:22 +02:00
75e2198315 try to build osx version 2017-04-04 22:57:16 +02:00
0d4bb4ed2d try to build osx version 2017-04-04 22:53:38 +02:00
577738ad0a try to build osx version 2017-04-04 21:43:19 +02:00
a8dfdb03fb try to build osx version 2017-04-04 21:08:31 +02:00
aebd470d49 try to build osx version 2017-04-04 20:28:39 +02:00
ce3dfd9678 try to build osx version 2017-04-04 20:20:31 +02:00
b82801b145 try to build osx version 2017-04-04 19:32:07 +02:00
a6ab35f1fb Revert "try to build osx version and xenial docker version"
This reverts commit a265cea4fb.
2017-04-04 19:03:52 +02:00
a265cea4fb try to build osx version and xenial docker version 2017-04-04 18:56:05 +02:00
166760651e try to build osx version and xenial docker version 2017-04-04 18:53:02 +02:00
41a9191223 try to build osx version and xenial docker version 2017-04-04 18:47:21 +02:00
1465e7760e try to build osx version 2017-04-04 18:20:35 +02:00
1a043f1dc0 try travis build with libqt5websockets5-dev binary 2017-04-04 17:34:54 +02:00
70c5b00c90 try travis build with libqt5websockets5-dev binary 2017-04-04 17:22:32 +02:00
bb5177dc79 debug 2017-04-04 17:06:40 +02:00
f60a9a632b try travis build with xenial (16.04) 2017-04-04 16:29:53 +02:00
024c47132b try travis build with qt532-trusty installed 2017-04-04 16:21:24 +02:00
73ce9cb4cc try travis build with qtbase5-private-dev installed 2017-04-04 15:51:44 +02:00
f45d439094 try travis build with qtcreator installed 2017-04-04 14:10:32 +02:00
2d5749a78c New request type : GetTransitionDuration 2017-03-19 23:57:01 +01:00
f078a10028 New request types for streaming and recording control
`StartStreaming`, `StopStreaming`, `StartRecording` and `StopRecording`
2017-03-19 23:50:23 +01:00
701f88e532 Added "SetSceneItemCrop" request type 2017-03-19 23:38:26 +01:00
1524a1997d Update README.md 2017-03-15 20:35:57 +01:00
b16f812f91 Update README.md
Mentions to contributors and added a link to my C# library
2017-03-15 19:55:38 +01:00
cde307d644 Update README.md 2017-03-15 16:29:17 +01:00
c47f6d093c Create BUILDING.md 2017-03-15 16:28:09 +01:00
f5336938c9 Update README.md 2017-03-12 11:57:41 +01:00
4985548edf Update pt-BR.ini 2017-03-10 08:33:39 +01:00
8bc4841e84 Merge pull request #53 from laris151/laris151-portuguese
Add Portuguese Translate
2017-03-10 08:32:46 +01:00
634f9833a1 Add Portuguese Translate 2017-03-09 22:28:04 -03:00
80052e62ee Update README.md 2017-03-05 17:10:18 +01:00
0672b6a157 Fixed typo in PROTOCOL.md 2017-03-02 15:55:46 +01:00
1e71bfa151 Fixed a crash when switching to another scene after switching to another scene collection 2017-03-02 15:36:48 +01:00
d4c2c8197a Merge branch 'master' of github.com:Palakis/obs-websocket 2017-03-02 15:07:11 +01:00
c9baed2df9 Fixes #24 2017-03-02 15:02:26 +01:00
1e2065c84a Merge pull request #50 from G-monitor/chinese
Add chinese translation
2017-03-02 14:24:28 +01:00
537d683dfb Add chinese translation 2017-03-02 17:46:10 +08:00
e6f1b9f8c8 Travis: trying something 2017-03-02 01:08:57 +01:00
5815d2bfce Lower minimum cmake version 2017-03-02 00:49:35 +01:00
7692e93306 Travis: install Qt Websockets from source 2017-03-02 00:44:23 +01:00
8768b83251 Added execute permission to Travis CI scripts 2017-03-02 00:29:36 +01:00
35d18810fc Setting up Travis CI 2017-03-02 00:25:58 +01:00
ccd40a1834 Update README.md 2017-03-01 23:29:13 +01:00
5c1f0c3541 CI: Move cached deps outside of obs-studio source dir 2017-03-01 22:46:17 +01:00
e237e52ae4 CI: Force Qt 5.7.0 like OBS Releases 2017-03-01 22:43:29 +01:00
effec90528 More fixes for appveyor 2017-03-01 22:13:34 +01:00
de4a2247c0 CI: Change MSVC version 2017-03-01 22:06:07 +01:00
b20b7cbc98 Appveyor artifact name change 2017-03-01 22:02:06 +01:00
3c347b9b77 Oops, typo 2017-03-01 21:50:20 +01:00
794c4066d8 Refactored appveyor.yml 2017-03-01 21:49:42 +01:00
8c8e3072a7 Fix appveyor artifact packaging 2017-03-01 21:45:40 +01:00
228708eec4 Added update type SceneItemVisibilityChanged 2017-03-01 21:41:42 +01:00
dcafaedaa8 What the hell happens with LibObs_DIR 2017-03-01 21:22:14 +01:00
a85297f2c8 CI: Build artifact from obs-websocket release folder 2017-03-01 21:12:37 +01:00
91f7450cbd CI Fix 2017-03-01 21:07:50 +01:00
e7b074991d Fix appveyor CI + fix build script 2017-03-01 20:59:35 +01:00
5f83ce2a28 Setting up Appveyor CI 2017-03-01 20:46:21 +01:00
44af896dee Force C++11 on Linux 2017-03-01 17:53:44 +01:00
9eaa9a98ee Use UTF-8 strings for the password 2017-03-01 15:42:38 +01:00
629880cd58 Update README.md 2017-02-28 20:40:51 +01:00
42266ed14f Fixes #17 2017-02-28 16:56:03 +01:00
9dd7a197e4 Update PROTOCOL.md 2017-02-28 16:05:41 +01:00
6f39da20a9 Removed TransitionEnd event + changed TransitionBegin triggering 2017-02-28 16:01:06 +01:00
1ebb6f9257 Transition events + scene change compatibility with OBS 18.0.0 2017-02-28 15:40:40 +01:00
e30e982ef0 Added update type TransitionDurationChanged 2017-02-27 13:24:25 +01:00
42a80c6185 Ability to get and set transition duration 2017-02-27 11:53:44 +01:00
0d495f4d65 Merge pull request #46 from mikhailswift/add_scene_name_to_other_source_commands
Add scene name to SetSceneItemPosition and SetSceneItemTransform
2017-02-27 00:08:06 +01:00
71f5e66bd1 Added back accidental nuked new line 2017-02-26 18:03:20 -05:00
a6f71b68f3 Update protocol.md 2017-02-26 18:02:46 -05:00
a527f343cd Moved some repeated code to utils, added source_name to other commands 2017-02-26 18:01:09 -05:00
f8e1c454d9 Merge pull request #45 from mikhailswift/toggle_source_on_specified_scene
Toggle source on specified scene
2017-02-26 23:24:02 +01:00
da6e55f09f Used same error message elsewhere, fixed null check 2017-02-26 17:13:50 -05:00
d277e2788a Fixed missed changes from currentScene -> scene 2017-02-26 16:40:29 -05:00
53ba747b78 Update protocol readme for added parameter 2017-02-26 16:36:38 -05:00
b9862acd1d Add scene-name optional parmeter to set source render command 2017-02-26 16:35:08 -05:00
d1c19382a1 Fixes #34 2017-02-26 19:46:09 +01:00
4141983ccd Fixed a potential bug + WIP on scene collection/profile change 2017-02-26 17:04:48 +01:00
2b8b5001e0 Merge branch 'master' of github.com:Palakis/obs-websocket 2017-02-25 17:43:52 +01:00
532126561e Rollback changes : my dev environment had the wrong Qt version (5.7.0-1 instead of 5.7.0) 2017-02-25 17:42:58 +01:00
3c026c4eef Update README.md 2017-02-25 17:33:46 +01:00
ae6c15158f Bugfix : copy issue in CMakeLists.txt 2017-02-25 16:39:55 +01:00
6aa57247f9 Better build instructions (including release packager on Windows) 2017-02-25 16:32:20 +01:00
50862ac945 Typo fix 2017-02-25 14:48:09 +01:00
01ce6faa20 Add german translation to CMakeLists.txt 2017-02-24 20:33:45 +01:00
6e571aef95 Fixed issue with exception "Must construct a QApplication before a QWidget" 2017-02-24 20:26:19 +01:00
d6091c83e2 A bit of cleaning 2017-02-24 20:09:42 +01:00
5ca55fc13e sprintf is more cross-platform 2017-02-24 18:30:04 +01:00
2b60da02e8 sprintf_s() is not cross-platform 2017-02-24 17:40:41 +01:00
f02297152c Update Utils.h 2017-02-24 17:37:44 +01:00
af7c0bbc72 Removed a log message with a password in it 2017-02-24 00:50:16 +01:00
40727d5a6c Changed config key names from snake_case to CamelCase 2017-02-23 22:40:11 +01:00
9bdd73dbc6 Merge pull request #41 from Frahmer/german-translation-2
Add german translation
2017-02-23 22:34:22 +01:00
3e1ed09f12 Update README.md 2017-02-23 22:31:56 +01:00
3682c625d7 Add german translation
Add "ServerEnable" and "ServerPort" translation.
2017-02-23 22:30:08 +01:00
70cd52ac95 Update README.md 2017-02-23 22:29:42 +01:00
3264da4b2f Update README.md 2017-02-23 22:29:24 +01:00
06c1648f55 Updated PROTOCOL.md : new table of contents 2017-02-23 22:21:06 +01:00
94c3e5d41d Update PROTOCOL.md : better lisibility 2017-02-23 22:07:14 +01:00
7ae20d8c3b Semver : version number row left shift to 4.0.0 2017-02-23 21:36:00 +01:00
0ff4411abf Bugfix : deadlock when closing the server
Caused by a non-recursive mutex
2017-02-23 21:25:26 +01:00
3d68b7c9e5 Updated locale variables names + WIP Dynamic Server Settings 2017-02-23 21:01:24 +01:00
f8b1cae0c9 Merge pull request #40 from Frahmer/german-translation
Add german translation
2017-02-23 18:28:54 +01:00
5bee0fc453 Add german translation 2017-02-22 19:30:15 +01:00
7162765824 Fix compiler warning 2017-02-20 23:03:00 +01:00
99aa6be887 Updated PROTOCOL.md and fixed some types 2017-02-17 17:01:25 +01:00
afaaff298f Fixes #35 2017-02-17 11:41:41 +01:00
68cf9af6a3 Provide a item's base size in its infos 2017-02-17 11:25:46 +01:00
8d5752d6b5 Version bump to the next release during dev 2017-02-15 20:18:06 +01:00
98dbcc4c69 Copyright update and credit where it's due 2017-02-15 18:33:32 +01:00
687d8fd120 PROTOCOL.md fix 2017-02-15 18:27:37 +01:00
114ace23f7 Added OBS version number in the response fields of GetVersion 2017-02-15 18:25:44 +01:00
3fbc221db0 Style changes 2017-02-15 17:09:34 +01:00
f2e6e137a6 Fixed #21 2017-02-15 11:35:35 +01:00
bb232f1b3e Merge pull request #37 from mikhailswift/add_mute_source_command
Add mute source commands
2017-02-13 21:17:16 +01:00
9dc153bc22 Bugfix : std's map and set can cause crashes 2017-02-13 19:03:52 +01:00
b9bbdf5978 Renamed pClient to pHandler 2017-02-13 18:25:07 +01:00
e0db0e394d Added missing return statements to ToggleMute and SetMute handlers 2017-02-13 12:09:44 -05:00
3e9001721e Fixed incorrect header level for SetMute and ToggleMute 2017-02-13 12:08:36 -05:00
bbf3b0f86f updated mistake in protocol.md 2017-02-12 23:06:00 -05:00
7f3eb9f11b update spaces to tabs in wsrequesthandler for toggle and set mute 2017-02-12 22:54:29 -05:00
c783c51915 update spaces to tabs in wsrequesthandler for set and toggle mute 2017-02-12 22:52:20 -05:00
0816d222c6 Fixed mistake in ToggleMute 2017-02-12 17:55:06 -05:00
af16c70143 Added ToggleMute and SetMute handlers, updated PROTOCOL.md 2017-02-12 18:46:02 +00:00
748b6f6e2e Protect clients object list with a QMutex 2017-02-12 19:20:32 +01:00
3bd600ed52 Preliminary work on event timestamping 2017-02-05 02:00:21 +01:00
78e6ad0f59 Fixes #27 2017-02-05 01:46:38 +01:00
2d71dc68f1 WTH is this 2017-02-05 00:36:55 +01:00
7dc2a00d47 Fixes #10 and #28 2017-02-04 21:38:19 +01:00
7ece78a05b Merge pull request #33 from haganbmj/master
[Protocol] Add OnTransitionChange/OnTransitionListChange (Fixes #19)
2017-02-04 19:06:01 +01:00
ff2bace1bf [Protocol] Add OnTransitionChange/OnTransitionListChange (Fixes #19) 2017-02-03 01:30:54 -05:00
e63ce01bdc Update README.md 2017-01-17 14:07:04 +01:00
4439ce71d0 Really fixed IP address issue 2016-12-13 00:08:32 +01:00
27ec094775 Fixed char pointer issue in log messages 2016-12-12 21:08:59 +01:00
1bed53e07b Changed websocket server name 2016-12-12 18:53:41 +01:00
8ae06c0c4f Update README.md 2016-12-10 21:51:51 +01:00
372db7865b Update README.md 2016-12-10 21:50:19 +01:00
cfeffc551e Bumped installer version 2016-12-10 21:24:22 +01:00
d519815a7e Bump to 0.3.2 2016-12-10 21:15:24 +01:00
32932eacf5 Fixes #18 2016-12-04 20:38:49 +01:00
1a057cf5a3 Merge branch 'master' of github.com:Palakis/obs-websocket 2016-12-04 20:24:20 +01:00
e3936dad9b Updated installer 2016-12-04 20:23:59 +01:00
4ec9b85506 Update README.md 2016-12-01 12:43:56 +01:00
fbae081c33 Added installer 2016-11-30 20:49:08 +01:00
cfb490b7e3 Bump version to 0.3.1 2016-11-30 20:05:20 +01:00
1947a3cfd5 Fixes #14 2016-11-30 20:04:24 +01:00
fca6774d4a Fixes #13 2016-11-30 18:54:46 +01:00
5fd1e978ec Fixed more memory leaks and crashes 2016-11-30 11:33:08 +01:00
4985c549dd Fixed #12 and identified several memory leaks 2016-11-30 11:09:29 +01:00
e6c378aef0 New Linux build instructions 2016-11-30 09:10:04 +01:00
0657835ba3 Update README.md 2016-11-29 14:52:05 +01:00
36934de04a Described authentication procedure in PROTOCOL.md 2016-11-28 16:01:20 +01:00
e735ab80b1 Better PROTOCOL.md 2016-11-28 11:55:20 +01:00
9904ca89d4 Fixes #11 2016-11-27 23:50:59 +01:00
554ab54690 Security fix : don't broadcast messages to unauthenticated clients 2016-11-27 17:40:57 +01:00
5abcd18ba0 Fixes #8 and #9 2016-11-27 17:32:37 +01:00
03bf41b250 Update README.md 2016-11-27 16:12:52 +01:00
0df6bde5cb Update README.md 2016-11-27 16:11:19 +01:00
e3a28b640f Update CMakeLists.txt 2016-11-23 21:45:25 +01:00
94576fae44 Better build process on Windows 2016-11-22 22:02:46 +01:00
d091d9c3e4 Better build process on Linux 2016-11-22 19:51:47 +01:00
01c9e48f4f Update README.md 2016-11-22 18:40:27 +01:00
5639c18c2f Wth did i do 2016-11-22 18:34:19 +01:00
25b835f4d8 Update README.md 2016-11-22 17:43:56 +01:00
78fb5b3093 Update README.md 2016-11-22 17:38:30 +01:00
73b85ef9df Update CMakeLists.txt 2016-11-22 17:38:04 +01:00
93c604a639 Update README.md 2016-11-22 17:01:29 +01:00
053676a320 Update PROTOCOL.md 2016-11-22 10:11:52 +01:00
d1a142cefd Fixed typo 2016-11-22 10:05:35 +01:00
36cf2a4126 Update README.md 2016-11-20 23:31:07 +01:00
a3bc9ae8d0 Update README.md 2016-11-20 23:24:47 +01:00
6d77ae24b3 Merge pull request #7 from haganbmj/master
[API-docs] Update Event/Request Fields
2016-11-20 04:32:46 +01:00
d9bbed5f69 [API-docs] Update Event/Request Fields 2016-11-19 21:06:47 -05:00
d6ce405e71 GetVersion : don't get OBS Studio's version number at compile time... 2016-11-19 17:58:13 +01:00
42 changed files with 5494 additions and 614 deletions

22
.github/CONTRIBUTING.md vendored Normal file
View 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
View 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.*

4
.gitignore vendored
View File

@ -1,3 +1,7 @@
*~
.DS_Store
/build/ /build/
/build32/ /build32/
/build64/ /build64/
/release/
/installer/Output/

55
.travis.yml Normal file
View 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
View 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 : [![Automated Build status for Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history)
- Linux & OS X : [![Automated Build status for Linux & OS X](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](https://travis-ci.org/Palakis/obs-websocket)

11
CI/build-osx.sh Executable file
View 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
View 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
View 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 /

View 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

View 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
View 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
View 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/*

View File

@ -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 --

View File

@ -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;
}
} }
void Config::OBSSaveCallback(obs_data_t *save_data, bool saving, void *private_data) { bfree(hash);
Config *conf = static_cast<Config *>(private_data); bfree(expected_response);
return authSuccess;
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() { Config* Config::Current()
{
return _instance; return _instance;
} }

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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 [![Gitter chat](https://badges.gitter.im/obs-websocket/obs-websocket.png)](https://gitter.im/obs-websocket/obs-websocket) [![Build Status - Windows](https://ci.appveyor.com/api/projects/status/github/Palakis/obs-websocket)](https://ci.appveyor.com/project/Palakis/obs-websocket/history) [![Build Status - Linux & OS X](https://travis-ci.org/Palakis/obs-websocket.svg?branch=master)](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.
[![Support Class](doc/supportclass_logo_blacktext.png)](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.
[![MediaUnit](doc/mediaunit_logo_black.png)](http://www.mediaunit.no/)

477
Utils.cpp
View File

@ -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* 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_array_t* data = static_cast<obs_data_array_t* >(param);
obs_data_array_push_back(data, GetSceneItemData(currentItem));
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_source_t* item_source = obs_sceneitem_get_source(item);
float item_width = float(obs_source_get_width(item_source));
float item_height = float(obs_source_get_height(item_source));
obs_data_t* data = obs_data_create(); 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, "name",
obs_data_set_string(data, "type", obs_source_get_id(obs_sceneitem_get_source(item))); obs_source_get_name(obs_sceneitem_get_source(item)));
obs_data_set_double(data, "volume", obs_source_get_volume(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)); const char* currentItemName =
if (strcmp(currentItemName, search->query) == 0) { 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;
}

53
Utils.h
View File

@ -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

View File

@ -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,99 +17,366 @@ 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;
} }
WSEvents::~WSEvents() { return false;
}
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::deferredInitOperations()
{
obs_source_t* transition = obs_frontend_get_current_transition();
connectTransitionSignals(transition);
obs_source_release(transition);
obs_source_t* scene = obs_frontend_get_current_scene();
connectSceneSignals(scene);
obs_source_release(scene);
}
void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private_data) void WSEvents::FrontendEventHandler(enum obs_frontend_event event, void* private_data)
{ {
WSEvents* owner = static_cast<WSEvents*>(private_data); WSEvents* owner = static_cast<WSEvents*>(private_data);
// TODO : implement SourceChanged, SourceOrderChanged and RepopulateSources if (!owner->_srv)
return;
if (event == OBS_FRONTEND_EVENT_SCENE_CHANGED) { // 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);
}
if (!transition_is_cut(transition))
{
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_data_t* data = obs_data_create();
obs_data_set_string(data, "scene-name", name);
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);
@ -118,14 +386,16 @@ 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);
@ -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; uint64_t bytes_between = bytes_sent - _lastBytesSent;
double timePassed = double(bytesSentTime - _lastBytesSentTime) / 1000000000.0; double time_passed =
double(bytes_sent_time - _lastBytesSentTime) / 1000000000.0;
uint64_t bitsPerSec = bitsBetween / timePassed; uint64_t bytes_per_sec = bytes_between / time_passed;
uint64_t bytesPerSec = bitsPerSec / 8;
_lastBytesSent = bytesSent; _lastBytesSent = bytes_sent;
_lastBytesSentTime = bytesSentTime; _lastBytesSentTime = bytes_sent_time;
uint64_t totalStreamTime = (os_gettime_ns() - _streamStartTime) / 1000000000; uint64_t totalStreamTime =
(os_gettime_ns() - _stream_starttime) / 1000000000;
int total_frames = obs_output_get_total_frames(stream_output);
int dropped_frames = obs_output_get_frames_dropped(stream_output);
float strain = obs_output_get_congestion(stream_output);
obs_data_t* data = obs_data_create(); obs_data_t* data = obs_data_create();
obs_data_set_bool(data, "streaming", streamingActive); obs_data_set_bool(data, "streaming", streaming_active);
obs_data_set_bool(data, "recording", recordingActive); obs_data_set_bool(data, "recording", recording_active);
obs_data_set_bool(data, "preview-only", false); // Retrocompat with OBSRemote obs_data_set_int(data, "bytes-per-sec", bytes_per_sec);
obs_data_set_int(data, "bytes-per-sec", bytesPerSec); // BUG : Computation seems buggy obs_data_set_int(data, "kbits-per-sec", (bytes_per_sec * 8) / 1024);
obs_data_set_double(data, "strain", 0.0); // dafuq is strain
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);
} }

View File

@ -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,10 +20,8 @@ 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
@ -30,22 +29,54 @@ 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

File diff suppressed because it is too large Load Diff

View File

@ -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,11 +20,11 @@ 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
@ -31,45 +32,85 @@ class WSRequestHandler : public QObject
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:
static obs_service_t* _service;
QWebSocket* _client; QWebSocket* _client;
bool _authenticated;
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 *owner); static void HandleGetVersion(WSRequestHandler* req);
static void HandleGetAuthRequired(WSRequestHandler *owner); static void HandleGetAuthRequired(WSRequestHandler* req);
static void HandleAuthenticate(WSRequestHandler *owner); static void HandleAuthenticate(WSRequestHandler* req);
static void HandleSetCurrentScene(WSRequestHandler *owner); static void HandleSetCurrentScene(WSRequestHandler* req);
static void HandleGetCurrentScene(WSRequestHandler *owner); static void HandleGetCurrentScene(WSRequestHandler* req);
static void HandleGetSceneList(WSRequestHandler *owner); static void HandleGetSceneList(WSRequestHandler* req);
static void HandleSetSourceRender(WSRequestHandler *owner);
static void HandleGetStreamingStatus(WSRequestHandler *owner); static void HandleSetSceneItemRender(WSRequestHandler* req);
static void HandleStartStopStreaming(WSRequestHandler *owner); static void HandleSetSceneItemPosition(WSRequestHandler* req);
static void HandleStartStopRecording(WSRequestHandler *owner); static void HandleSetSceneItemTransform(WSRequestHandler* req);
static void HandleSetSceneItemCrop(WSRequestHandler* req);
static void HandleGetTransitionList(WSRequestHandler *owner); static void HandleGetStreamingStatus(WSRequestHandler* req);
static void HandleGetCurrentTransition(WSRequestHandler *owner); static void HandleStartStopStreaming(WSRequestHandler* req);
static void HandleSetCurrentTransition(WSRequestHandler *owner); 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 HandleSetTextGDIPlusProperties(WSRequestHandler* req);
static void HandleGetTextGDIPlusProperties(WSRequestHandler* req);
static void HandleSetBrowserSourceProperties(WSRequestHandler* req);
static void HandleGetBrowserSourceProperties(WSRequestHandler* req);
}; };
#endif // WSPROTOCOL_H #endif // WSPROTOCOL_H

View File

@ -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")));
} }
} }

View File

@ -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,28 +21,34 @@ 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;
QMutex _clMutex;
QThread* _serverThread; QThread* _serverThread;
}; };

50
appveyor.yml Normal file
View 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
View 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"

View File

@ -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:"

View File

@ -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
View 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
View 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
View 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
View 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
View 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="密碼"

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -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,69 +17,94 @@ 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) { if (strcmp(Config::Current()->Secret, "") != 0)
Config::Current()->AuthRequired = true; conf->AuthRequired = true;
else
conf->AuthRequired = false;
} }
else { else
Config::Current()->AuthRequired = false; {
} conf->AuthRequired = false;
}
else {
Config::Current()->AuthRequired = false;
} }
obs_frontend_save(); conf->Save();
if (conf->ServerEnabled)
WSServer::Instance->Start(conf->ServerPort);
else
WSServer::Instance->Stop();
} }
SettingsDialog::~SettingsDialog() SettingsDialog::~SettingsDialog()

View File

@ -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

View File

@ -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,24 +28,64 @@
</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>
</widget>
</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> </property>
</widget> </widget>
</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
View 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;

View File

@ -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); // Core setup
eventHandler = new WSEvents(server); Config* config = Config::Current();
config->Load();
obs_frontend_add_save_callback(Config::OBSSaveCallback, Config::Current()); WSServer::Instance = new WSServer();
WSEvents::Instance = new WSEvents(WSServer::Instance);
QAction *menu_action = (QAction*)obs_frontend_add_tools_menu_qaction(obs_module_text("Menu.SettingsItem")); 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!");
} }

View File

@ -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