發佈日期:

MSE(Media Source Extensions)介紹

什麼是MSE?

媒體源擴展(Media Source Extensions, MSE)是一項由W3C制定的網頁API標準,旨在通過JavaScript動態生成和控制媒體流,從而實現無插件且基於Web的流媒體播放功能。MSE允許開發者將媒體數據源附加到HTMLMediaElement(如<audio><video>標籤),並動態地為這些元素構建媒體源

MSE的主要功能

  • 動態媒體流構建:MSE允許開發者使用JavaScript動態地創建和控制媒體流,這意味著可以根據需要動態加載和播放媒體數據,而不需要預先下載整個文件。
  • 自適應比特率流:MSE是實現自適應比特率流(如DASH和HLS)的基礎,這些技術允許根據網絡條件自動調整視頻質量,以提供最佳的觀看體驗。
  • 多種媒體格式支持:MSE支持多種媒體容器和編解碼格式,常見的包括H.264視頻編碼、AAC音頻編碼和MP4容器格式。

MSE的應用場景

  • 點播影片:MSE 可以用於點播影片服務,允許使用者在觀賞過程中動態加載不同解析度的影片片段,以適應不同的網路狀況。
  • 直播影片:MSE 也支援直播影片流,雖然在即時性要求較高的應用中(例如視訊通話),MSE 可能不如 WebRTC 那麼合適。
  • 自訂媒體播放:開發者可以利用 MSE 建立自訂的媒體播放器,實現更靈活的控制與功能,例如自訂緩衝策略及錯誤處理機制。

MSE的優勢

  • 動態加載:MSE允許根據需要動態加載媒體數據,減少了初始加載時間和帶寬消耗。
  • 自適應流媒體:MSE支持自適應比特率流媒體技術,如MPEG-DASH和HLS,提供更好的用戶體驗。
  • 高效緩衝管理:開發者可以精細控制緩衝區,實現更高效的媒體播放。

如何使用MSE

1. 確認瀏覽器支援度,可以使用以下JavaScript進行檢查:

if ('MediaSource' in window) {
    console.log('MSE is supported');
} else {
    console.log('MSE is not supported');
}

2. 創建MediaSource對象

創建一個MediaSource對象並將其附加到<video>元素上:

<video id="videoElement" controls></video>
<script>
    var video = document.getElementById('videoElement');
    var mediaSource = new MediaSource();
    video.src = URL.createObjectURL(mediaSource);
</script>

3. 處理sourceopen事件

mediaSource.addEventListener('sourceopen', function() {
    var sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');
    fetchAndAppendSegments(sourceBuffer);
});

4. 獲取和附加媒體片段

使用fetch API或其他方法來獲取媒體片段,並將其附加到SourceBuffer中:

function fetchAndAppendSegments(sourceBuffer) {
    fetch('path/to/video/segment.mp4')
        .then(response => response.arrayBuffer())
        .then(data => {
            sourceBuffer.appendBuffer(data);
        });
}

5. 處理緩衝更新

sourceBuffer.addEventListener('updateend', function() {
    if (mediaSource.readyState === 'open') {
        fetchAndAppendSegments(sourceBuffer);
    }
});

MSE的瀏覽器支持度

請見此:https://caniuse.com/mediasource

一直以來有低延遲需求的高分發直播需求都會很困擾iPhone對MSE的不支援

但是好消息!!!

Safari 17.1 brings the new Managed Media Source API to iPhone

https://www.radiantmediaplayer.com/blog/at-last-safari-17.1-now-brings-the-new-managed-media-source-api-to-iphone.html

蘋果公司的 Safari 17.1 更新為 iPhone 帶來了新的 Managed Media Source API (MSS),這是 Media Source Extensions (MSE) 的進化版本,旨在提供更好的電池效能和網絡優化。

Safari 17.1 的更新標誌著蘋果對 iPhone 的 Managed Media Source API 支持,這是一項長期以來由流媒體行業所期待的功能。MSS 是 MSE 的進化,旨在提供更高效的流媒體體驗,並且在 iOS 上的支持意味著現有的視頻播放器需要從 MSE 遷移到 MSS。MSS 允許瀏覽器更多地控制流媒體的邏輯和設備能力檢測,這些以往由視頻應用程序處理。蘋果強調 MSS 在電池消耗和網絡效率方面的優勢,尤其是在 5G 網絡下。儘管如此,蘋果仍然建議在 Safari 中僅支持蘋果設備的開發者優先使用原生 HLS。Radiant Media Player 已在測試 MSS 的過程中,並計劃添加對 iPhone Safari 中 MSS 的支持,同時保留原生 HLS 作為蘋果設備上的首選方案。

發佈日期:

WebRTC 點對點特性帶來的複雜性

什麼是WebRTC

WebRTC(Web Real-Time Communication)是一種開源技術,允許瀏覽器和移動應用程序進行音頻、視頻和數據的實時通信。它能夠在瀏覽器內直接進行音頻和視頻通話,無需安裝任何插件或額外的軟件,這大大簡化了用戶的操作。支持多種平台,包括Web、Android、iOS、Windows、MacOS和Linux。其非常的低延遲,這對於需要即時反應的應用場景(如視頻會議、在線遊戲等)非常重要。

WebRTC的關鍵技術

參考資料:https://webrtc.github.io/webrtc-org/architecture/

WebRTC 的架構分為兩個主要層次,一層是針對瀏覽器開發者的 WebRTC C++ API,另一層是針對網絡應用開發者的 Web API。

WebRTC 支援的音訊與影像引擎具備多種功能,包括各種音訊編解碼器(如 iSAC、iLBC、Opus)、回音消除(AEC)、降噪(NR)、影像編解碼器(如 VP8)、影像抖動緩衝(Video Jitter Buffer)以及畫面增強等。除此之外,WebRTC 的傳輸與會話層包含 RTP 網路層、STUN/ICE 用於網路連線建立,以及抽象的會話管理層,讓應用開發者可自行選擇協議實作方式。

技術目標與限制

WebRTC 的目標是打造一個強大的端對端即時通訊平台,讓開發者能創建豐富的即時多媒體應用,並能夠在不同的網頁瀏覽器及平台上執行。

WebRTC 是一種支持瀏覽器間進行實時音視頻通信的技術,利用點對點(P2P)的 UDP 傳輸來實現低延遲的數據流傳輸。然而,由於網絡環境中的 NAT(Network Address Translation)和防火牆的存在,直接的 P2P 連接可能會受到限制。因此,WebRTC 使用了 ICE 框架來解決 NAT 穿透問題

WebRTC 的 NAT 穿透技術

WebRTC的NAT穿透技術是實現點對點(P2P)通信的關鍵。如果NAT穿透失敗,則無法建立直接的P2P連線,這會影響通信的品質和延遲。

WebRTC使用多種技術來實現NAT穿透,主要包括ICE(Interactive Connectivity Establishment)、STUN(Session Traversal Utilities for NAT)和TURN(Traversal Using Relays around NAT)。

  1. ICE(Interactive Connectivity Establishment)
    • ICE 是一個框架,整合了 STUN 和 TURN 技術,用於確保端點之間的連接
  2. STUN(Session Traversal Utilities for NAT)
    • STUN 用於獲取位於 NAT 後面的設備的公共 IP 地址,實現 UDP 打洞,以便建立直接的 P2P 連接
  3. TURN(Traversal Using Relays around NAT)
    • 當 STUN 失敗時,TURN 提供中繼服務器,用於轉發數據流,確保通信的可靠性

即使有了這些協議,WebRTC 在某些情況下仍無法達到 100% 的網絡穿透性,特別是在某些複雜的網絡環境中,而這會造成無法建立直接的P2P連線而無法播放串流,即使TURN伺服器能夠建立連接,由於數據需要通過中繼伺服器轉發,這會增加通信的延遲,影響用戶體驗。使用TURN伺服器會增加網路頻寬消耗,因為所有數據都需要通過中繼伺服器進行轉發,這對於高流量的應用來說是一個挑戰。

相比之下,基於 HTTP 的協議如 HLS 更具有普遍性和 CDN 支援,但延遲較高。

不適合以下情境

  • 大規模廣播:WebRTC 的點對點架構非常適合小規模的通訊,但當需要大規模的多人廣播或直播時,WebRTC 效率較低,因為它需要每個用戶單獨建立連線。這樣會消耗大量的網路帶寬和資源。
  • 資料存儲與回放:WebRTC 主要設計用於即時通訊,不適合用來處理錄製和回放功能。如果需要錄製通訊內容,還需結合其他伺服器端解決方案來存儲和管理這些資料。
  • 多媒體品質保證:WebRTC 的多媒體傳輸受限於網路環境,無法完全保證高品質的影像和音訊。若需要穩定且高畫質的多媒體傳輸,可能需要更專業的解決方案。

為何不適合大規模直播的場景

WebRTC 雖然適合小規模通信(1-4 名參與者),但在大規模應用時需要後端基礎設施的支持。

這是因為WebRTC主要設計用於點對點(P2P)通信,這意味著它在處理大規模直播時會遇到性能瓶頸。當用戶數量超過一定範圍時,WebRTC的效能會顯著下降,並且需要額外的媒體伺服器(如MCU或SFU)來中繼和分發流量。

這邊網頁有介紹如何利用中繼點來避免效能瓶頸問題

https://medium.com/agora-io/webrtc-architectures-advantages-limitations-7016b666e1ae

WebRTC 的點對點特性帶來的複雜性

1. NAT 穿透(NAT Traversal)

  • NAT(Network Address Translation,網絡地址轉換) 是大多數家庭路由器或公司防火牆使用的技術,用來讓多個設備共享一個公共 IP 地址,並同時保護內網免受外部未經授權的訪問。
  • WebRTC 是點對點的協議,這意味著兩個終端需要直接互相通信。然而,當設備位於 NAT 後面時,直接的連線會變得困難,因為內網中的設備沒有公共 IP,無法直接被外部訪問。

NAT 穿透技術:

為了解決 NAT 穿透問題,WebRTC 使用了一些技術:

  • STUN(Session Traversal Utilities for NAT):STUN 伺服器幫助客戶端找到自己的公共 IP 和端口。當一個 WebRTC 用戶端連接到 STUN 伺服器時,伺服器會回傳用戶端的公共 IP 和端口,這些信息可以用來嘗試與另一個用戶端進行點對點連線。
  • TURN(Traversal Using Relays around NAT):如果 STUN 無法成功穿透 NAT(例如某些嚴格的 NAT 或防火牆阻止了連線),TURN 伺服器會作為中繼來幫助兩個用戶端進行連接。TURN 伺服器實際上會中繼點對點之間的數據,但這會增加延遲和成本,因為流量需要通過伺服器轉發。
  • ICE(Interactive Connectivity Establishment):ICE 是 WebRTC 中用來協調和選擇最佳連線方式的協議。它會嘗試使用 STUN 獲得公共 IP,並且如果 STUN 失敗,則會回退到使用 TURN 中繼伺服器來建立連接。ICE 的工作原理是嘗試多種連接路徑(直接連線、STUN、TURN),然後選擇最有效的路徑。

複雜性:

  • NAT 類型差異:不同類型的 NAT(如對稱 NAT 和全錐形 NAT)在處理穿透時行為不同,這會導致有時候需要 TURN 伺服器來中繼,即便兩個用戶端可能在理論上能夠直接通信。
  • STUN 和 TURN 伺服器部署:如果你在應用中使用 WebRTC,你需要設置和維護 STUN 和(可能的)TURN 伺服器,這增加了基礎架構的複雜性。

2. 信令伺服器(Signaling Server)

  • WebRTC 需要在兩個用戶端之間交換連線信息,例如 ICE 候選者、SDP(Session Description Protocol,用於交換媒體能力的協議),以協調和建立點對點的連線。這些交換的連線信息需要透過一個中心伺服器進行,這個伺服器稱為 信令伺服器
  • 信令本身不屬於 WebRTC 的範疇,這意味著開發者需要自行選擇或實現信令系統。信令系統可以使用任何協議,如 WebSocket、HTTP 或 WebRTC DataChannel。

信令的功能:

  • 交換 SDP 和 ICE 信息:信令伺服器的主要作用是幫助兩個用戶端交換 SDP 和 ICE 信息,以便雙方可以建立連線。
  • 管理連線建立與斷開:信令伺服器還負責管理連線的建立和斷開,例如用來處理 WebRTC 呼叫的邀請、接受、取消和斷開等控制訊息。

複雜性:

  • 不標準化:WebRTC 並沒有標準的信令協議,這意味著開發者需要選擇或設計一個合適的信令系統。常見的做法是使用 WebSocket 來實現實時的雙向信令通信。
  • 額外伺服器需求:信令伺服器必須在應用中持續運行,以支持每個連線的初始設置,這增加了額外的伺服器和開發成本。

3. 連線穩定性(Connection Stability)

由於 WebRTC 依賴點對點連線,網絡條件的波動會直接影響連線的穩定性。特別是在移動設備或網絡不穩定的情況下,WebRTC 連線可能會中斷或劣化。

挑戰:

  • 網絡切換:如果使用者在 Wi-Fi 和行動數據網絡之間切換,WebRTC 連線可能會中斷,因為 IP 地址和路由會發生變化。
  • 封包丟失與延遲:在不穩定的網絡環境中(如高延遲或丟包率高的網絡),WebRTC 連線的質量會受到很大影響,導致視頻或音頻卡頓、延遲增加,甚至連線斷開。
  • 重連機制:WebRTC 並沒有內建的自動重連機制,如果連線中斷,應用需要自己實現重連邏輯,這增加了實作的複雜性。
發佈日期:

利用emscripten來編譯WebAssembly 

WebAssembly 是甚麼

WebAssembly(Wasm)是一種二進制格式的指令集,用於在網頁瀏覽器中高效地執行程式碼。它由 W3C 標準組織制定,目的是提供一種高性能、跨平台的執行環境,讓開發者可以在網頁上運行接近原生效能的應用程式,尤其是使用 C、C++、Rust 等語言編寫的程式碼。

以下是 WebAssembly 的主要特點:

  1. 高效能:WebAssembly 使用二進制格式,這使得程式在瀏覽器中能夠快速加載和執行,效能接近原生應用(如桌面程式)。它的運行效率遠超 JavaScript,特別適合計算密集型的應用,如遊戲、圖形處理、數學計算等。
  2. 跨平台:WebAssembly 可以在所有現代主流瀏覽器中運行(如 Chrome、Firefox、Safari 和 Edge),並且它不依賴於具體的操作系統或硬體架構,確保跨平台兼容性。無論在桌面、移動設備還是其他嵌入式設備上,只要有瀏覽器支持,Wasm 程式都可以執行。
  3. 語言無關:雖然 WebAssembly 最初是為了 C、C++ 和 Rust 這些高效能語言設計的,但理論上任何語言都可以編譯成 WebAssembly。這使得許多非 JavaScript 語言的開發者可以將他們的程式碼移植到網頁上運行。
  4. 與 JavaScript 互操作:WebAssembly 可以與 JavaScript 互相調用,讓開發者在不拋棄現有 JavaScript 代碼的情況下,把 WebAssembly 作為性能密集型任務的輔助工具。JavaScript 可以用來處理前端邏輯,而 WebAssembly 則負責複雜的數據處理或計算任務。
  5. 安全性:WebAssembly 運行在瀏覽器的沙盒環境中,這意味著它繼承了網頁應用的安全模型,無法直接訪問用戶的系統或資料,確保了安全性。

WebAssembly 是一種讓網頁能夠運行接近原生應用效能的技術,特別適合對性能要求較高的應用,同時保持了跨平台的便利性。

WebAssembly 的應用場景

  • 遊戲開發:開發者可以將原生遊戲引擎(如 Unity、Unreal)編譯成 WebAssembly,從而在瀏覽器中直接運行高效能的 3D 遊戲。
  • 視頻處理和圖像處理:如實時編碼、解碼或濾鏡應用,能夠大幅提升處理速度。
  • 數據密集型應用:如科學計算、機器學習模型運行等,能夠在網頁端完成重度計算任務。

使用Emscripten將C專案編譯為WebAssembly

Emscripten 是一個開源編譯器工具,可以將用 C 和 C++ 編寫的程式碼編譯成 WebAssembly(.wasm)或 JavaScript,使這些程式能夠在瀏覽器中運行。它的主要目的是讓開發者能夠將桌面應用程式、遊戲或其他基於 C/C++ 的軟體移植到網頁環境中。

具體來說,Emscripten 的功能有以下幾個重點:

  1. 編譯到 WebAssembly:Emscripten 可以將 C/C++ 程式編譯成 WebAssembly(Wasm),這是一種高效的二進位格式,專為瀏覽器和其他環境中的快速執行而設計。Wasm 比 JavaScript 執行效率更高,特別適合需要高性能的應用。
  2. 編譯到 JavaScript:除了 WebAssembly,Emscripten 還可以將 C/C++ 程式編譯成 asm.js,這是一種高度優化的 JavaScript 子集,主要用於在沒有 WebAssembly 支援的舊瀏覽器上運行。
  3. 跨平台支持:Emscripten 支援多種系統函式庫和 API(如 POSIX、OpenGL 等),這使得在桌面應用中常用的功能也能在網頁中運行,減少了移植工作的難度。
  4. 使用 LLVM 編譯框架:Emscripten 基於 LLVM 編譯架構,這使得它能夠將編譯過程標準化,並且能很好地支援現代 C 和 C++ 標準。

Emscripten安裝流程

下載檔案

https://github.com/msys2/msys2-installer/releases/download/2024-01-13/msys2-x86_64-20240113.exe

開啟模擬器

開啟ucrt64.exe

cd /D/Git/lbde265/

安裝所需套件

pacman -Syu
pacman -S python3 clang cmake git make autoconf automake gcc
pacman -S mingw-w64-ucrt-x86_64-nodejs
pacman -S mingw-w64-ucrt-x86_64-gcc
pacman -S mingw-w64-ucrt-x86_64-emscripten
pacman -S base-devel mingw-w64-x86_64-toolchain

安裝Emscripten

nano ~/.bashrc
export EMSDK="/D/Git/lbde265/emsdk"
export PATH="$PATH:/D/Git/lbde265/emsdk"
source ~/.bashrc

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest --permanent
source ./emsdk_env.sh

nano ~/.bashrc
export PATH="$PATH:/d/Git/lbde265/emsdk/upstream/emscripten"
export PATH="$PATH:/d/Git/lbde265/emsdk"
export PATH="$PATH:/c/msys64/ucrt64/bin"
export PATH="$PATH:/c/msys64/usr/local/bin"
export PATH="$PATH:/c/msys64/usr/bin"
export PATH="$PATH:/c/Windows/System32"
export PATH="$PATH:/c/Windows"
export PATH="$PATH:/c/Windows/System32/Wbem"
export PATH="$PATH:/c/Windows/System32/WindowsPowerShell/v1.0/"
export PATH="$PATH:/c/msys64/usr/bin/site_perl"
export PATH="$PATH:/c/msys64/usr/bin/vendor_perl"
export PATH="$PATH:/c/msys64/usr/bin/core_perl"
source ~/.bashrc

測試

emcc -v

安裝libde265.js

git clone https://github.com/strukturag/libde265.js.git
chmod +x ./configure
sh build.sh

wget https://github.com/strukturag/libde265/releases/download/v1.0.15/libde265-1.0.15.tar.gz
tar xzf libde265-1.0.15.tar.gz
cd libde265-1.0.15
./configure --disable-sse --disable-dec265 --disable-sherlock265 --enable-log-error --enable-log-info --enable-log-trace
emmake make

———->引用 gcc 或 g++ 的行。對於 Emscripten,您需要將它們替換為 emcc 或 em++。(子目錄也要改)

如果您要針對 WebAssembly 進行編譯,您需要使用 Emscripten 編譯器(emcc 或 em++)代替傳統的 gcc(C 編譯器)或 g++(C++ 編譯器)。


———->改成CFLAGS = -g -O2 -Wall

CFLAGS 是編譯時傳給 C 編譯器的選項。這裡的選項有:

  • -g:產生調試信息,便於使用調試工具。
  • -O2:啟用優化等級 2,使生成的程式碼更高效。
  • -Wall:開啟所有警告,便於識別代碼中潛在的問題。

編譯AV1的WebAssembly

https://github.com/GoogleChromeLabs/wasm-av1

原本官方的教學是使用

make

但是這樣會失敗

應該要使用

emcmake cmake ./third_party/aom -DAOM_TARGET_CPU=generic
emmake make
發佈日期:

OBS開啟時可使用的相關指令

官方介紹

官方啟動指令介紹頁面: https://obsproject.com/kb/launch-parameters

如何使用啟動指令

要使用自定義啟動參數創建 OBS Studio 的捷徑:

  1. 複製一個指向 OBS Studio 的捷徑,或選擇一個已存在的捷徑(從「開始」菜單、工作欄等處)。
  2. 右鍵點擊捷徑,然後點擊「屬性」。
  3. 在目標欄位中,obs64.exe 路徑後面添加啟動參數(如左圖所示)。

若要通過計劃任務或其他自動方式啟動 OBS Studio,請確保也設置了工作目錄(「開始於…」),該目錄必須指向 obs64.exe 所在的文件夾。

啟動參數介紹

OBS Studio 支援的啟動參數(Launch Parameters)。這些參數用於自動化和便攜式使用。每個參數都有特定的功能,可以在啟動 OBS Studio 時進行特定的操作或設定。

參數及其描述如下:

  • --help, -h:獲取可用參數的列表。
  • --version, -v:獲取 OBS 的版本。
  • --startstreaming:自動開始直播。
  • --startrecording:自動開始錄影。
  • --startvirtualcam:自動開始虛擬攝影機。
  • --startreplaybuffer:自動開始重播緩存。
  • --collection "name":使用指定的場景集合啟動。
  • --profile "name":使用指定的配置檔啟動。
  • --scene "name":使用指定的場景啟動。
  • --studio-mode:啟動時啟用 Studio 模式。
  • --minimize-to-tray:啟動時最小化到系統托盤。
  • --portable, -p:使用便攜模式。
  • --multi, -m:啟動多個實例時不顯示警告。
  • --always-on-top:啟動時開啟「總在最前面」模式。
  • --verbose:使日誌更詳細。
  • --unfiltered_log:禁用日誌過濾(不抑制重複行)。
  • --disable-updater:禁用內置更新器(僅限 Windows/macOS)。
  • --allow-opengl:在 Windows 上允許 OpenGL 渲染器。
  • --only-bundled-plugins:僅使用內建模組啟動。
  • --safe-mode:強制 OBS 以安全模式啟動,禁用所有第三方插件、腳本和 WebSockets。
  • --disable-shutdown-check:禁用不潔關機檢測,該檢測會提示以安全模式啟動。
  • --disable-missing-files-check:禁用啟動時可能出現的缺失文件對話框。

這些參數提供了更多的控制和靈活性,使使用者可以根據自己的需求和工作流程自定義 OBS Studio 的啟動和運行方式。

發佈日期:

JavaScript H.264 解碼器介紹 – Broadway

Broadway介紹

Broadway 是一個 JavaScript H.264 解碼器。H.264 是一個廣泛使用的視頻壓縮標準,Broadway 提供了一種在瀏覽器中,特別是不支持該格式的瀏覽器中,直接解碼 H.264 視頻的能力。

主要特點

  1. 純JavaScript:Broadway 是完全用 JavaScript 寫的,這意味著它可以在任何支持 JavaScript 的平台上運行,不需要任何外部插件或擴展。
  2. 多線程支持:Broadway 可以在主線程上運行,也可以在背景工作線程上運行,從而提高性能和響應性。
  3. 網頁集成:使用 Broadway,開發者可以輕鬆地在網頁上集成 H.264 視頻播放功能,無需依賴外部播放器或插件。

Live Demo

當首次訪問上述示範頁面時,可能會感覺視頻播放器的速度有點慢,這是因為它需要首先下載整個視頻才能開始播放。但請有耐心,一旦視頻下載完畢,您可以點擊播放器來觀看視頻。

上面的左上角播放器在主線程上運行,而其餘的播放器在背景工作線程上運行。

本機端使用範例

把Player資料夾內的檔案下載下來,放進本地端的node.js專案的Player資料夾內

檔案連結: https://github.com/mbebenita/Broadway/tree/master/Player

接著撰寫node.js程式

const express = require('express');
const http = require('http');
const path = require('path');
const socketio = require('socket.io');

let eApp = express();
let server = http.Server(eApp);
let io = socketio(server, { pingInterval: 3000, pingTimeout: 60000 });

// 設定靜態檔案的路徑
eApp.use(express.static(path.join(__dirname, '..', 'Player')));

io.on('connection', (socket) => {
    console.log('A user connected');
    socket.on('disconnect', () => {
        console.log('A user disconnected');
    });
});

let config = {
    port: 8080  // 依您的URL端口設定為8080
};
server.listen(config.port, '0.0.0.0', () => {
    let address = server.address();
    console.log(`Server running at ${address.address}:${address.port}`);
});

接著開啟電腦的http://127.0.0.1:8080/treeDemo.html,就可以在本機運行可動的範例了

發佈日期:

幾個使用PYAV的簡單範例

使用PYAV的前提

PyAV 是FFmpeg函式庫的 Pythonic 綁定。目標是提供底層庫的所有功能和控制,但盡可能管理細節。

PyAV 可透過容器、串流、封包、編解碼器和影格直接、精確地存取您的媒體。它公開了該資料的一些轉換,並幫助您將資料傳入/傳出其他套件(例如 Numpy 和 Pillow)。

這種權力確實伴隨著一些責任,因為與媒體合作非常複雜,PyAV 無法將其抽象化或為您做出所有最佳決策。如果該ffmpeg命令無需您竭盡全力即可完成工作,那麼 PyAV 可能會成為障礙而不是幫助。

所以如果我們想要在推流前處理串流、或者是拉流後處理串流內容,或者是更改串流編碼的相關資訊等【較難直接使用ffmpeg指令達到的事情時】,才較適合使用PYAV。

用RTMP的方式推流

範例程式碼如下:

import av
import asyncio
import cv2
import numpy as np
import threading
import time
from av import VideoFrame
from av.codec import CodecContext

# Define the RTMP server URL
rtmp_url = 'rtmp://127.0.0.1/live/test1'  # Replace with your RTMP server URL

# Function to capture webcam frames and push to RTMP server
def capture_and_push():
    # Open the video capture device (webcam)
    cap = cv2.VideoCapture(0)

    # Create an output container for the RTMP stream
    output_container = av.open(rtmp_url, 'w', format='flv')

    # Set up video stream parameters
    video_stream = output_container.add_stream('h264', rate=30)
    video_stream.width = 640
    video_stream.height = 480
    # Create a codec context for H.264 encoding
    codecContext = CodecContext.create('h264', 'w')

    # Create a thread for encoding and pushing frames
    def encode_and_push_frames():
        while True:
            ret, frame = cap.read()
            if not ret:
                break

            # Convert the frame to a VideoFrame
            frame = VideoFrame.from_ndarray(frame, format='bgr24')
            frame.pts = frame.pts
            frame.time_base = frame.time_base

            # Encode the frame and write it to the output container
            packet = video_stream.encode(frame)
            
            output_container.mux(packet)

    encode_thread = threading.Thread(target=encode_and_push_frames)
    encode_thread.start()

    # Wait for the encode thread to finish
    encode_thread.join()

    # Release the video capture device and close the output container
    cap.release()
    output_container.close()

if __name__ == "__main__":
    capture_and_push()

輸出透明背景的影片

若要使用 pyav (Python 的 ffmpeg/avconv 綁定) 來輸出具有透明背景的影片,你通常會使用像 ProRes 4444 或者 VP9 (與 WebM 容器一同使用) 這類的編碼器,因為它們支持 alpha 通道 (透明度)。

import av
import numpy as np

# 定義影片參數
width, height = 640, 480
duration = 5  # seconds
fps = 30
total_frames = duration * fps

# 創建輸出容器和流
container = av.open('output.webm', mode='w')
stream = container.add_stream('libvpx-vp9', rate=fps)
stream.width = width
stream.height = height
stream.pix_fmt = 'yuv420p'

# 產生影片框,這裡僅作為範例用透明背景
for frame_idx in range(total_frames):
    # 創建一個全透明的框
    img = np.zeros((height, width, 4), dtype=np.uint8)
    
    # 只是一個範例,所以在畫面中央畫一個半透明的紅色圓
    center_x, center_y = width // 2, height // 2
    radius = min(width, height) // 4
    y, x = np.ogrid[-center_y:height-center_y, -center_x:width-center_x]
    mask = x*x + y*y <= radius*radius
    img[mask] = [255, 0, 0, 128]  # Semi-transparent red
    
    # 轉換成 AVFrame 和寫入流
    frame = av.VideoFrame.from_ndarray(img, format='rgba')
    for packet in stream.encode(frame):
        container.mux(packet)

# 結束編碼
for packet in stream.encode():
    container.mux(packet)

container.close()

在串流裡增加自定義的變數

pyav裡面的frame.side_data是一個屬性,它通常包含與該幀(幀)相關的附加資訊。這些資訊可能包括但不限於:

  1. 動態範圍資訊
  2. 運動向量資訊(對於某些視訊編解碼器)
  3. 其他 FFmpeg 內部為特定編解碼器或格式定義的元數據

舉個例子,如果你正在解碼一個使用 HEVC(或稱為 H.265)編碼的視訊串流,frame.side_data可能會包含關於這一幀的 HDR 元資料(如果該視訊支援 HDR)。

通常情況下,大多數應用程式可能不需要直接讀取side_data。但是,對於需要細節控製或分析的應用程序,這是一個非常有用的屬性。

import av
import pyav
import threading

# Define the source RTMP URL and destination RTMP URL
source_url = 'rtmp://127.0.0.1/live/test1'
destination_url = 'rtmp://127.0.0.1/live/test2'

# Create an input container for the source RTMP stream
input_container = av.open(source_url, mode='r')

# Create an output container for the destination RTMP stream
output_container = av.open(destination_url, mode='w')

# Set up a video encoder for H.264
video_stream = output_container.add_stream('h264', rate=30)
video_stream.options['x264opts'] = 'nal-hrd=cbr'
video_stream.options['c:v'] = 'libx264'

# Define a SEI message to add to the video frames (modify this as needed)
sei_data = b'Some SEI Data'

# Function to add SEI data to frames and write to the output
def process_frames():
    for packet in input_container.demux():
        if packet.stream.type == 'video':
            for frame in packet.decode():
                # Add SEI data to the frame
                frame.side_data['sei'] = sei_data
                # Encode and write the frame to the output
                output_container.mux(packet)

# Create a thread to process and write frames
frame_thread = threading.Thread(target=process_frames)

try:
    # Start the frame processing thread
    frame_thread.start()

    # Run the main loop to write the output to the destination RTMP stream
    while True:
        output_container.mux(output_container.recv())
except (KeyboardInterrupt, pyav.AVError):
    pass
finally:
    # Clean up resources and close the containers
    frame_thread.join()
    input_container.close()
    output_container.close()

發佈日期:

pyav介紹

甚麼是pyav

PyAV是FFmpeg的Python封裝,旨在提供底層庫的全部功能和控制,同時盡可能管理繁瑣的細節。PyAV用於直接和精確地訪問媒體,包括容器、流、封包、編解碼器和幀。它還提供了一些數據轉換功能,並幫助您在其他程序之間傳送數據(例如Numpy和Pillow)。

然而,由於媒體處理非常複雜,PyAV無法完全抽像或為您做出所有最佳決策。 如果FFmpeg命令可以滿足您的需求,那麼PyAV可能會成為阻礙。 但在必要時,PyAV是一項關鍵工具。安裝PyAV可能有一些複雜的依賴關係,但現在可以在PyPI上找到針對Linux、Mac和Windows的二進位安裝套件。

官方網站: https://pyav.org/docs/stable/

GitHub位置: https://github.com/PyAV-Org/PyAV

建議使用場景

pyAVffmpeg 都是用來處理影音的工具,但它們的使用場景和方法有所不同。以下是對兩者的比較,以及根據不同情境的建議:

  1. 使用介面
    • pyAV:是一個 Python 函式庫,允許開發者使用 Python 語言來操作影音資料。
    • ffmpeg:是一個命令行工具,通常被用於進行批量處理或在沒有 Python 環境的系統上執行。
  2. 易用性和彈性
    • pyAV:由於是 Python 函式庫,使用者可以利用 Python 語言的所有特性來進行更複雜的操作,比如條件式處理、迴圈等。這使得對於需要更細緻操作的場景,例如資料分析、特定範圍的編輯等,pyAV 更有優勢。
    • ffmpeg:對於直接和簡單的影音轉換、剪裁、合併等操作,ffmpeg 的命令行界面非常適用。它能夠快速完成大部分的基本任務。
  3. 整合和擴展性
    • 如果你正在開發一個 Python 應用程序,並且希望直接在程式中處理影音,那麼 pyAV 可能是更好的選擇。
    • 如果只是簡單的一次性任務,或者需要在不同的平台或系統上腳本化影音處理,那麼 ffmpeg 可能更為適合。

結論

  • 如果你是 Python 開發者,且希望在程式中進行複雜的影音操作,那麼 pyAV 是個不錯的選擇。
  • 如果你只需要執行基本的影音轉換、剪裁或合併等操作,且希望能在多種平台上快速執行,那麼直接使用 ffmpeg 命令行工具可能更加適合。

安裝方法

使用以下方式安裝(如果下面的指令失敗的話,請參考此頁面安裝: https://pyav.org/docs/stable/overview/installation.html)

pip install av

後來我是使用下面這方法安裝成功的(windows)

pip install av --no-binary av
git clone https://github.com/PyAV-Org/PyAV.git
cd PyAV-main
python setup.py build --ffmpeg-dir=C:\ffmpeg

簡單的拉取RTMP源流的範例

以下的範例會拉取rtmp://127.0.0.1/live/testStream並使用OpenCV的函數顯示影像在視窗裡

import av
import cv2
import numpy as np
import os
import signal

def exit(*args,**kwargs):
    os.kill( os.getpid(), 9 )
signal.signal(signal.SIGINT,exit)

print('opening video...')
video = av.open('rtmp://127.0.0.1/live/testStream', 'r')

print('start streaming')
try:
    for packet in video.demux():
        for frame in packet.decode():
            if packet.stream.type == 'video':
                img = frame.to_ndarray(format='bgr24')
                cv2.imshow("Test", img)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
except KeyboardInterrupt:
    print(KeyboardInterrupt)
    pass
cv2.destroyAllWindows()
發佈日期:

使用ffmpeg濾鏡功能做影片合成

下載ffmpeg

https://ffmpeg.org/download.html

推一個時鐘文字的影片

ffmpeg的指令如下

ffmpeg  -f lavfi -i color=c=0x00ff00:s=800x450 -vf "settb=AVTB, setpts='trunc(PTS/1K)*1K+st(1,trunc(RTCTIME/1K))-1K*trunc(ld(1)/1K)', drawtext=text='STREAM2-%{localtime}.%{eif\:1M*t-1K*trunc(t*1K)\:d}':x=100:y=100:fontsize=32:fontcolor=white" -c:v libx264 -f flv rtmp://192.168.189.11/live/test2
  1. -f lavfi -i color=c=0x00ff00:s=800×450:這部分使用lavfi作為輸入來源,並創建一個顏色填充的視頻流。 c=0x00ff00指定填充的顏色為綠色,s=800×450指定分辨率為 800×450。
  2. -vf “settb=AVTB, setpts=’trunc(PTS/1K)1K+st(1,trunc(RTCTIME/1K))-1Ktrunc(ld(1)/1K)’, drawtext=text=’STREAM2 -%{localtime}.%{eif\:1Mt-1Ktrunc(t*1K)\:d}’:x=100:y=100:fontsize=32:fontcolor=white”:這部分使用- vf標誌來應用視頻過濾器。其中有兩個主要的過濾器:
    • settb=AVTB, setpts=’trunc(PTS/1K)1K+st(1,trunc(RTCTIME/1K))-1Ktrunc(ld(1)/1K)’:這個過濾器設置視頻時間戳。 settb=AVTB設置時間基準,setpts用於計算新的時間戳,其中包括根據PTS(顯示時間戳)和RTCTIME(實時時鐘時間)進行一些計算。
    • drawtext=text=’STREAM2-%{localtime}.%{eif\:1Mt-1Ktrunc(t1K)\:d}’:x=100:y=100:fontsize=32:fontcolor=white “:這個過濾器使用drawtext來在視頻上繪製文本。它在視頻左上角繪製了一個文本,其中包含了時間戳信息。%{localtime}插入本地時間,%{eif\:1Mt-1K* trunc(t*1K)\:d}用於插入一個經過格式化的時間戳。
  3. -c:v libx264:這部分設置視頻編碼器為libx264,用於將視頻編碼為H.264格式。
  4. -f flv rtmp://192.168.189.11/live/test2:這部分設置輸出格式為FLV,並指定RTMP服務器的地址。視頻會通過RTMP流式傳輸到指定的地址。在這裡,rtmp://192.168.189.11/live/test2是RTMP服務器的地址。

使用虛擬鏡頭來推流

ffmpeg -f dshow -rtbufsize 200M -i video=OBS-Camera -pix_fmt yuv420p -c:v libx264 -profile:v baseline -level:v 3.1 -preset:v ultrafast -s 480x270 -g 240 -an -f flv -y rtmp://172.16.46.89/live/0101_dealerPC1 
  1. -f dshow -rtbufsize 200M -i video=OBS-Camera:這部分設置輸入來源為DirectShow,並指定攝像頭的名稱為 “OBS-Camera”。 -rtbufsize 200M 設置了實時緩衝區的大小為 200MB。
  2. -pix_fmt yuv420p:這部分設置輸出視頻的像素格式為YUV 4:2:0,這是常見的視頻格式。
  3. -c:v libx264 -profile:v baseline -level:v 3.1 -preset:v ultrafast:這部分設置視頻編碼器為libx264,使用baseline配置文件,3.1級別,並設置預設(編碼速度)為ultrafast,這意味著編碼速度非常快。
  4. -s 480×270:這部分設置輸出視頻的分辨率為 480×270,即寬度為480,高度為270。
  5. -g 240:這部分設置關鍵幀(I幀)之間的間隔為 240 幀,用於控制視頻的GOP結構。
  6. -an:這部分錶示不捕獲音頻。
  7. -f flv:這部分設置輸出格式為FLV格式。
  8. -y:這部分錶示在輸出文件存在時覆蓋已存在的文件。
  9. rtmp://172.16.46.89/live/0101_dealerPC1:這部分是輸出URL,指定了通過RTMP傳輸的目標地址。視頻將會通過RTMP協議傳輸到 rtmp://172.16.46.89/live/0101_dealerPC1 這個地址。

極低延遲播放串流

ffplay -fflags nobuffer -flags low_delay -rtmp_buffer 0 -rtmp_live live -framedrop -infbuf %desc%
  1. -fflags nobuffer: 禁用緩衝。這意味著ffplay將盡可能地減少緩衝,以減少播放的延遲。
  2. -flags low_delay: 啟用低延遲模式。這將優化播放以減少延遲,適用於實時音視頻流。
  3. -rtmp_buffer 0: 設置 RTMP 緩衝大小為 0。 RTMP 是一種流媒體協議,這個選項設置緩衝大小為 0 表示盡可能地減少緩衝,從而減少延遲。
  4. -rtmp_live live: 表示要播放的是實時流。
  5. -framedrop: 如果幀太多,ffplay 將刪除一些幀以避免播放過慢。這有助於保持播放的實時性。
  6. -infbuf: 禁用緩衝輸入。與 -fflags nobuffer 相似,這有助於減少延遲。
  7. %desc%: 這可能是一個佔位符,用於指定音視頻流的 URL 或描述符。

把虛擬鏡頭和某個線上串流做綠幕合成

ffmpeg -f dshow -rtbufsize 1M -i video="OBS Virtual Camera" -f flv -i rtmp://172.17.22.89/live/test1 -filter_complex "[0:v]colorkey=0x00ff00:0.3:0.2[keyed];[1:v][keyed]overlay[o]" -map "[o]" -c:v h264_nvenc -f flv rtmp://127.0.0.1/live/test3
  1. -f dshow: 指定輸入的多媒體設備類型為 DirectShow(Windows 平台上的多媒體框架)。
  2. -rtbufsize 1M: 設置輸入緩衝區大小為 1MB。這可能有助於減少輸入的延遲。
  3. -i video=”OBS Virtual Camera”: 指定輸入的視頻設備名稱為 “OBS Virtual Camera”。這是一個虛擬攝像頭,通常由 OBS(Open Broadcaster Software)等軟件創建,用於虛擬攝像頭設備的捕獲。
  4. -f flv -i rtmp://172.17.22.89/live/test1: 指定輸入的媒體流為 RTMP 流,其 URL 為 rtmp://172.17.22.89/live/test1。這是從另一個 RTMP 流獲取的視頻。
  5. -filter_complex “[0:v]colorkey=0x00ff00:0.3:0.2[keyed];[1:v][keyed]overlay[o]”: 使用濾鏡複雜處理,這裡進行了以下操作:
  6. [0:v]colorkey=0x00ff00:0.3:0.2[keyed]:應用顏色鍵(chroma key)效果來移除綠色(0x00ff00)背景。生成一個帶有透明背景的圖像。
    [1:v][keyed]overlay[o]:將第一個輸入流(來自虛擬攝像頭)與經過顏色鍵處理的圖像進行疊加,產生混合後的圖像。
    -map “[o]”: 從混合後的圖像中選擇 [o] 這個輸出流。
  7. -c:v h264_nvenc: 使用 NVIDIA GPU 的硬件編碼器 h264_nvenc 進行視頻編碼。這將利用 GPU 進行加速,提高編碼效率。
  8. -f flv rtmp://172.17.22.89/live/test3: 指定輸出為 RTMP 流,其 URL 為 rtmp://172.17.22.89/live/test3。這是輸出混合後的視頻流。

更多詳細設置串流的方式

set ffmpegBin=C:\apps\ffmpeg\
set PATH=%PATH%;%ffmpegBin%

set camName="OBS Virtual Camera"
set camBufferSize=1000
set desc=rtmp://127.0.0.1:1935/live/demo

set codec=libx264
set fps=24
set /a "keyint=%fps%*5"
set x264opts=keyint=120:min-keyint=%fps%:scenecut=0
set preset=medium
set profile=baseline
set level=3.1
set resolution=800x450
set bitrate=700

:: publish stream
ffmpeg -f dshow -rtbufsize %camBufferSize%M -i video=%camName% ^
       -vf format=yuv420p ^
       -vcodec %codec% -x264-params %x264opts% ^
       -preset %preset% -profile:v %profile% -level:v %level% ^
       -tune:v zerolatency ^
       -s %resolution% ^
       -r %fps% ^
       -b:v %bitrate%k -minrate %bitrate%k -maxrate %bitrate%k -bufsize %bitrate%k ^
       -an ^
       -f flv %desc%

`-r` 轉成多少 fps

`-y` (global) Overwrite output files without asking.

`-i` 輸入檔案

`-c:v` video codec

`-profile:v` video profile, streaming 用 baseline

`-level:v` video level, streaming 用 3.1

`-preset:v` 編碼速度 streaming 用 ultrafast

`-b:v` 設定 bitrate, 例如 700k 486k

`-s` 尺寸

`-g` GOP size (-4 24 -g 240 相當於 Keyframe frequency = 10s)

`-an` 不需要 audio

`-f flv` flv 格式

`-f dshow -i video=OBS-Camera` window 使用 direct show來抓 camera

`-rtbufsize 200M` 設定 buffer size 避免 drop frame

`-re` 串流轉碼會變慢 因為要求 encoder 根據 native frame rate 讀檔按維持品值,要做串流轉發不要延遲時要拿掉。

https://www.wowza.com/docs/how-to-restream-using-ffmpeg-with-wowza-streaming-engine

FFREPORT 環境變數設好 ffmpeg 就會根據設定存log

linux format for date
https://www.opencli.com/linux/linux-date-format-shell-script

偵測 video frame 資訊

取得 stream 資訊

ffprobe -v quiet -show_streams -select_streams v:0 input.flv

抓出所有 i-frame

ffprobe -show_frames input.flv | grep pict_type | grep -n I

FME 設定 24fps, keyframe frequency = 10s
在理想狀況,會每 240 frame 插入一個 i-frame

example:

1:pict_type=I
241:pict_type=I
481:pict_type=I
721:pict_type=I
961:pict_type=I
1201:pict_type=I
發佈日期:

限制ffmpeg初始連接的時間

ffmpeg中的analyzeduration和probesize

在FFmpeg中,-analyzeduration和-probesize是用於設置媒體分析的參數。

-analyzeduration參數用於指定分析媒體文件的持續時間。當你在FFmpeg中打開一個媒體文件時,它需要一些時間來分析文件的內容,以確定其格式、編解碼器和其他相關的信息。這個參數設置了分析的時間長度。較長的-analyzeduration值可能會導致更準確的分析結果,但同時也會增加打開文件的時間。預設值為5,000,000微秒(5秒)。

-probesize參數用於指定分析媒體文件時讀取的數據大小。當FFmpeg分析媒體文件時,它會從文件中讀取一些數據並進行分析。這個參數設置了從媒體文件中讀取的數據大小。較大的-probesize值可能會導致更準確的分析結果,但同時也會增加分析的時間和記憶體使用量。預設值為50,000字節。

前置metadata

播放器在網絡點播場景下去請求MP4 視頻數據,需要先獲取到文件的metadata,解析出該文件的編碼、幀率等信息後才能開始邊下邊播。如果MP4 的metadata 數據塊被編碼在文件尾部,這種情況會導致播放器只有下載完整個文件後才能成功解析並播放這個視頻。對於這種視頻,我們最好能夠在服務端將其重新編碼,將metadata 數據塊轉移到靠近文件頭部的位置,保證播放器在線請求時能較快播放。比如FFmpeg 的下列命令就可以支持這個操作:

ffmpeg -i bad.mp4 -movflags faststart good.mp4

控制讀取的數據量大小

在外部可以通過設置 probesize 和 analyzeduration 兩個參數來控制該函數讀取的數據量大小和分析時長為比較小的值來降低 avformat_find_stream_info 的耗時,從而優化播放器首屏秒開。但是,需要注意的是這兩個參數設置過小時,可能會造成預讀數據不足,無法解析出碼流信息,從而導致播放失敗、無音頻或無視頻的情況。所以,在服務端對視頻格式進行標準化轉碼,從而確定視頻格式,進而再去推算 avformat_find_stream_info 分析碼流信息所兼容的最小的 probesize 和analyzeduration,就能在保證播放成功率的情況下最大限度地區優化首屏秒開。

probesize 和 analyzeduration太短的可能影響


如果將-probesize和-analyzeduration設置得太短,可能會導致以下問題:

  • 不準確的媒體分析:probesize和analyzeduration參數用於指定媒體分析的數據大小和時間長度。如果這兩個值設置得太短,FFmpeg可能無法讀取足夠的數據或分析足夠長的時間,從而導致分析結果的不準確性。這可能會影響到媒體文件的正確解碼、格式識別和相關信息的獲取。
  • 遺漏關鍵信息:媒體文件中的關鍵信息通常在文件的早期部分或特定位置。如果probesize和analyzeduration設置得太短,FFmpeg可能無法讀取到這些關鍵信息,進而影響解碼、播放或處理過程的正確性和完整性。
  • 性能問題:probesize和analyzeduration參數的值也會影響處理媒體文件所需的時間和資源。如果值設置得太短,FFmpeg可能需要更頻繁地從媒體文件中讀取數據或進行分析,增加了I/O操作和CPU負載,進而導致性能下降。
發佈日期:

Chunked Encoding介紹

甚麼是Chunked Encoding

Chunked encoding(分塊編碼)是一種HTTP/1.1協議中的傳輸編碼方式,用於將HTTP消息主體分成多個塊(chunks),以便在網絡上進行有效傳輸。分塊編碼主要用於動態生成的內容,以及在事先不知道內容大小的情況下傳輸數據。

分塊編碼的工作原理如下:

  1. 服務器將HTTP消息主體分成多個大小可變的塊。每個塊由兩部分組成:塊大小(十六進制表示)和實際數據。
  2. 每個塊都以塊大小開頭,然後是一個回車換行符(CRLF),接著是實際數據。在每個塊的數據之後,還有另一個回車換行符(CRLF)。
  3. 數據傳輸完成後,服務器會發送一個大小為0的塊,表示數據已經全部傳輸完畢。接著,服務器可以選擇性地傳輸附加的HTTP頭部,以提供更多關於已傳輸數據的信息。
  4. 客戶端接收到分塊編碼的數據後,將各個塊重新組合成完整的HTTP消息主體。

要使用分塊編碼,服務器需要在HTTP響應頭中設置Transfer-Encoding字段為chunked。這告訴客戶端,接收到的數據將使用分塊編碼格式。

分塊編碼的主要優點是允許服務器在不知道最終內容大小的情況下開始傳輸數據。這對於動態生成的內容、實時數據流和大文件傳輸非常有用。此外,分塊編碼還可以實現數據的即時壓縮和傳輸,從而提高傳輸效率。

設定nginx以支持Chunked Encoding

以下是一個簡單的nginx.conf範例,用於支持在http://127.0.0.1/live下的文件開啟chunked encoding。此配置檔案會將請求代理到後端應用伺服器(例如:Node.js、Python或其他後端應用)進行處理。請注意,這裡假設後端應用伺服器已經正確配置並支持分塊編碼。

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    gzip  on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_types text/plain text/css text/javascript application/json application/javascript application/x-javascript application/xml application/xml+rss;

    server {
        listen       80;
        server_name  127.0.0.1;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }

        location /live {
            proxy_pass http://backend:3000; # 請將 "backend" 替換為您的後端應用伺服器地址(IP 或 域名),並將 "3000" 替換為您的後端應用伺服器的端口

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            proxy_http_version 1.1;
            proxy_set_header Connection "";
            chunked_transfer_encoding on;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
}
events {
    worker_connections  1024;
}

這個配置檔案將Nginx設置為代理位於http://127.0.0.1/live的請求,並將請求轉發到後端應用伺服器。在location /live部分,使用chunked_transfer_encoding on;指令開啟分塊編碼。

觀察你的檔案是否有啟用Chunked Encoding

我們可以從伺服器的回應看到這個伺服器的檔案傳輸是否有支持Chunked Encoding,如下圖