Gondwana Game Engine Development (master) - 2.6.1
Gondwana is a cross-platform 2.5D game and rendering engine written in C#/.NET 8. It provides fine-grained control over rendering, timing, and scene composition, with built-in support for parallax, z-ordering, pixel overhang, collision detection, and particle effects. Gondwana targets desktop and web platforms using SkiaSharp for graphics and pluggable audio backends for desktop and browser targets.
Loading...
Searching...
No Matches
DirectVideo.cs
1using System.Drawing;
7using SkiaSharp;
8
10
17{
18 private readonly IVideoPlayer _player;
19 private readonly VideoFrameMailbox _mailbox = new();
20 private SKBitmap? _frame;
21 private bool _videoDisposed;
22 private StretchMode _stretch = StretchMode.Fill;
23
24 // ---- knobs ----
25
40 {
41 get => _stretch;
42 set { if (_stretch != value) { _stretch = value; ForceRefresh(); } }
43 }
44
45 private double _playbackRate = 1.0;
46
66 public double PlaybackRate
67 {
68 get => _playbackRate;
69 set
70 {
71 _player.SetRate(value);
72 _playbackRate = value;
73 }
74 }
75
85 public bool Loop
86 {
87 get => _player.Loop;
88 set => _player.Loop = value;
89 }
90
98 public DirectVideo(IVideoPlayer player, Uri source, RenderSurfaceHostBase renderSurfaceHost,
99 SceneLayer sceneLayer, Rectangle worldBounds, string? name = null)
100 : this(player, VideoSource.FromUri(source), renderSurfaceHost, sceneLayer, worldBounds, name) { }
101
109 public DirectVideo(IVideoPlayer player, Uri source, RenderSurfaceHostBase renderSurfaceHost,
110 View view, Rectangle screenBounds, string? name = null)
111 : this(player, VideoSource.FromUri(source), renderSurfaceHost, view, screenBounds, name) { }
112
120 public DirectVideo(Func<IVideoPlayer> playerFactory, Uri source, RenderSurfaceHostBase renderSurfaceHost,
121 SceneLayer sceneLayer, Rectangle worldBounds, string? name = null)
122 : this(playerFactory, VideoSource.FromUri(source), renderSurfaceHost, sceneLayer, worldBounds, name) { }
123
131 public DirectVideo(Func<IVideoPlayer> playerFactory, Uri source, RenderSurfaceHostBase renderSurfaceHost,
132 View view, Rectangle screenBounds, string? name = null)
133 : this(playerFactory, VideoSource.FromUri(source), renderSurfaceHost, view, screenBounds, name) { }
134
142 public DirectVideo(IVideoPlayer player, VideoSource source, RenderSurfaceHostBase renderSurfaceHost,
143 SceneLayer sceneLayer, Rectangle worldBounds, string? name = null)
144 : this(() => player, source, renderSurfaceHost, DirectDrawingMode.SceneLayer, sceneLayer, null, null, worldBounds, name) { }
145
153 public DirectVideo(IVideoPlayer player, VideoSource source, RenderSurfaceHostBase renderSurfaceHost,
154 View view, Rectangle screenBounds, string? name = null)
155 : this(() => player, source, renderSurfaceHost, DirectDrawingMode.View, null, view, screenBounds, null, name) { }
156
164 public DirectVideo(Func<IVideoPlayer> playerFactory, VideoSource source, RenderSurfaceHostBase renderSurfaceHost,
165 SceneLayer sceneLayer, Rectangle worldBounds, string? name = null)
166 : this(playerFactory, source, renderSurfaceHost, DirectDrawingMode.SceneLayer, sceneLayer, null, null, worldBounds, name) { }
167
175 public DirectVideo(Func<IVideoPlayer> playerFactory, VideoSource source, RenderSurfaceHostBase renderSurfaceHost,
176 View view, Rectangle screenBounds, string? name = null)
177 : this(playerFactory, source, renderSurfaceHost, DirectDrawingMode.View, null, view, screenBounds, null, name) { }
178
179 private DirectVideo(Func<IVideoPlayer> playerFactory, VideoSource source,
180 RenderSurfaceHostBase renderSurfaceHost, DirectDrawingMode mode, SceneLayer? sceneLayer,
181 View? view, Rectangle? screenBounds, Rectangle? worldBounds, string? name)
182 : base(renderSurfaceHost, mode, sceneLayer, view, screenBounds, worldBounds, name)
183 {
184 try
185 {
186 ArgumentNullException.ThrowIfNull(playerFactory);
187 ArgumentNullException.ThrowIfNull(source);
188 _player = playerFactory() ?? throw new ArgumentException("The factory returned no video player.", nameof(playerFactory));
189 HookPlayer();
190 source.Open(_player);
191 _player.Play();
192 }
193 catch
194 {
195 _mailbox.Dispose();
196 if (_player is not null)
197 {
198 _player.FrameReady -= OnFrameReady;
199 _player.StateChanged -= OnPlayerStateChanged;
200 _player.Dispose();
201 }
202 base.Dispose(true); // Construction registered this drawing; undo on failure.
203 throw;
204 }
205 }
206
209 public void Open(VideoSource source)
210 {
211 ObjectDisposedException.ThrowIf(_videoDisposed, this);
212 ArgumentNullException.ThrowIfNull(source);
213 source.Open(_player);
214 _mailbox.Consume(ref _frame); // Clear the prior source immediately on this engine thread.
215 ForceRefresh();
216 _player.Play();
217 }
218
221 public void Open(Uri source) => Open(VideoSource.FromUri(source));
222
224 public VideoMetadata Metadata => _player.Metadata;
225
227 public (int width, int height) NaturalSize => _player.NaturalSize;
231 private void HookPlayer()
232 {
233 _player.FrameReady += OnFrameReady;
234 _player.StateChanged += OnPlayerStateChanged;
235 }
236
242 protected override void OnDraw(BackbufferBase backbuffer, RectangleF destRectScreen)
243 {
244 var bmp = _frame;
245 if (bmp is null) return;
246
247 var canvas = backbuffer.Canvas;
248 var dest = ComputeDestRect(destRectScreen, bmp.Width, bmp.Height, Stretch);
249
250 // DirectDrawingBase.Draw applies opacity and fades once.
251 canvas.Save();
252 canvas.ClipRect(new SKRect(destRectScreen.Left, destRectScreen.Top, destRectScreen.Right, destRectScreen.Bottom));
253 canvas.DrawBitmap(bmp, dest);
254 canvas.Restore();
255 }
256
265 internal static SKRect ComputeDestRect(RectangleF bounds, int srcW, int srcH, StretchMode mode)
266 {
267 var b = bounds;
268 var dst = new SKRect(b.Left, b.Top, b.Right, b.Bottom);
269 if (mode == StretchMode.Fill || srcW <= 0 || srcH <= 0) return dst;
270
271 float bw = b.Width, bh = b.Height;
272 float arSrc = (float)srcW / srcH;
273 float arDst = bw / bh;
274
275 if (mode == StretchMode.None)
276 return new SKRect(b.Left, b.Top, b.Left + srcW, b.Top + srcH);
277
278 if (mode == StretchMode.Uniform)
279 {
280 if (arSrc > arDst)
281 {
282 float w = bw, h = w / arSrc, y = b.Top + (bh - h) / 2f;
283 return new SKRect(b.Left, y, b.Left + w, y + h);
284 }
285 else
286 {
287 float h = bh, w = h * arSrc, x = b.Left + (bw - w) / 2f;
288 return new SKRect(x, b.Top, x + w, b.Top + h);
289 }
290 }
291
292 // UniformToFill
293 if (arSrc < arDst)
294 {
295 float w = bw, h = w / arSrc, y = b.Top + (bh - h) / 2f;
296 return new SKRect(b.Left, y, b.Left + w, y + h);
297 }
298 else
299 {
300 float h = bh, w = h * arSrc, x = b.Left + (bw - w) / 2f;
301 return new SKRect(x, b.Top, x + w, b.Top + h);
302 }
303 }
304
305 // The event pointer is valid only during the callback; copy before returning.
306 private void OnFrameReady(object? sender, VideoFrameReadyEventArgs e) => _mailbox.Publish(e);
307
308 private void OnPlayerStateChanged(object? sender, VideoStateChangedEventArgs e)
309 {
310 if (e.State is "MediaOpening" or "MediaOpened") _mailbox.Reset();
311 }
312
315 public override void Update(long tick)
316 {
317 if (_videoDisposed) return;
318 if (_mailbox.Consume(ref _frame)) ForceRefresh();
319 base.Update(tick);
320 }
321 // --- control surface (thin passthrough to player) ---
322
331 public void Play() => _player.Play();
332
340 public void Pause() => _player.Pause();
341
349 public void Stop() => _player.Stop();
350
376 public void Seek(TimeSpan position) => _player.Seek(position);
377
378 // --- cleanup ---
379
384 protected override void Dispose(bool disposing)
385 {
386 if (!disposing) { base.Dispose(false); return; }
387 if (_videoDisposed) return;
388 _videoDisposed = true;
389 _mailbox.Dispose();
390 _player.FrameReady -= OnFrameReady;
391 _player.StateChanged -= OnPlayerStateChanged;
392 try { _player.Dispose(); }
393 finally
394 {
395 _frame?.Dispose();
396 _frame = null;
397 base.Dispose(true);
398 }
399 }
400}
View? View
Gets the view to which this direct drawing is attached, or null if the drawing is in scene-layer mode...
SceneLayer? SceneLayer
Gets the scene layer to which this direct drawing is attached, or null if the drawing is in view mode...
DirectDrawingMovableBase(RenderSurfaceHostBase renderSurfaceHost, DirectDrawingMode mode, SceneLayer? sceneLayer, View? view, Rectangle? screenBounds, Rectangle? worldBounds, string? name=null)
Initializes a new instance of the DirectDrawingMovableBase class with integrated movement capabilitie...
double PlaybackRate
Gets or sets the playback speed multiplier.
DirectVideo(Func< IVideoPlayer > playerFactory, Uri source, RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, string? name=null)
Creates a screen-space video using an owned player from the factory.
void Open(Uri source)
Replaces the URI source and starts playback on the engine/UI thread.
DirectVideo(Func< IVideoPlayer > playerFactory, VideoSource source, RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, string? name=null)
Creates a world-space URI/stream/GAF video using an owned player from the factory.
DirectVideo(IVideoPlayer player, VideoSource source, RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, string? name=null)
Creates a screen-space URI/stream/GAF video and starts playback. Owns the player.
int width
Decoded source dimensions, or (0,0) until known.
override void Dispose(bool disposing)
Releases the unmanaged resources used by the DirectVideo and optionally releases the managed resource...
DirectVideo(IVideoPlayer player, Uri source, RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, string? name=null)
Creates a screen-space video with required presentation bounds. Owns the player.
DirectVideo(IVideoPlayer player, Uri source, RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, string? name=null)
Creates a world-space video with required presentation bounds. Owns the player.
override void OnDraw(BackbufferBase backbuffer, RectangleF destRectScreen)
Renders the current video frame to the backbuffer at the specified destination rectangle.
DirectVideo(Func< IVideoPlayer > playerFactory, VideoSource source, RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, string? name=null)
Creates a screen-space URI/stream/GAF video using an owned player from the factory.
DirectVideo(IVideoPlayer player, VideoSource source, RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, string? name=null)
Creates a world-space URI/stream/GAF video and starts playback. Owns the player.
void Stop()
Stops video playback and resets the playback position to the beginning.
void Pause()
Pauses video playback at the current position.
void Seek(TimeSpan position)
Seeks to a specific position in the video timeline.
void Play()
Starts or resumes video playback.
DirectVideo(Func< IVideoPlayer > playerFactory, Uri source, RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, string? name=null)
Creates a world-space video using an owned player from the factory.
void Open(VideoSource source)
Replaces the source and starts playback. Call on the engine/UI thread.
override void Update(long tick)
Consumes the latest decoded frame on the engine thread, then advances fades.
bool Loop
Gets or sets a value indicating whether the video should automatically restart from the beginning whe...
StretchMode Stretch
Gets or sets the stretch mode that determines how the video frame is scaled to fit within the destina...
VideoMetadata Metadata
Metadata snapshot for the active source; Pending values are not authoritative.
Represents a base class for managing a graphical backbuffer, and is the in-memory surface where rende...
SKCanvas Canvas
Gets the SkiaSharp canvas used for drawing operations on this backbuffer.
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
A URI, borrowed/owned stream, or GAF asset to open with a video backend.
Definition VideoSource.cs:7
static VideoSource FromUri(Uri source)
Creates a reusable URI source.
string State
Gets the current state of the video player.
Defines the contract for a video player that can play, pause, stop, and seek video content.
void Play()
Starts or resumes playback of the video.
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
@ View
Performs direct drawing at the view level using screen-space coordinates.
@ SceneLayer
Performs direct drawing within a scene layer using world-space coordinates.
StretchMode
Defines how video content should be stretched or scaled to fit within its display bounds.
Definition StretchMode.cs:7
record VideoMetadata(VideoMetadataStatus Status, bool HasAudio, TimeSpan Duration)
An immutable metadata snapshot. Values are authoritative only when status is Ready.