65.9K
CodeProject 正在变化。 阅读更多。
Home

压缩我的视频

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2012年10月30日

CPOL

1分钟阅读

viewsIcon

29074

一个非常简单的程序,用于就地压缩视频。

介绍  

下面的代码片段压缩了我的个人视频。

背景 

我的家用硬盘上有大量的个人视频。不幸的是,这些视频直接来自照相机,压缩率很低。

首先,我使用一个程序来删除重复文件:http://www.nirsoft.net/utils/search_my_files.html

然后,我尝试了一些工具来压缩它们。我找到的最佳工具是 winff。

不幸的是,它需要一个输出文件夹,并且不允许我在视频所在的文件夹中直接压缩它们。它也不能压缩文件路径中包含重音字母的文件。

当你有几百个视频时,这就会成为一个真正的问题。

使用代码

然后,由于我找不到一个可以就地压缩文件的工具,我编写了下面的程序。

虽然这个程序对我有效,但它是针对我自己的问题定制的,可能需要修改才能适用于你的场景。

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);
        }
    }
}

它将使用与 'winff' MPEG-4 非常高质量预设相同的参数进行压缩。

输出结果如下:

=== 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 

关注点

该程序在后台使用 FFMPEG 进行压缩。

比特率不是固定的,而是基于质量的。一些缓慢移动的视频比特率可能低至 500kbit/s 或更低,而其他视频可能高达 2000kbit/s 或更高。但质量始终非常好。

历史 

这只是我的程序的故事。

它可能对你有所帮助。

如果你对其进行进一步开发,请告诉我。

© . All rights reserved.