Skip to content

Commit 2b6e4d8

Browse files
committed
Code cleanup
Added Videoplayer class for handling playback Option to play videos till end or obey transitions gotoprevious/gotonext now work for video and images
1 parent 9cb00d0 commit 2b6e4d8

11 files changed

+296
-220
lines changed

AppSettings.cs

+13-4
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,13 @@ private AppSettings()
3939

4040
InfoBarFontSize = 50;
4141
SlideshowTransitionTime = 30000; // milliseconds between slides
42-
FadeTransitionTime = 1600; // milliseconds for fades
42+
FadeTransitionTime = 10000; // milliseconds for fades
4343
ImageStretch = Stretch.UniformToFill; // Default image stretch
44+
45+
// Video Settings
4446
VideoStretch = "Fill"; // Used for OMXPlayer
47+
PlaybackFullVideo = false; // This means videos will obey transition time by default
48+
4549
ExpandDirectoriesByDefault = false; // WebUI setting to expand the trees
4650
ListenerPort = 8000; // Default port for the WebUI to listen on
4751
IsSyncEnabled = false; // Sync with other frames off by default
@@ -60,9 +64,9 @@ private AppSettings()
6064
// Tag settings
6165
InclusiveTagFilters = ""; // Semicolon delimited list of images to include. Blank string means 'all'
6266
// Blurbox Settings
63-
BlurBoxSigmaX = 10; // See BlurboxImage.axaml.cs for usage
64-
BlurBoxSigmaY = 10; // Used in line: blurPaint.ImageFilter = SKImageFilter.CreateBlur(50, 50);
65-
BlurBoxMargin = -500; // Set to negative to scale the background image
67+
BlurBoxSigmaX = 30; // See BlurboxImage.axaml.cs for usage
68+
BlurBoxSigmaY = 30; // Used in line: blurPaint.ImageFilter = SKImageFilter.CreateBlur(50, 50);
69+
BlurBoxMargin = -400; // Set to negative to scale the background image
6670
}
6771

6872
private static string _jsonSource;
@@ -187,6 +191,11 @@ public enum InfoBar { Clock, FileInfo, DateTime, Error, IP, OFF, InitialIP, Exif
187191
/// </summary>
188192
public string VideoStretch { get; set; }
189193

194+
/// <summary>
195+
/// This controls how video playback is handled. If true the full video is always played, else transition time is obeyed
196+
/// </summary>
197+
198+
public bool PlaybackFullVideo { get; set; }
190199

191200
/// <summary>
192201
/// Control if we should reload settings on the page (for layout/rendering)

Dynaframe3.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
<TargetFramework>net5.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<AssemblyName>Dynaframe</AssemblyName>
7-
<Version>2.21</Version>
7+
<Version>2.23</Version>
88
<Authors>Joe Farro</Authors>
99
<Company>Geektoolkit</Company>
1010
<Product>Dynaframe</Product>
1111
<Description>Dynaframe is a dynamic photo application which can show images and videos as slideshows. It features a web frontend, home automation integration, and more.</Description>
12-
<AssemblyVersion>2.21.0.0</AssemblyVersion>
12+
<AssemblyVersion>2.22.0.0</AssemblyVersion>
1313
<Configurations>Release;Debug</Configurations>
14-
<FileVersion>2.21.0.0</FileVersion>
14+
<FileVersion>2.23.0.0</FileVersion>
1515
</PropertyGroup>
1616
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
1717
<DebugType></DebugType>

MainWindow.axaml

+8-5
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
>
1212
<Panel Name="mainPanel">
1313
<custom:CrossFadeTransition Name="CrossFadeImage"/>
14-
<!-- Future work-->
15-
<!--Panel Name="SubPanel" Height="60" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="30" Width="200">
16-
<Rectangle Fill="Black" Opacity=".2" Height="30"></Rectangle -->
17-
<TextBlock Name="tb" Text="" FontSize="30" HorizontalAlignment="Left" VerticalAlignment="Bottom" Foreground="AliceBlue" Opacity="1" Margin="0"/>
18-
<!--/Panel -->
14+
<TextBlock Name="tb"
15+
Text=""
16+
FontSize="30"
17+
HorizontalAlignment="Left"
18+
VerticalAlignment="Bottom"
19+
Foreground="AliceBlue"
20+
Opacity="1"
21+
Margin="0"/>
1922
</Panel>
2023
</Window>

MainWindow.axaml.cs

+39-187
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public class MainWindow : Window
3737
TextBlock tb;
3838
Window mainWindow;
3939
Panel mainPanel;
40-
Process videoProcess; // handle to the video Player
4140

4241
// Engines
4342
internal PlayListEngine playListEngine;
@@ -121,6 +120,8 @@ private void InitializeComponent()
121120
tb.Transitions.Add(fadeTransition);
122121
tb.Padding = new Thickness(30);
123122

123+
VideoPlayer.MainPanelHandle = this.mainPanel;
124+
VideoPlayer.MainWindowHandle = this.mainWindow;
124125

125126
string intro;
126127
if ((AppSettings.Default.Rotation == 0) || AppSettings.Default.Rotation == 180)
@@ -156,7 +157,7 @@ public void GoToNextImage()
156157
{
157158
tb.Transitions.Clear();
158159
playListEngine.GoToNext();
159-
PlayImageFile(500, playListEngine.CurrentPlayListItem.Path);
160+
PlayFile(playListEngine.CurrentPlayListItem.Path, 500);
160161
lastUpdated = DateTime.Now;
161162
tb.Transitions.Add(fadeTransition);
162163
}
@@ -167,7 +168,7 @@ public void GoToPreviousImage()
167168
{
168169
tb.Transitions.Clear();
169170
playListEngine.GoToPrevious();
170-
PlayImageFile(500, playListEngine.CurrentPlayListItem.Path);
171+
PlayFile(playListEngine.CurrentPlayListItem.Path, 500);
171172
lastUpdated = DateTime.Now;
172173
tb.Transitions.Add(fadeTransition);
173174
}
@@ -183,7 +184,7 @@ public void GoToFirstImage()
183184
}
184185
Logger.LogComment("Waking up and continuing playing files..");
185186

186-
PlayImageFile(500, playListEngine.CurrentPlayListItem.Path);
187+
PlayFile(playListEngine.CurrentPlayListItem.Path, 500);
187188
lastUpdated = DateTime.Now;
188189
tb.Transitions.Add(fadeTransition);
189190
}
@@ -347,90 +348,63 @@ private void Timer_Tick(object sender, EventArgs e)
347348
}
348349

349350
UpdateInfoBar();
351+
bool GoToNext = false;
352+
if ((playListEngine.CurrentPlayListItem!= null) && (playListEngine.CurrentPlayListItem.ItemType == PlayListItemType.Video))
353+
{
354+
if (!VideoPlayer.CheckStatus(false))
355+
{
356+
// Video exited before we expected it to! Recover gracefully
357+
GoToNext = true;
358+
}
359+
}
360+
350361

351-
//
352-
// This is the main check for if we need to switch frames. First we check to see if the
353-
// amount of time that has transpired is over our tranistion time. If so, we check:
354-
// 1) Are we syncing to other frames? if so send those signals
355-
// 2) Is it an audio or video? Call appropriate play method based on that.
356-
//
357-
if ((DateTime.Now.Subtract(lastUpdated).TotalMilliseconds > AppSettings.Default.SlideshowTransitionTime))
362+
// check transpired time against transition time...
363+
if ((DateTime.Now.Subtract(lastUpdated).TotalMilliseconds > AppSettings.Default.SlideshowTransitionTime) || GoToNext == true)
358364
{
365+
// See if a video is playing...
366+
if (VideoPlayer.CheckStatus(true))
367+
{
368+
// Video is still playing, tick off the next timer interval for now.
369+
slideTimer.Start();
370+
return;
371+
}
359372
lastUpdated = DateTime.Now;
360373
playListEngine.GoToNext();
361374
Logger.LogComment("Next file is: " + playListEngine.CurrentPlayListItem.Path);
362375

363376
// sync frame call
364-
if ((AppSettings.Default.IsSyncEnabled) &&(SyncedFrame.SyncEngine.syncedFrames.Count > 0))
365-
{
366-
Logger.LogComment("SyncFrames enabled...sending sync signals..");
367-
// We have frames to sync! Send this off to them:
368-
try
369-
{
370-
SyncedFrame.SyncEngine.SyncFrames(playListEngine.CurrentPlayListItem.Path);
371-
}
372-
catch(Exception exc)
373-
{
374-
// This is a 'backstop' to catch any nastiness from networking. The whole network blind call to
375-
// an IP thing is risky, and I've seen an instance where the try/catch in syncframes failed.
376-
// Adding this to try to catch that if it happens and to understand why. Also to not
377-
// bring down the entire app due to network flakiness.
378-
Logger.LogComment("ERROR: Excpetion trying to sync frames, caught in mainWindow. Excpetion: " + exc.ToString());
379-
}
380-
}
381-
382-
try
383-
{
384-
// TODO: Try to 'peek' at next file, if video, then slow down more
385-
if (playListEngine.CurrentPlayListItem.ItemType == PlayListItemType.Video)
386-
{
387-
KillVideoPlayer();
388-
PlayVideoFile(playListEngine.CurrentPlayListItem.Path);
389-
}
390-
else
391-
{
392-
PlayImageFile(AppSettings.Default.FadeTransitionTime, playListEngine.CurrentPlayListItem.Path);
393-
KillVideoPlayer(); // if a video is playing, get rid of it now that we've swapped images
394-
}
395-
}
396-
catch (InvalidOperationException exc)
377+
if ((AppSettings.Default.IsSyncEnabled) && (SyncedFrame.SyncEngine.syncedFrames.Count > 0))
397378
{
398-
Logger.LogComment("IOE Exception: " + exc.ToString());
399-
// We expect this if a process is no longer around
400-
}
401-
catch (Exception exc)
402-
{
403-
Logger.LogComment("ERROR: Exception processing file.." + exc.ToString());
379+
SyncedFrame.SyncEngine.SyncFrames(playListEngine.CurrentPlayListItem.Path);
404380
}
381+
PlayFile(playListEngine.CurrentPlayListItem.Path);
405382
}
406383
slideTimer.Start(); // start next iterations...this prevents reentry...
407384

408385
}
409386
public void PlayFile(string path)
410387
{
411-
// Externally exposed API to allow for frame syncing or automation scenarios.
412-
// note: The file that we try to play may not exist, if not, we should look at the folder and try to
413-
// find it, if that doesn't exist then show randome file from playlist
414-
//
415-
// Future use from Avalonia Gitter: How to load an image from network path..
416-
// var response = await httpClient.GetAsync(bitmapPath, HttpCompletionOption.ResponseContentRead);
417-
// var stream = await response.Content.ReadAsStreamAsync();
418-
// bitmap = new Bitmap(stream);
419-
388+
PlayFile(path, AppSettings.Default.FadeTransitionTime);
389+
}
390+
391+
public void PlayFile(string path, int transitionTime)
392+
{
393+
Logger.LogComment("PlayFile() called with TransitionTime=" + transitionTime + " and path: " + path);
420394
PlayListItemType type = PlayListEngineHelper.GetPlayListItemTypeFromPath(path);
395+
VideoPlayer.KillVideoPlayer(); // Kill this...if this is called from gotonext / gotoprevious it can cause bad effects.
421396
try
422397
{
423398
// TODO: Try to 'peek' at next file, if video, then slow down more
424399
if (type == PlayListItemType.Video)
425400
{
426-
KillVideoPlayer();
427-
PlayVideoFile(path);
401+
VideoPlayer.PlayVideo(path);
428402
}
429403
else
430404
{
431-
PlayImageFile(AppSettings.Default.FadeTransitionTime, path);
432-
KillVideoPlayer(); // if a video is playing, get rid of it now that we've swapped images
405+
crossFadeTransition.SetImage(path, AppSettings.Default.FadeTransitionTime);
433406
}
407+
Logger.LogComment("Media is now set.");
434408
}
435409
catch (InvalidOperationException)
436410
{
@@ -442,6 +416,8 @@ public void PlayFile(string path)
442416
}
443417

444418
}
419+
420+
445421
private void UpdateInfoBar()
446422
{
447423
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
@@ -504,131 +480,7 @@ private void UpdateInfoBar()
504480
} // end if
505481
});
506482
}
507-
public void PlayImageFile(int millisecondsDelay, string path)
508-
{
509-
Logger.LogComment("PlayImageFile() called with Delay=" + millisecondsDelay + " and path: " + path);
510-
crossFadeTransition.SetImage(path, AppSettings.Default.FadeTransitionTime);
511-
Logger.LogComment("PlayImageFile: New Image now set");
512-
}
513-
private void PlayVideoFile(string path)
514-
{
515-
Logger.LogComment("Entering PlayVideoFile with Path: " + path);
516-
ProcessStartInfo pInfo = new ProcessStartInfo();
517-
pInfo.WindowStyle = ProcessWindowStyle.Maximized;
518-
519-
// TODO: Parameterize omxplayer settings
520-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
521-
{
522-
Logger.LogComment("Linux Detected, setting up OMX Player");
523-
pInfo.FileName = "omxplayer";
524-
Logger.LogComment("Setting up Appsettings...");
525-
pInfo.Arguments = AppSettings.Default.OXMOrientnation + " --aspect-mode " + AppSettings.Default.VideoStretch + " ";
526-
527-
// Append volume command argument
528-
if (!AppSettings.Default.VideoVolume)
529-
{
530-
pInfo.Arguments += "--vol -6000 ";
531-
}
532-
533-
pInfo.Arguments += "\"" + path + "\"";
534-
Logger.LogComment("DF Playing: " + path);
535-
Logger.LogComment("OMXPLayer args: " + pInfo.Arguments);
536-
}
537-
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
538-
{
539-
pInfo.UseShellExecute = true;
540-
pInfo.FileName = "wmplayer.exe";
541-
pInfo.Arguments = "\"" + path + "\"";
542-
pInfo.Arguments += " /fullscreen";
543-
Logger.LogComment("Looking for media in: " + pInfo.Arguments);
544-
}
545-
546-
547-
videoProcess = new Process();
548-
videoProcess.StartInfo = pInfo;
549-
Logger.LogComment("PlayVideoFile: Starting player...");
550-
videoProcess.Start();
551-
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
552-
{
553-
mainPanel.Opacity = 0;
554-
});
555-
556-
// Give video player time to start, then fade out to reveal it...
557-
System.Threading.Thread.Sleep(1100);
558-
Logger.LogComment("PlayVideoFile: Fading Foreground to reveal video player.");
559-
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
560-
{
561-
mainWindow.Opacity = 0;
562-
});
563-
564-
int timer = 0;
565-
Logger.LogComment("PlayVideoFile: Entering Timerloop");
566-
while ((videoProcess != null) && (!videoProcess.HasExited))
567-
{
568-
timer += 1;
569-
System.Threading.Thread.Sleep(300);
570-
if (timer > 400)
571-
{
572-
// timeout to not 'hang'
573-
// TODO: Add a setting for this
574-
break;
575-
}
576-
577-
}
578-
Logger.LogComment("PlayVideoFile: Video has exited!");
579-
Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(() =>
580-
{
581-
mainPanel.Opacity = 1;
582-
mainWindow.Opacity = 1;
583-
});
584-
}
585-
586-
private void KillVideoPlayer()
587-
{
588-
Logger.LogComment("KillVideoPlayer - Entering Method.");
589-
try
590-
{
591-
if (videoProcess != null)
592-
{
593-
try
594-
{
595-
videoProcess.CloseMainWindow();
596-
videoProcess = null;
597-
}
598-
catch (InvalidOperationException)
599-
{
600-
// expected if the process isn't there.
601-
}
602-
catch (Exception exc)
603-
{
604-
Debug.WriteLine("Tried and failed to kill video process..." + exc.ToString());
605-
Logger.LogComment("Tried and failed to kill video process. Exception: " + exc.ToString());
606-
}
607-
}
608-
609-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
610-
{
611-
// OMXPlayer processes can be a bit tricky. to kill them we use
612-
// killall - 9 omxplayer.bin
613-
// -q quiets this down in case omxplayer isn't running
614483

615-
Helpers.RunProcess("killall", "-q -9 omxplayer.bin");
616-
videoProcess = null;
617-
618-
}
619-
else
620-
{
621-
videoProcess.Close();
622-
videoProcess.Dispose();
623-
videoProcess = null;
624-
}
625-
}
626-
catch (Exception)
627-
{
628-
// Swallow. This may no longer be there depending on what kills it (OMX player will exit if the video
629-
// completes for instance
630-
}
631-
}
632484

633485
public void SetupWebServer()
634486
{

Notes.txt

+5
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
Notes for future devlopment:
22
Screen blanking - https://linuxreviews.org/HOWTO_turn_Screensavers_and_Monitor_Power_Saving_on_and_off
3+
4+
Playing an image file from a network path in avalonia
5+
// var response = await httpClient.GetAsync(bitmapPath, HttpCompletionOption.ResponseContentRead);
6+
// var stream = await response.Content.ReadAsStreamAsync();
7+
// bitmap = new Bitmap(stream);

0 commit comments

Comments
 (0)