#coding: utf-8
import binascii
import socket
import time
import logging

from lib.socket.ClientSocket import ClientSocket

logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

from lib.protocal.Protocal_1078 import Protocal_1078


class StreamH264():
    def __init__(self):
        self.readSize = 10240                   # 每次读取文件的字节大小 (视频)
        self.readSizeAudio = 1024               # 每次读取文件的字节大小 (音频频)
        self.filePos = 0.001                       # 文件的读取位置
        self.mobile = "013146201117"           # 手机号
        self.channel = 1                       # 频道号

        # self.host = "10.100.11.125"            # 开发环境
        self.host = "10.100.12.3"              # 测试环境
        self.port = 1078
        self.client = None
        self.BUF_SIZE = 2048
        self.sendDur = 0                       # 消息发送间隔
        self.AVChangeBySecond = 2              # 每秒音视频切换数

        self.pos = 1                          # 当前的帧数(视频)
        self.posAudio = 1                     # 当前的帧数(音频)
        self.frameCount = 1                   # 对发送的帧计数(视频)，如果发送的帧刚搞到达帧率发送的帧，则需要加上补齐1秒的值 （例如1秒发送30帧，则发送了30帧后需要补齐 1毫秒，使时间刚好到1秒），对应repairTime
        self.frameCountAudio = 1              # 对发送的帧计数(视频)，用来补齐1秒的时间（同上）
        self.videoCount = 0                        # 用来使音视频同步的变量
        self.audioCount = 0                        # 用来使音视频同步的变量
        self.repairTime = 1                   # 需要补齐到1秒的时间，对应frameCount
        self.time = 33                                    # 时间戳，视频 （帧的时间，每发送一帧自动增加）
        self.timeAudio = 21                               # 时间戳，音频 （帧的时间，每发送一帧自动增加）
        self.singleFrameTime = 33                         # 视频单帧的时间（帧率为30 fps，单帧的时间为33 毫秒）
        self.singleFrameTimeAudio = 21                    # 音频的单帧时间  (毫秒)
        self.lastKeyTime = 33                             # Last I Frame Interval 该祯与上一个关键祯之间的时间间隔，单位毫秒（ms），当数据类型为非视频祯时，则没有该字段
        self.lastTime = 33                                # Last Frame Interval  该祯与上一个祯之间的时间时间，单位毫秒(ms)，当数据类型为非视频祯时，则没有该字段

        self.videoPath = "../h264/bbb3.h264"              # 要发送的视频地址
        self.audioPath = "../aac/bbb3.aac"                 # 要发送的音频地址

        self.sendFrameType = 0                            # 要发送的帧类型， 0：视频帧    1：音频帧   (针对视频和音频他同时发送的方法)

        self.aacFront_28 = ""                             # aac 音频前28个固定bit 的值
        self.aacIsFront_28 = 0                            # aac 是否已经取出了前28个字节

        self.frameNum = 0                                   # 发送的包数量，用于测试

    def setSendDur(self,data):
        self.sendDur = data
    def setAVChangeBySecond(self,data):
        self.AVChangeBySecond = data
    def setVideoPath(self,data):
        self.videoPath = data
    def setAudioPath(self,data):
        self.audioPath = data
    def setMobile(self,data):
        self.mobile = data
    def setChannel(self,data):
        self.channel = data
    def setHost(self,data):
        self.host = data
    def setPort(self,data):
        self.port = data
    def setIsLoop(self,data):
        self.isLoop = data

    ####################################################
    #      设置视频帧率
    ####################################################
    def setFPSVideo(self,data = 0):
        if(data == 0):
            pass
        else:
            self.time = int(1000 / data)
            self.singleFrameTime = int(1000 / data)
            self.lastTime = self.singleFrameTime

    ####################################################
    #      设置音频帧率
    ####################################################
    def setFPSAudio(self,data = 0):
        if (data == 0):
            pass
        else:
            self.timeAudio = int(1000 / data)
            self.singleFrameTimeAudio = int(1000 / 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))

    ####################################################
    #      同时发送视频和音频
    ####################################################
    def sendAVStream(self):
        protocal_1078 = Protocal_1078()
        protocal_1078.setSim(self.mobile)
        protocal_1078.setLogcC(self.channel)
        # 打开视频文件
        fv2 = open(self.videoPath, 'rb')
        con = fv2.read(self.readSize)
        data = con.hex()  # 16进制字节码转字符串
        frames, lastData = self.getFramesFromStr_2(data)
        for fra in frames:
            self.frameNum = self.frameNum + 1
            protocal_1078.setDataBody("00000001" + fra)
            frameType = protocal_1078.getFrameType()
            self.doFrameCount(frameType, protocal_1078)
            pkgs = protocal_1078.genPkgsByFrame()
            for msg in pkgs:
                # print("发送视频消息：" + msg)
                self.client.send(binascii.a2b_hex(msg))
                time.sleep(self.sendDur)
        frames = []

        # 打开音频文件
        fa2 =  open(self.audioPath, 'rb')
        conAudio = fa2.read(self.readSizeAudio)
        dataAudio = conAudio.hex()  # 16进制字节码转字符串
        framesAudio, lastDataAudio = self.getAudioFramesFromStr_2(dataAudio)
        for fraAudio in framesAudio:
            self.frameNum = self.frameNum + 1
            protocal_1078.setDataBody(self.aacFront_28 + fraAudio)
            self.doAudioFrameCount(protocal_1078)
            pkgsAudio = protocal_1078.genAudioPkgsByFrame()
            for msgAudio in pkgsAudio:
                # print("发送音频消息：" + msgAudio)
                self.client.send(binascii.a2b_hex(msgAudio))
                time.sleep(self.sendDur)
        framesAudio = []

        while con or conAudio:
            if self.sendFrameType == 0:
                videoTempFrame = []
                isVloop = 0
                for fra in frames:
                    self.frameNum = self.frameNum + 1
                    if self.videoCount >= int(self.singleFrameTime / self.AVChangeBySecond):
                        self.sendFrameType = 1
                        self.videoCount = 0
                        if self.videoCount == 0:
                            videoTempFrame.append(fra)
                            isVloop = 1
                            continue
                    else:
                        if isVloop == 0:
                            if len(fra) % 2 != 0:  # 处理有问题的帧数据
                                print("出现了视频问题帧，并自动修复...")
                                fra = fra[:len(fra) - 1]
                            protocal_1078.setDataBody("00000001" + fra)
                            frameType = protocal_1078.getFrameType()
                            self.doFrameCount(frameType, protocal_1078)
                            pkgs = protocal_1078.genPkgsByFrame()
                            for msg in pkgs:
                                # print("发送视频消息：" + msg)
                                self.client.send(binascii.a2b_hex(msg))
                                time.sleep(self.sendDur)
                        else:
                            videoTempFrame.append(fra)
                frames = videoTempFrame
                if len(frames) != 0:
                    continue

                con = fv2.read(self.readSize)
                data = con.hex()
                if con:
                    frames, lastData = self.getFramesFromStr_2(data, lastData)
                    for fra in frames:
                        self.frameNum = self.frameNum + 1
                        if self.videoCount >= int(self.singleFrameTime / self.AVChangeBySecond):
                            self.sendFrameType = 1
                            self.videoCount = 0
                        if self.videoCount == 0:
                            videoTempFrame.append(fra)
                        else:
                            if len(fra) % 2 != 0:  # 处理有问题的帧数据
                                print("出现了视频问题帧，并自动修复...")
                                print(fra)
                                fra = fra[:len(fra) - 1]
                            protocal_1078.setDataBody("00000001" + fra)
                            frameType = protocal_1078.getFrameType()
                            self.doFrameCount(frameType, protocal_1078)
                            pkgs = protocal_1078.genPkgsByFrame()
                            for msg in pkgs:
                                # print("发送视频消息：" + msg)
                                self.client.send(binascii.a2b_hex(msg))
                                time.sleep(self.sendDur)
                    frames = videoTempFrame
                else:
                    self.sendFrameType = 1
            elif self.sendFrameType == 1:
                audioTempFrame = []
                isALoop = 0
                for fraAudio in framesAudio:
                    self.frameNum = self.frameNum + 1
                    if self.audioCount >= int(1000 / (self.singleFrameTimeAudio * 2)):
                        self.sendFrameType = 0
                        self.audioCount = 0
                        if self.audioCount == 0:
                            audioTempFrame.append(fraAudio)
                            isALoop = 1
                            continue
                    else:
                        if isALoop == 0:
                            if len(self.aacFront_28 + fraAudio) % 2 != 0:
                                print("出现了问题帧，并自动修复...")
                                print(fraAudio)
                                fraAudio = fraAudio[:len(fraAudio) - 1]
                            protocal_1078.setDataBody(self.aacFront_28 + fraAudio)
                            self.doAudioFrameCount(protocal_1078)
                            pkgsAudio = protocal_1078.genAudioPkgsByFrame()
                            for msgAudio in pkgsAudio:
                                # print("发送音频消息：" + msgAudio)
                                self.client.send(binascii.a2b_hex(msgAudio))
                                time.sleep(self.sendDur)
                        else:
                            audioTempFrame.append(fraAudio)
                framesAudio = audioTempFrame
                if len(framesAudio) != 0:
                    continue

                conAudio = fa2.read(self.readSizeAudio)
                dataAudio = conAudio.hex()
                if conAudio:
                    framesAudio, lastDataAudio = self.getAudioFramesFromStr_2(dataAudio, lastDataAudio)
                    for fraAudio in framesAudio:
                        self.frameNum = self.frameNum + 1
                        if self.audioCount >= int(1000 / (self.singleFrameTimeAudio * 2)):
                            self.sendFrameType = 0
                            self.audioCount = 0
                        if self.audioCount == 0:
                            audioTempFrame.append(fraAudio)
                        else:
                            if len(self.aacFront_28 + fraAudio) % 2 != 0:
                                print("出现了问题帧，并自动修复...")
                                print(fraAudio)
                                fraAudio = fraAudio[:len(fraAudio) - 1]
                            protocal_1078.setDataBody(self.aacFront_28 + fraAudio)
                            self.doAudioFrameCount(protocal_1078)
                            pkgsAudio = protocal_1078.genAudioPkgsByFrame()
                            for msgAudio in pkgsAudio:
                                # print("发送音频消息：" + msgAudio)
                                self.client.send(binascii.a2b_hex(msgAudio))
                                time.sleep(self.sendDur)
                    framesAudio = audioTempFrame
                else:
                    self.sendFrameType = 0
        fv2.close()
        fa2.close()
        self.client.close()
        # print("----------视频" + str(self.pos))
        # print("----------音频" + str(self.posAudio))
        # print("---------------------------------")

    ####################################################
    #      读取h264文件并发送
    ####################################################
    def readStreamFromFileAndSend(self,fileName):
        protocal_1078 = Protocal_1078()
        protocal_1078.setSim(self.mobile)
        protocal_1078.setLogcC(self.channel)
        with open(fileName, 'rb') as f2:
            con = f2.read(self.readSize)
            data = con.hex()  # 16进制字节码转字符串
            frames, lastData = self.getFramesFromStr_2(data)
            for fra in frames:
                protocal_1078.setDataBody("00000001" + fra)
                frameType = protocal_1078.getFrameType()
                self.doFrameCount(frameType, protocal_1078)
                pkgs = protocal_1078.genPkgsByFrame()
                for msg in pkgs:
                    print("发送消息：" + msg)
                    self.client.send(binascii.a2b_hex(msg))
                    time.sleep(self.sendDur)

            while con:
                con = f2.read(self.readSize)
                data = con.hex()  # 16进制字节码转字符串
                frames, lastData = self.getFramesFromStr_2(data, lastData)
                for fra in frames:
                    if len(fra) % 2 != 0:              # 处理有问题的帧数据
                        print("出现了问题帧，并自动修复...")
                        fra = fra[:len(fra) - 1]
                    protocal_1078.setDataBody("00000001" + fra)
                    frameType = protocal_1078.getFrameType()
                    self.doFrameCount(frameType, protocal_1078)
                    pkgs = protocal_1078.genPkgsByFrame()
                    for msg in pkgs:
                        print("发送消息：" + msg)
                        self.client.send(binascii.a2b_hex(msg))
                        time.sleep(self.sendDur)
                    # print(self.pos)

    ####################################################
    #      读取aac文件并发送
    ####################################################
    def readAacStreamFromFileAndSend(self,fileName):
        protocal_1078 = Protocal_1078()
        protocal_1078.setSim(self.mobile)
        protocal_1078.setLogcC(self.channel)
        with open(fileName, 'rb') as f2:
            con = f2.read(self.readSizeAudio)
            data = con.hex()  # 16进制字节码转字符串
            frames,lastData = self.getAudioFramesFromStr_2(data)
            for fra in frames:
                protocal_1078.setDataBody(self.aacFront_28 + fra)
                self.doAudioFrameCount(protocal_1078)
                pkgs = protocal_1078.genAudioPkgsByFrame()
                for msg in pkgs:
                    print("发送消息：" + msg)
                    self.client.send(binascii.a2b_hex(msg))
                    time.sleep(self.sendDur)
            while con:
                con = f2.read(self.readSize)
                data = con.hex()  # 16进制字节码转字符串
                frames, lastData = self.getAudioFramesFromStr_2(data, lastData)
                for fra in frames:
                    if len(self.aacFront_28 + fra) % 2 != 0:
                        print("出现了问题帧，并自动修复...")
                        print(fra)
                        fra = fra[:len(fra) - 1]
                    protocal_1078.setDataBody(self.aacFront_28 + fra)
                    self.doAudioFrameCount(protocal_1078)
                    pkgs = protocal_1078.genAudioPkgsByFrame()
                    for msg in pkgs:
                        print("发送消息：" + msg)
                        self.client.send(binascii.a2b_hex(msg))
                        time.sleep(self.sendDur)
            print(self.posAudio)


    ####################################################
    #      读取h264文件(用来测试)
    ####################################################
    def readStreamFromFile(self,fileName):
        protocal_1078 = Protocal_1078()
        with open(fileName, 'rb') as f2:
            con = f2.read(self.readSize)
            data = con.hex()  # 16进制字节码转字符串
            frames,lastData = self.getFramesFromStr(data)
            for fra in frames:
                protocal_1078.setDataBody(fra)
                frameType = protocal_1078.getFrameType()
                if frameType == "67":
                    print(frameType)
                self.pos = self.pos + 1
            while con:
                con = f2.read(self.readSize)
                data = con.hex()  # 16进制字节码转字符串
                frames, lastData = self.getFramesFromStr(data, lastData)
                for fra in frames:
                    protocal_1078.setDataBody(fra)
                    frameType = protocal_1078.getFrameType()
                    if frameType == "67":
                        print(frameType)
                    self.pos = self.pos + 1
                    # print(self.pos)
        print(self.pos)

    ####################################################
    #      对帧时间进行计数(视频帧)
    ###################################################
    def doFrameCount(self,frameType,obj_1078):
        if frameType == "65" or frameType == "61":                       # 68 之后接00000001 便是关键帧
            self.time = self.time + self.singleFrameTime
            self.lastKeyTime = 0
            self.frameCount = self.frameCount + 1
            self.pos = self.pos + 1

            # timeStamp = time.time()
            # print(timeStamp)
            # timeArray = time.localtime(int(timeStamp - 8 * 3600))
            # dateTimeM = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
            # print(dateTimeM)

        elif frameType == "41":  # P帧
            self.time = self.time + self.singleFrameTime
            self.lastKeyTime = self.lastKeyTime + self.singleFrameTime
            self.frameCount = self.frameCount + 1
            self.pos = self.pos + 1
        elif frameType == "01":  # B帧
            self.time = self.time + self.singleFrameTime
            self.lastKeyTime = self.lastKeyTime + self.singleFrameTime
            self.frameCount = self.frameCount + 1
            self.pos = self.pos + 1
        if self.frameCount >= 30:
            self.time = self.time + 1
            self.frameCount = 1
        self.videoCount = self.videoCount + 1
        obj_1078.setTime(self.time)
        obj_1078.setLastKeyTime(self.lastKeyTime)
        obj_1078.setLastTime(self.lastTime)

    ####################################################
    #      对帧时间进行计数(音频帧)
    ###################################################
    def doAudioFrameCount(self,obj_1078):
        self.timeAudio = self.timeAudio + self.singleFrameTimeAudio
        if self.frameCountAudio >= int(1000 / self.singleFrameTimeAudio):
            self.frameCountAudio = 0
            self.timeAudio = self.timeAudio + 1000 % self.singleFrameTimeAudio
        self.frameCountAudio = self.frameCountAudio + 1
        self.audioCount = self.audioCount + 1
        self.posAudio = self.posAudio + 1
        obj_1078.setTime(self.timeAudio)

    ####################################################
    #      读取h264文件（测试程序）
    ####################################################
    def readTest(self,fileName):
        with open(fileName, 'rb') as f2:
            con = f2.read(204800)
            data = con.hex()  # 16进制字节码转字符串
            data_H = data.split("00000001")
            for i in range(1,len(data_H)):
                print("00000001" + data_H[i])
    ####################################################
    #      读取h264文件（测试程序）
    ####################################################
    def readTest2(self,fileName):
        with open(fileName, 'rb') as f2:
            con = f2.read(200)
            data = con.hex()
            print(data)
            con = f2.read(207)
            data = con.hex()
            print(data)
            con = f2.read(212)
            data = con.hex()
            print(data)
    ####################################################
    #      读取h264文件（测试程序），从指定位置开始读取
    ####################################################
    def readTest3(self,fileName):
        with open(fileName, 'rb') as f2:
            f2.seek(4,0)
            con = f2.read(1)
            data = con.hex()
            print(data)

    #####################################################
    # 从帧数据中获取帧类型
    #####################################################
    def getFrameType(self,frame):
        # 0x67 (0 11 00111) SPS    非常重要       type = 7
        # 0x68 (0 11 01000) PPS     非常重要       type = 8
        # 0x65 (0 11 00101) IDR帧  关键帧  非常重要 type = 5
        # 0x61 (0 11 00001) I帧        重要         type=1    非IDR的I帧 不大常见
        # 0x41 (0 10 00001) P帧      重要         type = 1
        # 0x01 (0 00 00001) B帧     不重要        type = 1
        # 0x06 (0 00 00110) SEI     不重要        type = 6
        frameTypeHex = frame[8:10]
        return frameTypeHex

    ####################################################
    #      16进制字符串分割为个多个帧（通过split方式分割）
    ####################################################
    def getFramesFromStr(self,data,lastDataParam = ""):
        data_H = data.split("00000001")
        frames = []
        if(len(data_H) == 1):                               #如果字符串中不包含  000001(说明内容都属于一帧的中间位置)
            lastData = lastDataParam + data
            return frames,lastData
        else:
            for i in range(0, len(data_H)):
                if i != (len(data_H) - 1):
                    if data_H[i] != "":
                        if lastDataParam == "":                            #文件的开始
                            frame = "" + data_H[i]
                            frames.append(frame)
                        else:
                            if i == 0:
                                frame = "" + lastDataParam + data_H[i]
                                frames.append(frame)
                            else:
                                frame = "" + data_H[i]
                                frames.append(frame)
                    else:
                        if lastDataParam == "":                            #文件的开始
                            pass
                        else:
                            frame = "" + data_H[i] + lastDataParam
                            frames.append(frame)
                else:
                    lastData = data_H[i]
        tempFrames = []
        for tmp in frames:
            if self.getFrameType("00000001" + tmp) == "68" or self.getFrameType("00000001" + tmp) == "09":
                fra = tmp.split("000001")
                for fra2 in fra:
                    tempFrames.append(fra2)
            else:
                tempFrames.append(tmp)
        frames = tempFrames
        return frames,lastData

    ####################################################
    #      16进制字符串分割为个多个帧（一个字节一个字节的读取）
    ####################################################
    def getFramesFromStr_2(self,data,lastDataParam = ""):
        dataStr = data
        frame = ""
        frames = []
        frameSlice = dataStr[:2]
        frame = lastDataParam + frame + frameSlice
        dataStr = dataStr[2:]
        while len(dataStr) > 0:
            frameSlice = dataStr[:2]
            dataStr = dataStr[2:]
            if "00" == frameSlice:
                frameTmp = frameSlice
                frameTmp2 = dataStr[:2]
                dataStr = dataStr[2:]
                while frameTmp2 == "00":
                    frameTmp = frameTmp + frameTmp2
                    frameTmp2 = dataStr[:2]
                    dataStr = dataStr[2:]
                frameTmp = frameTmp + frameTmp2
                if frameTmp == "00000001":
                    frames.append(frame)
                    frame = ""
                else:
                    frame = frame + frameTmp
            else:
                frame = frame + frameSlice
        lastData = frame
        tempFrames = []
        for tmp in frames:
            if self.getFrameType("00000001" + tmp) == "68" or self.getFrameType("00000001" + tmp) == "09":
                fra = tmp.split("000001")
                for fra2 in fra:
                    tempFrames.append(fra2)
            else:
                tempFrames.append(tmp)
        frames = tempFrames
        return frames,lastData

    ####################################################
    #      获取音频帧(通过前面28位来截取)
    ####################################################
    def getAudioFramesFromStr_2(self,data,lastDataParam = ""):
        if self.aacIsFront_28 == 0:
            self. aacFront_28 = data[:7]
            self.aacIsFront_28 = 1

        data_H = data.split(self.aacFront_28)
        frames = []
        if(len(data_H) == 1):                               #如果字符串中不包含  000001(说明内容都属于一帧的中间位置)
            lastData = lastDataParam + data
            return frames,lastData
        else:
            for i in range(0, len(data_H)):
                if i != (len(data_H) - 1):
                    if data_H[i] != "":
                        if lastDataParam == "":                            #文件的开始
                            frame = "" + data_H[i]
                            frames.append(frame)
                        else:
                            if i == 0:
                                frame = "" + lastDataParam + data_H[i]
                                frames.append(frame)
                            else:
                                frame = "" + data_H[i]
                                frames.append(frame)
                    else:
                        if lastDataParam == "":                            #文件的开始
                            pass
                        else:
                            frame = "" + data_H[i] + lastDataParam
                            frames.append(frame)
                else:
                    lastData = data_H[i]
        # for i in frames:
        #     print(self.aacFront_28 + frame)
        return frames,lastData

if __name__ == "__main__":
    obj = StreamH264()
    obj.connectServer()
    obj.setFPSVideo(30)
    obj.setFPSAudio(47)
    obj.setSendDur(0.005)
    obj.setAVChangeBySecond(2)
    obj.setVideoPath("../../h264/aaa3.h264")
    obj.setAudioPath("../../aac/aaa3.aac")
    # obj.readStreamFromFileAndSend("../../h264/aaa3.h264")        # 发送视频
    # obj.readAacStreamFromFileAndSend("../../aac/aaa3.aac")       # 发送音频
    obj.sendAVStream()                                             # 同时发送音视频

    # StreamH264().readTest("../../h264/aaa.h264")
    # StreamH264().readTest2("../../h264/bbb3.h264")
    # StreamH264().readTest2("../../aac/aaa.aac")
    # StreamH264().readStreamFromFile("../h264/aaa2.264")

