如何使用C#编码/解码video?

有一点背景,我被赋予了修复一些“小”错误的任务,并维护这个解决方案,以便在我们的应用程序的两个实例之间通过网络传输video。 解决方案是由不在场的人编写的,所以代码中有一些神秘感以及一些非常有趣的陷阱。 该解决方案是使用ffmpeg编写的,其中编写的C ++代码用于包装编码/解码相关代码以及一些流代码。 然后用SWIG封装这个C ++,以便它可以与C#互操作,并使用生活在WPF控件中的VideoRendererElement将video帧传递到它们呈现的位置 。 帧被传递的主要原因是因为我们需要一些自定义协议来发送video数据,而这些协议是使用C#编写的,因此当video帧被传递出来时,我们将它们包装在我们自己的数据包中并通过线路发送出去。 这个解决方案有效,我们可以使用我们的自定义协议流式传输video,尽管维护和使用它是一件噩梦。

我的问题是有更好的方法来解决这个问题吗? 我正在寻找在较低级别使用video数据(在C#中)工作的方法,以便我可以拍摄video帧并将它们打包在我们自己的数据包中并发送出去,并能够接收和重建video另一边。 ffmpeg似乎是常见的解决方案,但我遇到了很多问题,我认为GPL / LGPL是一个问题。

我想要实现的基本流程,video文件 – >编码 – >包裹在数据包中 – >在协议X上通过线路发送 – >从数据包中获取video数据 – >解码 – >渲染/保存到磁盘

DirectShow是你的朋友。 DirectShow是大多数Windows“多媒体”应用程序(如媒体播放器,音频编码器等)使用的低级层。

即使这个库是为本机开发人员制作的,也可以通过DirectShow.net从托管环境中访问它。 http://directshownet.sourceforge.net这是一个众所周知且稳定的DirectShow托管包装器。

你唯一要做的就是学习一点DirectShow来理解图形和filter的概念,然后创建自己的filter和图形来使用DirectShow的强大function!

在我们的项目中,我们使用Microsoft Expression Encoder。 它不是免费的。 它可以将video转换为不同的格式和大小,提取缩略图等。

这是一个例子:

using Microsoft.Expression.Encoder; //... //skiped //... MediaItem mediaItem = new MediaItem(videoToEncode.SourceFilePath); mediaItem.ApplyPreset(PresetFilePath); Job job = new Job(); job.ApplyPreset(PresetFilePath); // path to preset file, where settings of bit-rate, codec etc job.MediaItems.Add(mediaItem); job.EncodeProgress += OnProgress; job.EncodeCompleted += EncodeCompleted; job.DefaultMediaOutputFileName = "{OriginalFilename}.encoded.{DefaultExtension}"; job.CreateSubfolder = false; job.OutputDirectory = videoToEncode.EncodedFilePath; job.Encode(); 

使用ffmpeg包装到DLL中时遇到了各种各样的麻烦。 我的video项目非常简单 – 我只需要转换器从WMV中获取一个缩略图。

在尝试了您描述的内容之后,我的解决方案是将ffmpeg.exe二进制文件作为外部库复制到我的项目中。 这也巧妙地解决了任何代码许可问题,AFAIK ……

  Guid temp = Guid.NewGuid(); // just throw our ffmpeg commands at cmd.exe System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("cmd.exe"); psi.WorkingDirectory = Page.MapPath(@"~\Lib\ffmpeg.rev12665"); psi.UseShellExecute = false; psi.RedirectStandardError = true; psi.RedirectStandardOutput = true; psi.RedirectStandardInput = true; System.Diagnostics.Process ps = System.Diagnostics.Process.Start(psi); StreamReader outputReader = ps.StandardOutput; StreamReader errorReader = ps.StandardError; StreamWriter inputWrite = ps.StandardInput; // uses extra cheap logging facility inputWrite.WriteLine("echo \"Ripping " + copiedFile + " " + temp.ToString() + "\" >> log.txt"); inputWrite.WriteLine("ffmpeg.exe -i \"" + copiedFile + "\" -f image2 -vframes 1 -y -ss 2 tmp\\" + temp.ToString() + ".jpg"); inputWrite.WriteLine("exit"); ps.WaitForExit(3000); if (ps.HasExited) { string thumbFile = Page.MapPath(@"~\Lib\ffmpeg.rev12665\tmp") + @"\" + temp.ToString() + ".jpg"; // ... } 

您的ffmpeg命令行可能与我的示例有很大不同,但这是我发现缩略图最稳定的方式。 我在网上找到的关于ffmpeg的其他东西特别没有这个解决方案(基于cmd.exe),但这是我唯一一个运行良好的解决方案。 祝好运!

您可能会尝试看看SharpFFmpeg 。 它是使用GPL许可的,虽然您可能能够看到他们如何编写包装器,并且您可以编写自己的包装,或者获得有关如何修复当前解决方案的想法。

编辑:

在code.google.com上有一个类似于ffmpeg-sharp的包装器,它使用LGPL – 您可以在商业应用程序中使用它。 我怀疑这两个包装器的function大致相同,尽管SharpFFmpeg更老,可能更成熟。

当没有有效的方法在WPF(v3.0)中呈现video时,我写回了VideoRendererElement。 它使用一些hackery来使它工作。

如果您想稍微简化一下,请删除VRE并使用InteropBitmap进行渲染(WriteableBitmap没问题,但效率不高)。 同时删除SWIG并使您的C ++ DLL成为CLI / C ++ DLL,这样您就可以直接与C#中的C ++通信(反之亦然)。

你可以去的另一条路线就是创建一个包含你的传输/解码内容的DirectShow源filter,你可以使用类似我的WPF MediaKit将它呈现到WPF中(它使用D3DImage.0。hacks)。

另外,不要害怕LGPL。 只要您将其保留在自己的DLL中并且不更改源,您就在许可限制范围内。

您还可以查看可供下载的各种Microsoft Windows Media SDK。 在几年前的一个项目中,我们使用Windows Media Format SDK从上传的video中提取缩略图。 这些SDK还具有.NET示例代码。

我们正在为我们的mediadatabase应用程序将video文件转换为各种输出格式(divx编码的avi,flv,mp4等)。 由于我们始终使用CLI应用程序进行媒体转换(谈论使用ImageMagick / GS将EPS文件栅格化为JPG),因此我们非常依赖FFMPEG-CLI。

在我们的特殊环境中,我们使用“哑”UNIX服务器作为转换机器(只安装了sshd,ffmpeg,misc .ffmpeg库和samba)。 它们通过PuTTy的CLI从C#(WCF webservice)通过SSH命令进行控制,以进行真正的转换。

对ffmpeg的调用是通过ssh进行的,并且专门用于每个TransformationType。 putty CLI是通过C#的System.Diagnostics.Process命名空间启动的,输出和错误消息的事件是为了记录目的而处理的。

互联网提供了很多关于“如何使用ffmpeg将mpg转换为flv?”等问题的资源,一些研究将帮助你。 由于我们正在谈论版权申请,我无法发布完整的代码摘录。 但它应该为您提供有关使用C#的可靠,快速video编码后端的架构概念。