19 private readonly VideoFrameMailbox _mailbox =
new();
20 private SKBitmap? _frame;
21 private bool _videoDisposed;
42 set {
if (_stretch != value) { _stretch = value; ForceRefresh(); } }
45 private double _playbackRate = 1.0;
71 _player.SetRate(value);
72 _playbackRate = value;
88 set => _player.Loop = value;
99 SceneLayer sceneLayer, Rectangle worldBounds,
string? name =
null)
100 : this(player,
VideoSource.FromUri(source), renderSurfaceHost, sceneLayer, worldBounds, name) { }
110 View view, Rectangle screenBounds,
string? name =
null)
111 : this(player,
VideoSource.FromUri(source), renderSurfaceHost, view, screenBounds, name) { }
121 SceneLayer sceneLayer, Rectangle worldBounds,
string? name =
null)
122 : this(playerFactory,
VideoSource.FromUri(source), renderSurfaceHost, sceneLayer, worldBounds, name) { }
132 View view, Rectangle screenBounds,
string? name =
null)
133 : this(playerFactory,
VideoSource.FromUri(source), renderSurfaceHost, view, screenBounds, name) { }
143 SceneLayer sceneLayer, Rectangle worldBounds,
string? name =
null)
154 View view, Rectangle screenBounds,
string? name =
null)
155 : this(() => player, source, renderSurfaceHost,
DirectDrawingMode.
View, null, view, screenBounds, null, name) { }
165 SceneLayer sceneLayer, Rectangle worldBounds,
string? name =
null)
176 View view, Rectangle screenBounds,
string? name =
null)
177 : this(playerFactory, source, renderSurfaceHost,
DirectDrawingMode.
View, null, view, screenBounds, null, name) { }
181 View? view, Rectangle? screenBounds, Rectangle? worldBounds,
string? name)
182 : base(renderSurfaceHost, mode, sceneLayer, view, screenBounds, worldBounds, name)
186 ArgumentNullException.ThrowIfNull(playerFactory);
187 ArgumentNullException.ThrowIfNull(source);
188 _player = playerFactory() ??
throw new ArgumentException(
"The factory returned no video player.", nameof(playerFactory));
190 source.Open(_player);
196 if (_player is not
null)
198 _player.FrameReady -= OnFrameReady;
199 _player.StateChanged -= OnPlayerStateChanged;
211 ObjectDisposedException.ThrowIf(_videoDisposed,
this);
212 ArgumentNullException.ThrowIfNull(source);
213 source.Open(_player);
214 _mailbox.Consume(ref _frame);
231 private void HookPlayer()
233 _player.FrameReady += OnFrameReady;
234 _player.StateChanged += OnPlayerStateChanged;
245 if (bmp is
null)
return;
247 var canvas = backbuffer.
Canvas;
248 var dest = ComputeDestRect(destRectScreen, bmp.Width, bmp.Height,
Stretch);
252 canvas.ClipRect(
new SKRect(destRectScreen.Left, destRectScreen.Top, destRectScreen.Right, destRectScreen.Bottom));
253 canvas.DrawBitmap(bmp, dest);
265 internal static SKRect ComputeDestRect(RectangleF bounds,
int srcW,
int srcH,
StretchMode mode)
268 var dst =
new SKRect(b.Left, b.Top, b.Right, b.Bottom);
269 if (mode ==
StretchMode.Fill || srcW <= 0 || srcH <= 0)
return dst;
271 float bw = b.Width, bh = b.Height;
272 float arSrc = (float)srcW / srcH;
273 float arDst = bw / bh;
276 return new SKRect(b.Left, b.Top, b.Left + srcW, b.Top + srcH);
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);
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);
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);
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);
306 private void OnFrameReady(
object? sender, VideoFrameReadyEventArgs e) => _mailbox.Publish(e);
308 private void OnPlayerStateChanged(
object? sender, VideoStateChangedEventArgs e)
310 if (e.
State is
"MediaOpening" or
"MediaOpened") _mailbox.Reset();
317 if (_videoDisposed)
return;
318 if (_mailbox.Consume(ref _frame)) ForceRefresh();
331 public void Play() => _player.Play();
340 public void Pause() => _player.Pause();
349 public void Stop() => _player.Stop();
376 public void Seek(TimeSpan position) => _player.Seek(position);
384 protected override void Dispose(
bool disposing)
386 if (!disposing) { base.Dispose(
false);
return; }
387 if (_videoDisposed)
return;
388 _videoDisposed =
true;
390 _player.FrameReady -= OnFrameReady;
391 _player.StateChanged -= OnPlayerStateChanged;
392 try { _player.Dispose(); }
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.
int int height NaturalSize
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.
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.
record VideoMetadata(VideoMetadataStatus Status, bool HasAudio, TimeSpan Duration)
An immutable metadata snapshot. Values are authoritative only when status is Ready.