Commit 6bf4e020 authored by liyuanhong's avatar liyuanhong

小优化

parent 84c722f5
#coding: utf-8
import binascii
import json
import re
import time
import traceback
import requests
from lib.protocal.Protocal_1078 import Protocal_1078
from lib.socket.ClientSocket import ClientSocket
class StreamH264Flv():
def __init__(self):
self.isFileHead = 0 # 读取文件的时候,是否是文件的开头 0:不是 1:是
self.readSize = 1024 # 读取文件的大小
self.videoLastKeyTime = 0 # Last I Frame Interval 该祯与上一个关键祯之间的时间间隔,单位毫秒(ms),当数据类型为非视频祯时,则没有该字段
self.videoLastTime = 0 # Last Frame Interval 该祯与上一个祯之间的时间时间,单位毫秒(ms),当数据类型为非视频祯时,则没有该字段
self.sendDur = 0.003 # 每发送一帧的间隔时间
self.client = None
self.mobile = "013146201117" # 手机号
self.channel = 1 # 频道号
self.isSendAudio = 1 # 是否发送音频0:不发送 1:发送
self.isSend = 0 # 是否发送消息 0:不发送 1:发送
self.isEndPush = 0 # 是否结束推流 0:不结束 1:结束
# self.host = "10.100.11.125" # 开发环境
self.host = "10.100.12.3" # 测试环境
self.port = 1078
self.videoPath = "../../flv/bbb3.flv" # 要推流的视频路劲
self.getGetPlayUrl = "http://10.100.11.110:9999/video/streamInfo" # 获取推流播放地址的url
# self.getGetPlayUrl = "http://fb-test.vandyo.com/video/streamInfo" # 获取推流播放地址的url
self.isGetPlayUrl = 0 # 是否已经获取了playUrl 0:没有 1:获取了
self.playUrl = "" # 视频播放地址
self.mainframe = None # 图形界面主窗体,用于弹出对话框
self.logTextCtr = None # 日志输出框,用于输出日志
self.pushStatusText = None # 状态显示窗
self.multiPushStatus = 0 # 多次推流标记, 0:该连接第一次推流 1:该连接第一次对流 2: 该连接第二次对流
self.timeStampSeek = 0 # 时间搓偏移,推理完成后设置为最后一帧的时间搓值;循环推流的时候需要加上该值
self.isOpenVideoLog = 0 # 是否开启视频日志 0:不开启 1:开启
self.isOpenAudioLog = 0 # 是否开启音频日志 0:不开启 1:开启
self.protocal_1078 = Protocal_1078()
def setMobile(self,data):
self.mobile = data
def setChannel(self,data):
self.channel = data
def setReadSize(self,data):
self.readSize = data
def setIsSendAudio(self,data):
self.isSendAudio = data
def setHost(self,data):
self.host = data
def setPort(self,data):
self.port = data
def setSendDur(self,data):
self.sendDur = data
def setVideoPath(self,data):
self.videoPath = data
def setIsSend(self,data):
self.isSend = data
def setIsEndPush(self,data):
self.isEndPush = data
def setMainFrame(self,data):
self.mainframe = data
def setLogTextCtr(self,data):
self.logTextCtr = data
def setPushStatusText(self,data):
self.pushStatusText = data
def setIsOpenVideoLog(self,data):
self.isOpenVideoLog = data
def setIsOpenAudioLog(self,data):
self.isOpenAudioLog = data
####################################################
# socket 连接
####################################################
def connectServer(self):
socketObj = ClientSocket()
socketObj.setHost(self.host)
socketObj.setPort(self.port)
self.client = socketObj.connect()
# self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# self.client.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # 在客户端开启心跳
# self.client.connect((self.host, self.port))
####################################################
# socket 断开
####################################################
def disConnectServer(self):
self.client.close()
####################################################
# 读取flv文件,并发送 (h264编码,并且音频为aac编码格式)
####################################################
def readFlvAndSend(self):
self.isSend = 1 # 控制暂停或继续推流
self.isEndPush = 0
self.isFileHead = 0
timeStamp = 0
doCount = 0 # map 缓存
frameMap = {}
with open(self.videoPath, 'rb') as fi:
con = fi.read(self.readSize)
data = con.hex()
if self.isFileHead == 0:
data = data[18:]
self.isFileHead = 1
else:
data = data
while con:
if self.isEndPush != 0:
break
else:
if self.isSend == 1:
if len(data) < 30:
con = fi.read(self.readSize)
data = data + con.hex()
else:
tagSize = self.getAVTagSize(data)
if len(data) < (tagSize * 2 + 30):
con = fi.read(self.readSize)
data = data + con.hex()
else:
tag = data[:tagSize * 2 + 30] # flv tag
tagType = self.getTagType(tag) # flv tag 类型,是音频 还是视频
AVtag = tag[30:] # 音视频 tag
if self.multiPushStatus < 1:
avTimeStamp = self.getAVTimeStamp(tag) #获取时间戳
timeStamp = avTimeStamp
if tagType == "09":
timeStamp = timeStamp + self.getCompositionTime(AVtag)
else:
avTimeStamp = self.getAVTimeStamp(tag) + self.timeStampSeek # 获取时间戳
timeStamp = avTimeStamp
if tagType == "09":
timeStamp = timeStamp + self.getCompositionTime(AVtag)
if doCount < 30:
frameMap[timeStamp] = AVtag
doCount = doCount + 1
else:
frameMap[timeStamp] = AVtag
frameArr = sorted(frameMap.keys(),reverse = True)
AVtag = frameMap.pop(frameArr.pop())
if avTimeStamp > 100:
if self.isGetPlayUrl == 0:
self.getPlayUrl(self.mobile,self.channel)
self.isGetPlayUrl = 1
if tagType == "08": # 音频
AVdata = AVtag[4:] # 音频数据
if self.isSendAudio == 1:
frameInfo = self.getAudioFrame(AVtag)
for frame in frameInfo["frames"]:
self.sendAudioFrame(frame,avTimeStamp)
elif tagType == "09": # 视频
AVdata = AVtag[10:] # 视频数据
frameInfo = self.getVideoFrame(AVtag)
for frame in frameInfo["frames"]:
self.sendVideoFrame(frame,avTimeStamp,AVtag)
elif tagType == "12": # 脚本
AVdata = AVtag # 脚本数据
else:
AVdata = AVtag
data = data[(tagSize * 2 + 30):]
else:
time.sleep(1)
while len(frameMap) > 0:
frameArr = sorted(frameMap.keys(), reverse=True)
AVtag = frameMap.pop(frameArr.pop())
if avTimeStamp > 100:
if self.isGetPlayUrl == 0:
self.getPlayUrl(self.mobile, self.channel)
self.isGetPlayUrl = 1
if tagType == "08": # 音频
AVdata = AVtag[4:] # 音频数据
if self.isSendAudio == 1:
frameInfo = self.getAudioFrame(AVtag)
for frame in frameInfo["frames"]:
self.sendAudioFrame(frame, avTimeStamp)
elif tagType == "09": # 视频
AVdata = AVtag[10:] # 视频数据
frameInfo = self.getVideoFrame(AVtag)
for frame in frameInfo["frames"]:
self.sendVideoFrame(frame, avTimeStamp, AVtag)
elif tagType == "12": # 脚本
AVdata = AVtag # 脚本数据
else:
AVdata = AVtag
data = data[(tagSize * 2 + 30):]
self.pushStatusText.SetValue("已连网")
timeCur = self.getCurTime()
self.multiPushStatus = self.multiPushStatus + 1
self.timeStampSeek = timeStamp + 1000
self.logTextCtr.WriteText(timeCur + "视频推流完,自动结束推流!\n")
# self.client.close()
####################################################
# 停止发送消息
####################################################
def stopSend(self):
self.isSend = 0
####################################################
# 获取视频播放地址
####################################################
def getPlayUrl(self,mobile,channel):
res = requests.post(self.getGetPlayUrl, data=json.dumps({'devId': mobile,'chan': str(channel)})).text
res = json.loads(res)
url = res["data"]["url"]
# url = self.replaceHost(res["data"]["url"],"video-test.vandyo.com:")
print("播放地址:" + url)
####################################################
# 替换url的host
####################################################
def replaceHost(self,url,replaceWord):
data = url
strinfo = re.compile('(\d+.\d+.\d+.\d+.:)')
result = strinfo.sub(replaceWord, data)
return result
####################################################
# 发送视频帧
####################################################
def sendVideoFrame(self,fra,timeStamp,avTag):
self.protocal_1078.setSim(self.mobile)
self.protocal_1078.setLogcC(self.channel)
# avTimeStamp = timeStamp + self.getCompositionTime(avTag)
self.protocal_1078.setTime(timeStamp)
frameType = fra[8:10]
if frameType == "65":
self.videoLastKeyTime = timeStamp
self.protocal_1078.setLastKeyTime(timeStamp - self.videoLastKeyTime)
self.protocal_1078.setLastTime(timeStamp - self.videoLastTime)
self.videoLastTime = timeStamp
else:
self.protocal_1078.setLastKeyTime(timeStamp - self.videoLastKeyTime)
self.protocal_1078.setLastTime(timeStamp - self.videoLastTime)
self.videoLastTime = timeStamp
if len(fra) % 2 != 0: # 处理有问题的帧数据
print("出现了视频问题帧,并自动修复...")
fra = fra[:len(fra) - 1]
self.protocal_1078.setDataBody(fra)
pkgs = self.protocal_1078.genPkgsByFrame()
for msg in pkgs:
if self.isOpenVideoLog != 0:
print("发送视频消息:" + msg)
try:
self.client.send(binascii.a2b_hex(msg))
except:
traceback.print_exc()
print(msg)
time.sleep(self.sendDur)
####################################################
# 发送音频帧
####################################################
def sendAudioFrame(self,fra,timeStamp):
self.protocal_1078.setSim(self.mobile)
self.protocal_1078.setLogcC(self.channel)
self.protocal_1078.setTime(timeStamp)
if len(fra) % 2 != 0: # 处理有问题的帧数据
print("出现了视频问题帧,并自动修复...")
fra = fra[:len(fra) - 1]
self.protocal_1078.setDataBody(fra)
pkgs = self.protocal_1078.genAudioPkgsByFrame()
for msg in pkgs:
if self.isOpenAudioLog != 0:
print("发送音频消息:" + msg)
self.client.send(binascii.a2b_hex(msg))
time.sleep(self.sendDur)
####################################################
# 获取TagType
# 0x08 音频
# 0x09 视频
# 0x12 脚本
####################################################
def getTagType(self,tag):
tagType = tag[8:10]
return tagType
####################################################
# 获取 flv tag 的数据长度,其实如图里 audio tag 头及其数据长度
####################################################
def getAVTagSize(self,tag):
tagSize = tag[10:16]
tagSize = int(tagSize,16)
return tagSize
####################################################
# 获取相对时间戳 (音视频相对时间)
####################################################
def getAVTimeStamp(self,tag):
timeStamp = tag[16:22]
timeStamp = tag[22:24] + timeStamp
timeStamp = int(timeStamp,16)
return timeStamp
####################################################
# 获取音视频数据 (音视频头 + 数据部分)
####################################################
def getAVData(self,tag,tagSize):
flvData = tag[34:(tagSize * 2)]
lastData = tag[(tagSize * 2):]
return flvData,lastData
####################################################
# 根据AVtag,获取视频帧数据
####################################################
def getVideoFrame(self,AVTag):
frameData = {}
frames = []
# frameData["frameType"] = int(AVTag[:2],16) >> 4 # 视频帧类型
frameData["codeId"] = int(AVTag[:2],16) & 7 # 编码id ,7 为h264编码 (程序里默认都是h264)
frameData["AVCPkgType"] = int(AVTag[2:4],16)
if frameData["AVCPkgType"] == 0:
spsLen = int(AVTag[22:26], 16)
sps = "00000001" + AVTag[26:26 + spsLen * 2]
ppsLen = int(AVTag[26 + spsLen * 2 + 2:26 + spsLen * 2 + 6],16)
pps = "00000001" + AVTag[26 + spsLen * 2 + 6:26 + spsLen *2 + 6 + ppsLen * 2]
frames.append(sps)
frames.append(pps)
frameData["frames"] = frames
elif frameData["AVCPkgType"] == 1:
dataLen = int(AVTag[10:18], 16)
frame = "00000001" + AVTag[18:18 + dataLen * 2]
frames.append(frame)
lastData = AVTag[18 + dataLen * 2:]
if len(lastData) != 0:
dataLen_65 = int(AVTag[18 + dataLen * 2:18 + dataLen * 2 + 8],16)
frame_65 = "00000001" + AVTag[18 + dataLen * 2 + 8:]
frames.append(frame_65)
else:
pass
frameData["frames"] = frames
elif frameData["AVCPkgType"] == 2:
frameData["frames"] = []
# print("2222222----------" + AVTag)
else:
frameData["frames"] = []
return frameData
####################################################
# 获取视频帧的CompositionTime
####################################################
def getCompositionTime(self,AVTag):
AVCPkgType = int(AVTag[2:4], 16)
compositionTime = 0
if AVCPkgType == 0:
pass
else:
compositionTime = int(AVTag[4:10],16)
return compositionTime
####################################################
# 根据AVtag,获取音频帧数据
####################################################
def getAudioFrame(self,AVTag):
frameData = {}
frames = []
AVdata = AVTag[4:]
cp_id_bit = 0 << 27
cp_id_start = 0 << 26
aac_frame_length = int(len(AVdata) / 2 + 7) << 13
adts_buffer_funess = 2047 << 2
raw_data_frame = 0
infoAac = cp_id_bit + cp_id_start + aac_frame_length + adts_buffer_funess + raw_data_frame
infoAac = hex(infoAac)[2:]
while len(infoAac) < 7:
infoAac = "0" + infoAac
frame = "fff14c8" + infoAac + AVdata
frames.append(frame)
frameData["frames"] = frames
return frameData
####################################################
# 获取当前时间
####################################################
def getCurTime(self):
timeCur = int(time.time() * 1000)
timeArray = time.localtime(timeCur / 1000)
timeCur = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
timeCur = "[" + timeCur + "] "
return timeCur
if __name__ == "__main__":
obj = StreamH264Flv()
obj.setSendDur(0.003)
obj.setIsSendAudio(1)
# obj.setMobile("013146201117")
obj.setMobile("142043390091")
obj.setChannel(1)
obj.setHost("10.100.11.125")
# obj.setHost("video-test.vandyo.com")
obj.setPort(1078)
obj.setVideoPath("../../flv/aaa3.flv")
obj.connectServer()
obj.readFlvAndSend()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment