Skip to content

Feature/FFMPEG_Core : Rewrite parts of the video playbacks and Render To File.#912

Draft
ExtraBinoss wants to merge 31 commits intotixl3d:mainfrom
ExtraBinoss:feature/ffmpeg_core
Draft

Feature/FFMPEG_Core : Rewrite parts of the video playbacks and Render To File.#912
ExtraBinoss wants to merge 31 commits intotixl3d:mainfrom
ExtraBinoss:feature/ffmpeg_core

Conversation

@ExtraBinoss
Copy link
Contributor

@ExtraBinoss ExtraBinoss commented Jan 10, 2026

This is the first steps of the FFMPEG video playback and encoding rewrite.

This will include (but not limited to) :

  • FFMpeg Playback [PlayVideo] [PlayVideoClip]
  • Render to file (with FFMPEG) supporting Mp4 (LibOpenH264), Webm, WebmAlpha (if working), HAP, HAP Alpha
  • Testing render using ffmpeg with Linux and MacOS devices, which should be working better, since we would be using industry standards.

Right now this feature branch I made includes the following :

  • FFmpeg Render in the Window --> FFmpegRender (next to Render to File)
  • You can render a video using seconds mode (didn't try the others. Didn't bother for the moment)
  • Codecs tested and working are : Webm (VP9), ProRes, HAP, HAP Alpha, MP4 with Libopenh264 works.
  • Image sequence work with auto increment

Roadblocks :

  • FFMpeg licensing is hard, the library we are using FFmpegCore
  • This library is using a GPL compiled version, with LibX264, LibX265 with are contaminating the whole binary, and thus cannot be used in Tixl (it would override the MIT licence).
  • UX is the most important, shipping it in the installation of Tixl is probably the best thing, even though it increases filesize. However if users have to download them, it's a big dealbreaker.
  • We need to precompile the library in the setup.exe of Tixl, with flags like :
Format / Feature Requirement Recommended Flags Patent Risk
Legal: License LGPL v3 --disable-gpl --disable-nonfree --enable-version3 None
Legal: Linking Dynamic --enable-shared --disable-static None
WebM (VP9) Encoder/Decoder --enable-libvpx Royalty Free
WebM (AV1) Encoder/Decoder --enable-libaom --enable-libsvtav1 Royalty Free
MP4 (H.264) "Open" CPU Encoder --enable-libopenh264 Paid by Cisco*
MP4 (H.264) NVIDIA GPU --enable-nvenc Paid by NVIDIA
MP4 (H.264) Intel GPU --enable-libmfx Paid by Intel
MP4 (H.264) AMD GPU --enable-amf Paid by AMD
HAP / HAP Alpha Encoder/Decoder (Native) Royalty Free
ProRes / Alpha Encoder/Decoder (Native) Apple "Unauthorized"
MP3 Encoder/Decoder --enable-libmp3lame Expired (Free)
OGG (Vorbis) Encoder/Decoder --enable-libvorbis Royalty Free
OGG (Opus) Encoder/Decoder --enable-libopus Royalty Free

Ideas :
We could add av1 encoding since it's better and open-source. We will see if the library and ffmpeg supports it.

Good luck!

…wn implem of FFMpegCore to support libopenh264), WEBM (without alpha doesn't work) (maybe we can fix in our implementation), HAP and HAP alpha works.

We need to recompile into a LGPL version + shared libs, probably that the user downloads intentionally.
Copilot AI review requested due to automatic review settings January 10, 2026 00:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an FFMpeg-based video rendering system as an alternative to the existing render-to-file functionality. The implementation adds a new window for FFMpeg rendering with support for multiple codecs (H.264, H.265, ProRes, HAP, VP9) and includes automatic FFMpeg binary download functionality.

Key Changes:

  • New FFMpeg-based rendering pipeline with support for multiple video codecs and formats
  • Automatic FFMpeg binary download and installation functionality via FFMpegCore library
  • Integration of FFMpeg rendering into the existing editor UI as a separate window

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 37 comments.

Show a summary per file
File Description
Editor/Gui/Windows/RenderExport/FFMpegRenderWindow.cs New UI window for FFMpeg rendering with time range controls, codec selection, and render settings
Editor/Gui/Windows/RenderExport/FFMpegRenderSettings.cs Settings class defining render parameters, codecs, and file format mappings
Editor/Gui/Windows/RenderExport/FFMpegRenderProcess.cs Core rendering process manager handling frame updates, FFMpeg installation, and export lifecycle
Editor/Gui/Windows/RenderExport/FFMpeg/FFMpegVideoWriter.cs Video writer implementation handling GPU texture readback and FFMpeg encoding via pipes
Editor/Gui/Windows/Layouts/WindowManager.cs Registers the new FFMpegRenderWindow in the window management system
Editor/Gui/T3Ui.Update.cs Integrates FFMpegRenderProcess.Update() into the main update loop
Editor/Editor.csproj Adds FFMpegCore and FFMpegCore.Extensions.Downloader NuGet package references

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

internal static class FFMpegRenderProcess
{
public static string LastHelpString { get; private set; } = string.Empty;
public static double Progress => _frameCount <= 1 ? 0.0 : (_frameIndex / (double)(_frameCount - 1));
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The progress calculation uses _frameCount - 1 in the denominator, which will cause division by zero if _frameCount is 1. While rendering a single frame might be unusual, this should be handled gracefully. Additionally, the check should be _frameCount <= 0 rather than <= 1 to properly handle edge cases.

Suggested change
public static double Progress => _frameCount <= 1 ? 0.0 : (_frameIndex / (double)(_frameCount - 1));
public static double Progress => _frameCount <= 0
? 0.0
: _frameCount == 1
? (_frameIndex > 0 ? 1.0 : 0.0)
: (_frameIndex / (double)(_frameCount - 1));

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +131
if (_renderSettings.Bitrate > 0 && true) // Check auto-increment?
{
// Maybe implement increment if settings have it
}

Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition check "true" is a hardcoded boolean that makes the entire conditional statement pointless. Either remove the condition or implement the actual auto-increment logic that was intended here.

Suggested change
if (_renderSettings.Bitrate > 0 && true) // Check auto-increment?
{
// Maybe implement increment if settings have it
}

Copilot uses AI. Check for mistakes.
Comment on lines +231 to +244
DownloadProgress = 0f;

try
{
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);

Log.Info($"Downloading FFMpeg to {folder}...");

// Progress not supported in this version of FFMpegCore.Extensions.Downloader
await FFMpegDownloader.DownloadBinaries(FFMpegCore.Extensions.Downloader.Enums.FFMpegVersions.LatestAvailable,
options: new FFOptions { BinaryFolder = folder });

DownloadProgress = 1.0f;
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DownloadProgress field is updated from an async context (InstallFFMpeg) and read from the UI thread without synchronization. This could cause visibility issues across threads. Consider using volatile keyword or proper synchronization mechanisms.

Copilot uses AI. Check for mistakes.
FormInputs.AddVerticalSpace();

FormInputs.AddFloat("FPS", ref FFMpegRenderSettings.Fps, 0);
if (FFMpegRenderSettings.Fps < 0) FFMpegRenderSettings.Fps = -FFMpegRenderSettings.Fps;
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FPS value validation on line 185 uses absolute value to convert negative FPS to positive, but negative FPS could indicate a logic error elsewhere that should be caught and reported rather than silently fixed. Consider logging a warning or throwing an exception for invalid FPS values.

Suggested change
if (FFMpegRenderSettings.Fps < 0) FFMpegRenderSettings.Fps = -FFMpegRenderSettings.Fps;
if (FFMpegRenderSettings.Fps < 0)
{
System.Diagnostics.Debug.WriteLine($"[FFMpegRenderWindow] Invalid negative FPS value ({FFMpegRenderSettings.Fps}) encountered. Converting to a positive value.");
FFMpegRenderSettings.Fps = -FFMpegRenderSettings.Fps;
}

Copilot uses AI. Check for mistakes.
private static int _frameIndex;
private static int _frameCount;

private static RenderSettings _renderSettings = null!; // Using standard settings for timing helpers
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field _renderSettings is marked with null! but is only initialized when TryStart is called. This creates a potential null reference issue if Update() is called before TryStart() and uses _renderSettings in RenderTiming methods. Consider initializing with a default value or adding null checks.

Suggested change
private static RenderSettings _renderSettings = null!; // Using standard settings for timing helpers
private static RenderSettings _renderSettings = new RenderSettings(); // Using standard settings for timing helpers

Copilot uses AI. Check for mistakes.

if (FFMpegRenderProcess.State == FFMpegRenderProcess.States.NoValidOutputType)
{
_lastHelpString = FFMpegRenderProcess.MainOutputType == null
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write to static field from instance method, property, or constructor.

Copilot uses AI. Check for mistakes.
return;
}

_lastHelpString = "Ready to render.";
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Write to static field from instance method, property, or constructor.

Copilot uses AI. Check for mistakes.
var height = cpuAccessTexture.Description.Height;
var rowStride = SharpDX.WIC.PixelFormat.GetStride(SharpDX.WIC.PixelFormat.Format32bppRGBA, width);

var dataBox = ResourceManager.Device.ImmediateContext.MapSubresource(cpuAccessTexture, 0, 0, MapMode.Read, MapFlags.None, out var inputStream);
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is manually disposed in a finally block - consider a C# using statement as a preferable resource management technique.

Copilot uses AI. Check for mistakes.
// Actually, let's keep the structure similar.

public TimeRanges TimeRange = TimeRanges.Custom;
public float ResolutionFactor = 1f;
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field 'ResolutionFactor' can be 'readonly'.

Suggested change
public float ResolutionFactor = 1f;
public readonly float ResolutionFactor = 1f;

Copilot uses AI. Check for mistakes.
public int OverrideMotionBlurSamples; // forwarded for operators that might read it


public RenderModes RenderMode = RenderModes.Video;
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field 'RenderMode' can be 'readonly'.

Suggested change
public RenderModes RenderMode = RenderModes.Video;
public readonly RenderModes RenderMode = RenderModes.Video;

Copilot uses AI. Check for mistakes.
enderprism and others added 21 commits January 10, 2026 18:28
…ing and breathes finally. mostly for ffmeg render right now.
@ExtraBinoss ExtraBinoss marked this pull request as draft January 17, 2026 15:56
@enderprism
Copy link
Contributor

the FFMpegRenderWindow is missing some new stuff from RenderWindow

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.

3 participants