FFmpeg Muxer HLS

使用FFmpeg命令來研究它對HLS協議的支持程度是最好的方法:

ffmpeg -h muxer=hls

Muxer HLS

Muxer hls [Apple HTTP Live Streaming]:Common extensions: m3u8.Default video codec: h264.Default audio codec: aac.Default subtitle codec: webvtt.

這里面告訴我們,FFmpeg中的Muxer hls實際上是對于Apple HTTP Live Streaming的一種實現(HLS,全稱HTTP Live Streaming,是Apple公司發布的協議),這里明確說明了HLS只是一種封裝格式而與編碼無關。

默認的文件擴展名為m3u8?,我們在瀏覽器中觀看動漫、電影的時候,可以使用工具去查看它里面的鏈接。最終,你大概率會發現這樣一種m3u8?文件的訪問鏈接。

FFmpeg的hls muxer默認支持的視頻、音頻和字幕的編碼格式分別是:h264?、aac?和webvtt?。這也就意味著如果我們想要對其他編碼格式的音頻或者視頻進行HLS封裝,那么就需要顯式地去指定這些編碼格式。需要注意的是,這些其他的編碼格式需要是HLS協議支持的編碼格式。

假設我們需要對一個MP4文件進行HLS切片,更準確地說是將MP$的封裝格式轉換成HLS的封裝格式,只不過HLS這個封裝格式是由多個音視頻文件和一個M3U8(該文件在HLS協議中被稱為Media Playlist,用作指導這些切片后的音視頻如何播放)組成的。

例子

多的不說,我這里找一個MP4文件,使用FFmpeg將其轉換成hls的封裝格式,看看會出現什么樣的結果。

這里我使用ffprobe查看該MP4文件的編碼,來明確是否需要進行轉碼操作:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test_input.mp4':Metadata:major_brand     : isomminor_version   : 512compatible_brands: isomiso2avc1mp41encoder         : Lavf60.16.100Duration: 00:00:14.82, start: 0.000000, bitrate: 2662 kb/sStream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709, progressive), 1920x1200 [SAR 1:1 DAR 8:5], 2489 kb/s, 60 fps, 60 tbr, 15360 tbn (default)Metadata:handler_name    : VideoHandlervendor_id       : [0][0][0][0]Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 159 kb/s (default)Metadata:handler_name    : SoundHandlervendor_id       : [0][0][0][0]

你可以看到,視頻編碼h264?和音頻編碼aac?都是默認的,這意味著我們不需要轉碼。

因此,我們可以使用以下命令來進行轉封裝:

mkdir output & ffmpeg -i test_input.mp4 -c copy -f hls output/index.m3u8

使用命令來查看output?目錄中的內容:

> tree output
output
├── index.m3u8
├── index0.ts
├── index1.ts
├── index2.ts
└── index3.ts> cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.166667,
index0.ts
#EXTINF:4.166667,
index1.ts
#EXTINF:4.166667,
index2.ts
#EXTINF:2.366667,
index3.ts
#EXT-X-ENDLIST

此刻,output?目錄中的所有內容,組成了HLS協議的封裝格式,雖然它們由多個文件組成。

但是,當需要切片的文件變大時,index.m3u8?的內容會和實際的切片數量對不上,原因就是:默認的hls_list_size?的值為5?。因此,index.m3u8文件中只會記錄最新的5個切片。使用-hls_list_size?能夠自己指定這個值。

選項

額外指定一些選項,讓HLS的切片符合你的需求。

start_number

設置開始的序列號,默認從0開始。這里我們設置序列號為1進行切片,那么產生的結果為:

> mkdir output & ffmpeg -i test_input.mp4 -c copy -f hls -start_number 1 output/index.m3u8
> tree output
output
├── index.m3u8
├── index1.ts
├── index2.ts
├── index3.ts
└── index4.ts

注意,生成的切片文件名的序列號和m3u8文件中的序列號是對應的。

hls_time

指定切片的時間長度,單位為秒,默認值為2,類型是float?。

> cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.166667,
index0.ts
#EXTINF:4.166667,
index1.ts
#EXTINF:4.166667,
index2.ts
#EXTINF:2.366667,
index3.ts
#EXT-X-ENDLIST

#EXTINF?就是切片的時間長度,這里切片的時間長度為4.166667?,這和默認值2?不相符。這是什么原因導致的呢?我猜測,這可能是因為HLS對于每個切片的關鍵幀具有某種要求,導致了最終的切片時間按照原本視頻的關鍵幀分布來進行切片。

使用-force_key_frames "expr:gte(t,n_forced*2)"?來讓視頻GOP大小為2秒,以此讓切片能夠按照我們所設置的參數運行:

$ ffmpeg -i test_input.mp4 \ 
> -f hls \ 
> -force_key_frames "expr:gte(t,n_forced*2)" \
> -hls_time 2 \
> output/index.m3u8$ cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:3
#EXTINF:2.000000,
index3.ts
#EXTINF:2.000000,
index4.ts
#EXTINF:2.000000,
index5.ts
#EXTINF:2.000000,
index6.ts
#EXTINF:0.866667,
index7.ts
#EXT-X-ENDLIST

這證明我們的猜測是正確的。注意,由于-force_key_frames?選項改變了原本的視頻幀,因此不能夠指定-c copy?(如果指定,則會導致我們無法對原本的視頻編碼做出任何改變,-force_key_frames?就會失效)。

你可以看到上面只記錄了5個切片,3 4 5 6 7?,而我們的切片明明是從0開始的,并且0 1 2?確實存在于output目錄中,但卻沒有被m3u8文件記錄,這就關系到-hls_list_size?的使用了

hls_list_size

前面說了,由于m3u8?這個Media Playlist只會記錄最新的幾個切片,這可能會導致播放錯誤。該選項默認值是5,當切片多于5個時,你就要考慮將其設置地大一些,防止切片錯誤。

hls_base_url

你可以指定切片的基礎路徑,比如:http://xxx.com/?。這樣瀏覽器可以通過讀取m3u8?文件,然后通過網絡來訪問這些切片。

比如:

> ffmpeg -i test_input.mp4 -c copy -f hls \
> -hls_base_url "http://www.aderversa.com/" \
> output/index.m3u8> cat output/index.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:4.166667,
http://www.aderversa.com/index0.ts
#EXTINF:4.166667,
http://www.aderversa.com/index1.ts
#EXTINF:4.166667,
http://www.aderversa.com/index2.ts
#EXTINF:2.316667,
http://www.aderversa.com/index3.ts
#EXT-X-ENDLIST

該選項通常和服務器配合使用。你設置了一個URL,然后瀏覽器可以通過從服務器獲取m3u8?文件和視頻切片文件。

遠程播放HLS視頻

在前面,我們已經知道了,HLS封裝格式會生成以下文件:

output
├── index.m3u8
├── index0.ts
├── index1.ts
├── index2.ts
└── index3.ts

播放器拿到m3u8文件,它應該能夠按照HLS協議并參考m3u8上的內容自主獲取視頻切片并播放。

這里我使用Qt6.6的QMediaPlayer?來播放m3u8文件(這樣方便理解,實際上M3U8不包含視頻數據,它只含有該怎樣播放的信息)。

如何播放呢?抓住一個關鍵點:只要QMediaPlayer?拿到了M3U8文件,它就能夠依照該M3U8文件播放上面的資源,而我們是不需要了解它具體如何播放的,因為播放器的開發者為我們完成了這部分工作。我們唯一需要關注的就是:如何讓QMediaPlayer?獲取到這份M3U8文件。

這里我在網上隨便找一個M3U8文件的鏈接(隨便找個不是很正規的視頻網站一般都能夠找到,查看其HTML代碼你就能夠發現隱藏在其中的M3U8文件):

?

具體是什么URL我就不放出來了。

我們可以發現該播放器需要一個M3U8的URL才能夠播放。

我們將url=?后面的鏈接命名為VideoURL?,方便后續說明。

這里我們在瀏覽器中請求VideoURL?,看看會發生什么?結果就是,瀏覽器給我們下載了一個M3U8文件。

我們使用以下Qt6.6中的代碼來播放VideoURL?,看看能否播放成功:

int main(int argc, char *argv[])
{QApplication a(argc, argv);QMediaPlayer player;player.setSource(QUrl("https://play.modujx11.com/20250104/pZZhNChc/index.m3u8"));QVideoWidget video_widget;player.setVideoOutput(&video_widget);video_widget.show();QAudioOutput audio_output;player.setAudioOutput(&audio_output);player.play();return a.exec();
}

結果是,播放成功了。

這說明了什么呢?說明了只要服務器能夠提供一個接口,讓瀏覽器能夠通過訪問該接口URL下載到M3U8文件,且M3U8中的資源是可以被瀏覽器訪問到的,那么實現了HLS協議的播放器應該就能播放該M3U8文件。

利用上面實驗出來的特性搭建視頻平臺的一些猜想

那么,如果我們的應用程序想要通過HLS協議實現視頻遠程播放的功能,首先客戶端需要有M3U8文件并進行播放的能力;而服務器只需要提供下載M3U8文件和下載M3U8文件中對應的切片文件的接口即可。

若應用程序能夠播放的視頻是服務器端規定好的,用戶無法上傳任何視頻,那么我們在服務器端完全可以自己在相應的文件夾下使用FFmpeg命令來慢慢進行切片。

若用戶可在應用程序中上傳視頻,那么上傳完成之后,服務器端如果不追求性能和定制化,我個人認為直接調用FFmpeg的命令行程序來完成HLS的切片是沒有問題的。如果用戶上傳的視頻的編碼或者封裝格式不合適,那么要么禁止用戶上傳這類視頻;要么就在后端慢慢進行轉碼,若同一時間有大量轉碼的視頻,那么對于性能的消耗將是災難性的,因此大部分應用程序都不會允許用戶將格式不合適的視頻直接上傳到服務器端,而是讓用戶自行找方法轉碼,轉碼完成后再發送到服務器端。

用戶若是能夠上傳視頻,如果應用程序具有一定的用戶基數,那么上傳的視頻數量大概率是會逐漸增加的,對服務器的性能要求也會逐漸提高(不管是空間上還是時間上)。

應用程序可能一開始就是將這些視頻開放給所有用戶的,用戶可以通過客戶端來訪問或自己上傳的、或別人上傳的視頻數據。我們可能需要使用數據庫存業務數據 + 文件系統存視頻數據 + 數據庫和文件系統之間存在某種數據上的聯系,以此數據存儲為基礎構建服務器,然后客戶端/前端再基于服務器的接口構建符合需求的交互界面。

實現一個簡單的服務器來驗證猜想

首先,在SpringBoot中實現這樣一個Controller,用來下載服務器上的文件:

@Controller
public class FileDownloadController {private static final String FILE_DIR = "/videos";@GetMapping("/download/{dirname}/{filename:.+}")@ResponseBodypublic ResponseEntity<Resource> downloadFile(@PathVariable("dirname") String dirname,@PathVariable("filename") String filename) {String path = System.getProperty("user.dir") + FILE_DIR + "/" + dirname;Path media_list_path = Paths.get(path).resolve(filename).normalize();try {UrlResource resource =  new UrlResource(media_list_path.toUri());if (resource.exists() || resource.isReadable()) {return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"").body(resource);}else {return ResponseEntity.notFound().build();}}catch (Exception e) {e.printStackTrace();return ResponseEntity.status(500).build();}}
}

接著,我們在指定的/videos?目錄下創建好HLS封裝的文件:

> ffmpeg -i hls_video.mp4 -f hls \
> -hls_base_url \  
> http://127.0.0.1:8080/download/hlstest1/ \
> -c copy \ 
> -hls_list_size 1000 \
> hlstest1/index.m3u8> tree .
.
├── hls_video.mp4
└── hlstest1├── index.m3u8├── index0.ts├── index1.ts...

大致內容如上所示,然后在替換Qt原本代碼中的URL成http://localhost:8080/download/hlstest1/index.m3u8?,播放成功。

我這里的服務器運行在本機中,所以ip為localhost?。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/65671.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/65671.shtml
英文地址,請注明出處:http://en.pswp.cn/web/65671.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Apache和PHP:構建動態網站的黃金組合

在當今的互聯網世界&#xff0c;網站已經成為了企業、個人和機構展示自己、與用戶互動的重要平臺。而在這些動態網站的背后&#xff0c;Apache和PHP無疑是最受開發者青睞的技術組合之一。這一組合提供了高效、靈活且可擴展的解決方案&#xff0c;幫助您快速搭建出強大的網站&am…

git相關操作筆記

git相關操作筆記 1. git init git init 是一個 Git 命令&#xff0c;用于初始化一個新的 Git 倉庫。執行該命令后&#xff0c;Git 會在當前目錄創建一個 .git 子目錄&#xff0c;這是 Git 用來存儲所有版本控制信息的地方。 使用方法如下&#xff1a; &#xff08;1&#xff…

Docker Desktop 構建java8基礎鏡像jdk安裝配置失效解決

Docker Desktop 構建java8基礎鏡像jdk安裝配置失效解決 文章目錄 1.問題2.解決方法3.總結 1.問題 之前的好幾篇文章中分享了在Linux(centOs上)和windows10上使用docker和docker Desktop環境構建java8的最小jre基礎鏡像&#xff0c;前幾天我使用Docker Desktop環境重新構建了一個…

VUE + pdfh5 實現pdf 預覽,主要用來uniappH5實現嵌套預覽PDF

1. 安裝依賴 npm install pdfh5 2. pdfh5 預覽(移動端&#xff0c;h5) npm install pdfh5 , &#xff08;會報錯&#xff0c;需要其他依賴&#xff0c;不能直接用提示的語句直接npm下載&#xff0c;依舊會報錯&#xff0c;npm報錯&#xff1a;These dependencies were not fou…

Node.js——fs(文件系統)模塊

個人簡介 &#x1f440;個人主頁&#xff1a; 前端雜貨鋪 &#x1f64b;?♂?學習方向&#xff1a; 主攻前端方向&#xff0c;正逐漸往全干發展 &#x1f4c3;個人狀態&#xff1a; 研發工程師&#xff0c;現效力于中國工業軟件事業 &#x1f680;人生格言&#xff1a; 積跬步…

Microsoft Azure Cosmos DB:全球分布式、多模型數據庫服務

目錄 前言1. Azure Cosmos DB 簡介1.1 什么是 Azure Cosmos DB&#xff1f;1.2 核心技術特點 2. 數據模型與 API 支持2.1 文檔存儲&#xff08;Document Store&#xff09;2.2 圖數據庫&#xff08;Graph DBMS&#xff09;2.3 鍵值存儲&#xff08;Key-Value Store&#xff09;…

springboot項目讀取resources目錄下文件

要用以下這種方式讀取 classPathResource new ClassPathResource("template/test.docx");不能用以下這種獲取絕對路徑的方式&#xff0c;idea調試正常&#xff0c;但是部署window和linux的目錄結構不一樣&#xff0c;部署后會找不到文件&#xff0c;另外window直接…

Ruby語言的軟件開發工具

Ruby語言的軟件開發工具概述 引言 Ruby是一種簡單且功能強大的編程語言&#xff0c;它以優雅的語法和靈活性而聞名。自1995年首次發布以來&#xff0c;Ruby已經被廣泛應用于各種開發領域&#xff0c;特別是Web開發。隨著Ruby語言的普及&#xff0c;相關的開發工具也日益豐富。…

C++例程:使用I/O模擬IIC接口(6)

完整的STM32F405代碼工程I2C驅動源代碼跟蹤 一&#xff09;myiic.c #include "myiic.h" #include "delay.h" #include "stm32f4xx_rcc.h" //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphCl…

CNN-BiLSTM-Attention模型詳解及應用分析

CNN-BiLSTM-Attention結構 CNN-BiLSTM-Attention結構是一種強大的深度學習架構,巧妙地結合了三種不同的技術優勢:卷積神經網絡(CNN)、雙向長短期記憶網絡(BiLSTM)和注意力機制(Attention)。這種創新性的組合使得模型能夠在處理復雜序列數據時表現出色,尤其適用于自然…

2025年華為OD上機考試真題(Java)——整數對最小和

題目&#xff1a; 給定兩個整數數組array1、array2&#xff0c;數組元素按升序排列。假設從array1、array2中分別取出一個元素可構成一對元素&#xff0c;現在需要取出k對元素&#xff0c;并對取出的所有元素求和&#xff0c;計算和的最小值。 注意&#xff1a;兩對元素如果對應…

【Java知識】Groovy 一個兼容java的編程語言

groovy語言介紹 概述一、基本特點二、主要特性三、應用領域四、與Java的比較 基本語法特性一、基本語法二、數據類型三、運算符四、字符串五、方法六、閉包七、類與對象八、異常處理九、其他特性 集成到springboot項目1. 創建Spring Boot項目2. 添加Groovy依賴3. 編寫Groovy類4…

Python網絡爬蟲:從入門到實戰

Python以其簡潔易用和強大的庫支持成為網絡爬蟲開發的首選語言。本文將系統介紹Python網絡爬蟲的開發方法&#xff0c;包括基礎知識、常用工具以及實戰案例&#xff0c;幫助讀者從入門到精通。 什么是網絡爬蟲&#xff1f; 網絡爬蟲&#xff08;Web Crawler&#xff09;是一種…

【vLLM 學習】安裝

vLLM 是一款專為大語言模型推理加速而設計的框架&#xff0c;實現了 KV 緩存內存幾乎零浪費&#xff0c;解決了內存管理瓶頸問題。 更多 vLLM 中文文檔及教程可訪問 →https://vllm.hyper.ai/ vLLM 是一個 Python 庫&#xff0c;包含預編譯的 C 和 CUDA (12.1) 二進制文件。 …

npm : 無法加載文件 D:\SoftFile\npm.ps1,因為在此系統上禁止運行腳本。

這個錯誤是由于 Windows PowerShell 的執行策略禁止執行腳本&#xff0c;導致無法運行 npm 命令。你可以通過以下步驟來解決這個問題&#xff1a; 以管理員身份運行 PowerShell&#xff1a; 點擊“開始”菜單&#xff0c;搜索“PowerShell”&#xff0c;然后右鍵點擊“Windows …

7 分布式定時任務調度框架

先簡單介紹下分布式定時任務調度框架的使用場景和功能和架構&#xff0c;然后再介紹世面上常見的產品 我們在大型的復雜的系統下&#xff0c;會有大量的跑批&#xff0c;定時任務的功能&#xff0c;如果在獨立的子項目中單獨去處理這些任務&#xff0c;隨著業務的復雜度的提高…

網絡安全 | 網絡安全法規:GDPR、CCPA與中國網絡安全法

網絡安全 | 網絡安全法規&#xff1a;GDPR、CCPA與中國網絡安全法 一、前言二、歐盟《通用數據保護條例》&#xff08;GDPR&#xff09;2.1 背景2.2 主要內容2.3 特點2.4 實施效果與影響 三、美國《加利福尼亞州消費者隱私法案》&#xff08;CCPA&#xff09;3.1 背景3.2 主要內…

Elixir語言的計算機基礎

Elixir語言的計算機基礎 引言 在當今這個快速發展的技術時代&#xff0c;編程語言層出不窮。Elixir作為一種較新的編程語言&#xff0c;以其高并發、低延遲和強大的容錯能力受到越來越多開發者的青睞。它基于Erlang虛擬機&#xff08;BEAM&#xff09;&#xff0c;自然繼承了…

mysql的mvcc理解

人閱讀 一、說到mvcc就少不了事務隔離級別&#xff08;大白話解釋&#xff09; 序列化&#xff08;SERIALIZABLE&#xff09;&#xff1a;事務之間完全隔離&#xff0c;當成一個序列&#xff0c;一個一個執行。 1 可重復讀&#xff08;REPEATABLE READ&#xff09;&#xff…

“AI智能陪練培訓服務系統,讓學習更輕松、更高效

大家好&#xff0c;我是資深產品經理小李&#xff0c;今天咱們來侃侃一個新興的教育輔助工具——AI智能陪練培訓服務系統。這個系統可謂是教育培訓行業的一股新勢力&#xff0c;它究竟有什么神奇之處呢&#xff1f;下面我就跟大家伙兒好好聊聊。 一、什么是AI智能陪練培訓服務系…