在C#中获取视频文件的缩略图图片

42

对于现在遇到此问题的任何人。 上面的答案对我没有用,所以这是我的解决方案: 关于这个问题,我进行了详细的回答。 - Mohammad Subhan
5个回答

65

FFMpeg是一个可以用来在某个位置提取视频帧的好工具。 你可以像上面提到的那样调用ffmpeg.exe,或者只需使用现有的.NET包装器(例如.NET视频转换器(它是免费的)),只需要一行代码即可获得缩略图:


var ffMpeg = new NReco.VideoConverter.FFMpegConverter();
ffMpeg.GetVideoThumbnail(pathToVideoFile, thumbJpegStream,5);

1
我该如何使用它?我已经通过NuGet添加了“Install-Package NReco.Application.Web”,但无法使用那些代码行。 - gsiradze
3
你安装了错误的包。正确的是“Install-Package NReco.VideoConverter”(NReco.Application.Web是关于ASP.NET应用程序的NReco框架)。 - Vitaliy Fedorchenko
1
为了以后参考:性能非常好,从一个3.5 GB的视频文件中提取了1954个缩略图,在一台不错的机器上只花费了约5分钟。 - dvdmn
2
它能用于获取特定尺寸的缩略图吗?因为我得到的图像是默认尺寸,即426x240,而我需要720x360。 - sohaib javed
2
@AkshayHazari 你可以使用ConvertMedia方法将其转换为1帧MJPEG输出(这就是GetVideoThumbnail实际执行的操作)。不过,向GetVideoThumbnail添加额外选项是个好主意,感谢你注意到了这一点。 - Vitaliy Fedorchenko
显示剩余12条评论

19

您可以编写代码来执行FFmpeg,生成缩略图文件。然后打开该文件,以便根据需要进行使用。

这是一些示例代码:

public static Bitmap GetThumbnail(string video, string thumbnail)
{
    var cmd = "ffmpeg  -itsoffset -1  -i " + '"' + video + '"' + " -vcodec mjpeg -vframes 1 -an -f rawvideo -s 320x240 " + '"' + thumbnail + '"';

    var startInfo = new ProcessStartInfo
    {
        WindowStyle = ProcessWindowStyle.Hidden,
        FileName = "cmd.exe",
        Arguments = "/C " + cmd
    };

    var process = new Process
    {
        StartInfo = startInfo
    };

    process.Start();
    process.WaitForExit(5000);

    return LoadImage(thumbnail);
}

static Bitmap LoadImage(string path)
{
    var ms = new MemoryStream(File.ReadAllBytes(path));
    return (Bitmap)Image.FromStream(ms);
}

2
Why WaitForExit(5000) ? - Parimal Raj
@PaRiMaLRaJ,你试过这段代码吗?你在参数“thumbnail”中传递了什么?我甚至创建了一个扩展名为.bmp的文件,并将其传递给缩略图(C:\Users\Public\Videos\Sample Videos\test1.bmp),但它在代码的最后一行给出了一个错误=“参数无效”。你能帮帮我吗?谢谢您的帮助。 - CodeEngine
这个程序在服务器/远程虚拟机上部署的应用中能运行吗?我计划为用户上传到我的网站上的视频生成缩略图。位图应该即时生成。由于这将在服务器上运行,所以这种方法是否可行?虽然在本地机器上完全可以运行。 - Aditya
如果您从不受信任的来源(例如通过网络或来自ASP表单)获取“video”或“thumbnail”参数,则攻击者可能利用此代码在您的系统上执行代码。您应该至少通过Path.GetFullPath()运行这两个参数,然后将每个规范路径与已知安全基本路径进行比较。如果您想拒绝UNC路径(例如\\evilserver\foo),则还应使用Uri.IsUnc - Polynomial
对于那些好奇为什么要使用WaitForExit(5000)的人,也许这个问答可以帮到你。基本上它可以帮助防止死锁。 - Weihui Guo

15

对于不想在商业软件中使用FFMpeg的人,我有一个旧解决方案在这里:

ShellFile shellFile = ShellFile.FromFilePath(VideoFileName);
Bitmap bm = shellFile.Thumbnail.Bitmap;

然后您将获得一个可以用于绘图的位图对象。如果您想要一个文件,只需执行以下操作:

bm.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);

如果您想在Xaml绑定中使用一个BitmapImage,请将Bitmap转换为BitmapImage。以下是一个例子:

如果您想在Xaml绑定中使用一个BitmapImage,请将Bitmap转换为BitmapImage。以下是一个例子:

public static BitmapImage ConvertBitmapToBitmapImage(Bitmap bitmap)
        {
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            ms.Seek(0, SeekOrigin.Begin);
            image.StreamSource = ms;
            image.EndInit();

            return image;
        }

6
这种方法与FFMpeg解决方案不同。您需要安装一个Nuget包(WindowsAPICodePack-Shell)才能使用它。 - Mr. TA
BitmapImage 是属于哪个命名空间/包的? - Chris
1
@Chris,BitmapImage来自System.Windows.Media.Imaging命名空间。请查看此答案以获取更多信息 - LiamJM

14

Xabe.FFmpeg - 免费(仅限非商业用途),开源且跨平台的库。提供流畅的 FFmpeg API。在 Xabe.F 中生成视频缩略图。

    string output = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + FileExtensions.Png);
    IConversionResult result = await Conversion.Snapshot(Resources.Mp4WithAudio, output, TimeSpan.FromSeconds(0))
                                               .Start();

需要像其他答案中一样使用FFmpeg可执行文件,但你可以通过下载来获取它

    FFmpeg.GetLatestVersion();

完整文档在此处 - Xabe.FFmpeg 文档


2
只是留个注,这个库可以免费用于非商业用途。 - Juan Carlos

-2
 [HttpPost]
        [Route("UploadImages")]
        public HttpResponseMessage Post()
        {
            HttpResponseMessage response = new HttpResponseMessage();
            var httpRequest = HttpContext.Current.Request;
            if (httpRequest.Files.Count > 0)
            {
                var docfiles = new List<string>();
                foreach (string file in httpRequest.Files)
                {
                    var postedFile = httpRequest.Files[file];
                    var filePath1 = HttpContext.Current.Server.MapPath("~/ImgFolder/" + postedFile.FileName);

                    Stream strm = postedFile.InputStream;

                    CreateThumbnail(strm, postedFile.FileName);

                    Compressimage(strm, filePath1, postedFile.FileName);


                }
                response = Request.CreateResponse(HttpStatusCode.Created, docfiles);
            }
            else
            {
                response = Request.CreateResponse(HttpStatusCode.BadRequest);
            }
            return response;
        }
        public static void **CreateThumbnail**(Stream sourcePath, string filename)
        {
            Image image = Image.FromStream(sourcePath);
            Image thumb = image.GetThumbnailImage(120, 120, () => false, IntPtr.Zero);
             var filePath1 = HttpContext.Current.Server.MapPath("~/Thumbnail/" + filename);

             thumb.Save(filePath1 + filename);

        }

        public static void Compressimage(Stream sourcePath, string targetPath, String filename)  
        {  


            try  
            {  
                using (var image = Image.FromStream(sourcePath))  
                {  
                    float maxHeight = 900.0f;  
                    float maxWidth = 900.0f;  
                    int newWidth;  
                    int newHeight;  
                    string extension;  
                    Bitmap originalBMP = new Bitmap(sourcePath);  
                    int originalWidth = originalBMP.Width;  
                    int originalHeight = originalBMP.Height;  

                    if (originalWidth > maxWidth || originalHeight > maxHeight)  
                    {  

                        // To preserve the aspect ratio  
                        float ratioX = (float)maxWidth / (float)originalWidth;  
                        float ratioY = (float)maxHeight / (float)originalHeight;  
                        float ratio = Math.Min(ratioX, ratioY);  
                        newWidth = (int)(originalWidth * ratio);  
                        newHeight = (int)(originalHeight * ratio);  
                    }  
                    else  
                    {  
                        newWidth = (int)originalWidth;  
                        newHeight = (int)originalHeight;  

                    }  
                    Bitmap bitMAP1 = new Bitmap(originalBMP, newWidth, newHeight);  
                    Graphics imgGraph = Graphics.FromImage(bitMAP1);  
                    extension = Path.GetExtension(targetPath);  
                    if (extension == ".png" || extension == ".gif")  
                    {  
                        imgGraph.SmoothingMode = SmoothingMode.AntiAlias;  
                        imgGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;  
                        imgGraph.DrawImage(originalBMP, 0, 0, newWidth, newHeight);  


                        bitMAP1.Save(targetPath, image.RawFormat);  

                        bitMAP1.Dispose();  
                        imgGraph.Dispose();  
                        originalBMP.Dispose();  
                    }  
                    else if (extension == ".jpg")  
                    {  

                        imgGraph.SmoothingMode = SmoothingMode.AntiAlias;  
                        imgGraph.InterpolationMode = InterpolationMode.HighQualityBicubic;  
                        imgGraph.DrawImage(originalBMP, 0, 0, newWidth, newHeight);  
                        ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);  
                        Encoder myEncoder = Encoder.Quality;  
                        EncoderParameters myEncoderParameters = new EncoderParameters(1);  
                        EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, 50L);  
                        myEncoderParameters.Param[0] = myEncoderParameter;  
                        bitMAP1.Save(targetPath, jpgEncoder, myEncoderParameters);  

                        bitMAP1.Dispose();  
                        imgGraph.Dispose();  
                        originalBMP.Dispose();  

                    }  


                }  

            }  
            catch (Exception)  
            {  
                throw;  

            }  
        }  


        public static ImageCodecInfo GetEncoder(ImageFormat format)  
        {  

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();  

            foreach (ImageCodecInfo codec in codecs)  
            {  
                if (codec.FormatID == format.Guid)  
                {  
                    return codec;  
                }  
            }  
            return null;  
        }  

2
这是使用WebAPI生成图像缩略图的内容。 - Debendra Dash
1
虽然这段代码片段可能可以解决问题,但包括解释 真的可以提高您帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您提供代码建议的原因。同时,请尽量避免在代码中加入过多的解释性注释,因为这会降低代码和解释的可读性! - Blue
2
这不是答案。原帖想要获取一个视频的缩略图。 - mbomb007

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接