Repository navigation
Configuration and profiles
src/config.cpp, src/config.h, src/json.cpp
qBlank.json, beside the executable. Nothing is written to the registry —
the one exception being the virtual camera's COM registration, which has to be
machine-wide and is not qBlank's own setting. See Virtual camera.
Written a second after the last change, or at once when saved on purpose. The
text is serialised on the render thread, where the settings live, and handed
to configJobs_ for the disk (see
Latency). Writes run in order, so the last
one wins. When Windows ends the session, the program waits for the write
before it lets go.
A settings file written under a former name is recognised by what is in it, not
by what it is called: FindForeignSettings() reads every .json beside the
executable and picks out the ones that are this program's. Found alone, such a
file is simply used from then on; found alongside a current one, the program
asks which to keep, in the language the older file was written with. The loser
is renamed to .bak rather than deleted. See Updates.
The JSON parser and writer are hand-written (src/json.cpp, 450 lines) rather
than pulled in as a dependency. The file is small, the schema is known, and every
read goes through a defaulting accessor:
app.settingsTab = a["settingsTab"].AsInt(0);A missing or malformed key yields the default instead of an error, so a config file from an older build always loads.
struct Profile {
std::string name = "Standard";
long autoSelectStandard = 0; // which detected standard means this profile
CaptureSettings capture; // device, input, format, video standard
ImageSettings image; // scaling, deinterlacing, composite filter, crop, colour
AudioSettings audio; // devices, buffer, offset, volume, recording sources
};One per source. Selected with Ctrl+1 to Ctrl+9, which is deliberately not rebindable — see Shortcuts.
A profile is everything that depends on what is plugged in. Switching profiles restarts the capture graph, because the device or the format may have changed.
The usual way to make one, and the reason there is no plain "Duplicate" any more. Connecting a second console almost never means starting over: you change what is different and want to keep the result. An empty profile forces you to set up everything that was already right.
It copies the active profile — which is the current state, since settings take
effect immediately and are written there — and opens the name prompt with the
keyboard focus already in the field. SetKeyboardFocusHere() fires only on the
popup's first frame, or it would grab the input again every frame.
App::UpdateProfileForStandard(), and one long in the profile:
long autoSelectStandard = 0; // 0 = neverA profile can say which video standard means me. Set it in the profiles tab under Choose automatically, and when the automatic search settles on that standard, qBlank switches to that profile — the PAL console and the NTSC one on the same input, each with its own geometry, deinterlacer and colour settings, without picking from a menu.
It is stored on the profile and not next to the standard on the source page, although it is the same list. There the standard answers what should the card be set to; here it answers when am I meant. That is a statement about the profile, and it belongs where the profiles are.
Compared by group, not by value. The list offers eight groups — PAL, PAL 60, PAL M, PAL N, NTSC, NTSC Japan, NTSC 4.43, SECAM — and the rule matches any standard in the same one. PAL B and PAL G differ in the sound carrier, which is no part of the picture, and a rule meaning "the PAL console" must not miss because the search settled on the other spelling of PAL. The value written to the config is still an ordinary standard, so the file format does not grow a second vocabulary; only the comparison is by group.
The trigger is the end of stage two, not the lock. Until the colour round
has settled, all that is known is a line count, and 525 lines are five different
standards in four different groups. colourCheckedStandard_ is the value that
says both stages are done; a second member remembers what was last acted on, so
a standing state becomes a one-time event.
Four things stop a switch, and each of them is a case where switching would be wrong rather than merely unwanted:
| Not switched | Why |
|---|---|
| While recording, or while the crop tool is open | Both would be interrupted by a graph rebuild |
| Onto a profile that pins a video standard | It would never search, so its rule could never fire — and a rule that quietly does nothing is worse than no rule |
| Onto a profile on another device or another crossbar input | The detected standard says nothing about a source that is not the one being watched |
| When the active profile's own rule already matches | The loop guard |
That last one is the one that matters. Two profiles both claiming PAL, without it, would hand the detection back and forth forever. With it, a profile that already answers to the standard on screen keeps it, and nothing moves. A profile chosen by hand stays until the source really changes.
The combo is disabled outright while the profile pins a standard, with the reason written under it rather than left to be worked out.
Hiding a control is not enough. Somebody who had the dot crawl filters and a native pixel grid set for a SNES, and then plugs in an HD console, would otherwise be looking at a picture they did not ask for with no control anywhere to change it — the control is gone precisely because it does not fit.
App::EffectiveImage() returns the settings as they apply to the current
source, and it is that copy which is drawn:
| Neutralised | When |
|---|---|
chromaSoft, temporalDenoise, dotNotch, nativeWidth, lineDouble
|
the source is not analogue |
nativeWidth |
the source is taller than 576 lines, whatever sourceLines says |
a hand-picked deinterlacer (forced to deinterlaceAuto) |
the source has no fields |
It neutralises the copy, never the profile. The stored values survive, because a profile describes a console and that console will come back.
What it deliberately does not do is remember and restore across a source change. The next analogue source may be a different console wanting different values, so restoring would be a guess. That is what profiles are for.
The same predicate, App::SourceIsAnalogue(), drives both what the settings
show and what the renderer applies. Two answers to one question would mean
something acting where nothing can be adjusted.
Measured, with nativeWidth set to 256 against a 1080p source: with the signal
type forced to Analogue the grid applies and a row shows 55 column
transitions; on Automatic, where 1080 lines cannot have come from an analogue
decoder, the same row shows 240. The setting is dropped, not merely hidden.
What is not in a profile — AppSettings — is everything that depends on
you: theme, accent colour, language, vsync, always-on-top, cursor hiding, sleep
prevention, the toolbar, the overlay level, where the settings window sits, which
tab it was on, and every recording, encoder and update setting.
Two places in the configuration carry an index into a table rather than a value, and both are noted in the source at the table:
-
videoStandard— the index intokStandardsinsrc/capture/dshow_util.cpp. Entries may be appended but never reordered. Two values are not indices:0means leave the decoder alone and-1means find it, which is what the automatic search reads. -
speed— the old software encoder speed, kept working alongside the newer preset setting so nobody's existing configuration changes meaning. See Encoder settings.
preventSleep, on by default. Playing a game through a capture card involves no
keyboard or mouse input as far as Windows is concerned, so the screen saver would
come on mid-session. App calls SetThreadExecutionState periodically while a
capture is running.
Builds before 5.5 compiled their Direct3D 11 shaders at the first start and kept
them as shader-<hash>.cso next to the executable. The build compiles them now,
the executable carries them, and Updater::CleanUpPreviousBuild() deletes any
shader-*.cso it finds at startup.