1- using Microsoft . UI . Xaml ;
1+ using Hi3Helper . Win32 . Native . Enums . DXGI ;
2+ using Hi3Helper . Win32 . Native . Structs . DXGI ;
3+ using Hi3Helper . Win32 . WinRT . SwapChainPanelHelper ;
4+ using Microsoft . Graphics . Canvas ;
5+ using Microsoft . Graphics . Canvas . UI . Xaml ;
6+ using Microsoft . Graphics . DirectX ;
7+ using Microsoft . UI . Xaml ;
28using Microsoft . UI . Xaml . Controls ;
39using Microsoft . UI . Xaml . Data ;
4- using Microsoft . UI . Xaml . Media ;
510using Microsoft . UI . Xaml . Media . Animation ;
611using System ;
712using System . Collections . Generic ;
813using System . Diagnostics . CodeAnalysis ;
914using System . IO ;
1015using System . Linq ;
1116using System . Runtime . CompilerServices ;
17+ using System . Runtime . InteropServices ;
1218using System . Threading ;
1319using System . Threading . Tasks ;
20+ using Windows . Foundation ;
21+ using Windows . Graphics . Capture ;
22+ using Windows . Graphics . DirectX ;
23+ using Windows . Media . Playback ;
24+ using Windows . Storage . Streams ;
25+ using WinRT . Interop ;
1426
1527// ReSharper disable StringLiteralTypo
1628// ReSharper disable CommentTypo
@@ -79,6 +91,13 @@ private enum MediaSourceType
7991
8092 #endregion
8193
94+ #region Fields
95+
96+ private bool _isResizingVideoCanvas ;
97+ private bool _isDrawingVideoFrame ;
98+
99+ #endregion
100+
82101 #region Loaders
83102
84103 private static void PlaceholderSource_OnChange ( DependencyObject d , DependencyPropertyChangedEventArgs e )
@@ -150,6 +169,13 @@ private async void LoadFromSourceAsyncDetached(
150169 {
151170 try
152171 {
172+ if ( IsInPreloadGrid ( ) )
173+ {
174+ return ;
175+ }
176+
177+ DisposeVideoPlayer ( ) ;
178+
153179 object ? source = GetValue ( sourceProperty ) ;
154180 if ( source is null )
155181 {
@@ -185,16 +211,13 @@ await LoadVideoFromSourceAsync(source,
185211 stretchProperty ,
186212 horizontalAlignmentProperty ,
187213 verticalAlignmentProperty ,
188- nameof ( instance . IsAudioEnabled ) ,
189- nameof ( instance . AudioVolume ) ,
190214 instance ,
191215 grid ) )
192216 {
193217 return ;
194218 }
195219
196220 ClearGrid :
197- DisposeMediaPlayerElements ( grid ) ;
198221 ClearMediaGrid ( grid ) ;
199222 }
200223 catch
@@ -203,27 +226,6 @@ await LoadVideoFromSourceAsync(source,
203226 }
204227 }
205228
206- private static async ValueTask < bool > LoadVideoFromSourceAsync (
207- object ? source ,
208- string stretchProperty ,
209- string horizontalAlignmentProperty ,
210- string verticalAlignmentProperty ,
211- string isAudioEnabledProperty ,
212- string audioVolumeProperty ,
213- LayeredBackgroundImage instance ,
214- Grid grid )
215- {
216- try
217- {
218- return false ;
219- }
220- catch ( Exception e )
221- {
222- Console . WriteLine ( e ) ;
223- return false ;
224- }
225- }
226-
227229 private static async ValueTask < bool > LoadImageFromSourceAsync (
228230 object ? source ,
229231 string stretchProperty ,
@@ -238,12 +240,13 @@ private static async ValueTask<bool> LoadImageFromSourceAsync(
238240 Image image = new ( ) ;
239241
240242 // Bind property
241- image . BindProperty ( instance , stretchProperty , Image . StretchProperty , BindingMode . OneWay ) ;
243+ image . BindProperty ( instance , stretchProperty , Image . StretchProperty , BindingMode . OneWay ) ;
242244 image . BindProperty ( instance , horizontalAlignmentProperty , HorizontalAlignmentProperty , BindingMode . OneWay ) ;
243- image . BindProperty ( instance , verticalAlignmentProperty , VerticalAlignmentProperty , BindingMode . OneWay ) ;
245+ image . BindProperty ( instance , verticalAlignmentProperty , VerticalAlignmentProperty , BindingMode . OneWay ) ;
244246
245247 image . Transitions . Add ( new ContentThemeTransition ( ) ) ;
246248 grid . Children . Add ( image ) ;
249+
247250 image . Tag = ( grid , instance ) ;
248251 image . ImageOpened += Image_ImageOpened ;
249252
@@ -276,6 +279,192 @@ private static async ValueTask<bool> LoadImageFromSourceAsync(
276279 }
277280 }
278281
282+ private static async Task < bool > LoadVideoFromSourceAsync (
283+ object ? source ,
284+ string stretchProperty ,
285+ string horizontalAlignmentProperty ,
286+ string verticalAlignmentProperty ,
287+ LayeredBackgroundImage instance ,
288+ Grid grid )
289+ {
290+ // Setting up video player
291+ instance . SetupVideoPlayer ( ) ;
292+ MediaPlayer player = instance . _videoPlayer ;
293+ player . MediaOpened += InitializeVideoFrameOnMediaOpened ;
294+
295+ if ( instance . MediaCacheHandler is { } cacheHandler )
296+ {
297+ MediaCacheResult cacheResult = await cacheHandler . LoadCachedSource ( source ) ;
298+ source = cacheResult . CachedSource ;
299+ }
300+
301+ Uri ? sourceUri = source as Uri ;
302+
303+ if ( sourceUri == null &&
304+ source is string asStringSource )
305+ {
306+ sourceUri = asStringSource . GetStringAsUri ( ) ;
307+ }
308+
309+ Stream ? sourceStream = null ;
310+ if ( source is Stream { CanSeek : true , CanRead : true } asSeekableStream )
311+ {
312+ sourceStream = asSeekableStream ;
313+ }
314+
315+ if ( sourceStream == null &&
316+ sourceUri == null )
317+ {
318+ goto CleanupAndReturnFalse ;
319+ }
320+
321+ // Assign media source
322+ if ( sourceStream != null )
323+ {
324+ IRandomAccessStream ? sourceStreamRandom = sourceStream . AsRandomAccessStream ( ) ;
325+ player . SetStreamSource ( sourceStreamRandom ) ;
326+ }
327+ else if ( sourceUri != null )
328+ {
329+ player . SetUriSource ( sourceUri ) ;
330+ }
331+ else
332+ {
333+ goto CleanupAndReturnFalse ;
334+ }
335+
336+ return true ;
337+
338+ CleanupAndReturnFalse :
339+ player . MediaOpened -= InitializeVideoFrameOnMediaOpened ;
340+ return false ;
341+
342+ void InitializeVideoFrameOnMediaOpened ( MediaPlayer sender , object args )
343+ {
344+ instance . DispatcherQueue . TryEnqueue ( ( ) =>
345+ {
346+ // Create instance
347+ Image image = new ( )
348+ {
349+ Tag = ( grid , instance )
350+ } ;
351+
352+ // Bind property
353+ image . BindProperty ( instance ,
354+ stretchProperty ,
355+ Image . StretchProperty ,
356+ BindingMode . OneWay ) ;
357+ image . BindProperty ( instance ,
358+ horizontalAlignmentProperty ,
359+ HorizontalAlignmentProperty ,
360+ BindingMode . OneWay ) ;
361+ image . BindProperty ( instance ,
362+ verticalAlignmentProperty ,
363+ VerticalAlignmentProperty ,
364+ BindingMode . OneWay ) ;
365+
366+ instance . InitializeCanvasBitmapSource ( image , sender . PlaybackSession ) ;
367+
368+ // Register events
369+ image . Loaded += Image_VideoFrameOnLoaded ;
370+ image . Unloaded += Image_VideoFrameOnUnloaded ;
371+
372+ // Add to children
373+ image . Transitions . Add ( new ContentThemeTransition ( ) ) ;
374+ grid . Children . Add ( image ) ;
375+ instance . _videoPlayer . Play ( ) ;
376+ } ) ;
377+ }
378+ }
379+
380+ private unsafe void VideoPlayer_VideoFrameAvailable ( MediaPlayer sender , object args )
381+ {
382+ if ( _canvasImageSource == null )
383+ {
384+ return ;
385+ }
386+
387+ // Use Interlock to ensure thre session is only created one time. But with one downside that
388+ // the first frame will always be skipped due to atomic operation.
389+ // Note:
390+ // _currentCanvasDrawingSession is ensured to expect null first, then create and exit.
391+ // The next frame will be drawn.
392+ CanvasDrawingSession ? session = null ;
393+ if ( ( session = Interlocked . Exchange ( ref _currentCanvasDrawingSession , null ) ) == null )
394+ {
395+ _currentCanvasDrawingSession = _canvasImageSource . CreateDrawingSession ( default , _currentCanvasRenderSize ) ;
396+ return ;
397+ }
398+
399+ try
400+ {
401+ #if USENATIVESWAPCHAIN
402+ DXGI_PRESENT_PARAMETERS parameter = default ;
403+
404+ var swapChain = _swapChainContext . DXGISwapChain ;
405+ var surface = _swapChainContext . Direct3DSurface ;
406+
407+ Rect renderArea = new ( 0 , 0 , _canvasWidth , _canvasHeight ) ;
408+ // CanvasDrawingSession? canvasSession = _canvasImageSource.CreateDrawingSession(default);
409+ sender . CopyFrameToVideoSurface ( surface , renderArea ) ;
410+ swapChain ? . Present1 ( 1 , 0 , in parameter ) ;
411+ #else
412+ sender . CopyFrameToVideoSurface ( _canvasRenderTarget ) ;
413+ session . DrawImage ( _canvasRenderTarget ) ;
414+
415+ if ( DispatcherQueue . HasThreadAccess )
416+ {
417+ FinalizeFrame ( ) ;
418+ }
419+ else
420+ {
421+ DispatcherQueue . TryEnqueue ( FinalizeFrame ) ;
422+ }
423+
424+ void FinalizeFrame ( )
425+ {
426+ try
427+ {
428+ session . Dispose ( ) ;
429+ }
430+ catch { }
431+ finally
432+ {
433+ }
434+ }
435+ #endif
436+ }
437+ catch ( Exception ex )
438+ {
439+ Console . WriteLine ( ex ) ;
440+ }
441+ finally
442+ {
443+ Interlocked . Exchange ( ref _isDrawingVideoFrame , false ) ;
444+ }
445+ }
446+
447+ private static void Image_VideoFrameOnLoaded ( object sender , RoutedEventArgs e )
448+ {
449+ if ( sender is not Image { Tag : ValueTuple < Grid , LayeredBackgroundImage > parentGrid } )
450+ {
451+ return ;
452+ }
453+
454+ parentGrid . Item2 . Play ( ) ;
455+ Image_ImageOpened ( sender , e ) ;
456+ }
457+
458+ private static void Image_VideoFrameOnUnloaded ( object sender , RoutedEventArgs e )
459+ {
460+ if ( sender is not Image { Tag : ValueTuple < Grid , LayeredBackgroundImage > parentGrid } )
461+ {
462+ return ;
463+ }
464+
465+ parentGrid . Item2 . Pause ( ) ;
466+ }
467+
279468 private static void Image_ImageOpened ( object sender , RoutedEventArgs e )
280469 {
281470 if ( sender is not Image { Tag : ValueTuple < Grid , LayeredBackgroundImage > parentGrid } image )
@@ -293,7 +482,6 @@ private static void Image_ImageOpened(object sender, RoutedEventArgs e)
293482
294483 // HACK: Tells the Grid to temporarily detach all UIElement children
295484 // then re-add the image to the grid
296- DisposeMediaPlayerElements ( parentGrid . Item1 ) ;
297485 ClearMediaGrid ( parentGrid . Item1 , image ) ;
298486
299487 // Remove transition once loaded
@@ -339,14 +527,19 @@ private static bool TryGetMediaPathFromSource(object? source, [NotNullWhen(true)
339527 private static void ClearMediaGrid ( Grid grid , UIElement ? except = null )
340528 {
341529 List < UIElement > elementExcepted =
342- grid . Children
343- . Where ( x => x != except )
344- . ToList ( ) ;
530+ except == null ? grid . Children . ToList ( ) :
531+ grid . Children . Where ( x => x != except )
532+ . ToList ( ) ;
345533
346534 foreach ( Image image in elementExcepted . OfType < Image > ( ) )
347535 {
536+ // This one is for Image. The source will always guarantee to call this event.
348537 image . ImageOpened -= Image_ImageOpened ;
349- image . Source = null ; // Clears the loaded ImageSource
538+ // This one is for Video since ImageOpened with Canvas source will never triggers this so we use Loaded instead.
539+ image . Loaded -= Image_VideoFrameOnLoaded ;
540+ image . Unloaded -= Image_VideoFrameOnUnloaded ;
541+ // Clears the loaded ImageSource
542+ image . Source = null ;
350543 }
351544
352545 foreach ( UIElement element in elementExcepted )
@@ -355,19 +548,5 @@ private static void ClearMediaGrid(Grid grid, UIElement? except = null)
355548 }
356549 }
357550
358- private static void DisposeMediaPlayerElements ( Grid grid )
359- {
360- foreach ( UIElement element in grid . Children )
361- {
362- if ( element is not MediaPlayerElement asMediaElement )
363- {
364- continue ;
365- }
366-
367- asMediaElement . MediaPlayer . Pause ( ) ;
368- asMediaElement . MediaPlayer . Dispose ( ) ;
369- }
370- }
371-
372- #endregion
551+ #endregion
373552}
0 commit comments