二、实践与视频解决方案

一、视频解决方案

说明:

公司下户拍摄视频,上传存储一直用的优酷云(视频压缩、解码、播放)当然还支持水印。

现在场景,我们公司内部买服务器,下户拍摄视频上传到我们自己服务内,需要解决的问题,下户拍摄视频很大,需要解决的问题:

1、(下户视频过大)需要压缩处理、

2、(视频格式、播放帧处理)解码格式

3、(提供url)提供接口让内部人员可以播放

 解决方案1、亲测

使用官网:ffmpeg 、GitHub:https://github.com/FFmpeg/FFmpeg

 

 

在下载页面上,我们可以看到,对于32位和64位版本,分别提供了三种不同的模式:static、shared和dev

  • static: 该版本提供了静态版本的FFmpeg工具,将依赖的库生成在了最终的可执行文件中;作为工具而言此版本就可以满足我们的需求;
  • share: 该版本的工具包括可执行文件和dll,程序运行过程必须依赖于提供的dll文件;
  • dev: 提供了库的头文件和dll的引导库;

 

这里下载的是static版本,将其下载解压到项目目录下:

C#代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ffmpeg
{
    class Program
    {
        static string FFmpegPath = @"C:UsersBrucesource
eposffmpegffmpeginDebugffmpeg-20190820-74e6800-win64-staticinffmpeg.exe";
        static void Main(string[] args)
        {
            string videoUrl = @"D:videoWildlife.wmv";
            string targetUrl = @"D:video
ewFile.mp4";

            //视频转码
            string para = string.Format("-i {0} -b 1024k -acodec copy -f mp4 {1}", videoUrl, targetUrl);
            RunMyProcess(para);
            Console.WriteLine("完成!");
            Console.ReadKey();

        }
        static void RunMyProcess(string Parameters)
        {
            var p = new Process();
            p.StartInfo.FileName = FFmpegPath;
            p.StartInfo.Arguments = Parameters;
            //是否使用操作系统shell启动
            p.StartInfo.UseShellExecute = false;
            //不显示程序窗口
            p.StartInfo.CreateNoWindow = true;
            p.Start();
            Console.WriteLine("
开始转码...
");
            p.WaitForExit();
            p.Close();
            p.Dispose();//释放资源
        }

    }
}

引入exe文件

但是由于ffmpeg视频转码,转码完成后程序一直卡死且一直未收到exit消息,

原因是ffmpeg是StandardError流,要读取StandardError而不是StandardOutput。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ffmpeg
{
    class Program
    {
        static string FFmpegPath = @"C:UsersBrucesource
eposffmpegffmpeginDebugffmpeg-20190820-74e6800-win64-staticinffmpeg.exe";
        static void Main(string[] args)
        {
            string videoUrl = @"D:videoWildlife.mp4";
            string targetUrl = @"D:video
ewFile2.mp4";
            //视频转码
            string para = string.Format("-i {0} -r 10 -b:a 32k {1}", videoUrl, targetUrl);
            RunMyProcess(para);
            Console.WriteLine("转码结束时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "
完成!");
            Console.ReadKey();
        }
        static void RunMyProcess(string Parameters)
        {
            var p = new Process();
            p.StartInfo.FileName = FFmpegPath;
            p.StartInfo.Arguments = Parameters;
            //是否使用操作系统shell启动
            p.StartInfo.UseShellExecute = false;
            //不显示程序窗口
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.RedirectStandardError = true;//重定向标准错误输出
            p.ErrorDataReceived += new DataReceivedEventHandler(Output);//外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
            p.Start();
            p.BeginErrorReadLine();//开始异步读取
            Console.WriteLine("转码开始时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "
开始转码...
");
            p.WaitForExit();//阻塞等待进程结束
            p.Close();//关闭进程
            p.Dispose();//释放资源
        }
        private static void Output(object sendProcess, DataReceivedEventArgs output)
        {
            if (!String.IsNullOrEmpty(output.Data))
            {
                //处理方法...
                Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss")+":" +output.Data);
            }
        }

    }
}

2、视频大小信息

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ffmpeg
{
    class Program
    {
        static string FFmpegPath = @"C:UsersBrucesource
eposffmpegffmpeginDebugffmpeg-20190820-74e6800-win64-staticinffmpeg.exe";
        static void Main(string[] args)
        {
            string videoUrl = @"D:videoWildlife.MP4";
            //视频转码
            int time = GetVideoDuration(FFmpegPath, videoUrl);
            Console.WriteLine("视频时间:" + time + "");
            Console.ReadKey();
        }

        private static int GetVideoDuration(string ffmpegfile, string sourceFile)
        {
            try
            {
                using (System.Diagnostics.Process ffmpeg = new System.Diagnostics.Process())
                {
                    String duration;  // soon will hold our video‘s duration in the form "HH:MM:SS.UU"  
                    String result;  // temp variable holding a string representation of our video‘s duration  
                    StreamReader errorreader;  // StringWriter to hold output from ffmpeg  
                    // we want to execute the process without opening a shell  
                    ffmpeg.StartInfo.UseShellExecute = false;
                    //ffmpeg.StartInfo.ErrorDialog = false;  
                    ffmpeg.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
                    // redirect StandardError so we can parse it  
                    // for some reason the output comes through over StandardError  
                    ffmpeg.StartInfo.RedirectStandardError = true;
                    // set the file name of our process, including the full path  
                    // (as well as quotes, as if you were calling it from the command-line)  
                    ffmpeg.StartInfo.FileName = ffmpegfile;
                    // set the command-line arguments of our process, including full paths of any files  
                    // (as well as quotes, as if you were passing these arguments on the command-line)  
                    ffmpeg.StartInfo.Arguments = "-i " + sourceFile;
                    // start the process  
                    ffmpeg.Start();
                    // now that the process is started, we can redirect output to the StreamReader we defined  
                    errorreader = ffmpeg.StandardError;
                    // wait until ffmpeg comes back  
                    ffmpeg.WaitForExit();
                    // read the output from ffmpeg, which for some reason is found in Process.StandardError  
                    result = errorreader.ReadToEnd();
                    // a little convoluded, this string manipulation...  
                    // working from the inside out, it:  
                    // takes a substring of result, starting from the end of the "Duration: " label contained within,  
                    // (execute "ffmpeg.exe -i somevideofile" on the command-line to verify for yourself that it is there)  
                    // and going the full length of the timestamp  
                    duration = result.Substring(result.IndexOf("Duration: ") + ("Duration: ").Length, ("00:00:00").Length);
                    string[] ss = duration.Split(':');
                    int h = int.Parse(ss[0]);
                    int m = int.Parse(ss[1]);
                    int s = int.Parse(ss[2]);
                    return h * 3600 + m * 60 + s;
                }
            }
            catch (System.Exception ex)
            {
                return 60;
            }
        }
    }
}

 

3、视频进度百分比

 

 

 

 代码  转码进度公式=当前时间/总时间*100

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ffmpeg
{
    class Program
    {
        static void Main(string[] args)
        {
            FFmpeg.Execute();
            Console.ReadKey();
        }
    }
    public class FFmpeg
    {
        static int timeCount;
        public static void Execute()
        {
            string ffmpegPath = @"C:UsersBrucesource
eposffmpegffmpeginDebugffmpeg-20190820-74e6800-win64-staticinffmpeg.exe";
            Process p = new Process();
            p.StartInfo.FileName = ffmpegPath;
            p.StartInfo.UseShellExecute = false;
            string videoUrl = @"D:video\_Wildlife.mp4";
            string targetUrl = @"D:video
ewFile15.mp4";
            //视频转码
            string para = string.Format("-i {0} -r 10 -b:a 32k {1}", videoUrl, targetUrl);
            p.StartInfo.Arguments = para;  //执行参数
            //-i D:/vts-collector/test/swap/7a3c9800-2e2f-42fe-ba39-56b25f972e06.mov -y -ab 56 -ar 22050 -b 800 -r 29.97 -s 420x340 D:/vts-collector/test/swap/7a3c9800-2e2f-42fe-ba39-56b25f972e06_mov_stream.mp4
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;//把外部程序错误输出写到StandardError流中
            p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);
            p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
            using (p)
            {
                p.Start();
                p.BeginErrorReadLine();//开始异步读取
                p.WaitForExit();//阻塞等待进程结束
                p.Close();//关闭进程
            }
        }

        private static void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            var output = e.Data;
            if (output != null)
            {
                if (output.Contains("Duration"))
                {
                    Int32 indexOfDuration = output.IndexOf("Duration");
                    Int32 indexOfBegin = output.IndexOf(":", indexOfDuration);
                    Int32 indexOfEnd = output.IndexOf(",", indexOfBegin);
                    var duration = output.Substring(indexOfBegin + 1, indexOfEnd - indexOfBegin - 1);
                    timeCount = ConvertStringtoSecond(duration);
                }
                if (output.Contains("time="))
                {
                    Int32 indexOfTime = output.IndexOf("time=");
                    Int32 indexOfBegin = output.IndexOf("=", indexOfTime);
                    Int32 indexOfEnd = output.IndexOf("bitrate", indexOfBegin);
                    string timeStr = output.Substring(indexOfBegin + 1, indexOfEnd - indexOfBegin - 1);
                    //
                    var time = Convert.ToDouble(ConvertStringtoSecond(timeStr));
                    //
                    var process = time / timeCount * 100;  //当前毫秒单位
                    process = Math.Round(process);
                    Console.Write("{0}% ", process);
                }
                //Console.WriteLine(e.Data);
            }
        }

        private static int ConvertStringtoSecond(string timeStr)
        {
            int totalSecond = 0;
            try
            {
                DateTime dateTime = Convert.ToDateTime(timeStr);
                totalSecond = dateTime.Hour * 3600000 + dateTime.Minute * 60000 + dateTime.Second * 1000 + dateTime.Millisecond;
            }
            catch (System.Exception ex)
            {
                Console.Write(ex.Message);
            }
            return totalSecond;
        }

        private static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            Console.WriteLine(e.Data);
        }
    }

}

 

4、视频转码其他指令

string para = string.Format("-i {0} -b:v 400k -s 1920*1080 {1}", videoUrl, targetUrl);   //模糊不清楚,1920*1080 完全行不通 很模糊
string para = string.Format("-i {0}  {1}", videoUrl, targetUrl);   //3.67G转换后1.06G  简单压缩方式

截图

ffmpeg.exe -i D:videoWildlife.MP4 -ss 5 -vframes 1 D:videoimg.jpg  //截图

把视频的前30帧转换成一个Animated Gif

ffmpeg.exe -i D:video\_Wildlife.MP4 -vframes 30 -y -f gif D:video.gif

 水印

//加水印:
ffmpeg -i D:video
ewFile2.mp4 -i D:videoimg.jpg -filter_complex overlay D:video\__Wildlife.MP4

 参数

说明:LogoName为图片名,overlay=100:100意义为overlay=x:y,在(x,y)坐标处開始加入水印。

         左上角:overlay=10:10 

         右上角:overlay=main_w-overlay_w-10:10

         左下角:overlay=10:main_h-overlay_h-10 

         右下角:overlay=main_w-overlay_w-10:main_h-overlay_h-10

//gif图到左下角视频中,经常看视频,类似那些视频广告gif 
ffmpeg -y -i D:video
ewFile2.mp4 -ignore_loop 0 -i D:video	img.gif  -filter_complex overlay=0:H-h D:video\__Wildlife.MP4

4、论坛说明

这里下载的是dev版本,将其下载解压到项目目录下:

不是很方便调用,就采用 

FFmpeg.AutoGen

 一、项目搭建nuget添加ffmpeg.autogen引用。

 

 

原文地址:https://www.cnblogs.com/fger/p/11387491.html