diff --git a/docs/ENGINES.md b/docs/ENGINES.md index 7cff38f8..d8921e12 100644 --- a/docs/ENGINES.md +++ b/docs/ENGINES.md @@ -1407,6 +1407,9 @@ streams without a container: FFmpeg reports them as format `hevc` / `h264` (`Med encoded, never copied, because their frames carry no timestamps (a copy into MP4 had non-monotonic DTS). A stream without timing information is read at FFmpeg's default 25 fps, so a camera that recorded at another rate plays faster or slower; its frames are all kept. +CRI USM (`.usm`, read only, #485), the video of many games' cutscenes, is read by FFmpeg's `usm` demuxer and goes to +every video and audio target like the other read-only containers. Encrypted USM files (common in recent games) can't +be read and fail with FFmpeg's error. A Video CD's `MPEGAV/AVSEQ01.DAT` (MPEG-1 in a RIFF/CDXA wrapper) counts as MPEG: `.dat` is an extension of that format, and FFmpeg reads what the file holds, so a `.dat` with no video in it fails instead of making a file. diff --git a/src/Filee.Core/Formats/FormatRegistry.cs b/src/Filee.Core/Formats/FormatRegistry.cs index 24c69e5d..333a0184 100644 --- a/src/Filee.Core/Formats/FormatRegistry.cs +++ b/src/Filee.Core/Formats/FormatRegistry.cs @@ -224,6 +224,8 @@ public static class FormatRegistry new("swf", "SWF", FormatCategory.Video, ["swf"], Writable: false), new("wtv", "WTV", FormatCategory.Video, ["wtv", "dvr-ms", "dvr"], Writable: false), new("cavs", "CAVS", FormatCategory.Video, ["cavs"], Writable: false), + // CRI Middleware's video for game cutscenes (#485): FFmpeg's usm demuxer reads unencrypted ones. + new("usm", "USM", FormatCategory.Video, ["usm"], Writable: false), // Raw H.265 / H.264 streams with no container, as security cameras and VMS software export them (issue #301). new("h265", "H.265", FormatCategory.Video, ["h265", "265", "hevc"], Writable: false), new("h264", "H.264", FormatCategory.Video, ["h264", "264"], Writable: false), diff --git a/src/Filee.Core/Settings/AppSettings.cs b/src/Filee.Core/Settings/AppSettings.cs index 65c287f4..f9baf498 100644 --- a/src/Filee.Core/Settings/AppSettings.cs +++ b/src/Filee.Core/Settings/AppSettings.cs @@ -123,7 +123,7 @@ public static bool IsFlipKeyAllowed(string key, IEnumerable gest public sealed class AppSettings { /// Current schema version. Increase when the format changes and add a migration. - public const int CurrentSchemaVersion = 43; + public const int CurrentSchemaVersion = 44; public int SchemaVersion { get; set; } = CurrentSchemaVersion; diff --git a/src/Filee.Core/Settings/UserDataStore.cs b/src/Filee.Core/Settings/UserDataStore.cs index 8c9d1f46..5a996e06 100644 --- a/src/Filee.Core/Settings/UserDataStore.cs +++ b/src/Filee.Core/Settings/UserDataStore.cs @@ -840,6 +840,9 @@ public static bool ApplyToLibrary(int from, List presets, List p.Id == "video"); + video.Extensions.Remove("usm"); + + Assert.True(SettingsMigrations.ApplyToLibrary(43, presets, profiles)); + + Assert.Contains("usm", video.Extensions); + Assert.False(SettingsMigrations.ApplyToLibrary(43, presets, profiles)); + } +} diff --git a/tests/Filee.Engines.Tests/MediaTests.cs b/tests/Filee.Engines.Tests/MediaTests.cs index 51fc46e3..0bfe086f 100644 --- a/tests/Filee.Engines.Tests/MediaTests.cs +++ b/tests/Filee.Engines.Tests/MediaTests.cs @@ -23,6 +23,17 @@ public sealed class FfmpegCollection; [Collection(nameof(FfmpegCollection))] public class MediaTests(EngineFixture fx) : IClassFixture { + [Fact] + public void Cri_usm_game_videos_go_to_mp4_through_ffmpeg() + { + // #485: read only, through FFmpeg's usm demuxer; never a step towards something else. + SkipUnlessInstalled(); + var route = fx.Catalog.CreatePlanner().Plan("usm", "mp4"); + Assert.NotNull(route); + Assert.Equal("ffmpeg", Assert.Single(route.Steps).Converter.Id); + Assert.Null(fx.Catalog.CreatePlanner().Plan("mp4", "usm")); + } + private static readonly (string Ffmpeg, string Ffprobe)? Tools = FfmpegConverter.Locate(); private static void SkipUnlessInstalled() =>