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 庫文件,需要手動配置項目文件:
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 的工作流程是:
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
參數的重載用於指定協議設置。