diff --git a/README.md b/README.md
index f9b7e0f..f480782 100644
--- a/README.md
+++ b/README.md
@@ -2,34 +2,37 @@
Creates Color Bands from movies
It is now the second time I see Color Bands on reddit without a source code provided,
-as if it was made by some evil magic.
+as if it was made by some evil magic. This time the person creating those is also selling these images.
-# How to use
-Just double click the .exe file and fill in the requested values.
-
-**Hint:** When asked for file names, you can drag the file itself on the console to fill in the full path.
-
-## Questions asked
-List of questions you are asked to answer
+Well now you can create them on your own. And it's fast too.
+For most source files it encodes with at least 20x playback speed.
-### Source video file
-The path and name of the video file to use
+# How to use
+This application can be used in Windowed and Console mode.
-### Destination image file
-The path and name of the destination image (the color band)
+## Windowed mode
+Just double click the .exe file to get the graphical user interface.
-### Image height
-The height of the image. The width is determined by the number of frames in the video.
+## Console mode
+You can use it in command line mode (untested as fo now).
+Type `ColorBand /?` for help.
-### Use single color
-If you answer `y`, then each frame is reduced to one color.
-If you answer `n`, then each frame is reduced to a width of 1.
+# Single color mode
+Single color mode reduces a frame to a single pixel instead of a vertical line.
+This causes the frame band to be only one color per frame.
+The height value still works. This is what most people generate, but it looks less awesome.
+Multi color mode examples can be found on [imgur](http://imgur.com/a/M9oIx).
# TODO
-- Make the programm accept command line arguments
-- GUI
-- Error checking
+- [X] Adding graphical interface
+- [X] Autodetect height from video
+- [X] Use command line arguments instead of prompts
+- [X] Some error checking
+- [ ] Make image joiner async for UI
-# FFMPEG
+# License
+This application is licensed under the [GNU agpl 3.0](https://www.gnu.org/licenses/agpl-3.0.txt).
+FFmpeg is licensed under [this mess](https://www.ffmpeg.org/legal.html).
+# FFMPEG
This work contains the compiled ffmpeg binaries inside blob.bin.
diff --git a/colorBand/Compressor.cs b/colorBand/Compressor.cs
index b9b5888..8f9c57b 100644
--- a/colorBand/Compressor.cs
+++ b/colorBand/Compressor.cs
@@ -16,7 +16,8 @@ public static class Compressor
/// During compression, the path information is lost
/// Full file names.
/// Destination stream
- public static void Compress(string[] Files, Stream Destination)
+ /// Logs each compressed file if true
+ public static void Compress(string[] Files, Stream Destination, bool Verbose)
{
FileInfo[] FF = Array.ConvertAll(Files, delegate(string f) { return new FileInfo(f); });
@@ -27,9 +28,10 @@ public static void Compress(string[] Files, Stream Destination)
BW.Write(Files.Length);
foreach (FileInfo F in FF)
{
-#if DEBUG
- Console.WriteLine("Compressing {0}", F.Name);
-#endif
+ if (Verbose)
+ {
+ Console.Error.WriteLine("Compressing {0}", F.Name);
+ }
byte[] Data = File.ReadAllBytes(F.FullName);
BW.Write(Encoding.UTF8.GetByteCount(F.Name));
BW.Write(Encoding.UTF8.GetBytes(F.Name));
@@ -45,7 +47,8 @@ public static void Compress(string[] Files, Stream Destination)
///
/// Destination directory
/// Compressed source stream
- public static void Decompress(string Directory, Stream Source)
+ /// Logs each decompressed file if true
+ public static void Decompress(string Directory, Stream Source, bool Verbose)
{
using (GZipStream GZ = new GZipStream(Source, CompressionMode.Decompress))
{
@@ -55,9 +58,10 @@ public static void Decompress(string Directory, Stream Source)
for (int i = 0; i < Count; i++)
{
string FileName = Encoding.UTF8.GetString(BR.ReadBytes(BR.ReadInt32()));
-#if DEBUG
- Console.WriteLine("Decompressing {0}", FileName);
-#endif
+ if (Verbose)
+ {
+ Console.Error.WriteLine("Decompressing {0}", FileName);
+ }
byte[] Content = BR.ReadBytes(BR.ReadInt32());
File.WriteAllBytes(Path.Combine(Directory, FileName), Content);
}
diff --git a/colorBand/Program.cs b/colorBand/Program.cs
index 2fe1f40..8d85831 100644
--- a/colorBand/Program.cs
+++ b/colorBand/Program.cs
@@ -7,152 +7,86 @@
using System.IO.Compression;
using colorBand.Properties;
using AyrA.IO;
+using System.Windows.Forms;
namespace colorBand
{
- public struct StatusLine
- {
- public int frame;
- public double fps, speed;
- public TimeSpan Time;
-
- public StatusLine(string Line)
- {
- bool skip = false;
- string Trimmed = string.Empty;
-
- frame = 0;
- fps = speed = 0.0;
- Time = new TimeSpan(0L);
-
- if (Line.StartsWith("frame="))
- {
- foreach (char c in Line.Trim())
- {
- if (skip)
- {
- if (c != ' ')
- {
- Trimmed += c;
- skip = false;
- }
- }
- else
- {
- if (c == '=')
- {
- skip = true;
- }
- Trimmed += c;
- }
- }
- foreach (string Part in Trimmed.Split(' '))
- {
- if (Part.Contains("="))
- {
- switch (Part.Split('=')[0])
- {
- case "time":
- string[] Segments = Part.Split('=')[1].Split(':');
-
- if (Segments.Length == 3)
- {
- Time = new TimeSpan(
- TryInt(Segments[0], 0),
- TryInt(Segments[1], 0),
- TryInt(Segments[2].Split('.')[0], 0));
- }
-
- break;
- case "frame":
- int.TryParse(Part.Split('=')[1], out frame);
- break;
- case "fps":
- double.TryParse(Part.Split('=')[1], out fps);
- break;
- case "speed":
- double.TryParse(Part.Split('=')[1].Trim('x'), out speed);
- break;
- }
- }
- }
- }
- }
-
- private int TryInt(string s, int Default)
- {
- int i = 0;
- return int.TryParse(s, out i) ? i : Default;
- }
- }
-
public class Program
{
- ///
- /// Command line for FFMPEG.
- /// Explanation: takes video input and renders it with 1 fps and downscaled to a single pixel to an array of png files
- ///
- const string CMDLINE = "-i \"{0}\" -lavfi fps=1,scale=1:{2}:flags=lanczos \"{1}\\%06d.png\"";
-
///
/// Main entry of Application
///
/// Command line arguments
+ [STAThread]
static void Main(string[] args)
{
+#if DEBUG
+ args = new string[]
+ {
+ @"C:\Temp\media\Sim City SNES TAS (Tool Assisted Speedrun) - last input 06_52.09 _ 600k people 47_00-2g6uPk-A1HY.mp4",
+ @"C:\Temp\Band.png"
+ };
+#endif
+ //Show Help and exit if requested
+ if (HasHelp(args))
+ {
+ PrintHelp();
+ return;
+ }
+ //Show UI if no Command line arguments given
+ if (args.Length == 0)
+ {
+ ShowUI();
+ return;
+ }
+
+ //Parse Arguments
+ Arguments A=ParseArgs(args);
+ if (!A.Valid)
+ {
+ return;
+ }
+
Color[] Colors;
- string TempDir = GetTempDir("frameband");
+ string TempDir = Tools.GetTempDir("frameband");
string FFmpegFile = Path.Combine(TempDir, "ffmpeg.exe");
-#if DEBUG
- //in debug mode I use hardcoded information because lazy.
- //The source is a Youtube video with the ID 2g6uPk-A1HY
- string VideoFile = @"C:\Temp\media\Sim City SNES TAS (Tool Assisted Speedrun) - last input 06_52.09 _ 600k people 47_00-2g6uPk-A1HY.mp4";
- string OutputFile = @"C:\temp\band.png";
- bool SingleColor = false;
- int Height = 240;
-#else
- string VideoFile = Ask("Source video file").Trim('"');
- string OutputFile = Ask("Destination image file").Trim('"');
- int Height = 0;
- while (!int.TryParse(Ask("Image Height"), out Height) || Height < 1) ;
- bool SingleColor = Ask("Use single color [y/n]").ToLower() == "y";
-#endif
- Console.Write("Extracting FFmpeg...");
- WriteEncoder(TempDir);
- Console.WriteLine("[DONE]");
+
+ Console.Error.Write("Extracting FFmpeg...");
+ Tools.WriteEncoder(TempDir);
+ Console.Error.WriteLine("[DONE]");
DateTime Start = DateTime.Now;
//Generate single frames into PNG
//Note: check if input is a video with at least 1 frame at all.
- CreateImages(VideoFile, TempDir, FFmpegFile, SingleColor ? 1 : Height);
+ CreateImages(A.InputFile, TempDir, FFmpegFile, A.SingleColor ? 1 : A.Height);
DateTime VideoGenerated = DateTime.Now;
- if (SingleColor)
+ if (A.SingleColor)
{
//Extract color information from the PNG images.
//GetFiles is rather slow, especially for a large number of files,
//but it is certainly easier to use than manually using the Windows API
- Colors = ExtractColorFromPixel(Directory.GetFiles(TempDir, "*.png"));
+ Colors = Tools.ExtractColorFromPixel(Directory.GetFiles(TempDir, "*.png"));
//It is possible, that no colors were extracted,
//for example if the input is an audio file.
if (Colors.Length > 0)
{
- RenderBand(Colors, OutputFile, Height);
+ Tools.RenderBand(Colors, A.OutputFile, A.Height);
}
else
{
//Probably invalid video file
- Console.WriteLine("No frames were extracted");
+ Console.Error.WriteLine("No frames were extracted");
}
}
else
{
- JoinImages(Directory.GetFiles(TempDir, "*.png"), OutputFile);
+ Tools.JoinImages(Directory.GetFiles(TempDir, "*.png"), A.OutputFile);
}
//Remove temporary PNG files.
@@ -165,7 +99,7 @@ static void Main(string[] args)
TimeSpan tsVideo = VideoGenerated.Subtract(Start);
TimeSpan tsImage = DateTime.Now.Subtract(VideoGenerated);
- Console.WriteLine(@"Done. Durations:
+ Console.Error.WriteLine(@"Done. Durations:
Total: {0:00}:{1:00}:{2:00}
Video: {3:00}:{4:00}:{5:00}
Image: {6:00}:{7:00}:{8:00}",
@@ -183,61 +117,6 @@ static void Main(string[] args)
#endif
}
- private static void WriteEncoder(string Dir)
- {
- using(MemoryStream MS=new MemoryStream(Resources.blob,false))
- {
- Compressor.Decompress(Dir, MS);
- }
- }
-
- ///
- /// Renders colors into a frame band
- ///
- /// List of colors
- /// Destination image file name
- /// Height of band
- private static void RenderBand(Color[] Colors, string DestinationFile, int Height)
- {
- using (Bitmap Output = new Bitmap(Colors.Length, Height))
- {
- //if height is 1, then use SetPixel
- if (Height == 1)
- {
- for (int i = 0; i < Colors.Length; i++)
- {
- Output.SetPixel(i, 0, Colors[i]);
- //This actually slows down the application quite a lot.
- Console.Write('-');
- }
- }
- else
- {
- using (Graphics G = Graphics.FromImage(Output))
- {
- //Note: Setting G.InterpolationMode to a "cheap" value has no effect,
- //as it is only used for scaling.
- //Creating a 1 pixel high image and then scale the height up does not increases the speed.
- for (int i = 0; i < Colors.Length; i++)
- {
- //Why is this an IDisposable?
- using (Pen P = new Pen(Colors[i]))
- {
- //Drawing a line is by far faster than setting individual pixels.
- G.DrawLine(P, new Point(i, 0), new Point(i, Height - 1));
- //This actually slows down the application quite a lot.
- Console.Write('-');
- }
- }
- }
- }
- //This detects the image format from the extension
- //See System.Drawing.Imaging.ImageFormat enumeration for supported types.
- Output.Save(DestinationFile);
- Console.WriteLine();
- }
- }
-
///
/// Create an array of frame images from a video file
///
@@ -247,15 +126,15 @@ private static void RenderBand(Color[] Colors, string DestinationFile, int Heigh
/// Height of generated frame bands.
private static void CreateImages(string Video, string ImagePath, string FFmpeg, int Height)
{
- Console.WriteLine("Press [q] to abort processing and use existing frames");
- using (Process P = Process.Start(new ProcessStartInfo(FFmpeg, string.Format(CMDLINE, Video, ImagePath, Height))
- {
- CreateNoWindow = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- UseShellExecute = false
- }))
+ VideoInfo VI = Tools.GetVideoInfo(Video);
+ if (Height < 1)
+ {
+ Height = VI.Resolution.Height;
+ }
+ DateTime Start = DateTime.Now;
+
+ Console.Error.WriteLine("Press [q] to abort processing and use existing frames");
+ using (Process P = Tools.BeginConversion(Video, ImagePath, Height))
{
using (StreamReader SR = P.StandardError)
{
@@ -263,18 +142,24 @@ private static void CreateImages(string Video, string ImagePath, string FFmpeg,
int Y = Console.CursorTop;
while (!SR.EndOfStream)
{
- if (Console.KeyAvailable && Console.ReadKey().Key == ConsoleKey.Q)
+ if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Q)
{
P.StandardInput.WriteLine("q");
}
SL = new StatusLine(SR.ReadLine());
- if (SL.frame > 0)
+ TimeSpan Estimate = new TimeSpan(0, 0, (int)(VI.Duration.TotalSeconds / SL.Speed));
+ //This creates a timespan with the milliseconds cut out of.
+ TimeSpan CurrentTime = new TimeSpan(0,0,(int)((DateTime.Now.Ticks-Start.Ticks)/10000000L));
+
+ if (SL.Frame > 0)
{
Console.SetCursorPosition(0, Y);
- Console.WriteLine(@"Time : {0}
-FPS : {1}
-Speed : {2}
-Frames: {3}",SL.Time,SL.fps,SL.speed,SL.frame);
+ Console.Error.WriteLine(@"Time : {0}
+Speed : {1,-6}
+Frames : {2,-6}
+
+Estimate: {3}
+Current : {4}", SL.Time, SL.Speed, SL.Frame, Estimate, CurrentTime);
}
}
}
@@ -285,70 +170,136 @@ private static void CreateImages(string Video, string ImagePath, string FFmpeg,
}
///
- /// Extracts Color information from the top left pixel
+ /// Scans arguments for common help requests
///
- /// List of files
- /// List of pixel colors
- private static Color[] ExtractColorFromPixel(string[] Files)
+ /// Arguments
+ /// True, if help requested
+ private static bool HasHelp(string[] Args)
{
- List Colors = new List();
- foreach (string s in Files)
+ foreach (string Arg in Args)
{
- Bitmap I = null;
- try
+ if (Arg.ToLower() == "--help" ||
+ Arg.ToLower() == "/h" ||
+ Arg == "/?" ||
+ Arg == "-?")
{
- //Ironically Bitmap.FromFile comes from the Image class and will return an Image object,
- //but it is fully compatible with the Bitmap type so we just cast.
- I = (Bitmap)Bitmap.FromFile(s);
- }
- catch
- {
- //I never had this happen but better add an error resolver.
- //In this case, we just ignore that frame.
- Console.Write('X');
- continue;
- }
- using (I)
- {
- //Extract image color
- Colors.Add(I.GetPixel(0, 0));
- //This actually slows down the application quite a lot.
- Console.Write('.');
+ return true;
}
}
- Console.WriteLine();
- return Colors.ToArray();
+ return false;
+ }
+
+ ///
+ /// Print Help message
+ ///
+ private static void PrintHelp()
+ {
+ Console.Error.WriteLine(@"ColorBand.exe [OutputFile] [Height] [/s]
+
+InputFile - Soure video file to extract frames
+OutputFile - Destination file to write color band. If not specified,
+ The band is saved in the InputFile directory
+Height - Height of color band.
+ If not specified, it uses the video height.
+/s - If specified, reduces each frame to a single pixel instead of
+ a line. Not faster. Just looks differently");
}
///
- /// Joins an array of images horizontally
+ /// Parses command line argument into formatted structure
///
- /// List of images
- /// Destination File
- private static void JoinImages(string[] Files, string OutputFile)
+ /// Raw arguments
+ /// Command line arguments structure
+ private static Arguments ParseArgs(string[] Args)
{
- int x = 0;
- int Height = 0;
- using (Image I = Image.FromFile(Files[0]))
+ Arguments A = new Arguments()
{
- Height = I.Height;
- }
- using (Bitmap B = new Bitmap(Files.Length, Height))
+ InputFile = null,
+ OutputFile = null,
+ SingleColor = false,
+ Height = 0,
+ Valid = true
+ };
+ bool PNGSet = false;
+
+ foreach (string Arg in Args)
{
- using (Graphics G = Graphics.FromImage(B))
+ switch (Arg.ToLower())
{
- foreach(string Name in Files)
- {
- using (Image I = Image.FromFile(Name))
+ case "/s":
+ A.SingleColor = true;
+ break;
+ default:
+ //if integer, assume it is the height
+ if (Tools.IsInt(Arg))
{
- G.DrawImageUnscaled(I, new Point(x++, 0));
+ if (int.Parse(Arg) > 0)
+ {
+ if (A.Height == 0)
+ {
+ A.Height = int.Parse(Arg);
+ }
+ else
+ {
+ Console.Error.WriteLine("Height has been specified twice");
+ A.Valid = false;
+ return A;
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine("Height must be bigger than 0");
+ A.Valid = false;
+ return A;
+ }
}
- Console.Write('-');
- }
+ else
+ {
+ //either input or output file
+ if (A.InputFile == null)
+ {
+ //First file is input file
+ A.InputFile = Arg;
+ if (File.Exists(Arg))
+ {
+ //automatically set output file sot it becomes an optional argument
+ A.OutputFile = Tools.SwapExt(Arg, "png");
+ }
+ else
+ {
+ Console.Error.WriteLine("InputFile does not exists or is inaccessible");
+ A.Valid = false;
+ return A;
+ }
+ }
+ else if (!PNGSet)
+ {
+ //second file is output file
+ PNGSet = true;
+ try
+ {
+ File.Create(Arg).Close();
+ File.Delete(Arg);
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine("Can't create output file. Error: {0}", ex.Message);
+ A.Valid = false;
+ return A;
+ }
+ A.OutputFile = Arg;
+ }
+ else
+ {
+ Console.Error.WriteLine("More than two files specified");
+ A.Valid = false;
+ return A;
+ }
+ }
+ break;
}
- B.Save(OutputFile);
- Console.WriteLine();
}
+ return A;
}
///
@@ -363,27 +314,21 @@ private static string Ask(string p)
}
///
- /// Attempts to create a temporary folder
+ /// Show the graphical user interface
///
- /// Name of the folder to create
- /// Full path of folder created
- static string GetTempDir(string Dirname)
+ private static void ShowUI()
{
- int i = 0;
- string TempRoot = Path.Combine(Path.GetTempPath(), Dirname + "_");
- while (true)
- {
- string current = string.Format("{0}{1}", TempRoot, i++);
- try
- {
- Directory.CreateDirectory(current);
- return current;
- }
- catch
- {
- //you can increase i here instead if you want.
- }
- }
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new frmMain());
}
+
+ }
+
+ public struct Arguments
+ {
+ public string InputFile, OutputFile;
+ public int Height;
+ public bool SingleColor, Valid;
}
}
diff --git a/colorBand/Properties/AssemblyInfo.cs b/colorBand/Properties/AssemblyInfo.cs
index 7f8d8c8..7379173 100644
--- a/colorBand/Properties/AssemblyInfo.cs
+++ b/colorBand/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@
// 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.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: AssemblyVersion("2.0.0.0")]
+[assembly: AssemblyFileVersion("2.0.0.0")]
diff --git a/colorBand/StatusLine.cs b/colorBand/StatusLine.cs
new file mode 100644
index 0000000..54f4a68
--- /dev/null
+++ b/colorBand/StatusLine.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace colorBand
+{
+ public struct StatusLine
+ {
+ public int Frame;
+ public double FPS, Speed;
+ public TimeSpan Time;
+
+ public StatusLine(string Line)
+ {
+ bool skip = false;
+ string Trimmed = string.Empty;
+
+ Frame = 0;
+ FPS = Speed = 0.0;
+ Time = new TimeSpan(0L);
+
+ if (Line.StartsWith("frame="))
+ {
+ foreach (char c in Line.Trim())
+ {
+ if (skip)
+ {
+ if (c != ' ')
+ {
+ Trimmed += c;
+ skip = false;
+ }
+ }
+ else
+ {
+ if (c == '=')
+ {
+ skip = true;
+ }
+ Trimmed += c;
+ }
+ }
+ foreach (string Part in Trimmed.Split(' '))
+ {
+ if (Part.Contains("="))
+ {
+ switch (Part.Split('=')[0])
+ {
+ case "time":
+ string[] Segments = Part.Split('=')[1].Split(':');
+
+ if (Segments.Length == 3)
+ {
+ Time = new TimeSpan(
+ TryInt(Segments[0], 0),
+ TryInt(Segments[1], 0),
+ TryInt(Segments[2].Split('.')[0], 0));
+ }
+
+ break;
+ case "frame":
+ int.TryParse(Part.Split('=')[1], out Frame);
+ break;
+ case "fps":
+ double.TryParse(Part.Split('=')[1], out FPS);
+ break;
+ case "speed":
+ double.TryParse(Part.Split('=')[1].Trim('x'), out Speed);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private int TryInt(string s, int Default)
+ {
+ int i = 0;
+ return int.TryParse(s, out i) ? i : Default;
+ }
+ }
+}
diff --git a/colorBand/Terminal.cs b/colorBand/Terminal.cs
new file mode 100644
index 0000000..6a7ade8
--- /dev/null
+++ b/colorBand/Terminal.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace AyrA.IO
+{
+ ///
+ /// Provides functions to work with consoles
+ ///
+ public static class Terminal
+ {
+ [DllImport("kernel32.dll")]
+ private static extern bool AllocConsole();
+
+ [DllImport("kernel32.dll")]
+ private static extern bool FreeConsole();
+
+ [DllImport("kernel32.dll")]
+ private static extern bool AttachConsole(int dwProcessId);
+
+ ///
+ /// For AttachToConsole, to attach to the parent process console
+ ///
+ public const int PARENT = -1;
+
+ ///
+ /// Returns all console colors sorted from 0x0 to 0xF
+ ///
+ public static ConsoleColor[] ColorRow
+ {
+ get
+ {
+ ConsoleColor[] c = new ConsoleColor[16];
+ for (int i = 0; i < 16; i++)
+ {
+ c[i] = (ConsoleColor)i;
+ }
+ return c;
+ }
+ }
+
+ ///
+ /// creates a new console, an application can only have one
+ ///
+ /// true, if created
+ public static bool CreateConsole()
+ {
+ return AllocConsole();
+ }
+
+ ///
+ /// Removes your application from the console handle.
+ /// If you are the last application to have a handle,
+ /// the console is closed
+ ///
+ /// true, on success
+ public static bool RemoveConsole()
+ {
+ return FreeConsole();
+ }
+
+ ///
+ /// Attaches your application to a given processes console
+ ///
+ /// Process to attach to. null to attach to parent process
+ /// true, if success
+ public static bool AttachToConsole(Process P)
+ {
+ return AttachToConsole(P == null ? PARENT : P.Id);
+ }
+
+ ///
+ /// Attach to a console of the given process ID
+ ///
+ /// process ID, or PARENT constant
+ /// true, on success
+ public static bool AttachToConsole(int ID)
+ {
+ return AttachConsole(ID);
+ }
+
+ ///
+ /// Writes a line with colored text and adds a CRLF
+ ///
+ /// Text to be written
+ /// Foreground color map
+ /// Background color Map
+ public static void printColorL(string text, string mapF, string mapB)
+ {
+ printColor(text, mapF, mapB);
+ System.Console.WriteLine();
+ }
+
+ ///
+ /// Writes a single line with colored text
+ ///
+ /// Line to write
+ /// Foreground color map
+ /// Background color Map
+ public static void printColor(string text, string mapF, string mapB)
+ {
+ int color = 0;
+
+ mapF = mapF.ToUpper().Replace(' ', '_');
+ mapB = mapB.ToUpper().Replace(' ', '_');
+
+ if (text.Length != mapF.Length || mapF.Length != mapB.Length)
+ {
+ throw new Exception("All params must be of the same length");
+ }
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (mapF[i] != '_')
+ {
+ if (!int.TryParse(mapF.Substring(i, 1), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out color))
+ {
+ throw new Exception(string.Format("Foreground color at pos {0} is invalid. Is: '{1}'", i, mapF[i]));
+ }
+ }
+ if (mapB[i] != '_')
+ {
+ if (!int.TryParse(mapB.Substring(i, 1), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out color))
+ {
+ throw new Exception(string.Format("Background color at pos {0} is invalid. Is: '{1}'", i, mapB[i]));
+ }
+ }
+ }
+
+
+ ConsoleColor[] C = new ConsoleColor[] { System.Console.ForegroundColor, System.Console.BackgroundColor };
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (mapF[i] != '_')
+ {
+ System.Console.ForegroundColor = (ConsoleColor)int.Parse(mapF.Substring(i, 1), NumberStyles.HexNumber);
+ }
+ if (mapB[i] != '_')
+ {
+ System.Console.BackgroundColor = (ConsoleColor)int.Parse(mapB.Substring(i, 1), NumberStyles.HexNumber);
+ }
+ System.Console.Write(text[i]);
+ }
+ System.Console.ForegroundColor = C[0];
+ System.Console.BackgroundColor = C[1];
+ }
+ }
+}
diff --git a/colorBand/Tools.cs b/colorBand/Tools.cs
new file mode 100644
index 0000000..f285b07
--- /dev/null
+++ b/colorBand/Tools.cs
@@ -0,0 +1,403 @@
+using System.IO;
+using System.Drawing;
+using AyrA.IO;
+using System;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using colorBand.Properties;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace colorBand
+{
+ public delegate void ThreadExitHandler(Thread T);
+
+ public static class Tools
+ {
+ private const string DURATION = "-show_entries format -v quiet \"{0}\"";
+ private const string RESOLUTION = "-show_entries stream=width,height -select_streams v:0 -v quiet \"{0}\"";
+
+ private const string FFMPEG = "-i \"{0}\" -lavfi fps=1,scale=1:{2}:flags=lanczos \"{1}\\%06d.png\"";
+
+ private static string CurrentEncoder = "ffmpeg.exe";
+ private static string CurrentInfoTool = "ffplay.exe";
+
+ public static bool LogToConsole = false;
+
+ public static event ThreadExitHandler ThreadExit;// = delegate { };
+
+ ///
+ /// Attempts to create a temporary folder
+ ///
+ /// Name of the folder to create
+ /// Full path of folder created
+ public static string GetTempDir(string Dirname)
+ {
+ int i = 0;
+ string TempRoot = Path.Combine(Path.GetTempPath(), Dirname + "_");
+ while (true)
+ {
+ string current = string.Format("{0}{1}", TempRoot, i++);
+ try
+ {
+ Directory.CreateDirectory(current);
+ return current;
+ }
+ catch
+ {
+ //you can increase i here instead if you want.
+ }
+ }
+ }
+
+ ///
+ /// Renders colors into a frame band
+ ///
+ /// List of colors
+ /// Destination image file name
+ /// Height of band
+ public static void RenderBand(Color[] Colors, string DestinationFile, int Height)
+ {
+ using (Bitmap Output = new Bitmap(Colors.Length, Height))
+ {
+ //if height is 1, then use SetPixel
+ if (Height == 1)
+ {
+ for (int i = 0; i < Colors.Length; i++)
+ {
+ Output.SetPixel(i, 0, Colors[i]);
+ if (LogToConsole)
+ {
+ //This actually slows down the application quite a lot.
+ Console.Error.Write('-');
+ }
+ }
+ }
+ else
+ {
+ using (Graphics G = Graphics.FromImage(Output))
+ {
+ //Note: Setting G.InterpolationMode to a "cheap" value has no effect,
+ //as it is only used for scaling.
+ //Creating a 1 pixel high image and then scale the height up does not increases the speed.
+ for (int i = 0; i < Colors.Length; i++)
+ {
+ //Why is this an IDisposable?
+ using (Pen P = new Pen(Colors[i]))
+ {
+ //Drawing a line is by far faster than setting individual pixels.
+ G.DrawLine(P, new Point(i, 0), new Point(i, Height - 1));
+ if (LogToConsole)
+ {
+ //This actually slows down the application quite a lot.
+ Console.Error.Write('-');
+ }
+ }
+ }
+ }
+ }
+ //This detects the image format from the extension
+ //See System.Drawing.Imaging.ImageFormat enumeration for supported types.
+ Output.Save(DestinationFile);
+ if (LogToConsole)
+ {
+ Console.Error.WriteLine();
+ }
+ }
+ }
+
+ ///
+ /// Joins an array of images horizontally
+ ///
+ /// List of images
+ /// Destination File
+ public static void JoinImages(string[] Files, string OutputFile)
+ {
+ int x = 0;
+ int Height = 0;
+ using (Image I = Image.FromFile(Files[0]))
+ {
+ Height = I.Height;
+ }
+ using (Bitmap B = new Bitmap(Files.Length, Height))
+ {
+ using (Graphics G = Graphics.FromImage(B))
+ {
+ foreach (string Name in Files)
+ {
+ using (Image I = Image.FromFile(Name))
+ {
+ G.DrawImageUnscaled(I, new Point(x++, 0));
+ }
+ Console.Write('-');
+ }
+ }
+ B.Save(OutputFile);
+ Console.WriteLine();
+ }
+ }
+
+ ///
+ /// Extracts Color information from the top left pixel
+ ///
+ /// List of files
+ /// List of pixel colors
+ public static Color[] ExtractColorFromPixel(string[] Files)
+ {
+ List Colors = new List();
+ foreach (string s in Files)
+ {
+ Bitmap I = null;
+ try
+ {
+ //Ironically Bitmap.FromFile comes from the Image class and will return an Image object,
+ //but it is fully compatible with the Bitmap type so we just cast.
+ I = (Bitmap)Bitmap.FromFile(s);
+ }
+ catch
+ {
+ //I never had this happen but better add an error resolver.
+ //In this case, we just ignore that frame.
+ if (LogToConsole)
+ {
+ Console.Error.Write('X');
+ }
+ continue;
+ }
+ using (I)
+ {
+ //Extract image color
+ Colors.Add(I.GetPixel(0, 0));
+ if (LogToConsole)
+ {
+ //This actually slows down the application quite a lot.
+ Console.Error.Write('.');
+ }
+ }
+ }
+ if (LogToConsole)
+ {
+ Console.Error.WriteLine();
+ }
+ return Colors.ToArray();
+ }
+
+ ///
+ /// Extract FFmpeg
+ ///
+ /// Destination Directory
+ public static void WriteEncoder(string Dir)
+ {
+ using (MemoryStream MS = new MemoryStream(Resources.blob, false))
+ {
+ Compressor.Decompress(Dir, MS, LogToConsole);
+ CurrentEncoder = Path.Combine(Dir, "ffmpeg.exe");
+ CurrentInfoTool = Path.Combine(Dir, "ffprobe.exe");
+ }
+ }
+
+ ///
+ /// Extracts some basic information from the supplied Video file
+ ///
+ /// Video file
+ /// Video information
+ public static VideoInfo GetVideoInfo(string FileName)
+ {
+ VideoInfo VI = new VideoInfo()
+ {
+ CodecName = string.Empty,
+ VideoFile = FileName,
+ Resolution = new Size(0, 0),
+ Duration = new TimeSpan(0)
+ };
+
+ double TempDuration = 0.0;
+ long TempValue = 0;
+
+ //Basic video info
+ using (Process P = GetInfoProcess(DURATION, FileName))
+ {
+ var Lines = GetLines(P);
+ foreach (string Line in Lines)
+ {
+ if (Line.Contains("="))
+ {
+ var Name = Line.Substring(0, Line.IndexOf('=')).ToLower();
+ var Value = Line.Substring(Line.IndexOf('=') + 1);
+ switch (Name)
+ {
+ case "format_long_name":
+ VI.CodecName = Value;
+ break;
+ case "duration":
+ if (double.TryParse(Value, out TempDuration))
+ {
+ VI.Duration = new TimeSpan(0, 0, (int)TempDuration);
+ }
+ break;
+ case "bit_rate":
+ if (long.TryParse(Value, out TempValue))
+ {
+ VI.Bitrate = TempValue;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ //Video dimensions
+
+ using (Process P = GetInfoProcess(RESOLUTION, FileName))
+ {
+ int TempResolution = 0;
+ var Lines = GetLines(P);
+ foreach (string Line in Lines)
+ {
+ if (Line.Contains("="))
+ {
+ var Name = Line.Substring(0, Line.IndexOf('=')).ToLower();
+ var Value = Line.Substring(Line.IndexOf('=') + 1);
+ switch (Name)
+ {
+ case "width":
+ if (int.TryParse(Value, out TempResolution))
+ {
+ VI.Resolution.Width = TempResolution;
+ }
+ break;
+ case "height":
+ if (int.TryParse(Value, out TempResolution))
+ {
+ VI.Resolution.Height = TempResolution;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return VI;
+ }
+
+ ///
+ /// Calls an event when the thread exits
+ ///
+ /// Thread to watch for
+ public static void WatchThread(Thread T)
+ {
+ Thread Temp = new Thread(delegate()
+ {
+ T.Join();
+ if (LogToConsole)
+ {
+ Console.Error.WriteLine("Thread {0} exited", T.Name);
+ }
+ ThreadExit(T);
+
+ }) { IsBackground = true, Name = "Watcher of " + T.Name };
+ Temp.Start();
+ }
+
+ ///
+ /// Gets a process object for ffprobe.exe
+ ///
+ /// Type of info
+ /// File name to scan
+ /// Process object (not yet started)
+ private static Process GetInfoProcess(string ArgType, string FileName)
+ {
+ return new Process()
+ {
+ StartInfo = new ProcessStartInfo()
+ {
+ FileName = CurrentInfoTool,
+ Arguments = string.Format(ArgType, FileName),
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ WindowStyle = ProcessWindowStyle.Hidden
+ }
+ };
+ }
+
+ private static string[] GetLines(Process P)
+ {
+ List Lines = new List();
+ P.Start();
+ P.WaitForExit();
+ while (!P.StandardOutput.EndOfStream)
+ {
+ Lines.Add(P.StandardOutput.ReadLine().Trim());
+ }
+ return Lines.ToArray();
+ }
+
+ ///
+ /// Begins conversion using FFmpeg
+ ///
+ /// Source Video File
+ /// Path to put extracted Frames
+ /// Height of video File
+ /// FFmpeg process (already started)
+ public static Process BeginConversion(string VideoFile, string TempPath, int VideoHeight)
+ {
+ Process P = new Process()
+ {
+ StartInfo = new ProcessStartInfo(CurrentEncoder, string.Format(FFMPEG, VideoFile, TempPath, VideoHeight))
+ {
+ CreateNoWindow = true,
+ RedirectStandardError = true,
+ RedirectStandardInput = true,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ UseShellExecute = false
+ },
+ EnableRaisingEvents = true
+ };
+ P.Start();
+ return P;
+ }
+
+ ///
+ /// Checks if a number is a valid integer
+ ///
+ /// Number as string
+ /// True if integer (int.Parse won't crash)
+ public static bool IsInt(string s)
+ {
+ int i = 0;
+ return int.TryParse(s, out i);
+ }
+
+ ///
+ /// Swaps the extension of a file name with a new one
+ ///
+ /// File name (path optional)
+ /// New extension
+ /// New File name
+ public static string SwapExt(string FileName, string NewExt)
+ {
+ string[] Segments = FileName.Split(Path.DirectorySeparatorChar);
+ int Last = Segments.Length - 1;
+
+ //if the name contains at least one dot, replace the part after the last occurence
+ if (Segments[Last].Contains("."))
+ {
+ Segments[Last] = Segments[Last].Substring(0, Segments[Last].LastIndexOf('.') + 1) + NewExt;
+ }
+ else
+ {
+ //No extension present. Just append the extension
+ Segments[Last] += "." + NewExt;
+ }
+ return string.Join(Path.DirectorySeparatorChar.ToString(), Segments);
+ }
+ }
+
+ public struct VideoInfo
+ {
+ public string VideoFile, CodecName;
+ public long Bitrate;
+ public Size Resolution;
+ public TimeSpan Duration;
+ }
+}
diff --git a/colorBand/colorBand.csproj b/colorBand/colorBand.csproj
index b906a74..6d36570 100644
--- a/colorBand/colorBand.csproj
+++ b/colorBand/colorBand.csproj
@@ -34,10 +34,23 @@
+
+
+ Form
+
+
+ frmEncoder.cs
+
+
+ Form
+
+
+ frmMain.cs
+
@@ -45,8 +58,18 @@
True
Resources.resx
+
+
+
+
+ frmEncoder.cs
+ Designer
+
+
+ frmMain.cs
+
ResXFileCodeGenerator
Resources.Designer.cs
diff --git a/colorBand/frmEncoder.Designer.cs b/colorBand/frmEncoder.Designer.cs
new file mode 100644
index 0000000..19ee0a5
--- /dev/null
+++ b/colorBand/frmEncoder.Designer.cs
@@ -0,0 +1,91 @@
+namespace colorBand
+{
+ partial class frmEncoder
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.pbStatus = new System.Windows.Forms.ProgressBar();
+ this.btnAbort = new System.Windows.Forms.Button();
+ this.lblEncodingProgress = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // pbStatus
+ //
+ this.pbStatus.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.pbStatus.Location = new System.Drawing.Point(12, 12);
+ this.pbStatus.Name = "pbStatus";
+ this.pbStatus.Size = new System.Drawing.Size(344, 23);
+ this.pbStatus.TabIndex = 0;
+ //
+ // btnAbort
+ //
+ this.btnAbort.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnAbort.Location = new System.Drawing.Point(362, 12);
+ this.btnAbort.Name = "btnAbort";
+ this.btnAbort.Size = new System.Drawing.Size(70, 23);
+ this.btnAbort.TabIndex = 1;
+ this.btnAbort.Text = "&Abort";
+ this.btnAbort.UseVisualStyleBackColor = true;
+ this.btnAbort.Click += new System.EventHandler(this.btnAbort_Click);
+ //
+ // lblEncodingProgress
+ //
+ this.lblEncodingProgress.AutoSize = true;
+ this.lblEncodingProgress.Location = new System.Drawing.Point(12, 45);
+ this.lblEncodingProgress.Name = "lblEncodingProgress";
+ this.lblEncodingProgress.Size = new System.Drawing.Size(95, 13);
+ this.lblEncodingProgress.TabIndex = 2;
+ this.lblEncodingProgress.Text = "Starting Encoder...";
+ //
+ // frmEncoder
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(444, 168);
+ this.ControlBox = false;
+ this.Controls.Add(this.lblEncodingProgress);
+ this.Controls.Add(this.btnAbort);
+ this.Controls.Add(this.pbStatus);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "frmEncoder";
+ this.Text = "Encoding";
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.frmEncoder_FormClosed);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ProgressBar pbStatus;
+ private System.Windows.Forms.Button btnAbort;
+ private System.Windows.Forms.Label lblEncodingProgress;
+ }
+}
\ No newline at end of file
diff --git a/colorBand/frmEncoder.cs b/colorBand/frmEncoder.cs
new file mode 100644
index 0000000..e013086
--- /dev/null
+++ b/colorBand/frmEncoder.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Windows.Forms;
+using System.Threading;
+using System.Diagnostics;
+using System.IO;
+
+namespace colorBand
+{
+ public partial class frmEncoder : Form
+ {
+ private string TempDir, SourceFile, DestinationFile;
+ private int BandHeight;
+ private bool SingleColor;
+ private Thread Encoder;
+ private bool Aborted = false;
+ private VideoInfo VI;
+
+ public frmEncoder(string TempDir, string SourceFile, string DestinationFile, int BandHeight, bool SingleColor)
+ {
+ this.TempDir = TempDir;
+ this.SourceFile = SourceFile;
+ this.DestinationFile = DestinationFile;
+ this.BandHeight = BandHeight;
+ this.SingleColor = SingleColor;
+
+ Tools.ThreadExit += Tools_ThreadExit;
+
+ VI = Tools.GetVideoInfo(SourceFile);
+
+ InitializeComponent();
+
+ pbStatus.Maximum = (int)VI.Duration.TotalSeconds;
+
+ Encoder = new Thread(delegate()
+ {
+ using (Process P = Tools.BeginConversion(SourceFile, TempDir, BandHeight))
+ {
+ using (StreamReader SR = P.StandardError)
+ {
+ StatusLine SL;
+ while (!SR.EndOfStream)
+ {
+ if (Aborted)
+ {
+ P.StandardInput.WriteLine("q");
+ }
+ SL = new StatusLine(SR.ReadLine());
+ if (!Aborted)
+ {
+ TimeSpan Estimate = new TimeSpan(0, 0, (int)(VI.Duration.TotalSeconds / SL.Speed));
+ if (SL.Frame > 0)
+ {
+ this.Invoke((MethodInvoker)delegate
+ {
+ lblEncodingProgress.Text = string.Format(@"Status:
+Speed: {0} times playback speed
+Current Time in Video: {1}
+Current Frame: {2}
+Progress: {3}
+Estimated Runtime: {4}",
+ SL.Speed, SL.Time, SL.Frame, Perc(SL.Frame, (int)VI.Duration.TotalSeconds), Estimate);
+ //Set progress bar only if valid
+ if (pbStatus.Maximum >= SL.Frame)
+ {
+ pbStatus.Value = SL.Frame;
+ }
+ });
+ }
+ }
+ }
+ }
+ P.WaitForExit();
+ }
+ })
+ {
+ IsBackground = true,
+ Name = "Encoding of " + SourceFile
+ };
+ Encoder.Start();
+ Tools.WatchThread(Encoder);
+ }
+
+ private int Perc(double Current, double Max)
+ {
+ if (Current > Max)
+ {
+ return 100;
+ }
+ if (Current < 0.0 || Max <= 0.0)
+ {
+ return 0;
+ }
+ return (int)(Current / Max * 100.0);
+ }
+
+ private void Tools_ThreadExit(Thread T)
+ {
+ if (T == Encoder)
+ {
+ if (!Aborted || MessageBox.Show("Do you still want to create the Frame Band?\r\nSelecting [YES] will use the existing frames", "Working with existing material", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
+ {
+ //Encoding Done
+ this.Invoke((MethodInvoker)EncodePNG);
+ }
+ this.Invoke((MethodInvoker)Close);
+ }
+ }
+
+ private void EncodePNG()
+ {
+ btnAbort.Enabled = false;
+ if (SingleColor)
+ {
+ //Extract color information from the PNG images.
+ //GetFiles is rather slow, especially for a large number of files,
+ //but it is certainly easier to use than manually using the Windows API
+ var Colors = Tools.ExtractColorFromPixel(Directory.GetFiles(TempDir, "*.png"));
+
+
+ //It is possible, that no colors were extracted,
+ //for example if the input is an audio file.
+ if (Colors.Length > 0)
+ {
+ Tools.RenderBand(Colors, DestinationFile, Height);
+ }
+ else
+ {
+ //Probably invalid video file
+ MessageBox.Show("No frames were extracted. Make sure the video file is not corrupted", "No frames extracted", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+ else
+ {
+ Tools.JoinImages(Directory.GetFiles(TempDir, "*.png"), DestinationFile);
+ }
+ }
+
+ private void btnAbort_Click(object sender, EventArgs e)
+ {
+ if (MessageBox.Show("Abort the encoding process?", "Abort encoding", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.Yes)
+ {
+ Aborted = true;
+ //Encoder.Join();
+ /*
+ if (MessageBox.Show("Do you still want to create the Frame Band?\r\nSelecting [YES] will use the existing frames", "Working with existing material", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
+ {
+ EncodePNG();
+ }
+ Close();
+ //*/
+ }
+ }
+
+ private void frmEncoder_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ Tools.ThreadExit -= Tools_ThreadExit;
+ }
+ }
+}
diff --git a/colorBand/frmEncoder.resx b/colorBand/frmEncoder.resx
new file mode 100644
index 0000000..19dc0dd
--- /dev/null
+++ b/colorBand/frmEncoder.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/colorBand/frmMain.Designer.cs b/colorBand/frmMain.Designer.cs
new file mode 100644
index 0000000..10251d4
--- /dev/null
+++ b/colorBand/frmMain.Designer.cs
@@ -0,0 +1,215 @@
+namespace colorBand
+{
+ partial class frmMain
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.btnSource = new System.Windows.Forms.Button();
+ this.btnDestination = new System.Windows.Forms.Button();
+ this.tbSource = new System.Windows.Forms.TextBox();
+ this.tbDestination = new System.Windows.Forms.TextBox();
+ this.btnStart = new System.Windows.Forms.Button();
+ this.nudHeight = new System.Windows.Forms.NumericUpDown();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.OFD = new System.Windows.Forms.OpenFileDialog();
+ this.SFD = new System.Windows.Forms.SaveFileDialog();
+ this.tbInfo = new System.Windows.Forms.TextBox();
+ this.cbSingleColor = new System.Windows.Forms.CheckBox();
+ ((System.ComponentModel.ISupportInitialize)(this.nudHeight)).BeginInit();
+ this.SuspendLayout();
+ //
+ // btnSource
+ //
+ this.btnSource.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnSource.Location = new System.Drawing.Point(503, 12);
+ this.btnSource.Name = "btnSource";
+ this.btnSource.Size = new System.Drawing.Size(35, 23);
+ this.btnSource.TabIndex = 2;
+ this.btnSource.Text = "...";
+ this.btnSource.UseVisualStyleBackColor = true;
+ this.btnSource.Click += new System.EventHandler(this.btnSource_Click);
+ //
+ // btnDestination
+ //
+ this.btnDestination.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnDestination.Location = new System.Drawing.Point(503, 41);
+ this.btnDestination.Name = "btnDestination";
+ this.btnDestination.Size = new System.Drawing.Size(35, 23);
+ this.btnDestination.TabIndex = 5;
+ this.btnDestination.Text = "...";
+ this.btnDestination.UseVisualStyleBackColor = true;
+ this.btnDestination.Click += new System.EventHandler(this.btnDestination_Click);
+ //
+ // tbSource
+ //
+ this.tbSource.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tbSource.Location = new System.Drawing.Point(117, 14);
+ this.tbSource.Name = "tbSource";
+ this.tbSource.Size = new System.Drawing.Size(380, 20);
+ this.tbSource.TabIndex = 1;
+ this.tbSource.Leave += new System.EventHandler(this.tbSource_Leave);
+ //
+ // tbDestination
+ //
+ this.tbDestination.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tbDestination.Location = new System.Drawing.Point(117, 43);
+ this.tbDestination.Name = "tbDestination";
+ this.tbDestination.Size = new System.Drawing.Size(380, 20);
+ this.tbDestination.TabIndex = 4;
+ //
+ // btnStart
+ //
+ this.btnStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnStart.Location = new System.Drawing.Point(478, 70);
+ this.btnStart.Name = "btnStart";
+ this.btnStart.Size = new System.Drawing.Size(60, 23);
+ this.btnStart.TabIndex = 9;
+ this.btnStart.Text = "Start";
+ this.btnStart.UseVisualStyleBackColor = true;
+ this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
+ //
+ // nudHeight
+ //
+ this.nudHeight.Location = new System.Drawing.Point(117, 70);
+ this.nudHeight.Maximum = new decimal(new int[] {
+ 10000,
+ 0,
+ 0,
+ 0});
+ this.nudHeight.Name = "nudHeight";
+ this.nudHeight.Size = new System.Drawing.Size(83, 20);
+ this.nudHeight.TabIndex = 7;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(12, 17);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(53, 13);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "Video File";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(12, 46);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(58, 13);
+ this.label2.TabIndex = 3;
+ this.label2.Text = "Output File";
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(12, 72);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(91, 13);
+ this.label3.TabIndex = 6;
+ this.label3.Text = "Color Band height";
+ //
+ // OFD
+ //
+ this.OFD.Filter = "Common Video Files|*.avi;*.mpeg;*.mpg;*.mkv;*.flv;*.mp4|All Files|*.*";
+ this.OFD.Title = "Video File";
+ //
+ // SFD
+ //
+ this.SFD.DefaultExt = "png";
+ this.SFD.Filter = "PNG files|*.png|All Files|*.*";
+ this.SFD.Title = "Frameband File";
+ //
+ // tbInfo
+ //
+ this.tbInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tbInfo.Location = new System.Drawing.Point(3, 99);
+ this.tbInfo.Multiline = true;
+ this.tbInfo.Name = "tbInfo";
+ this.tbInfo.ReadOnly = true;
+ this.tbInfo.Size = new System.Drawing.Size(535, 198);
+ this.tbInfo.TabIndex = 10;
+ this.tbInfo.Text = "File info appears here once you select a video";
+ //
+ // cbSingleColor
+ //
+ this.cbSingleColor.AutoSize = true;
+ this.cbSingleColor.Location = new System.Drawing.Point(206, 72);
+ this.cbSingleColor.Name = "cbSingleColor";
+ this.cbSingleColor.Size = new System.Drawing.Size(82, 17);
+ this.cbSingleColor.TabIndex = 8;
+ this.cbSingleColor.Text = "Single Color";
+ this.cbSingleColor.UseVisualStyleBackColor = true;
+ //
+ // frmMain
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(550, 309);
+ this.Controls.Add(this.cbSingleColor);
+ this.Controls.Add(this.tbInfo);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.nudHeight);
+ this.Controls.Add(this.btnStart);
+ this.Controls.Add(this.tbDestination);
+ this.Controls.Add(this.tbSource);
+ this.Controls.Add(this.btnDestination);
+ this.Controls.Add(this.btnSource);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
+ this.Name = "frmMain";
+ this.Text = "Frameband Generator";
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.frmMain_FormClosed);
+ ((System.ComponentModel.ISupportInitialize)(this.nudHeight)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button btnSource;
+ private System.Windows.Forms.Button btnDestination;
+ private System.Windows.Forms.TextBox tbSource;
+ private System.Windows.Forms.TextBox tbDestination;
+ private System.Windows.Forms.Button btnStart;
+ private System.Windows.Forms.NumericUpDown nudHeight;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.OpenFileDialog OFD;
+ private System.Windows.Forms.SaveFileDialog SFD;
+ private System.Windows.Forms.TextBox tbInfo;
+ private System.Windows.Forms.CheckBox cbSingleColor;
+ }
+}
\ No newline at end of file
diff --git a/colorBand/frmMain.cs b/colorBand/frmMain.cs
new file mode 100644
index 0000000..aa51a8a
--- /dev/null
+++ b/colorBand/frmMain.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using System.IO;
+
+namespace colorBand
+{
+ public partial class frmMain : Form
+ {
+ string TempPath = Tools.GetTempDir("frameband");
+
+ public frmMain()
+ {
+ Console.Error.WriteLine("Extracting FFmpeg...");
+ Tools.LogToConsole = true;
+ Tools.WriteEncoder(TempPath);
+ Tools.LogToConsole = false;
+ AyrA.IO.Terminal.RemoveConsole();
+ InitializeComponent();
+ }
+
+ private void btnSource_Click(object sender, EventArgs e)
+ {
+ if (OFD.ShowDialog() == DialogResult.OK)
+ {
+ tbSource.Text = OFD.FileName;
+ if (string.IsNullOrEmpty(tbDestination.Text))
+ {
+ AutoName();
+ }
+ SetInfo();
+ }
+ }
+
+ private void btnDestination_Click(object sender, EventArgs e)
+ {
+ if (SFD.ShowDialog() == DialogResult.OK)
+ {
+ tbDestination.Text = SFD.FileName;
+ if (!tbDestination.Text.EndsWith(".png"))
+ {
+ MessageBox.Show("Output file type must be PNG. It will be changed now");
+ tbDestination.Text = Tools.SwapExt(tbDestination.Text, "png");
+ }
+ }
+ }
+
+ private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
+ {
+ Directory.Delete(TempPath, true);
+ }
+
+ private void btnStart_Click(object sender, EventArgs e)
+ {
+ if (nudHeight.Value < 1)
+ {
+ MessageBox.Show("Please specify a height\r\nYou can open the video again to have this automatically assigned", "Height invalid", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ else if (!File.Exists(tbSource.Text))
+ {
+ MessageBox.Show("The input file doesn't exists.", "Input file not found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ else if (!tbDestination.Text.ToLower().EndsWith(".png"))
+ {
+ MessageBox.Show("Output file must be PNG. We change that now.", "Output file format invalid", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ tbDestination.Text = Tools.SwapExt(tbDestination.Text, "png");
+ }
+ else
+ {
+ var Encoder = new frmEncoder(TempPath, tbSource.Text, tbDestination.Text, (int)nudHeight.Value, cbSingleColor.Checked);
+ Encoder.ShowDialog();
+ //everything OK. Begin conversion
+ }
+ }
+
+ private void tbSource_Leave(object sender, EventArgs e)
+ {
+ if (!string.IsNullOrEmpty(tbSource.Text))
+ {
+ SetInfo();
+ }
+ }
+
+ private void AutoName()
+ {
+ tbDestination.Text = Tools.SwapExt(tbSource.Text, "png");
+ }
+
+ private void SetInfo()
+ {
+ if (File.Exists(tbSource.Text))
+ {
+ VideoInfo VI = Tools.GetVideoInfo(tbSource.Text);
+ nudHeight.Value = VI.Resolution.Height;
+ tbInfo.Text = string.Format(@"File: {0}
+Type: {1}
+Runtime: {2}
+Resolution: {3}
+Bitrate: {4} kbit/s
+Frames: {5} (estimated from runtime)",
+ VI.VideoFile, VI.CodecName, VI.Duration, VI.Resolution, VI.Bitrate/1000, Math.Floor(VI.Duration.TotalSeconds));
+ }
+ else
+ {
+ MessageBox.Show("The input file doesn't exists.", "Input file not found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
+ }
+ }
+ }
+}
diff --git a/colorBand/frmMain.resx b/colorBand/frmMain.resx
new file mode 100644
index 0000000..9a38770
--- /dev/null
+++ b/colorBand/frmMain.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 93, 17
+
+
\ No newline at end of file