一、视频解决方案
说明:
公司下户拍摄视频,上传存储一直用的优酷云(视频压缩、解码、播放)当然还支持水印。
现在场景,我们公司内部买服务器,下户拍摄视频上传到我们自己服务内,需要解决的问题,下户拍摄视频很大,需要解决的问题:
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:\Users\Bruce\source\repos\ffmpeg\ffmpeg\bin\Debug\ffmpeg-20190820-74e6800-win64-static\bin\ffmpeg.exe";static void Main(string[] args){string videoUrl = @"D:\video\Wildlife.wmv";string targetUrl = @"D:\video\newFile.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("\n开始转码...\n");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:\Users\Bruce\source\repos\ffmpeg\ffmpeg\bin\Debug\ffmpeg-20190820-74e6800-win64-static\bin\ffmpeg.exe";static void Main(string[] args){string videoUrl = @"D:\video\Wildlife.mp4";string targetUrl = @"D:\video\newFile2.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") + "\n完成!");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") + "\n开始转码...\n");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:\Users\Bruce\source\repos\ffmpeg\ffmpeg\bin\Debug\ffmpeg-20190820-74e6800-win64-static\bin\ffmpeg.exe";static void Main(string[] args){string videoUrl = @"D:\video\Wildlife.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:\Users\Bruce\source\repos\ffmpeg\ffmpeg\bin\Debug\ffmpeg-20190820-74e6800-win64-static\bin\ffmpeg.exe";Process p = new Process();p.StartInfo.FileName = ffmpegPath;p.StartInfo.UseShellExecute = false;string videoUrl = @"D:\video\_Wildlife.mp4";string targetUrl = @"D:\video\newFile15.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.mp4p.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:\video\Wildlife.MP4 -ss 5 -vframes 1 D:\video\img.jpg //截图

把视频的前30帧转换成一个Animated Gif
ffmpeg.exe -i D:\video\_Wildlife.MP4 -vframes 30 -y -f gif D:\video\.gif 
水印
//加水印: ffmpeg -i D:\video\newFile2.mp4 -i D:\video\img.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\newFile2.mp4 -ignore_loop 0 -i D:\video\timg.gif -filter_complex overlay=0:H-h D:\video\__Wildlife.MP4
4、论坛说明
http://bbs.chinaffmpeg.com
这里下载的是dev版本,将其下载解压到项目目录下:
不是很方便调用,就采用
FFmpeg.AutoGen

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


















