Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / video

Compressing my videos

5.00/5 (3 votes)
30 Oct 2012CPOL1 min read 28.6K  
Very simple program for in-situ video compression.

Introduction  

 The piece of code below compressed my personal videos. 

Background 

I have a ton of personal videos on my home hard drive. Sadly coming straight from the photo cameras, most of the videos are badly compressed.  

First I used a program to remove duplicates http://www.nirsoft.net/utils/search_my_files.html 

Then I did try a few utilities to compress them. The best utility I could find is called winff.

Sadly it requires an output folder and would not let  me compress the videos in the folder they come from. It also would not compress the files with accentuated letter in the file path. 

When you got a few hundred videos it become a real problem.  

Using the code  

Then as I could not find a utility to compress the files in-situ, I wrote the program below. 

Although this program worked for me, it is tailored to my own problem and will probably need amendments to work with your scenario. 

C#
using System;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace CompressVideos
{
    class Program
    {
        // 20,875 files / 538 folders
        // 57.0 GB (61,256,840,351 bytes)
        const string FFMPEG_SUFFIX = "(ffmpeg)";
        private static int nbVideoFound;
        private static long totalInputLength;
        private static long totalOutputLength;
        private static int totalFileCount;

        static void Main(string[] args)
        {
            ProcessDirectory(new DirectoryInfo(args[0]));
            Console.WriteLine("Complete");
            if (totalInputLength > 0)
            {
                Console.WriteLine("In " + totalInputLength.ToString("#,##0"));
                Console.WriteLine("Out " + totalOutputLength.ToString("#,##0"));
                Console.WriteLine("Compressed size " + 100 * totalOutputLength / totalInputLength + "%");
            }
            Console.ReadKey();
        }

        private static void ProcessDirectory(DirectoryInfo parentDirectory)
        {
            bool firstFile = true;
            foreach (var fsi in parentDirectory.GetFileSystemInfos())
            {
                try
                {
                    var fi = fsi as FileInfo;
                    if (fi != null && fi.Length>0)
                    {
                        if (Path.GetFileNameWithoutExtension(fi.Name).EndsWith(FFMPEG_SUFFIX)) continue;
                        var ext = fi.Extension.ToLower();

                        switch (ext)
                        {
                            case ".mp4":
                                // my camera does thumbnails of every video and I don't need or like them
                                var thumb = new FileInfo(fi.Directory + @"\" + Path.GetFileNameWithoutExtension(fi.Name) + ".jpg");
                                if (thumb.Exists) thumb.Delete();
                                break;

                            case ".mov":
                            case ".avi":
                            case ".wmv":
                            case ".flv":
                            case ".3gp":
                                nbVideoFound++;
                                if (firstFile)
                                {
                                    firstFile = false;
                                    Console.WriteLine("=== " + parentDirectory.FullName + " ===");
                                }
                                FileInfo fo;
                                if (ProcessFile(fi, out fo))
                                {
                                    int rate = (int)Math.Round(fo.Length * 100.0 / fi.Length);

                                    if (rate < 3)
                                    {
                                        Console.ForegroundColor = ConsoleColor.Red;
                                        Console.WriteLine("[Too Small] " + fsi.Name + " " + rate + "%");
                                        Console.ForegroundColor = ConsoleColor.Gray;
                                        fo.Delete();
                                    }
                                    else if (rate > 50)
                                    {
                                        Console.ForegroundColor = ConsoleColor.Yellow;
                                        Console.WriteLine("[Too big] " + fsi.Name + " " + rate + "%");
                                        Console.ForegroundColor = ConsoleColor.Gray;
                                        fo.Delete();
                                    }
                                    else
                                    {
                                        try
                                        {
                                            totalInputLength += fi.Length;
                                            totalOutputLength += fo.Length;
                                            
                                            fi.Delete();
                                            var fo2 = new FileInfo(fi.Directory + @"\" + Path.GetFileNameWithoutExtension(fi.Name) + ".mp4");
                                            if (fo2.Exists) fo2.Delete();
                                            fo.MoveTo(fo2.FullName);
                                            totalFileCount++;
                                            if ((totalFileCount % 25) == 0)
                                            {
                                                Console.WriteLine("In " + totalInputLength.ToString("#,##0"));
                                                Console.WriteLine("Out " + totalOutputLength.ToString("#,##0"));
                                                Console.WriteLine("Compressed size " + 100 * totalOutputLength / totalInputLength + "%");
                                            }
                                            Console.WriteLine("[Success] " + fsi.Name + " " + rate + "%");
                                        }
                                        catch (Exception)
                                        {
                                            Console.ForegroundColor = ConsoleColor.Red;
                                            Console.WriteLine("* Failed to copy " + fsi.Name);
                                            Console.ForegroundColor = ConsoleColor.Gray;
                                        }
                                    }
                                }
                                else
                                {
                                    Console.WriteLine("[Failed] " + fsi.Name);
                                }
                                break;
                        }
                    }
                    var di = fsi as DirectoryInfo;
                    if (di != null)
                    {
                        ProcessDirectory(di);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("[Failed] " + fsi.Name + " " + ex.Message);
                }
            }
        }

        private static bool ProcessFile(FileInfo fi, out FileInfo fo)
        {
            fo = new FileInfo(fi.Directory + @"\" + Path.GetFileNameWithoutExtension(fi.Name) + " " + FFMPEG_SUFFIX + ".mp4");


            if (fo.Exists && fo.Length > (fi.Length / 100))
            {
                return true;
            }
            var program = "C:\\Program Files (x86)\\WinFF\\ffmpeg.exe";
            var input = "-y -i \"" + fi.FullName + "\"";
            var options = " -crf 25.0 -vcodec libx264 -acodec libfaac -ar 48000 -b:a 160k -coder 1 -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8 -me_method hex -subq 6 -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -b_strategy 1 -qcomp 0.6 -qmin 0 -qmax 69 -qdiff 4 -bf 3 -refs 8 -direct-pred 3 -trellis 2 -wpredp 2 -rc_lookahead 60 -threads 0";
            var output = " \"" + fo.FullName + "\"";

            var args = input + options + output;
            var psi = new ProcessStartInfo(program, args);
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;
            var p = Process.Start(psi);
            DisplayOutput(p.StandardError,false);
            DisplayOutput(p.StandardOutput,true);
            p.WaitForExit();
            ClearCurrentConsoleLine();
            fo.Refresh();
            return (p.ExitCode == 0);
        }
        private static void DisplayOutput(StreamReader sr, bool error)
        {
            Task.Run(() =>
            {
                string line;
                while ((line = sr.ReadLine()) != null)
                {
                    if (error)
                    {
                        ClearCurrentConsoleLine();
                        Console.WriteLine(line);
                    }
                    else
                    {
                        if (line.Length > 78) line = line.Substring(0, 78);
                        Console.Write(line + "\r");
                    }
                }
            });
        }
        public static void ClearCurrentConsoleLine()
        {
            int currentLineCursor = Console.CursorTop;
            Console.SetCursorPosition(0, Console.CursorTop);
            for (int i = 0; i < Console.WindowWidth; i++)
                Console.Write(" ");
            if (currentLineCursor == Console.CursorTop)
            {
                //we should not be here, we have scrolled
                Console.SetCursorPosition(0, currentLineCursor - 1);
            }
            else Console.SetCursorPosition(0, currentLineCursor);
        }
    }
}

This will compress with the same parameters that 'winff' MPEG-4 very high qualiy preset. 

The output look like this:  

=== Z:\Users\Public\Pictures\2011\2011-01-23 Galette des rois ===
[Success] Sesame Tree 009.MOV 9%
[Success] Sesame Tree 010.MOV 11%
[Success] Sesame Tree 035.MOV 11%
[Success] Sesame Tree 036.MOV 10%
[Success] Sesame Tree 037.MOV 10%
[Success] Sesame Tree 038.MOV 10%
[Success] Sesame Tree 039.MOV 10%
[Success] Sesame Tree 040.MOV 11%
[Success] Sesame Tree 041.MOV 9%
[Success] Sesame Tree 042.MOV 11%
=== Z:\Users\Public\Pictures\2011\2011-01-24 Sesame Tree ===
[Success] Sesame Tree 009 20020215.MOV 9%
[Success] Sesame Tree 035 59518234.MOV 11%
[Success] Sesame Tree 036 38066230.MOV 10%
[Success] Sesame Tree 037 541614982.MOV 10%
[Success] Sesame Tree 038 65036214.MOV 10%
[Success] Sesame Tree 039 514636214.MOV 10%
[Success] Sesame Tree 040 92674886.MOV 11%
[Success] Sesame Tree 041 22075926.MOV 9%
[Success] Sesame Tree 042 29313510.MOV 11%
[Success] Ballet 041.MOV 8%s\2011\2011-01-30 Celine ===
[Success] Ballet 042.MOV 20%
[Success] Ballet 051.MOV 20%
=== Z:\Users\Public\Pictures\2011\2011-02-05 Pingoins au Zoo ===
[Success] Ballet 010.MOV 12%
[Success] Ballet 020.MOV 11%
[Success] Ballet 021.MOV 9%
[Success] Ballet 022.MOV 9%
[Success] Ballet 023.MOV 7%
=== Z:\Users\Public\Pictures\2011\2011-04-08 Ballet ===
[Success] Ballet 061.MOV 10%
[Success] Ballet 064.MOV 9%
frame= 1133 fps= 42 q=31.0 size=    6037kB time=00:00:36.03 bitrate=1372.6kbit 

Points of Interest

The program uses FFMPEG behind to do the compression.

The bitrate is not fixed, but quality based. Some slow moving videos wil have a slow bitrate of 500kbit/s or less while other might have 2000kbit/s or more. The quality will always be very good though. 

History 

This is just the story of my program. 

It might be useful to you. 

You might want to take it further, please let me know if you do.  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)