Python實現FLV視訊拼接功能
文章摘要
本文簡單說明了FLV檔案的格式,以此為出發點,使用 Python 實現FLV視訊的拼接。
一.FLV檔案格式
關於FLV檔案格式的解析網上有諸多文章,在這裡就簡單介紹一下需要了解的部分,以便讀者更好地明白各段程式碼的功能。
FLV檔案是由檔案頭(Header)和檔案體(Body)按順序拼接而成。審查FLV內容時,以二進位制方式讀取內容。
Header:檔案頭表明了檔案的封裝格式為FLV,儲存物件為音訊、視訊或兩者。
以下為FLV檔案的Header,共 9 個位元組:
b'FLV\x01\x05\x00\x00\x00\t'
前 3 個位元組(FLV)說明這是一個FLV檔案
第 4 個位元組(\x01)為版本號,固定為 1第 5 個位元組(\x05)表明儲存物件,需將其轉化成二進位制(00000101)檢視,左、右邊的 1 分別表示檔案含有音訊和視訊
後 4 個位元組(\x00\x00\x00\t)表示檔案頭的長度,其值固定為 9
Body:檔案體由若干個 Tag 組成,除了第一個,每個 Tag 是由頭部( 11 位元組)、主體(不定長)和尾部( 4 位元組)組成。第一個 Tag 只有尾部。
Tag 又分為 3 類,指令碼(scripts)、音訊(audio)和視訊(video)。通常第 2 個 Tag 為指令碼型別,且只有一個,後續的都是音視訊型別。
以下為指令碼 Tag 的部分,作為示例介紹一下:
頭部:b'\x12\x00\tb\x00\x00\x00\x00\x00\x00\x00'
第 1 個位元組(\x12)表示 Tag 型別,指令碼型別的對應值為 18 ,音訊為 8 ,視訊為 9
第 2-4 個位元組(\x00\tb)表示 Tag 主體的長度,此處為 2402
第 5-7 個位元組(\x00\x00\x00)為時間戳,指令碼型別的時間戳通常為 0
第 8 個位元組(\x00)是時間戳的擴充套件,當前 3 個位元組不夠用時會用這個位元組當作大端
後 3 個位元組(\x00\x00\x00)是 Stream id,固定為 0
主體:指令碼 Tag 的主體包含FLV視訊的基本資訊,如時長、大小、解析度等,比較複雜,在此不作介紹
尾部:b'\x00\x00\tm'
固定 4 位元組,表示 Tag 頭部加主體的長度,即 11 + 2402 = 2413
二.FLV視訊拼接
將多個FLV視訊合成一個可以正常播放的視訊,便足夠滿足大部分的需求。因此,在接下來的拼接過程中,不會對FLV進行細緻入微的調整,達到基本要求即可。
設定閱讀器
閱讀器可以使我們很方便地讀取檔案內容。
class Reader(): def __init__(self,content): # content (bytes):FLV檔案的二進位制內容 self.content = content self.start = 0 self.eof = False # 判斷是否已讀完全部內容 self.length = len(self.content) def read(self,n=1): # 設定 if 語句防止過度讀取內容 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
在這裡假設要拼接的視訊基本資訊相似,即都含有音視訊,解析度、位元速率等相同或相近。
為了生成一個可以正常播放的FLV視訊,Header 和 Tag 是必不可少的。我們可以選取第一個FLV的檔案頭寫入新建FLV中,然後依次將修改過時間戳的 Tag 寫入其中,便可達到拼接目的。
def add_flv(flv,target,videoTimeStamp,audioTimeStamp): # 修改並新增 Tag 的函式 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: # 一直讀取直到讀完,此時 reader.eof = True dataType = reader.read(1) dataSize = reader.read(3) timeStamp = int.from_bytes(reader.read(3),'big') # 將 3 位元組轉換成整數 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') # 將整數轉換成 3 位元組 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): # 主函式 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 個FLV視訊的檔案頭 with open(target,'wb') as f: f.write(header) videoTS,audioTS = add_flv(flv,videoTS,audioTS)
拼接
import time since = time.time() flvs = ['m1.flv','m2.flv','m3.flv','m4.flv'] # 視訊大小:45MB,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 個共 178MB視訊用時 0.88 秒。
總結
FLV檔案格式還是比較簡明的,對資料的要求也是比較寬鬆的,即便沒有對 Scripts 裡的引數作調整,拼接後的視訊依然能夠正常播放。
不過,拼接的視訊是有不少隱形問題,如到視訊末尾可能會出現音畫不同步( 0.5 秒左右)的現象,以及不能夠方便地分離出完整的視訊和音訊。
以上所述是小編給大家介紹的Python實現FLV視訊拼接功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對我們網站的支援!
如果你覺得本文對你有幫助,歡迎轉載,煩請註明出處,謝謝!