# 视频文件(多个)流式TCP协议定义

## 背景

先前实现的HTTP上传接口要求将待上传的文件load到内存中再上传, 这种行为对于内存比较大的H6影响较小, 但是对于雄迈摄像头(32MB内存)而言, 有比较高的风险导致内存溢出.

鉴于此本文定义了一套视频文件TCP流式上传协议.



## 总览

``` mermaid
sequenceDiagram
    participant EVCamera
    participant JavaVideoUploadSVC
    
    EVCamera ->>JavaVideoUploadSVC: TCP_CONN
    JavaVideoUploadSVC ->>EVCamera: TCP_ACCEPT_ACK
    
    loop transfer
      EVCamera ->>JavaVideoUploadSVC: <<DD>><<fragmented>> STREAM_DATA_T
      JavaVideoUploadSVC ->>JavaVideoUploadSVC: <<FD>> parse_stream_and_save_files
    end
    
    EVCamera ->>JavaVideoUploadSVC: FIN(close)
    JavaVideoUploadSVC ->>EVCamera: FIN/ACK(close)
```

其中 TCP_CONN, FIN/ACK由TCP库提供. 我们主要关注的是视频文件流包格式定义以及异常处理



## 数据格式定义(DD)

### STREAM_DATA_T: 大端在前

```BNF
STREAM_DATA_T := MAGIC_HEAD, {PAYLOAD}-, MAGIC_TAIL     # 总体定义
MAGIC_HEAD := 0xBE, 0xEF                                # 魔法头
PAYLOAD := META, CRC16, BODY						    						# 负载定义
META := SN_LEN, SN, START_TS, END_TS, TYPE, NUM, {FILE_META*NUM}	# 元数据
SN_LEN := UINT16                                        # 摄像头SN长度 一般8字节
SN := {UINT8*SN_LEN}                                    # 摄像头SN, 长度为SN_LEN
START_TS := TS										# 事件开始时间
END_TS := TS											# 时间结束时间
TYPE := UINT8                     # 0 - 未定义, 1 - AI人形, 2 - AI人脸, 3 - 移动侦测, 
								 									# 4 - 覆盖, 5 - 覆盖解除, 6 - 视频获取
								 									# 其他 - 由发起请求的微服务自定义
FILE_META := FILE_TS,SIZE
FILE_TS := TS    # 文件开始时间
SIZE := UINT32   # 文件字节数
NUM := UINT32    # 4-byte unsigned int. 视频文件数目
TS := INT64      # 8-byte long. 时间戳
CRC16 := UINT16  # 2-byte int. 调用crc16(META). 算法见[]
BODY := {FILE_DATA*NUM}
FILE_DATA := SIZE, {UINT8 * SIZE}   # size redundant for robust.
MAGIC_TAIL := 0xFF, 0xFF, 0xDE, 0xAD
```

1. 服务器端可用非阻塞多线程模式监听上传端口. 建议使用Netty简化工作量.
2. 服务器在确认所有META定义的文件列表都收完成并且接下来两个字节是0XFF,0xFF (MAGIC_TAIL)时, 即可关闭连接.

3. 任何网络或内部错误及异常均需终止连接, 并且边缘侧需按策略重试.
4. 一次连接可上传至少一个事件的视频.

## CRC16

因测试过程发现偶尔会有数据包错误, 需要加CRC校验. 但是视频文件字节数的计算量对边缘侧压力比较大, 所以至计算META部分的CRC. 算法如下:

```c
unsigned short crc16(const unsigned char* data_p, unsigned char length){
    unsigned char x;
    unsigned short crc = 0xFFFF;

    while (length--){
        x = crc >> 8 ^ *data_p++;
        x ^= x>>4;
        crc = (crc << 8) ^ ((unsigned short)(x << 12)) ^ ((unsigned short)(x <<5)) ^ ((unsigned short)x);
    }
    return crc;
}
```



## 出错重传机制

统一约定以服务器端主动发请求为主. 参见[EVCamera与云微服务的交互MQTT协议定义]和 [请求摄像头视频文件协议]

