Skip to content

Release xSTUDIO v1.4.0 - #358

Merged
tedwaine merged 126 commits into
mainfrom
develop
Oct 27, 2026
Merged

tedwaine merged 126 commits into
mainfrom
develop

Conversation

@tedwaine

Copy link
Copy Markdown
Collaborator

Release xSTUDIO v1.4.0

This pull request merges develop branch into main to release version 1.4.0 of xSTUDIO.

kenmcgaugh and others added 30 commits June 22, 2026 14:59
This adds an Escape hotkey that doesn the following:
  - stopping playback
  - exiting presentation mode if active
  - making the viewed container the inspected container
  - resetting the compare mode to the default for viewed container type
  - soloing the selection to just the single hero selected media item
  - leaving pinned-source mode if active

In a dailies environment, when I'm done reviewing a submission I found that I needed to go through quite a few steps before moving on to the next submission. And these step often involved a cumbersome back-and-forth between mouse and keyboard. Now when I want to move on I just hit Escape and the Down arrow key.

This is possibly a subjective improvement, but hopefully OK to merge since the Escape key is only currently used by the grading tool to cancel construction of a polygon mask shape. This commit includes a modification to that code so that it works when needed without doubling up.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
This adds a feature to the media panels such that they now remember their scroll position for each container.

This also adds a 'F' hotkey for "framing" on the selected media.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
Added a hotkey for opening the preferences manager with "Control+," as its default sequence.
Not hotkey related but in the same file is a change to strip the "file://" prefixes from the recent files menu. This is a UI-only change.

Added a hotkey and menu itme (with no default sequence) for copying selected media item's names to the clipboard. Our pipeline sets the name to a simple version identifier (ie. "myproj/shots/myscene/myshot/99") that we use both in communications as well as pasting into various apps.

Removed the paste_hotkey as it was redundant with the add_from_clipboard_hotkey. Moved the "Ctrl+V" default sequence over as well.

Added "Ctrl+C" as a default sequence to the file_path_to_clipboard_hotkey to complement the default "Ctrl+V" hotkey.

Added a hotkey to add a timeline marker with "Ctrl+M" as its default sequence. Note this uses "any" as the context since I find I often want to use this while the mouse is over the viewport. This also includes a fix for when the Return key is used to accept and close the Add Marker dialog since the focus didn't return to the app.

Likewise changed the next_marker an previous_marker hotkeys to be "any" as I found it frustrating having to always relocate the mouse to use them. Happy to keep this an internal modification if needed.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
…uence "Meta+S".

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
…python API was referencing it even though it wasn't exposed.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
Under MacOS, these changes swap Ctrl/Control, Meta, and Alt for Cmd/Command, Ctrl/Control, Opt/Option in menus, tooltips, and the new hotkey manager.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
Some are self-explanatory fixes for warnings or spurious debug messages, but for those that are not:

src/ui/qml/session/src/session_model_methods_ui.cpp
Modified SessionModel::handleOtherDropFuture() to use getNextName() for creating the playlist name.

src/utility/src/chrono.cpp
This doesn't appear to be used anywhere in xstudio, but under our ubunutu build environment it doesn't compile as-is. So this has been modified to use an older/more portable method.

ui/qml/xstudio/application/XsSessionWindow.qml
If xstudio is started in presentation mode, the Tab hotkey wouldn't work correctly. This fix forces it to change to "Review" mode in that situation.

ui/qml/xstudio/application/auxillary/XsConformTool.qml
When multiple media items are selected and Conform->Compare operation is called, the newly added media are not in a sensible order. This change makes it so that the newly added media items are always next to the item against which they are being compared.
"Conform to Current Sequence" now creates the track with the name "Conformed Media", same as "Conform Media".

ui/qml/xstudio/application/panels/media/list_view/XsMediaList.qml
Changed to prevent onContentYChanged triggering itself. Putting a debug output within this handler showed that it was being called twice before the beforeMoveContentY guard could break the recursion.

ui/qml/xstudio/application/panels/media/functions/XsMediaListFunctions.qml
When using Shift+Down to extend the selection downwards didn't work when only the first media item is selected.
When deleting an item in the middle of the container, select the item *after* the deleted item rather than the one before it. This way doing a Copy, Delete, Paste sequence (or a not-yet-implemented Cut-Paste operation) returns the list to its original order.
Added missing sessionLinkPrefix preference.
Added copyColumnToClipboard() function to match the other copy-to-clipboard actions.

ui/qml/xstudio/application/panels/media/widgets/XsMediaListContextMenu.qml
Changed the "Copy To Clipboard|Selected Column" menus to use the newly created copyColumnToClipboard() function above.
Cleaned out unneeded preferences.

ui/qml/xstudio/application/panels/media/grid_view/XsMediaGrid.qml
This copies over the feature from the XsMediaList.qml that keeps the scroll position when media items are deleted.

ui/qml/xstudio/application/panels/playlists/XsPlaylists.qml
Commented out a line that was causing a qml error. This doesn't appear to change any functionality, however I suspect the error was preventing some desired function from working (namely being able to Escape out of a pop-up window).

src/plugin/utility/filesystem_browser/FileSystemBrowser/xstudio/FSPathInput.qml
Added missing getCommonPrefix function.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
…tput resolution is small (ie. for a thumbnail) since the size of the text is absolute and clutters resulting image.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
…er of columns is small.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
… already exists on the current frame. This way hitting the hotkey repeatedly will not pollute the notes with a bunch of empty ones.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
Signed-off-by: Sam.Richards@taurich.org <Sam.Richards@taurich.org>
… double-grade)

The dedicated 'thumbnail_processor' OCIO instance has no Display set (never
attached to a viewport/media source). OCIOEngine::process_thumbnail's combined
`if (display.empty() or view.empty())` reset _view to the config default view
(Client-look), so raw / display-referred media (e.g. dailies MOVs) got a double
grade in thumbnails. Fill display and view independently so a valid caller-supplied
view (e.g. "raw") survives an empty display, and use _view for the to-linear step.

Complements 3d8678c3 (sws colorspace/range for the thumbnail decode): raw view +
range-correct decode = the baked look, matching the viewport.

(cherry picked from commit 4a0d5c7697939162bb30b473bac0777573ce47cf)
Signed-off-by: Julien Hery <julien.hery@yahoo.fr>
…ers)

Same defect as the process_thumbnail fix (4a0d5c76), second site:
display_transform() reset both display AND view to the config defaults
whenever either was empty. Offscreen/snapshot viewport pipeline
instances always have an empty Display attribute, so a valid resolved
view (e.g. 'raw' for un-tone-mapped media) was replaced by the default
Client-look view, double-grading annotation exports and any offscreen
render. Fill display and view independently instead.

(cherry picked from commit e53a9849fc764cb25b25d2f05120cc30d7511b47)
Signed-off-by: Julien Hery <julien.hery@yahoo.fr>
Register viewport_scale_atom and viewport_pan_atom in py_atoms.cpp
(both already existed in C++ viewport.cpp but were not Python-
accessible). Register Imath::V2f as Vec2f message type so the pan
getter/setter round-trip correctly. Add Viewport.scale (float
read/write) and Viewport.pan (tuple(float,float) read/write) properties.
Avoids the serialise_atom/deserialise_atom crash path entirely.

Signed-off-by: Sam.Richards@taurich.org <Sam.Richards@taurich.org>
Signed-off-by: Sam.Richards@taurich.org <Sam.Richards@taurich.org>
…anism to python plugin base class

Signed-off-by: Ted Waine <ted.waine@gmail.com>
Signed-off-by: Ted Waine <ted.waine@gmail.com>
Add annotation event group to annotations tool, and subscription mech…
feat(python): expose viewport scale and pan atoms
…obber

OCIO: don't discard resolved view when pipeline instance has no display (thumbnails / offscreen renders)
FSDirectoryTreeBody.qml
- Replaced '...' with animated busy indicator.
- Eliminated some benign error messages.
- Prevented pressing "Full Scan" button from re-centring the direcotry list's scroll position to be consistent with just selecting the directory.

FSMainHeader.qml
- Added "Expand Sequences" toggle. When checked will disable the use of fileseq to compress frames into sequences.

FSThumbItem.qml
- Commented-out "thumbSource" debug message spamming the console.
- Replaced hyphens in comments with dashes as the hyphens don't play nice with some tools and end up being replaced with question marks.

FSTreeView.qml
- Added "name_and_count" property to FSDirectoryGroup item to avoid error message.

FileSystemBrowser.qml
- Added callback to automatically stop active scanning when the browser becomes hidden.
- Fixed bug preventing the scanning feedback console from working.
- Fixed copy-paste error where the fileTreeView was incorrectly calling the fileListView.getItemAtIndex().
- Modifed the right-click pop-up to select the item under the mouse if it isn't already selected to avoid confusion over which items the menus acted on.
- Modified the thumbnail tooltip to pass through any mouse events.

config.json
- Added more supported extensions.
- Exposed "non_sequence_extensions" so it can be customised.
- Exposed "follow_symlinks" so it can be customised.

filesystem_browser.py
- Removed unused imports and member variables.
- Added support for the "Expand Sequences" toggle.
- Added support for customising the "non_sequence_extensions".
- Added support for customising the "follow_symlinks".
- Commented out the scanning for subdirs-with-subdirs as it was causing the directory tree to hang for minutes at a time.

scanner.py
- Removed unused imports and member variables.
- Added support for the "Expand Sequences" toggle.
- Added support for customising the "non_sequence_extensions".
- Added support for customising the "follow_symlinks".
- Cleared out some dead code.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
Many PDF files do not have an explicit background and for them the QPdfDocument render() method produced unpremultiplied RGB over black. This fix composites the rendered result over white for both the "Pages RGB" stream and thumbnails. This leaves the "Pages RGBA" stream over black, but it has been modified to premult the RGB so that if it were composited 'over' a white image it would look correct.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
Signed-off-by: Ted Waine <ted.waine@gmail.com>
Signed-off-by: Ted Waine <ted.waine@gmail.com>
tedwaine and others added 29 commits September 16, 2026 10:40
feat(filesystem_browser) Misc fixes and improvements
feat(hotkey): Add support for numeric keypad hotkeys
…-package

feat(build): add portable Windows packaging target
…e-timeout

(fix): Return a response that arrived instead of reporting a timeout
Add 'Manual' auto align mode with persistent per-source compare offsets
Current documentation build jobs in readthedocs are failing.
This adds the missing python dependenceis and configuration.

Issue: RELENG-5212
Signed-off-by: Matthew Watkins <mwatkins@linuxfoundation.org>
Fix: Linux Foundation broken RTD documentation jobs
Signed-off-by: Ted Waine <ted.waine@gmail.com>
…working.

This changes "PlayheadList" to "Playlist" which was breaking the parent_playlist() method.
Fixed the same typo in timeline/__init__.py, but there it was only in a comment and only
affected documentation.

Signed-off-by: Ken McGaugh <ken@mcgaugh.co.uk>
The URI/path helpers now correctly preserve the server component of UNC paths
whether they arrive with backslashes (`\\server\share`) or forward slashes
(`//server/share`). The latter form is produced by `fs::path::generic_string()`
and by the command-line launcher (`run_xstudio.bat //host/...`).

Changes:
- `frame_list.cpp`: use `generic_string()` on Windows so regex matching works
  against forward-slash UNC paths and formatted output aligns with
  `directory_iterator` entries.
- `helpers.cpp`: `parse_cli_posix_path` no longer strips the leading separator
  of a UNC path; `posix_path_to_uri` accepts both separator styles and encodes
  the server as the URI host.
- Added tests covering round-trip encoding/decoding and CLI parsing for both
  UNC separator styles.

Signed-off-by: philippe leprince <github.pleprince@gmail.com>
Handle Windows UNC paths with both separator styles
fix(python): typo in subset.py was preventing parent_playlist() from working
Signed-off-by: Ted Waine <ted.waine@gmail.com>
Signed-off-by: Ted Waine <ted.waine@gmail.com>
Merge all updates from DNEG internal repo
The HUD maps the pointer into image space with round(), and the inside-image
test it guards accepts the full closed range [-1, 1]. On the last column or
row that rounds to width or height - one past the last pixel - so every
reader's pixel picker rejects it as out of bounds and returns nothing.

The result is that the probe reads N/A along the right and top edges of every
image, which looks like a reader problem rather than a rounding one.

Clamp the coordinate to the last pixel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Sam.Richards@taurich.org <Sam.Richards@taurich.org>
The OIIO reader never overrode pixel_picker_func(), so it inherited
MediaReader::default_pixel_picker, which returns an empty PixelInfo. The
colour pipeline needs at least three raw channels to derive the linear and
display rows, so for anything OIIO reads every row of the pixel probe showed
N/A - not just the code values.

That covers a lot of media, because the OIIO preferences mark DPX, PNG, TIFF
and JPEG as MRC_FORCE, which takes them away from the ffmpeg reader even
though ffmpeg also claims them.

Add oiio_buffer_pixel_picker, a C++ mirror of fetch_rgba_pixel in the OIIO
shader, reporting raw values for every format and code values for the
integer ones.

OIIO stores integer data that is not a whole number of bytes at the next byte
boundary, and expands it by replicating the high bits into the low bits
(BaseTypeConvertU10ToU16 in OIIO's libdpx: (v << 6) | (v >> 4)). Reporting
the stored value would therefore show 26214 for a 40% grey in a 10 bit DPX
rather than 409. The reader now carries oiio:BitsPerSample through the
shader params as native_bits_per_sample, and the picker shifts the replicated
bits back off. That inverts the expansion exactly for every 10 and 12 bit
value. PNG does not set oiio:BitsPerSample, so it falls back to the stored
depth, which is correct since PNG data is a whole number of bytes.

Measured on SMPTE HD bars at a 40% grey pixel:

    10 bit DPX   code R=G=B=409     raw 0.3998
    16 bit DPX   code R=G=B=26214   raw 0.4
    16 bit PNG   code R=G=B=26214   raw 0.4

Also turn the placeholder test into real coverage. The tests synthesize
images with known code values rather than depending on media outside the
repo, and cover the 10 bit round trip, 16 bit DPX and PNG, 8 bit PNG, the
three raw channels the colour pipeline needs, and out of bounds probes.

On macOS these tests need the companion change "cmake: give test targets the
same macOS defines as the libraries". Test targets currently miss the
__apple__ define the libraries get, so the '#undef nil' guards in atoms.hpp
and friends are skipped and caf::uuid::nil() fails to compile in any test
that includes media.hpp. That is pre-existing and breaks the stock
ffmpeg_test too. With the companion change these tests build and pass on
macOS; it is not part of this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Signed-off-by: Sam.Richards@taurich.org <Sam.Richards@taurich.org>
…formats

Pixel formats the GPU shader cannot unpack were converted by sws to 8 bit
RGBA on the CPU. That had three effects:

  - The YUV data was gone before the pixel picker ever saw it, and the RGBA
    path recorded no code values, so the probe read N/A for common
    uncompressed formats - uyvy422, v30xle (v410), uyva (v408) and rgb48be.

  - Display quality dropped to 8 bits, so a 10 bit v410 movie or a 16 bit
    PNG was being shown at 8 bits.

  - sws_getCachedContext never reads the frame's colourspace. Unless told
    otherwise with sws_setColorspaceDetails it uses SWS_CS_DEFAULT, which is
    SWS_CS_ITU601. So a stream correctly tagged BT.709 was converted as
    BT.601 anyway, giving visibly wrong colour.

Instead, repack each unsupported format to the closest one the shader already
handles, via shader_friendly_pix_format: packed YUV to planar at the same bit
depth and subsampling, and big endian RGB to little endian. These are lossless
repacks, so the original code values survive for the probe and the frame goes
down the planar path, where our own shader applies the matrix built from the
frame's colourspace in set_shader_pix_format_info. 8 bit input is never
promoted to a deeper format, since that would rescale its code values.

Only formats with no sensible lossless target still fall back to RGBA, and
that path now sets the stream's real coefficients and range on the sws
context, so it is colour correct too.

8 bit YUV with alpha (uyva) had no planar target at all, so add YUVA420P,
YUVA422P and YUVA444P to the supported formats, and read the alpha plane in
the 8 bit YUV branch of both the shader and its C++ mirror - previously only
the 10 and 12 bit branches did.

Fixes in the pixel picker along the way:

  - Report the YUV chroma channels as Cb and Cr rather than U and V.
  - Record code values for 8 bit packed RGB and RGBA, which recorded none.
  - The RGBA case returned all zeros and BGRA had its channels in the wrong
    order. Both now match the shader.
  - The alpha code value was being given the normalised 0-1 float, so it
    reported 0 or 1. It now reports the stored value.

Measured through the reader on a v408 (uyva, BT.709) SMPTE bars movie, at a
yellow bar:

    code Y=168 Cb=44 Cr=136 A=255
    raw  R=0.7503 G=0.7476 B=-0.0018

The code values match ffmpeg's own format=yuva444p extraction exactly, and
the raw values match BT.709 to four places. BT.601 would give 0.745, 0.798,
0.030.

shader_friendly_pix_format and sws_colourspace_from_av move out of the
anonymous namespace so they can be tested, and ffmpeg_test now links ffmpeg
directly, since the plugin links it privately.

As with the OIIO tests, on macOS these need the companion change "cmake: give
test targets the same macOS defines as the libraries" to build. With it they
build and pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Sam.Richards@taurich.org <Sam.Richards@taurich.org>
Signed-off-by: tedwaine <59868698+tedwaine@users.noreply.github.com>
Signed-off-by: Ted Waine <ted.waine@gmail.com>
Signed-off-by: Ted Waine <ted.waine@gmail.com>
@tedwaine
tedwaine merged commit bb05faa into main Oct 27, 2026
3 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.