目的是能使用Python進行rtmp推流,方便在h264幀里加入彈幕等操作。
librtmp使用的是0.3.0,使用樹莓派noir官方攝像頭適配的。
通過wireshark抓ffmpeg的包一點點改動,最終可以在red5和斗魚上推流了。
沒怎么寫過python,有不恰當的地方請包涵。
上代碼:
# -- 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可以通過一個類文件的對象來輸出,實現write方法即可 conn = None # rtmp連接 sps = None # 記錄sps幀,發過以后就不需要再發了(抓包看到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幀間隔符 完成 # 通過間隔符個數確定類型,樹莓派攝像頭的第一幀是sps+pps同時發的 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: # 關鍵幀,根據wire shark抓包結果,需要拼裝sps pps 幀內容 三部分,長度都用4個字節表示 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 # 第一次發出的時候,發時間戳0,此后發真時間戳 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): # 返回兩字節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() # 拼裝視頻格式的數據包 meta_body_array = [get_meta_string('@setDataFrame'), get_meta_string('onMetaData'), bytes(bytearray([0x08, 0x00, 0x00, 0x00, 0x06])), # 兩個字符串和ECMA array頭,共計6個元素,注釋掉了音頻相關數據 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) # 開始錄制,數據輸出到Writer的對象里 while True:#永遠不停止 time.sleep(60) camera.stop_recording() camera.stop_preview()
以上這篇樹莓派使用python-librtmp實現rtmp推流h264的方法就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
