章节前言

        1.0 版,弄出来后,便迫不及待的和身边的宝妈分享,这下好了反馈了问题一推,例如通过配置文件填写路径、视频图片要自己截图,打开软件加载时没有加载过程等等,甚至软件名称怎么不是自己宝宝的?

        不要太高估你的客户,他们真的不会这些。那只好优化一下了,视频图片改为自动截取,为了美观需要剪裁一下

视频截图

        万事揭百度,这些方法都来自于园里其他博主写的,Ffmpeg 是个不错的视频处理组件,功能强大,教程也多,但就是 ffmpeg.exe 软件有点大,相比 MediaToolkit 没太多教程,好处就是dll 要小很多,我使用这俩个都实现视频截图的方法,不过最终用 MediaToolkit ,毕竟软件打包要小点好。

Ffmpeg

        前提要到官网下载 ffmpeg.exe 放置执行文件夹下

    /// <summary>
    /// Ffmpeg 命令行的方式提取
    /// </summary>
    public static class FfmpegHelper
    {
        private static System.Diagnostics.ProcessStartInfo cmdFfmpeg;
        private static System.Diagnostics.ProcessStartInfo cmdFfprobe;
        static FfmpegHelper()
        {

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                string ffmpegPath = "/usr/local/ffmpeg/ffmpeg";
                string ffprobePath = "/usr/local/ffmpeg/ffprobe";
                cmdFfmpeg = new System.Diagnostics.ProcessStartInfo(ffmpegPath);
                cmdFfprobe = new System.Diagnostics.ProcessStartInfo(ffprobePath);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                string ffmpegPath = AppDomain.CurrentDomain.BaseDirectory + "ffmpeg\\ffmpeg.exe";
                string ffprobePath = AppDomain.CurrentDomain.BaseDirectory + "ffmpeg\\ffprobe.exe";
                cmdFfmpeg = new System.Diagnostics.ProcessStartInfo(ffmpegPath);
                cmdFfprobe = new System.Diagnostics.ProcessStartInfo(ffprobePath);

            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                string ffmpegPath = "/usr/local/ffmpeg/ffmpeg";
                string ffprobePath = "/usr/local/ffmpeg/ffprobe";
                cmdFfmpeg = new System.Diagnostics.ProcessStartInfo(ffmpegPath);
                cmdFfprobe = new System.Diagnostics.ProcessStartInfo(ffprobePath);
            }

            cmdFfmpeg.RedirectStandardError = false; // 输出错误
            cmdFfmpeg.RedirectStandardOutput = true; //输出打印
            cmdFfmpeg.UseShellExecute = false; //使用Shell
            cmdFfmpeg.CreateNoWindow = true;  //创建黑窗

            cmdFfprobe.RedirectStandardError = false; //set false
            cmdFfprobe.RedirectStandardOutput = true;
            cmdFfprobe.UseShellExecute = false; //set true
            cmdFfprobe.CreateNoWindow = true;  //don't need the black window
        }

        /// <summary>
        /// 获取视频信息
        /// </summary>
        /// <param name="path"></param>
        public static async Task<string> GetVideoInfo(string path)
        {
            string command = $"-i {path} -print_format json -show_format -show_streams -show_data";
            cmdFfprobe.Arguments = command;

            System.Diagnostics.Process cmd = new System.Diagnostics.Process();
            cmd.StartInfo = cmdFfprobe;
            cmd.Start();

            string InfoStr = await cmd.StandardOutput.ReadToEndAsync();
            cmd.WaitForExit();
            return InfoStr;
        }

        /// <summary>
        /// 视频截图
        /// </summary>
        /// <param name="path">视频地址</param>
        /// <param name="outPath">图片地址</param>
        /// <param name="m">第几秒处</param>
        public static void VideoScreenshot(string path, string outPath, int m = 5)
        {
            string command = $"-i \"{path}\" -ss " + m + " -y -q:v 7 -f image2 -t 0.001 \"{outPath}\"";
            cmdFfmpeg.Arguments = command;

            System.Diagnostics.Process cmd = new System.Diagnostics.Process();
            cmd.StartInfo = cmdFfmpeg;
            cmd.Start();
            cmd.WaitForExit();
        }
    }
MediaToolkit

        到NuGet去查找应用

public class VideoSnapshot
    {
        /// <summary>
        /// 视频截图
        /// </summary>
        /// <param name="videoFilePath">视频文件</param>
        /// <param name="outputImagePath">存放路径</param>
        /// <param name="m">截取第?秒的视频帧</param>
        public static void ExtractSnapshot(string videoFilePath, string outputImagePath, int m = 5)
        {
            TimeSpan position = TimeSpan.FromSeconds(m);

            try
            {
                if (!File.Exists(videoFilePath))
                {
                    Console.WriteLine("文件不存在!!!");
                    return;
                }
                // 创建 MediaFile 对象,指定输入和输出文件路径
                var inputFile = new MediaFile { Filename = videoFilePath };
                var outputFile = new MediaFile { Filename = outputImagePath };

                // 创建 ConversionOptions 对象,并设置截图的时间点
                var conversionOptions = new ConversionOptions { Seek = position };

                // 使用Engine类来处理视频截图
                using (var engine = new Engine())
                {
                    // 获取视频文件的元数据信息
                    engine.GetMetadata(inputFile);

                    // 截取指定时间点的视频帧,并保存为图像文件
                    engine.GetThumbnail(inputFile, outputFile, conversionOptions);
                }

                Console.WriteLine($"视频截图成功:{outputImagePath}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"视频截图失败:{ex.Message}");
            }
        }


        /*
            engine.GetThumbnail(inputFile, outputFile, position);
            engine 是一个 Engine 类型的对象,它是 MediaToolkit 库中的核心引擎,用于处理多媒体文件。
            GetThumbnail 是 Engine 类的一个方法,用于从指定的多媒体文件中获取缩略图或截图。
            inputFile 是 MediaFile 类型的对象,表示输入的多媒体文件。
            outputFile 是 MediaFile 类型的对象,表示输出的缩略图或截图的文件。
            position 是一个 TimeSpan 类型的参数,表示截图的时间点。
         */
    }
缩略图、裁剪

        视频截图后,为了美观需要对图片进行裁剪

/// <summary>
    /// 缩略图
    /// </summary>
    public class ThumbnailMaker
    {

        /// <summary>
        /// 制作图片的缩略图
        /// </summary>
        /// <param name="originalImage">原图</param>
        /// <param name="width">指定的宽度</param>
        /// <param name="height">指定的高度</param>
        /// <param name="mode">指定的模式</param>
        /// <remarks>
        ///     <paramref name="mode">
        ///         <para>ThumbnailMode.UsrHeightWidth:指定高宽缩放(可能变形)</para>
        ///         <para>ThumbnailMode.UsrHeightWidthBound:指定高宽缩放(可能变形,过小则不变)</para>
        ///         <para>ThumbnailMode.UsrWidth:指定宽,高按比例</para>
        ///         <para>ThumbnailMode.UsrWidthBound:指定宽(过小则不变),高按比例</para>
        ///         <para>ThumbnailMode.UsrHeight:指定高,宽按比例</para>
        ///         <para>ThumbnailMode.UsrHeightBound:指定高(过小则不变,宽按比例)</para>
        ///     </paramref>
        /// </remarks>
        /// <returns></retCut,urns>
        public static Image MakeThumbnail(Image originalImage, int width, int height, ThumbnailMode mode)
        {
            int towidth = width;
            int toheight = height;

            int x = 0;
            int y = 0;
            int initWidth = originalImage.Width;
            int initHeight = originalImage.Height;


            switch (mode)
            {
                case ThumbnailMode.UsrHeightWidth: //指定高宽缩放(可能变形)
                    break;
                case ThumbnailMode.UsrHeightWidthBound: //指定高宽缩放(可能变形)(过小则不变)
                    if (originalImage.Width <= width && originalImage.Height <= height)
                    {
                        return originalImage;
                    }
                    if (originalImage.Width < width)
                    {
                        towidth = originalImage.Width;
                    }
                    if (originalImage.Height < height)
                    {
                        toheight = originalImage.Height;
                    }
                    break;
                case ThumbnailMode.UsrWidth: //指定宽,高按比例
                    toheight = originalImage.Height * width / originalImage.Width;
                    break;
                case ThumbnailMode.UsrWidthBound: //指定宽(过小则不变),高按比例
                    if (originalImage.Width <= width)
                    {
                        return originalImage;
                    }
                    else
                    {
                        toheight = originalImage.Height * width / originalImage.Width;
                    }
                    break;
                case ThumbnailMode.UsrHeight: //指定高,宽按比例
                    towidth = originalImage.Width * height / originalImage.Height;
                    break;
                case ThumbnailMode.UsrHeightBound: //指定高(过小则不变),宽按比例
                    if (originalImage.Height <= height)
                    {
                        return originalImage;
                    }
                    else
                    {
                        towidth = originalImage.Width * height / originalImage.Height;
                    }
                    break;
                case ThumbnailMode.Cut: //指定高宽裁减(不变形)
                    //计算宽高比
                    double srcScale = (double)originalImage.Width / (double)originalImage.Height;
                    double destScale = (double)towidth / (double)toheight;
                    //宽高比相同
                    if (srcScale - destScale >= 0 && srcScale - destScale <= 0.001)
                    {
                        x = 0;
                        y = 0;
                        initWidth = originalImage.Width;
                        initHeight = originalImage.Height;
                    }
                    //源宽高比大于目标宽高比
                    //(源的宽比目标的宽大)
                    else if (srcScale > destScale)
                    {
                        initWidth = originalImage.Height * towidth / toheight;
                        initHeight = originalImage.Height;
                        x = (originalImage.Width - initWidth) / 2;
                        y = 0;
                    }
                    //源宽高比小于目标宽高小,源的高度大于目标的高度
                    else
                    {
                        initWidth = originalImage.Width;
                        initHeight = originalImage.Width * height / towidth;
                        x = 0;
                        y = (originalImage.Height - initHeight) / 2;
                    }
                    break;
                default:
                    break;
            }
            Image bitmap = new Bitmap(towidth, toheight);
            //新建一个画板
            using (Graphics g = Graphics.FromImage(bitmap))
            {
                //设置高质量插值法
                g.CompositingQuality = CompositingQuality.HighQuality;
                //设置高质量,低速段呈现的平滑程度
                g.SmoothingMode = SmoothingMode.HighQuality;

                //在指定的位置上,并按指定大小绘制原图片的指定部分
                g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight), new Rectangle(x, y, initWidth, initHeight), GraphicsUnit.Pixel);
            }
            return bitmap;
        }

        public static Image MakeThumbnail(Stream originalStream, int width, int height, ThumbnailMode mode)
        {
            Image originalImage = Image.FromStream(originalStream);
            try
            {
                return MakeThumbnail(originalImage, width, height, mode);
            }
            catch (Exception)
            {
                originalStream.Dispose();
                return null;
            }
        }

        public static void MakeThumbnail(Image originalImage, string savePath, int width, int height, ThumbnailMode mode)
        {
            Image image = MakeThumbnail(originalImage, width, height, mode);
            try
            {
                image.Save(savePath);
            }
            catch (Exception)
            {
                image.Dispose();
            }
            finally { image.Dispose(); }
        }

        public static void MakeThumbnail(string originalImagePath, string savePath, int width, int height, ThumbnailMode mode)
        {
            Image image = Image.FromFile(originalImagePath);
            try
            {
                MakeThumbnail(image, savePath, width, height, mode);
            }
            catch (Exception)
            {
                image.Dispose();
            }
            finally
            {

                image.Dispose();

            }
        }

    }


    public enum ThumbnailMode
    {
        /// <summary>
        /// 宽高缩放模式,可能变形
        /// </summary>
        UsrHeightWidth,
        UsrHeightWidthBound,
        /// <summary>
        /// 指定宽度,高按比例
        /// </summary>
        UsrWidth,
        /// <summary>
        /// 指定宽(过小则不变),高按比例
        /// </summary>
        UsrWidthBound,
        /// <summary>
        /// 自定高度,宽按比例
        /// </summary>
        UsrHeight,
        /// <summary>
        /// 指定高(过小则不变),宽按比例
        /// </summary>
        UsrHeightBound,
        /// <summary>
        /// 剪切
        /// </summary>
        Cut,
        NONE,
    }
使用方法

        项目中在遍历完文件夹后,便对没有图片的文件夹进行视频截图

        /// <summary>
        /// 创建缩略图
        /// </summary>
        void CreateThumbnails()
        {

            foreach (var item in VideoList)
            {
                //检测没有图片
                if (item.PicturePath == @"pack://application:,,,/YiZaiPlayer;component/Resource/null.png")
                {
                    string pathTemp = item.FolderPath + "\\temp.jpg"; //临时

                    string pathPreview = item.FolderPath + "\\preview.jpg"; //预览图

                    string[] _fileVideos = Utils.GitFileVideos(item.FolderPath);

                    //Common.FfmpegHelper.VideoScreenshot(_fileVideos[0], pathTemp); //临时预览图生成,方法一

                    Common.VideoSnapshot.ExtractSnapshot(_fileVideos[0], pathTemp); //临时预览图生成,方法二

                    if (File.Exists(pathTemp))
                    {
                        Common.ThumbnailMaker.MakeThumbnail(pathTemp, pathPreview, 200, 200, Common.ThumbnailMode.Cut);

                        item.PicturePath = pathPreview;

                        File.Delete(pathTemp);
                    }
                }
            }
        }

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐