FFmpegInterop 是微软推出的封装 FFmpeg 的一个开源库,旨在方便在 Windows 10、Windows 8.1 以及 Windows Phone 8.1 应用中使用 FFmpeg 进行媒体内容播放。FFmpegInterop 实现了一个 MediaStreamSource 以便通过 FFmpeg 对媒体内容进行解码后输送到 Windows 多媒体管线进行播放。
FFmpegInterop 项目托管于 Github,项目地址: FFmpegInterop 。
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
编译 FFmpegInterop 之前,我们首先需要编译 FFmpeg 本体。编译 FFmpeg 需要先准备特定的编译环境。
对于 Windows 8.1,要求使用 Visual Studio 2013 Update 3 RTM 或更新的版本。 对于 Windows 10,要求使用 Visual Studio 2015。
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 一个完全重写 NASM 编译器的汇编语言编译器,也是编译 FFmpeg 的必要工具之一。有关 YASM 的更多信息,可以访问其官网 yasm.tortall.net 。
YASM 下载地址: http://yasm.tortall.net/Download.html
截至目前 YASM 的最新版本为 2014 年 8 月 10 日发布的 1.3.0 版。注意 YASM 在其下载页面上列举了多个不同的版本可供下载:
注意我们需要的是上述列表中加粗的两个通用版本。根据自己使用计算器的架构选择对应的通用版本下载即可。下载后,将下载回来的 yasm-1.3.0-win64.exe
改名为 yaml.exe,并放置于 MSYS2 安装目录中。例如,MSYS2 安装在 C:/msys64
,则将 yaml.exe
放置到 c:/msys64/usr/bin/
中。
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 的环境已经基本准备就绪。我们还需要对环境进行一下验证,以保证环境确实准备完毕能够顺利进行编译。
通过开始菜单找到 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
这个目录加入系统的环境变量。
在 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/目标平台/架构
目录内。
打开 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 库文件,需要手动配置项目文件:
使用文本编辑器(推荐 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 项目与 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# 和 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: av free packet deprecated 。
FFmpegInterop 的工作流程是:
FFmpegInteropMSS.CreateFFmpegInteropMSSFromStream()
方法创建一个 FFmpegInteropObject
,并为其传递媒体文件流和强制软解设置。 FFmpegInteropObject
互操作对象的 GetMediaStreamSource()
方法获得 MediaStreamSource
。 MediaStreamSource
设置给 MediaElement
(C#) 或 VideoTag
(Javascript)进行播放。 FFmpegInteropMSS
中提供了两个用于创建 FFmpegInteropObject
:
CreateFFmpegInteropMSSFromStream
方法接收三个参数 IRandomAccessStream^ stream, bool forceAudioDecode, bool forceVideoDecode
, stream
即输入的待播放媒体文件流; forceAudioDecode
用于设置是否强制使用 FFmpeg 对音频进行软解; forceVideoDecode
用于设置是否强制使用 FFmpeg 对视频进行软解。如果不设置强制使用 FFmpeg 进行软解,那么 MediaStreamSource
会把压缩数据直接送入 MediaElement
进行播放,目前只有 mp3、aac 和 H.264 支持硬解播放。
关于 Windows 10 系统本身支持硬解的格式,可以参考 Supported codecs 。
除了上述三个参数, CreateFFmpegInteropMSSFromStream
方法还有一个重载接收第四个参数 PropertySet^ ffmpegOptions
。 ffmpegOptions
用于设置 FFmpeg 中 libavformat 库所使用的访问资源时要求的协议。所有属性列表可以参阅 FFmpeg Protocols 。
CreateFFmpegInteropMSSFromUri
方法用于播放一个 URI 提供的媒体流,其接收参数为 String^ uri, bool forceAudioDecode, bool forceVideoDecode
,并且同样有一个接收 PropertySet^ ffmpegOptions
参数的重载用于指定协议设置。