Media Play/Pause/Stop buttons added

This commit is contained in:
Jordan Wages 2018-07-01 17:02:50 -05:00
parent 573ce727c0
commit ec5e493e4f
6 changed files with 367 additions and 13 deletions

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace butterflow_ui
{
public class ButterflowWrapper
{
#region Members
/// <summary> Full pathname of the butterflow executable file. </summary>
private Lazy<string> executablePath = new Lazy<string>(() => Path.Combine(Assembly.GetExecutingAssembly().Location, "ThirdPartyCompiled", "butterflow.exe"));
#endregion
#region Methods
/// <summary> Runs butterflow with the given <paramref name="optionsConfiguration"/>. </summary>
/// <param name="optionsConfiguration"> The options configuration. </param>
public void Run(OptionsConfiguration optionsConfiguration)
{
string arguments = optionsConfiguration.ToButterflowArguments();
Run(arguments);
}
public void Probe(string videoFile)
{
string arguments = string.Format("-prb \"{0}\"", videoFile);
}
/// <summary> Runs butterflow with the given <paramref name="arguments"/>. </summary>
/// <param name="arguments"> Options for controlling the operation. </param>
private void Run(string arguments)
{
var processStartInfo = new ProcessStartInfo(executablePath.Value, arguments);
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardOutput = true;
}
#endregion
}
}

View File

@ -31,4 +31,85 @@
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="SaveIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M15.003 3h2.997v5h-2.997v-5zm8.997 1v20h-24v-24h20l4 4zm-19 5h14v-7h-14v7zm16 4h-18v9h18v-9z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="OpenIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M9 2h6v1h-6v-1zm6-1v-1h-6v1h6zm-5.146 21l-1.854 2h8l-1.854-2h-4.292zm10.146-14h-5v-4h-6v4h-5l8 8 8-8zm-2 11h-12v-6.172l-2-2v10.172h16v-10.172l-2 2v6.172z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="SaveAsIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M15.563 22.282l-3.563.718.72-3.562 2.843 2.844zm-2.137-3.552l2.845 2.845 7.729-7.73-2.845-2.845-7.729 7.73zm-3.062 2.27h-7.364v-7h12.327l6.673-6.688v-2.312l-4-4h-18v22h9.953l.411-2zm-5.364-18h12v7h-12v-7zm8.004 6h2.996v-5h-2.996v5z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="PlayIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M3 22v-20l18 10-18 10z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="PauseIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M11 22h-4v-20h4v20zm6-20h-4v20h4v-20z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="StopIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M2 2h20v20h-20z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="ForwardIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M0 3.795l2.995-2.98 11.132 11.185-11.132 11.186-2.995-2.981 8.167-8.205-8.167-8.205zm18.04 8.205l-8.167 8.205 2.995 2.98 11.132-11.185-11.132-11.186-2.995 2.98 8.167 8.206z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="BackwardIcon">
<Viewbox>
<Path Name="path4846" Fill="#000000">
<Path.Data>
<PathGeometry Figures="M 24 3.795 21.005 0.815 9.873 12 21.005 23.186 24 20.205 15.833 12 Z M 5.96 12 14.127 20.205 11.132 23.185 0 12 11.132 0.814 l 2.995 2.98 z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
<ControlTemplate x:Key="CopyIcon">
<Viewbox>
<Path Fill="#000000">
<Path.Data>
<PathGeometry Figures="M15.143 13.244l.837-2.244 2.698 5.641-5.678 2.502.805-2.23s-8.055-3.538-7.708-10.913c2.715 5.938 9.046 7.244 9.046 7.244zm8.857-7.244v18h-18v-6h-6v-18h18v6h6zm-2 2h-12.112c-.562-.578-1.08-1.243-1.521-2h7.633v-4h-14v14h4v-3.124c.6.961 1.287 1.823 2 2.576v6.548h14v-14z" FillRule="NonZero"/>
</Path.Data>
</Path>
</Viewbox>
</ControlTemplate>
</ResourceDictionary>

View File

@ -11,9 +11,21 @@
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="{x:Static loc:Localization.FileMenu}">
<MenuItem Header="Open" />
<MenuItem Header="Save Configuration" />
<MenuItem Header="Save Configuration As..." />
<MenuItem Header="Open">
<MenuItem.Icon>
<ContentControl Template="{StaticResource OpenIcon}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Configuration">
<MenuItem.Icon>
<ContentControl Template="{StaticResource SaveIcon}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Save Configuration As...">
<MenuItem.Icon>
<ContentControl Template="{StaticResource SaveAsIcon}" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Header="{x:Static loc:Localization.EditMenu}" />
<MenuItem Header="{x:Static loc:Localization.HelpMenu}">
@ -56,7 +68,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{x:Static loc:Localization.FileLabel}"/>
<TextBox Name="txtFileName" Grid.Column="1" IsReadOnly="True"/>
<TextBox Name="txtFileName" Grid.Column="1" IsReadOnly="True" Text="{Binding OptionsConfiguration.VideoInput, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="2" MinWidth="25" Name="btnFilePicker" Click="btnFilePicker_Click">...</Button>
</Grid>
</GroupBox>
@ -125,7 +137,49 @@
</StackPanel>
</ScrollViewer>
</GroupBox>
<MediaElement Grid.Row="1" Grid.RowSpan="2" Grid.Column="2" Grid.ColumnSpan="2" Name="mediaPreview" />
<Grid Grid.Row="1" Grid.RowSpan="2" Grid.Column="2" Grid.ColumnSpan="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<MediaElement Grid.Row="0" Name="mediaPreview" ScrubbingEnabled="True" LoadedBehavior="Manual" UnloadedBehavior="Stop" Source="{Binding OptionsConfiguration.VideoInput, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged}" MediaOpened="mediaPreview_MediaOpened" MediaEnded="mediaPreview_MediaEnded" />
<Slider Grid.Row="1" Value="{Binding ElementName=mediaPreview, Path=Position.Seconds, Mode=OneWay}" Maximum="{Binding ElementName=mediaPreview, Path=NaturalDuration.TimeSpan.Seconds}" />
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Name="bntVideoBackward" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Click="bntVideoBackward_Click">
<ContentControl HorizontalAlignment="Center" Template="{StaticResource BackwardIcon}" />
</Button>
<Button Name="bntVideoPlay" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Click="bntVideoPlay_Click">
<ContentControl HorizontalAlignment="Center" Name="PlayPauseButtonIcon" Template="{StaticResource PlayIcon}" />
</Button>
<Button Name="bntVideoStop" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Click="bntVideoStop_Click">
<ContentControl HorizontalAlignment="Center" Template="{StaticResource StopIcon}" />
</Button>
<Button Name="bntVideoForward" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Click="bntVideoForward_Click">
<ContentControl HorizontalAlignment="Center" Template="{StaticResource ForwardIcon}" />
</Button>
</StackPanel>
</Grid>
<StatusBar Grid.Row="3" Grid.ColumnSpan="4">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<Button>
<ContentControl Template="{StaticResource CopyIcon}" />
</Button>
</StatusBarItem>
<StatusBarItem Grid.Column="1">
<TextBlock Text="{Binding OptionsConfiguration.CommandLineOutput, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}}"/>
</StatusBarItem>
</StatusBar>
</Grid>
</DockPanel>
</Window>

View File

@ -21,6 +21,13 @@ namespace butterflow_ui
/// </summary>
public partial class MainWindow : Window
{
#region Members
/// <summary> True if the media element is playing a video, false if not. </summary>
private bool isPlaying = false;
#endregion
#region Properties
/// <summary> Gets or sets the butyterflow options configuration. </summary>
@ -47,11 +54,12 @@ namespace butterflow_ui
var result = ofd.ShowDialog(this);
if (result.HasValue && result.Value)
{
txtFileName.Text = ofd.FileName;
mediaPreview.Source = new Uri(ofd.FileName);
this.OptionsConfiguration.Width = mediaPreview.NaturalVideoWidth.ToString();
this.OptionsConfiguration.Height = mediaPreview.NaturalVideoHeight.ToString();
this.OptionsConfiguration.VideoInput = ofd.FileName;
//Hack to get the first frame to display in the media preview element.
//This also triggers the MediaOpened event so we can get the metadata from the element.
mediaPreview.Play();
mediaPreview.Pause();
}
}
@ -68,5 +76,70 @@ namespace butterflow_ui
this.OptionsConfiguration.PlaybackRate = tag;
}
}
/// <summary> Event handler. Called by bntVideoPlay for click events. </summary>
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Routed event information. </param>
private void bntVideoPlay_Click(object sender, RoutedEventArgs e)
{
if (!this.isPlaying && this.mediaPreview.Source.IsFile)
{
this.isPlaying = true;
this.mediaPreview.Play();
this.PlayPauseButtonIcon.Template = Application.Current.Resources["PauseIcon"] as ControlTemplate;
}
else
{
this.isPlaying = false;
this.mediaPreview.Pause();
this.PlayPauseButtonIcon.Template = Application.Current.Resources["PlayIcon"] as ControlTemplate;
}
}
/// <summary> Event handler. Called by bntVideoStop for click events. </summary>
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Routed event information. </param>
private void bntVideoStop_Click(object sender, RoutedEventArgs e)
{
this.isPlaying = false;
this.PlayPauseButtonIcon.Template = Application.Current.Resources["PlayIcon"] as ControlTemplate;
this.mediaPreview.Stop();
}
/// <summary> Event handler. Called by bntVideoForward for click events. </summary>
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Routed event information. </param>
private void bntVideoForward_Click(object sender, RoutedEventArgs e)
{
this.mediaPreview.Position.Add(TimeSpan.FromSeconds(5));
}
/// <summary> Event handler. Called by bntVideoBackward for click events. </summary>
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Routed event information. </param>
private void bntVideoBackward_Click(object sender, RoutedEventArgs e)
{
this.mediaPreview.Position.Subtract(TimeSpan.FromSeconds(5));
}
/// <summary> Event handler. Called by mediaPreview for media opened events. </summary>
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Routed event information. </param>
private void mediaPreview_MediaOpened(object sender, RoutedEventArgs e)
{
this.OptionsConfiguration.Width = this.mediaPreview.NaturalVideoWidth.ToString();
this.OptionsConfiguration.Height = this.mediaPreview.NaturalVideoHeight.ToString();
}
/// <summary> Event handler. Called by mediaPreview for media ended events. </summary>
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Routed event information. </param>
private void mediaPreview_MediaEnded(object sender, RoutedEventArgs e)
{
this.isPlaying = false;
this.PlayPauseButtonIcon.Template = Application.Current.Resources["PlayIcon"] as ControlTemplate;
}
}
}

View File

@ -18,18 +18,32 @@ namespace butterflow_ui
/// <summary> Occurs when a property value changes. </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary> An interpreter used to ensure numeric input is correctly calculated. </summary>
private InputInterpreter interpreter = new InputInterpreter();
private string playbackRate;
private bool keepAudio;
private int width;
private int height;
private bool keepAspectRatio;
private bool losslessQuality;
private string videoInput;
private string videoOutput;
#endregion
#region Properties
/// <summary> Gets the command line output given the current configuration. </summary>
/// <value> The command line output. </value>
public string CommandLineOutput
{
get
{
return ToButterflowArguments();
}
}
/// <summary> Gets or sets the playback rate. </summary>
/// <value> The playback rate. </value>
public string PlaybackRate
@ -60,8 +74,8 @@ namespace butterflow_ui
}
}
/// <summary> Gets or sets the width. </summary>
/// <value> The width. </value>
/// <summary> Gets or sets the width of the video output. </summary>
/// <value> The width of the video output. </value>
public string Width
{
get
@ -76,8 +90,8 @@ namespace butterflow_ui
}
}
/// <summary> Gets or sets the height. </summary>
/// <value> The height. </value>
/// <summary> Gets or sets the height of the video output. </summary>
/// <value> The height of the video output. </value>
public string Height
{
get
@ -92,6 +106,21 @@ namespace butterflow_ui
}
}
/// <summary> Gets or sets a value indicating whether the keep aspect ratio of the input video file for the output video file. </summary>
/// <value> True if keep aspect ratio, false if not. </value>
public bool KeepAspectRatio
{
get
{
return this.keepAspectRatio;
}
set
{
this.keepAspectRatio = value;
OnPropertyChanged("KeepAspectRatio");
}
}
/// <summary> Gets or sets a value indicating whether the result is rendered in lossless quality. </summary>
/// <value> True if lossless quality is selected, false if not. </value>
public bool LosslessQuality
@ -107,6 +136,36 @@ namespace butterflow_ui
}
}
/// <summary> Gets or sets the video input file path. </summary>
/// <value> The video input file path. </value>
public string VideoInput
{
get
{
return this.videoInput;
}
set
{
this.videoInput = value;
OnPropertyChanged("VideoInput");
}
}
/// <summary> Gets or sets the video output file path. </summary>
/// <value> The video output file path. </value>
public string VideoOutput
{
get
{
return this.videoOutput;
}
set
{
this.videoOutput = value;
OnPropertyChanged("VideoOutput");
}
}
#endregion
#region Methods
@ -116,6 +175,39 @@ namespace butterflow_ui
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CommandLineOutput"));
}
/// <summary> Converts this object to a butterflow options. </summary>
/// <returns> This object as a string. </returns>
public string ToButterflowArguments()
{
var stringBuilder = new StringBuilder("-v "); //Verbose
if(this.KeepAspectRatio)
{
stringBuilder.AppendFormat("-vs {0}:-1 ", this.Width);
}
else
{
stringBuilder.AppendFormat("-vs {0}:{1} ", this.Width, this.Height);
}
stringBuilder.AppendFormat("-r {0} ", this.PlaybackRate);
if (this.KeepAudio) stringBuilder.Append("-audio ");
if (this.LosslessQuality) stringBuilder.Append("-l ");
stringBuilder.AppendFormat("\"{0}\"", this.VideoInput);
return stringBuilder.ToString();
}
/// <summary> Returns a string that represents the current object. </summary>
/// <returns> A string that represents the current object. </returns>
public override string ToString()
{
return ToButterflowArguments();
}
#endregion

View File

@ -74,6 +74,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ButterflowOption.cs" />
<Compile Include="ButterflowWrapper.cs" />
<Compile Include="Localization\Localization.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
@ -267,4 +268,7 @@
<Content Include="ThirdPartyCompiled\zlib1.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>XCOPY "$(SolutionDir)\ThirdPartyCompiled" "$(TargetDir)\ThirdPartyCompiled\" /S /Y</PostBuildEvent>
</PropertyGroup>
</Project>