#coding: utf-8
import binascii
import json
import re
import socket
import time

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                              # 时间搓偏移，推理完成后设置为最后一帧的时间搓值；循环推流的时候需要加上该值


    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


    ####################################################
    #      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
        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
                                else:
                                    avTimeStamp = self.getAVTimeStamp(tag) + self.timeStampSeek # 获取时间戳
                                    timeStamp = avTimeStamp
                                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)
                                elif tagType == "12":                  # 脚本
                                    AVdata = AVtag                     # 脚本数据
                                else:
                                    AVdata = AVtag
                                data = data[(tagSize * 2 + 30):]
                    else:
                        time.sleep(1)
        self.pushStatusText.SetValue("已连网")
        timeCur = self.getCurTime()
        self.multiPushStatus = self.multiPushStatus + 1
        self.timeStampSeek = self.timeStampSeek + timeStamp + 5000
        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):
        protocal_1078 = Protocal_1078()
        protocal_1078.setSim(self.mobile)
        protocal_1078.setLogcC(self.channel)
        protocal_1078.setTime(timeStamp)
        frameType = fra[8:10]
        if frameType == "65":
            self.videoLastKeyTime = timeStamp
            protocal_1078.setLastKeyTime(timeStamp - self.videoLastKeyTime)
            protocal_1078.setLastTime(timeStamp - self.videoLastTime)
            self.videoLastTime = timeStamp
        else:
            protocal_1078.setLastKeyTime(timeStamp - self.videoLastKeyTime)
            protocal_1078.setLastTime(timeStamp - self.videoLastTime)
            self.videoLastTime = timeStamp
        if len(fra) % 2 != 0:  # 处理有问题的帧数据
            print("出现了视频问题帧，并自动修复...")
            fra = fra[:len(fra) - 1]
        protocal_1078.setDataBody(fra)
        pkgs = protocal_1078.genPkgsByFrame()
        for msg in pkgs:
            # print("发送视频消息：" + msg)
            self.client.send(binascii.a2b_hex(msg))
            time.sleep(self.sendDur)

    ####################################################
    #      发送音频帧
    ####################################################
    def sendAudioFrame(self,fra,timeStamp):
        protocal_1078 = Protocal_1078()
        protocal_1078.setSim(self.mobile)
        protocal_1078.setLogcC(self.channel)
        protocal_1078.setTime(timeStamp)
        if len(fra) % 2 != 0:  # 处理有问题的帧数据
            print("出现了视频问题帧，并自动修复...")
            fra = fra[:len(fra) - 1]
        protocal_1078.setDataBody(fra)
        pkgs = protocal_1078.genAudioPkgsByFrame()
        for msg in pkgs:
            # 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 = 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

    ####################################################
    #     根据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.001)
    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/yyy.flv")
    obj.connectServer()
    obj.readFlvAndSend()



