侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

树莓派使用python-librtmp实现rtmp推流h264的方法

Python  /  管理员 发布于 7年前   285

目的是能使用Python进行rtmp推流,方便在h264帧里加入弹幕等操作。

librtmp使用的是0.3.0,使用树莓派noir官方摄像头适配的。

通过wireshark抓ffmpeg的包一点点改动,最终可以在red5和斗鱼上推流了。

没怎么写过python,有不恰当的地方请包涵。

上代码:

# -- coding: utf-8 --# http://blog.csdn.net/luhangleiimport picameraimport timeimport tracebackimport ctypesfrom librtmp import * global meta_packetglobal 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 idstream = 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的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


  • 上一条:
    python判断一个对象是否可迭代的例子
    下一条:
    python实现大文件分割与合并
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客