FFmpegInterop 是微軟推出的封裝 FFmpeg 的一個開源庫,旨在方便在 Windows 10、Windows 8.1 以及 Windows Phone 8.1 應用中使用 FFmpeg 進行媒體內容播放。FFmpegInterop 實現了一個 MediaStreamSource 以便通
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 的工作流程是:
- 讀取媒體文件流。
- 通過
FFmpegInteropMSS.CreateFFmpegInteropMSSFromStream()
方法創建一個FFmpegInteropObject
,併為其傳遞媒體文件流和強制軟解設置。 - 調用
FFmpegInteropObject
互操作對象的GetMediaStreamSource()
方法獲得MediaStreamSource
。 - 將
MediaStreamSource
設置給MediaElement
(C#) 或VideoTag
(Javascript)進行播放。
FFmpegInteropMSS
中提供了兩個用於創建 FFmpegInteropObject
:
- CreateFFmpegInteropMSSFromStream
- CreateFFmpegInteropMSSFromUri
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
參數的重載用於指定協議設置。