EVS Neo [VCon OG]
For controlling the EVS Neo the EVS OpenGateAPI is used. This is a REST Api that contains the baisc elements needed to control a recording.
From the EVS website:
The VIA OpenGate engine offers a complete REST API catalog enabling seamless certified third-party integrations for ingest, asset and metadata management, media transformation, publishing, and playout.
But currently only a subset is implemented, i.e. playout is completely missing.
What the EVS Neo recorder currently does is reading all available recorders from the configuration and create and control a recording.
Setup
The installation of a EVS Neo recorder is done by a dynamic playbook. Below is an example (91_vpms3.evsrecorder.v2.yaml from the project-wdr-phx repository):
evsrecorder:
playbook:
name: evsrecorder
abbreviation: evsrecorder
#######################################################
# Main Chart part #
# Work in progress #
#######################################################
default:
endpoints:
http:
fqdn: "{{ (default_endpoint_scheme + lookup('prepared_config', 'kubernetesPublicEndpoint') + '/' if not lookup('prepared_config', 'kubernetesPublicEndpoint').endswith('/') else '' | string) | urlsplit('hostname') }}"
helm_chart:
install: true
chart_name: recorderevsneo
chart_subpath:
chart_folder: VidiControl/Recorders/Evs
chart_category: VPMS
chart_namespace: "{{ server_config.system.name }}-vctl"
chart_instance_name: recorderevsneo
chart_version: 24.4.257451
chart_values:
hull:
config:
general:
globalImageRegistryToFirstRegistrySecretServer: true
fullnameOverride: recorderevsneo
data:
installation:
config:
customCaCertificates:
{% for key, value in vpms3.global.system.ssl.custom_ca_certs.items() %}
{{ key }}: "{{ (value | singleline) + '\n' }}"
{% endfor %}
endpoints:
vidicore:
uri:
api: "{{ default_endpoint_scheme + lookup('prepared_config', 'kubernetesPublicEndpoint') + '/API'}}"
authservice:
uri:
api: "{{ default_endpoint_scheme + lookup('prepared_config', 'kubernetesPublicEndpoint') + vpms3.authservice.base.default.app_path_authservice }}"
auth:
token:
installationClientId: installerClient
installationClientSecret: "{{ vault.vidicontrol.base.default.installer_client_secret }}"
configportal:
uri:
api: "{{ default_endpoint_scheme + lookup('prepared_config', 'kubernetesPublicEndpoint') + vpms3.configportal.base.default.app_path_configportal_api }}"
ui: "{{ default_endpoint_scheme + lookup('prepared_config', 'kubernetesPublicEndpoint') + vpms3.configportal.base.default.app_path_configportal_ui }}"
notification: "{{ default_endpoint_scheme + lookup('prepared_config', 'kubernetesPublicEndpoint') + vpms3.configportal.base.default.app_path_configportal_notification }}"
auth:
token:
installationClientId: installerClient
installationClientSecret: "{{ vault.configportal.base.default.installer_client_secret }}"
notification:
uri:
api: "http://core-services-core-services-noti-api:8080/Platform.Core/Notification/"
vidispine:
uri:
api: "{{ default_endpoint_scheme + lookup('prepared_config', 'kubernetesPublicEndpoint') + '/API/' }}"
rule:
uri:
api: "http://core-services-core-services-rule-api:8080/Platform.Core/Rule/"
specific:
ingress:
controllerClass: "{{ vpms3.global.system.ingress.controllerClass }}"
registry: "{{ vpms3.global.docker.registry.endpoint }}"
authService:
installerClientId: installerClient
installerClientSecret: "{{ vault.vidicontrol.base.default.installer_client_secret }}"
recorderevsneo:
tag: 24.4.257451
endpoint: "http://recorderevsneo-evsrecorder.phxdev-vctl-vctl:8080"
devicesettings: '"{\"RECORDER_EVS_NEO_API_URL\":\"https://phx-via-cluster.int.wdr.de\",\"RECORDER_EVS_NEO_API_KEY\":\"dummy\",\"RECORDER_EVS_NEO_INGESTTARGET_ID\":\"dummy\"}"'
database:
host: "{{ hostvars['sql-server']['host'] }}"
port: "{{ hostvars['sql-server']['port'] | default(1433) }}"
user: "{{ hostvars['sql-server']['admin_user'] }}"
type: postgres
connectionstrings:
recorderevsneo: "User ID = {{ hostvars['sql-server']['admin_user'] }};Server={{ hostvars['sql-server']['host'] }};Port={{ hostvars['sql-server']['port'] | default(1433) }};Database={{ vpms3.global.system.name | regex_replace('-', '_') }}_recorderevs;Pooling=true;"
names:
recorderevsneo: "{{ vpms3.global.system.name | regex_replace('-', '_') }}_recorderevs"
objects:
registry:
vpms3:
server: "{{ vpms3.global.docker.registry.endpoint }}"
username: "{{ vpms3.global.docker.registry.user }}"
password: "{{ vpms3.global.docker.registry.pass }}"
deployment:
vidicontrol-recorderevsneo:
pod:
containers:
vidicontrol-recorderevsneo:
image:
repository: mediacomponent/recorders/recorderevsneo
The versions of the Helm chart and the Docker image must be updated to the currently used versions. Additionally, the device settings must be adapted, mainly the RECORDER_EVS_NEO_API_URL, the RECORDER_EVS_NEO_API_KEY and the RECORDER_EVS_NEO_INGESTTARGET_ID.
The Api Key can be created on the Account Settings Page of the VIA MAP UI.
For installing the recorder, the preferred installation method with the tag “evsrecorder” can be used, i.e.
./prepared.sh --log=vvv --tags=evsrecorder
Customize config after installation
Workflow
When a recording is started a VidiFlow workflow will be triggered.
This workflow must be created, named 'WF_EvsRecording' and have 'ItemId' and 'RecordingSession' as parameters (type string). Workflow name and version can be changed via the VidiControl Configuration Tool.
The version of the workflow must correspond to the version listed in the event 'RecorderEvsNeo.startWorkflow'.
The module executes a periodic status check of the EVS server and, if necessary, sets the VidiControl device status to online or offline.
Capability
EvsTargets must be added to the recorder capability after installation.
Add a field of type 'enum' with name EvsTarget to the input of the Capability Content.
The TargetRef values (Guid) of the targets from the EVS system, which should be available for selection in VidiControl, are used as enum values.
This may also be possible with the Config UI.
Example:
"input": {
"name": {
"type": "string",
"optional": "false"
},
"EvsTarget": {
"label": "EVS Targets",
"hint": "Select an option",
"type": "enum",
"items": {
"item0": {
"label": "Target1",
"value": "087dfs6b-987df987sd-984375f"
},
"item1": {
"label": "Target2",
"value": "087dfs6b-98sdaddsd-98dsddd",
"default": true
}
}
}
}
Configuration
The Guid for the default target (e.g. for crash record; intial value in DeviceConfiguration:DeviceSettings RECORDER_EVS_NEO_INGESTTARGET_ID) is saved as EvsDefaultIngestTargetId in the recoder config table and can be changed via the Config UI for each Pod.
The timezone Id of the EVSApi (intial value in DeviceConfiguration:DeviceSettings 'RECORDER_EVS_NEO_API_TIMEZONE') is saved as EvsTimezone in the recoder config table and can be changed via the Config UI for each Pod.
The Timezone IDs are operating system dependent. To get these IDs, start the recorder once without RECORDER_EVS_NEO_API_TIMEZONE. Then it should output a warning in the logfile 'Couldn't load timezone info' and below it a list of all available timezones in the format '<TimezoneId>; <TimezoneName>'.
Example: On a windows system the timezone Id for London is 'GMT Standard Time', on Linux it is 'Europe/London'. (List of IANA timezone Ids for Linux)
Additional config values
Config value | Type | Comment |
|---|---|---|
VIDICONTROL_DATABASE_CONNECTION_STRING | string | |
VIDICONTROL_DATABASE_PASSWORD | string |
|
VIDICONTROL_IDSALT | string | |
VIDICONTROL_SITEID | string | site id (alphanumeric) |
RECORDER_EVS_NEO_DATABASE_CONNECTION_STRING | string | |
RECORDER_EVS_NEO_DATABASE_PASSWORD | string | |
RECORDER_EVS_NEO_INGESTTARGET_ID | string | (DeviceConfiguration:DeviceSettings) default target |
RECORDER_EVS_NEO_API_KEY | string | (DeviceConfiguration:DeviceSettings) |
RECORDER_EVS_NEO_API_URL | string | (DeviceConfiguration:DeviceSettings) |
RECORDER_EVS_NEO_API_TIMEZONE | string | (DeviceConfiguration:DeviceSettings) |
RECORDER_EVS_NEO_API_STATUS_ONLINE | string | (DeviceConfiguration:DeviceSettings) comma-separated list of the EVS server status values that mean 'online' for the device, e.g. 'CONNECTED,SomeOtherValue' |
RECORDER_EVS_NEO_API_STATUS_REC_UNAVAILABLE | string | (DeviceConfiguration:DeviceSettings) comma-separated list of the EVS recorder status values that mean 'offline' for the device, e.g. 'in error,anotherValue' |
RECORDER_EVS_NEO_API_STATUS_CHECK_PERIOD_IN_S | int | (DeviceConfiguration:DeviceSettings) Interval in seconds in which the EVS recorder status check is executed, default is -1. If <=0 status check is disabled. |
RECORDER_EVS_NEO_DISABLE_ALL_CHECK_COUNT | int | (DeviceConfiguration:DeviceSettings) Number of failed status checks after which all devices are set to offline, default is 0 |
Config for VidiControl initialization (Device, Capabilities, Events)
Values for Capabilities and Events are fixed, values in DeviceConfiguration are system specific
"DeviceConfiguration": {
"DeviceName": "RecorderEvsNeo",
"Uri": "<http://localhost:5187>",
"DeviceSettings": "{\"RECORDER_EVS_NEO_API_URL\":\"the-EVS-API-Endpoint\",\"RECORDER_EVS_NEO_API_KEY\":\"the-APIKey-for-EVS-API\",\"RECORDER_EVS_NEO_INGESTTARGET_ID\":\"the-default-recording-target-Id\",\"RECORDER_EVS_NEO_API_TIMEZONE\":\"GMT Standard Time\",\"RECORDER_EVS_NEO_API_STATUS_ONLINE\":\"CONNECTED\",\"RECORDER_EVS_NEO_API_STATUS_CHECK_PERIOD_IN_S\":45,\"RECORDER_EVS_NEO_API_STATUS_REC_UNAVAILABLE\":\"in error\"}",
"CapabilityGroup": "RecorderEvsNeoGroup"
},
"Capability": [
{
"Content": "{\"action\":\"Record\",\"input\":{\"name\":{\"type\":\"string\",\"optional\":\"false\"}},\"properties\":{\"nonBlockingProvisioningTime\":{\"type\":\"integer\",\"kind\":\"fix\"}},\"events\":[\"RecorderEvsNeo.startWorkflow\", \"RecorderEvsNeo.recordingFailed\", \"RecorderEvsNeo.recordingStarted\", \"RecorderEvsNeo.recordingFinished\", \"RecorderEvsNeo.recordingScheduled\",\"RecorderEvsNeo.recordingUpdateFailed\"]}",
"NeedsStopCommand": true,
"CapabilityType": 0,
"DefaultConfig": "{\"nonBlockingProvisioningTime\":10}"
}
],
"EventType": [
{
"Name": "RecorderEvsNeo.startWorkflow",
"HandleCustomerSpecific": false,
"Description": "",
"Content": "[{\"action\": \"startWorkflow\",\"input\": {\"Name\": \"WF_EvsRecording\",\"Version\": \"1.Latest\",\"Data\": {\"Parameters\": [{\"Name\": \"RecordingSession\",\"Value\": \"{-{Placeholder_RecordingsSession}-}\",\"Type\": \"string\"}, {\"Name\": \"ItemId\",\"Value\": \"{-{Placeholder_VCItemId}-}\",\"Type\": \"string\"}],\"Metadata\": null,\"Priority\": null}}}]"
},
{
"Name": "RecorderEvsNeo.recordingStarted",
"HandleCustomerSpecific": false,
"Description": "",
"Content": "[{\"action\":\"notifyClient\",\"input\":{\"TimeStamp\":\"{-{Placeholder_TimeStamp}-}\",\"ChunkId\":\"{-{Placeholder_ExternalChunkId}-}\",\"UpdateNeeded\":false,\"MessageType\":\"RecorderEvsNeo.recordingStarted\",\"Emitter\":\"{-{Placeholder_Emitter}-}\",\"Severity\":\"Information\",\"Message\":[{\"Lang\":\"en_US\",\"Text\":\"Recording started.\"},{\"Lang\":\"de_DE\",\"Text\":\"Aufnahme gestartet.\"}]}}]"
},
{
"Name": "RecorderEvsNeo.recordingFinished",
"HandleCustomerSpecific": false,
"Description": "",
"Content": "[{\"action\":\"notifyClient\",\"input\":{\"TimeStamp\":\"{-{Placeholder_TimeStamp}-}\",\"ChunkId\":\"{-{Placeholder_ExternalChunkId}-}\",\"UpdateNeeded\":false,\"MessageType\":\"RecorderEvsNeo.recordingFinished\",\"Emitter\":\"{-{Placeholder_Emitter}-}\",\"Severity\":\"Information\",\"Message\":[{\"Lang\":\"en_US\",\"Text\":\"Recording finished.\"},{\"Lang\":\"de_DE\",\"Text\":\"Aufnahme beendet.\"}]}}]"
},
{
"Name": "RecorderEvsNeo.recordingFailed",
"HandleCustomerSpecific": false,
"Description": "",
"Content": "[{\"action\":\"notifyClient\",\"input\":{\"TimeStamp\":\"{-{Placeholder_TimeStamp}-}\",\"ChunkId\":\"{-{Placeholder_ExternalChunkId}-}\",\"UpdateNeeded\":false,\"MessageType\":\"RecorderEvsNeo.recordingFailed\",\"Emitter\":\"{-{Placeholder_Emitter}-}\",\"Severity\":\"Error\",\"Message\":[{\"Lang\":\"en_US\",\"Text\":\"Recording failed.\"},{\"Lang\":\"de_DE\",\"Text\":\"Aufnahme gescheitert.\"}]}}]"
},
{
"Name": "RecorderEvsNeo.recordingScheduled",
"HandleCustomerSpecific": false,
"Description": "",
"Content": "[{\"action\":\"notifyClient\",\"input\":{\"TimeStamp\":\"{-{Placeholder_TimeStamp}-}\",\"ChunkId\":\"{-{Placeholder_ExternalChunkId}-}\",\"UpdateNeeded\":false,\"MessageType\":\"RecorderEvsNeo.recordingScheduled\",\"Emitter\":\"{-{Placeholder_Emitter}-}\",\"Severity\":\"Information\",\"Message\":[{\"Lang\":\"en_US\",\"Text\":\"Recording scheduled.\"},{\"Lang\":\"de_DE\",\"Text\":\"Aufnahme geplant.\"}]}}]"
},
{
"Name": "RecorderEvsNeo.recordingUpdateFailed",
"HandleCustomerSpecific": false,
"Description": "",
"Content": "[{\"action\":\"notifyClient\",\"input\":{\"TimeStamp\":\"{-{Placeholder_TimeStamp}-}\",\"ChunkId\":\"{-{Placeholder_ExternalChunkId}-}\",\"UpdateNeeded\":false,\"MessageType\":\"RecorderEvsNeo.recordingUpdateFailed\",\"Emitter\":\"{-{Placeholder_Emitter}-}\",\"Severity\":\"Warning\",\"Message\":[{\"Lang\":\"en_US\",\"Text\":\"Recording update failed.\"},{\"Lang\":\"de_DE\",\"Text\":\"Update der Aufnahme fehlgeschlagen.\"}]}}]"
}
]
Device
Requires a ConnectionInfo with EVS server Id (RecorderRef)
{
"RecorderRef": "eccaa4ff-7371-4ffe-834e-963a524c42a6"
}
Capability
Requires a Content with an input parameter 'name'
{
"action": "Record",
"input": {
"name": {
"type": "string",
"optional": "false"
}
},
"output": {
},
"properties": {
"preroll": {
"type": "integer",
"kind": "fix"
}
},
"events": ["RecorderEvsNeo.fileCreated", "RecorderEvsNeo.recordingFailed", "RecorderEvsNeo.recordingStarted", "RecorderEvsNeo.recordingFinished"]
}
EventType
Requires an EventType with action 'startWorkflow' and placeholder 'Placeholder_RecordingsSession' for the EVS RecordingsSession data
[{"action": "startWorkflow","input": {"Name": "WF_EvsRecording","Version": "latest","Data": {"Parameters": [{"Name": "RecordingSession","Value": "{{Placeholder_RecordingsSession}}","Type": "string"}, {"Name": "Item","Value": "{{Placeholder_VCItemId}}","Type": "string"}],"Metadata": null,"Priority": null}}}]