在.net中使用FFmpeg?
所以我知道它是一个相当大的挑战,但我想用c#编写一个基本的电影播放器/转换器使用FFmpeg库。 不过,我需要克服的第一个障碍是将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。 但是,它有很多编译错误。 原来它是为单声道编译器编写的,我尝试用单声道编译它,但不知道如何。 然后,我开始自己手动修复编译器错误,但遇到了一些可怕的错误,并认为我最好不要这些。 所以我放弃了ffmpeg-sharp。
然后,我看着SharpFFmpeg,它看起来像我想要的,所有的functionP /为我调用。 但是它的GPL? AVCodec.cs和AVFormat.cs文件看起来像avcodec.c和avformat.c的端口,我认为我可以移植自己? 那么不必担心授权。
但是在我开始编码之前,我想先弄清楚。 我是不是该:
- 编写我自己的C ++库,与ffmpeg进行交互,然后让我的C#程序和C ++库交谈,以便播放/转换video等。
要么
- 端口avcodec.h和avformat.h(是我所需要的?)通过使用一大堆的DllImports c#完全写在C#?
首先考虑到我对C ++并不擅长,因为我很less使用它,但是我知道足够了解。 我认为#1的原因可能是更好的select是,大多数FFmpeg教程是在C + +中,我也有更多的控制内存pipe理,如果我要在C#中做。
你怎么看? 你也会碰巧有任何有用的链接(也许是一个教程)使用FFmpeg?
几个其他的托pipe包装让你看看
- FFMpeg.NET
- FFmpeg的夏普
- FFLib.NET
在.NET中编写自己的互操作包装可能是一个耗时且困难的过程。 编写一个用于interop的C ++库有一些优点 – 尤其是因为它可以大大简化C#代码的接口。 但是,如果您只需要库的一个子集,则可能会使您的生活更轻松,只需在C#中执行interop即可。
原来的问题现在已经超过5年了。 与此同时,现在有一个来自ffmpeg的WinRT解决scheme和一个来自Microsoft的集成示例。
GPL编译的ffmpeg只能在非GPL程序(商业项目)中作为命令行实用程序在单独的进程中调用; 所有与ffmpeg库(包括微软的FFMpegInterop)链接的包装器只能使用LGPL构build的ffmpeg。
你可以尝试我的.NET包装FFMpeg: video转换为.NET (我是这个库的作者)。 它将FFMpeg.exeembedded到DLL中以便于部署,并且不会中断GPL规则(FFMpeg未链接,包装程序在与System.Diagnostics.Process分开的进程中调用它)。
您可以从这里尝试一个简单的ffmpeg包装.NET: http : //ivolo.mit.edu/post/Convert-Audio-Video-to-Any-Format-using-C.aspx
对于Linux和Windows都可行的解决scheme是习惯于在代码中使用控制台ffmpeg。 我堆积线程,写一个简单的线程控制器类,然后你可以很容易地使用你想使用的ffmpeg的任何function。
作为一个例子,这包含了使用ffmpeg从我指定的时间创build缩略图的部分。
在线程控制器中,你有类似的东西
List<ThrdFfmpeg> threads = new List<ThrdFfmpeg>();
哪一个是你正在运行的线程列表,我使用了一个定时器来极点这些线程,你也可以设置一个事件,如果Pole'ing不适合你的应用程序。 在这种情况下thw类Thrdffmpeg包含,
public class ThrdFfmpeg { public FfmpegStuff ffm { get; set; } public Thread thrd { get; set; } }
FFmpegStuff包含各种ffmpegfunction,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(我使用数据库)告诉线程从数据库中使用哪个video。 fname用于我的函数的其他部分使用FilesToProcess,但在这里没有使用。 durationSeconds – 由收集video持续时间的线程填充。 imgFiles用于返回创build的任何缩略图。
我不想在代码中陷入困境,因为这样做的目的是鼓励在易于控制的线程中使用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(); } }
把你自己的事件放到控制器类中是行之有效的,但是在video工作中,当我自己的代码实际上并没有做任何video文件处理时,那么在控制类中调用一个事件也同样适用。
使用这种方法,我已经慢慢地build立了几乎每一个video和剧照function,我想我将永远使用,所有包含在一个类中,并且该类作为文本文件可用于Lunux和Windows版本,只有一小部分预处理指令。
你可以使用这个nuget包:
Install-Package Xabe.FFmpeg
我试图使易于使用,跨平台的FFmpeg包装。
你可以在https://github.com/tomaszzmuda/Xabe.FFmpegfind更多关于这方面的信息;
更多信息在这里: https : //github.com/tomaszzmuda/Xabe.FFmpeg/wiki/Getting-an-information-about-video
转换很简单:
bool result = await ConversionHelper.ToMp4("myVideo.mkv", "output.mp4") .Start()