Python實(shí)現(xiàn)FLV視頻拼接功能
文章摘要
本文簡(jiǎn)單說(shuō)明了FLV文件的格式,以此為出發(fā)點(diǎn),使用 Python 實(shí)現(xiàn)FLV視頻的拼接。
一.FLV文件格式
關(guān)于FLV文件格式的解析網(wǎng)上有諸多文章,在這里就簡(jiǎn)單介紹一下需要了解的部分,以便讀者更好地明白各段代碼的功能。
FLV文件是由文件頭(Header)和文件體(Body)按順序拼接而成。審查FLV內(nèi)容時(shí),以二進(jìn)制方式讀取內(nèi)容。
Header:文件頭表明了文件的封裝格式為FLV,存儲(chǔ)對(duì)象為音頻、視頻或兩者。
以下為FLV文件的Header,共 9 個(gè)字節(jié):
b'FLV\x01\x05\x00\x00\x00\t'
前 3 個(gè)字節(jié)(FLV)說(shuō)明這是一個(gè)FLV文件
第 4 個(gè)字節(jié)(\x01)為版本號(hào),固定為 1
第 5 個(gè)字節(jié)(\x05)表明存儲(chǔ)對(duì)象,需將其轉(zhuǎn)化成二進(jìn)制(00000101)查看,左、右邊的 1 分別表示文件含有音頻和視頻
后 4 個(gè)字節(jié)(\x00\x00\x00\t)表示文件頭的長(zhǎng)度,其值固定為 9
Body:文件體由若干個(gè) Tag 組成,除了第一個(gè),每個(gè) Tag 是由頭部( 11 字節(jié))、主體(不定長(zhǎng))和尾部( 4 字節(jié))組成。第一個(gè) Tag 只有尾部。
Tag 又分為 3 類,腳本(scripts)、音頻(audio)和視頻(video)。通常第 2 個(gè) Tag 為腳本類型,且只有一個(gè),后續(xù)的都是音視頻類型。
以下為腳本 Tag 的部分,作為示例介紹一下:
頭部:b'\x12\x00\tb\x00\x00\x00\x00\x00\x00\x00'
第 1 個(gè)字節(jié)(\x12)表示 Tag 類型,腳本類型的對(duì)應(yīng)值為 18 ,音頻為 8 ,視頻為 9
第 2-4 個(gè)字節(jié)(\x00\tb)表示 Tag 主體的長(zhǎng)度,此處為 2402
第 5-7 個(gè)字節(jié)(\x00\x00\x00)為時(shí)間戳,腳本類型的時(shí)間戳通常為 0
第 8 個(gè)字節(jié)(\x00)是時(shí)間戳的擴(kuò)展,當(dāng)前 3 個(gè)字節(jié)不夠用時(shí)會(huì)用這個(gè)字節(jié)當(dāng)作大端
后 3 個(gè)字節(jié)(\x00\x00\x00)是 Stream id,固定為 0
主體:腳本 Tag 的主體包含F(xiàn)LV視頻的基本信息,如時(shí)長(zhǎng)、大小、分辨率等,比較復(fù)雜,在此不作介紹
尾部:b'\x00\x00\tm'
固定 4 字節(jié),表示 Tag 頭部加主體的長(zhǎng)度,即 11 + 2402 = 2413
二.FLV視頻拼接
將多個(gè)FLV視頻合成一個(gè)可以正常播放的視頻,便足夠滿足大部分的需求。因此,在接下來(lái)的拼接過(guò)程中,不會(huì)對(duì)FLV進(jìn)行細(xì)致入微的調(diào)整,達(dá)到基本要求即可。
設(shè)置閱讀器
閱讀器可以使我們很方便地讀取文件內(nèi)容。
class Reader():
def __init__(self, content): # content (bytes):FLV文件的二進(jìn)制內(nèi)容
self.content = content
self.start = 0
self.eof = False # 判斷是否已讀完全部?jī)?nèi)容
self.length = len(self.content)
def read(self, n=1):
# 設(shè)置 if 語(yǔ)句防止過(guò)度讀取內(nèi)容
if self.length > (self.start + n):
out = self.content[self.start:self.start + n]
self.start += n
else:
out = self.content[self.start:]
self.eof = True
return out
向新建FLV文件寫入 Header 和 Tag
在這里假設(shè)要拼接的視頻基本信息相似,即都含有音視頻,分辨率、碼率等相同或相近。
為了生成一個(gè)可以正常播放的FLV視頻,Header 和 Tag 是必不可少的。我們可以選取第一個(gè)FLV的文件頭寫入新建FLV中,然后依次將修改過(guò)時(shí)間戳的 Tag 寫入其中,便可達(dá)到拼接目的。
def add_flv(flv, target, videoTimeStamp, audioTimeStamp): # 修改并添加 Tag 的函數(shù)
with open(flv, 'rb') as f:
content = f.read()
reader = Reader(content)
header = reader.read(13)
with open(target, 'ab') as f:
while not reader.eof: # 一直讀取直到讀完,此時(shí) reader.eof = True
dataType = reader.read(1)
dataSize = reader.read(3)
timeStamp = int.from_bytes(reader.read(3), 'big') # 將 3 字節(jié)轉(zhuǎn)換成整數(shù)
headerRemained = reader.read(4)
if dataType == b'\t': # 視頻
timeStamp += videoTimeStamp
videoTS = timeStamp
if dataType == b'\x08': # 音頻
timeStamp += audioTimeStamp
audioTS = timeStamp
timeStamp = timeStamp.to_bytes(3, 'big') # 將整數(shù)轉(zhuǎn)換成 3 字節(jié)
tagHeader = dataType + dataSize + timeStamp + headerRemained
tagData_andSize = reader.read(int.from_bytes(dataSize, 'big') + 4)
f.write(tagHeader)
f.write(tagData_andSize)
return videoTS, audioTS
def merge_flv(flvs, target): # 主函數(shù)
videoTS = 0
audioTS = 0
for i, flv in enumerate(flvs):
with open(flv, 'rb') as f:
content = f.read()
reader = Reader(content)
header = reader.read(13) # flvHeader + tagSize0
if i == 0: # 寫入第 1 個(gè)FLV視頻的文件頭
with open(target, 'wb') as f:
f.write(header)
videoTS, audioTS = add_flv(flv, target, videoTS, audioTS)
拼接
import time
since = time.time()
flvs = ['m1.flv', 'm2.flv', 'm3.flv', 'm4.flv'] # 視頻大?。?5MB,20MB,59MB,54MB
target = 't.flv'
merge_flv(flvs, target)
end = time.time()
print('Merging flvs takes {:.2f} s'.format(end - since))
# Merging flvs takes 0.88 s
可以看到,拼接 4 個(gè)共 178MB視頻用時(shí) 0.88 秒。
總結(jié)
FLV文件格式還是比較簡(jiǎn)明的,對(duì)數(shù)據(jù)的要求也是比較寬松的,即便沒(méi)有對(duì) Scripts 里的參數(shù)作調(diào)整,拼接后的視頻依然能夠正常播放。
不過(guò),拼接的視頻是有不少隱形問(wèn)題,如到視頻末尾可能會(huì)出現(xiàn)音畫不同步( 0.5 秒左右)的現(xiàn)象,以及不能夠方便地分離出完整的視頻和音頻。
以上所述是小編給大家介紹的Python實(shí)現(xiàn)FLV視頻拼接功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
詳解python的網(wǎng)絡(luò)編程基礎(chǔ)
這篇文章主要為大家介紹了python網(wǎng)絡(luò)編程的基礎(chǔ),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01
用Python解析身份證號(hào)獲取年齡和性別的實(shí)現(xiàn)方法
身份證號(hào)碼包含了豐富的信息,包括生日和性別,Python提供了處理和解析身份證號(hào)的功能,讓我們能夠從中提取出相關(guān)的信息,本文將介紹如何利用Python解析身份證號(hào),獲取持有者的年齡和性別信息,感興趣的朋友可以參考下2023-12-12
Python使用pyinstaller打包spec文件的方法詳解
PyInstaller是一個(gè)用于將Python腳本打包成獨(dú)立的可執(zhí)行文件的工具,使用PyInstaller您可以將Python應(yīng)用程序轉(zhuǎn)換為可執(zhí)行文件,而無(wú)需用戶安裝Python解釋器或任何額外的庫(kù),這篇文章主要給大家介紹了關(guān)于Python使用pyinstaller打包spec文件的相關(guān)資料,需要的朋友可以參考下2024-08-08
python機(jī)器學(xué)習(xí)GCN圖卷積神經(jīng)網(wǎng)絡(luò)原理解析
這篇文章主要為大家介紹了GCN圖卷積神經(jīng)網(wǎng)絡(luò)原理及代碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
圖文詳解Python中如何簡(jiǎn)單地解決Microsoft?Visual?C++?14.0報(bào)錯(cuò)
有的時(shí)候安裝python依賴包的時(shí)候,報(bào)錯(cuò)信息"Microsoft?visual?c++?14.0?is?required"的解決辦法,下面這篇文章主要給大家介紹了關(guān)于Python中如何簡(jiǎn)單地解決Microsoft?Visual?C++?14.0報(bào)錯(cuò)的相關(guān)資料,需要的朋友可以參考下2023-02-02
tensorflow 變長(zhǎng)序列存儲(chǔ)實(shí)例
今天小編就為大家分享一篇tensorflow 變長(zhǎng)序列存儲(chǔ)實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-01-01
Python實(shí)現(xiàn)調(diào)用達(dá)夢(mèng)數(shù)據(jù)庫(kù)的教程分享
這篇文章主要為大家詳細(xì)介紹了Python是如何調(diào)用達(dá)夢(mèng)數(shù)據(jù)庫(kù)的,文中的示例代碼簡(jiǎn)潔易懂,具有一定的學(xué)習(xí)和參考價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-06-06

