Adding Multible File Support

Added the neccesary functions to support batch processing.
This commit is contained in:
Jordan Wages 2018-07-24 02:12:53 -05:00
parent cd23ef899d
commit b9a9c3e503
10 changed files with 217 additions and 40 deletions

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace butterflow_ui
{
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(Visibility))
{
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
throw new InvalidCastException(string.Format("Cannot convert type to {0} from bool.", targetType.Name));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(Visibility))
{
return ((Visibility)value == Visibility.Visible) ? true : false;
}
throw new InvalidCastException(string.Format("Cannot convert type {0} to boolean.", targetType.Name));
}
}
}

View File

@ -27,6 +27,8 @@ namespace butterflow_ui
/// <summary> Full pathname of the butterflow executable file. </summary>
private Lazy<string> executablePath = new Lazy<string>(() => Path.Combine(Directory.GetCurrentDirectory(), "ThirdPartyCompiled", "butterflow.exe"));
/// <summary> Queue of butterflow commands to run. </summary>
private Queue<string> runQueue = new Queue<string>();
/// <summary> The console output from butterflow. </summary>
private string consoleOutput = string.Empty;
/// <summary> True if butterflow is running, false if not. </summary>
@ -93,38 +95,26 @@ namespace butterflow_ui
#region Methods
/// <summary> Runs butterflow with the given <paramref name="optionsConfiguration"/>. </summary>
/// <summary> Runs butterflow with the given <paramref name="optionsConfiguration"/> by adding it to the queue. </summary>
/// <param name="optionsConfiguration"> The options configuration. </param>
public void Run(OptionsConfiguration optionsConfiguration)
{
string arguments = optionsConfiguration.ToButterflowArguments();
Run(arguments);
}
/// <summary> Kills the running instance of butterflow, cancelling its current operation. </summary>
public void Cancel()
{
if(this.IsRunning && this.runningProcess != null)
for(int i = 0; i < optionsConfiguration.VideoInput.Count(); i++)
{
this.runningProcess.Kill();
var arguments = optionsConfiguration.ToButterflowArguments(i);
this.runQueue.Enqueue(arguments);
}
ProcessQueue();
}
/// <summary> Probes a video file. </summary>
/// <param name="videoFile"> The video file to be probed. </param>
public void Probe(string videoFile)
/// <summary> Process the queue of butterflow arguments. </summary>
public void ProcessQueue()
{
string arguments = string.Format("-prb \"{0}\"", videoFile);
Run(arguments);
}
/// <summary> Runs butterflow with the given <paramref name="arguments"/>. </summary>
/// <param name="arguments"> Options for controlling the operation. </param>
private void Run(string arguments)
{
if (!this.IsRunning)
if (!this.IsRunning && this.runQueue.Any())
{
var arguments = this.runQueue.Dequeue();
var process = new Process();
process.StartInfo = new ProcessStartInfo(executablePath.Value, arguments);
@ -146,6 +136,34 @@ namespace butterflow_ui
}
}
/// <summary> Kills the running instance of butterflow, cancelling its current operation. </summary>
public void Cancel()
{
if(this.IsRunning && this.runningProcess != null)
{
this.runningProcess.Kill();
}
this.runQueue.Clear();
}
/// <summary> Probes a video file. </summary>
/// <param name="videoFile"> The video file to be probed. </param>
public void Probe(string videoFile)
{
string arguments = string.Format("-prb \"{0}\"", videoFile);
Run(arguments);
}
/// <summary> Runs butterflow with the given <paramref name="arguments"/> by adding it to the queue. </summary>
/// <param name="arguments"> Options for controlling the operation. </param>
private void Run(string arguments)
{
this.runQueue.Enqueue(arguments);
ProcessQueue();
}
/// <summary> Event handler. Called by Process for exited events. </summary>
/// <param name="sender"> Source of the event. </param>
/// <param name="e"> Event information. </param>
@ -153,6 +171,8 @@ namespace butterflow_ui
{
this.IsRunning = false;
this.runningProcess = null;
ProcessQueue();
}
/// <summary>

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace butterflow_ui
{
[ValueConversion(typeof(bool), typeof(Visibility))]
public class InverseBoolVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(Visibility))
{
return (bool)value ? Visibility.Hidden : Visibility.Visible;
}
throw new InvalidCastException(string.Format("Cannot convert type to {0} from bool.", targetType.Name));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType == typeof(Visibility))
{
return ((Visibility)value == Visibility.Visible) ? false : true;
}
throw new InvalidCastException(string.Format("Cannot convert type {0} to boolean.", targetType.Name));
}
}
}

View File

@ -465,6 +465,24 @@ namespace butterflow_ui.Localization {
}
}
/// <summary>
/// Looks up a localized string similar to Video previewing and subregion clipping is not available when processing multiple files..
/// </summary>
public static string MultipleFilesPreviewWarningLabel {
get {
return ResourceManager.GetString("MultipleFilesPreviewWarningLabel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to [Multiple Files].
/// </summary>
public static string MultipleFilesText {
get {
return ResourceManager.GetString("MultipleFilesText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to OK.
/// </summary>

View File

@ -360,4 +360,10 @@
<data name="VersionLabel" xml:space="preserve">
<value>Version</value>
</data>
<data name="MultipleFilesPreviewWarningLabel" xml:space="preserve">
<value>Video previewing and subregion clipping is not available when processing multiple files.</value>
</data>
<data name="MultipleFilesText" xml:space="preserve">
<value>[Multiple Files]</value>
</data>
</root>

View File

@ -25,6 +25,8 @@
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<butterflow_ui:InverseBoolConverter x:Key="InverseBoolConverter" />
<butterflow_ui:BoolVisibilityConverter x:Key="BoolVisibilityConverter" />
<butterflow_ui:InverseBoolVisibilityConverter x:Key="InverseBoolVisibilityConverter" />
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top">
@ -94,7 +96,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{x:Static loc:Localization.FileLabel}"/>
<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}"/>
<TextBox Name="txtFileName" Grid.Column="1" IsReadOnly="True"/>
<Button Grid.Column="2" MinWidth="25" Name="btnFilePicker" Click="btnFilePicker_Click">...</Button>
</Grid>
</GroupBox>
@ -106,7 +108,7 @@
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{x:Static loc:Localization.FileLabel}"/>
<TextBox Name="txtFileOutputName" Grid.Column="1" IsReadOnly="True" Text="{Binding OptionsConfiguration.VideoOutput, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="txtFileOutputName" Grid.Column="1" IsReadOnly="True" Text="{Binding OptionsConfiguration.VideoOutput, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged}"/>
<Button Grid.Column="2" MinWidth="25" Name="btnFileOutputPicker" Click="btnFileOutputPicker_Click">...</Button>
</Grid>
</GroupBox>
@ -307,8 +309,11 @@
</ProgressBar.Style>
</ProgressBar>
</Grid>
<gu:MediaElementWrapper Grid.Row="1" Name="mediaPreview" Stretch="Uniform" ScrubbingEnabled="True" Source="{Binding OptionsConfiguration.VideoInput, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged}" MediaOpened="mediaPreview_MediaOpened" MediaEnded="mediaPreview_MediaEnded" />
<Grid Grid.Row="2">
<Canvas x:Name="canvas" Grid.Row="1" Grid.RowSpan="3" Visibility="{Binding OptionsConfiguration.MultipleFiles, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BoolVisibilityConverter}}">
<TextBlock TextAlignment="Center" VerticalAlignment="Center" Width="{Binding ActualWidth, ElementName=canvas}" Height="{Binding ActualHeight, ElementName=canvas}" TextWrapping="WrapWithOverflow" Text="{x:Static loc:Localization.MultipleFilesPreviewWarningLabel}" />
</Canvas>
<gu:MediaElementWrapper Grid.Row="1" Name="mediaPreview" Stretch="Uniform" ScrubbingEnabled="True" MediaOpened="mediaPreview_MediaOpened" MediaEnded="mediaPreview_MediaEnded" Visibility="{Binding OptionsConfiguration.MultipleFiles, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource InverseBoolVisibilityConverter}}" />
<Grid Grid.Row="2" Visibility="{Binding OptionsConfiguration.MultipleFiles, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource InverseBoolVisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
@ -320,7 +325,7 @@
Maximum="{Binding Path=Length, ElementName=mediaPreview, Converter={x:Static gu:NullableTimeSpanToSecondsConverter.Default}}" />
<Label Grid.Column="2" Content="{Binding Path=Length, ElementName=mediaPreview, Converter={x:Static gu:TimeSpanToStringConverter.Default}}" />
</Grid>
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center">
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center" Visibility="{Binding OptionsConfiguration.MultipleFiles, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type butterflow_ui:MainWindow}}, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource InverseBoolVisibilityConverter}}">
<Button Name="bntVideoBackward" BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" Click="bntVideoBackward_Click">
<ContentControl HorizontalAlignment="Center" Template="{StaticResource BackwardIcon}" />
</Button>

View File

@ -110,18 +110,29 @@ namespace butterflow_ui
private void btnFilePicker_Click(object sender, RoutedEventArgs e)
{
var ofd = new OpenFileDialog();
ofd.Multiselect = true;
ofd.Filter = "Supported Video Files|*.3dostr;*.3g2;*.3gp;*.4xm;*.a64;*.aa;*.aac;*.ac3;*.acm;*.act;*.adf;*.adp;*.ads;*.adts;*.adx;*.aea;*.afc;*.aiff;*.aix;*.alaw;*.alias_pix;*.alsa;*.amr;*.anm;*.apc;*.ape;*.apng;*.aqtitle;*.asf;*.asf_o;*.asf_stream;*.ass;*.ast;*.au;*.avi;*.avisynth;*.avm2;*.avr;*.avs;*.bethsoftvid;*.bfi;*.bfstm;*.bin;*.bink;*.bit;*.bmp_pipe;*.bmv;*.boa;*.brender_pix;*.brstm;*.c93;*.caca;*.caf;*.cavsvideo;*.cdg;*.cdxl;*.chromaprint;*.cine;*.concat;*.crc;*.dash;*.data;*.daud;*.dcstr;*.dds_pipe;*.dfa;*.dirac;*.dnxhd;*.dpx_pipe;*.dsf;*.dsicin;*.dss;*.dts;*.dtshd;*.dv;*.dv1394;*.dvbsub;*.dvbtxt;*.dvd;*.dxa;*.ea;*.ea_cdata;*.eac3;*.epaf;*.exr_pipe;*.f32be;*.f32le;*.f4v;*.f64be;*.f64le;*.fbdev;*.ffm;*.ffmetadata;*.fifo;*.film_cpk;*.filmstrip;*.flac;*.flic;*.flv;*.framecrc;*.framehash;*.framemd5;*.frm;*.fsb;*.g722;*.g723_1;*.g729;*.genh;*.gif;*.gsm;*.gxf;*.h261;*.h263;*.h264;*.hash;*.hds;*.hevc;*.hls;*.hls;*.applehttp;*.hnm;*.ico;*.idcin;*.idf;*.iec61883;*.iff;*.ilbc;*.image2;*.image2pipe;*.ingenient;*.ipmovie;*.ipod;*.ircam;*.ismv;*.iss;*.iv8;*.ivf;*.ivr;*.j2k_pipe;*.jack;*.jacosub;*.jpeg_pipe;*.jpegls_pipe;*.jv;*.latm;*.lavfi;*.libcdio;*.libdc1394;*.libgme;*.libopenmpt;*.live_flv;*.lmlm4;*.loas;*.lrc;*.lvf;*.lxf;*.m4v;*.matroska;*.matroska;*.webm;*.md5;*.mgsts;*.microdvd;*.mjpeg;*.mkvtimestamp_v2;*.mkv;*.mlp;*.mlv;*.mm;*.mmf;*.mov;*.mov;*.mp4;*.m4a;*.3gp;*.3g2;*.mj2;*.mp2;*.mp3;*.mp4;*.mpc;*.mpc8;*.mpeg;*.mpeg1video;*.mpeg2video;*.mpegts;*.mpegtsraw;*.mpegvideo;*.mpjpeg;*.mpl2;*.mpsub;*.msf;*.msnwctcp;*.mtaf;*.mtv;*.mulaw;*.musx;*.mv;*.mvi;*.mxf;*.mxf_d10;*.mxf_opatom;*.mxg;*.nc;*.nistsphere;*.nsv;*.null;*.nut;*.nuv;*.oga;*.ogg;*.ogv;*.oma;*.openal;*.opengl;*.opus;*.oss;*.paf;*.pam_pipe;*.pbm_pipe;*.pcx_pipe;*.pgm_pipe;*.pgmyuv_pipe;*.pictor_pipe;*.pjs;*.pmp;*.png_pipe;*.ppm_pipe;*.psp;*.psxstr;*.pulse;*.pva;*.pvf;*.qcp;*.qdraw_pipe;*.r3d;*.rawvideo;*.realtext;*.redspark;*.rl2;*.rm;*.roq;*.rpl;*.rsd;*.rso;*.rtp;*.rtp_mpegts;*.rtsp;*.s16be;*.s16le;*.s24be;*.s24le;*.s32be;*.s32le;*.s8;*.sami;*.sap;*.sbg;*.sdl;*.sdl2;*.sdp;*.sdr2;*.segment;*.sgi_pipe;*.shn;*.siff;*.singlejpeg;*.sln;*.smjpeg;*.smk;*.smoothstreaming;*.smush;*.sndio;*.sol;*.sox;*.spdif;*.spx;*.srt;*.stl;*.stream_segment;*.ssegment;*.subviewer;*.subviewer1;*.sunrast_pipe;*.sup;*.svag;*.svcd;*.swf;*.tak;*.tedcaptions;*.tee;*.thp;*.tiertexseq;*.tiff_pipe;*.tmv;*.truehd;*.tta;*.tty;*.txd;*.u16be;*.u16le;*.u24be;*.u24le;*.u32be;*.u32le;*.u8;*.uncodedframecrc;*.v210;*.v210x;*.v4l2;*.vag;*.vc1;*.vc1test;*.vcd;*.video4linux2;*.v4l2;*.vivo;*.vmd;*.vob;*.vobsub;*.voc;*.vpk;*.vplayer;*.vqf;*.w64;*.wav;*.wc3movie;*.webm;*.webm_chunk;*.webm_dash_manifest;*.webp;*.webp_pipe;*.webvtt;*.wsaud;*.wsd;*.wsvqa;*.wtv;*.wv;*.wve;*.x11grab;*.xa;*.xbin;*.xmv;*.xv;*.xvag;*.xwma;*.yop;*.yuv4mpegpipe";
var result = ofd.ShowDialog(this);
if (result.HasValue && result.Value)
{
this.OptionsConfiguration.VideoInput = ofd.FileName;
this.OptionsConfiguration.VideoInput = ofd.FileNames;
this.ButterflowWrapper.Probe(ofd.FileName);
//Hack to get the first frame to display in the media preview element.
mediaPreview.Play();
mediaPreview.PausePlayback();
if (ofd.FileNames.Count() == 1)
{
this.txtFileName.Text = ofd.FileName;
this.mediaPreview.Source = new Uri(ofd.FileName);
//Hack to get the first frame to display in the media preview element.
this.mediaPreview.Play();
this.mediaPreview.PausePlayback();
}
else
{
this.txtFileName.Text = Localization.Localization.MultipleFilesText;
this.mediaPreview.Source = default(Uri);
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -29,6 +30,8 @@ namespace butterflow_ui
private const decimal DEFAULT_SMOOTH_DERIVATIVE_STANDARD_DEVIATION = 1.1m;
/// <summary> The default flow filter type setting. </summary>
private const FlowFilterType DEFAULT_FLOW_FILTER_TYPE = FlowFilterType.box;
/// <summary> The output file format when operating on more than one video. </summary>
private const string OUTPUT_FILE_FORMAT = "{0}_{1}";
/// <summary> An input interpreter used for converting string values to numeric values. </summary>
[NonSerialized]
@ -52,8 +55,8 @@ namespace butterflow_ui
private bool smoothMotion;
/// <summary> A value indicating whether or not to lock the aspect ratio to the <seealso cref="width"/> of the video. </summary>
private bool lockAspectRatio;
/// <summary> The video input file. </summary>
private string videoInput;
/// <summary> The video input files. </summary>
private IEnumerable<string> videoInput;
/// <summary> The video output file. </summary>
private string videoOutput;
/// <summary> A value indicating whether or not to use fast pyramids when processing a video. </summary>
@ -89,6 +92,16 @@ namespace butterflow_ui
}
}
/// <summary> Gets a value indicating whether butterflow will process multiple files. </summary>
/// <value> True if butterflow will process multiple files, false if not. </value>
public bool MultipleFiles
{
get
{
return this.VideoInput != null && this.VideoInput.Count() > 1;
}
}
/// <summary> Gets or sets the playback rate. </summary>
/// <value> The playback rate. </value>
public string PlaybackRate
@ -229,7 +242,7 @@ namespace butterflow_ui
/// <summary> Gets or sets the video input file path. </summary>
/// <value> The video input file path. </value>
public string VideoInput
public IEnumerable<string> VideoInput
{
get
{
@ -239,6 +252,7 @@ namespace butterflow_ui
{
this.videoInput = value;
OnPropertyChanged();
OnPropertyChanged("MultipleFiles");
}
}
@ -420,6 +434,8 @@ namespace butterflow_ui
this.smoothDerivativeStandardDeviation = DEFAULT_SMOOTH_DERIVATIVE_STANDARD_DEVIATION;
this.flowFilterType = DEFAULT_FLOW_FILTER_TYPE;
this.videoInput = new string[0];
this.subregions.CollectionChanged += Subregions_CollectionChanged; ;
}
@ -502,7 +518,7 @@ namespace butterflow_ui
/// <summary> Converts this object to a butterflow options. </summary>
/// <returns> This object as a string. </returns>
public string ToButterflowArguments()
public string ToButterflowArguments(int videoInputIndex = 0)
{
var stringBuilder = new StringBuilder("-v "); // Verbose
@ -556,9 +572,38 @@ namespace butterflow_ui
if (this.pixelNeighborhood != DEFAULT_PIXEL_NEIGHBORHOOD) stringBuilder.AppendFormat("--poly-n {0} ", this.PixelNeighborhood);
if (this.smoothDerivativeStandardDeviation != DEFAULT_SMOOTH_DERIVATIVE_STANDARD_DEVIATION) stringBuilder.AppendFormat("--poly-s {0} ", this.SmoothDerivativeStandardDeviation);
if (this.FlowFilterType != DEFAULT_FLOW_FILTER_TYPE) stringBuilder.AppendFormat("-ff {0} ", this.FlowFilterType);
if (!string.IsNullOrWhiteSpace(this.VideoOutput)) stringBuilder.AppendFormat("-o \"{0}\" ", this.VideoOutput);
stringBuilder.AppendFormat("\"{0}\"", this.VideoInput);
if (!string.IsNullOrWhiteSpace(this.VideoOutput))
{
string videoOutputFile = string.Empty;
if (this.MultipleFiles)
{
var format = new StringBuilder(Path.GetFileNameWithoutExtension(this.VideoOutput));
format.Append("_{0:");
for (int i = 0; i < this.videoInput.Count().ToString().Length; i++)
{
format.Append("0");
}
format.Append("}");
format.Append(Path.GetExtension(this.VideoOutput));
var newName = string.Format(format.ToString(), videoInputIndex + 1); // Since the index is zero based, we will add one to make the output more human readable.
videoOutputFile = Path.Combine(Path.GetDirectoryName(this.VideoOutput), newName);
}
else
{
videoOutputFile = this.VideoOutput;
}
stringBuilder.AppendFormat("-o \"{0}\" ", videoOutputFile);
}
if (this.VideoInput.Any())
{
stringBuilder.AppendFormat("\"{0}\"", this.VideoInput.ElementAt(videoInputIndex));
}
return stringBuilder.ToString();
}

View File

@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.1.*")]
[assembly: AssemblyVersion("1.0.2.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -68,8 +68,10 @@
<Compile Include="AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile>
<Compile Include="BoolVisibilityConverter.cs" />
<Compile Include="FlowFilterType.cs" />
<Compile Include="InverseBoolConverter.cs" />
<Compile Include="InverseBoolVisibilityConverter.cs" />
<Compile Include="OptionsConfigurationFile.cs" />
<Compile Include="OptionsWindow.xaml.cs">
<DependentUpon>OptionsWindow.xaml</DependentUpon>