Refactor and enhance handling of payloads and data structures

Added support for handling various payload types, including GPS and alarms, with new structures and constants. Introduced helper methods for JSON marshalling/unmarshalling of GPS data and modularized the handling of certificates, configurations, and alarms. Implemented foundational server code for testing and expanded several package functionalities.
This commit is contained in:
Александр Лазаренко 2025-02-22 19:23:16 +03:00
parent f3083820dc
commit 16da4affa8
Signed by: Kerblif
GPG Key ID: 5AFAD6640F4670C3
7 changed files with 607 additions and 61 deletions

View File

@ -23,7 +23,7 @@ const (
type CertificateConnectRequest struct { type CertificateConnectRequest struct {
Net NetConnectionType `json:"NET"` Net NetConnectionType `json:"NET"`
SerialNumber string `json:"DNSO"` SerialNumber string `json:"DSNO"`
DeviceName string `json:"DEVNAME"` DeviceName string `json:"DEVNAME"`
ChannelsNumber uint `json:"CHANNEL"` ChannelsNumber uint `json:"CHANNEL"`
LicensePlate string `json:"CARNUM"` LicensePlate string `json:"CARNUM"`
@ -35,6 +35,25 @@ type CertificateConnectRequest struct {
EvidenceSupport string `json:"EV"` EvidenceSupport string `json:"EV"`
} }
type CertificateConnectResponse struct {
ErrorCode uint `json:"ERRORCODE"`
ErrorCause string `json:"ERRORCAUSE"`
CommandMask uint `json:"MASKCMD"`
}
type CommandMaskParameters uint
const (
CommandMaskAlarm = 1 << iota
CommandMaskScanningGun
CommandMaskPassengerFlow
CommandMaskFaceContrast
CommandMaskCard
CommandMaskShutdownReport
CommandMaskGPSReport
CommandMaskAll = 0b1111111
)
/* /*
func (e *Package) RequestConnect(session string, serial string, numOfCams int) { func (e *Package) RequestConnect(session string, serial string, numOfCams int) {
e.Payload = map[string]any{ e.Payload = map[string]any{

174
cmd/dev-server/main.go Normal file
View File

@ -0,0 +1,174 @@
package main
import (
"fmt"
"gitea.unprism.ru/KRBL/n9m"
"net"
"os"
)
func main() {
ln, err := net.Listen("tcp", "0.0.0.0:5556")
if err != nil {
panic(err)
}
for {
var conn net.Conn
conn, err = ln.Accept()
if err != nil {
continue
}
go handle(conn)
}
}
func handle(conn net.Conn) {
var err error
var rn int
var serial string
packS := n9m.Package{}
tmp := make([]byte, 1024)
for {
rn, err = conn.Read(tmp)
if err != nil {
fmt.Println(err)
return
}
packS.AddToAccum(tmp[:rn])
for packS.ReadPackage() {
switch packS.PayloadType {
case n9m.PayloadTypeData:
combined := packS.Payload.Module + ":" + packS.Payload.Operation
switch combined {
case "CERTIFICATE:CONNECT":
var params n9m.CertificateConnectRequest
if err = packS.GetParametersAs(&params); err != nil {
fmt.Println(combined, err)
return
}
var response = n9m.CertificateConnectResponse{
ErrorCode: 0,
CommandMask: n9m.CommandMaskAll,
}
packS.SetResponse(response)
if _, err = conn.Write(packS.PackPackage()); err != nil {
fmt.Println(combined, err)
return
}
fmt.Println("Connected:", params.SerialNumber)
serial = params.SerialNumber
var request n9m.ConfigModelGetRequest
request.MDVR = "?"
packS.Payload.Module = "CONFIGMODEL"
packS.Payload.Operation = "GET"
packS.SetParameters(request)
conn.Write(packS.PackPackage())
/*
go func() {
pack := packS
pack.Payload.Module = "EVEM"
pack.Payload.Operation = "GALARMING"
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
fmt.Println("Sent!")
if _, err := conn.Write(pack.PackPackage()); err != nil {
fmt.Println("Failed to send GALARMING:", err)
return
}
}
}()
*/
case "CERTIFICATE:KEEPALIVE":
packS.SetResponse(nil)
if _, err = conn.Write(packS.PackPackage()); err != nil {
fmt.Println(combined, err)
return
}
fmt.Println("Connection is still alive!")
case "EVEM:GGALARMING":
fmt.Println(string(packS.RawPayload))
var response n9m.EventModelGetAlarmingResponse
if err = packS.GetResponseAs(&response); err != nil {
fmt.Println(combined, err)
continue
}
fmt.Printf("%+v\n", response)
case "CONFIGMODEL:GET":
os.WriteFile(fmt.Sprintf("./%s.json", serial), packS.RawPayload, 0644)
case "EVEM:SENDALARMINFO":
var params n9m.SendAlarmInfoParameters
var response n9m.SendAlarmInfoResponse
if err = packS.GetParametersAs(&params); err != nil {
fmt.Printf("Error: %s\nData: %s", err, packS.RawPayload)
continue
}
response.ErrorCode = 0
response.AlarmType = params.AlarmType
response.CommandType = params.CommandType
response.AlarmUID = params.AlarmUID
response.NumberOfRestarts = params.NumberOfRestarts
response.InstructionSerial = params.InstructionSerial
switch params.AlarmType {
case n9m.AlarmTypeMotionDetection:
break
case n9m.AlarmTypeVideoLoss, n9m.AlarmTypeCameraCovered:
var cameraParams n9m.SendAlarmInfoCameraParameters
if err = packS.GetParametersAs(&cameraParams); err != nil {
fmt.Printf("Error: %s\nData: %s", err, packS.RawPayload)
continue
}
fmt.Printf("%+v\n", cameraParams)
packS.SetResponse(response)
conn.Write(packS.PackPackage())
default:
fmt.Println("Unknown alarm type:", params.AlarmType)
}
default:
fmt.Println("Strange operation:", combined)
}
case n9m.PayloadTypeSpecial:
switch packS.SSRC {
case n9m.SpecialPayloadTypeGPS:
fmt.Printf("%+v\n", packS.GPS)
default:
fmt.Println("Unhandled special operation:", packS.SSRC)
}
default:
fmt.Println("Unhandled operation:", packS.PayloadType)
}
}
}
}

View File

@ -28,37 +28,41 @@ func (e *Package) SetParameters(params map[string]any, serial int, session strin
} }
// log.Println(e.Payload) // log.Println(e.Payload)
} // end of 'SetParameters' function } // end of 'SetParameters' function
*/
// todo al1 type ConfigModelSetRequest struct {
func (e *Package) ConfigeModel(Sid string) { MDVR Setting `json:"MDVR"`
e.Payload = map[string]any{
"MODULE": "CONFIGMODEL",
"OPERATION": "SET",
"PARAMETER": map[string]any{
"MDVR": map[string]any{
"KEYS": map[string]any{ // KEY parameters
"GV": 1, // GPS version
},
"PGDSM": map[string]any{ // Network monitoring status parameters
"PGPS": map[string]any{ // GPS position
"EN": 1, // Real-time position monitoring
"TM": 10, // Time interval
},
},
"PSI": map[string]any{ // Platform basic information
"CG": map[string]any{ // Call information
"AS": 0, // Automatic answer
},
},
"SUBSTRNET": map[string]any{
"SM": 1, // 0-Smooth .. 4-Clear
},
},
},
}
} }
// main server util type ConfigModelGetRequest struct {
MDVR interface{} `json:"MDVR"`
}
type ConfigModelSetResponse struct {
MDVR Setting `json:"MDVR"`
}
func (e *Package) InitialConfigModelSetRequest() {
e.SetParameters(ConfigModelSetRequest{
MDVR: Setting{
KEYS: KEYS{
GV: 1, // GPS version
},
PGDSM: PGDSM{
PGPS: PGPS{
EN: 1, // Real-time position monitoring
MODE: 0b10, // Enable timer
TM: 10, // Time interval
},
},
SUBSTRNET: SUBSTRNET{
SM: 1,
},
},
})
}
/*
func (e *Package) ResponseConfigModelSet(Sid string) { func (e *Package) ResponseConfigModelSet(Sid string) {
e.Payload = map[string]any{ e.Payload = map[string]any{
"MODULE": "CONFIGMODUL", // it's not error "MODULE": "CONFIGMODUL", // it's not error

169
evem.go
View File

@ -1,5 +1,174 @@
package n9m package n9m
// 3.4.1.3
type EventModelGetAlarmStatusInfoResponse struct {
ErrorCode uint `json:"ERRORCODE"`
ErrorCause string `json:"ERRORCAUSE"`
StorageErrors []StorageErrorStatus `json:"ST"`
AnotherStorageErrors []AnotherStorageErrorStatus `json:"VS"`
VideoLossErrors []VideoLossErrorStatus `json:"VL"`
GPSError GPSErrorStatus `json:"GFA"`
GPSAntennaError GPSAntennaErrorStatus `json:"GPSS"`
}
// 3.4.1.4.21
type StorageErrorStatus struct {
CameraCovered uint `json:"ISA"`
ChannelBind uint `json:"LHC"`
}
// 3.4.1.4.4
type AnotherStorageErrorStatus struct {
}
// 3.4.1.4.5
type VideoLossErrorStatus struct {
}
// 3.4.1.4.44
type GPSErrorStatus struct {
}
// 3.4.1.4.46
type GPSAntennaErrorStatus struct {
}
// 3.4.1.5
// Alarm upload
type SendAlarmInfoParameters struct {
AlarmType AlarmType `json:"ALARMTYPE"`
CommandType uint `json:"CMDTYPE"`
AlarmUID uint `json:"ALARMUID"`
NumberOfRestarts uint `json:"RUN"`
AlarmLevel AlarmLevel `json:"ALARMAS"`
AlarmCount uint `json:"ALARMCOUNT"`
TriggerType TriggerType `json:"TRIGGERTYPE"`
ContinueTime uint `json:"CONTINUETIME"`
CurrentTime uint `json:"CURRENTTIME"`
Language Language `json:"L"`
GPSData GPSData `json:"P"`
RealTimeUpload uint `json:"REAL"`
InstructionSerial uint `json:"CMDNO"`
}
// 3.4.1.5
// Alarm upload
type SendAlarmInfoResponse struct {
ErrorCode uint `json:"ERRORCODE"`
AlarmType AlarmType `json:"ALARMTYPE"`
ErrorCause string `json:"ERRORCAUSE"`
CommandType uint `json:"CMDTYPE"`
AlarmUID uint `json:"ALARMUID"`
NumberOfRestarts uint `json:"RUN"`
InstructionSerial uint `json:"CMDNO"`
}
// 3.4.1.5.1
type SendAlarmInfoCameraParameters struct {
SendAlarmInfoParameters
Channel uint `json:"CHANNEL"`
ChannelMask uint `json:"CHANNELMASK"`
LCH []uint `json:"LCH"`
Push uint `json:"PUSH"`
AlarmName string `json:"ALARMNAME"`
AlarmAbbreviation string `json:"SER"`
}
type AlarmType uint
const (
AlarmTypeVideoLoss AlarmType = iota
AlarmTypeCameraCovered
AlarmTypeMotionDetection
AlarmTypeStorageAbnormal
AlarmTypeUserDefined
AlarmTypeSentriesInspection
AlarmTypeViolation
AlarmTypeEmergency
AlarmTypeSpeedAlarm
AlarmTypeLowVoltage
AlarmTypeOutOfFence = iota + 7
AlarmTypeAccAlarm
AlarmTypePeripheralsDropped
AlarmTypeStopAnnouncement
AlarmTypeGpsAntenna
AlarmTypeDayNightSwitch
AlarmTypeProhibitDriving
AlarmTypeSerialAlarm = iota + 15
AlarmTypeFatigueAlarm
AlarmTypeTakeOutParking
AlarmTypeGestureAlarm
AlarmTypeGreenDriving
AlarmTypeIllegalIgnition
AlarmTypeIllegalShutdown
AlarmTypeCustomExternal
AlarmTypeThinkingLKJ
AlarmTypeTAX3
AlarmTypeOilAlarm
AlarmTypeBusLineOccupation
AlarmTypeForgottenAlarm
AlarmTypeSpecialCustomerFault
AlarmTypeTemperatureAbnormal
AlarmTypeTemperatureChangeAbnormal
AlarmTypeSmokeAlarm
AlarmTypeGBox
AlarmTypeLicensePlateRecognition
AlarmTypeAnotherSpeedAlarm
AlarmTypeWirelessSignalAbnormal
AlarmTypeArming
AlarmTypePhoneCall
AlarmTypeGPSFault
AlarmTypeDSMFault
AlarmTypeFireBox
)
type AlarmLevel uint
const (
AlarmLevelImportant AlarmLevel = iota
AlarmLevelGeneral
AlarmLevelEmergency
)
type TriggerType uint
const (
TriggerTypeManual TriggerType = iota
TriggerTypeAutomatic
)
type Language uint
const (
LanguageSimplifiedChinese Language = iota
LanguageEnglish
LanguageKorean
LanguageItalian
LanguageGerman
LanguageThai
LanguageTurkey
LanguagePortugal
LanguageSpain
LanguageRomania
LanguageGreece
LanguageFrench
LanguageRussian
LanguageDutch
LanguageHebrew
LanguageChineseTraditional
)
type EventModelGetAlarmingResponse struct {
CameraCoveredChannelMask uint `json:"VS_CH"`
CameraCoveredAlarmMask uint `json:"VS_AT"`
CameraCoveredStatusMask uint `json:"VS_AS"`
VideoLossChannelMask uint `json:"VL_CH"`
VideoLossAlarmMask uint `json:"VL_AT"`
VideoLossStatusMask uint `json:"VL_AS"`
}
/* /*
// main server util // main server util
func (e *Package) ResponseAlarm(alarmType int64, alarmUID int64, cmdno int64, cmdtype int64, run int64, serial string, Sid string) { func (e *Package) ResponseAlarm(alarmType int64, alarmUID int64, cmdno int64, cmdtype int64, run int64, serial string, Sid string) {

53
io.go
View File

@ -2,10 +2,12 @@ package n9m
import ( import (
"bytes" "bytes"
"encoding/binary"
"encoding/json" "encoding/json"
"log" "fmt"
"github.com/icza/bitio" "github.com/icza/bitio"
"log"
"time"
) )
// Read package // Read package
@ -20,7 +22,7 @@ func (e *Package) ReadPackage() bool {
e.CompressFlag = r.TryReadBool() e.CompressFlag = r.TryReadBool()
e.CSRCCount = uint8(r.TryReadBits(4)) e.CSRCCount = uint8(r.TryReadBits(4))
e.PayloadType = PayloadType(r.TryReadBits(8)) e.PayloadType = PayloadType(r.TryReadBits(8))
e.SSRC = uint16(r.TryReadBits(16)) e.SSRC = SpecialPayloadType((r.TryReadBits(8) | (r.TryReadBits(8) << 8)))
if e.EncryptionFlag && e.CompressFlag { if e.EncryptionFlag && e.CompressFlag {
// TODO: get snippet, that use this code // TODO: get snippet, that use this code
@ -60,11 +62,34 @@ func (e *Package) ReadPackage() bool {
e.Accum = e.Accum[12+e.payloadLen:] e.Accum = e.Accum[12+e.payloadLen:]
if e.PayloadType == PayloadTypeData { switch e.PayloadType {
case PayloadTypeData:
if err := json.Unmarshal(e.RawPayload, &e.Payload); err != nil { if err := json.Unmarshal(e.RawPayload, &e.Payload); err != nil {
log.Printf("Error parsing JSON payload: %v", err) log.Printf("Error parsing JSON payload: %v", err)
return false return false
} }
case PayloadTypeSpecial:
switch e.SSRC {
case SpecialPayloadTypeGPS:
e.GPS.GPSStatus = e.RawPayload[0]
e.GPS.Expand = e.RawPayload[1]
e.GPS.Real = e.RawPayload[2]
e.GPS.Longitude = float64(binary.BigEndian.Uint32(e.RawPayload[4:8])) / 1e6
e.GPS.Latitude = float64(binary.BigEndian.Uint32(e.RawPayload[8:12])) / 1e6
e.GPS.Speed = float64(binary.BigEndian.Uint32(e.RawPayload[12:16])) / 100
e.GPS.Direction = float64(binary.BigEndian.Uint32(e.RawPayload[16:20])) / 100
e.GPS.Altitude = int32(binary.BigEndian.Uint32(e.RawPayload[20:24]))
var err error
if e.GPS.Time, err = time.Parse("20060102150405", string(e.RawPayload[24:38])); err != nil {
log.Printf("Error parsing time: %v", err)
}
default:
fmt.Println("N9M parser warning: unknown special payload type", e.SSRC)
}
default:
fmt.Println("N9M parser warning: unknown payload type", e.PayloadType)
} }
if r.TryError != nil { if r.TryError != nil {
@ -141,3 +166,23 @@ func (e *Package) GetParametersAs(parameters any) error {
return json.Unmarshal(marshal, parameters) return json.Unmarshal(marshal, parameters)
} }
func (e *Package) SetParameters(parameters any) {
e.Payload.Response = struct{}{}
e.Payload.Parameter = parameters
}
func (e *Package) GetResponseAs(response any) error {
marshal, err := json.Marshal(e.Payload.Response)
if err != nil {
return err
}
return json.Unmarshal(marshal, response)
}
func (e *Package) SetResponse(response any) {
e.Payload.Parameter = struct{}{}
e.Payload.Response = response
}

View File

@ -1,5 +1,6 @@
package n9m package n9m
// 7.2
type RIP struct { type RIP struct {
DEVID string // ID номер устройства DEVID string // ID номер устройства
BN string // бортовой номер BN string // бортовой номер
@ -218,7 +219,8 @@ type DNS struct {
} }
type KEYS struct { type KEYS struct {
MAC string // MAC-адрес MAC string `json:"MAC,omitempty"` // MAC-адрес
GV uint `json:"GV,omitempty"` // GPS version
} }
type WIFI struct { type WIFI struct {
@ -267,29 +269,67 @@ type SP struct {
MUPORT int // UDP-порт медиасервера MUPORT int // UDP-порт медиасервера
} }
type Setting struct { // 7.30
RIP RIP type PGDSM struct {
VS VS PGPS PGPS `json:"PGPS,omitempty"`
GSP GSP PDSM PDSM `json:"PDSM,omitempty"`
TIMEP TIMEP }
ETHERNET ETHERNET
KEYS KEYS // 7.30.1
WIFI WIFI // GPS (position) status parameter
M3G M3G type PGPS struct {
MCMS MCMS EN uint // Enable
ATP ATP MODE uint
SSP SSP SEP uint `json:"SEP,omitempty"`
SWUS SWUS TM uint `json:"TM,omitempty"`
UMP UMP NUM uint `json:"NUM,omitempty"`
SUBSTRNET SUBSTRNET }
DOSD DOSD
AR AR // 7.30.2
EOSD []EOSD type PDSM struct {
MAIN []VEC }
IOP []IOP
SAP SAP // 7.39.3.9
UAP UAP // Platform basic information
PVLAS PVLAS type PSI struct {
PMDAS PMDAS CG CG `json:"CG,omitempty"`
DSM DSM }
// 7.39.3.8
// Total record information for call
type CG struct {
ECL uint // The maximum calling time
ECML uint // The maximum calling time per month
AS uint // Terminal phonecall answering strategy
AT uint // Answer automatically when exceed time
}
type Setting struct {
KEYS KEYS `json:"KEYS,omitempty"`
RIP RIP `json:"RIP,omitempty"`
TIMEP TIMEP `json:"TIMEP,omitempty"`
ATP ATP `json:"ATP,omitempty"`
SSP SSP `json:"SSP,omitempty"`
GSP GSP `json:"GSP,omitempty"`
UMP UMP `json:"UMP,omitempty"`
ETHERNET ETHERNET `json:"ETHERNET,omitempty"`
WIFI WIFI `json:"WIFI,omitempty"`
M3G M3G `json:"M3G,omitempty"`
MCMS MCMS `json:"MCMS,omitempty"`
SUBSTRNET SUBSTRNET `json:"SUBSTRNET,omitempty"`
DOSD DOSD `json:"DOSD,omitempty"`
AR AR `json:"AR,omitempty"`
MAIN []VEC `json:"MAIN,omitempty"`
EOSD []EOSD `json:"EOSD,omitempty"`
IOP []IOP `json:"IOP,omitempty"`
SAP SAP `json:"SAP,omitempty"`
UAP UAP `json:"UAP,omitempty"`
PGDSM PGDSM `json:"PGDSM,omitempty"`
PVLAS PVLAS `json:"PVLAS,omitempty"`
PMDAS PMDAS `json:"PMDAS,omitempty"`
VS VS `json:"VS,omitempty"`
PSI PSI `json:"PSI,omitempty"`
SWUS SWUS `json:"SWUS,omitempty"`
DSM DSM `json:"DSM,omitempty"`
} }

View File

@ -1,5 +1,12 @@
package n9m package n9m
import (
"encoding/json"
"fmt"
"strconv"
"time"
)
type PayloadType uint8 type PayloadType uint8
const ( const (
@ -13,10 +20,28 @@ const (
PayloadTypeTransmissionSubStream PayloadType = 15 PayloadTypeTransmissionSubStream PayloadType = 15
PayloadTypeRecordingSubStream PayloadType = 16 PayloadTypeRecordingSubStream PayloadType = 16
PayloadTypeBlackBox PayloadType = 17 PayloadTypeBlackBox PayloadType = 17
PayloadTypeGPS PayloadType = 22 PayloadTypeSpecial PayloadType = 22
PayloadTypeMaintainData PayloadType = 30 PayloadTypeMaintainData PayloadType = 30
) )
type SpecialPayloadType uint16
const (
SpecialPayloadTypeHeartbeat SpecialPayloadType = iota
SpecialPayloadTypeHeartbeatWithoutBody
SpecialPayloadTypeGPS
SpecialPayloadTypeMileage
SpecialPayloadTypeEnvironmentalQuality
SpecialPayloadTypeDrivingPosture
SpecialPayloadTypeScanningGun
SpecialPayloadTypeOil
SpecialPayloadTypeGDS
SpecialPayloadTypeGPSToBWS
SpecialPayloadTypeCANBOX
SpecialPayloadTypeGSenor
SpecialPayloadTypeAckGPS
)
type Message struct { type Message struct {
Module string `json:"MODULE"` Module string `json:"MODULE"`
Session string `json:"SESSION"` Session string `json:"SESSION"`
@ -25,6 +50,74 @@ type Message struct {
Response interface{} `json:"RESPONSE,omitempty"` Response interface{} `json:"RESPONSE,omitempty"`
} }
// 3.4.5.27.1
type GPSData struct {
GPSStatus uint8
Expand uint8
Real uint8
Longitude float64
Latitude float64
Speed float64
Direction float64
Altitude int32
Time time.Time
}
func (g *GPSData) MarshalJSON() ([]byte, error) {
var alias struct {
GPSStatus uint8 `json:"V"`
Longitude string `json:"J"`
Latitude string `json:"W"`
Speed uint `json:"S"`
Direction uint `json:"C"`
Altitude int32 `json:"H"`
Time string `json:"T"`
}
alias.GPSStatus = g.GPSStatus
alias.Longitude = fmt.Sprintf("%.6f", g.Longitude)
alias.Latitude = fmt.Sprintf("%.6f", g.Latitude)
alias.Speed = uint(g.Speed * 100)
alias.Direction = uint(g.Direction * 100)
alias.Altitude = g.Altitude
alias.Time = g.Time.Format("20060102150405")
return json.Marshal(alias)
}
func (g *GPSData) UnmarshalJSON(data []byte) (err error) {
var alias struct {
GPSStatus uint8 `json:"V"`
Longitude string `json:"J"`
Latitude string `json:"W"`
Speed uint `json:"S"`
Direction uint `json:"C"`
Altitude int32 `json:"H"`
Time string `json:"T"`
}
if err = json.Unmarshal(data, &alias); err != nil {
return
}
g.GPSStatus = alias.GPSStatus
if g.Longitude, err = strconv.ParseFloat(alias.Longitude, 64); err != nil {
return fmt.Errorf("invalid longitude: %w", err)
}
if g.Latitude, err = strconv.ParseFloat(alias.Latitude, 64); err != nil {
return fmt.Errorf("invalid latitude: %w", err)
}
g.Speed = float64(alias.Speed) / 100.0
g.Direction = float64(alias.Direction) / 100.0
g.Altitude = alias.Altitude
g.Time, _ = time.Parse("20060102150405", alias.Time)
return nil
}
type Package struct { type Package struct {
Version uint8 Version uint8
EncryptionFlag bool EncryptionFlag bool
@ -32,10 +125,12 @@ type Package struct {
CSRCCount uint8 CSRCCount uint8
PayloadType PayloadType PayloadType PayloadType
SSRC uint16 SSRC SpecialPayloadType
Reserved uint64 Reserved uint64
CSRC [16]uint64 CSRC [16]uint64
GPS GPSData
payloadLen uint64 payloadLen uint64
Payload Message Payload Message
RawPayload []byte RawPayload []byte