<template>
  <div class="disk-video">
    <video
      v-if="hasMediaSource"
      ref="video"
      class="video"
      autoplay
      muted
      controls
    ></video>
    <canvas
      v-else
      class="canvas"
      width="720"
      height="1280"
    ></canvas>
  </div>
</template>

<script>
import JMuxer from 'jmuxer';
import qs from 'qs';
// bcc校验码计算
// arry: 要计算的数组
// 返回计算协议中校验位的校验码
function calBcc(arry) {
  let bcc = 0;
  for (let i = 0; i < arry.length; i++) {
    bcc ^= arry[i];
  }
  return bcc;
}

function makeFrameExtend(sn, dataType, body) {
  let index = 0;
  const dataLen = body.length;
  const frameLen = dataLen + 26;
  const outPut = new Uint8Array(frameLen);
  outPut[index++] = 0x68;
  outPut[index++] = (dataLen & 0xff000000) >> 24;
  outPut[index++] = (dataLen & 0x00ff0000) >> 16;
  outPut[index++] = (dataLen & 0x0000ff00) >> 8;
  outPut[index++] = dataLen & 0x000000ff;
  outPut[index++] = 0; // 类型为client

  // sn号赋值，string转ascii
  for (let i = 0; i < sn.length; i++) {
    outPut[index++] = sn[i].charCodeAt();
  }

  outPut[index++] = dataType; // 指定数据类型为json
  // json string转ascii
  for (let i = 0; i < body.length; i++) {
    outPut[index++] = body[i];
  }

  const bccBuffer = outPut.slice(1, frameLen - 3 + 1); // 忽略协议头和协议尾
  outPut[index++] = calBcc(bccBuffer);
  outPut[index++] = 0x16;
  return outPut;
}

// 生成鉴权报文
function VerifyCode(sn, code) {
  const len = code.length + 1;
  const codeBuffer = new TextEncoder('utf-8').encode(code); // 获取字符串ascii码
  const buffer = new Uint8Array(len);
  buffer[0] = 0x04;

  for (let i = 0; i < codeBuffer.length; i++) {
    buffer[i + 1] = codeBuffer[i];
  }

  return makeFrameExtend(sn, 6, buffer);
}

// I 帧请求报文生成
function RequestIFrame(sn) {
  // let sn = "RK3923C1201900139";
  const outPut = new Uint8Array([0x20]);
  return makeFrameExtend(sn, 6, outPut);
}

// 数组打印，调试用
function PrintArry(data) {
  let str = '';

  for (let i = 0; i < data.length; i++) {
    str = str + data[i].toString(16).padStart(2, '0');
  }

  str = str.toUpperCase();
  return str;
}
// 检查鉴权报文
function CheckVerifyCode(data) {
  const dataLen = data.length - 26;
  const body = data.slice(24, 24 + dataLen);
  console.log('打印:' + PrintArry(body));

  if (body[3] === 0x03) {
    return true;
  }

  return false;
}
// 通道配置
function ConfigChannel(sn) {
  const outPut = new Uint8Array([0x07]);
  return makeFrameExtend(sn, 6, outPut);
}

// 查询屏幕方向
function GetScreenState(sn) {
  // let sn = "RK3923C1201900139";
  const outPut = new Uint8Array([
    0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02,
  ]);
  return makeFrameExtend(sn, 5, outPut);
}

// 根据报文识别屏幕方向, 0横屏，1竖屏
function CheckScreenDirection(data) {
  if (data[0] === 0 && data[1] === 0 && data[2] === 0 && data[3] === 1) {
    if (data[4] === 1 && data[5] === 1) {
      if (data[6] === 1) {
        const screen = data[7];
        return screen;
      }
    }
  }
}

// 多端登录数据解析
function checkMultiLoginInfo(input) {
  const dataLen = input.length - 26; // 得到json 长度
  const jsonHex = input.slice(24, 24 + dataLen); // 截取json hex二进制数据
  const jsonStr = new TextDecoder('utf-8').decode(jsonHex);
  console.log('取得json 字符串:' + jsonStr);
  const jsonObj = JSON.parse(jsonStr);
  return jsonObj;
}

// 切换清晰度
function makeSharpness(sn, level) {
  // var sn = "RK3923C1201900139";
  const jsonObj = {
    type: 2,
    data: { definition: level, clientType: 'h5', sceneType: 'cloudPhone' },
  };
  const jsonStr = JSON.stringify(jsonObj);
  const outPut = new TextEncoder('utf-8').encode(jsonStr);
  return makeFrameExtend(sn, 0xd, outPut);
}
export default {
  name: 'DiskVideo',
  props: {
    info: {
      type: Object,
      default: null,
    },
  },
  data() {
    return {
      hasMediaSource: false,
    };
  },
  async fetch() {
    this.info && (await this.initPlugflowWebSocket());
  },
  computed: {
    sn() {
      return this.info.sn;
    },
  },
  watch: {
    info: '$fetch',
  },
  created() {},
  mounted() {
    this.initVideo();
  },
  methods: {
    initPlugflowWebSocket() {
      this?._ws?.close();
      const { internetHttps, localIp, sn, cardToken } = this.info;
      const url = `wss://${internetHttps}/plugflow${qs.stringify(
        {
          cardIp: localIp,
          token: cardToken,
          type: 'business',
        },
        { addQueryPrefix: true },
      )}`;
      const ws = new WebSocket(url);
      ws.binaryType = 'arraybuffer';
      ws.addEventListener('open', (e) => {
        ws.send('ping');
        ws._pingInterval = setInterval(() => {
          ws.send('ping');
        }, 1000 * 1);
        ws.send(VerifyCode(sn, cardToken));
        // ws.send(makeSharpness(sn, 4));
        // ws.send(RequestIFrame(sn));
      });
      ws.addEventListener('error', (e) => {});
      ws.addEventListener('message', (event) => {
        const ParseProto = (data) => {
          const input = new Uint8Array(data);
          let duration;
          let video;
          let frameType;
          let audio;

          if (
            input[0] === 0 &&
            input[1] === 0 &&
            input[2] === 0 &&
            input[3] === 1
          ) {
            video = input;
            duration = 24;
            const nalType = input[4] & 0x1f;
            frameType = nalType;

            // if (!isFeed) {
            //   if (nalType == 0x05 && isVisuable) {
            //     isFeed = true;
            //   }
            // }
          } else if (input[0] === 0xff) {
            audio = input;
            duration = 24;
          } else if (input[0] === 0x68) {
            if (input[23] === 0x5c) {
              console.log('收到消息:' + PrintArry(input));

              if (CheckVerifyCode(input)) {
                ws.send(ConfigChannel(sn));
                // ws.send(GetScreenState(sn));
              } else {
                // connect('update');
              }
            }
            if (input[23] === 0x05) {
              // 横竖屏标识
              const state = CheckScreenDirection(input.slice(24, 24 + 8));

              if (state === 1) {
                console.log('安卓卡此时竖屏');
                // 竖屏处理
                // resolving = 1;
              } else {
                console.log('安卓卡此时横屏');
                // 横屏处理
                // resolving = 0;
              }
            }
            if (input[23] === 0x0b) {
              // 多端登录处理, 数据从索引24开始取, input 是接收到的原始数据
              const jsonobj = checkMultiLoginInfo(input);
              // console.log(
              //   '🚀 ~ file: disk.vue ~ line 324 ~ ParseProto ~ jsonobj',
              //   jsonobj,
              // );
            }
          }

          return {
            audio,
            video,
            duration,
            frameType,
          };
        };
        const data = ParseProto(event.data); // JAVA服务器转发
        // console.log(
        //   '🚀 ~ file: disk.vue ~ line 336 ~ ws.addEventListener ~ data',
        //   data,
        // );
        if (data.video) {
          this.pushVideo(data);
        }
      });
      ws.addEventListener('close', (event) => {
        clearInterval(event.currentTarget._pingInterval);
      });
      this.$once('hook:beforeDestroy', () => {
        ws.close();
      });
      this._ws = ws;
    },
    initVideo() {
      this.hasMediaSource = !!window.MediaSource;
      // this.hasMediaSource = false;
      if (this.hasMediaSource) {
        this._jmuxer = new JMuxer({
          node: this.$refs.video,
          flushingTime: 33,
          fps: 30,
          mode: 'video',
          debug: true,
          onReady :()=>{
            console.log('JMuxer ready');
          }
        });
      } else {
        throw new Error('当前设备不支持MediaSource');
      }
    },
    pushVideo(data) {
      console.log("🚀 ~ file: video.vue ~ line 315 ~ pushVideo ~ data", data)
      if (this.hasMediaSource) {
        this?._jmuxer?.feed({
          video: data.video,
          // duration: data.duration,
          // frameType: data.frameType,
        });
        this.$refs?.video?.play();
      }
    },
  },
};
</script>


<style lang="scss" scoped>
.disk-video {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 0;
}
.video {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: fill;
}
.canvas {
  position: relative;
  width: 100%;
  height: 100%;
}
</style>

