使用ffmpeg把mp4與m3u8相互轉換的操作

来源:https://www.cnblogs.com/xusx2014/archive/2022/05/07/16243080.html
-Advertisement-
Play Games

移動端瀑布流佈局是一種比較流行的網頁佈局方式,視覺上來看就是一種像瀑布一樣垂直落下的排版。每張圖片並不是顯示的正正方方的,而是有的長有的短,呈現出一種不規則的形狀。但是它們的寬度通常都是相同的 因為移動端瀑布流佈局主要為豎向瀑布流,因此本文所探討的是豎向瀑布流 特點 豎向瀑布流佈局主要有下麵幾種特點 ...


FFmpeg 是一套可以用來記錄、轉換數字音頻、視頻,並能將其轉化為流的開源電腦程式。它提供了錄製、轉換以及流化音視頻的完整解決方案。

官方下載網站 http://www.ffmpeg.org/download.html,下載解壓縮後請配置環境。

一、MP4 轉 M3U8

M3U8 是 Unicode 版本的 M3U,用 UTF-8 編碼。”M3U” 和 “M3U8” 文件都是蘋果公司使用的 HTTP Live Streaming(HLS) 協議格式的基礎,這種協議格式可以在 iPhone 和 Macbook 等設備播放。

簡單來說,m3u8是一個視頻格式,就是將一個視頻分成很多的小部分,這樣方便視頻的載入。

1、操作簡單,但效率低

ffmpeg -i input.mp4 -c:v libx264 -c:a aac -strict -2 -f hls -hls_list_size 2 -hls_time 15 output.m3u8

生成的效果是:

將 input.mp4 視頻文件每 15 秒生成一個 ts 文件,最後生成一個 m3u8 文件,m3u8 文件是 ts 的索引文件。

我們直接用 VLC media player 等播放軟體是可以直接打開 m3u8 文件,像播放 mp4 一樣。

預設的每片長度為 2 秒,m3u8 文件中預設只保存最新的 5 條片的信息,導致最後播放的時候只能播最後的一小部分(直播的時候特別註意)。
-hls_time n 設置每片的長度,預設值為 2,單位為秒。
-hls_list_size n 設置播放列表保存的最多條目,設置為 0 會保存有所片信息,預設值為5。
-hls_wrap n 設置多少片之後開始覆蓋,如果設置為0則不會覆蓋,預設值為0。這個選項能夠避免在磁碟上存儲過多的 片,而且能夠限制寫入磁碟的最多的片的數量。
-hls_start_number n 設置播放列表中 sequence number 的值為 number,預設值為 0。
註意:播放列表的 sequence number 對每個 segment 來說都必須是唯一的,而且它不能和片的文件名(當使用 wrap 選項時,文件名有可能會重覆使用)混淆。

更多參數請看文檔:ffmpeg.org/ffmpeg.html#Video-Options

2、效率優化版,提升效率

TS 文件是一種媒體的擴展名,它是日本高清攝像機拍攝下進行的封裝格式。MPEG2-TS(Transport Stream“傳輸流”;又稱TS、TP、MPEG-TS 或 M2T)是用於音效、圖像與數據的通信協定,最早應用於DVD的實時傳送節目。MPEG2-TS格式的特點就是要求從視頻流的任一片段開始都是可以獨立解碼的。

# 1.視頻整體轉碼ts
ffmpeg -y -i music.mp4  -vcodec copy -acodec copy -vbsf h264_mp4toannexb out\music.ts
# 2. ts 文件切片
ffmpeg -i music.ts -c copy -map 0 -f segment -segment_list out\music.m3u8 -segment_time 10 out\15s_%3d.ts

3、hls_time 切片時間不准確的問題

播放 m3u8 的 ts 切片,必須要完整的下載一個 ts 切片,才能夠播放,設置hls_time 的時間間隔越短越好( 根據實際情況來 ),實際過程中設置切片時間間隔為 2 秒,調用如下指令:

ffmpeg -i test.mp4 -c:v libx264 -c:a aac -strict -2 -f hls -hls_time 2 index.m3u8

但沒有按照參數輸入,進行切片。

原因:

ts 文件的切割,還跟原文件視頻的 GOP 大小有關係(也就是兩個 I 幀之間的時間間隔),因為任何一個 ts 分片第一幀必須是I幀,否則無法最快播放,並且第一幀不是 I 幀,對於播放器也是沒有任何的意義,直接被播放器扔掉。任何一個視頻流必須在獲取到第一個I幀才能成功解碼出圖片。雖然指定了 1 秒切割一個 ts 文件,實際上,由於原視頻流可能好幾秒才有一個 I 幀,所以必須等到下一個 I 幀,才會重新開始切片。

解決:

既然知道要1秒產生一個ts分片,那就必須產生切片的過程中,強制一秒中產生一個關鍵幀。

設置關鍵幀間隔,設置間隔為 2 秒的參數如下:-force_key_frames "expr:gte(t,n_forced*2)

完整指令如:

ffmpeg -i test.mp4 -force_key_frames "expr:gte(t,n_forced*2)" -strict -2 -c:a aac -c:v libx264 -hls_time 2 -f hls index.m3u8

4、m3u8 格式解析

完整的 m3u8 文件有三部分:

  • index.m3u8,保存視頻的基本信息和分段文件順序;
  • key,如果視頻加密,保存密鑰;
  • data文件,其他都是視頻的數據文件。

具體內容解析:

  • #EXTM3U,是文件開始
  • #EXT-X-VERSION,標識HLS的協議版本號;
  • #EXT-X-TARGETDURATION,表示每個視頻分段最大的時長(單位秒);
  • #EXT-X-MEDIA-SEQUENCE,表示播放列表第一個 URL 片段文件的序列號;
  • #EXT-X-PLAYLIST-TYPE,表明流媒體類型;
  • #EXT-X-KEY,加密方式,這裡加密方式為AES-128,同時指定IV,在解密時需要;
  • #EXTINF,表示其後 URL 指定的媒體片段時長(單位為秒)。

二、播放演示

HLS 的工作原理是把整個流分成一個個小的基於 HTTP 的文件來下載,每次只下載一些。

當媒體流正在播放時,客戶端可以選擇從許多不同的備用源中以不同的速率下載同樣的資源,允許流媒體會話適應不同的數據速率。

在開始一個流媒體會話時,客戶端會下載一個包含元數據的 extended M3U (m3u8) playlist文件,用於尋找可用的媒體流。

HLS 只請求基本的 HTTP 報文,與實時傳輸協議(RTP)不同,HLS 可以穿過任何允許 HTTP 數據通過的防火牆或者代理伺服器。

它也很容易使用內容分髮網絡來傳輸媒體流。

video.js 播放 hls 示例

https://xushanxiang.com/demo/ffmpeg/video_hls.html

hls.js 播放示例

https://xushanxiang.com/demo/ffmpeg/hls_js.html

三、m3u8(ts) 合併為 MP4

遠程文件

ffmpeg -i “https://xushanxiang.com/demo/ffmpeg/hls265/output.m3u8” -vcodec copy -acodec copy -absf aac_adtstoasc output.mp4

本地文件

1、打開 cmd 2、輸入指令,按照文件的實際路徑合併 合併成 ts文件 copy /b  F:\f\*.ts  E:\f\new.ts 合併成 MP4 文件 copy /b  F:\f\*.ts  E:\f\new.MP4

而通過 ffmpeg 命令如下:

直接轉:
ffmpeg -i new.ts -c copy -map 0:v -map 0:a output.mp4

指定音頻流(一般用這個):
ffmpeg -i new.ts -c copy -map 0:v -map 0:a -bsf:a aac_adtstoasc output.mp4

重編碼視頻:
ffmpeg -y -i new.ts -c:v libx264 -c:a copy -bsf:a aac_adtstoasc output.mp4

php實現代碼

$url = 'https://******.m3u8?Expires=1585381145&OSSAccessKeyId=******&Signature=******';

$ts_content = file_get_contents($url);
$ts_content = explode(',', $ts_content);
$ts_file = array();

foreach ($ts_content as $key => $value) {
    if($key == 0) continue;
    $value = trim($value);
    $ts_file[] = substr($value, 0, strpos($value, '.ts') + 3);
}

$url_prefix = substr($url, 0, strpos($url, '.m3u8'));
$url_prefix = substr($url, 0, strrpos($url, '/') + 1);
$file_content = '';

foreach ($ts_file as $key => $value) {
    $file_content .= file_get_contents($url_prefix . $value);
}

file_put_contents('tmp_out.ts', $file_content);

// FFMPEG_PATH 是你自己解壓ffmpeg的bin路徑,例如我的是F:/ffmpeg/bin/
exec(FFMPEG_PATH . "ffmpeg -i tmp_out.ts tmp_out.mp4");

Python實現代碼

 目錄結構
./
  |-- m3u8.py
  |-- result
  |-- 文件1
    |-- key
    |-- index.m3u8
    |-- data...
  |-- 文件2
    |-- ...
import os
import sys
import time
from Crypto.Cipher import AES

def fileList(findex):
    rpath = os.path.dirname(os.path.realpath(findex))
    name = rpath.split("\\")[-1]
    fi = open(findex, 'r')
    flag = False
    IV = None
    tl = []
    for line in fi.readlines():
        if line.startswith("#EXT-X-KEY"):
            # 如果存在 IV 則提取;
            if line.split(",")[-1].startswith("IV="):
                IV = line.split(",")[-1][5:]
                IV = bytes.fromhex(IV)
        if line.startswith("#EXTINF"):
            flag = not flag
            continue
        if flag:
            tmp = line.strip().split("/")[-1]
            tmp = os.path.join(rpath, tmp)
            tl.append(tmp)
            flag = not flag
    fi.close()
    fk = open(os.path.join(rpath, "key"), 'rb')
    key = fk.read()
    fk.close()
    return name, tl, key, IV

def aes_decode(data, key, IV):
    # 如果沒有指定 IV 值,則直接使用 key 值
    if not IV:
        IV = key
    cryptor = AES.new(key, AES.MODE_CBC, IV)
    plain_text = cryptor.decrypt(data)
    return plain_text

def main():
    fp = os.listdir()
    used = [s[:-4] for s in os.listdir("./result/")]
    for ind in fp:
        if not ind.isdigit():
            continue
        if ind in used:
            continue
        try: 
            name, fl, key, IV = fileList(os.path.join(ind, "index.m3u8"))
        except:
            print("-"*30)
            print("[-] Errot! file: ", ind)
            print("-"*30)
            continue
        print("[*] Begin process file: ", name)
        start = time.time()
        f = open(os.path.join("./result/", name+".mp4"), 'ab')
        for i in fl:
            with open(i, 'rb') as inf:
                data = inf.read()
                f.write(aes_decode(data, key, IV))
        f.close()
        print("[+] Sucessfully! Cost time: ", time.time()-start)

main()
   原文:使用ffmpeg把mp4與m3u8相互轉換的操作 (xushanxiang.com)
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • **導讀:**京東智能商客之推薦賣點是基於NLP的產品,目前已廣泛地助力和賦能於京東商城的各個平臺。今天和大家分享一下自然語言處理如何在工業界落地實現。主要圍繞以下5個方面展開: 推薦賣點技術背景 架構描述 核心AI技術 模型研發與實踐 產品的落地與回報 -- 01 推薦賣點技術背景 1. 什麼是推 ...
  • 大家見過這張圖嗎? Swami Chandrasekaran在2013年用地鐵圖來描述如何通過“一段旅程”來成為數據科學家 (鏈接:http://nirvacana.com/thoughts/2013/07/08/becoming-a-data-scientist/) 這個圖幫助很多人叩開了數據科學 ...
  • 解決MySQL 8.0在Linux環境下的安裝、初始化、配置。參考環境:MySQL Community Server 8.0.28;CentOS Linux release 7.9.2009。 ...
  • 本文收集了各種資料庫的SQL語句優化原理思路、技術要點和方法實操案例文檔!希望大家都能寫得一手好SQL、掌握資料庫高性能運行秘訣! ...
  • HarmonyOS Connect智能硬體開放生態即將步入富設備產業化時代!為了讓廣大開發者能搶先體驗鴻蒙智聯富設備開發,本期我們將為大家帶來七款支持富設備開發的開發板。 ...
  • 精準推送是移動端產品留存階段的主要運營手段,精準推送常常會與用戶畫像緊密結合,針對用戶的喜好、畫像,採用不同策略,但基於用戶所屬區域推送消息卻很難實現。目前市面上大多數第三方消息推送服務商,在系統未深度定製的情況下,通常不支持將推送人群範圍精確到某個商圈或較小的區域,而地理圍欄技術可以很好地彌補這一 ...
  • 前言 本文主要是整理了使用WebRTC做音視頻通訊時的各知識點及問題點。有理解不足和不到位的地方也歡迎指正。 對於你感興趣的部分可以選擇性觀看。 WebRTC的初始化 在使用WebRTC的庫之前,需要對WebRTC進行初始化, 用到的代碼如下: RTCInitializeSSL(); 轉定義後可以看 ...
  • 今天的內容有意思了,朋友們繼續對我們之前的案例完善,是這樣的我們之前是不是靠props來完成父給子,子給父之間傳數據,其實父給子最好的方法就是props但是自給父就不是了,並且今天學下來,不僅如此,組件間任何層級的關係我都可以傳數據了,兄弟之間,爺孫之間等等等等 七.瀏覽器本地存儲 1.localS ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文將以 C# 語言來實現一個簡單的布隆過濾器,為簡化說明,設計得很簡單,僅供學習使用。 感謝@時總百忙之中的指導。 布隆過濾器簡介 布隆過濾器(Bloom filter)是一種特殊的 Hash Table,能夠以較小的存儲空間較快地判斷出數據是否存在。常用於允許一定誤判率的數據過濾及防止緩存 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 「簡單有價值的事情長期堅持做」 這是成功最簡單,但也最難學的秘訣。不經過訓練,人很難意識到時間複利的威力。 仙劍奇俠傳的「十里坡劍神」和金庸群俠傳的「十級野球拳」,就是簡單的事情持之以恆反覆做,最後就有巨大的威力 唐家三少成為網文收入第一,最重要的一步是十四年從未斷日更 這樣的案例很多,一開始可能成 ...
  • 迎面走來了你的面試官,身穿格子衫,挺著啤酒肚,髮際線嚴重後移的中年男子。 手拿泡著枸杞的保溫杯,胳膊夾著MacBook,MacBook上還貼著公司標語:“我愛加班”。 面試開始,直入正題。 面試官: 看你簡歷上面寫著精通MySQL,我先問你事務的特性是什麼? 老生常談,這個還有誰不會背的嗎? 我: ...
  • 基礎知識 python是一門腳本語言,它是解釋執行的。 python使用縮進做為語法,而且python2環境下同一個py文件中不能同時存在tab和空格縮進,否則會出錯,建議在IDE中顯示縮進符。 python在聲明變數時不寫數據類型,可以type(xx)來獲取欄位的類型,然後可以int(),list ...
  • 為什麼要多線程下載 俗話說要以終為始,那麼我們首先要明確多線程下載的目標是什麼,不外乎是為了更快的下載文件。那麼問題來了,多線程下載文件相比於單線程是不是更快? 對於這個問題可以看下圖。 橫坐標是線程數,縱坐標是使用對應線程數下載對應文件時花費的時間,藍橙綠代表下載文件的大小,每個線程下載對應文件2 ...
  • 詳細講解python爬蟲代碼,爬微博搜索結果的博文數據。 爬取欄位: 頁碼、微博id、微博bid、微博作者、發佈時間、微博內容、轉發數、評論數、點贊數。 爬蟲技術: 1、requests 發送請求 2、datetime 時間格式轉換 3、jsonpath 快速解析json數據 4、re 正則表達式提... ...
  • 背景: 一般我們可以用HashMap做本地緩存,但是HashMap功能比較弱,不支持Key過期,不支持數據範圍查找等。故在此實現了一個簡易的本地緩存,取名叫fastmap。 功能: 1.支持數據過期 2.支持等值查找 3.支持範圍查找 4.支持key排序 實現思路: 1.等值查找採用HashMap2 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 本章是系列文章的第八章,用著色演算法進行寄存器的分配過程。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 寄存器分配 寄存器分配是為程式處理的值找到存儲位置的問題 這些值可以存放到寄存器,也可以存放在記憶體中 寄存器更快,但數量有限 記憶體很多,但 ...