zoukankan      html  css  js  c++  java
  • 二、实践与视频解决方案

    一、视频解决方案

    说明:

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

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

    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引用。

     

     

  • 相关阅读:
    nginx 安装部署
    cordova 安装使用
    git 设置和取消代理
    SQL语句 合并列值 将一列的多个值合并成一行
    idea 注册码 地址:
    mac 安装redis
    redisTemplate 方法
    js 点击 隐藏弹出层
    windows 安装redis
    docker 安装redis
  • 原文地址:https://www.cnblogs.com/fger/p/11387491.html
Copyright © 2011-2022 走看看