在.net中使用FFmpeg?


68

因此,我知道这是一个相当大的挑战,但是我想使用FFmpeg库在c#中编写一个基本的电影播放器​​/转换器。但是,我需要克服的第一个障碍是将FFmpeg库包装在c#中。我已经下载了ffmpeg,但无法在Windows上对其进行编译,因此我为我下载了预编译的版本。好棒 然后,我开始寻找C#包装器。

我环顾四周,发现了一些包装器,例如SharpFFmpeg(http://sourceforge.net/projects/sharpffmpeg/)和ffmpeg-sharp(http://code.google.com/p/ffmpeg-sharp/)。首先,我想使用ffmpeg-sharp作为LGPL,而SharpFFmpeg是GPL。但是,它有很多编译错误。原来它是为mono编译器编写的,我尝试使用mono编译它,但不知道如何。然后,我自己开始手动修复编译器错误,但遇到了一些令人恐惧的错误,并认为我最好不理会那些错误。所以我放弃了ffmpeg-sharp。

然后我查看了SharpFFmpeg,它看起来像我想要的,所有功能都是P /为我调用的。但是它的GPL?AVCodec.cs和AVFormat.cs文件都看起来像avcodec.c和avformat.c的端口,我认为我可以自己移植?那就不用担心许可问题了。

但是我想在开始编程之前就弄清楚这一点。我是不是该:

  1. 编写我自己的C ++库以与ffmpeg进行交互,然后让我的C#程序与C ++库对话以播放/转换视频等。

要么

  1. 通过使用大量的DllImports将avcodec.h和avformat.h(是我所需要的吗?)移植到c#并将其完全用C#编写?

首先,考虑到我不太擅长C ++,因为我很少使用它,但我知道足够多,可以绕开它。我之所以认为#1可能是更好的选择,是因为大多数FFmpeg教程都使用C ++编写,并且与使用c#相比,我对内存管理的控制更多。

你怎么看?您还会碰巧有使用FFmpeg的任何有用链接(也许是教程)吗?



为什么不执行C#-> DirectShow-> FFMPEG之类的操作?这可能是题外话,但还请确保您不要最后访问ffmpeg.org/shame.html
Adam Baxter,

Answers:



26

其他一些托管包装器供您签出

在.NET中,编写自己的互操作包装器可能是一个耗时且困难的过程。为互操作编写C ++库有一些优点-特别是因为它允许您大大简化C#代码的接口。但是,如果只需要一部分库,那么仅使用C#进行互操作就可以使您的生活更轻松。


嗨,马克!我所需要的只是一个包装器,该包装器应公开一种方法:public static void Convert(string sourcePath, string targetPath, MediaType mediaType)(MediaType应该引用一个指定目标文件类型的枚举,例如AVI,MP4,MP3等),这将是一个耗时的消费者,因为它仅出于以下方面的开发能力而开发包装器: ffmpeg(我只需要基本转换,以便我的设备可以播放某些不受支持的格式的文件)。有什么线索吗?您能否将我重定向到可以从哪里开始?真的那么简单吗?谢谢!
Shimmy Weitzhandler,2011年

没有像swig这样的图书馆可以为您自动包装互操作性详细信息吗?
rogerdpack

1
@Shimmy(如果您要做的只是转换文件),最简单的方法是使用ffmpeg命令行。
David Chappelle 2014年

@DavidChappelle这就是我最终要做的,我构建了一个具有异步进度报告的.NET包装器。
Shimmy Weitzhandler 2014年

我们可以下载FFv1 dll进行图像转换吗?
thevan 2015年

6

仅当在单独的进程中作为命令行实用程序调用了GPL编译的ffmpeg时,才可以从非GPL程序(商业项目)中使用它。与ffmpeg库链接的所有包装器(包括Microsoft的FFMpegInterop)都只能使用ffmpeg的LGPL版本。

您可以尝试FFMpeg的.NET包装器:.NET的视频转换器(我是该库的作者)。它将FFMpeg.exe嵌入到DLL中以便于部署,并且不会破坏GPL规则(未链接FFMpeg,并且包装程序使用System.Diagnostics.Process在单独的进程中调用它)。


这样做确实会调用ffmpeg.exe,而不是使用dll。
2014年

1
尽管此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能会失效。-来自评论
dingo_d

1
@dingo_d感谢您的通知,我在答案中添加了更多详细信息。
Vitaliy Fedorchenko '16

6

您可以使用以下nuget软件包:

Install-Package Xabe.FFmpeg

我试图使易于使用的跨平台FFmpeg包装器。

您可以在Xabe.FFmpeg上找到有关此信息的更多信息。

文档中的更多信息

转换很简单:

IConversionResult result = await Conversion.ToMp4(Resources.MkvWithAudio, output).Start();

3

对于Linux和Windows都可行的解决方案是习惯于在代码中使用控制台ffmpeg。我堆叠线程,编写一个简单的线程控制器类,然后您可以轻松利用想要使用的ffmpeg的功能。

作为示例,其中包含使用ffmpeg的部分,从我指定的时间开始创建缩略图。

在线程控制器中,您有类似

List<ThrdFfmpeg> threads = new List<ThrdFfmpeg>();

这是您正在运行的线程的列表,我利用计时器来对这些线程进行极点操作,如果极点操作不适合您的应用程序,您还可以设置一个事件。在这种情况下,Thrdffmpeg类包含

public class ThrdFfmpeg
{
    public FfmpegStuff ffm { get; set; }
    public Thread thrd { get; set; }
}

FFmpegStuff包含各种ffmpeg功能,显然thrd是线程。

FfmpegStuff中的一个属性是FilesToProcess类,该类用于将信息传递给被调用的进程,并在线程停止后接收信息。

public class FileToProcess
{
    public int videoID { get; set; }
    public string fname { get; set; }
    public int durationSeconds { get; set; }
    public List<string> imgFiles { get; set; }
}

VideoID(我使用数据库)告诉线程处理要使用的视频是从数据库中获取的。fname在使用FilesToProcess的函数的其他部分中使用,但此处未使用。durationSeconds-由仅收集视频持续时间的线程填充。imgFiles用于返回创建的所有缩略图。

当这样做的目的是鼓励在易于控制的线程中使用ffmpeg时,我不想陷入僵局。

现在我们有了我们可以添加到线程列表中的片段,因此在我们的控制器中,我们做了类似的事情,

        AddThread()
        {
        ThrdFfmpeg thrd;
        FileToProcess ftp;

        foreach(FileToProcess ff in  `dbhelper.GetFileNames(txtCategory.Text))`
        {
            //make a thread for each
            ftp = new FileToProcess();
            ftp = ff;
            ftp.imgFiles = new List<string>();
            thrd = new ThrdFfmpeg();
            thrd.ffm = new FfmpegStuff();
            thrd.ffm.filetoprocess = ftp;
            thrd.thrd = new   `System.Threading.Thread(thrd.ffm.CollectVideoLength);`

         threads.Add(thrd);
        }
        if(timerNotStarted)
             StartThreadTimer();
        }

现在,给我们的线程穿针引线变得很简单,

private void timerThreads_Tick(object sender, EventArgs e)
    {
        int runningCount = 0;
        int finishedThreads = 0;
        foreach(ThrdFfmpeg thrd in threads)
        {
            switch (thrd.thrd.ThreadState)
            {
                case System.Threading.ThreadState.Running:
                    ++runningCount;


 //Note that you can still view data progress here,
    //but remember that you must use your safety checks
    //here more than anywhere else in your code, make sure the data
    //is readable and of the right sort, before you read it.
                    break;
                case System.Threading.ThreadState.StopRequested:
                    break;
                case System.Threading.ThreadState.SuspendRequested:
                    break;
                case System.Threading.ThreadState.Background:
                    break;
                case System.Threading.ThreadState.Unstarted:


//Any threads that have been added but not yet started, start now
                    thrd.thrd.Start();
                    ++runningCount;
                    break;
                case System.Threading.ThreadState.Stopped:
                    ++finishedThreads;


//You can now safely read the results, in this case the
   //data contained in FilesToProcess
   //Such as
                    ThumbnailsReadyEvent( thrd.ffm );
                    break;
                case System.Threading.ThreadState.WaitSleepJoin:
                    break;
                case System.Threading.ThreadState.Suspended:
                    break;
                case System.Threading.ThreadState.AbortRequested:
                    break;
                case System.Threading.ThreadState.Aborted:
                    break;
                default:
                    break;
            }
        }


        if(flash)
        {//just a simple indicator so that I can see
         //that at least one thread is still running
            lbThreadStatus.BackColor = Color.White;
            flash = false;
        }
        else
        {
            lbThreadStatus.BackColor = this.BackColor;
            flash = true;
        }
        if(finishedThreads >= threads.Count())
        {

            StopThreadTimer();
            ShowSample();
            MakeJoinedThumb();
        }
    }

将您自己的事件放到控制器类中效果很好,但是在视频工作中,当我自己的代码实际上未执行任何视频文件处理时,在控制类中轮询然后调用事件也同样有效。

使用这种方法,我已经慢慢建立了几乎所有我想使用的视频和静止图像功能,所有这些功能都包含在一个类中,并且该类作为文本文件可在Lunux和Windows版本上使用,只有少量预处理指令。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.