Skip to content

Commit 542f4e6

Browse files
committed
Add RFC: Move to MVC
1 parent e5c925b commit 542f4e6

File tree

1 file changed

+348
-0
lines changed

1 file changed

+348
-0
lines changed

text/0053-move-to-mvc.md

Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
# Summary
2+
3+
- Apply the MVC pattern
4+
- Global config, profile and scene collections are represented by their own object
5+
- Frontend API functions related to config file are replaced
6+
7+
# Motivation
8+
9+
The actual code is almost a chimera of software architectural patterns, applying the MVC pattern will allow to untangle the code and make it more clear.
10+
11+
# Design
12+
13+
**The naming of new types is not final.**
14+
15+
Flowchart diagrams will be used as illustration so the following is the key for those:
16+
- Parallelogram nodes represent concepts/abstraction.
17+
- Rectangle nodes represent a piece of software (code, classes, API…).
18+
- If with round edges, a user can see it and/or interract with it.
19+
<!-- Hexagon nodes represent plugins-->
20+
21+
This is a diagram representing basic MVC:
22+
```mermaid
23+
flowchart LR
24+
user(["User(s)"])
25+
model[/OBS Model/]
26+
view[/OBS View/]
27+
controller[/OBS Controller/]
28+
29+
user-- "interact with" -->view
30+
view-- "user action" -->controller
31+
controller-- update -->view
32+
controller-- manipulate -->model
33+
model-- notify -->controller
34+
```
35+
---
36+
37+
The following is the same diagram but considering splitting the model in three like it is in OBS Studio (global, profile, scene collection):
38+
```mermaid
39+
flowchart LR
40+
user(["User(s)"])
41+
view[/OBS View/]
42+
controller[/OBS Controller/]
43+
44+
globconf[/Global Config/]
45+
profile[/Profile/]
46+
scenecol[/Scene Collection/]
47+
48+
user-- "interact with" -->view
49+
view-- "user action" -->controller
50+
controller-- "update" -->view
51+
52+
controller-- manipulate -->globconf
53+
controller-- manipulate -->profile
54+
controller-- manipulate -->scenecol
55+
scenecol-- notify -->controller
56+
57+
```
58+
---
59+
60+
If those concept are replaced by their matching elements in the actual OBS Studio (without considering the frontend API), it looks like that:
61+
```mermaid
62+
flowchart LR
63+
user(["User(s)"])
64+
ui("OBS Studio UI")
65+
66+
globconf["globalConfig (ConfigFile)"]
67+
68+
subgraph profile [Profile]
69+
basicconf["basicConfig (ConfigFile)"]
70+
streamenc["streamEncoder (OBSData)"]
71+
recordenc["recordEncoder (OBSData)"]
72+
service["service (OBSService)"]
73+
end
74+
75+
user-- "interact with" -->ui
76+
ui-- "user action" -->ui
77+
ui-- "update view" -->ui
78+
79+
ui-- manipulate -->globconf
80+
ui-- manipulate --> basicconf
81+
ui-- manipulate --> streamenc
82+
ui-- manipulate --> recordenc
83+
ui-- manipulate --> service
84+
ui-- "manipulate scene collection" -->ui
85+
ui-- "scene collection notify" -->ui
86+
```
87+
88+
"*OBS Studio UI*" combines "*OBS View*", "*OBS Controller*" and "*Scene Collection*" which is a part of the "*OBS Model*".
89+
Note that ConfigFile is not a good type to represent *globalConfig* and *basicConfig*.
90+
91+
"*Profile*" is also splitted into three or four parts (*recordEncoder* is unused if the encode is the same as the stream), so this one really need a type to represent it.
92+
93+
Note: from now, the node "*Profile*" will be used if the diagram is not related to "*Profile*" itself.
94+
95+
---
96+
97+
So by applying MVC, "*OBS Studio UI*" should only serve as "*OBS View*":
98+
```mermaid
99+
flowchart LR
100+
user(["User(s)"])
101+
102+
subgraph view ["OBS View"]
103+
ui("OBS Studio UI")
104+
end
105+
106+
controller[/OBS Controller/]
107+
108+
globconf["globalConfig (ConfigFile)"]
109+
profile[/Profile/]
110+
scenecol[/Scene Collection/]
111+
112+
user-- "interact with" -->ui
113+
ui-- "user action" -->controller
114+
controller-- update -->ui
115+
controller-- manipulate -->globconf
116+
controller-- manipulate -->profile
117+
controller-- manipulate -->scenecol
118+
scenecol-- notify -->controller
119+
```
120+
121+
## "*Scene Collection*" and "*OBS Studio UI*"
122+
123+
The "*Scene Collection*" contains everything related to scenes, sources (and filters) are tightly connected to them:
124+
- Transitions
125+
- Projectors
126+
- Scaling of the preview
127+
- Video mix configuration for the virtual camera
128+
- Settings of some plugins bound to the collection
129+
130+
Except the last point, everything is stored in parts of the UI. "*Scene Collection*" shall be split from it into a new type/object.
131+
132+
```mermaid
133+
flowchart LR
134+
user(["User(s)"])
135+
136+
ui("OBS Studio UI")
137+
138+
globconf["globalConfig (ConfigFile)"]
139+
profile[/Profile/]
140+
scenecol["sceneCollection (OBSSceneCollection)"]
141+
142+
user-- "interact with" -->ui
143+
ui-- "user action" -->ui
144+
ui-- "update view" -->ui
145+
146+
ui-- manipulate -->globconf
147+
ui-- manipulate --> profile
148+
ui-- manipulate --> scenecol
149+
scenecol-- notify -->ui
150+
```
151+
152+
The changes around the "*notify*" part are explained later.
153+
154+
## "*Profile*" and "*Global Config*"
155+
156+
New types/objects will be created to represent those:
157+
158+
```mermaid
159+
flowchart LR
160+
user(["User(s)"])
161+
162+
ui("OBS Studio UI")
163+
164+
globconf["globalConfig (OBSGlobalConfig)"]
165+
profile["profile (OBSProfile)"]
166+
scenecol["sceneCollection (OBSSceneCollection)"]
167+
168+
user-- "interact with" -->ui
169+
ui-- "user action" -->ui
170+
ui-- "update view" -->ui
171+
172+
ui-- manipulate -->globconf
173+
ui-- manipulate --> profile
174+
ui-- manipulate --> scenecol
175+
scenecol-- notify -->ui
176+
```
177+
178+
Note: *OBSProfile* also includes encoders and service.
179+
180+
Their ConfigFile could be openned only when loading and saving but…
181+
182+
### "*OBS Frontend API*" and ConfigFile
183+
184+
"*OBS Frontend API*" provide functions to access *globalConfig* and *basicConfig* (profile) and modify them without restriction ignoring "*OBS Controller*", the program is not even aware is one of its own config happen to be changed.
185+
186+
```mermaid
187+
flowchart LR
188+
plugins{{"Plugin(s)"}}
189+
190+
ui("OBS Studio UI")
191+
api["OBS Frontend API"]
192+
193+
194+
subgraph model [OBS Model]
195+
globconf["globalConfig (ConfigFile)"]
196+
subgraph profile [Profile]
197+
basicconf["basicConfig (ConfigFile)"]
198+
streamenc["streamEncoder (OBSData)"]
199+
recordenc["recordEncoder (OBSData)"]
200+
service["service (OBSService)"]
201+
end
202+
scenecol["sceneCollection (OBSSceneCollection)"]
203+
end
204+
205+
plugins-- action -->api
206+
api-- "event callback" -->plugins
207+
208+
ui-- "frontend events" -->api
209+
api-- action -->ui
210+
211+
ui-- "update view" -->ui
212+
213+
ui-- manipulate -->globconf
214+
ui-- manipulate --> basicconf
215+
ui-- manipulate --> streamenc
216+
ui-- manipulate --> recordenc
217+
ui-- manipulate --> service
218+
ui-- manipulate --> scenecol
219+
220+
plugins-- "manipulate configs" -->api
221+
api-- manipulate -->globconf
222+
api-- manipulate --> basicconf
223+
```
224+
225+
So `obs_frontend_get_global_config` and `obs_frontend_get_profile_config` will be deprecated.
226+
227+
Until those are removed, OBSGlobalConfig or OBSProfile will open the ConfigFile and keep it updated. But changing a OBS config from a plugin will have no effect, the stored value will overwrite what the plugin has put.
228+
229+
To replace those two functions to allow plugins to save their config in the global config or the profile. Functions like the following will be created for `bool`, `double`, `int64_t`, `uint64_t` and `const char *`(string):
230+
231+
```c
232+
EXPORT void obs_frontend_set_global_bool_config(const char *section, const char *name, bool value);
233+
234+
EXPORT bool obs_frontend_get_global_bool_config(const char *section, const char *name);
235+
```
236+
237+
```c
238+
EXPORT void obs_frontend_set_profile_bool_config(const char *section, const char *name, bool value);
239+
240+
EXPORT bool obs_frontend_get_profile_module_bool_config(const char *section, const char *name);
241+
```
242+
243+
If a section owned by OBS is used with a setter, the setter will do nothing.
244+
245+
Those settings will be stored in OBSGlobalConfig or OBSProfile, and then written in the file while saving.
246+
247+
To check if functions to get some OBS config is needed, asking third-party plugins developper may be needed.
248+
249+
Now "*OBS Frontend API*" no longer bypass "*OBS Controller*" to manipulate the global config and the profile:
250+
251+
```mermaid
252+
flowchart LR
253+
plugins{{"Plugin(s)"}}
254+
255+
ui("OBS Studio UI")
256+
api["OBS Frontend API"]
257+
258+
globconf["globalConfig (OBSGlobalConfig)"]
259+
profile["profile (OBSProfile)"]
260+
scenecol["sceneCollection (OBSSceneCollection)"]
261+
262+
plugins-- action -->api
263+
api-- "event callback" -->plugins
264+
265+
ui-- "frontend events" -->api
266+
api-- action -->ui
267+
268+
ui-- "update view" -->ui
269+
270+
ui-- manipulate -->globconf
271+
ui-- manipulate --> profile
272+
ui-- manipulate --> scenecol
273+
scenecol-- notify -->ui
274+
```
275+
276+
## "*OBS Controller*" and "*OBS Studio UI*"
277+
278+
"*OBS Studio UI*" act as "*OBS View*" and "*OBS Controller*". "*OBS Controller*" is scattered in various elements of the UI.
279+
280+
```mermaid
281+
flowchart LR
282+
user(["User(s)"])
283+
284+
ui("OBS Studio UI")
285+
286+
globconf["globalConfig (OBSGlobalConfig)"]
287+
profile["profile (OBSProfile)"]
288+
scenecol["sceneCollection (OBSSceneCollection)"]
289+
290+
user-- "interact with" -->ui
291+
ui-- "user action" -->ui
292+
ui-- "update view" -->ui
293+
294+
ui-- manipulate -->globconf
295+
ui-- manipulate --> profile
296+
ui-- manipulate --> scenecol
297+
scenecol-- notify -->ui
298+
```
299+
---
300+
301+
*OBS Controller* shall be split from the UI:
302+
303+
```mermaid
304+
flowchart LR
305+
user(["User(s)"])
306+
307+
control["OBSController"]
308+
309+
ui("OBS Studio UI")
310+
311+
globconf["globalConfig (OBSGlobalConfig)"]
312+
profile["profile (OBSProfile)"]
313+
scenecol["sceneCollection (OBSSceneCollection)"]
314+
315+
user-- "interact with" -->ui
316+
ui-- "user action" -->control
317+
control-- "update view" -->ui
318+
319+
control-- manipulate -->globconf
320+
control-- manipulate --> profile
321+
control-- manipulate --> scenecol
322+
scenecol-- notify -->control
323+
324+
```
325+
326+
Other windows (e.g. settings) will parented to the main window but will be created from "*OBSController*".
327+
328+
---
329+
330+
Note that OBSController will manage all OBSSignal and bound them to a Qt signal/slot meant for the UI, no OBSSignal will be directly bound to an UI element.
331+
332+
```mermaid
333+
flowchart LR
334+
ui("OBS Studio UI")
335+
control["OBSController"]
336+
scenecol["OBSSceneCollection"]
337+
338+
ui-- "1. QSignal to QSlot" -->control
339+
control-- "4. QSlot from QSignal" -->ui
340+
control-- "2. manipulate" -->scenecol
341+
scenecol-- "3. OBSSignal" -->control
342+
```
343+
344+
# Additional Information
345+
346+
The frontend API is the only part of OBS Studio that have access to "*OBS View*" (dock, action…), "*OBS Controller*" and "*OBS Model*" (sources, scenes…).
347+
348+
The "OBSBasic" naming could retired from while progressing on the application of this RFC.

0 commit comments

Comments
 (0)