From 33c9902cc4a83e7f165311a9c28d6e845004b686 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 25 Feb 2026 10:08:12 +0000 Subject: [PATCH 01/33] Start with v1.1.x schemas --- .../schemas/activation-response-schema.json | 44 ++++ .../APIs/schemas/activation-schema.json | 35 ++++ .../schemas/bulk-receiver-post-schema.json | 24 +++ .../APIs/schemas/bulk-response-schema.json | 36 ++++ .../APIs/schemas/bulk-sender-post-schema.json | 24 +++ .../APIs/schemas/connectionapi-base.json | 16 ++ .../APIs/schemas/connectionapi-bulk.json | 16 ++ .../APIs/schemas/connectionapi-receiver.json | 18 ++ .../APIs/schemas/connectionapi-sender.json | 19 ++ .../APIs/schemas/connectionapi-single.json | 16 ++ .../APIs/schemas/constraint-schema.json | 62 ++++++ .../APIs/schemas/constraints-schema-mqtt.json | 43 ++++ .../APIs/schemas/constraints-schema-rtp.json | 81 ++++++++ .../schemas/constraints-schema-websocket.json | 23 +++ .../APIs/schemas/constraints-schema.json | 24 +++ .../is-05/v1.2.x/APIs/schemas/error.json | 27 +++ .../schemas/receiver-response-schema.json | 37 ++++ .../APIs/schemas/receiver-stage-schema.json | 30 +++ .../APIs/schemas/receiver-transport-file.json | 27 +++ .../schemas/receiver_transport_params.json | 30 +++ .../receiver_transport_params_dash.json | 3 + .../receiver_transport_params_ext.json | 11 + .../receiver_transport_params_mqtt.json | 83 ++++++++ .../receiver_transport_params_rtp.json | 152 ++++++++++++++ .../receiver_transport_params_websocket.json | 42 ++++ .../APIs/schemas/sender-receiver-base.json | 11 + .../APIs/schemas/sender-response-schema.json | 33 +++ .../APIs/schemas/sender-stage-schema.json | 27 +++ .../APIs/schemas/sender_transport_params.json | 30 +++ .../schemas/sender_transport_params_dash.json | 3 + .../schemas/sender_transport_params_ext.json | 11 + .../schemas/sender_transport_params_mqtt.json | 83 ++++++++ .../schemas/sender_transport_params_rtp.json | 191 ++++++++++++++++++ .../sender_transport_params_websocket.json | 45 +++++ .../transporttype-response-schema.json | 17 ++ 35 files changed, 1374 insertions(+) create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/activation-response-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/activation-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-receiver-post-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-response-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-sender-post-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-base.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-bulk.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-receiver.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-sender.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-single.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/constraint-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-mqtt.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-rtp.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-websocket.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/error.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-response-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-stage-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-transport-file.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_dash.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_ext.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mqtt.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_rtp.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_websocket.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender-receiver-base.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender-response-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender-stage-schema.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_dash.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_ext.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mqtt.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_rtp.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_websocket.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-response-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-response-schema.json new file mode 100644 index 000000000..5bb0f4f74 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-response-schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Parameters concerned with activation of the transport parameters", + "type": "object", + "additionalProperties": false, + "required": [ + "mode", + "requested_time", + "activation_time" + ], + "properties": { + "mode": { + "description": "Mode of activation: immediate (on message receipt), scheduled_absolute (when internal clock >= requested_time), scheduled_relative (when internal clock >= time of message receipt + requested_time), or null (no activation scheduled). This parameter returns to null on the staged endpoint once an activation is completed or when it is explicitly set to null. For immediate activations, in the response to the PATCH request this field will be set to 'activate_immediate', but will be null in response to any subsequent GET requests.", + "anyOf": [{ + "type": "string", + "enum": [ + "activate_immediate", + "activate_scheduled_absolute", + "activate_scheduled_relative" + ] + }, { + "type": "null" + }] + }, + "requested_time": { + "description": "String formatted TAI timestamp (:) indicating time (absolute or relative) for activation requested. This field returns to null once the activation is completed on the staged endpoint or when the resource is unlocked by setting the activation mode to null. For an immediate activation this field will always be null on the staged endpoint, even in the response to the PATCH request.", + "anyOf": [{ + "type": "string", + "pattern": "^[0-9]+:[0-9]+$" + }, { + "type": "null" + }] + }, + "activation_time": { + "description": "String formatted TAI timestamp (:) indicating the absolute time the sender or receiver will or did actually activate for scheduled activations, or the time activation occurred for immediate activations. On the staged endpoint this field returns to null once the activation is completed or when the resource is unlocked by setting the activation mode to null. For immediate activations on the staged endpoint this property will be the time the activation actually occurred in the response to the PATCH request, but null in response to any GET requests thereafter.", + "anyOf": [{ + "type": "string", + "pattern": "^[0-9]+:[0-9]+$" + }, { + "type": "null" + }] + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-schema.json new file mode 100644 index 000000000..27cefb795 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "title": "Activation resource", + "description": "Parameters concerned with activation of the transport parameters", + "type": "object", + "additionalProperties": false, + "required": [ + "mode" + ], + "properties": { + "mode": { + "description": "Mode of activation: immediate (on message receipt), scheduled_absolute (when internal clock >= requested_time), scheduled_relative (when internal clock >= time of message receipt + requested_time), or null (no activation scheduled)", + "anyOf": [{ + "type": "string", + "enum": [ + "activate_immediate", + "activate_scheduled_absolute", + "activate_scheduled_relative" + ] + }, { + "type": "null" + }] + }, + "requested_time": { + "description": "String formatted TAI timestamp (:) indicating time (absolute or relative) for activation. Should be null or not present if 'mode' is null.", + "anyOf": [{ + "type": "string", + "pattern": "^[0-9]+:[0-9]+$" + }, { + "type": "null" + }] + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-receiver-post-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-receiver-post-schema.json new file mode 100644 index 000000000..4b9be73a2 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-receiver-post-schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes a bulk receiver update resource", + "title": "Bulk receiver resource", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "params" + ], + "properties": { + "id": { + "description": "ID of the target receiver to apply parameters to", + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "params": { + "$ref": "receiver-stage-schema.json" + } + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-response-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-response-schema.json new file mode 100644 index 000000000..46ab6d531 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-response-schema.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes a response to a bulk activation request", + "title": "Bulk activation response", + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "code" + ], + "properties": { + "id": { + "description": "ID of a device to be activated", + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "code": { + "description": "HTTP status code that would have resulted from an individual activation on this device", + "type": "integer", + "anyOf": [ + { "minimum": 200, "maximum": 299 }, + { "minimum": 400, "maximum": 599 } + ] + }, + "error": { + "description": "Human readable message which is suitable for user interface display, and helpful to the user. Only included if 'code' indicates an error state", + "type": "string" + }, + "debug": { + "description": "Debug information which may assist a programmer working with the API. Only included if 'code' indicates an error state", + "type": ["null", "string"] + } + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-sender-post-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-sender-post-schema.json new file mode 100644 index 000000000..b5fb0dca0 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-sender-post-schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes a bulk sender update resource", + "title": "Bulk sender resource", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "params" + ], + "properties": { + "id": { + "description": "ID of the target sender to apply parameters to", + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "params": { + "$ref": "sender-stage-schema.json" + } + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-base.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-base.json new file mode 100644 index 000000000..400106144 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-base.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Connection API base resource", + "title": "Connection API base resource", + "items": { + "type": "string", + "enum": [ + "bulk/", + "single/" + ] + }, + "minItems": 2, + "maxItems": 2, + "uniqueItems": true +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-bulk.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-bulk.json new file mode 100644 index 000000000..03fc8f778 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-bulk.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Connection API /bulk base resource", + "title": "Connection API /bulk base resource", + "items": { + "type": "string", + "enum": [ + "senders/", + "receivers/" + ] + }, + "minItems": 2, + "maxItems": 2, + "uniqueItems": true +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-receiver.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-receiver.json new file mode 100644 index 000000000..99cd2d347 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-receiver.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Connection API /single/receivers/{receiverId} base resource", + "title": "Connection API /single/receivers/{receiverId} base resource", + "items": { + "type": "string", + "enum": [ + "constraints/", + "staged/", + "active/", + "transporttype/" + ] + }, + "minItems": 4, + "maxItems": 4, + "uniqueItems": true +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-sender.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-sender.json new file mode 100644 index 000000000..8d072dbb7 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-sender.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Connection API /single/senders/{senderId} base resource", + "title": "Connection API /single/senders/{senderId} base resource", + "items": { + "type": "string", + "enum": [ + "constraints/", + "staged/", + "active/", + "transportfile/", + "transporttype/" + ] + }, + "minItems": 5, + "maxItems": 5, + "uniqueItems": true +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-single.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-single.json new file mode 100644 index 000000000..0d36e835f --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-single.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Connection API /single base resource", + "title": "Connection API /single base resource", + "items": { + "type": "string", + "enum": [ + "senders/", + "receivers/" + ] + }, + "minItems": 2, + "maxItems": 2, + "uniqueItems": true +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraint-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraint-schema.json new file mode 100644 index 000000000..fb9631173 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraint-schema.json @@ -0,0 +1,62 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Definition of a single constraint record", + "title": "Constraint", + "definitions": { + "constraint": { + "type": "object", + "description": "The constraints for a single transport parameter", + "properties": { + "maximum": { + "description": "The inclusive maximum value the parameter can be set to", + "type": [ + "integer", + "number" + ] + }, + "minimum": { + "description": "The inclusive minimum value the parameter can be set to", + "type": [ + "integer", + "number" + ] + }, + "enum": { + "description": "An array of allowed values", + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "integer" + }, + { + "type": "null" + }, + { + "type": "number" + }, + { + "type": "string" + } + ] + } + }, + "pattern": { + "description": "A regex pattern that must be satisfied for this parameter", + "type": "string", + "format": "regex" + }, + "description": { + "description": "A human readable string describing the constraint (optional)", + "type": "string" + } + }, + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-mqtt.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-mqtt.json new file mode 100644 index 000000000..1219dc19e --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-mqtt.json @@ -0,0 +1,43 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Used to express the dynamic constraints on MQTT transport parameters. These constraints may be set and changed at run time. Every transport parameter must have an entry, even if it is only an empty object.", + "required": [ + "broker_topic", + "broker_protocol", + "broker_authorization", + "connection_status_broker_topic" + ], + "additionalProperties": false, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$":{ + "$ref": "constraint-schema.json#/definitions/constraint" + } + }, + "properties": { + "destination_host": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "source_host": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "broker_topic": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "broker_protocol": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "broker_authorization": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "connection_status_broker_topic": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "source_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "destination_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-rtp.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-rtp.json new file mode 100644 index 000000000..a20613be6 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-rtp.json @@ -0,0 +1,81 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Used to express the dynamic constraints on RTP transport parameters. These constraints may be set and changed at run time. Every transport parameter must have an entry, even if it is only an empty object.", + "required": [ + "source_ip", + "destination_port", + "rtp_enabled" + ], + "additionalProperties": false, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$":{ + "$ref": "constraint-schema.json#/definitions/constraint" + } + }, + "properties": { + "multicast_ip": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "destination_ip": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "destination_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "source_ip": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "interface_ip": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "source_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec_enabled": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec_destination_ip": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec_mode": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec_type": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec_block_width": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec_block_height": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec1D_destination_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec2D_destination_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec1D_source_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "fec2D_source_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "rtcp_enabled": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "rtcp_destination_ip": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "rtcp_destination_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "rtcp_source_port": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "rtp_enabled": { + "$ref": "constraint-schema.json#/definitions/constraint" + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-websocket.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-websocket.json new file mode 100644 index 000000000..cf3215eb8 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-websocket.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Used to express the dynamic constraints on WebSocket transport parameters. These constraints may be set and changed at run time. Every transport parameter must have an entry, even if it is only an empty object.", + "required": [ + "connection_uri", + "connection_authorization" + ], + "additionalProperties": false, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$":{ + "$ref": "constraint-schema.json#/definitions/constraint" + } + }, + "properties": { + "connection_uri": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "connection_authorization": { + "$ref": "constraint-schema.json#/definitions/constraint" + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema.json new file mode 100644 index 000000000..d43a11db4 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Used to express the dynamic constraints on transport parameters. These constraints may be set and changed at run time. Parameters must also conform with constraints inferred from the specification. Every transport parameter must have an entry, even if it is only an empty object.", + "title": "Constraints", + "anyOf": [{ + "type": "array", + "items": { + "$ref": "constraints-schema-rtp.json" + } + }, + { + "type": "array", + "items": { + "$ref": "constraints-schema-websocket.json" + } + }, + { + "type": "array", + "items": { + "$ref": "constraints-schema-mqtt.json" + } + } + ] +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/error.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/error.json new file mode 100644 index 000000000..402147b52 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/error.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Describes the standard error response which is returned with HTTP codes 400 and above", + "title": "Error response", + "required": [ + "code", + "error", + "debug" + ], + "properties": { + "code": { + "description": "HTTP error code", + "type": "integer", + "minimum": 400, + "maximum": 599 + }, + "error": { + "description": "Human readable message which is suitable for user interface display, and helpful to the user", + "type": "string" + }, + "debug": { + "description": "Debug information which may assist a programmer working with the API", + "type": ["null", "string"] + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-response-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-response-schema.json new file mode 100644 index 000000000..df3343fcf --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-response-schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Describes a receiver", + "title": "Receiver resource", + "additionalProperties": false, + "required": [ + "sender_id", + "master_enable", + "activation", + "transport_file", + "transport_params" + ], + "properties": { + "sender_id": { + "description": "ID of the Sender subscribed to by this Receiver. This will be null if the receiver has not been configured to receive anything, or if it is receiving from a non-NMOS sender.", + "type": [ + "string", + "null" + ], + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "master_enable": { + "description": "Master on/off control for receiver", + "type": "boolean" + }, + "activation": { + "$ref": "activation-response-schema.json" + }, + "transport_file": { + "$ref": "receiver-transport-file.json" + }, + "transport_params": { + "$ref": "receiver_transport_params.json" + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-stage-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-stage-schema.json new file mode 100644 index 000000000..301719123 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-stage-schema.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Describes a receiver", + "title": "Receiver resource", + "additionalProperties": false, + "properties": { + "sender_id": { + "description": "ID of the Sender subscribed to by this Receiver. This will be null if the receiver has not been configured to receive anything, or if it is receiving from a non-NMOS sender.", + "type": [ + "string", + "null" + ], + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "master_enable": { + "description": "Master on/off control for receiver", + "type": "boolean" + }, + "activation": { + "$ref": "activation-schema.json" + }, + "transport_file": { + "$ref": "receiver-transport-file.json" + }, + "transport_params": { + "$ref": "receiver_transport_params.json" + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-transport-file.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-transport-file.json new file mode 100644 index 000000000..3c7be7c2e --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-transport-file.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Transport file parameters. 'data' and 'type' must both be strings or both be null. If 'type' is non-null 'data' is expected to contain a valid instance of the specified media type.", + "title": "Transport file", + "additionalProperties": false, + "required": [ + "data", + "type" + ], + "properties": { + "data": { + "description": "Content of the transport file", + "type": [ + "string", + "null" + ] + }, + "type": { + "description": "IANA assigned media type for file (e.g application/sdp)", + "type": [ + "string", + "null" + ] + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json new file mode 100644 index 000000000..3656ead3a --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Transport-specific parameters. If this parameter is included in a client request it must include the same number of array elements (or 'legs') as specified in the constraints. If no changes are required to a specific leg it must be included as an empty object ({}).", + "title": "Receiver Transport Parameters", + "anyOf": [{ + "type": "array", + "items": { + "$ref": "receiver_transport_params_rtp.json" + } + }, + { + "type": "array", + "items": { + "$ref": "receiver_transport_params_dash.json" + } + }, + { + "type": "array", + "items": { + "$ref": "receiver_transport_params_websocket.json" + } + }, + { + "type": "array", + "items": { + "$ref": "receiver_transport_params_mqtt.json" + } + } + ] +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_dash.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_dash.json new file mode 100644 index 000000000..ca07e7fce --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_dash.json @@ -0,0 +1,3 @@ +{ + "not": {} +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_ext.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_ext.json new file mode 100644 index 000000000..9b0dbb9b5 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_ext.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes external Receiver transport parameters defined in other AMWA specifications. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint.", + "title": "External Receiver Transport Parameters", + "type":[ + "string", + "boolean", + "null", + "number" + ] +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mqtt.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mqtt.json new file mode 100644 index 000000000..84f6548b1 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mqtt.json @@ -0,0 +1,83 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes MQTT Receiver transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint. MQTT Receivers must support all parameters in this schema.", + "title": "MQTT Receiver Transport Parameters", + "type": "object", + "title": "Receiver Input", + "properties": { + "source_host": { + "type": [ + "string", + "null" + ], + "description": "Hostname or IP hosting the MQTT broker. If the parameter is set to auto the Receiver should establish for itself which broker it should use, based on a discovery mechanism or its own internal configuration. A null value indicates that the Receiver has not yet been configured.", + "anyOf": [{ + "pattern": "^auto$" + }, + { + "format": "hostname" + }, + { + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "type": "null" + } + ] + }, + "source_port": { + "type": [ + "integer", + "string" + ], + "description": "Source port for MQTT traffic. If the parameter is set to auto the Receiver should establish for itself which broker it should use, based on a discovery mechanism or its own internal configuration.", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "broker_protocol": { + "type": "string", + "description": "Indication of whether TLS is used for communication with the broker. 'mqtt' indicates operation without TLS, and 'secure-mqtt' indicates use of TLS. If the parameter is set to auto the Receiver should establish for itself which protocol it should use, based on a discovery mechanism or its own internal configuration.", + "enum": [ + "auto", + "mqtt", + "secure-mqtt" + ] + }, + "broker_authorization": { + "type": [ + "string", + "boolean" + ], + "description": "Indication of whether authorization is used for communication with the broker. If the parameter is set to auto the Receiver should establish for itself whether authorization should be used, based on a discovery mechanism or its own internal configuration.", + "enum": [ + "auto", + true, + false + ] + }, + "broker_topic": { + "type": [ + "string", + "null" + ], + "description": "The topic which MQTT messages will be received from via the MQTT broker. A null value indicates that the Receiver has not yet been configured." + }, + "connection_status_broker_topic": { + "type": [ + "string", + "null" + ], + "description": "The topic used for MQTT status messages such as MQTT Last Will which are received via the MQTT broker. A null value indicates that the Receiver has not yet been configured, or is not using a connection status topic." + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "receiver_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_rtp.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_rtp.json new file mode 100644 index 000000000..ee119205c --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_rtp.json @@ -0,0 +1,152 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes RTP Receiver transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint. Receivers must support at least the `source_ip`, `interface_ip`, `rtp_enabled` and `destination_port` parameters, and must support the `multicast_ip` parameter if they are capable of multicast operation. Receivers supporting FEC and/or RTCP must support parameters prefixed with `fec` and `rtcp` respectively.", + "title": "RTP Receiver Transport Parameters", + "type": "object", + "title": "Receiver Input", + "properties": { + "source_ip": { + "type": [ + "string", + "null" + ], + "description": "Source IP address of RTP packets in unicast mode, source filter for source specific multicast. A null value indicates that the source IP address has not been configured in unicast mode, or the Receiver is in any-source multicast mode.", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "type": "null" + } + ] + }, + "multicast_ip": { + "type": [ + "string", + "null" + ], + "description": "IP multicast group address used in multicast operation only. Should be set to null during unicast operation. A null value indicates the parameter has not been configured, or the receiver is operating in unicast mode.", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "type": "null" + } + ] + }, + "interface_ip": { + "type": "string", + "description": "IP address of the network interface the receiver should use. The receiver should provide an enum in the constraints endpoint, which should contain the available interface addresses. If set to auto in multicast mode the receiver should determine which interface to use for itself, for example by using the routing tables. The behaviour of auto is undefined in unicast mode, and controllers should supply a specific interface address.", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "pattern": "^auto$" + } + ] + }, + "destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTP packets (auto = 5004 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "fec_enabled": { + "type": "boolean", + "description": "FEC on/off" + }, + "fec_destination_ip": { + "type": "string", + "description": "May be used if NAT is being used at the destination (auto = multicast_ip (multicast mode) or interface_ip (unicast mode) by default)", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "pattern": "^auto$" + } + ] + }, + "fec_mode": { + "type": "string", + "description": "forward error correction mode to apply. (auto = highest available number of dimensions by default)", + "enum": [ + "auto", + "1D", + "2D" + ] + }, + "fec1D_destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTP Column FEC packets (auto = RTP destination_port + 2 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "fec2D_destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTP Row FEC packets (auto = RTP destination_port + 4 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "rtcp_destination_ip": { + "type": "string", + "description": "Destination IP address of RTCP packets (auto = multicast_ip (multicast mode) or interface_ip (unicast mode) by default)", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "pattern": "^auto$" + } + ] + }, + "rtcp_enabled": { + "type": "boolean", + "description": "RTCP on/off" + }, + "rtcp_destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTCP packets (auto = RTP destination_port + 1 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "rtp_enabled": { + "type": "boolean", + "description": "RTP reception active/inactive" + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "receiver_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_websocket.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_websocket.json new file mode 100644 index 000000000..38f168143 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_websocket.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes WebSocket Receiver transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint. WebSocket Receivers must support all parameters in this schema.", + "title": "WebSocket Receiver Transport Parameters", + "type": "object", + "title": "Receiver Input", + "properties": { + "connection_uri": { + "type": [ + "string", + "null" + ], + "description": "URI hosting the WebSocket server as defined in RFC 6455 Section 3. A null value indicates that the receiver has not yet been configured.", + "anyOf": [{ + "format": "uri", + "pattern": "^wss?:\/\/.*" + }, + { + "type": "null" + } + ] + }, + "connection_authorization": { + "type": [ + "string", + "boolean" + ], + "description": "Indication of whether authorization is required to make a connection. If the parameter is set to auto the Receiver should establish for itself whether authorization should be used, based on its own internal configuration.", + "enum": [ + "auto", + true, + false + ] + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "receiver_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-receiver-base.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-receiver-base.json new file mode 100644 index 000000000..942bf0d86 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-receiver-base.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "description": "Describes the Connection API sender/receiver base resource", + "title": "Connection API sender/receiver base resource", + "items": { + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/$" + }, + "uniqueItems": true +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-response-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-response-schema.json new file mode 100644 index 000000000..958bc40db --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-response-schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Describes a sender", + "title": "Sender resource", + "additionalProperties": false, + "required": [ + "receiver_id", + "master_enable", + "activation", + "transport_params" + ], + "properties": { + "receiver_id": { + "description": "ID of the target Receiver of this Sender. This will be null if the sender is operating in multicast mode, or has not been assigned a receiver in unicast mode, or is sending to a non-NMOS receiver in unicast mode.", + "type": [ + "string", + "null" + ], + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "master_enable": { + "description": "Master on/off control for sender", + "type": "boolean" + }, + "activation": { + "$ref": "activation-response-schema.json" + }, + "transport_params": { + "$ref": "sender_transport_params.json" + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-stage-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-stage-schema.json new file mode 100644 index 000000000..6afc2dd22 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-stage-schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Describes a sender", + "title": "Sender resource", + "additionalProperties": false, + "properties": { + "receiver_id": { + "description": "ID of the target Receiver of this Sender. This will be null if the sender is operating in multicast mode, or has not been assigned a receiver in unicast mode, or is sending to a non-NMOS receiver in unicast mode.", + "type": [ + "string", + "null" + ], + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + "master_enable": { + "description": "Master on/off control for sender", + "type": "boolean" + }, + "activation": { + "$ref": "activation-schema.json" + }, + "transport_params": { + "$ref": "sender_transport_params.json" + } + } +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json new file mode 100644 index 000000000..226ba4b5a --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Transport-specific parameters. If this parameter is included in a client request it must include the same number of array elements (or 'legs') as specified in the constraints. If no changes are required to a specific leg it must be included as an empty object ({}).", + "title": "Sender Transport Parameters", + "anyOf": [{ + "type": "array", + "items": { + "$ref": "sender_transport_params_rtp.json" + } + }, + { + "type": "array", + "items": { + "$ref": "sender_transport_params_dash.json" + } + }, + { + "type": "array", + "items": { + "$ref": "sender_transport_params_websocket.json" + } + }, + { + "type": "array", + "items": { + "$ref": "sender_transport_params_mqtt.json" + } + } + ] +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_dash.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_dash.json new file mode 100644 index 000000000..ca07e7fce --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_dash.json @@ -0,0 +1,3 @@ +{ + "not": {} +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_ext.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_ext.json new file mode 100644 index 000000000..8209fd754 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_ext.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes external Sender transport parameters defined in other AMWA specifications. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint.", + "title": "External Sender Transport Parameters", + "type":[ + "string", + "boolean", + "null", + "number" + ] +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mqtt.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mqtt.json new file mode 100644 index 000000000..a7e98074d --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mqtt.json @@ -0,0 +1,83 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes MQTT Sender transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint. MQTT Senders must support all properties in this schema.", + "title": "MQTT Sender Transport Parameters", + "type": "object", + "title": "Sender Output", + "properties": { + "destination_host": { + "type": [ + "string", + "null" + ], + "description": "Hostname or IP hosting the MQTT broker. If the parameter is set to auto the Sender should establish for itself which broker it should use, based on a discovery mechanism or its own internal configuration. A null value indicates that the Sender has not yet been configured.", + "anyOf": [{ + "pattern": "^auto$" + }, + { + "format": "hostname" + }, + { + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "type": "null" + } + ] + }, + "destination_port": { + "type": [ + "integer", + "string" + ], + "description": "Destination port for MQTT traffic. If the parameter is set to auto the Sender should establish for itself which broker it should use, based on a discovery mechanism or its own internal configuration.", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "broker_protocol": { + "type": "string", + "description": "Indication of whether TLS is used for communication with the broker. 'mqtt' indicates operation without TLS, and 'secure-mqtt' indicates use of TLS. If the parameter is set to auto the Sender should establish for itself which protocol it should use, based on a discovery mechanism or its own internal configuration.", + "enum": [ + "auto", + "mqtt", + "secure-mqtt" + ] + }, + "broker_authorization": { + "type": [ + "string", + "boolean" + ], + "description": "Indication of whether authorization is used for communication with the broker. If the parameter is set to auto the Sender should establish for itself whether authorization should be used, based on a discovery mechanism or its own internal configuration.", + "enum": [ + "auto", + true, + false + ] + }, + "broker_topic": { + "type": [ + "string", + "null" + ], + "description": "The topic which MQTT messages will be sent to on the MQTT broker. A null value indicates that the Sender has not yet been configured." + }, + "connection_status_broker_topic": { + "type": [ + "string", + "null" + ], + "description": "The topic which MQTT status messages such as MQTT Last Will are sent to on the MQTT broker. A null value indicates that the Sender has not yet been configured, or is not using a connection status topic." + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "sender_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_rtp.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_rtp.json new file mode 100644 index 000000000..b849b9b7b --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_rtp.json @@ -0,0 +1,191 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes RTP Sender transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint. As a minimum all senders must support `source_ip`, `destination_ip`, `source_port`, `rtp_enabled` and `destination_port`. Senders supporting FEC and/or RTCP must support parameters prefixed with `fec` and `rtcp` respectively.", + "title": "RTP Sender Transport Parameters", + "type": "object", + "title": "Sender Output", + "properties": { + "source_ip": { + "type": "string", + "description": "IP address from which RTP packets will be sent (IP address of interface bound to this output). The sender should provide an enum in the constraints endpoint, which should contain the available interface addresses. If the parameter is set to auto the sender should establish for itself which interface it should use, based on routing rules or its own internal configuration.", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "pattern": "^auto$" + } + ] + }, + "destination_ip": { + "type": "string", + "description": "IP address to which RTP packets will be sent. If auto is set the sender should select a multicast address to send to itself. For example it may implement MADCAP (RFC 2730), ZMAAP, or be allocated address by some other system responsible for co-ordination multicast address use.", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "pattern": "^auto$" + } + ] + }, + "source_port": { + "type": [ + "integer", + "string" + ], + "description": "source port for RTP packets (auto = 5004 by default)", + "minimum": 0, + "maximum": 65535, + "pattern": "^auto$" + }, + "destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTP packets (auto = 5004 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "fec_enabled": { + "type": "boolean", + "description": "FEC on/off" + }, + "fec_destination_ip": { + "type": "string", + "description": "May be used if NAT is being used at the destination (auto = destination_ip by default)", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "pattern": "^auto$" + } + ] + }, + "fec_type": { + "type": "string", + "description": "forward error correction mode to apply", + "enum": [ + "XOR", + "Reed-Solomon" + ] + }, + "fec_mode": { + "type": "string", + "description": "forward error correction mode to apply", + "enum": [ + "1D", + "2D" + ] + }, + "fec_block_width": { + "type": "integer", + "description": "width of block over which FEC is calculated in packets", + "minimum": 4, + "maximum": 200 + }, + "fec_block_height": { + "type": "integer", + "description": "height of block over which FEC is calculated in packets", + "minimum": 4, + "maximum": 200 + }, + "fec1D_destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTP Column FEC packets (auto = RTP destination_port + 2 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "fec2D_destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTP Row FEC packets (auto = RTP destination_port + 4 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "fec1D_source_port": { + "type": [ + "integer", + "string" + ], + "description": "source port for RTP FEC packets (auto = RTP source_port + 2 by default)", + "minimum": 0, + "maximum": 65535, + "pattern": "^auto$" + }, + "fec2D_source_port": { + "type": [ + "integer", + "string" + ], + "description": "source port for RTP FEC packets (auto = RTP source_port + 4 by default)", + "minimum": 0, + "maximum": 65535, + "pattern": "^auto$" + }, + "rtcp_enabled": { + "type": "boolean", + "description": "rtcp on/off" + }, + "rtcp_destination_ip": { + "type": "string", + "description": "IP address to which RTCP packets will be sent (auto = same as RTP destination_ip by default)", + "anyOf": [{ + "format": "ipv4" + }, + { + "format": "ipv6" + }, + { + "pattern": "^auto$" + } + ] + }, + "rtcp_destination_port": { + "type": [ + "integer", + "string" + ], + "description": "destination port for RTCP packets (auto = RTP destination_port + 1 by default)", + "minimum": 1, + "maximum": 65535, + "pattern": "^auto$" + }, + "rtcp_source_port": { + "type": [ + "integer", + "string" + ], + "description": "source port for RTCP packets (auto = RTP source_port + 1 by default)", + "minimum": 0, + "maximum": 65535, + "pattern": "^auto$" + }, + "rtp_enabled": { + "type": "boolean", + "description": "RTP transmission active/inactive" + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "sender_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_websocket.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_websocket.json new file mode 100644 index 000000000..65c187f28 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_websocket.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes WebSocket Sender transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint. WebSocket Senders must support all parameters in this schema.", + "title": "WebSocket Sender Transport Parameters", + "type": "object", + "title": "Sender Output", + "properties": { + "connection_uri": { + "type": [ + "string", + "null" + ], + "description": "URI hosting the WebSocket server as defined in RFC 6455 Section 3. The sender should provide an enum in the constraints endpoint, which should contain the available interface addresses formatted as connection URIs. If the parameter is set to auto the sender should establish for itself which interface it should use, based on routing rules or its own internal configuration. A null value indicates that the sender has not yet been configured.", + "anyOf": [{ + "pattern": "^auto$" + }, + { + "format": "uri", + "pattern": "^wss?:\/\/.*" + }, + { + "type": "null" + } + ] + }, + "connection_authorization": { + "type": [ + "string", + "boolean" + ], + "description": "Indication of whether authorization is required to make a connection. If the parameter is set to auto the Sender should establish for itself whether authorization should be used, based on its own internal configuration.", + "enum": [ + "auto", + true, + false + ] + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "sender_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json new file mode 100644 index 000000000..77ce38fd3 --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Transport Type", + "description": "Transport type URN base used by the Sender or Receiver (i.e. with any subclassifications or versions removed)", + "type": "string", + "oneOf": [ + { + "enum": [ + "urn:x-nmos:transport:rtp", + "urn:x-nmos:transport:dash", + "urn:x-nmos:transport:websocket", + "urn:x-nmos:transport:mqtt" + ] + } + ], + "format": "uri" +} From 7f4d4a6a244bb3d326b222728e36404b66cfce29 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 25 Feb 2026 10:10:29 +0000 Subject: [PATCH 02/33] Add schema changes for MXL as a built-in transport --- .../schemas/receiver_transport_params.json | 6 ++++ .../receiver_transport_params_mxl.json | 29 +++++++++++++++++++ .../APIs/schemas/sender_transport_params.json | 6 ++++ .../schemas/sender_transport_params_mxl.json | 29 +++++++++++++++++++ .../transporttype-response-schema.json | 3 +- 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json create mode 100644 Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json index 3656ead3a..b6c9aa6dc 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json @@ -25,6 +25,12 @@ "items": { "$ref": "receiver_transport_params_mqtt.json" } + }, + { + "type": "array", + "items": { + "$ref": "receiver_transport_params_mxl.json" + } } ] } diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json new file mode 100644 index 000000000..31bcda06b --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes MXL Receiver transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint.", + "title": "MXL Receiver Transport Parameters", + "type": "object", + "properties": { + "flow_id": { + "type": [ + "string", + "null" + ], + "description": "ID of the Flow to read.", + "anyOf": [{ + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + { + "type": "null" + } + ] + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "receiver_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json index 226ba4b5a..c8d0d7586 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json @@ -25,6 +25,12 @@ "items": { "$ref": "sender_transport_params_mqtt.json" } + }, + { + "type": "array", + "items": { + "$ref": "sender_transport_params_mxl.json" + } } ] } diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json new file mode 100644 index 000000000..d059b7f3c --- /dev/null +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Describes MXL Sender transport parameters. The constraints in this schema are minimum constraints, but may be further constrained at the constraints endpoint.", + "title": "MXL Sender Transport Parameters", + "type": "object", + "properties": { + "flow_id": { + "type": [ + "string", + "null" + ], + "description": "ID of the Flow to write.", + "anyOf": [{ + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + { + "type": "null" + } + ] + } + }, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "sender_transport_params_ext.json" + } + }, + "additionalProperties": false +} diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json index 77ce38fd3..6a3f1a346 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json @@ -9,7 +9,8 @@ "urn:x-nmos:transport:rtp", "urn:x-nmos:transport:dash", "urn:x-nmos:transport:websocket", - "urn:x-nmos:transport:mqtt" + "urn:x-nmos:transport:mqtt", + "urn:x-nmos:transport:mxl" ] } ], From f28ca6a0a33deec601138adf0023e36619a56944 Mon Sep 17 00:00:00 2001 From: lo-simon Date: Wed, 25 Feb 2026 11:18:23 +0000 Subject: [PATCH 03/33] Add IS-05 schema cpp generation --- Development/cmake/NmosCppLibraries.cmake | 46 +++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index ca49cd857..982e000b0 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -407,9 +407,50 @@ set(NMOS_IS05_SCHEMAS_HEADERS nmos/is05_schemas/is05_schemas.h ) +set(NMOS_IS05_V1_2_TAG v1.2.x) set(NMOS_IS05_V1_1_TAG v1.1.x) set(NMOS_IS05_V1_0_TAG v1.0.x) +set(NMOS_IS05_V1_2_SCHEMAS_JSON + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/activation-response-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/activation-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/bulk-receiver-post-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/bulk-response-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/bulk-sender-post-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/connectionapi-base.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/connectionapi-bulk.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/connectionapi-receiver.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/connectionapi-sender.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/connectionapi-single.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/constraint-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/constraints-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/constraints-schema-mqtt.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/constraints-schema-rtp.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/constraints-schema-websocket.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/error.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver_transport_params.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver_transport_params_dash.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver_transport_params_ext.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver_transport_params_mqtt.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver_transport_params_mxl.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver_transport_params_rtp.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver_transport_params_websocket.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver-response-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver-stage-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/receiver-transport-file.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender_transport_params.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender_transport_params_dash.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender_transport_params_ext.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender_transport_params_mqtt.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender_transport_params_mxl.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender_transport_params_rtp.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender_transport_params_websocket.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender-receiver-base.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender-response-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/sender-stage-schema.json + third_party/is-05/${NMOS_IS05_V1_2_TAG}/APIs/schemas/transporttype-response-schema.json + ) + set(NMOS_IS05_V1_1_SCHEMAS_JSON third_party/is-05/${NMOS_IS05_V1_1_TAG}/APIs/schemas/activation-response-schema.json third_party/is-05/${NMOS_IS05_V1_1_TAG}/APIs/schemas/activation-schema.json @@ -474,10 +515,11 @@ set(NMOS_IS05_V1_0_SCHEMAS_JSON set(NMOS_IS05_SCHEMAS_JSON_MATCH "third_party/is-05/([^/]+)/APIs/schemas/([^;]+)\\.json") set(NMOS_IS05_SCHEMAS_SOURCE_REPLACE "${CMAKE_CURRENT_BINARY_DIR_REPLACE}/nmos/is05_schemas/\\1/\\2.cpp") +string(REGEX REPLACE "${NMOS_IS05_SCHEMAS_JSON_MATCH}(;|$)" "${NMOS_IS05_SCHEMAS_SOURCE_REPLACE}\\3" NMOS_IS05_V1_2_SCHEMAS_SOURCES "${NMOS_IS05_V1_2_SCHEMAS_JSON}") string(REGEX REPLACE "${NMOS_IS05_SCHEMAS_JSON_MATCH}(;|$)" "${NMOS_IS05_SCHEMAS_SOURCE_REPLACE}\\3" NMOS_IS05_V1_1_SCHEMAS_SOURCES "${NMOS_IS05_V1_1_SCHEMAS_JSON}") string(REGEX REPLACE "${NMOS_IS05_SCHEMAS_JSON_MATCH}(;|$)" "${NMOS_IS05_SCHEMAS_SOURCE_REPLACE}\\3" NMOS_IS05_V1_0_SCHEMAS_SOURCES "${NMOS_IS05_V1_0_SCHEMAS_JSON}") -foreach(JSON ${NMOS_IS05_V1_1_SCHEMAS_JSON} ${NMOS_IS05_V1_0_SCHEMAS_JSON}) +foreach(JSON ${NMOS_IS05_V1_2_SCHEMAS_JSON} ${NMOS_IS05_V1_1_SCHEMAS_JSON} ${NMOS_IS05_V1_0_SCHEMAS_JSON}) string(REGEX REPLACE "${NMOS_IS05_SCHEMAS_JSON_MATCH}" "${NMOS_IS05_SCHEMAS_SOURCE_REPLACE}" SOURCE "${JSON}") string(REGEX REPLACE "${NMOS_IS05_SCHEMAS_JSON_MATCH}" "\\1" NS "${JSON}") string(REGEX REPLACE "${NMOS_IS05_SCHEMAS_JSON_MATCH}" "\\2" VAR "${JSON}") @@ -509,11 +551,13 @@ endforeach() add_library( nmos_is05_schemas STATIC ${NMOS_IS05_SCHEMAS_HEADERS} + ${NMOS_IS05_V1_2_SCHEMAS_SOURCES} ${NMOS_IS05_V1_1_SCHEMAS_SOURCES} ${NMOS_IS05_V1_0_SCHEMAS_SOURCES} ) source_group("nmos\\is05_schemas\\Header Files" FILES ${NMOS_IS05_SCHEMAS_HEADERS}) +source_group("nmos\\is05_schemas\\${NMOS_IS05_V1_2_TAG}\\Source Files" FILES ${NMOS_IS05_V1_2_SCHEMAS_SOURCES}) source_group("nmos\\is05_schemas\\${NMOS_IS05_V1_1_TAG}\\Source Files" FILES ${NMOS_IS05_V1_1_SCHEMAS_SOURCES}) source_group("nmos\\is05_schemas\\${NMOS_IS05_V1_0_TAG}\\Source Files" FILES ${NMOS_IS05_V1_0_SCHEMAS_SOURCES}) From d0c600e8016208aa53422f51bfe804e13207cf17 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 25 Feb 2026 11:12:38 +0000 Subject: [PATCH 04/33] Add IS-05 v1.2 as copy of IS-05 v1.1 --- Development/nmos/is05_schemas/is05_schemas.h | 20 ++++++++++++++ Development/nmos/is05_versions.h | 3 +- Development/nmos/json_schema.cpp | 29 ++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/Development/nmos/is05_schemas/is05_schemas.h b/Development/nmos/is05_schemas/is05_schemas.h index 4550dbe77..5430ede77 100644 --- a/Development/nmos/is05_schemas/is05_schemas.h +++ b/Development/nmos/is05_schemas/is05_schemas.h @@ -7,6 +7,26 @@ namespace nmos { namespace is05_schemas { + namespace v1_2_x + { + extern const char* activation_schema; + extern const char* sender_stage_schema; + extern const char* sender_transport_params; + extern const char* sender_transport_params_rtp; + extern const char* sender_transport_params_dash; + extern const char* sender_transport_params_websocket; + extern const char* sender_transport_params_mqtt; + extern const char* sender_transport_params_ext; + extern const char* receiver_stage_schema; + extern const char* receiver_transport_file; + extern const char* receiver_transport_params; + extern const char* receiver_transport_params_rtp; + extern const char* receiver_transport_params_dash; + extern const char* receiver_transport_params_websocket; + extern const char* receiver_transport_params_mqtt; + extern const char* receiver_transport_params_ext; + } + namespace v1_1_x { extern const char* activation_schema; diff --git a/Development/nmos/is05_versions.h b/Development/nmos/is05_versions.h index dfb39a527..4ed870ce9 100644 --- a/Development/nmos/is05_versions.h +++ b/Development/nmos/is05_versions.h @@ -12,8 +12,9 @@ namespace nmos { const api_version v1_0{ 1, 0 }; const api_version v1_1{ 1, 1 }; + const api_version v1_2{ 1, 2 }; - const std::set all{ nmos::is05_versions::v1_0, nmos::is05_versions::v1_1 }; + const std::set all{ nmos::is05_versions::v1_0, nmos::is05_versions::v1_1, nmos::is05_versions::v1_2 }; inline std::set from_settings(const nmos::settings& settings) { diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index 3860509ca..d08668d6a 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -77,6 +77,16 @@ namespace nmos return{ _XPLATSTR("https://github.com/AMWA-TV/is-05/raw/") + tag + _XPLATSTR("/APIs/schemas/") + ref }; } + // See https://github.com/AMWA-TV/is-05/blob/v1.2.x/APIs/schemas/ + namespace v1_2 + { + using namespace nmos::is05_schemas::v1_2_x; + const utility::string_t tag(_XPLATSTR("v1.2.x")); + + const web::uri connectionapi_sender_staged_patch_request_uri = make_schema_uri(tag, _XPLATSTR("sender-stage-schema.json")); + const web::uri connectionapi_receiver_staged_patch_request_uri = make_schema_uri(tag, _XPLATSTR("receiver-stage-schema.json")); + } + // See https://github.com/AMWA-TV/is-05/blob/v1.1.x/APIs/schemas/ namespace v1_1 { @@ -323,6 +333,23 @@ namespace nmos return { + // v1.2 + { make_schema_uri(v1_2::tag, _XPLATSTR("sender-stage-schema.json")), make_schema(v1_2::sender_stage_schema) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver-stage-schema.json")), make_schema(v1_2::receiver_stage_schema) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver-transport-file.json")), make_schema(v1_2::receiver_transport_file) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("activation-schema.json")), make_schema(v1_2::activation_schema) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params.json")), make_schema(v1_2::sender_transport_params) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_rtp.json")), make_schema(v1_2::sender_transport_params_rtp) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_dash.json")), make_schema(v1_2::sender_transport_params_dash) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_websocket.json")), make_schema(v1_2::sender_transport_params_websocket) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_mqtt.json")), make_schema(v1_2::sender_transport_params_mqtt) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_ext.json")), make_schema(v1_2::sender_transport_params_ext) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params.json")), make_schema(v1_2::receiver_transport_params) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_rtp.json")), make_schema(v1_2::receiver_transport_params_rtp) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_dash.json")), make_schema(v1_2::receiver_transport_params_dash) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_websocket.json")), make_schema(v1_2::receiver_transport_params_websocket) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_mqtt.json")), make_schema(v1_2::receiver_transport_params_mqtt) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_ext.json")), make_schema(v1_2::receiver_transport_params_ext) }, // v1.1 { make_schema_uri(v1_1::tag, _XPLATSTR("sender-stage-schema.json")), make_schema(v1_1::sender_stage_schema) }, { make_schema_uri(v1_1::tag, _XPLATSTR("receiver-stage-schema.json")), make_schema(v1_1::receiver_stage_schema) }, @@ -487,12 +514,14 @@ namespace nmos web::uri make_connectionapi_sender_staged_patch_request_schema_uri(const nmos::api_version& version) { + if (is05_versions::v1_2 <= version) return is05_schemas::v1_2::connectionapi_sender_staged_patch_request_uri; if (is05_versions::v1_1 <= version) return is05_schemas::v1_1::connectionapi_sender_staged_patch_request_uri; return is05_schemas::v1_0::connectionapi_sender_staged_patch_request_uri; } web::uri make_connectionapi_receiver_staged_patch_request_schema_uri(const nmos::api_version& version) { + if (is05_versions::v1_2 <= version) return is05_schemas::v1_2::connectionapi_receiver_staged_patch_request_uri; if (is05_versions::v1_1 <= version) return is05_schemas::v1_1::connectionapi_receiver_staged_patch_request_uri; return is05_schemas::v1_0::connectionapi_receiver_staged_patch_request_uri; } From 19db280004b21d517f2224bc1f791db551dd6ebd Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 25 Feb 2026 11:18:23 +0000 Subject: [PATCH 05/33] Add MXL senders and receivers --- Development/nmos/connection_api.cpp | 4 + Development/nmos/connection_resources.cpp | 87 ++++++++++++++++++++ Development/nmos/connection_resources.h | 3 + Development/nmos/is05_schemas/is05_schemas.h | 2 + Development/nmos/json_fields.h | 2 + Development/nmos/json_schema.cpp | 2 + Development/nmos/transport.h | 2 + 7 files changed, 102 insertions(+) diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index eb63bcafb..83cdd641b 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -86,6 +86,9 @@ namespace nmos }, { nmos::is05_versions::v1_1, { nmos::transports::websocket, nmos::transports::mqtt } + }, + { + nmos::is05_versions::v1_2, { nmos::transports::mxl } } }; @@ -245,6 +248,7 @@ namespace nmos if (nmos::transports::rtp == transport_base) return rtp_auto_constraints(); if (nmos::transports::websocket == transport_base) return websocket_auto_constraints(); if (nmos::transports::mqtt == transport_base) return mqtt_auto_constraints(); + //if (nmos::transports::mxl == transport_base) return mxl_auto_constraints(); static const std::map> no_auto_constraints; return no_auto_constraints; diff --git a/Development/nmos/connection_resources.cpp b/Development/nmos/connection_resources.cpp index 79d81a7e9..acdee5735 100644 --- a/Development/nmos/connection_resources.cpp +++ b/Development/nmos/connection_resources.cpp @@ -612,4 +612,91 @@ namespace nmos // See https://specs.amwa.tv/is-07/releases/v1.0.1/docs/5.1._Transport_-_MQTT.html#33-connection_status_broker_topic return U("x-nmos/events/") + make_api_version(version) + U("/connections/") + connection_id; } + + namespace details + { + web::json::value make_connection_mxl_sender_core_constraints() + { + using web::json::value; + using web::json::value_of; + + const auto unconstrained = value::object(); + return value_of({ + { nmos::fields::flow_id, unconstrained } + }); + } + + web::json::value make_connection_mxl_sender_staged_core_parameter_set() + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::flow_id, value::null() } + }); + } + + web::json::value make_connection_mxl_receiver_core_constraints() + { + using web::json::value; + using web::json::value_of; + + const auto unconstrained = value::object(); + return value_of({ + { nmos::fields::flow_id, unconstrained } + }); + } + + web::json::value make_connection_mxl_receiver_staged_core_parameter_set() + { + using web::json::value; + using web::json::value_of; + + return value_of({ + { nmos::fields::flow_id, value::null() } + }); + } + } + + nmos::resource make_connection_mxl_sender(const nmos::id& id) + { + using web::json::value; + using web::json::value_of; + + const auto redundant = false; + + auto data = details::make_connection_resource_core(id, redundant); + + data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(), redundant); + + data[nmos::fields::endpoint_staged][nmos::fields::receiver_id] = value::null(); + data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_sender_staged_core_parameter_set(), redundant); + + data[nmos::fields::endpoint_active] = data[nmos::fields::endpoint_staged]; + + // Note that the transporttype endpoint is implemented in terms of the matching IS-04 sender + + return{ is05_versions::v1_2, types::sender, std::move(data), false }; + } + + nmos::resource make_connection_mxl_receiver(const nmos::id& id) + { + using web::json::value; + + const auto redundant = false; + + auto data = details::make_connection_resource_core(id, redundant); + + data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_receiver_core_constraints(), redundant); + + data[nmos::fields::endpoint_staged][nmos::fields::sender_id] = value::null(); + data[nmos::fields::endpoint_staged][nmos::fields::transport_file] = details::make_connection_receiver_staging_transport_file(); + data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_receiver_staged_core_parameter_set(), redundant); + + data[nmos::fields::endpoint_active] = data[nmos::fields::endpoint_staged]; + + // Note that the transporttype endpoint is implemented in terms of the matching IS-04 receiver + + return{ is05_versions::v1_2, types::receiver, std::move(data), false }; + } } diff --git a/Development/nmos/connection_resources.h b/Development/nmos/connection_resources.h index adb035d34..045ed92e0 100644 --- a/Development/nmos/connection_resources.h +++ b/Development/nmos/connection_resources.h @@ -60,6 +60,9 @@ namespace nmos utility::string_t make_events_mqtt_broker_topic(const nmos::id& source_id, const nmos::settings& settings); utility::string_t make_events_mqtt_connection_status_broker_topic(const nmos::id& connection_id, const nmos::settings& settings); + + nmos::resource make_connection_mxl_sender(const nmos::id& id); + nmos::resource make_connection_mxl_receiver(const nmos::id& id); } #endif diff --git a/Development/nmos/is05_schemas/is05_schemas.h b/Development/nmos/is05_schemas/is05_schemas.h index 5430ede77..2c3db393a 100644 --- a/Development/nmos/is05_schemas/is05_schemas.h +++ b/Development/nmos/is05_schemas/is05_schemas.h @@ -16,6 +16,7 @@ namespace nmos extern const char* sender_transport_params_dash; extern const char* sender_transport_params_websocket; extern const char* sender_transport_params_mqtt; + extern const char* sender_transport_params_mxl; extern const char* sender_transport_params_ext; extern const char* receiver_stage_schema; extern const char* receiver_transport_file; @@ -24,6 +25,7 @@ namespace nmos extern const char* receiver_transport_params_dash; extern const char* receiver_transport_params_websocket; extern const char* receiver_transport_params_mqtt; + extern const char* receiver_transport_params_mxl; extern const char* receiver_transport_params_ext; } diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 0ff9921db..5e3b4739f 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -172,6 +172,8 @@ namespace nmos const web::json::field_as_value_or broker_authorization{ U("broker_authorization"), {} }; // string or bool const web::json::field_as_value_or broker_topic{ U("broker_topic"), {} }; // string or null const web::json::field_as_value_or connection_status_broker_topic{ U("connection_status_broker_topic"), {} }; // string or null + // for urn:x-nmos:transport:mxl + //const web::json::field_as_value flow_id{ U("flow_id") }; // see nmos::id // IS-07 Event & Tally diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index d08668d6a..e6491e7f0 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -343,12 +343,14 @@ namespace nmos { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_dash.json")), make_schema(v1_2::sender_transport_params_dash) }, { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_websocket.json")), make_schema(v1_2::sender_transport_params_websocket) }, { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_mqtt.json")), make_schema(v1_2::sender_transport_params_mqtt) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_mxl.json")), make_schema(v1_2::sender_transport_params_mxl) }, { make_schema_uri(v1_2::tag, _XPLATSTR("sender_transport_params_ext.json")), make_schema(v1_2::sender_transport_params_ext) }, { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params.json")), make_schema(v1_2::receiver_transport_params) }, { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_rtp.json")), make_schema(v1_2::receiver_transport_params_rtp) }, { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_dash.json")), make_schema(v1_2::receiver_transport_params_dash) }, { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_websocket.json")), make_schema(v1_2::receiver_transport_params_websocket) }, { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_mqtt.json")), make_schema(v1_2::receiver_transport_params_mqtt) }, + { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_mxl.json")), make_schema(v1_2::receiver_transport_params_mxl) }, { make_schema_uri(v1_2::tag, _XPLATSTR("receiver_transport_params_ext.json")), make_schema(v1_2::receiver_transport_params_ext) }, // v1.1 { make_schema_uri(v1_1::tag, _XPLATSTR("sender-stage-schema.json")), make_schema(v1_1::sender_stage_schema) }, diff --git a/Development/nmos/transport.h b/Development/nmos/transport.h index 8b45279b0..3da059d08 100644 --- a/Development/nmos/transport.h +++ b/Development/nmos/transport.h @@ -21,6 +21,8 @@ namespace nmos const transport mqtt{ U("urn:x-nmos:transport:mqtt") }; const transport websocket{ U("urn:x-nmos:transport:websocket") }; + + const transport mxl{ U("urn:x-nmos:transport:mxl") }; } // "Subclassifications are defined as the portion of the URN which follows the first occurrence of a '.', but prior to any '/' character." From de4b223339e83683b5eb9e7af293f2e713eefdce Mon Sep 17 00:00:00 2001 From: garethsb Date: Wed, 25 Feb 2026 14:08:42 +0000 Subject: [PATCH 06/33] Fix no_auto_constraints --- Development/nmos/connection_api.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index 83cdd641b..1b294511a 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -250,7 +250,15 @@ namespace nmos if (nmos::transports::mqtt == transport_base) return mqtt_auto_constraints(); //if (nmos::transports::mxl == transport_base) return mxl_auto_constraints(); - static const std::map> no_auto_constraints; + static const std::map> no_auto_constraints + { + { + nmos::types::sender, {} + }, + { + nmos::types::receiver, {} + } + }; return no_auto_constraints; } From 5f8b46803bbd718ea252dc945b4bb67de98181db Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 25 Feb 2026 12:23:28 +0000 Subject: [PATCH 07/33] nmos-cpp-node support for MXL senders and receivers --- .../nmos-cpp-node/node_implementation.cpp | 138 +++++++++++++++++- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 1e074fe23..4be5b1584 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -154,13 +154,22 @@ namespace impl // example number/enum event const port catcall{ U("c") }; + // video/v210, video/v210a, etc. + const port mxl_video{ U("xv") }; + // audio/float32 + const port mxl_audio{ U("xa") }; + // video/smpte291 + const port mxl_data{ U("xd") }; + const std::vector rtp{ video, audio, data, mux }; const std::vector ws{ temperature, burn, nonsense, catcall }; - const std::vector all{ boost::copy_range>(boost::range::join(rtp, ws)) }; + const std::vector mxl{ mxl_video, mxl_audio, mxl_data }; + const std::vector all{ boost::copy_range>(boost::join(boost::join(rtp, ws), mxl)) }; } bool is_rtp_port(const port& port); bool is_ws_port(const port& port); + bool is_mxl_port(const port& port); std::vector parse_ports(const web::json::value& value); const std::vector channels_repeat{ @@ -404,9 +413,11 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto sender_ports = impl::parse_ports(impl::fields::senders(model.settings)); const auto rtp_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_rtp_port)); const auto ws_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_ws_port)); + const auto mxl_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_mxl_port)); const auto receiver_ports = impl::parse_ports(impl::fields::receivers(model.settings)); const auto rtp_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_rtp_port)); const auto ws_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_ws_port)); + const auto mxl_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_mxl_port)); const auto frame_rate = nmos::parse_rational(impl::fields::frame_rate(model.settings)); const auto frame_width = impl::fields::frame_width(model.settings); const auto frame_height = impl::fields::frame_height(model.settings); @@ -528,13 +539,14 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr { auto sender_ids = impl::make_ids(seed_id, nmos::types::sender, rtp_sender_ports, how_many); if (0 <= nmos::fields::events_port(model.settings)) boost::range::push_back(sender_ids, impl::make_ids(seed_id, nmos::types::sender, ws_sender_ports, how_many)); + boost::range::push_back(sender_ids, impl::make_ids(seed_id, nmos::types::sender, mxl_sender_ports, how_many)); auto receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, receiver_ports, how_many); auto device = nmos::make_device(device_id, node_id, sender_ids, receiver_ids, model.settings); device.data[nmos::fields::tags] = impl::fields::device_tags(model.settings); if (!insert_resource_after(delay_millis, model.node_resources, std::move(device), gate)) throw node_implementation_init_exception(); } - // example sources, flows and senders + // example rtp sources, flows and senders for (int index = 0; index < how_many; ++index) { for (const auto& port : rtp_sender_ports) @@ -672,7 +684,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } } - // example receivers + // example rtp receivers for (int index = 0; index < how_many; ++index) { for (const auto& port : rtp_receiver_ports) @@ -903,6 +915,121 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } } + // example mxl sources, flows and senders + for (int index = 0; index < how_many; ++index) + { + for (const auto& port : mxl_sender_ports) + { + const auto source_id = impl::make_id(seed_id, nmos::types::source, port, index); + const auto flow_id = impl::make_id(seed_id, nmos::types::flow, port, index); + const auto sender_id = impl::make_id(seed_id, nmos::types::sender, port, index); + + nmos::resource source; + if (impl::ports::mxl_video == port) + { + source = nmos::make_video_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); + } + else if (impl::ports::mxl_audio == port) + { + const auto channels = boost::copy_range>(boost::irange(0, channel_count) | boost::adaptors::transformed([&](const int& index) + { + return impl::channels_repeat[index % (int)impl::channels_repeat.size()]; + })); + + source = nmos::make_audio_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, channels, model.settings); + } + else if (impl::ports::mxl_data == port) + { + source = nmos::make_data_source(source_id, device_id, nmos::clock_names::clk0, frame_rate, model.settings); + } + impl::insert_parents(source, seed_id, port, index); + impl::set_label_description(source, port, index); + + nmos::resource flow; + + if (impl::ports::mxl_video == port) + { + flow = nmos::make_coded_video_flow( + flow_id, source_id, device_id, + frame_rate, + frame_width, frame_height, interlace_mode, + colorspace, transfer_characteristic, sampling, bit_depth, + video_type, + model.settings + ); + } + else if (impl::ports::mxl_audio == port) + { + flow = nmos::make_raw_audio_flow(flow_id, source_id, device_id, 48000, 32, model.settings); + flow.data[nmos::fields::media_type] = value::string(U("audio/float32")); + // add optional grain_rate + flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate); + } + else if (impl::ports::mxl_data == port) + { + nmos::did_sdid timecode{ 0x60, 0x60 }; + flow = nmos::make_sdianc_data_flow(flow_id, source_id, device_id, { timecode }, model.settings); + // add optional grain_rate + flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate); + } + impl::insert_parents(flow, seed_id, port, index); + impl::set_label_description(flow, port, index); + + auto sender = nmos::make_sender(sender_id, flow_id, nmos::transports::mxl, device_id, {}, {}, model.settings); + impl::set_label_description(sender, port, index); + impl::insert_group_hint(sender, port, index); + + auto connection_sender = nmos::make_connection_mxl_sender(sender_id); + + if (impl::fields::activate_senders(model.settings)) + { + // initialize this sender with a scheduled activation, e.g. to enable the IS-05-01 test suite to run immediately + auto& staged = connection_sender.data[nmos::fields::endpoint_staged]; + staged[nmos::fields::master_enable] = value::boolean(true); + staged[nmos::fields::activation] = value_of({ + { nmos::fields::mode, nmos::activation_modes::activate_scheduled_relative.name }, + { nmos::fields::requested_time, U("0:0") }, + { nmos::fields::activation_time, nmos::make_version() } + }); + } + + if (!insert_resource_after(delay_millis, model.node_resources, std::move(source), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.node_resources, std::move(flow), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.node_resources, std::move(sender), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.connection_resources, std::move(connection_sender), gate)) throw node_implementation_init_exception(); + } + } + + // example mxl receivers + for (int index = 0; index < how_many; ++index) + { + for (const auto& port : mxl_receiver_ports) + { + const auto receiver_id = impl::make_id(seed_id, nmos::types::receiver, port, index); + + nmos::resource receiver; + if (impl::ports::mxl_video == port) + { + receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::video, { video_type }, model.settings); + } + else if (impl::ports::mxl_audio == port) + { + receiver = nmos::make_audio_receiver(receiver_id, device_id, nmos::transports::mxl, {}, {}, model.settings); + } + else if (impl::ports::mxl_data == port) + { + receiver = nmos::make_sdianc_data_receiver(receiver_id, device_id, nmos::transports::mxl, {}, model.settings); + } + impl::set_label_description(receiver, port, index); + impl::insert_group_hint(receiver, port, index); + + auto connection_receiver = nmos::make_connection_mxl_receiver(receiver_id); + + if (!insert_resource_after(delay_millis, model.node_resources, std::move(receiver), gate)) throw node_implementation_init_exception(); + if (!insert_resource_after(delay_millis, model.connection_resources, std::move(connection_receiver), gate)) throw node_implementation_init_exception(); + } + } + // example channelmapping resources demonstrating a range of input/output capabilities // see https://github.com/sony/nmos-cpp/issues/111#issuecomment-740613137 @@ -2263,6 +2390,11 @@ namespace impl return impl::ports::ws.end() != boost::range::find(impl::ports::ws, port); } + bool is_mxl_port(const impl::port& port) + { + return impl::ports::mxl.end() != boost::range::find(impl::ports::mxl, port); + } + std::vector parse_ports(const web::json::value& value) { if (value.is_null()) return impl::ports::all; From 9fa16508766a11c6cf6b1ce1a2880de212ccb9f5 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 25 Feb 2026 14:08:42 +0000 Subject: [PATCH 08/33] Add flow_id as initial value (next, try making it a /constraints value and use "auto" in /staged) --- Development/nmos-cpp-node/node_implementation.cpp | 2 +- Development/nmos/connection_resources.cpp | 8 ++++---- Development/nmos/connection_resources.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 4be5b1584..ca447f725 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -979,7 +979,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr impl::set_label_description(sender, port, index); impl::insert_group_hint(sender, port, index); - auto connection_sender = nmos::make_connection_mxl_sender(sender_id); + auto connection_sender = nmos::make_connection_mxl_sender(sender_id, flow_id); if (impl::fields::activate_senders(model.settings)) { diff --git a/Development/nmos/connection_resources.cpp b/Development/nmos/connection_resources.cpp index acdee5735..0526d0855 100644 --- a/Development/nmos/connection_resources.cpp +++ b/Development/nmos/connection_resources.cpp @@ -626,13 +626,13 @@ namespace nmos }); } - web::json::value make_connection_mxl_sender_staged_core_parameter_set() + web::json::value make_connection_mxl_sender_staged_core_parameter_set(const nmos::id& flow_id) { using web::json::value; using web::json::value_of; return value_of({ - { nmos::fields::flow_id, value::null() } + { nmos::fields::flow_id, flow_id.empty() ? value::null() : value::string(flow_id) } }); } @@ -658,7 +658,7 @@ namespace nmos } } - nmos::resource make_connection_mxl_sender(const nmos::id& id) + nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& flow_id) { using web::json::value; using web::json::value_of; @@ -670,7 +670,7 @@ namespace nmos data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(), redundant); data[nmos::fields::endpoint_staged][nmos::fields::receiver_id] = value::null(); - data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_sender_staged_core_parameter_set(), redundant); + data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_sender_staged_core_parameter_set(flow_id), redundant); data[nmos::fields::endpoint_active] = data[nmos::fields::endpoint_staged]; diff --git a/Development/nmos/connection_resources.h b/Development/nmos/connection_resources.h index 045ed92e0..24d03b339 100644 --- a/Development/nmos/connection_resources.h +++ b/Development/nmos/connection_resources.h @@ -61,7 +61,7 @@ namespace nmos utility::string_t make_events_mqtt_broker_topic(const nmos::id& source_id, const nmos::settings& settings); utility::string_t make_events_mqtt_connection_status_broker_topic(const nmos::id& connection_id, const nmos::settings& settings); - nmos::resource make_connection_mxl_sender(const nmos::id& id); + nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& flow_id); nmos::resource make_connection_mxl_receiver(const nmos::id& id); } From 18a90974959dc86858f3bce3f1ae66e471ec2393 Mon Sep 17 00:00:00 2001 From: Gareth Sylvester-Bradley Date: Wed, 25 Feb 2026 20:40:33 +0000 Subject: [PATCH 09/33] Add support for "auto" to MXL sender flow_id transport parameter --- .../nmos-cpp-node/node_implementation.cpp | 21 +++++++++++++------ Development/nmos/connection_api.cpp | 20 +++++++++++++++++- Development/nmos/connection_resources.cpp | 17 +++++++++------ .../receiver_transport_params_mxl.json | 1 - .../schemas/sender_transport_params_mxl.json | 6 ++++-- 5 files changed, 49 insertions(+), 16 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index ca447f725..101526b51 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1672,7 +1672,8 @@ void node_implementation_run(nmos::node_model& model, nmos::experimental::contro const auto sender_ports = impl::parse_ports(impl::fields::senders(model.settings)); const auto rtp_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_rtp_port)); const auto ws_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_ws_port)); - const auto rtp_receiver_ports = boost::copy_range>(impl::parse_ports(impl::fields::receivers(model.settings)) | boost::adaptors::filtered(impl::is_rtp_port)); + const auto receiver_ports = impl::parse_ports(impl::fields::receivers(model.settings)); + const auto rtp_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_rtp_port)); const auto simulate_status_monitor_activity = impl::fields::simulate_status_monitor_activity(model.settings); auto& control_protocol_resources = model.control_protocol_resources; @@ -2010,19 +2011,23 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c const auto seed_id = nmos::experimental::fields::seed_id(settings); const auto device_id = impl::make_id(seed_id, nmos::types::device); const auto how_many = impl::fields::how_many(settings); - const auto rtp_sender_ports = boost::copy_range>(impl::parse_ports(impl::fields::senders(settings)) | boost::adaptors::filtered(impl::is_rtp_port)); + const auto sender_ports = impl::parse_ports(impl::fields::senders(settings)); + const auto rtp_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_rtp_port)); const auto rtp_sender_ids = impl::make_ids(seed_id, nmos::types::sender, rtp_sender_ports, how_many); - const auto ws_sender_ports = boost::copy_range>(impl::parse_ports(impl::fields::senders(settings)) | boost::adaptors::filtered(impl::is_ws_port)); + const auto ws_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_ws_port)); const auto ws_sender_ids = impl::make_ids(seed_id, nmos::types::sender, ws_sender_ports, how_many); + const auto mxl_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_mxl_port)); + const auto mxl_sender_ids = impl::make_ids(seed_id, nmos::types::sender, mxl_sender_ports, how_many); const auto ws_sender_uri = nmos::make_events_ws_api_connection_uri(device_id, settings); - const auto rtp_receiver_ports = boost::copy_range>(impl::parse_ports(impl::fields::receivers(settings)) | boost::adaptors::filtered(impl::is_rtp_port)); + const auto receiver_ports = impl::parse_ports(impl::fields::receivers(settings)); + const auto rtp_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_rtp_port)); const auto rtp_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, rtp_receiver_ports, how_many); - const auto ws_receiver_ports = boost::copy_range>(impl::parse_ports(impl::fields::receivers(settings)) | boost::adaptors::filtered(impl::is_ws_port)); + const auto ws_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_ws_port)); const auto ws_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, ws_receiver_ports, how_many); // although which properties may need to be defaulted depends on the resource type, // the default value will almost always be different for each resource - return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) + return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids, mxl_sender_ids](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) { const std::pair id_type{ connection_resource.id, connection_resource.type }; // this code relies on the specific constraints added by node_implementation_thread @@ -2057,6 +2062,10 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c { nmos::details::resolve_auto(transport_params[0], nmos::fields::connection_authorization, [&] { return value::boolean(false); }); } + else if (mxl_sender_ids.end() != boost::range::find(mxl_sender_ids, id_type.first)) + { + nmos::details::resolve_auto(transport_params[0], nmos::fields::flow_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::flow_id))); }); + } }; } diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index 1b294511a..b6ec11478 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -243,12 +243,30 @@ namespace nmos return auto_constraints; } + static const std::map>& mxl_auto_constraints() + { + // These are the constraints that support "auto" in /staged + static const std::map> auto_constraints + { + { + nmos::types::sender, + { + nmos::fields::flow_id + } + }, + { + nmos::types::receiver, {} + } + }; + return auto_constraints; + } + static const std::map>& auto_constraints(const nmos::transport& transport_base) { if (nmos::transports::rtp == transport_base) return rtp_auto_constraints(); if (nmos::transports::websocket == transport_base) return websocket_auto_constraints(); if (nmos::transports::mqtt == transport_base) return mqtt_auto_constraints(); - //if (nmos::transports::mxl == transport_base) return mxl_auto_constraints(); + if (nmos::transports::mxl == transport_base) return mxl_auto_constraints(); static const std::map> no_auto_constraints { diff --git a/Development/nmos/connection_resources.cpp b/Development/nmos/connection_resources.cpp index 0526d0855..e6bbc82eb 100644 --- a/Development/nmos/connection_resources.cpp +++ b/Development/nmos/connection_resources.cpp @@ -615,24 +615,28 @@ namespace nmos namespace details { - web::json::value make_connection_mxl_sender_core_constraints() + web::json::value make_connection_mxl_sender_core_constraints(const nmos::id& flow_id) { using web::json::value; using web::json::value_of; const auto unconstrained = value::object(); return value_of({ - { nmos::fields::flow_id, unconstrained } + { nmos::fields::flow_id, flow_id.empty() ? unconstrained : value_of({ + { nmos::fields::constraint_enum, value_of({ + flow_id + }) } + }) } }); } - web::json::value make_connection_mxl_sender_staged_core_parameter_set(const nmos::id& flow_id) + web::json::value make_connection_mxl_sender_staged_core_parameter_set() { using web::json::value; using web::json::value_of; return value_of({ - { nmos::fields::flow_id, flow_id.empty() ? value::null() : value::string(flow_id) } + { nmos::fields::flow_id, U("auto") } }); } @@ -667,12 +671,13 @@ namespace nmos auto data = details::make_connection_resource_core(id, redundant); - data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(), redundant); + data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(flow_id), redundant); data[nmos::fields::endpoint_staged][nmos::fields::receiver_id] = value::null(); - data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_sender_staged_core_parameter_set(flow_id), redundant); + data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_sender_staged_core_parameter_set(), redundant); data[nmos::fields::endpoint_active] = data[nmos::fields::endpoint_staged]; + // The caller must resolve all instances of "auto" in the /active endpoint into the actual values that will be used! // Note that the transporttype endpoint is implemented in terms of the matching IS-04 sender diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json index 31bcda06b..58931c3be 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json @@ -11,7 +11,6 @@ ], "description": "ID of the Flow to read.", "anyOf": [{ - "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }, { diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json index d059b7f3c..7e96974fa 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json @@ -9,11 +9,13 @@ "string", "null" ], - "description": "ID of the Flow to write.", + "description": "ID of the Flow to write. If the parameter is set to auto the Sender should use the Flow it is bound to. A null value indicates that the Sender has not yet been configured.", "anyOf": [{ - "type": "string", "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }, + { + "pattern": "^auto$" + }, { "type": "null" } From e5c01057fd283b81a40ad87a05ae278693c38580 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 26 Feb 2026 10:43:53 +0000 Subject: [PATCH 10/33] Fix audio sender --- Development/nmos-cpp-node/node_implementation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 101526b51..f48788e72 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -1014,7 +1014,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } else if (impl::ports::mxl_audio == port) { - receiver = nmos::make_audio_receiver(receiver_id, device_id, nmos::transports::mxl, {}, {}, model.settings); + receiver = nmos::make_audio_receiver(receiver_id, device_id, nmos::transports::mxl, {}, std::vector{}, model.settings); } else if (impl::ports::mxl_data == port) { From a8b753589358aff11966096eac258e86dfad19a0 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 4 Mar 2026 12:00:31 +0000 Subject: [PATCH 11/33] update config.json to include mxl ports --- Development/nmos-cpp-node/config.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index c529de695..1545b1081 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -23,8 +23,8 @@ // senders, receivers: controls which kinds of sender and receiver are instantiated by the example node // the values must be an array of unique strings identifying the kinds of 'port', like ["v", "a", "d"], see impl::ports // when omitted, all ports are instantiated - //"senders": ["v", "a"], - //"receivers": [], + "senders": ["xv", "xa", "xd"], + "receivers": ["xv", "xa", "xd"], // frame_rate: controls the grain_rate of video, audio and ancillary data sources and flows // and the equivalent parameter constraint on video receivers @@ -52,7 +52,7 @@ //"component_depth": 10, // video_type: media type of video flows, e.g. "video/raw" or "video/jxsv", see nmos::media_types - //"video_type": "video/jxsv", + "video_type": "video/v210", // channel_count: controls the number of channels in audio sources //"channel_count": 8, From cfb3726e506041d4504163385a209006ade62e8b Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 5 May 2026 15:49:20 +0100 Subject: [PATCH 12/33] Added mxl_domain_id transport parameter to MXL sender and receiver connections Renamed flow_id to mxl_flow_id in MXL sender and receiver connections --- .../nmos-cpp-node/node_implementation.cpp | 32 ++++++++++++++--- Development/nmos/connection_api.cpp | 9 +++-- Development/nmos/connection_resources.cpp | 34 +++++++++++++------ Development/nmos/connection_resources.h | 4 +-- .../receiver_transport_params_mxl.json | 24 +++++++++++-- .../schemas/sender_transport_params_mxl.json | 21 ++++++++++-- 6 files changed, 100 insertions(+), 24 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index f48788e72..ace97122a 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -409,6 +409,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto seed_id = nmos::experimental::fields::seed_id(model.settings); const auto node_id = impl::make_id(seed_id, nmos::types::node); const auto device_id = impl::make_id(seed_id, nmos::types::device); + const auto mxl_domain_id = nmos::make_repeatable_id(seed_id, U("/x-nmos/mxl/domain")); const auto how_many = impl::fields::how_many(model.settings); const auto sender_ports = impl::parse_ports(impl::fields::senders(model.settings)); const auto rtp_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_rtp_port)); @@ -979,7 +980,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr impl::set_label_description(sender, port, index); impl::insert_group_hint(sender, port, index); - auto connection_sender = nmos::make_connection_mxl_sender(sender_id, flow_id); + auto connection_sender = nmos::make_connection_mxl_sender(sender_id, flow_id, mxl_domain_id); if (impl::fields::activate_senders(model.settings)) { @@ -1014,7 +1015,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } else if (impl::ports::mxl_audio == port) { - receiver = nmos::make_audio_receiver(receiver_id, device_id, nmos::transports::mxl, {}, std::vector{}, model.settings); + receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::audio, { nmos::media_type{ U("audio/float32") } }, model.settings); } else if (impl::ports::mxl_data == port) { @@ -1023,7 +1024,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr impl::set_label_description(receiver, port, index); impl::insert_group_hint(receiver, port, index); - auto connection_receiver = nmos::make_connection_mxl_receiver(receiver_id); + auto connection_receiver = nmos::make_connection_mxl_receiver(receiver_id, mxl_domain_id); if (!insert_resource_after(delay_millis, model.node_resources, std::move(receiver), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.connection_resources, std::move(connection_receiver), gate)) throw node_implementation_init_exception(); @@ -2024,10 +2025,12 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c const auto rtp_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, rtp_receiver_ports, how_many); const auto ws_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_ws_port)); const auto ws_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, ws_receiver_ports, how_many); + const auto mxl_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_mxl_port)); + const auto mxl_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, mxl_receiver_ports, how_many); // although which properties may need to be defaulted depends on the resource type, // the default value will almost always be different for each resource - return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids, mxl_sender_ids](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) + return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids, mxl_sender_ids, mxl_receiver_ids](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) { const std::pair id_type{ connection_resource.id, connection_resource.type }; // this code relies on the specific constraints added by node_implementation_thread @@ -2064,7 +2067,26 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c } else if (mxl_sender_ids.end() != boost::range::find(mxl_sender_ids, id_type.first)) { - nmos::details::resolve_auto(transport_params[0], nmos::fields::flow_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::flow_id))); }); + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_flow_id))); }); + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_domain_id))); }); + } + else if (mxl_receiver_ids.end() != boost::range::find(mxl_receiver_ids, id_type.first)) + { + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_domain_id))); }); + // mxl_flow_id may be unconstrained on receivers; resolve auto only when constraints enumerate values + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] + { + const auto& fc = constraints.at(0).at(nmos::fields::mxl_flow_id); + if (fc.is_object() && fc.has_field(nmos::fields::constraint_enum)) + { + const auto& en = nmos::fields::constraint_enum(fc); + if (en.is_array() && 0 != en.as_array().size()) + { + return web::json::front(en); + } + } + return web::json::value::null(); + }); } }; } diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index b6ec11478..864fd0bb5 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -251,11 +251,16 @@ namespace nmos { nmos::types::sender, { - nmos::fields::flow_id + nmos::fields::mxl_flow_id, + nmos::fields::mxl_domain_id } }, { - nmos::types::receiver, {} + nmos::types::receiver, + { + nmos::fields::mxl_domain_id, + nmos::fields::mxl_flow_id + } } }; return auto_constraints; diff --git a/Development/nmos/connection_resources.cpp b/Development/nmos/connection_resources.cpp index e6bbc82eb..93c4512ed 100644 --- a/Development/nmos/connection_resources.cpp +++ b/Development/nmos/connection_resources.cpp @@ -615,16 +615,21 @@ namespace nmos namespace details { - web::json::value make_connection_mxl_sender_core_constraints(const nmos::id& flow_id) + web::json::value make_connection_mxl_sender_core_constraints(const nmos::id& mxl_flow_id, const nmos::id& mxl_domain_id) { using web::json::value; using web::json::value_of; const auto unconstrained = value::object(); return value_of({ - { nmos::fields::flow_id, flow_id.empty() ? unconstrained : value_of({ + { nmos::fields::mxl_flow_id, mxl_flow_id.empty() ? unconstrained : value_of({ { nmos::fields::constraint_enum, value_of({ - flow_id + mxl_flow_id + }) } + }) }, + { nmos::fields::mxl_domain_id, mxl_domain_id.empty() ? unconstrained : value_of({ + { nmos::fields::constraint_enum, value_of({ + mxl_domain_id }) } }) } }); @@ -636,18 +641,24 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::flow_id, U("auto") } + { nmos::fields::mxl_flow_id, U("auto") }, + { nmos::fields::mxl_domain_id, U("auto") } }); } - web::json::value make_connection_mxl_receiver_core_constraints() + web::json::value make_connection_mxl_receiver_core_constraints(const nmos::id& mxl_domain_id) { using web::json::value; using web::json::value_of; const auto unconstrained = value::object(); return value_of({ - { nmos::fields::flow_id, unconstrained } + { nmos::fields::mxl_flow_id, unconstrained }, + { nmos::fields::mxl_domain_id, mxl_domain_id.empty() ? unconstrained : value_of({ + { nmos::fields::constraint_enum, value_of({ + mxl_domain_id + }) } + }) } }); } @@ -657,12 +668,13 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::flow_id, value::null() } + { nmos::fields::mxl_flow_id, value::null() }, + { nmos::fields::mxl_domain_id, U("auto") } }); } } - nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& flow_id) + nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& mxl_flow_id, const nmos::id& mxl_domain_id) { using web::json::value; using web::json::value_of; @@ -671,7 +683,7 @@ namespace nmos auto data = details::make_connection_resource_core(id, redundant); - data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(flow_id), redundant); + data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(mxl_flow_id, mxl_domain_id), redundant); data[nmos::fields::endpoint_staged][nmos::fields::receiver_id] = value::null(); data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_sender_staged_core_parameter_set(), redundant); @@ -684,7 +696,7 @@ namespace nmos return{ is05_versions::v1_2, types::sender, std::move(data), false }; } - nmos::resource make_connection_mxl_receiver(const nmos::id& id) + nmos::resource make_connection_mxl_receiver(const nmos::id& id, const nmos::id& mxl_domain_id) { using web::json::value; @@ -692,7 +704,7 @@ namespace nmos auto data = details::make_connection_resource_core(id, redundant); - data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_receiver_core_constraints(), redundant); + data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_receiver_core_constraints(mxl_domain_id), redundant); data[nmos::fields::endpoint_staged][nmos::fields::sender_id] = value::null(); data[nmos::fields::endpoint_staged][nmos::fields::transport_file] = details::make_connection_receiver_staging_transport_file(); diff --git a/Development/nmos/connection_resources.h b/Development/nmos/connection_resources.h index 24d03b339..5f8b4ddc6 100644 --- a/Development/nmos/connection_resources.h +++ b/Development/nmos/connection_resources.h @@ -61,8 +61,8 @@ namespace nmos utility::string_t make_events_mqtt_broker_topic(const nmos::id& source_id, const nmos::settings& settings); utility::string_t make_events_mqtt_connection_status_broker_topic(const nmos::id& connection_id, const nmos::settings& settings); - nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& flow_id); - nmos::resource make_connection_mxl_receiver(const nmos::id& id); + nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& mxl_flow_id, const nmos::id& mxl_domain_id); + nmos::resource make_connection_mxl_receiver(const nmos::id& id, const nmos::id& mxl_domain_id); } #endif diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json index 58931c3be..bc20bbf5b 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json @@ -4,15 +4,35 @@ "title": "MXL Receiver Transport Parameters", "type": "object", "properties": { - "flow_id": { + "mxl_domain_id": { "type": [ "string", "null" ], - "description": "ID of the Flow to read.", + "description": "MXL domain identity (UUID). Use auto to select from constraints.", "anyOf": [{ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }, + { + "pattern": "^auto$" + }, + { + "type": "null" + } + ] + }, + "mxl_flow_id": { + "type": [ + "string", + "null" + ], + "description": "MXL flow identity for the read operation. Need not match the IS-04 Flow id. Use auto to select from constraints.", + "anyOf": [{ + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + { + "pattern": "^auto$" + }, { "type": "null" } diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json index 7e96974fa..527b1c908 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json @@ -4,12 +4,29 @@ "title": "MXL Sender Transport Parameters", "type": "object", "properties": { - "flow_id": { + "mxl_domain_id": { "type": [ "string", "null" ], - "description": "ID of the Flow to write. If the parameter is set to auto the Sender should use the Flow it is bound to. A null value indicates that the Sender has not yet been configured.", + "description": "MXL domain identity (UUID). Use auto to select from constraints.", + "anyOf": [{ + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" + }, + { + "pattern": "^auto$" + }, + { + "type": "null" + } + ] + }, + "mxl_flow_id": { + "type": [ + "string", + "null" + ], + "description": "MXL flow identity for the write operation. Need not match the IS-04 Flow id. Use auto to select from constraints.", "anyOf": [{ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }, From 4fb707f96c0c5dca569ea6273d246bf1ecbd62f3 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 5 May 2026 16:08:35 +0100 Subject: [PATCH 13/33] Add missing JSON fields for MXL sender and receiver connections --- Development/nmos/json_fields.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index 5e3b4739f..bc91ff896 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -172,8 +172,9 @@ namespace nmos const web::json::field_as_value_or broker_authorization{ U("broker_authorization"), {} }; // string or bool const web::json::field_as_value_or broker_topic{ U("broker_topic"), {} }; // string or null const web::json::field_as_value_or connection_status_broker_topic{ U("connection_status_broker_topic"), {} }; // string or null - // for urn:x-nmos:transport:mxl - //const web::json::field_as_value flow_id{ U("flow_id") }; // see nmos::id + // for urn:x-nmos:transport:mxl (see AMWA BCP-007-03 NMOS With MXL) + const web::json::field_as_value_or mxl_domain_id{ U("mxl_domain_id"), {} }; // UUID string, auto, or null + const web::json::field_as_value_or mxl_flow_id{ U("mxl_flow_id"), {} }; // UUID string, auto, or null // IS-07 Event & Tally From d36cf53fbb9ef5af5da403464ba9a46f985268ff Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 6 May 2026 11:46:45 +0100 Subject: [PATCH 14/33] Make mxl domain id configurable --- Development/nmos-cpp-node/node_implementation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index ace97122a..9cb721fac 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -127,6 +127,9 @@ namespace impl // simulate_status_monitor_activity: when true status monitor statuses will change randomly after activation const web::json::field_as_bool_or simulate_status_monitor_activity{U("simulate_status_monitor_activity"), true}; + + // mxl_domain_id: optional, used to override the generated MXL domain id + const web::json::field_as_string_or mxl_domain_id{ U("mxl_domain_id"), {} }; } nmos::interlace_mode get_interlace_mode(const nmos::settings& settings); @@ -409,7 +412,9 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto seed_id = nmos::experimental::fields::seed_id(model.settings); const auto node_id = impl::make_id(seed_id, nmos::types::node); const auto device_id = impl::make_id(seed_id, nmos::types::device); - const auto mxl_domain_id = nmos::make_repeatable_id(seed_id, U("/x-nmos/mxl/domain")); + const nmos::id mxl_domain_id = model.settings.has_field(impl::fields::mxl_domain_id) + ? impl::fields::mxl_domain_id(model.settings) + : nmos::make_repeatable_id(seed_id, U("/x-nmos/mxl/domain")); const auto how_many = impl::fields::how_many(model.settings); const auto sender_ports = impl::parse_ports(impl::fields::senders(model.settings)); const auto rtp_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_rtp_port)); From b4389a7394e1fefa08e050bcc432aaf3f119c872 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 6 May 2026 14:06:45 +0100 Subject: [PATCH 15/33] Added mxl_domain_id to config.json --- Development/nmos-cpp-node/config.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index 1545b1081..dbb43d263 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -198,6 +198,9 @@ // seed id [registry, node]: optional, used to generate repeatable id values when running with the same configuration //"seed_id": uuid-string, + // mxl_domain_id [node]: optional, overrides the generated MXL domain id used by MXL sender/receiver transport params + //"mxl_domain_id": uuid-string, + // label [registry, node]: used in resource label field //"label": "", From d2f8dbf29e415bf233f6aa81686cde3a2002b478 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 6 May 2026 15:28:43 +0100 Subject: [PATCH 16/33] Ensure consistent mxl_domain_id and mxl_flow_id ordering --- Development/nmos/connection_api.cpp | 4 ++-- Development/nmos/connection_resources.cpp | 26 +++++++++++------------ Development/nmos/connection_resources.h | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index 864fd0bb5..9a7b34d27 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -251,8 +251,8 @@ namespace nmos { nmos::types::sender, { - nmos::fields::mxl_flow_id, - nmos::fields::mxl_domain_id + nmos::fields::mxl_domain_id, + nmos::fields::mxl_flow_id } }, { diff --git a/Development/nmos/connection_resources.cpp b/Development/nmos/connection_resources.cpp index 93c4512ed..55ddd4885 100644 --- a/Development/nmos/connection_resources.cpp +++ b/Development/nmos/connection_resources.cpp @@ -615,21 +615,21 @@ namespace nmos namespace details { - web::json::value make_connection_mxl_sender_core_constraints(const nmos::id& mxl_flow_id, const nmos::id& mxl_domain_id) + web::json::value make_connection_mxl_sender_core_constraints(const nmos::id& mxl_domain_id, const nmos::id& mxl_flow_id) { using web::json::value; using web::json::value_of; const auto unconstrained = value::object(); return value_of({ - { nmos::fields::mxl_flow_id, mxl_flow_id.empty() ? unconstrained : value_of({ + { nmos::fields::mxl_domain_id, mxl_domain_id.empty() ? unconstrained : value_of({ { nmos::fields::constraint_enum, value_of({ - mxl_flow_id + mxl_domain_id }) } }) }, - { nmos::fields::mxl_domain_id, mxl_domain_id.empty() ? unconstrained : value_of({ + { nmos::fields::mxl_flow_id, mxl_flow_id.empty() ? unconstrained : value_of({ { nmos::fields::constraint_enum, value_of({ - mxl_domain_id + mxl_flow_id }) } }) } }); @@ -641,8 +641,8 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::mxl_flow_id, U("auto") }, - { nmos::fields::mxl_domain_id, U("auto") } + { nmos::fields::mxl_domain_id, U("auto") }, + { nmos::fields::mxl_flow_id, U("auto") } }); } @@ -653,12 +653,12 @@ namespace nmos const auto unconstrained = value::object(); return value_of({ - { nmos::fields::mxl_flow_id, unconstrained }, { nmos::fields::mxl_domain_id, mxl_domain_id.empty() ? unconstrained : value_of({ { nmos::fields::constraint_enum, value_of({ mxl_domain_id }) } - }) } + }) }, + { nmos::fields::mxl_flow_id, unconstrained } }); } @@ -668,13 +668,13 @@ namespace nmos using web::json::value_of; return value_of({ - { nmos::fields::mxl_flow_id, value::null() }, - { nmos::fields::mxl_domain_id, U("auto") } + { nmos::fields::mxl_domain_id, U("auto") }, + { nmos::fields::mxl_flow_id, value::null() } }); } } - nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& mxl_flow_id, const nmos::id& mxl_domain_id) + nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& mxl_domain_id, const nmos::id& mxl_flow_id) { using web::json::value; using web::json::value_of; @@ -683,7 +683,7 @@ namespace nmos auto data = details::make_connection_resource_core(id, redundant); - data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(mxl_flow_id, mxl_domain_id), redundant); + data[nmos::fields::endpoint_constraints] = details::legs_of(details::make_connection_mxl_sender_core_constraints(mxl_domain_id, mxl_flow_id), redundant); data[nmos::fields::endpoint_staged][nmos::fields::receiver_id] = value::null(); data[nmos::fields::endpoint_staged][nmos::fields::transport_params] = details::legs_of(details::make_connection_mxl_sender_staged_core_parameter_set(), redundant); diff --git a/Development/nmos/connection_resources.h b/Development/nmos/connection_resources.h index 5f8b4ddc6..2f757a728 100644 --- a/Development/nmos/connection_resources.h +++ b/Development/nmos/connection_resources.h @@ -61,7 +61,7 @@ namespace nmos utility::string_t make_events_mqtt_broker_topic(const nmos::id& source_id, const nmos::settings& settings); utility::string_t make_events_mqtt_connection_status_broker_topic(const nmos::id& connection_id, const nmos::settings& settings); - nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& mxl_flow_id, const nmos::id& mxl_domain_id); + nmos::resource make_connection_mxl_sender(const nmos::id& id, const nmos::id& mxl_domain_id, const nmos::id& mxl_flow_id); nmos::resource make_connection_mxl_receiver(const nmos::id& id, const nmos::id& mxl_domain_id); } From 6a31cab8c819a623efa611568f732ff4f69d8fd2 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 15 May 2026 12:17:02 +0100 Subject: [PATCH 17/33] Update MXL configuration and implementation according to latest draft of BCP-007-03 --- Development/nmos-cpp-node/config.json | 9 +-- .../nmos-cpp-node/node_implementation.cpp | 66 ++++++++++++++----- Development/nmos/connection_api.cpp | 20 +++++- Development/nmos/json_fields.h | 2 +- .../receiver_transport_params_mxl.json | 5 +- 5 files changed, 74 insertions(+), 28 deletions(-) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index dbb43d263..6501951e3 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -22,9 +22,10 @@ // senders, receivers: controls which kinds of sender and receiver are instantiated by the example node // the values must be an array of unique strings identifying the kinds of 'port', like ["v", "a", "d"], see impl::ports + // for MXL Senders and Receivers, the values must be ["xv", "xa", "xd"] // when omitted, all ports are instantiated - "senders": ["xv", "xa", "xd"], - "receivers": ["xv", "xa", "xd"], + //"senders": ["v", "a", "d"], + //"receivers": ["v", "a", "d"], // frame_rate: controls the grain_rate of video, audio and ancillary data sources and flows // and the equivalent parameter constraint on video receivers @@ -51,8 +52,8 @@ // component_depth: controls the bits per component sample of video flows //"component_depth": 10, - // video_type: media type of video flows, e.g. "video/raw" or "video/jxsv", see nmos::media_types - "video_type": "video/v210", + // video_type: media type of video flows, e.g. "video/raw", "video/jxsv", or "video/v210" for MXL Senders and Receivers, see nmos::media_types + //"video_type": "video/raw", // channel_count: controls the number of channels in audio sources //"channel_count": 8, diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 9cb721fac..aba7fd005 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -985,7 +985,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr impl::set_label_description(sender, port, index); impl::insert_group_hint(sender, port, index); - auto connection_sender = nmos::make_connection_mxl_sender(sender_id, flow_id, mxl_domain_id); + auto connection_sender = nmos::make_connection_mxl_sender(sender_id, mxl_domain_id, flow_id); if (impl::fields::activate_senders(model.settings)) { @@ -1017,14 +1017,61 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr if (impl::ports::mxl_video == port) { receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::video, { video_type }, model.settings); + if (nmos::media_types::video_raw == video_type) + { + const auto interlace_modes = nmos::interlace_modes::progressive != interlace_mode + ? std::vector{ nmos::interlace_modes::interlaced_bff.name, nmos::interlace_modes::interlaced_tff.name, nmos::interlace_modes::interlaced_psf.name } + : std::vector{ nmos::interlace_modes::progressive.name }; + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ + { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ frame_rate }) }, + { nmos::caps::format::frame_width, nmos::make_caps_integer_constraint({ frame_width }) }, + { nmos::caps::format::frame_height, nmos::make_caps_integer_constraint({ frame_height }) }, + { nmos::caps::format::interlace_mode, nmos::make_caps_string_constraint(interlace_modes) }, + { nmos::caps::format::color_sampling, nmos::make_caps_string_constraint({ sampling.name }) } + }) + }); + } + else if (nmos::media_types::video_jxsv == video_type) + { + const auto max_format_bit_rate = nmos::get_video_jxsv_bit_rate(frame_rate, frame_width, frame_height, max_bits_per_pixel); + const auto max_transport_bit_rate = uint64_t(transport_bit_rate_factor * max_format_bit_rate / 1e3 + 0.5) * 1000; + + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ + { nmos::caps::format::profile, nmos::make_caps_string_constraint({ profile.name }) }, + { nmos::caps::format::level, nmos::make_caps_string_constraint({ level.name }) }, + { nmos::caps::format::sublevel, nmos::make_caps_string_constraint({ nmos::sublevels::Sublev3bpp.name, nmos::sublevels::Sublev4bpp.name }) }, + { nmos::caps::format::bit_rate, nmos::make_caps_integer_constraint({}, nmos::no_minimum(), (int64_t)max_format_bit_rate) }, + { nmos::caps::transport::bit_rate, nmos::make_caps_integer_constraint({}, nmos::no_minimum(), (int64_t)max_transport_bit_rate) }, + { nmos::caps::transport::packet_transmission_mode, nmos::make_caps_string_constraint({ nmos::packet_transmission_modes::codestream.name }) } + }) + }); + } + receiver.data[nmos::fields::version] = receiver.data[nmos::fields::caps][nmos::fields::version] = value(nmos::make_version()); } else if (impl::ports::mxl_audio == port) { receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::audio, { nmos::media_type{ U("audio/float32") } }, model.settings); + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ + { nmos::caps::format::media_type, nmos::make_caps_string_constraint({ U("audio/float32") }) }, + { nmos::caps::format::channel_count, nmos::make_caps_integer_constraint({}, 1, channel_count) }, + { nmos::caps::format::sample_rate, nmos::make_caps_rational_constraint({ { 48000, 1 } }) }, + { nmos::caps::format::sample_depth, nmos::make_caps_integer_constraint({ 32 }) } + }) + }); + receiver.data[nmos::fields::version] = receiver.data[nmos::fields::caps][nmos::fields::version] = value(nmos::make_version()); } else if (impl::ports::mxl_data == port) { receiver = nmos::make_sdianc_data_receiver(receiver_id, device_id, nmos::transports::mxl, {}, model.settings); + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ + { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ frame_rate }) } + }) + }); + receiver.data[nmos::fields::version] = receiver.data[nmos::fields::caps][nmos::fields::version] = value(nmos::make_version()); } impl::set_label_description(receiver, port, index); impl::insert_group_hint(receiver, port, index); @@ -2072,26 +2119,13 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c } else if (mxl_sender_ids.end() != boost::range::find(mxl_sender_ids, id_type.first)) { - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_flow_id))); }); nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_domain_id))); }); + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_flow_id))); }); } else if (mxl_receiver_ids.end() != boost::range::find(mxl_receiver_ids, id_type.first)) { + // BCP-007-03: mxl_flow_id does not use "auto" on receivers (UUID or null only). nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_domain_id))); }); - // mxl_flow_id may be unconstrained on receivers; resolve auto only when constraints enumerate values - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] - { - const auto& fc = constraints.at(0).at(nmos::fields::mxl_flow_id); - if (fc.is_object() && fc.has_field(nmos::fields::constraint_enum)) - { - const auto& en = nmos::fields::constraint_enum(fc); - if (en.is_array() && 0 != en.as_array().size()) - { - return web::json::front(en); - } - } - return web::json::value::null(); - }); } }; } diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index 9a7b34d27..aa9994a84 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -246,6 +246,8 @@ namespace nmos static const std::map>& mxl_auto_constraints() { // These are the constraints that support "auto" in /staged + // BCP-007-03: MXL Receivers MUST NOT use "auto" for mxl_flow_id (domain only). + // See https://specs.amwa.tv/bcp-007-03/branches/publish-auto-null/docs/NMOS-With-MXL.html static const std::map> auto_constraints { { @@ -258,8 +260,7 @@ namespace nmos { nmos::types::receiver, { - nmos::fields::mxl_domain_id, - nmos::fields::mxl_flow_id + nmos::fields::mxl_domain_id } } }; @@ -518,6 +519,20 @@ namespace nmos throw std::logic_error("matching IS-04 and IS-05 resources not found"); } + const nmos::transport transport_subclassification(nmos::fields::transport(matching_resource->data)); + + // BCP-007-03: PATCH /staged for MXL receivers must not contain transport_file. + // See https://specs.amwa.tv/bcp-007-03/branches/publish-auto-null/docs/NMOS-With-MXL.html + if (nmos::types::receiver == id_type.second && nmos::transports::mxl == nmos::transport_base(transport_subclassification)) + { + if (patch.has_field(nmos::fields::transport_file.key)) + { + slog::log(gate, SLOG_FLF) << "Rejecting PATCH for MXL receiver with transport_file"; + + return details::make_connection_resource_patch_error_response(status_codes::BadRequest, U("transport_file must not be used with MXL receivers")); + } + } + // Merge this patch request into a *copy* of the current staged endpoint // so that the merged parameters can be validated against the constraints // before the current values are overwritten. @@ -578,7 +593,6 @@ namespace nmos slog::log(gate, SLOG_FLF) << "Validating staged transport parameters against constraints"; - const nmos::transport transport_subclassification(nmos::fields::transport(matching_resource->data)); details::validate_staged_constraints(resource->type, nmos::fields::endpoint_constraints(resource->data), nmos::transport_base(transport_subclassification), merged); // Perform any final validation diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index bc91ff896..e195e61ae 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -174,7 +174,7 @@ namespace nmos const web::json::field_as_value_or connection_status_broker_topic{ U("connection_status_broker_topic"), {} }; // string or null // for urn:x-nmos:transport:mxl (see AMWA BCP-007-03 NMOS With MXL) const web::json::field_as_value_or mxl_domain_id{ U("mxl_domain_id"), {} }; // UUID string, auto, or null - const web::json::field_as_value_or mxl_flow_id{ U("mxl_flow_id"), {} }; // UUID string, auto, or null + const web::json::field_as_value_or mxl_flow_id{ U("mxl_flow_id"), {} }; // senders: UUID, auto, or null; receivers: UUID or null (BCP-007-03) // IS-07 Event & Tally diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json index bc20bbf5b..c1153be59 100644 --- a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json +++ b/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json @@ -26,13 +26,10 @@ "string", "null" ], - "description": "MXL flow identity for the read operation. Need not match the IS-04 Flow id. Use auto to select from constraints.", + "description": "MXL flow identity for the read operation. Need not match the IS-04 Flow id. BCP-007-03: null or UUID only; the literal auto is not used for receivers.", "anyOf": [{ "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" }, - { - "pattern": "^auto$" - }, { "type": "null" } From c70da56f101bba83adac29f8c188ac24162afb7d Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe <64410119+jonathan-r-thorpe@users.noreply.github.com> Date: Mon, 18 May 2026 16:54:15 +0100 Subject: [PATCH 18/33] Apply suggestions from code review Co-authored-by: Gareth Sylvester-Bradley <31761158+garethsb@users.noreply.github.com> --- Development/nmos-cpp-node/config.json | 5 +++-- Development/nmos/is05_schemas/is05_schemas.h | 2 +- Development/nmos/json_schema.cpp | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index 6501951e3..5e798bfa6 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -22,7 +22,7 @@ // senders, receivers: controls which kinds of sender and receiver are instantiated by the example node // the values must be an array of unique strings identifying the kinds of 'port', like ["v", "a", "d"], see impl::ports - // for MXL Senders and Receivers, the values must be ["xv", "xa", "xd"] + // for MXL senders and receivers, the values must be like ["xv", "xa", "xd"] // when omitted, all ports are instantiated //"senders": ["v", "a", "d"], //"receivers": ["v", "a", "d"], @@ -52,7 +52,8 @@ // component_depth: controls the bits per component sample of video flows //"component_depth": 10, - // video_type: media type of video flows, e.g. "video/raw", "video/jxsv", or "video/v210" for MXL Senders and Receivers, see nmos::media_types + // video_type: media type of video flows, e.g. "video/raw", "video/jxsv" for ST 2110 senders and receivers + // or "video/v210" for MXL senders and receivers, see nmos::media_types //"video_type": "video/raw", // channel_count: controls the number of channels in audio sources diff --git a/Development/nmos/is05_schemas/is05_schemas.h b/Development/nmos/is05_schemas/is05_schemas.h index 2c3db393a..7b2fecee2 100644 --- a/Development/nmos/is05_schemas/is05_schemas.h +++ b/Development/nmos/is05_schemas/is05_schemas.h @@ -7,7 +7,7 @@ namespace nmos { namespace is05_schemas { - namespace v1_2_x + namespace v1_2_dev { extern const char* activation_schema; extern const char* sender_stage_schema; diff --git a/Development/nmos/json_schema.cpp b/Development/nmos/json_schema.cpp index e6491e7f0..ab021e059 100644 --- a/Development/nmos/json_schema.cpp +++ b/Development/nmos/json_schema.cpp @@ -77,11 +77,11 @@ namespace nmos return{ _XPLATSTR("https://github.com/AMWA-TV/is-05/raw/") + tag + _XPLATSTR("/APIs/schemas/") + ref }; } - // See https://github.com/AMWA-TV/is-05/blob/v1.2.x/APIs/schemas/ + // See https://github.com/AMWA-TV/is-05/blob/v1.2-dev/APIs/schemas/ namespace v1_2 { - using namespace nmos::is05_schemas::v1_2_x; - const utility::string_t tag(_XPLATSTR("v1.2.x")); + using namespace nmos::is05_schemas::v1_2_dev; + const utility::string_t tag(_XPLATSTR("v1.2-dev")); const web::uri connectionapi_sender_staged_patch_request_uri = make_schema_uri(tag, _XPLATSTR("sender-stage-schema.json")); const web::uri connectionapi_receiver_staged_patch_request_uri = make_schema_uri(tag, _XPLATSTR("receiver-stage-schema.json")); From 255da9bd1873e5f0d69ad5530adf329be4b589a5 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Mon, 18 May 2026 16:55:54 +0100 Subject: [PATCH 19/33] Patch to fix the build-test workflow. --- .github/workflows/build-test.yml | 2 ++ .github/workflows/src/amwa-test.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 18c0cdfa9..b3efeac6a 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -429,6 +429,8 @@ jobs: run_python="python" fi pip install -r utilities/run-test-suites/gsheetsImport/requirements.txt + # PyOpenSSL 26.2+ removed OpenSSL.crypto.X509Extension; nmos-testing still uses it for certificate tests. + $run_python -m pip install 'pyOpenSSL<26.2' if [[ "${{ runner.os }}" == "Windows" ]]; then # install certificates diff --git a/.github/workflows/src/amwa-test.yml b/.github/workflows/src/amwa-test.yml index 68f7778b1..b0f2151f8 100644 --- a/.github/workflows/src/amwa-test.yml +++ b/.github/workflows/src/amwa-test.yml @@ -76,6 +76,8 @@ run_python="python" fi pip install -r utilities/run-test-suites/gsheetsImport/requirements.txt + # PyOpenSSL 26.2+ removed OpenSSL.crypto.X509Extension; nmos-testing still uses it for certificate tests. + $run_python -m pip install 'pyOpenSSL<26.2' if [[ "${{ runner.os }}" == "Windows" ]]; then # install certificates From 9aefbc4990fe177208ad18e37d15f210e131dc9f Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Mon, 18 May 2026 17:38:10 +0100 Subject: [PATCH 20/33] Moved IS-05 v1.2.x schemas to v1.2-dev. --- Development/cmake/NmosCppLibraries.cmake | 2 +- .../APIs/schemas/activation-response-schema.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/activation-schema.json | 0 .../APIs/schemas/bulk-receiver-post-schema.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/bulk-response-schema.json | 0 .../APIs/schemas/bulk-sender-post-schema.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-base.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-bulk.json | 0 .../APIs/schemas/connectionapi-receiver.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-sender.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-single.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/constraint-schema.json | 0 .../APIs/schemas/constraints-schema-mqtt.json | 0 .../APIs/schemas/constraints-schema-rtp.json | 0 .../APIs/schemas/constraints-schema-websocket.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/constraints-schema.json | 0 .../is-05/{v1.2.x => v1.2-dev}/APIs/schemas/error.json | 0 .../APIs/schemas/receiver-response-schema.json | 0 .../APIs/schemas/receiver-stage-schema.json | 0 .../APIs/schemas/receiver-transport-file.json | 0 .../APIs/schemas/receiver_transport_params.json | 0 .../APIs/schemas/receiver_transport_params_dash.json | 0 .../APIs/schemas/receiver_transport_params_ext.json | 0 .../APIs/schemas/receiver_transport_params_mqtt.json | 0 .../APIs/schemas/receiver_transport_params_mxl.json | 0 .../APIs/schemas/receiver_transport_params_rtp.json | 0 .../APIs/schemas/receiver_transport_params_websocket.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/sender-receiver-base.json | 0 .../APIs/schemas/sender-response-schema.json | 0 .../{v1.2.x => v1.2-dev}/APIs/schemas/sender-stage-schema.json | 0 .../APIs/schemas/sender_transport_params.json | 0 .../APIs/schemas/sender_transport_params_dash.json | 0 .../APIs/schemas/sender_transport_params_ext.json | 0 .../APIs/schemas/sender_transport_params_mqtt.json | 0 .../APIs/schemas/sender_transport_params_mxl.json | 0 .../APIs/schemas/sender_transport_params_rtp.json | 0 .../APIs/schemas/sender_transport_params_websocket.json | 0 .../APIs/schemas/transporttype-response-schema.json | 0 38 files changed, 1 insertion(+), 1 deletion(-) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/activation-response-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/activation-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/bulk-receiver-post-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/bulk-response-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/bulk-sender-post-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-base.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-bulk.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-receiver.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-sender.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/connectionapi-single.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/constraint-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/constraints-schema-mqtt.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/constraints-schema-rtp.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/constraints-schema-websocket.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/constraints-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/error.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver-response-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver-stage-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver-transport-file.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver_transport_params.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver_transport_params_dash.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver_transport_params_ext.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver_transport_params_mqtt.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver_transport_params_mxl.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver_transport_params_rtp.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/receiver_transport_params_websocket.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender-receiver-base.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender-response-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender-stage-schema.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender_transport_params.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender_transport_params_dash.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender_transport_params_ext.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender_transport_params_mqtt.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender_transport_params_mxl.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender_transport_params_rtp.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/sender_transport_params_websocket.json (100%) rename Development/third_party/is-05/{v1.2.x => v1.2-dev}/APIs/schemas/transporttype-response-schema.json (100%) diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index 982e000b0..ea1fe5f89 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -407,7 +407,7 @@ set(NMOS_IS05_SCHEMAS_HEADERS nmos/is05_schemas/is05_schemas.h ) -set(NMOS_IS05_V1_2_TAG v1.2.x) +set(NMOS_IS05_V1_2_TAG v1.2-dev) set(NMOS_IS05_V1_1_TAG v1.1.x) set(NMOS_IS05_V1_0_TAG v1.0.x) diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-response-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/activation-response-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/activation-response-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/activation-response-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/activation-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/activation-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/activation-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/activation-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-receiver-post-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/bulk-receiver-post-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-receiver-post-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/bulk-receiver-post-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-response-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/bulk-response-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-response-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/bulk-response-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-sender-post-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/bulk-sender-post-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/bulk-sender-post-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/bulk-sender-post-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-base.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-base.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-base.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-base.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-bulk.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-bulk.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-bulk.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-bulk.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-receiver.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-receiver.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-receiver.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-receiver.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-sender.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-sender.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-sender.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-sender.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-single.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-single.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/connectionapi-single.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/connectionapi-single.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraint-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraint-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/constraint-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/constraint-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-mqtt.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-mqtt.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-mqtt.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-mqtt.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-rtp.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-rtp.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-rtp.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-rtp.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-websocket.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-websocket.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema-websocket.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-websocket.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/constraints-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/error.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/error.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/error.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/error.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-response-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver-response-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-response-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver-response-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-stage-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver-stage-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-stage-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver-stage-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-transport-file.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver-transport-file.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver-transport-file.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver-transport-file.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_dash.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_dash.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_dash.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_dash.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_ext.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_ext.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_ext.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_ext.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mqtt.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_mqtt.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mqtt.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_mqtt.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_mxl.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_mxl.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_mxl.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_rtp.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_rtp.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_rtp.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_rtp.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_websocket.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_websocket.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/receiver_transport_params_websocket.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/receiver_transport_params_websocket.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-receiver-base.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender-receiver-base.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender-receiver-base.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender-receiver-base.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-response-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender-response-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender-response-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender-response-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender-stage-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender-stage-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender-stage-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender-stage-schema.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_dash.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_dash.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_dash.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_dash.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_ext.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_ext.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_ext.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_ext.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mqtt.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_mqtt.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mqtt.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_mqtt.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_mxl.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_mxl.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_mxl.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_rtp.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_rtp.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_rtp.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_rtp.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_websocket.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_websocket.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/sender_transport_params_websocket.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/sender_transport_params_websocket.json diff --git a/Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/transporttype-response-schema.json similarity index 100% rename from Development/third_party/is-05/v1.2.x/APIs/schemas/transporttype-response-schema.json rename to Development/third_party/is-05/v1.2-dev/APIs/schemas/transporttype-response-schema.json From a873136b5be6e77ebba5c5efa927e51805056ab9 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 19 May 2026 13:54:41 +0100 Subject: [PATCH 21/33] Responded to review comments on pull request --- Development/cmake/NmosCppLibraries.cmake | 1 + Development/nmos-cpp-node/config.json | 10 +-- .../nmos-cpp-node/node_implementation.cpp | 66 +++++++------------ Development/nmos/json_fields.h | 2 +- Development/nmos/mxl.h | 25 +++++++ 5 files changed, 58 insertions(+), 46 deletions(-) create mode 100644 Development/nmos/mxl.h diff --git a/Development/cmake/NmosCppLibraries.cmake b/Development/cmake/NmosCppLibraries.cmake index ea1fe5f89..f60ef3233 100644 --- a/Development/cmake/NmosCppLibraries.cmake +++ b/Development/cmake/NmosCppLibraries.cmake @@ -1214,6 +1214,7 @@ set(NMOS_CPP_NMOS_HEADERS nmos/mdns_api.h nmos/mdns_versions.h nmos/media_type.h + nmos/mxl.h nmos/model.h nmos/mutex.h nmos/node_api.h diff --git a/Development/nmos-cpp-node/config.json b/Development/nmos-cpp-node/config.json index 5e798bfa6..c23476ba8 100644 --- a/Development/nmos-cpp-node/config.json +++ b/Development/nmos-cpp-node/config.json @@ -24,8 +24,8 @@ // the values must be an array of unique strings identifying the kinds of 'port', like ["v", "a", "d"], see impl::ports // for MXL senders and receivers, the values must be like ["xv", "xa", "xd"] // when omitted, all ports are instantiated - //"senders": ["v", "a", "d"], - //"receivers": ["v", "a", "d"], + //"senders": ["v", "a"], + //"receivers": [], // frame_rate: controls the grain_rate of video, audio and ancillary data sources and flows // and the equivalent parameter constraint on video receivers @@ -52,10 +52,12 @@ // component_depth: controls the bits per component sample of video flows //"component_depth": 10, - // video_type: media type of video flows, e.g. "video/raw", "video/jxsv" for ST 2110 senders and receivers - // or "video/v210" for MXL senders and receivers, see nmos::media_types + // video_type: media type of video flows, e.g. "video/raw", "video/jxsv" for ST 2110 senders and receivers, see nmos::media_types //"video_type": "video/raw", + // mxl_video_type: media type of MXL video flows and receivers, e.g. "video/v210" or "video/v210a", see nmos/mxl.h + //"mxl_video_type": "video/v210", + // channel_count: controls the number of channels in audio sources //"channel_count": 8, diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index aba7fd005..4d24058bc 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -38,6 +38,7 @@ #include "nmos/lldp_manager.h" #endif #include "nmos/media_type.h" +#include "nmos/mxl.h" #include "nmos/model.h" #include "nmos/node_interfaces.h" #include "nmos/node_resource.h" @@ -119,6 +120,9 @@ namespace impl // video_type: media type of video flows, e.g. "video/raw" or "video/jxsv", see nmos::media_types const web::json::field_as_string_or video_type{ U("video_type"), U("video/raw") }; + // mxl_video_type: media type of MXL video flows and receivers, e.g. "video/v210" or "video/v210a", see nmos/mxl.h + const web::json::field_as_string_or mxl_video_type{ U("mxl_video_type"), nmos::media_types::video_v210.name }; + // channel_count: controls the number of channels in audio sources const web::json::field_as_integer_or channel_count{ U("channel_count"), 4 }; @@ -412,9 +416,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto seed_id = nmos::experimental::fields::seed_id(model.settings); const auto node_id = impl::make_id(seed_id, nmos::types::node); const auto device_id = impl::make_id(seed_id, nmos::types::device); - const nmos::id mxl_domain_id = model.settings.has_field(impl::fields::mxl_domain_id) - ? impl::fields::mxl_domain_id(model.settings) - : nmos::make_repeatable_id(seed_id, U("/x-nmos/mxl/domain")); + const auto mxl_domain_id = impl::fields::mxl_domain_id(model.settings); const auto how_many = impl::fields::how_many(model.settings); const auto sender_ports = impl::parse_ports(impl::fields::senders(model.settings)); const auto rtp_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_rtp_port)); @@ -433,6 +435,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto sampling = sdp::sampling{ impl::fields::color_sampling(model.settings) }; const auto bit_depth = impl::fields::component_depth(model.settings); const auto video_type = nmos::media_type{ impl::fields::video_type(model.settings) }; + const auto mxl_video_type = nmos::media_type{ impl::fields::mxl_video_type(model.settings) }; const auto channel_count = impl::fields::channel_count(model.settings); const auto smpte2022_7 = impl::fields::smpte2022_7(model.settings); @@ -960,16 +963,14 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr frame_rate, frame_width, frame_height, interlace_mode, colorspace, transfer_characteristic, sampling, bit_depth, - video_type, + mxl_video_type, model.settings ); } else if (impl::ports::mxl_audio == port) { flow = nmos::make_raw_audio_flow(flow_id, source_id, device_id, 48000, 32, model.settings); - flow.data[nmos::fields::media_type] = value::string(U("audio/float32")); - // add optional grain_rate - flow.data[nmos::fields::grain_rate] = nmos::make_rational(frame_rate); + flow.data[nmos::fields::media_type] = value::string(nmos::media_types::audio_float32.name); } else if (impl::ports::mxl_data == port) { @@ -1016,46 +1017,29 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr nmos::resource receiver; if (impl::ports::mxl_video == port) { - receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::video, { video_type }, model.settings); - if (nmos::media_types::video_raw == video_type) - { - const auto interlace_modes = nmos::interlace_modes::progressive != interlace_mode - ? std::vector{ nmos::interlace_modes::interlaced_bff.name, nmos::interlace_modes::interlaced_tff.name, nmos::interlace_modes::interlaced_psf.name } - : std::vector{ nmos::interlace_modes::progressive.name }; - receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ - value_of({ - { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ frame_rate }) }, - { nmos::caps::format::frame_width, nmos::make_caps_integer_constraint({ frame_width }) }, - { nmos::caps::format::frame_height, nmos::make_caps_integer_constraint({ frame_height }) }, - { nmos::caps::format::interlace_mode, nmos::make_caps_string_constraint(interlace_modes) }, - { nmos::caps::format::color_sampling, nmos::make_caps_string_constraint({ sampling.name }) } - }) - }); - } - else if (nmos::media_types::video_jxsv == video_type) - { - const auto max_format_bit_rate = nmos::get_video_jxsv_bit_rate(frame_rate, frame_width, frame_height, max_bits_per_pixel); - const auto max_transport_bit_rate = uint64_t(transport_bit_rate_factor * max_format_bit_rate / 1e3 + 0.5) * 1000; - - receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ - value_of({ - { nmos::caps::format::profile, nmos::make_caps_string_constraint({ profile.name }) }, - { nmos::caps::format::level, nmos::make_caps_string_constraint({ level.name }) }, - { nmos::caps::format::sublevel, nmos::make_caps_string_constraint({ nmos::sublevels::Sublev3bpp.name, nmos::sublevels::Sublev4bpp.name }) }, - { nmos::caps::format::bit_rate, nmos::make_caps_integer_constraint({}, nmos::no_minimum(), (int64_t)max_format_bit_rate) }, - { nmos::caps::transport::bit_rate, nmos::make_caps_integer_constraint({}, nmos::no_minimum(), (int64_t)max_transport_bit_rate) }, - { nmos::caps::transport::packet_transmission_mode, nmos::make_caps_string_constraint({ nmos::packet_transmission_modes::codestream.name }) } - }) - }); - } + receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::video, { mxl_video_type }, model.settings); + const auto interlace_modes = nmos::interlace_modes::progressive != interlace_mode + ? std::vector{ nmos::interlace_modes::interlaced_bff.name, nmos::interlace_modes::interlaced_tff.name, nmos::interlace_modes::interlaced_psf.name } + : std::vector{ nmos::interlace_modes::progressive.name }; + receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ + value_of({ + { nmos::caps::format::media_type, nmos::make_caps_string_constraint({ mxl_video_type.name }) }, + { nmos::caps::format::grain_rate, nmos::make_caps_rational_constraint({ frame_rate }) }, + { nmos::caps::format::frame_width, nmos::make_caps_integer_constraint({ frame_width }) }, + { nmos::caps::format::frame_height, nmos::make_caps_integer_constraint({ frame_height }) }, + { nmos::caps::format::interlace_mode, nmos::make_caps_string_constraint(interlace_modes) }, + { nmos::caps::format::color_sampling, nmos::make_caps_string_constraint({ sampling.name }) }, + { nmos::caps::format::component_depth, nmos::make_caps_integer_constraint({ bit_depth }) } + }) + }); receiver.data[nmos::fields::version] = receiver.data[nmos::fields::caps][nmos::fields::version] = value(nmos::make_version()); } else if (impl::ports::mxl_audio == port) { - receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::audio, { nmos::media_type{ U("audio/float32") } }, model.settings); + receiver = nmos::make_receiver(receiver_id, device_id, nmos::transports::mxl, {}, nmos::formats::audio, { nmos::media_types::audio_float32 }, model.settings); receiver.data[nmos::fields::caps][nmos::fields::constraint_sets] = value_of({ value_of({ - { nmos::caps::format::media_type, nmos::make_caps_string_constraint({ U("audio/float32") }) }, + { nmos::caps::format::media_type, nmos::make_caps_string_constraint({ nmos::media_types::audio_float32.name }) }, { nmos::caps::format::channel_count, nmos::make_caps_integer_constraint({}, 1, channel_count) }, { nmos::caps::format::sample_rate, nmos::make_caps_rational_constraint({ { 48000, 1 } }) }, { nmos::caps::format::sample_depth, nmos::make_caps_integer_constraint({ 32 }) } diff --git a/Development/nmos/json_fields.h b/Development/nmos/json_fields.h index e195e61ae..118dc0380 100644 --- a/Development/nmos/json_fields.h +++ b/Development/nmos/json_fields.h @@ -174,7 +174,7 @@ namespace nmos const web::json::field_as_value_or connection_status_broker_topic{ U("connection_status_broker_topic"), {} }; // string or null // for urn:x-nmos:transport:mxl (see AMWA BCP-007-03 NMOS With MXL) const web::json::field_as_value_or mxl_domain_id{ U("mxl_domain_id"), {} }; // UUID string, auto, or null - const web::json::field_as_value_or mxl_flow_id{ U("mxl_flow_id"), {} }; // senders: UUID, auto, or null; receivers: UUID or null (BCP-007-03) + const web::json::field_as_value_or mxl_flow_id{ U("mxl_flow_id"), {} }; // senders: UUID, auto, or null; receivers: UUID or null // IS-07 Event & Tally diff --git a/Development/nmos/mxl.h b/Development/nmos/mxl.h new file mode 100644 index 000000000..22625c69f --- /dev/null +++ b/Development/nmos/mxl.h @@ -0,0 +1,25 @@ +#ifndef NMOS_MXL_H +#define NMOS_MXL_H + +#include "nmos/media_type.h" + +namespace nmos +{ + namespace media_types + { + // MXL video media types + + const media_type video_v210{ U("video/v210") }; + const media_type video_v210a{ U("video/v210a") }; + + // MXL audio media types + + const media_type audio_float32{ U("audio/float32") }; + + // MXL data media types + + //const media_type video_smpte291{ U("video/smpte291") }; + } +} + +#endif \ No newline at end of file From d499aaccc06665b4f98a4bc0000d25aa85c9c4d4 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 19 May 2026 14:38:27 +0100 Subject: [PATCH 22/33] Fix Windows CI host IP discovery on windows-2022 runners. Get-NetIPConfiguration fails when multiple adapters map to one interface; use the default route and Get-NetIPAddress instead, and only disable vEthernet (nat) when present. --- .github/workflows/build-test.yml | 24 +++++++++++++++--------- .github/workflows/src/build-setup.yml | 24 +++++++++++++++--------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index b3efeac6a..09950a79f 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -122,16 +122,22 @@ jobs: run: | # set compiler to cl.exe to avoid building with gcc. echo "CMAKE_COMPILER_ARGS=-DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe" >> $env:GITHUB_ENV - # disable unused network interface - netsh interface set interface name="vEthernet (nat)" admin=DISABLED - # get host IP address + # disable unused network interface (may not exist on windows-2022 runners) + $nat = Get-NetAdapter -Name 'vEthernet (nat)' -ErrorAction SilentlyContinue + if ($nat) { + netsh interface set interface name="vEthernet (nat)" admin=DISABLED + } + # get host IP address (avoid Get-NetIPConfiguration which fails when multiple adapters map to one interface) + $ifIndex = ( + Get-NetRoute -DestinationPrefix '0.0.0.0/0' | + Sort-Object RouteMetric | + Select-Object -First 1 + ).InterfaceIndex $env:hostip = ( - Get-NetIPConfiguration | - Where-Object { - $_.IPv4DefaultGateway -ne $null -and - $_.NetAdapter.Status -ne "Disconnected" - } - ).IPv4Address.IPAddress + Get-NetIPAddress -InterfaceIndex $ifIndex -AddressFamily IPv4 | + Where-Object { $_.IPAddress -notlike '169.254.*' } | + Select-Object -First 1 + ).IPAddress echo "HOST_IP_ADDRESS=$env:hostip" >> $env:GITHUB_ENV ipconfig # add the CRL Distribution Point to hosts so that it's discoverable when running the AMWA test suite in mDNS mode diff --git a/.github/workflows/src/build-setup.yml b/.github/workflows/src/build-setup.yml index 2fe1e7f63..34d27a658 100644 --- a/.github/workflows/src/build-setup.yml +++ b/.github/workflows/src/build-setup.yml @@ -24,16 +24,22 @@ run: | # set compiler to cl.exe to avoid building with gcc. echo "CMAKE_COMPILER_ARGS=-DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe" >> $env:GITHUB_ENV - # disable unused network interface - netsh interface set interface name="vEthernet (nat)" admin=DISABLED - # get host IP address + # disable unused network interface (may not exist on windows-2022 runners) + $nat = Get-NetAdapter -Name 'vEthernet (nat)' -ErrorAction SilentlyContinue + if ($nat) { + netsh interface set interface name="vEthernet (nat)" admin=DISABLED + } + # get host IP address (avoid Get-NetIPConfiguration which fails when multiple adapters map to one interface) + $ifIndex = ( + Get-NetRoute -DestinationPrefix '0.0.0.0/0' | + Sort-Object RouteMetric | + Select-Object -First 1 + ).InterfaceIndex $env:hostip = ( - Get-NetIPConfiguration | - Where-Object { - $_.IPv4DefaultGateway -ne $null -and - $_.NetAdapter.Status -ne "Disconnected" - } - ).IPv4Address.IPAddress + Get-NetIPAddress -InterfaceIndex $ifIndex -AddressFamily IPv4 | + Where-Object { $_.IPAddress -notlike '169.254.*' } | + Select-Object -First 1 + ).IPAddress echo "HOST_IP_ADDRESS=$env:hostip" >> $env:GITHUB_ENV ipconfig # add the CRL Distribution Point to hosts so that it's discoverable when running the AMWA test suite in mDNS mode From 5410b2f461f5bd1f4c30e9d1c1b028ba831427d7 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 19 May 2026 15:52:48 +0100 Subject: [PATCH 23/33] Ignore IS-04-01 test_19 due to conflict with MXL sender and receivers --- Sandbox/run_nmos_testing.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sandbox/run_nmos_testing.sh b/Sandbox/run_nmos_testing.sh index 12fa14b62..e8624cf15 100755 --- a/Sandbox/run_nmos_testing.sh +++ b/Sandbox/run_nmos_testing.sh @@ -25,7 +25,7 @@ cd - expected_disabled_BCP_003_01=0 # test_12 -expected_disabled_IS_04_01=1 +expected_disabled_IS_04_01=2 expected_disabled_IS_04_03=0 expected_disabled_IS_05_01=0 expected_disabled_IS_05_02=0 @@ -202,7 +202,7 @@ if $secure; then do_run_test BCP-003-01 $expected_disabled_BCP_003_01 --host "${host}" --port 1080 --version v1.0 fi -do_run_test IS-04-01 $expected_disabled_IS_04_01 --host "${host}" --port 1080 --version v1.3 +do_run_test IS-04-01 $expected_disabled_IS_04_01 --host "${host}" --port 1080 --version v1.3 --ignore test_19 do_run_test IS-04-03 $expected_disabled_IS_04_03 --host "${host}" --port 1080 --version v1.3 From 6f3496ed399fd2cddbdb4d4d5aba0730d76d5b17 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Tue, 19 May 2026 17:08:24 +0100 Subject: [PATCH 24/33] Refactor audio flow creation in node implementation to use make_coded_audio_flow for MXL audio ports --- Development/nmos-cpp-node/node_implementation.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 4d24058bc..4caa8c451 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -969,8 +969,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } else if (impl::ports::mxl_audio == port) { - flow = nmos::make_raw_audio_flow(flow_id, source_id, device_id, 48000, 32, model.settings); - flow.data[nmos::fields::media_type] = value::string(nmos::media_types::audio_float32.name); + flow = nmos::make_coded_audio_flow(flow_id, source_id, device_id, 48000, nmos::media_types::audio_float32, model.settings); } else if (impl::ports::mxl_data == port) { From dc68fa1118566983d8ccf061585f47c5c434c53f Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 20 May 2026 15:38:25 +0100 Subject: [PATCH 25/33] Add auto resolution for transport parameters in node implementation initialization --- Development/nmos-cpp-node/node_implementation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 4caa8c451..bc6f7c18c 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -999,6 +999,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr }); } + resolve_auto(sender, connection_sender, connection_sender.data[nmos::fields::endpoint_active][nmos::fields::transport_params]); + if (!insert_resource_after(delay_millis, model.node_resources, std::move(source), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.node_resources, std::move(flow), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.node_resources, std::move(sender), gate)) throw node_implementation_init_exception(); @@ -1061,6 +1063,8 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr auto connection_receiver = nmos::make_connection_mxl_receiver(receiver_id, mxl_domain_id); + resolve_auto(receiver, connection_receiver, connection_receiver.data[nmos::fields::endpoint_active][nmos::fields::transport_params]); + if (!insert_resource_after(delay_millis, model.node_resources, std::move(receiver), gate)) throw node_implementation_init_exception(); if (!insert_resource_after(delay_millis, model.connection_resources, std::move(connection_receiver), gate)) throw node_implementation_init_exception(); } From 253b8b6f9fad3dbbdb43692f5ce0f04c1f812ad7 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Wed, 20 May 2026 15:38:44 +0100 Subject: [PATCH 26/33] Add support for null and auto values in MXL staged transport parameters schema --- Development/nmos/connection_api.cpp | 52 ++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index aa9994a84..17cf148d3 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -149,6 +149,51 @@ namespace nmos }); } + // BCP-007-03: MXL staged transport parameters accept null (unconfigured) as well as constraint values (and auto where applicable) + // See https://specs.amwa.tv/bcp-007-03/branches/publish-auto-null/docs/NMOS-With-MXL.html + // and sender_transport_params_mxl.json / receiver_transport_params_mxl.json + web::json::value make_mxl_staged_param_schema(const web::json::value& schema, bool auto_value) + { + using web::json::value; + using web::json::value_of; + + const bool keep_order = true; + + static const utility::string_t mxl_uuid_pattern{ + U("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$") + }; + + auto any_of = value::array(); + + if (auto_value) + { + web::json::push_back(any_of, value_of({ + { U("type"), U("string") }, + { U("pattern"), U("^auto$") } + }, keep_order)); + } + + if (!schema.as_object().empty()) + { + web::json::push_back(any_of, schema); + } + else + { + web::json::push_back(any_of, value_of({ + { U("type"), U("string") }, + { U("pattern"), mxl_uuid_pattern } + }, keep_order)); + } + + web::json::push_back(any_of, value_of({ + { U("type"), U("null") } + }, keep_order)); + + return value_of({ + { U("anyOf"), any_of } + }, keep_order); + } + static const std::map>& rtp_auto_constraints() { // These are the constraints that support "auto" in /staged; cf. resolve_rtp_auto @@ -304,7 +349,12 @@ namespace nmos for (const auto& constraint : leg.as_object()) { - if (type_auto_constraints.end() != type_auto_constraints.find(constraint.first)) + if (nmos::transports::mxl == transport_base) + { + const bool auto_value = type_auto_constraints.end() != type_auto_constraints.find(constraint.first); + properties[constraint.first] = make_mxl_staged_param_schema(constraint.second, auto_value); + } + else if (type_auto_constraints.end() != type_auto_constraints.find(constraint.first)) { properties[constraint.first] = make_auto_schema(constraint.second); } From 05f0f2fcb33407f4b9bf1ef6e82f1149710830aa Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 21 May 2026 15:37:53 +0100 Subject: [PATCH 27/33] Remove transport_file validation for MXL receivers in connection API and update mxl_video_type to use a string literal. Implement error handling for transport_file usage in node implementation for MXL receivers. --- Development/nmos-cpp-node/node_implementation.cpp | 9 ++++++++- Development/nmos/connection_api.cpp | 12 ------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index bc6f7c18c..4639bbcec 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -121,7 +121,7 @@ namespace impl const web::json::field_as_string_or video_type{ U("video_type"), U("video/raw") }; // mxl_video_type: media type of MXL video flows and receivers, e.g. "video/v210" or "video/v210a", see nmos/mxl.h - const web::json::field_as_string_or mxl_video_type{ U("mxl_video_type"), nmos::media_types::video_v210.name }; + const web::json::field_as_string_or mxl_video_type{ U("mxl_video_type"), U("video/v210") }; // channel_count: controls the number of channels in audio sources const web::json::field_as_integer_or channel_count{ U("channel_count"), 4 }; @@ -2019,6 +2019,13 @@ nmos::transport_file_parser make_node_implementation_transport_file_parser() // (if this callback is specified, an 'empty' std::function is not allowed) return [](const nmos::resource& receiver, const nmos::resource& connection_receiver, const utility::string_t& transport_file_type, const utility::string_t& transport_file_data, slog::base_gate& gate) { + // BCP-007-03: MXL receivers do not use transport_file (non-null data is rejected here when parsed) + const nmos::transport transport_subclassification(nmos::fields::transport(receiver.data)); + if (nmos::transports::mxl == nmos::transport_base(transport_subclassification)) + { + throw std::runtime_error("MXL does not use a transport_file"); + } + const auto validate_sdp_parameters = [](const web::json::value& receiver, const nmos::sdp_parameters& sdp_params) { if (nmos::media_types::video_jxsv == nmos::get_media_type(sdp_params)) diff --git a/Development/nmos/connection_api.cpp b/Development/nmos/connection_api.cpp index 17cf148d3..48654c982 100644 --- a/Development/nmos/connection_api.cpp +++ b/Development/nmos/connection_api.cpp @@ -571,18 +571,6 @@ namespace nmos const nmos::transport transport_subclassification(nmos::fields::transport(matching_resource->data)); - // BCP-007-03: PATCH /staged for MXL receivers must not contain transport_file. - // See https://specs.amwa.tv/bcp-007-03/branches/publish-auto-null/docs/NMOS-With-MXL.html - if (nmos::types::receiver == id_type.second && nmos::transports::mxl == nmos::transport_base(transport_subclassification)) - { - if (patch.has_field(nmos::fields::transport_file.key)) - { - slog::log(gate, SLOG_FLF) << "Rejecting PATCH for MXL receiver with transport_file"; - - return details::make_connection_resource_patch_error_response(status_codes::BadRequest, U("transport_file must not be used with MXL receivers")); - } - } - // Merge this patch request into a *copy* of the current staged endpoint // so that the merged parameters can be validated against the constraints // before the current values are overwritten. From 463fade0e15fe547222a006f32f895fb6a326443 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 21 May 2026 16:07:57 +0100 Subject: [PATCH 28/33] Refactor audio flow creation by adding an overload for make_raw_audio_flow to accept media_type, updating existing calls in node implementation to use this new overload. --- Development/nmos-cpp-node/node_implementation.cpp | 2 +- Development/nmos/node_resources.cpp | 9 ++++++++- Development/nmos/node_resources.h | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 4639bbcec..0c2e00b48 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -969,7 +969,7 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr } else if (impl::ports::mxl_audio == port) { - flow = nmos::make_coded_audio_flow(flow_id, source_id, device_id, 48000, nmos::media_types::audio_float32, model.settings); + flow = nmos::make_raw_audio_flow(flow_id, source_id, device_id, 48000, nmos::media_types::audio_float32, 32, model.settings); } else if (impl::ports::mxl_data == port) { diff --git a/Development/nmos/node_resources.cpp b/Development/nmos/node_resources.cpp index 6e8a1cba1..846fe564b 100644 --- a/Development/nmos/node_resources.cpp +++ b/Development/nmos/node_resources.cpp @@ -397,13 +397,20 @@ namespace nmos // See https://specs.amwa.tv/is-04/releases/v1.2.0/APIs/schemas/with-refs/flow_audio_raw.html nmos::resource make_raw_audio_flow(const nmos::id& id, const nmos::id& source_id, const nmos::id& device_id, const nmos::rational& sample_rate, unsigned int bit_depth, const nmos::settings& settings) + { + return make_raw_audio_flow(id, source_id, device_id, sample_rate, nmos::media_types::audio_L(bit_depth), bit_depth, settings); + } + + // See https://specs.amwa.tv/is-04/releases/v1.2.0/APIs/schemas/with-refs/flow_audio_raw.html + // (media_type must *not* be nmos::media_types::audio_L(bit_depth); cf. the bit_depth overload) + nmos::resource make_raw_audio_flow(const nmos::id& id, const nmos::id& source_id, const nmos::id& device_id, const nmos::rational& sample_rate, const nmos::media_type& media_type, unsigned int bit_depth, const nmos::settings& settings) { using web::json::value; auto resource = make_audio_flow(id, source_id, device_id, sample_rate, settings); auto& data = resource.data; - data[U("media_type")] = value::string(nmos::media_types::audio_L(bit_depth).name); + data[U("media_type")] = value::string(media_type.name); data[U("bit_depth")] = bit_depth; return resource; diff --git a/Development/nmos/node_resources.h b/Development/nmos/node_resources.h index a1b9f6555..6044a4f62 100644 --- a/Development/nmos/node_resources.h +++ b/Development/nmos/node_resources.h @@ -84,6 +84,8 @@ namespace nmos // See https://specs.amwa.tv/is-04/releases/v1.2.0/APIs/schemas/with-refs/flow_audio_raw.html nmos::resource make_raw_audio_flow(const nmos::id& id, const nmos::id& source_id, const nmos::id& device_id, const nmos::rational& sample_rate, unsigned int bit_depth, const nmos::settings& settings); + // (media_type must *not* be nmos::media_types::audio_L(bit_depth); cf. the bit_depth overload) + nmos::resource make_raw_audio_flow(const nmos::id& id, const nmos::id& source_id, const nmos::id& device_id, const nmos::rational& sample_rate, const nmos::media_type& media_type, unsigned int bit_depth, const nmos::settings& settings); nmos::resource make_raw_audio_flow(const nmos::id& id, const nmos::id& source_id, const nmos::id& device_id, const nmos::settings& settings); // See https://specs.amwa.tv/is-04/releases/v1.2.0/APIs/schemas/with-refs/flow_audio_coded.html From e3ad404beba420d1668a50aa8ac31bb664737e9d Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 21 May 2026 16:47:35 +0100 Subject: [PATCH 29/33] Add MXL transport parameters constraints schema and update constraints-schema to reference it --- .../APIs/schemas/constraints-schema-mxl.json | 23 +++++++++++++++++++ .../APIs/schemas/constraints-schema.json | 6 +++++ 2 files changed, 29 insertions(+) create mode 100644 Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-mxl.json diff --git a/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-mxl.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-mxl.json new file mode 100644 index 000000000..fffd12737 --- /dev/null +++ b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema-mxl.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "description": "Used to express the dynamic constraints on MXL transport parameters. These constraints may be set and changed at run time. Every transport parameter must have an entry, even if it is only an empty object. See BCP-007-03.", + "required": [ + "mxl_domain_id", + "mxl_flow_id" + ], + "additionalProperties": false, + "patternProperties": { + "^ext_[a-zA-Z0-9_]+$": { + "$ref": "constraint-schema.json#/definitions/constraint" + } + }, + "properties": { + "mxl_domain_id": { + "$ref": "constraint-schema.json#/definitions/constraint" + }, + "mxl_flow_id": { + "$ref": "constraint-schema.json#/definitions/constraint" + } + } +} diff --git a/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema.json b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema.json index d43a11db4..ab3969c97 100644 --- a/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema.json +++ b/Development/third_party/is-05/v1.2-dev/APIs/schemas/constraints-schema.json @@ -19,6 +19,12 @@ "items": { "$ref": "constraints-schema-mqtt.json" } + }, + { + "type": "array", + "items": { + "$ref": "constraints-schema-mxl.json" + } } ] } From a64e1e1f8fe288962100df04ebd4576d033554c6 Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Thu, 21 May 2026 22:07:24 +0100 Subject: [PATCH 30/33] Add MXL domain ID retrieval and auto resolution for transport parameters in node implementation --- .../nmos-cpp-node/node_implementation.cpp | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 0c2e00b48..2622f064a 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -138,6 +138,11 @@ namespace impl nmos::interlace_mode get_interlace_mode(const nmos::settings& settings); + // Effective MXL domain id from settings, or a repeatable default when omitted (used by resolve_auto) + nmos::id get_mxl_domain_id(const nmos::settings& settings); + + web::json::value resolve_mxl_auto_value(const web::json::value& constraint, const nmos::id& fallback); + // the different kinds of 'port' (standing for the format/media type/event type) implemented by the example node // each 'port' of the example node has a source, flow, sender and/or compatible receiver DEFINE_STRING_ENUM(port) @@ -2073,10 +2078,11 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c const auto ws_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, ws_receiver_ports, how_many); const auto mxl_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_mxl_port)); const auto mxl_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, mxl_receiver_ports, how_many); + const auto mxl_domain_id = impl::get_mxl_domain_id(settings); // although which properties may need to be defaulted depends on the resource type, // the default value will almost always be different for each resource - return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids, mxl_sender_ids, mxl_receiver_ids](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) + return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids, mxl_sender_ids, mxl_receiver_ids, mxl_domain_id](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) { const std::pair id_type{ connection_resource.id, connection_resource.type }; // this code relies on the specific constraints added by node_implementation_thread @@ -2113,13 +2119,22 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c } else if (mxl_sender_ids.end() != boost::range::find(mxl_sender_ids, id_type.first)) { - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_domain_id))); }); - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_flow_id))); }); + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] + { + return impl::resolve_mxl_auto_value(constraints.at(0).at(nmos::fields::mxl_domain_id), mxl_domain_id); + }); + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] + { + return impl::resolve_mxl_auto_value(constraints.at(0).at(nmos::fields::mxl_flow_id), nmos::fields::flow_id(resource.data).as_string()); + }); } else if (mxl_receiver_ids.end() != boost::range::find(mxl_receiver_ids, id_type.first)) { // BCP-007-03: mxl_flow_id does not use "auto" on receivers (UUID or null only). - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_domain_id))); }); + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] + { + return impl::resolve_mxl_auto_value(constraints.at(0).at(nmos::fields::mxl_domain_id), mxl_domain_id); + }); } }; } @@ -2427,6 +2442,28 @@ nmos::create_device_model_object_handler make_create_device_model_object_handler namespace impl { + nmos::id get_mxl_domain_id(const nmos::settings& settings) + { + const auto seed_id = nmos::experimental::fields::seed_id(settings); + return fields::mxl_domain_id(settings).empty() + ? nmos::make_repeatable_id(seed_id, U("/x-nmos/mxl/domain")) + : fields::mxl_domain_id(settings); + } + + web::json::value resolve_mxl_auto_value(const web::json::value& constraint, const nmos::id& fallback) + { + using web::json::value; + if (constraint.has_field(nmos::fields::constraint_enum)) + { + const auto& en = nmos::fields::constraint_enum(constraint); + if (en.is_array() && 0 != en.as_array().size()) + { + return web::json::front(en); + } + } + return value::string(fallback); + } + nmos::interlace_mode get_interlace_mode(const nmos::settings& settings) { if (settings.has_field(impl::fields::interlace_mode)) From 09a8845944f5f2f88bb18ddb305e1fb61718303f Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 22 May 2026 14:12:24 +0100 Subject: [PATCH 31/33] Refactor MXL domain ID handling by removing redundant function and implementing UUID generation directly in node initialization. Update auto resolution logic for transport parameters to streamline MXL sender and receiver processing. --- .../nmos-cpp-node/node_implementation.cpp | 58 ++++--------------- 1 file changed, 12 insertions(+), 46 deletions(-) diff --git a/Development/nmos-cpp-node/node_implementation.cpp b/Development/nmos-cpp-node/node_implementation.cpp index 2622f064a..deaf527bc 100644 --- a/Development/nmos-cpp-node/node_implementation.cpp +++ b/Development/nmos-cpp-node/node_implementation.cpp @@ -138,11 +138,6 @@ namespace impl nmos::interlace_mode get_interlace_mode(const nmos::settings& settings); - // Effective MXL domain id from settings, or a repeatable default when omitted (used by resolve_auto) - nmos::id get_mxl_domain_id(const nmos::settings& settings); - - web::json::value resolve_mxl_auto_value(const web::json::value& constraint, const nmos::id& fallback); - // the different kinds of 'port' (standing for the format/media type/event type) implemented by the example node // each 'port' of the example node has a source, flow, sender and/or compatible receiver DEFINE_STRING_ENUM(port) @@ -421,7 +416,10 @@ void node_implementation_init(nmos::node_model& model, nmos::experimental::contr const auto seed_id = nmos::experimental::fields::seed_id(model.settings); const auto node_id = impl::make_id(seed_id, nmos::types::node); const auto device_id = impl::make_id(seed_id, nmos::types::device); - const auto mxl_domain_id = impl::fields::mxl_domain_id(model.settings); + // If mxl_domain_id is not set, generate a random UUID for the MXL domain + const auto mxl_domain_id = impl::fields::mxl_domain_id(model.settings).empty() + ? nmos::make_repeatable_id(seed_id, U("/x-nmos/mxl/domain")) + : impl::fields::mxl_domain_id(model.settings); const auto how_many = impl::fields::how_many(model.settings); const auto sender_ports = impl::parse_ports(impl::fields::senders(model.settings)); const auto rtp_sender_ports = boost::copy_range>(sender_ports | boost::adaptors::filtered(impl::is_rtp_port)); @@ -2078,11 +2076,10 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c const auto ws_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, ws_receiver_ports, how_many); const auto mxl_receiver_ports = boost::copy_range>(receiver_ports | boost::adaptors::filtered(impl::is_mxl_port)); const auto mxl_receiver_ids = impl::make_ids(seed_id, nmos::types::receiver, mxl_receiver_ports, how_many); - const auto mxl_domain_id = impl::get_mxl_domain_id(settings); // although which properties may need to be defaulted depends on the resource type, // the default value will almost always be different for each resource - return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids, mxl_sender_ids, mxl_receiver_ids, mxl_domain_id](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) + return [rtp_sender_ids, rtp_receiver_ids, ws_sender_ids, ws_sender_uri, ws_receiver_ids, mxl_sender_ids, mxl_receiver_ids](const nmos::resource& resource, const nmos::resource& connection_resource, value& transport_params) { const std::pair id_type{ connection_resource.id, connection_resource.type }; // this code relies on the specific constraints added by node_implementation_thread @@ -2117,24 +2114,15 @@ nmos::connection_resource_auto_resolver make_node_implementation_auto_resolver(c { nmos::details::resolve_auto(transport_params[0], nmos::fields::connection_authorization, [&] { return value::boolean(false); }); } - else if (mxl_sender_ids.end() != boost::range::find(mxl_sender_ids, id_type.first)) + else if (mxl_sender_ids.end() != boost::range::find(mxl_sender_ids, id_type.first) + || mxl_receiver_ids.end() != boost::range::find(mxl_receiver_ids, id_type.first)) { - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] - { - return impl::resolve_mxl_auto_value(constraints.at(0).at(nmos::fields::mxl_domain_id), mxl_domain_id); - }); - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_domain_id))); }); + if (nmos::types::sender == id_type.second) { - return impl::resolve_mxl_auto_value(constraints.at(0).at(nmos::fields::mxl_flow_id), nmos::fields::flow_id(resource.data).as_string()); - }); - } - else if (mxl_receiver_ids.end() != boost::range::find(mxl_receiver_ids, id_type.first)) - { - // BCP-007-03: mxl_flow_id does not use "auto" on receivers (UUID or null only). - nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_domain_id, [&] - { - return impl::resolve_mxl_auto_value(constraints.at(0).at(nmos::fields::mxl_domain_id), mxl_domain_id); - }); + // BCP-007-03: mxl_flow_id does not use "auto" on receivers (UUID or null only). + nmos::details::resolve_auto(transport_params[0], nmos::fields::mxl_flow_id, [&] { return web::json::front(nmos::fields::constraint_enum(constraints.at(0).at(nmos::fields::mxl_flow_id))); }); + } } }; } @@ -2442,28 +2430,6 @@ nmos::create_device_model_object_handler make_create_device_model_object_handler namespace impl { - nmos::id get_mxl_domain_id(const nmos::settings& settings) - { - const auto seed_id = nmos::experimental::fields::seed_id(settings); - return fields::mxl_domain_id(settings).empty() - ? nmos::make_repeatable_id(seed_id, U("/x-nmos/mxl/domain")) - : fields::mxl_domain_id(settings); - } - - web::json::value resolve_mxl_auto_value(const web::json::value& constraint, const nmos::id& fallback) - { - using web::json::value; - if (constraint.has_field(nmos::fields::constraint_enum)) - { - const auto& en = nmos::fields::constraint_enum(constraint); - if (en.is_array() && 0 != en.as_array().size()) - { - return web::json::front(en); - } - } - return value::string(fallback); - } - nmos::interlace_mode get_interlace_mode(const nmos::settings& settings) { if (settings.has_field(impl::fields::interlace_mode)) From 7935a22393bc7f9e2bc187c310a4c5d14bac2e5d Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 22 May 2026 19:06:26 +0100 Subject: [PATCH 32/33] Test against NMOS Testing tool draft BCP-007-03 test suite. Update README.md to include BCP-007-03 references --- .github/workflows/build-test.yml | 4 ++-- .github/workflows/src/amwa-test.yml | 4 ++-- README.md | 5 +++++ Sandbox/run_nmos_testing.sh | 8 +++++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 09950a79f..25e1d5eaf 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -374,8 +374,8 @@ jobs: set -x root_dir=`pwd` - # Install AMWA NMOS Testing Tool - git clone https://github.com/AMWA-TV/nmos-testing.git + # Install NMOS Testing Tool (fork with BCP-007-03 suite) + git clone --branch bcp-007-03 --depth 1 https://github.com/jonathan-r-thorpe/nmos-testing.git cd nmos-testing # Configure the Testing Tool so all APIs are tested with TLS and authorization diff --git a/.github/workflows/src/amwa-test.yml b/.github/workflows/src/amwa-test.yml index b0f2151f8..0c482ce7d 100644 --- a/.github/workflows/src/amwa-test.yml +++ b/.github/workflows/src/amwa-test.yml @@ -15,8 +15,8 @@ set -x root_dir=`pwd` - # Install AMWA NMOS Testing Tool - git clone https://github.com/AMWA-TV/nmos-testing.git + # Install NMOS Testing Tool (fork with BCP-007-03 suite) + git clone --branch bcp-007-03 --depth 1 https://github.com/jonathan-r-thorpe/nmos-testing.git cd nmos-testing # Configure the Testing Tool so all APIs are tested with TLS and authorization diff --git a/README.md b/README.md index 25967f0e8..a09ba47ee 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ This repository contains an implementation of the [AMWA Networked Media Open Spe - [AMWA BCP-003-02 Authorization in NMOS Systems](https://specs.amwa.tv/bcp-003-02/) - [AMWA BCP-004-01 NMOS Receiver Capabilities](https://specs.amwa.tv/bcp-004-01/) - [AMWA BCP-006-01 NMOS With JPEG XS](https://specs.amwa.tv/bcp-006-01/) +- [AMWA BCP-007-03 NMOS With MXL](https://specs.amwa.tv/bcp-007-03/) - [AMWA BCP-008-01 NMOS Receiver Status](https://specs.amwa.tv/bcp-008-01/) - [AMWA BCP-008-02 NMOS Sender Status](https://specs.amwa.tv/bcp-008-02/) - [AMWA MS-05-01 NMOS Control Architecture](https://specs.amwa.tv/ms-05-01/) @@ -82,6 +83,7 @@ The [AMWA NMOS API Testing Tool](https://github.com/AMWA-TV/nmos-testing) is aut **Test Suite/Status:** [![BCP-003-01][BCP-003-01-badge]][BCP-003-01-sheet] [![BCP-006-01-01][BCP-006-01-01-badge]][BCP-006-01-01-sheet] +[![BCP-007-03-01][BCP-007-03-01-badge]][BCP-007-03-01-sheet] [![BCP-008-01-01][BCP-008-01-01-badge]][BCP-008-01-01-sheet] [![BCP-008-02-01][BCP-008-02-01-badge]][BCP-008-02-01-sheet] [![IS-04-01][IS-04-01-badge]][IS-04-01-sheet] @@ -100,6 +102,7 @@ The [AMWA NMOS API Testing Tool](https://github.com/AMWA-TV/nmos-testing) is aut [BCP-003-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/BCP-003-01.svg [BCP-006-01-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/BCP-006-01-01.svg +[BCP-007-03-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/BCP-007-03-01.svg [BCP-008-01-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/BCP-008-01-01.svg [BCP-008-02-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/BCP-008-02-01.svg [IS-04-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/IS-04-01.svg @@ -117,6 +120,7 @@ The [AMWA NMOS API Testing Tool](https://github.com/AMWA-TV/nmos-testing) is aut [IS-14-01-badge]: https://raw.githubusercontent.com/sony/nmos-cpp/badges/IS-14-01.svg [BCP-003-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit#gid=468090822 [BCP-006-01-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit?gid=1835994800 +[BCP-007-03-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit?gid=926264123 [BCP-008-01-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit?gid=1290589116 [BCP-008-02-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit?gid=582384563 [IS-04-01-sheet]: https://docs.google.com/spreadsheets/d/1UgZoI0lGCMDn9-zssccf2Azil3WN6jogroMT8Wh6H64/edit#gid=0 @@ -139,6 +143,7 @@ The implementation is designed to be extended. Development is ongoing, following Recent activity on the project (newest first): +- Added support for BCP-007-03 NMOS With MXL - Added support for IS-14 NMOS Device Configuration - Added support for BCP-008-01 Receiver Status Monitoring - Added support for BCP-008-02 Sender Status Monitoring diff --git a/Sandbox/run_nmos_testing.sh b/Sandbox/run_nmos_testing.sh index e8624cf15..15d365907 100755 --- a/Sandbox/run_nmos_testing.sh +++ b/Sandbox/run_nmos_testing.sh @@ -39,6 +39,7 @@ expected_disabled_IS_09_01=0 expected_disabled_IS_12_01=0 expected_disabled_IS_14_01=1 expected_disabled_BCP_006_01_01=0 +expected_disabled_BCP_007_03_01=0 expected_disabled_BCP_008_01_01=0 expected_disabled_BCP_008_02_01=0 @@ -70,7 +71,9 @@ registry_params=",\ node_params=",\ \"label\":\"nmos-cpp-node\",\ \"video_type\": \"video/jxsv\",\ - \"simulate_status_monitor_activity\":false\ + \"simulate_status_monitor_activity\":false,\ + \"senders\":[\"v\",\"a\",\"d\",\"m\",\"t\",\"b\",\"s\",\"c\",\"xv\",\"xa\",\"xd\"],\ + \"receivers\":[\"v\",\"a\",\"d\",\"m\",\"t\",\"b\",\"s\",\"c\",\"xv\",\"xa\",\"xd\"]\ " if [[ "${config_secure}" == "True" ]]; then @@ -152,6 +155,7 @@ else (( expected_disabled_IS_09_01+=7 )) (( expected_disabled_IS_14_01+=7 )) (( expected_disabled_BCP_006_01_01+=7 )) + (( expected_disabled_BCP_007_03_01+=14 )) fi "${node_command}" "{\"how_many\":6,\"http_port\":1080 ${common_params} ${node_params}}" > ${results_dir}/nodeoutput 2>&1 & @@ -226,6 +230,8 @@ do_run_test IS-14-01 $expected_disabled_IS_14_01 --host "${host}" "${host}" null do_run_test BCP-006-01-01 $expected_disabled_BCP_006_01_01 --host "${host}" --port 1080 --version v1.3 +do_run_test BCP-007-03-01 $expected_disabled_BCP_007_03_01 --host "${host}" "${host}" --port 1080 1080 --version v1.3 v1.2 + do_run_test BCP-008-01-01 $expected_disabled_BCP_008_01_01 --host "${host}" "${host}" "${host}" null --port 1080 1080 1082 0 --version v1.3 v1.1 v1.0 v1.0 --urlpath null null x-nmos/ncp/v1.0 null do_run_test BCP-008-02-01 $expected_disabled_BCP_008_02_01 --host "${host}" "${host}" "${host}" null --port 1080 1080 1082 0 --version v1.3 v1.1 v1.0 v1.0 --urlpath null null x-nmos/ncp/v1.0 null From 9e425062875162b50629eba651f6c5fbc38df27e Mon Sep 17 00:00:00 2001 From: jonathan-r-thorpe Date: Fri, 22 May 2026 21:12:46 +0100 Subject: [PATCH 33/33] Remove test_19 ignore from IS-04-01 and update IS-05-02 version requirement to v1.2 for compatibility with Connection API. --- Sandbox/run_nmos_testing.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sandbox/run_nmos_testing.sh b/Sandbox/run_nmos_testing.sh index 15d365907..4653d59b0 100755 --- a/Sandbox/run_nmos_testing.sh +++ b/Sandbox/run_nmos_testing.sh @@ -206,13 +206,14 @@ if $secure; then do_run_test BCP-003-01 $expected_disabled_BCP_003_01 --host "${host}" --port 1080 --version v1.0 fi -do_run_test IS-04-01 $expected_disabled_IS_04_01 --host "${host}" --port 1080 --version v1.3 --ignore test_19 +do_run_test IS-04-01 $expected_disabled_IS_04_01 --host "${host}" --port 1080 --version v1.3 do_run_test IS-04-03 $expected_disabled_IS_04_03 --host "${host}" --port 1080 --version v1.3 do_run_test IS-05-01 $expected_disabled_IS_05_01 --host "${host}" --port 1080 --version v1.1 -do_run_test IS-05-02 $expected_disabled_IS_05_02 --host "${host}" "${host}" --port 1080 1080 --version v1.3 v1.1 +# Connection API v1.2+ is required so MXL senders/receivers appear in IS-05 (see IS-05 upgrade path) +do_run_test IS-05-02 $expected_disabled_IS_05_02 --host "${host}" "${host}" --port 1080 1080 --version v1.3 v1.2 do_run_test IS-07-01 $expected_disabled_IS_07_01 --host "${host}" --port 1080 --version v1.0