Skip to content

Commit cd433a1

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

File tree

1 file changed

+354
-0
lines changed

1 file changed

+354
-0
lines changed

text/0053-move-to-mvc.md

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
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), the config section will be named after the plugin filename:
230+
231+
```c
232+
#define obs_frontend_set_global_bool_config(name, value) \
233+
obs_frontend_set_global_module_bool_config(obs_current_module(), name, value)
234+
EXPORT void obs_frontend_set_global_module_bool_config(obs_module_t *module, const char *name, bool value);
235+
236+
#define obs_frontend_get_global_bool_config(name) \
237+
obs_frontend_get_global_module_bool_config(obs_current_module(), name)
238+
EXPORT bool obs_frontend_get_global_module_bool_config(obs_module_t *module, const char *name);
239+
```
240+
241+
```c
242+
#define obs_frontend_set_profile_bool_config(name, value) \
243+
obs_frontend_set_profile_module_bool_config(obs_current_module(), name, value)
244+
EXPORT void obs_frontend_set_profile_module_bool_config(obs_module_t *module, const char *name, bool value);
245+
246+
#define obs_frontend_get_profile_bool_config(name) \
247+
obs_frontend_get_profile_module_bool_config(obs_current_module(), name)
248+
EXPORT bool obs_frontend_get_profile_module_bool_config(obs_module_t *module, const char *name);
249+
```
250+
251+
Those settings will be stored in OBSGlobalConfig or OBSProfile, and then written in the file while saving.
252+
253+
To check if functions to get some OBS config is needed, asking third-party plugins developper may be needed.
254+
255+
Now "*OBS Frontend API*" no longer bypass "*OBS Controller*" to manipulate the global config and the profile:
256+
257+
```mermaid
258+
flowchart LR
259+
plugins{{"Plugin(s)"}}
260+
261+
ui("OBS Studio UI")
262+
api["OBS Frontend API"]
263+
264+
globconf["globalConfig (OBSGlobalConfig)"]
265+
profile["profile (OBSProfile)"]
266+
scenecol["sceneCollection (OBSSceneCollection)"]
267+
268+
plugins-- action -->api
269+
api-- "event callback" -->plugins
270+
271+
ui-- "frontend events" -->api
272+
api-- action -->ui
273+
274+
ui-- "update view" -->ui
275+
276+
ui-- manipulate -->globconf
277+
ui-- manipulate --> profile
278+
ui-- manipulate --> scenecol
279+
scenecol-- notify -->ui
280+
```
281+
282+
## "*OBS Controller*" and "*OBS Studio UI*"
283+
284+
"*OBS Studio UI*" act as "*OBS View*" and "*OBS Controller*". "*OBS Controller*" is scattered in various elements of the UI.
285+
286+
```mermaid
287+
flowchart LR
288+
user(["User(s)"])
289+
290+
ui("OBS Studio UI")
291+
292+
globconf["globalConfig (OBSGlobalConfig)"]
293+
profile["profile (OBSProfile)"]
294+
scenecol["sceneCollection (OBSSceneCollection)"]
295+
296+
user-- "interact with" -->ui
297+
ui-- "user action" -->ui
298+
ui-- "update view" -->ui
299+
300+
ui-- manipulate -->globconf
301+
ui-- manipulate --> profile
302+
ui-- manipulate --> scenecol
303+
scenecol-- notify -->ui
304+
```
305+
---
306+
307+
*OBS Controller* shall be split from the UI:
308+
309+
```mermaid
310+
flowchart LR
311+
user(["User(s)"])
312+
313+
control["OBSController"]
314+
315+
ui("OBS Studio UI")
316+
317+
globconf["globalConfig (OBSGlobalConfig)"]
318+
profile["profile (OBSProfile)"]
319+
scenecol["sceneCollection (OBSSceneCollection)"]
320+
321+
user-- "interact with" -->ui
322+
ui-- "user action" -->control
323+
control-- "update view" -->ui
324+
325+
control-- manipulate -->globconf
326+
control-- manipulate --> profile
327+
control-- manipulate --> scenecol
328+
scenecol-- notify -->control
329+
330+
```
331+
332+
Other windows (e.g. settings) will parented to the main window but will be created from "*OBSController*".
333+
334+
---
335+
336+
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.
337+
338+
```mermaid
339+
flowchart LR
340+
ui("OBS Studio UI")
341+
control["OBSController"]
342+
scenecol["OBSSceneCollection"]
343+
344+
ui-- "1. QSignal to QSlot" -->control
345+
control-- "4. QSlot from QSignal" -->ui
346+
control-- "2. manipulate" -->scenecol
347+
scenecol-- "3. OBSSignal" -->control
348+
```
349+
350+
# Additional Information
351+
352+
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…).
353+
354+
The "OBSBasic" naming could retired from while progressing on the application of this RFC.

0 commit comments

Comments
 (0)