Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
742d3d8
feat(wgc): read a list of cameras from the helper config
christian-wr Oct 3, 2026
d09a1ac
feat(wgc): record every camera in the config on the shared clock
christian-wr Oct 4, 2026
e86b382
fix(wgc): drop a camera whose encoder cannot start
christian-wr Oct 4, 2026
bce2d6d
feat(recording): read per-camera helper events
christian-wr Oct 4, 2026
c0b235a
feat(recording): keep additional cameras in the session and media links
christian-wr Oct 4, 2026
1dead65
feat(recording): start and stop several cameras on Windows
christian-wr Oct 4, 2026
9665e7b
fix(wgc): never open the same camera twice in one take
christian-wr Oct 4, 2026
e45dad0
fix(recording): tell cameras with the same name apart
christian-wr Oct 4, 2026
1a41993
feat(project): keep a recording's additional cameras
christian-wr Oct 4, 2026
aa84135
feat(hud): pick additional cameras for a Windows recording
christian-wr Oct 4, 2026
66bb3b5
fix(hud): note why the capture probe failed
christian-wr Oct 4, 2026
a8fea4b
docs(e2e): several cameras on Windows
christian-wr Oct 4, 2026
1607b29
fix(project): no fixed limit on additional camera tracks
christian-wr Oct 4, 2026
3f3eb2c
fix(recording): never match identical cameras by name across an id
christian-wr Oct 4, 2026
21c848e
fix(wgc): report a camera file that could not be removed
christian-wr Oct 4, 2026
31e8d31
feat(recording): name a camera that stopped early
christian-wr Oct 4, 2026
20f7044
docs(e2e): identical cameras and screen pacing with several cameras
christian-wr Oct 4, 2026
38d838b
docs(e2e): log the first real two-camera run on Windows on ARM
christian-wr Oct 5, 2026
b4d10be
fix(wgc): stop and report a camera that is unplugged mid-take
christian-wr Oct 5, 2026
0aeb914
docs(e2e): log the on-site camera runs and the unplug fix
christian-wr Oct 5, 2026
0857621
feat(camera): desk view for Full Camera sections, with a covered tilt
christian-wr Oct 3, 2026
f0bc3d1
feat(camera): several cameras in the picture, with layout sections
christian-wr Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion THIRD-PARTY-NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ application resources and satisfies the attribution and source-offer obligations
that come with them.

npm dependencies are not listed here: they are resolved from `package.json` and
distributed by their own registries, not redistributed inside our binaries.
distributed by their own registries, not redistributed inside our binaries. The
exception is code bundled into the renderer whose licence asks for its notice in
the distributed form (js-aruco2 below): the bundler strips the source headers.

---

Expand Down Expand Up @@ -172,6 +174,49 @@ distributed by their own registries, not redistributed inside our binaries.
notice as OpenScreen's own [LICENSE](LICENSE). Published by Recordly under MIT,
before its relicensing in March 2026.

## js-aruco2 — ArUco marker detection

- **Components**: `src/aruco.js` and `src/cv.js` of the npm package `js-aruco2`
2.0.0, compiled into the renderer bundle (`dist/assets/*.js`).
- **Used by**: the camera calibration dialog, which finds the four printed corner
markers in a camera still.
- **License**: MIT — Copyright (c) 2020 Damiano Falcioni, Copyright (c) 2011 Juan
Mellado, under the same permission notice as OpenScreen's own
[LICENSE](LICENSE).
- **Upstream**: <https://github.com/damianofalcioni/js-aruco2>, release 2.0.0.

## OpenCV — ArUco 4×4 dictionary codes

- **Component**: the marker codes in js-aruco2's
`src/dictionaries/aruco_4x4_1000.js`, compiled into the renderer bundle. They
are taken from OpenCV's `predefined_dictionaries.hpp`; the marker sheet the
editor prints draws markers 0–3 of this dictionary.
- **License**: BSD 3-Clause — Copyright (C) 2013, OpenCV Foundation, all rights
reserved. Third party copyrights are property of their respective owners.

> Redistribution and use in source and binary forms, with or without
> modification, are permitted provided that the following conditions are met:
>
> - Redistributions of source code must retain the above copyright notice,
> this list of conditions and the following disclaimer.
> - Redistributions in binary form must reproduce the above copyright notice,
> this list of conditions and the following disclaimer in the documentation
> and/or other materials provided with the distribution.
> - Neither the names of the copyright holders nor the names of the
> contributors may be used to endorse or promote products derived from this
> software without specific prior written permission.
>
> This software is provided by the copyright holders and contributors "as is"
> and any express or implied warranties, including, but not limited to, the
> implied warranties of merchantability and fitness for a particular purpose
> are disclaimed. In no event shall copyright holders or contributors be liable
> for any direct, indirect, incidental, special, exemplary, or consequential
> damages (including, but not limited to, procurement of substitute goods or
> services; loss of use, data, or profits; or business interruption) however
> caused and on any theory of liability, whether in contract, strict liability,
> or tort (including negligence or otherwise) arising in any way out of the use
> of this software, even if advised of the possibility of such damage.

## Rust crates — compiled into the compositor addon

- **Component**: `compositor_view.node`, under
Expand Down
64 changes: 41 additions & 23 deletions crates/compositor-view-napi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use openscreen_compositor::frame_geometry::FootageQuad;
use openscreen_compositor::gif_export::{GifExportParams, GifStats};
use openscreen_compositor::gif_export_control::{GifExportCancelled, GifExportControl};
use openscreen_compositor::live::{LiveView, PausedPreviews};
use openscreen_compositor::scene::Scene;
use openscreen_compositor::scene::{Scene, SceneClipCamera};
use openscreen_compositor::{config, pipeline};
use std::collections::HashMap;
use std::path::PathBuf;
Expand Down Expand Up @@ -368,6 +368,9 @@ pub fn present_time(id: i32, seconds: f64) {
/// Remplace les sources du clip actif sans recréer la vue ni son thread de rendu. L'identité
/// timeline et le playhead source sont atomiques avec le switch : deux clips partageant les
/// mêmes fichiers restent distincts, et les deux décodeurs ouvrent directement la bonne frame.
///
/// `additional_cameras`: cameras 2-4 of the clip (index k-1 = camera k, an empty `path` = no
/// camera in that slot). Absent = none; the view decodes only those its layout regions show.
#[napi]
pub fn set_active_clip(
id: i32,
Expand All @@ -376,18 +379,33 @@ pub fn set_active_clip(
webcam_offset_sec: f64,
clip_index: u32,
source_time_sec: f64,
additional_cameras: Option<Vec<ClipCameraInput>>,
) {
if let Some(v) = registry().lock().unwrap().get(&id) {
let cameras = additional_cameras
.unwrap_or_default()
.into_iter()
.map(|c| SceneClipCamera { path: c.path, offset_sec: c.offset_sec })
.collect();
v.set_active_clip(
&screen_path,
&webcam_path,
webcam_offset_sec,
cameras,
clip_index as usize,
source_time_sec,
);
}
}

/// An additional camera of a clip (= TS `CompositorClipCamera`).
#[napi(object)]
pub struct ClipCameraInput {
pub path: String,
/// Camera source time = screen source time - this.
pub offset_sec: f64,
}

/// Installe la scène de l'app (JSON `SceneDescription`) sur la vue : layout preset piloté par
/// l'app au lieu de la fixture. JSON invalide → ignoré côté natif.
#[napi]
Expand Down Expand Up @@ -504,6 +522,26 @@ pub struct ClipInput {
pub webcam_offset_sec: f64,
/// `false` évite une ouverture ffmpeg vouée à échouer et réserve du silence à ce clip.
pub has_audio: bool,
/// Cameras 2-4 of the clip (index k-1 = camera k, an empty `path` = none). Absent = none.
pub additional_cameras: Option<Vec<ClipCameraInput>>,
}

/// `ClipInput` → `pipeline::ClipSource`, shared by the MP4 and the GIF export.
fn clip_source(c: ClipInput) -> pipeline::ClipSource {
pipeline::ClipSource {
screen: c.screen_path,
webcam: c.webcam_path,
source_start_sec: c.source_start_sec,
source_end_sec: c.source_end_sec,
webcam_offset_sec: c.webcam_offset_sec,
has_audio: c.has_audio,
additional_cameras: c
.additional_cameras
.unwrap_or_default()
.into_iter()
.map(|cam| pipeline::ClipCamera { path: cam.path, offset_sec: cam.offset_sec })
.collect(),
}
}

/// The export's pixel size. The SHAPE belongs to the scene: `scene.output` is what the live
Expand Down Expand Up @@ -701,17 +739,7 @@ pub fn export_multi(
params: Option<ExportParamsInput>,
on_progress: Option<JsFunction>,
) -> Result<AsyncTask<ExportMultiTask>> {
let clips = clips
.into_iter()
.map(|c| pipeline::ClipSource {
screen: c.screen_path,
webcam: c.webcam_path,
source_start_sec: c.source_start_sec,
source_end_sec: c.source_end_sec,
webcam_offset_sec: c.webcam_offset_sec,
has_audio: c.has_audio,
})
.collect();
let clips = clips.into_iter().map(clip_source).collect();
Ok(AsyncTask::new(ExportMultiTask {
out_path,
clips,
Expand Down Expand Up @@ -882,17 +910,7 @@ pub fn export_gif(
// Deliberately the same argument shape as `export_multi`: the caller builds
// one clip list and one scene, and picks the container. Cursor comes from
// the scene like every other effect — there is no GIF-specific input left.
let clips = clips
.into_iter()
.map(|c| pipeline::ClipSource {
screen: c.screen_path,
webcam: c.webcam_path,
source_start_sec: c.source_start_sec,
source_end_sec: c.source_end_sec,
webcam_offset_sec: c.webcam_offset_sec,
has_audio: c.has_audio,
})
.collect();
let clips = clips.into_iter().map(clip_source).collect();
let gif_params = params
.map(|p| GifExportParams {
width: p.width,
Expand Down
Loading
Loading