feat(protocol): carry stream policy and gateway telemetry
This commit is contained in:
+383
-27
@@ -13,7 +13,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const SchemaSHA256 = "e98c75ef81bbeac6be2b8f11202c1ffecec0aa515b48576a26756290e99d5dd8"
|
||||
const SchemaSHA256 = "fe4d6be69665f09f04e2cd89273ebc5d2dddf4cde98b94a9d8c4f726504ffe1b"
|
||||
const ProtocolVersion = "1.0.0"
|
||||
const CurrentWireVersion = "1"
|
||||
const NMinus1WireVersion = "0"
|
||||
@@ -191,13 +191,14 @@ type GatewayDrain struct {
|
||||
}
|
||||
|
||||
type GatewayHeartbeat struct {
|
||||
Version string `json:"version"`
|
||||
GatewayID string `json:"gateway_id"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
ObservedAt string `json:"observed_at"`
|
||||
ActiveConnections int64 `json:"active_connections"`
|
||||
EgressKbps int64 `json:"egress_kbps"`
|
||||
State string `json:"state"`
|
||||
Version string `json:"version"`
|
||||
GatewayID string `json:"gateway_id"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
ObservedAt string `json:"observed_at"`
|
||||
ActiveConnections int64 `json:"active_connections"`
|
||||
EgressKbps int64 `json:"egress_kbps"`
|
||||
State string `json:"state"`
|
||||
Telemetry GatewayTelemetry `json:"telemetry"`
|
||||
}
|
||||
|
||||
type GatewayRegistration struct {
|
||||
@@ -216,6 +217,27 @@ type GatewayRegistration struct {
|
||||
Capabilities CapabilityProfile `json:"capabilities"`
|
||||
}
|
||||
|
||||
type GatewayTelemetry struct {
|
||||
AdmittedSessions int64 `json:"admitted_sessions"`
|
||||
AdmissionRejects int64 `json:"admission_rejects"`
|
||||
Reconnects int64 `json:"reconnects"`
|
||||
DrainTransitions int64 `json:"drain_transitions"`
|
||||
MediaDrops int64 `json:"media_drops"`
|
||||
MediaPackets int64 `json:"media_packets"`
|
||||
MediaBytes int64 `json:"media_bytes"`
|
||||
QueueDelayMicros int64 `json:"queue_delay_micros"`
|
||||
ProcessingDelayMicros int64 `json:"processing_delay_micros"`
|
||||
ProcessingSamples int64 `json:"processing_samples"`
|
||||
PacingDelayMicros int64 `json:"pacing_delay_micros"`
|
||||
ProviderErrors int64 `json:"provider_errors"`
|
||||
InputRejected int64 `json:"input_rejected"`
|
||||
ControlRttMicros int64 `json:"control_rtt_micros"`
|
||||
ControlJitterMicros int64 `json:"control_jitter_micros"`
|
||||
ControlLossPpm int64 `json:"control_loss_ppm"`
|
||||
PendingReliable int64 `json:"pending_reliable"`
|
||||
ProviderState string `json:"provider_state"`
|
||||
}
|
||||
|
||||
type GrantReference struct {
|
||||
OpaqueValue string `json:"opaque_value"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
@@ -265,25 +287,26 @@ type PageInfo struct {
|
||||
}
|
||||
|
||||
type ProviderSessionWork struct {
|
||||
Version string `json:"version"`
|
||||
SessionID string `json:"session_id"`
|
||||
GatewayID string `json:"gateway_id"`
|
||||
ReconnectSequence int64 `json:"reconnect_sequence"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
ProviderProfile string `json:"provider_profile"`
|
||||
ProviderIdentity string `json:"provider_identity"`
|
||||
PolicyVersionID string `json:"policy_version_id"`
|
||||
ApplicationID string `json:"application_id"`
|
||||
ClientID string `json:"client_id"`
|
||||
ManagementHost string `json:"management_host"`
|
||||
ManagementPort int64 `json:"management_port"`
|
||||
StreamHost string `json:"stream_host"`
|
||||
StreamPort int64 `json:"stream_port"`
|
||||
ClientCertificatePem string `json:"client_certificate_pem"`
|
||||
ClientPrivateKeyPem string `json:"client_private_key_pem"`
|
||||
ServerCertificatePem string `json:"server_certificate_pem"`
|
||||
ClipboardPolicy ClipboardPolicy `json:"clipboard_policy"`
|
||||
ProviderApplicationTerminationAllowed bool `json:"provider_application_termination_allowed"`
|
||||
Version string `json:"version"`
|
||||
SessionID string `json:"session_id"`
|
||||
GatewayID string `json:"gateway_id"`
|
||||
ReconnectSequence int64 `json:"reconnect_sequence"`
|
||||
ExpiresAt string `json:"expires_at"`
|
||||
ProviderProfile string `json:"provider_profile"`
|
||||
ProviderIdentity string `json:"provider_identity"`
|
||||
PolicyVersionID string `json:"policy_version_id"`
|
||||
StreamPolicy ProviderStreamPolicy `json:"stream_policy"`
|
||||
ApplicationID string `json:"application_id"`
|
||||
ClientID string `json:"client_id"`
|
||||
ManagementHost string `json:"management_host"`
|
||||
ManagementPort int64 `json:"management_port"`
|
||||
StreamHost string `json:"stream_host"`
|
||||
StreamPort int64 `json:"stream_port"`
|
||||
ClientCertificatePem string `json:"client_certificate_pem"`
|
||||
ClientPrivateKeyPem string `json:"client_private_key_pem"`
|
||||
ServerCertificatePem string `json:"server_certificate_pem"`
|
||||
ClipboardPolicy ClipboardPolicy `json:"clipboard_policy"`
|
||||
ProviderApplicationTerminationAllowed bool `json:"provider_application_termination_allowed"`
|
||||
}
|
||||
|
||||
type ProviderState struct {
|
||||
@@ -294,6 +317,15 @@ type ProviderState struct {
|
||||
Channels []string `json:"channels"`
|
||||
}
|
||||
|
||||
type ProviderStreamPolicy struct {
|
||||
ResolutionWidth int64 `json:"resolution_width"`
|
||||
ResolutionHeight int64 `json:"resolution_height"`
|
||||
Fps int64 `json:"fps"`
|
||||
Codec string `json:"codec"`
|
||||
BitrateKbps int64 `json:"bitrate_kbps"`
|
||||
AudioEnabled bool `json:"audio_enabled"`
|
||||
}
|
||||
|
||||
type ReauthGrant struct {
|
||||
Token string `json:"token"`
|
||||
Purpose string `json:"purpose"`
|
||||
@@ -2370,6 +2402,12 @@ func (v GatewayHeartbeat) Validate() error {
|
||||
if v.State != "" && !(v.State == "ready" || v.State == "draining" || v.State == "offline") {
|
||||
violations = append(violations, FieldViolation{Field: "state", Code: "invalid_value"})
|
||||
}
|
||||
if reflect.DeepEqual(v.Telemetry, GatewayTelemetry{}) {
|
||||
violations = append(violations, FieldViolation{Field: "telemetry", Code: "required"})
|
||||
}
|
||||
if err := v.Telemetry.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "telemetry", Code: "invalid_object"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
@@ -2403,6 +2441,9 @@ func DecodeGatewayHeartbeat(data []byte) (GatewayHeartbeat, error) {
|
||||
if raw, ok := fields["state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "state", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["telemetry"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "telemetry", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "version", Code: "required"}}}
|
||||
}
|
||||
@@ -2623,6 +2664,210 @@ func EncodeGatewayRegistration(value GatewayRegistration) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v GatewayTelemetry) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.AdmittedSessions != 0 && v.AdmittedSessions < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "admitted_sessions", Code: "minimum"})
|
||||
}
|
||||
if v.AdmittedSessions > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "admitted_sessions", Code: "maximum"})
|
||||
}
|
||||
if v.AdmissionRejects != 0 && v.AdmissionRejects < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "admission_rejects", Code: "minimum"})
|
||||
}
|
||||
if v.AdmissionRejects > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "admission_rejects", Code: "maximum"})
|
||||
}
|
||||
if v.Reconnects != 0 && v.Reconnects < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "reconnects", Code: "minimum"})
|
||||
}
|
||||
if v.Reconnects > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "reconnects", Code: "maximum"})
|
||||
}
|
||||
if v.DrainTransitions != 0 && v.DrainTransitions < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "drain_transitions", Code: "minimum"})
|
||||
}
|
||||
if v.DrainTransitions > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "drain_transitions", Code: "maximum"})
|
||||
}
|
||||
if v.MediaDrops != 0 && v.MediaDrops < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "media_drops", Code: "minimum"})
|
||||
}
|
||||
if v.MediaDrops > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "media_drops", Code: "maximum"})
|
||||
}
|
||||
if v.MediaPackets != 0 && v.MediaPackets < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "media_packets", Code: "minimum"})
|
||||
}
|
||||
if v.MediaPackets > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "media_packets", Code: "maximum"})
|
||||
}
|
||||
if v.MediaBytes != 0 && v.MediaBytes < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "media_bytes", Code: "minimum"})
|
||||
}
|
||||
if v.MediaBytes > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "media_bytes", Code: "maximum"})
|
||||
}
|
||||
if v.QueueDelayMicros != 0 && v.QueueDelayMicros < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "queue_delay_micros", Code: "minimum"})
|
||||
}
|
||||
if v.QueueDelayMicros > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "queue_delay_micros", Code: "maximum"})
|
||||
}
|
||||
if v.ProcessingDelayMicros != 0 && v.ProcessingDelayMicros < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "processing_delay_micros", Code: "minimum"})
|
||||
}
|
||||
if v.ProcessingDelayMicros > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "processing_delay_micros", Code: "maximum"})
|
||||
}
|
||||
if v.ProcessingSamples != 0 && v.ProcessingSamples < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "processing_samples", Code: "minimum"})
|
||||
}
|
||||
if v.ProcessingSamples > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "processing_samples", Code: "maximum"})
|
||||
}
|
||||
if v.PacingDelayMicros != 0 && v.PacingDelayMicros < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "pacing_delay_micros", Code: "minimum"})
|
||||
}
|
||||
if v.PacingDelayMicros > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "pacing_delay_micros", Code: "maximum"})
|
||||
}
|
||||
if v.ProviderErrors != 0 && v.ProviderErrors < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "provider_errors", Code: "minimum"})
|
||||
}
|
||||
if v.ProviderErrors > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "provider_errors", Code: "maximum"})
|
||||
}
|
||||
if v.InputRejected != 0 && v.InputRejected < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "input_rejected", Code: "minimum"})
|
||||
}
|
||||
if v.InputRejected > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "input_rejected", Code: "maximum"})
|
||||
}
|
||||
if v.ControlRttMicros != 0 && v.ControlRttMicros < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "control_rtt_micros", Code: "minimum"})
|
||||
}
|
||||
if v.ControlRttMicros > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "control_rtt_micros", Code: "maximum"})
|
||||
}
|
||||
if v.ControlJitterMicros != 0 && v.ControlJitterMicros < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "control_jitter_micros", Code: "minimum"})
|
||||
}
|
||||
if v.ControlJitterMicros > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "control_jitter_micros", Code: "maximum"})
|
||||
}
|
||||
if v.ControlLossPpm != 0 && v.ControlLossPpm < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "control_loss_ppm", Code: "minimum"})
|
||||
}
|
||||
if v.ControlLossPpm > 1000000 {
|
||||
violations = append(violations, FieldViolation{Field: "control_loss_ppm", Code: "maximum"})
|
||||
}
|
||||
if v.PendingReliable != 0 && v.PendingReliable < 0 {
|
||||
violations = append(violations, FieldViolation{Field: "pending_reliable", Code: "minimum"})
|
||||
}
|
||||
if v.PendingReliable > 9223372036854775807 {
|
||||
violations = append(violations, FieldViolation{Field: "pending_reliable", Code: "maximum"})
|
||||
}
|
||||
if v.ProviderState == "" {
|
||||
violations = append(violations, FieldViolation{Field: "provider_state", Code: "required"})
|
||||
}
|
||||
if v.ProviderState != "" && !(v.ProviderState == "unknown" || v.ProviderState == "starting" || v.ProviderState == "ready" || v.ProviderState == "disconnected" || v.ProviderState == "terminating" || v.ProviderState == "terminated" || v.ProviderState == "cleanup_pending" || v.ProviderState == "failed") {
|
||||
violations = append(violations, FieldViolation{Field: "provider_state", Code: "invalid_value"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeGatewayTelemetry(data []byte) (GatewayTelemetry, error) {
|
||||
var value GatewayTelemetry
|
||||
if len(data) > 1024*1024 {
|
||||
return value, errors.New("protocol payload exceeds limit")
|
||||
}
|
||||
var fields map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &fields); err != nil {
|
||||
return value, err
|
||||
}
|
||||
if raw, ok := fields["admission_rejects"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "admission_rejects", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["admitted_sessions"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "admitted_sessions", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["control_jitter_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "control_jitter_micros", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["control_loss_ppm"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "control_loss_ppm", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["control_rtt_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "control_rtt_micros", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["drain_transitions"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "drain_transitions", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["input_rejected"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "input_rejected", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["media_bytes"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "media_bytes", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["media_drops"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "media_drops", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["media_packets"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "media_packets", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["pacing_delay_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "pacing_delay_micros", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["pending_reliable"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "pending_reliable", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["processing_delay_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "processing_delay_micros", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["processing_samples"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "processing_samples", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["provider_errors"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_errors", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["provider_state"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "provider_state", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["queue_delay_micros"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "queue_delay_micros", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["reconnects"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "reconnects", Code: "required"}}}
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
return value, err
|
||||
}
|
||||
var trailing any
|
||||
if err := decoder.Decode(&trailing); err != io.EOF {
|
||||
if err == nil {
|
||||
return value, errors.New("trailing JSON value")
|
||||
}
|
||||
return value, err
|
||||
}
|
||||
if err := value.Validate(); err != nil {
|
||||
return value, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func EncodeGatewayTelemetry(value GatewayTelemetry) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v GrantReference) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.OpaqueValue == "" {
|
||||
@@ -3287,6 +3532,12 @@ func (v ProviderSessionWork) Validate() error {
|
||||
if len(v.PolicyVersionID) > 128 {
|
||||
violations = append(violations, FieldViolation{Field: "policy_version_id", Code: "max_length"})
|
||||
}
|
||||
if reflect.DeepEqual(v.StreamPolicy, ProviderStreamPolicy{}) {
|
||||
violations = append(violations, FieldViolation{Field: "stream_policy", Code: "required"})
|
||||
}
|
||||
if err := v.StreamPolicy.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "stream_policy", Code: "invalid_object"})
|
||||
}
|
||||
if v.ApplicationID == "" {
|
||||
violations = append(violations, FieldViolation{Field: "application_id", Code: "required"})
|
||||
}
|
||||
@@ -3440,6 +3691,9 @@ func DecodeProviderSessionWork(data []byte) (ProviderSessionWork, error) {
|
||||
if raw, ok := fields["stream_host"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "stream_host", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["stream_policy"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "stream_policy", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["stream_port"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "stream_port", Code: "required"}}}
|
||||
}
|
||||
@@ -3555,6 +3809,108 @@ func EncodeProviderState(value ProviderState) ([]byte, error) {
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v ProviderStreamPolicy) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.ResolutionWidth == 0 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "required"})
|
||||
}
|
||||
if v.ResolutionWidth != 0 && v.ResolutionWidth < 320 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "minimum"})
|
||||
}
|
||||
if v.ResolutionWidth > 16384 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "maximum"})
|
||||
}
|
||||
if v.ResolutionHeight == 0 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "required"})
|
||||
}
|
||||
if v.ResolutionHeight != 0 && v.ResolutionHeight < 200 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "minimum"})
|
||||
}
|
||||
if v.ResolutionHeight > 8640 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "maximum"})
|
||||
}
|
||||
if v.Fps == 0 {
|
||||
violations = append(violations, FieldViolation{Field: "fps", Code: "required"})
|
||||
}
|
||||
if v.Fps != 0 && v.Fps < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "fps", Code: "minimum"})
|
||||
}
|
||||
if v.Fps > 240 {
|
||||
violations = append(violations, FieldViolation{Field: "fps", Code: "maximum"})
|
||||
}
|
||||
if v.Codec == "" {
|
||||
violations = append(violations, FieldViolation{Field: "codec", Code: "required"})
|
||||
}
|
||||
if v.Codec != "" && !(v.Codec == "H264" || v.Codec == "HEVC" || v.Codec == "AV1") {
|
||||
violations = append(violations, FieldViolation{Field: "codec", Code: "invalid_value"})
|
||||
}
|
||||
if v.BitrateKbps == 0 {
|
||||
violations = append(violations, FieldViolation{Field: "bitrate_kbps", Code: "required"})
|
||||
}
|
||||
if v.BitrateKbps != 0 && v.BitrateKbps < 100 {
|
||||
violations = append(violations, FieldViolation{Field: "bitrate_kbps", Code: "minimum"})
|
||||
}
|
||||
if v.BitrateKbps > 1000000 {
|
||||
violations = append(violations, FieldViolation{Field: "bitrate_kbps", Code: "maximum"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeProviderStreamPolicy(data []byte) (ProviderStreamPolicy, error) {
|
||||
var value ProviderStreamPolicy
|
||||
if len(data) > 1024*1024 {
|
||||
return value, errors.New("protocol payload exceeds limit")
|
||||
}
|
||||
var fields map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &fields); err != nil {
|
||||
return value, err
|
||||
}
|
||||
if raw, ok := fields["audio_enabled"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "audio_enabled", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["bitrate_kbps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "bitrate_kbps", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["codec"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "codec", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["fps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "fps", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["resolution_height"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "resolution_height", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["resolution_width"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "resolution_width", Code: "required"}}}
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
return value, err
|
||||
}
|
||||
var trailing any
|
||||
if err := decoder.Decode(&trailing); err != io.EOF {
|
||||
if err == nil {
|
||||
return value, errors.New("trailing JSON value")
|
||||
}
|
||||
return value, err
|
||||
}
|
||||
if err := value.Validate(); err != nil {
|
||||
return value, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func EncodeProviderStreamPolicy(value ProviderStreamPolicy) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v ReauthGrant) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.Token == "" {
|
||||
|
||||
Reference in New Issue
Block a user