樹莓派使用python-librtmp實現(xiàn)rtmp推流h264的方法
目的是能使用Python進行rtmp推流,方便在h264幀里加入彈幕等操作。
librtmp使用的是0.3.0,使用樹莓派noir官方攝像頭適配的。
通過wireshark抓ffmpeg的包一點點改動,最終可以在red5和斗魚上推流了。
沒怎么寫過python,有不恰當?shù)牡胤秸埌?/p>
上代碼:
# -- coding: utf-8 -- # http://blog.csdn.net/luhanglei import picamera import time import traceback import ctypes from librtmp import * global meta_packet global start_time class Writer(): # camera可以通過一個類文件的對象來輸出,實現(xiàn)write方法即可 conn = None # rtmp連接 sps = None # 記錄sps幀,發(fā)過以后就不需要再發(fā)了(抓包看到ffmpeg是這樣的) pps = None # 同上 sps_len = 0 # 同上 pps_len = 0 # 同上 time_stamp = 0 def __init__(self, conn): self.conn = conn def write(self, data): try: # 尋找h264幀間隔符 indexs = [] index = 0 data_len = len(data) while index < data_len - 3: if ord(data[index]) == 0x00 and ord(data[index + 1]) == 0x00 and ord( data[index + 2]) == 0x00 and ord(data[index + 3]) == 0x01: indexs.append(index) index = index + 3 index = index + 1 # 尋找h264幀間隔符 完成 # 通過間隔符個數(shù)確定類型,樹莓派攝像頭的第一幀是sps+pps同時發(fā)的 if len(indexs) == 1: # 非sps pps幀 buf = data[4: len(data)] # 裁掉原來的頭(00 00 00 01),把幀內容拿出來 buf_len = len(buf) type = ord(buf[0]) & 0x1f if type == 0x05: # 關鍵幀,根據(jù)wire shark抓包結果,需要拼裝sps pps 幀內容 三部分,長度都用4個字節(jié)表示 body0 = 0x17 data_body_array = [bytes(bytearray( [body0, 0x01, 0x00, 0x00, 0x00, (self.sps_len >> 24) & 0xff, (self.sps_len >> 16) & 0xff, (self.sps_len >> 8) & 0xff, self.sps_len & 0xff])), self.sps, bytes(bytearray( [(self.pps_len >> 24) & 0xff, (self.pps_len >> 16) & 0xff, (self.pps_len >> 8) & 0xff, self.pps_len & 0xff])), self.pps, bytes(bytearray( [(buf_len >> 24) & 0xff, (buf_len >> 16) & 0xff, (buf_len >> 8) & 0xff, (buf_len) & 0xff])), buf ] mbody = ''.join(data_body_array) time_stamp = 0 # 第一次發(fā)出的時候,發(fā)時間戳0,此后發(fā)真時間戳 if self.time_stamp != 0: time_stamp = int((time.time() - start_time) * 1000) packet_body = RTMPPacket(type=PACKET_TYPE_VIDEO, format=PACKET_SIZE_LARGE, channel=0x06, timestamp=time_stamp, body=mbody) packet_body.packet.m_nInfoField2 = 1 else: # 非關鍵幀 body0 = 0x27 data_body_array = [bytes(bytearray( [body0, 0x01, 0x00, 0x00, 0x00, (buf_len >> 24) & 0xff, (buf_len >> 16) & 0xff, (buf_len >> 8) & 0xff, (buf_len) & 0xff])), buf] mbody = ''.join(data_body_array) # if (self.time_stamp == 0): self.time_stamp = int((time.time() - start_time) * 1000) packet_body = RTMPPacket(type=PACKET_TYPE_VIDEO, format=PACKET_SIZE_MEDIUM, channel=0x06, timestamp=self.time_stamp, body=mbody) self.conn.send_packet(packet_body) elif len(indexs) == 2: # sps pps幀 if self.sps is not None: return data_body_array = [bytes(bytearray([0x17, 0x00, 0x00, 0x00, 0x00, 0x01]))] sps = data[indexs[0] + 4: indexs[1]] sps_len = len(sps) pps = data[indexs[1] + 4: len(data)] pps_len = len(pps) self.sps = sps self.sps_len = sps_len self.pps = pps self.pps_len = pps_len data_body_array.append(sps[1:4]) data_body_array.append(bytes(bytearray([0xff, 0xe1, (sps_len >> 8) & 0xff, sps_len & 0xff]))) data_body_array.append(sps) data_body_array.append(bytes(bytearray([0x01, (pps_len >> 8) & 0xff, pps_len & 0xff]))) data_body_array.append(pps) data_body = ''.join(data_body_array) body_packet = RTMPPacket(type=PACKET_TYPE_VIDEO, format=PACKET_SIZE_LARGE, channel=0x06, timestamp=0, body=data_body) body_packet.packet.m_nInfoField2 = 1 self.conn.send_packet(meta_packet, queue=True) self.conn.send_packet(body_packet, queue=True) except Exception, e: traceback.print_exc() def flush(self): pass def get_property_string(string): # 返回兩字節(jié)string長度及string length = len(string) return ''.join([chr((length >> 8) & 0xff), chr(length & 0xff), string]) def get_meta_string(string): # 按照meta packet要求格式返回bytes,帶02前綴 return ''.join([chr(0x02), get_property_string(string)]) def get_meta_double(db): nums = [0x00] fp = ctypes.pointer(ctypes.c_double(db)) cp = ctypes.cast(fp, ctypes.POINTER(ctypes.c_longlong)) for i in range(7, -1, -1): nums.append((cp.contents.value >> (i * 8)) & 0xff) return ''.join(bytes(bytearray(nums))) def get_meta_boolean(isTrue): nums = [0x01] if (isTrue): nums.append(0x01) else: nums.append(0x00) return ''.join(bytes(bytearray(nums))) conn = RTMP( 'rtmp://192.168.199.154/oflaDemo/test', # 推流地址 live=True) librtmp.RTMP_EnableWrite(conn.rtmp) conn.connect() start_time = time.time() # 拼裝視頻格式的數(shù)據(jù)包 meta_body_array = [get_meta_string('@setDataFrame'), get_meta_string('onMetaData'), bytes(bytearray([0x08, 0x00, 0x00, 0x00, 0x06])), # 兩個字符串和ECMA array頭,共計6個元素,注釋掉了音頻相關數(shù)據(jù) get_property_string('width'), get_meta_double(640.0), get_property_string('height'), get_meta_double(480.0), get_property_string('videodatarate'), get_meta_double(0.0), get_property_string('framerate'), get_meta_double(25.0), get_property_string('videocodecid'), get_meta_double(7.0), # get_property_string('audiodatarate'), get_meta_double(125.0), # get_property_string('audiosamplerate'), get_meta_double(44100.0), # get_property_string('audiosamplesize'), get_meta_double(16.0), # get_property_string('stereo'), get_meta_boolean(True), # get_property_string('audiocodecid'), get_meta_double(10.0), get_property_string('encoder'), get_meta_string('Lavf57.56.101'), bytes(bytearray([0x00, 0x00, 0x09])) ] meta_body = ''.join(meta_body_array) print meta_body.encode('hex') meta_packet = RTMPPacket(type=PACKET_TYPE_INFO, format=PACKET_SIZE_LARGE, channel=0x04, timestamp=0, body=meta_body) meta_packet.packet.m_nInfoField2 = 1 # 修改stream id stream = conn.create_stream(writeable=True) with picamera.PiCamera() as camera: camera.start_preview() time.sleep(2) camera.start_recording(Writer(conn), format='h264', resize=(640, 480), intra_period=25, quality=25) # 開始錄制,數(shù)據(jù)輸出到Writer的對象里 while True:#永遠不停止 time.sleep(60) camera.stop_recording() camera.stop_preview()
以上這篇樹莓派使用python-librtmp實現(xiàn)rtmp推流h264的方法就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
python正則表達式修復網站文章字體不統(tǒng)一的解決方法
python正則表達式修復網站文章字體不統(tǒng)一的解決方法,需要的朋友可以參考一下2013-02-02Flask框架實現(xiàn)給視圖函數(shù)增加裝飾器操作示例
這篇文章主要介紹了Flask框架實現(xiàn)給視圖函數(shù)增加裝飾器操作,結合實例形式分析了flask框架視圖添加裝飾器的具體操作方法及相關注意事項,需要的朋友可以參考下2018-07-07Python創(chuàng)建7種不同的文件格式的方法總結
今天的這篇文章呢,小編來介紹一下如何通過Python來創(chuàng)建各種形式的文件,這里包括了:文本文件、CSV文件、Excel文件、壓縮文件、XML文件、JSON文件和PDF文件,需要的可以參考一下2023-01-01Python函數(shù)之iterrows(),iteritems(),itertuples()的區(qū)別說明
這篇文章主要介紹了Python函數(shù)之iterrows(),iteritems(),itertuples()的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05Pandas庫中isnull函數(shù)的實現(xiàn)
isnull()是Pandas庫中DataFrame和Series對象的一個函數(shù),用于檢測數(shù)據(jù)中的缺失值,本文主要介紹了Pandas庫中isnull函數(shù)的實現(xiàn),具有一定參考價值,感興趣的可以了解一下2024-07-07Python讀取txt文件數(shù)據(jù)的方法(用于接口自動化參數(shù)化數(shù)據(jù))
這篇文章主要介紹了Python讀取txt文件數(shù)據(jù)的方法(用于接口自動化參數(shù)化數(shù)據(jù)),需要的朋友可以參考下2018-06-06