你的位置:首页 > ASP.net教程

[ASP.net教程]FFmpegInterop 库在 Windows 10 应用中的编译使用


FFmpegInterop 简介

FFmpegInterop 是微软推出的封装 FFmpeg 的一个开源库,旨在方便在 Windows 10、Windows 8.1 以及 Windows Phone 8.1 应用中使用 FFmpeg 进行媒体内容播放。FFmpegInterop 实现了一个 MediaStreamSource 以便通过 FFmpeg 对媒体内容进行解码后输送到 Windows 多媒体管线进行播放。

FFmpegInterop 项目托管于 Github,项目地址:FFmpegInterop 。


原文地址:http://validvoid.net/build-and-use-ffmpeginterop-win10/


 

编译步骤

FFmpegInterop 是对 FFmpeg 的封装,依赖 FFmpeg 库本身。要使用 FFmpegInterop 需要首先手动编译 FFmpeg 和 FFmpegInterop 库。

获取文件到本地

使用 git 命令或任意 git 工具将 FFmpegInterop 项目文件 clone 到本地:

git clone --recursive git://github.com/microsoft/FFmpegInterop.git

获取最新的 FFmpeg 代码:

 git clone git://github.com/microsoft/FFmpegInterop.git cd FFmpegInterop git clone git://source.ffmpeg.org/ffmpeg.git

FFmpegInterop 的 github 仓库 中链接的是 commit 620197d 的 FFmpeg 代码。不同版本的 FFmpeg 编译后实际表现可能有所不同。

进行以上操作后,你的本地目录结构应该同以下结构相同:

FFmpegInterop\
├ ffmpeg\ - FFmpeg 库代码目录
├ FFmpegInterop\ - FFmpegInterop WinRT 组件代码目录
├ Samples\ - 使用 C++, C# 以及 JavaScript 分别实现的例子
├ Tests\ - FFmpegInterop 的单元测试
├ BuildFFmpeg.bat - 用于编译 FFmpeg 库的批处理文件
├ FFmpegConfig.sh - FFmpeg 配置脚本
├ FFmpegWin8.1.sln - 用于 Windows 8.1 Windows Phone 8.1 开发的 Visual Studio 2013 解决方案
├ FFmpegWin10.sln - 用于 Windows 10 开发的 Visual Studio 2015 解决方案
├ LICENSE
└ README.md

编译 FFmpeg

编译 FFmpegInterop 之前,我们首先需要编译 FFmpeg 本体。编译 FFmpeg 需要先准备特定的编译环境。

Visual Studio

对于 Windows 8.1,要求使用 Visual Studio 2013 Update 3 RTM 或更新的版本。 对于 Windows 10,要求使用 Visual Studio 2015。

安装配置 MSYS2

MSYS2 是一个用于 Windows 平台的 GNU 编译环境套件。要编译 FFmpeg,必须安装使用 MSYS2。

MSYS2 下载地址:http://msys2.github.io/

下载页面提供了 x86 和 x64 两种架构对应的版本,选择当前计算机对应版本下载即可。下载启动安装程序后选择一个安装路径,注意尽量选择类似 C:\msys32 这样由字母数字构成的简单短路径,路径中不能包含中文、特殊字符、空格等。安装完成后立即运行 MSYS2。

启动 MSYS2 后,需要更新 MSYS2 提供的 GNU 环境,在 MSYS2 的终端中输入命令 update-core 进行更新。更新完毕后,关闭 MSYS2 再通过开始菜单重启 MSYS2。重启后,再输入 pacman -Su 同步 MSYS2 环境的包数据库。

有关 MSYS2 安装使用的更多内容,可参阅 MSYS2 Wiki

安装配置 YASM

YASM 一个完全重写 NASM 编译器的汇编语言编译器,也是编译 FFmpeg 的必要工具之一。有关 YASM 的更多信息,可以访问其官网 yasm.tortall.net。

YASM 下载地址:http://yasm.tortall.net/Download.html

截至目前 YASM 的最新版本为 2014 年 8 月 10 日发布的 1.3.0 版。注意 YASM 在其下载页面上列举了多个不同的版本可供下载:

  • Source .tar.gz (源代码)
  • Win32 VS2010 .zip (用于 VS2010+ 和 32 位 Windows)
  • Win64 VS2010 .zip (用于 VS2010+ 和 64 位 Windows)
  • Win32 .exe (32 位 Windows 通用)
  • Win64 .exe (64 位 Windows 通用)
  • CygWin32 .exe (用于 CygWin)
  • DOS .exe (用于纯 DOS 或 DJGPP)

注意我们需要的是上述列表中加粗的两个通用版本。根据自己使用计算器的架构选择对应的通用版本下载即可。下载后,将下载回来的 yasm-1.3.0-win64.exe 改名为 yaml.exe,并放置于 MSYS2 安装目录中。例如,MSYS2 安装在 C:\msys64,则将 yaml.exe 放置到c:\msys64\usr\bin\ 中。

安装配置 gas-preprocessor

gas-preprocessor 是用于编译 FFmpeg 的 perl 预处理脚本。

gas-preprocessor 下载地址:https://github.com/FFmpeg/gas-preprocessor

下载 gas-preprocessor.pl 文件后放置于 MSYS2 安装目录中。例如,MSYS2 安装在 C:\msys64,则将 gas-preprocessor.pl 放置到 c:\msys64\usr\bin\ 中。

验证 FFmpeg 编译环境

进行以上步骤之后,编译 FFmpeg 的环境已经基本准备就绪。我们还需要对环境进行一下验证,以保证环境确实准备完毕能够顺利进行编译。

通过开始菜单找到 Visual Studio 2013 或 Visual Studio 2015 菜单组,在其中找到 VS2015 x86 ARM Cross Tools Command Prompt 启动。注意,菜单组中可能存在多个名称类似的命令行快捷方式,需要选择 x86 ARM Cross Tools。 启动 VS2015 x86 ARM Cross Tools Command Prompt 后,在命令行中定位到 MSYS2 的安装目录,启动 MSYS2:C:\msys64\msys2_shell.bat。(这样通过 VS 提供的命令行启动 MSYS2 的目的在于让 MSYS2 能够检测到部分由 VS 提供的编译工具。)

在启动的 MSYS2 终端中分别运行一下命令观察各便于工具组件是否被正确找到:

$ which cl/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/BIN/x86_ARM/cl$ which link/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/BIN/x86_ARM/link$ which armasm/c/Program Files (x86)/Microsoft Visual Studio 14.0/VC/BIN/x86_ARM/armasm$ which yasm/usr/bin/yasm$ which cpp/usr/bin/cpp$ which gas-preprocessor.pl/usr/bin/gas-preprocessor.pl

如果所有组件均在指定位置被找到,则表示 FFmpeg 编译环境已经准备就绪,可以进入下一步骤编译 FFmpeg。如果没有通过 VS 提供的 x86 ARM Cross Tools 命令行启动 MSYS2,则 cl, link, armasam 这几个组件有可能定位不到。也可以选择将 c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\x86_ARM 这个目录加入系统的环境变量。

编译 FFmpeg

在 FFmpegInterop 中,微软已经提供了方便的编译批处理用于自动编译 FFmpeg,如果你想手动编译 FFmpeg,可以参阅 Compile and Use FFmpeg Libraries for Windows Runtime。

FFmpegInterop 项目中提供了一个名为 BuildFFmpeg.bat 的批处理文件,借助该批处理,可以轻松进行 FFmpeg 的编译工作。BuildFFmpeg.bat 接受两个可选参数,第一个参数表明目标平台,第二个参数表明目标架构,例如:

BuildFFmpeg.bat win10           - 为 Windows 10 的 ARM, x64 和 x86 编译 BuildFFmpeg.bat phone8.1 ARM       - 为 Windows Phone 8.1 的 ARM 编译 BuildFFmpeg.bat win8.1 x86 x64      - 为 Windows 8.1 的 x86 和 x64 编译 BuildFFmpeg.bat phone8.1 win10 ARM    - 为 Windows 10 和 Windows Phone 8.1 的 ARM 编译 BuildFFmpeg.bat win8.1 phone8.1 win10   - 为 所有平台所有架构编译 

编译时间较长,编译完成后批处理会自动退出。编译后的输出的文件位于项目的 ffmpeg/Build/目标平台/架构 目录内。

编译 FFmpegInterop

打开 Win 8.1 或 Win 10 对应的 项目解决方案文件。可见到 FFmpegInterop 解决方案整体的结构:

Solution "FFmpegWin10" ├ FFmpegInterop (Universal Windows, C++)├ MediaPlayerCPP (Universal Windows, C++)├ MediaPlayerCS (Universal Windows, C#)├ MediaPlayerJS (Universal Windows, Javascript)└ UnitTest

而解决方案的文件目录结构为

FFmpegInterop-master ├ FFmpegWin10.sln/ FFmpegWin8.1.sln (Visual Studio Solution)├ BuildFFmpeg.bat (Build script)├ FFmpegConfig.sh (Build script)├ FFmpegInterop (Project folder of FFmpegInterop)└ Samples (Project folder of sample players) ├ SamplesWin10 └ SamplesWin8.1

需要注意的是,在项目中使用 FFmpegInterop 时需要按照 FFmpegInterop 的文件目录结构对项目文件进行安放。如果没有直接使用 FFmpegInterop 提供的项目文件,记得配置 interop 项目对 FFmpeg 的引用:

 

另外 FFmpeg 编译后是区分 x86, x64, ARM 三种不同目标架构的,不同架构输出的目录并不相同,例如面向 ARM 平台的 Windows 10 版本的 FFmpeg 编译输出的文件位于 FFmpegInterop-master\ffmpeg\Build\Windows10\ARM 目录中。在 C# 版的播放器示例项目中,FFmpeg 的几个 .dll 文件是以链接的方式直接从 FFmpeg 的 build 目录引入项目的,这样在应用打包时,会将对应架构的 FFmpeg 库文件自动封入应用包中。

FFmpegInterop 提供的 MediaPlayerCS 项目已经做好了相关配置,如果需要在自己的项目中使用如上文所述的链接方式为项目添加 FFmpeg 库文件,需要手动配置项目文件:

C# 项目

使用文本编辑器(推荐 Sublime Text/Atom,不要使用记事本)或以 Visual Studio 文本模式(只打开文件不打开整个项目)打开项目的 .csproj 文件,找到 <ItemGroup></ItemGroup> 节点,在其中添加以下内容:

<Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avcodec-56.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avdevice-56.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avfilter-5.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avformat-56.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avutil-54.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\swresample-1.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\swscale-3.dll" /> 

<ItemGroup> 节点代表项目包含的文件组,<Content> 代表项目中的“内容”类型文件。$(SolutionDir) 和 $(PlatformTarget) 均为 Visual Studio 所用生成器可以识别的宏,$(SolutionDir) 代表解决方案目录;$(PlatformTarget) 代表目标平台。采用以上配置,C# 项目即可引入对应平台的 FFmpeg 库文件了。项目配置文件全文可参考MediaPlayerCS.csproj 。

Javascript 项目

Javascript 项目与 C# 项目类似,使用文本编辑器(推荐 Sublime Text/Atom,不要使用记事本)或以 Visual Studio 文本模式(只打开文件不打开整个项目)打开项目的 .jsproj 文件,找到 <ItemGroup></ItemGroup> 节点,在其中 <AppxManifest></AppxManifest>节点之后添加以下内容:

<Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avcodec-56.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avdevice-56.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avfilter-5.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avformat-56.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avutil-54.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\swresample-1.dll" /> <Content Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\swscale-3.dll" /> 

项目配置文件全文可参考 MediaPlayerJS.jsproj 。

C++ 项目

C++ 项目与 C# 和 Javascript 项目稍有不同,使用文本编辑器(推荐 Sublime Text/Atom,不要使用记事本)或以 Visual Studio 文本模式(只打开文件不打开整个项目)打开项目的 .vcxproj 文件,找到 <ItemGroup></ItemGroup> 节点,在其中 <AppxManifest></AppxManifest> 节点之后添加以下内容:

<None Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avcodec-56.dll">  <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent></None> <None Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avdevice-56.dll">  <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent></None> <None Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avfilter-5.dll">  <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent></None> <None Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avformat-56.dll">  <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent></None> <None Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\avutil-54.dll">  <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent></None> <None Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\swresample-1.dll">  <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent></None> <None Include="$(SolutionDir)ffmpeg\Build\Windows10\$(PlatformTarget)\bin\swscale-3.dll">  <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent> <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent></None> 

完整的 C++ 项目配置文件可以参考 MediaPlayerCPP.vcxproj 。

可以看到 FFmpeg 的几个 .dll 文件名中都有数字,例如 avformat-56.dll,不同版本的 FFmpeg 编译出来的文件名中这个版本号数字是不一样的,如果手动获取了不同版本的 FFmpeg 代码进行编译,注意在项目中添加 FFmpeg 的 .dll 时正确填写文件名。

如果在之前获取代码文件到本地的步骤中,你获取了最新版本的 FFmpeg 代码,则需要对 FFmpegInterop 项目进行一些改动才能够顺利编译。

在最新版本的 FFmpeg 代码中,FFmpegInterop 在 FFmpegReader.cpp 中调用的 av_free_packet 已被弃用,FFmpeg 在 commit ce70f28a1732c74a9cd7fec2d56178750bd6e457 中已经使用 av_packet_unref 替换了 av_free_packet,因此我们需要在 FFmpegReader.cpp 中改为使用 av_packet_unref。相关讨论可参见 Build error: avfreepacket deprecated 。

使用 FFmpegInterop

FFmpegInterop 的工作流程是:

  1. 读取媒体文件流。
  2. 通过 FFmpegInteropMSS.CreateFFmpegInteropMSSFromStream() 方法创建一个 FFmpegInteropObject,并为其传递媒体文件流和强制软解设置。
  3. 调用 FFmpegInteropObject 互操作对象的 GetMediaStreamSource() 方法获得 MediaStreamSource
  4. 将 MediaStreamSource 设置给 MediaElement(C#) 或 VideoTag(Javascript)进行播放。

FFmpegInteropMSS 中提供了两个用于创建 FFmpegInteropObject

  1. CreateFFmpegInteropMSSFromStream
  2. CreateFFmpegInteropMSSFromUri

CreateFFmpegInteropMSSFromStream 方法接收三个参数 IRandomAccessStream^ stream, bool forceAudioDecode, bool forceVideoDecodestream 即输入的待播放媒体文件流;forceAudioDecode 用于设置是否强制使用 FFmpeg 对音频进行软解;forceVideoDecode 用于设置是否强制使用 FFmpeg 对视频进行软解。如果不设置强制使用 FFmpeg 进行软解,那么 MediaStreamSource 会把压缩数据直接送入 MediaElement进行播放,目前只有 mp3、aac 和 H.264 支持硬解播放。

关于 Windows 10 系统本身支持硬解的格式,可以参考 Supported codecs。

除了上述三个参数,CreateFFmpegInteropMSSFromStream 方法还有一个重载接收第四个参数 PropertySet^ ffmpegOptionsffmpegOptions 用于设置 FFmpeg 中 libavformat 库所使用的访问资源时要求的协议。所有属性列表可以参阅 FFmpeg Protocols。

CreateFFmpegInteropMSSFromUri 方法用于播放一个 URI 提供的媒体流,其接收参数为 String^ uri, bool forceAudioDecode, bool forceVideoDecode,并且同样有一个接收 PropertySet^ ffmpegOptions 参数的重载用于指定协议设置。