ElementReview is a Windows desktop recording and replay tool for figure skating competitions. It combines:
- a local ASP.NET Core server
- a Windows Forms shell
- a WebView2 operator UI
ffmpeg/ffprobefor recording- MediaMTX for live RTSP relay into the browser UI
The current app version is v0.5.5.
ElementReview is built around a fast record-to-review workflow:
- show a live RTSP feed or demo video
- start recording
- mark element clips while the skater/team is performing
- stop recording and switch straight into replay mode
- trim, split, insert, or delete replay clips
- expose the replay over the LAN for judge and referee review
Current operator features include:
- record mode with clip start/stop marking
- record-mode undo/redo
- optional halfway/program timer tracking
- replay playback, scrubbing, looping, zoom, and frame stepping
- replay clip editing
- English/French UI switching from the main control bar
- a separate Judge Video Replay app for remote judge/referee review
- demand-driven Judge Video Replay client caching
- saved-video export into a metadata-based folder structure
- recording shortcuts:
Rstarts/stops recording,Spacestarts/stops clips, andSsets/resets the program start when halfway timing is active
JudgeVideoReplay.exe is the remote replay client for judges and the referee. It packages its own static UI under JudgeVideoReplay/wwwroot and connects to the ElementReview backend API over the LAN.
The same executable can be used by both judges and the referee. Referee timing functionality is available when the app settings role is Referee; the Judge role hides those timing controls.
Run JudgeVideoReplay.exe on each judge or referee computer. In the app settings, set the Server IP address to the computer running ElementReview (i.e., the VRO computer).
- Judge Video Replay starts in a rail/menu view. Element buttons loop their clipped region until playback is paused or another view is selected.
- Element buttons are clickable immediately. When a judge clicks a clip, the Judge Video Replay client downloads and caches only the needed video chunks.
- The video icon button beneath the element rail appears only when replay media is available and opens the full-video timeline with clip markers for reviewing portions of the performance outside the clipped elements.
- Cached chunks are reused, so repeated playback of the same region does not download the same bytes again.
- Judge Video Replay shows a session info bar when replay clips are available.
- The Judge Video Replay timer overlay appears above clip blocks and remains translucent so the clip underneath is still visible.
- Judge Video Replay is read-only. It can read session status, session metadata, and low-res replay video, but it cannot start/stop recording, mark clips, clear sessions, edit replay clips, change settings, or restart ElementReview.
Judge Video Replay client transfer behaviour is coordinated by the ElementReview backend:
- Element Review operator high-res replay requests never enter the Judge Video Replay transfer path.
- Judge Video Replay low-res on-demand chunk requests enter the Judge Video Replay transfer path.
When SaveVideos is enabled in appconfig.json, completed recordings are exported from the low-res replay file under:
SavedVideosFolder/
categoryName/
categoryDiscipline/
categoryFlight/
segmentName/
LastName-FirstName-Club-Section.mp4
LastName-FirstName-Club-Section.json
Folder and file names are built from SessionInfo.json.
- [shell/Program.cs] starts the local web server and native shell.
- [shell/MainForm.cs] hosts the main operator UI in WebView2.
- [AppServer.cs] serves static files and the local HTTP API.
- [JudgeVideoReplay/JudgeVideoReplay.csproj] builds the separate
JudgeVideoReplay.exeexecutable. - [Services/RecorderManager.cs] manages recording, replay-file generation, and saved-video export.
- [Services/MediaMtxManager.cs] runs MediaMTX for RTSP relay.
- [Services/SessionManager.cs] owns in-memory session and clip state.
- [wwwroot/index.html] is the main operator UI.
- [wwwroot/config.html] is the settings window.
- [JudgeVideoReplay/wwwroot/judge-video-replay.html] is the Judge Video Replay UI.
The local server listens on:
http://0.0.0.0:5050
Operator access is local-only:
http://127.0.0.1:5050
http://localhost:5050
LAN clients can reach only the Judge Video Replay read-only API surface:
GET /api/statusGET /api/sessionInfoGET /api/recording/file?kind=low-res&v=<ReplayMediaToken>
Operator-only pages and API actions are restricted to the ElementReview computer. On startup, ElementReview generates a disposable per-session operator token and the local WebView operator UI attaches it automatically to protected API calls. Installers and operators do not need to configure passwords, QR codes, or shared secrets. The token is not stored in source code and changes when the ElementReview server restarts.
Protected operator-only actions include configuration changes, recording start/stop, clip marking, undo/redo, session clearing, replay trim/split/insert/delete, diagnostics, restart, high-res replay access, demo/live operator pages, and the main operator UI.
To compile the app, you need:
- Windows
- .NET 10 SDK
- WebView2 Runtime
tools/ffmpeg.exetools/ffprobe.exetools/mediamtx.exe
Optional CSS helper executables should be placed beside ElementReview.exe:
GetSessionInfo_LegacyCSS.exepulls session information from legacy CSS into SessionInfo.jsonGetSessionInfo_OnlineCSS.exepulls session information from Online CSS into SessionInfo.jsonGetSessionInfo_OfflineCSS.exepulls session information from Offline CSS into SessionInfo.json
Writable per-user files live under:
%LocalAppData%\ElementReview\data\
Important files:
appconfig.jsonSessionInfo.jsondemovideo.mp4current-high-res.mp4current-low-res.mp4
Bundled files under data\ are used as fallbacks for development and packaging when the local copies do not exist.
During recording, ElementReview produces two replay MP4 files in parallel:
current-high-res.mp4: the main operator replay file, encoded withhighresVideoGopfor responsive seeking inindex.html.current-low-res.mp4: the Judge Video Replay and saved-video file, encoded as 720p/30 fps with the configuredlowresVideoGopandlowresVideoBitratevalues. WhenSaveVideosis enabled, AAC audio from the source is included for saved copies; Judge Video Replay clients keep playback muted.
When UseHardwareEncodingWhenAvailable is enabled and a supported encoder is available, both replay files use hardware encoding. Otherwise both files use software encoding.
The app currently reads and writes these canonical AppConfig fields:
LanguageUiZoomPercentClipMarkerAdvanceMsecDemoModeRtspUrlSourceFpsRtspTransportProtocolUseHardwareEncodingWhenAvailablehighresVideoGoplowresVideoGoplowresVideoBitrateCSSLinkDatabaseLocationEventIdCSSServerHostSaveVideosSavedVideosFolderAutoplaySelectedClip
Notes:
SaveVideosis forced off whenDemoModeis on.UiZoomPercentis shared by the shell and settings window.Languageis switched live in the main operator UI.AutoplaySelectedClipcontrols whether selecting a replay element immediately starts playback and defaults tofalse.highresVideoGopcontrols the high-res/operator replay video GOP and defaults to10.lowresVideoGopcontrols the low-res Judge Video Replay client/saved-video GOP and defaults to60.lowresVideoBitrateis stored in kbps and defaults to2500.
SessionInfo.json is expected to use the current canonical shape. The app currently reads these top-level fields from it:
categoryNamecategoryDisciplinecategoryFlightsegmentNamesegmentProgHalfTimecompetitorFirstNamecompetitorLastNamecompetitorClubcompetitorSectionelements
{
"categoryName": "STAR 10",
"categoryDiscipline": "Women",
"categoryFlight": "Grp 1",
"segmentName": "Free Program",
"segmentProgHalfTime": "1:30",
"competitorFirstName": "Cindy",
"competitorLastName": "Smith",
"competitorClub": "Example Club",
"competitorSection": "ON",
"elements": {
"1": { "code": "2A", "review": true },
"2": { "code": "LSp1", "review": false }
}
}Within elements, each numbered entry can include:
code: element label shown in the clip list and replay timelinereview: whether the element should be marked as a review item
The app uses SessionInfo.json data for:
- session banner text:
categoryName,categoryDiscipline,categoryFlight,segmentName,competitorFirstName,competitorLastName - halfway/program timing:
segmentProgHalfTime - replay element labels:
elements[n].code - replay review flags:
elements[n].review - saved-video folder naming:
categoryName,categoryDiscipline,categoryFlight,segmentName - saved-video file naming:
competitorLastName,competitorFirstName,competitorClub,competitorSection
Halfway/program timing controls are shown only when all of these are true:
categoryNameisSeniororJuniorcategoryDisciplineisWomenorMensegmentNameisFree ProgramorShort ProgramsegmentProgHalfTimecontains a valid positive time
When those conditions are not met, Set/Reset Start, Jump to Halfway, halfway display, halfway marker, and the H halfway shortcut are hidden or inactive.
Unknown extra properties are ignored by the current app.
From the project root:
dotnet runDuring development, wwwroot\, data\, and tools\ are copied to the output folder with PreserveNewest.
dotnet publish -c Release -r win-x64 --self-contained true /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=truePublished output is created under:
bin\Release\net10.0-windows\win-x64\publish\
AppServer.csAppPaths.csModels\Services\shell\wwwroot\data\tools\API-manual.md
See [API-manual.md] for the full endpoint list and request/response shapes.