Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In Azure IoT Operations, the media connector (preview) enables access to media from media sources such as edge-attached cameras. This article explains how to use the media connector to perform tasks such as:
- Send an image snapshot to the MQTT broker.
- Save a video stream to a local file system.
The media connector:
Uses asset endpoints to access media sources. An asset endpoint defines a connection to a media source such as a camera. The asset endpoint configuration includes the URL of the media source, the type of media source, and any credentials needed to access the media source.
Uses assets to represent media sources such as cameras. An asset defines the capabilities and properties of a media source such as a camera.
Prerequisites
A deployed instance of Azure IoT Operations. If you don't already have an instance, see Quickstart: Run Azure IoT Operations in GitHub Codespaces with K3s.
A camera connected to your network and accessible from your Azure IoT Operations cluster. The camera must support the Real Time Streaming Protocol for video streaming. You also need the camera's username and password to authenticate with it.
Deploy the media connector
To deploy the preview version of the connectors, you can either enable them when you deploy your Azure IoT Operations instance or enable them after you deploy your instance.
To enable the preview connectors when you deploy your Azure IoT Operations instance:
Select ONVIF Connector and Media Connector (Preview) in the Connectors section of the Install Azure IoT Operations > Basics page:
To enable the preview connectors after you deploy your Azure IoT Operations instance:
Go to your Azure IoT Operations instance in the Azure portal.
Enable the preview connectors:
Important
If you don't enable preview features, you see the following error message in the aio-supervisor-...
pod logs when you try to use the media or ONVIF connectors: No connector configuration present for AssetEndpointProfile: <AssetEndpointProfileName>
.
Deploy the media server
If you're using the media connector to stream live video, you need to install your own media server. To deploy a sample media server to use with the media connector, run the following commands:
kubectl create namespace media-server
kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/refs/heads/main/samples/media-server/media-server-deployment.yaml
kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/refs/heads/main/samples/media-server/media-server-service.yaml
kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/refs/heads/main/samples/media-server/media-server-service-public.yaml
Important
This media server is only suitable for testing and development purposes. In a production environment you need to provide your own media server.
To discover the cluster IP address of this media server, run the following command:
kubectl get service media-server-public --namespace media-server
Make a note of the CLUSTER-IP value, you use it later to access the media server.
Asset endpoint configuration
To configure the media connector, first create an asset endpoint that defines the connection to the media source. The asset endpoint includes the URL of the media source, the type of media source, and any credentials you need to access the media source.
If your camera requires authentication, create a secret in your Kubernetes cluster that stores the camera's username and password. The media connector uses this secret to authenticate with the camera:
Create a YAML file called contoso-secrets.yaml with the following content. Replace the placeholders with your camera's username and password encoded in base64:
apiVersion: v1 kind: Secret metadata: name: contoso-secrets type: Opaque data: username: "<YOUR CAMERA USERNAME BASE64 ENCODED>" password: "<YOUR CAMERA PASSWORD BASE64 ENCODED>"
Tip
To encode the username and password in base64 at a Bash prompt, use the following command:
echo -n "<STRING TO ENCODE>" | base64
.To add the secret to your cluster in the default Azure IoT Operations namespace, run the following command:
kubectl apply -f contoso-secrets.yaml -n azure-iot-operations
To create the asset endpoint by using a Bicep file:
Set the following environment variables:
SUBSCRIPTION_ID="<YOUR SUBSCRIPTION ID>" RESOURCE_GROUP="<YOUR AZURE IOT OPERATIONS RESOURCE GROUP>" TARGET_ADDRESS="<YOUR CAMERA RTSP ADDRESS>" AEP_NAME="contoso-rtsp-aep" SECRET_NAME="contoso-secrets"
Run the following script:
# Download the Bicep file wget https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/media-connector-bicep/aep-media-connector.bicep -O aep-media-connector.bicep # Find the name of your custom location CUSTOM_LOCATION_NAME=$(az iot ops list -g $RESOURCE_GROUP --query "[0].extendedLocation.name" -o tsv) # Use the Bicep file to deploy the asset endpoint az deployment group create --subscription $SUBSCRIPTION_ID --resource-group $RESOURCE_GROUP --template-file aep-media-connector.bicep --parameters targetAddress=$TARGET_ADDRESS customLocationName=$CUSTOM_LOCATION_NAME aepName=$AEP_NAME secretName=$SECRET_NAME
The following snippet shows the bicep file that you used to create the asset endpoint:
metadata description = 'Asset endpoint profile for media connector'
@description('The RTSP endpoint for the media stream.')
param targetAddress string
@description('The name of the custom location you are using.')
param customLocationName string
@description('Specifies the name of the asset endpoint resource to create.')
param aepName string
@description('The name of the Kubernetes secret you are using to store the camera credentials.')
param secretName string
/*****************************************************************************/
/* Asset endpoint profile */
/*****************************************************************************/
resource assetEndpoint 'Microsoft.DeviceRegistry/assetEndpointProfiles@2024-11-01' = {
name: aepName
location: resourceGroup().location
extendedLocation: {
type: 'CustomLocation'
name: customLocationName
}
properties: {
targetAddress: targetAddress
endpointProfileType: 'Microsoft.Media'
#disable-next-line no-hardcoded-env-urls //Schema required during public preview
additionalConfiguration: '{"@schema":"https://aiobrokers.blob.core.windows.net/aio-media-connector/1.0.0.json"}'
authentication: {
method: 'UsernamePassword'
usernamePasswordCredentials: {
passwordSecretName: '${secretName}/password'
usernameSecretName: '${secretName}/username'
}
}
}
}
The previous example configures the asset endpoint to authenticate with the camera with a username and password. In the Bicep file, the authentication section of the asset endpoint you created looks like the following example:
authentication: {
method: 'UsernamePassword'
usernamePasswordCredentials: {
passwordSecretName: '${secretName}/password'
usernameSecretName: '${secretName}/username'
}
If your camera doesn't require a username and password, configure anonymous authentication as shown in the following example:
authentication: {
method: 'Anonymous'
}
Asset configuration
When you configure an asset, the datasets.DataPoints
parameter specifies the action the media connector takes on the asset. A camera asset supports the following task types:
Task type | Description |
---|---|
snapshot-to-mqtt |
Capture snapshots from a camera and publishes them to an MQTT topic. |
snapshot-to-fs |
Capture snapshots from a camera and saves them to the local file system. |
clip-to-fs |
Capture video clips from a camera and saves them to the local file system. |
stream-to-rtsp |
Sends a live video stream from a camera to a media server. |
You can use the following settings to configure individual tasks:
autostart
: Whether the task starts automatically when the asset starts.realtime
: Whether the task runs in real time.loop
: Whether the task runs continuously.format
: The format of the media file.fps
: The frames per second for the media file.audioEnabled
: Whether audio is enabled for the media file.duration
: The duration of the media file.
The following examples show how to deploy assets for each task type.
Snapshot to MQTT
To configure an asset that captures snapshots from a camera and publishes them to an MQTT topic:
Set the following environment variables:
SUBSCRIPTION_ID="<YOUR SUBSCRIPTION ID>" RESOURCE_GROUP="<YOUR AZURE IOT OPERATIONS RESOURCE GROUP>" AEP_NAME="contoso-rtsp-aep"
Run the following script:
# Download the Bicep file wget https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/media-connector-bicep/asset-snapshot-to-mqtt.bicep -O asset-snapshot-to-mqtt.bicep # Find the name of your custom location CUSTOM_LOCATION_NAME=$(az iot ops list -g $RESOURCE_GROUP --query "[0].extendedLocation.name" -o tsv) # Use the Bicep file to deploy the asset az deployment group create --subscription $SUBSCRIPTION_ID --resource-group $RESOURCE_GROUP --template-file asset-snapshot-to-mqtt.bicep --parameters customLocationName=$CUSTOM_LOCATION_NAME aepName=$AEP_NAME
The following snippet shows the bicep file that you used to create the asset:
metadata description = 'Media asset that publishes snapshots to MQTT.'
@description('The name of the custom location you are using.')
param customLocationName string
@description('Specifies the name of the asset endpoint resource to use.')
param aepName string
@description('The name of the asset you are creating.')
param assetName string = 'asset-snapshot-to-mqtt'
/*****************************************************************************/
/* Asset */
/*****************************************************************************/
resource asset 'Microsoft.DeviceRegistry/assets@2024-11-01' = {
name: assetName
location: resourceGroup().location
extendedLocation: {
type: 'CustomLocation'
name: customLocationName
}
properties: {
assetEndpointProfileRef: aepName
datasets: [
{
name: 'dataset1'
dataPoints: [
{
name: 'snapshot-to-mqtt'
dataSource: 'snapshot-to-mqtt'
dataPointConfiguration: '{"taskType":"snapshot-to-mqtt","autostart":true,"realtime":true,"loop":true,"format":"jpeg","fps":1}'
}
]
}
]
}
}
To verify that snapshots are publishing to the MQTT broker, use the mosquitto_sub tool. In this example, you run the mosquitto_sub tool inside a pod in your Kubernetes cluster:
Run the following command to deploy a pod that includes the mosquitto_pub and mosquitto_sub tools that are useful for interacting with the MQTT broker in the cluster:
kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/quickstarts/mqtt-client.yaml
Caution
This configuration isn't secure. Don't use this configuration in a production environment.
When the mqtt-client pod is running, run the following command to create a shell environment in the pod you created:
kubectl exec --stdin --tty mqtt-client -n azure-iot-operations -- sh
At the Bash shell in the mqtt-client pod, run the following command to connect to the MQTT broker using the mosquitto_sub tool subscribed to the
azure-iot-operations/data
topic:mosquitto_sub --host aio-broker --port 18883 --topic "azure-iot-operations/data/#" -V 5 -F '%p' -C 1 --cafile /var/run/certs/ca.crt -D CONNECT authentication-method 'K8S-SAT' -D CONNECT authentication-data $(cat /var/run/secrets/tokens/broker-sat) > image.jpeg
This command captures the raw payload from a single message and saves it to a file called image.jpeg in the pod's filing system. To exit the pod's shell environment, type
exit
.To copy the image file from the pod to your local machine, run the following command:
kubectl cp azure-iot-operations/mqtt-client:image.jpeg image.jpeg
When you finish testing the asset, you can delete it by running the following command:
az iot ops asset delete -n asset-snapshot-to-mqtt -g $RESOURCE_GROUP
Snapshot to file system
To configure an asset that captures snapshots from a camera and saves them as files:
Set the following environment variables:
SUBSCRIPTION_ID="<YOUR SUBSCRIPTION ID>" RESOURCE_GROUP="<YOUR AZURE IOT OPERATIONS RESOURCE GROUP>" AEP_NAME="contoso-rtsp-aep"
Run the following script:
# Download the Bicep file wget https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/media-connector-bicep/asset-snapshot-to-fs.bicep -O asset-snapshot-to-fs.bicep # Find the name of your custom location CUSTOM_LOCATION_NAME=$(az iot ops list -g $RESOURCE_GROUP --query "[0].extendedLocation.name" -o tsv) # Use the Bicep file to deploy the asset az deployment group create --subscription $SUBSCRIPTION_ID --resource-group $RESOURCE_GROUP --template-file asset-snapshot-to-fs.bicep --parameters customLocationName=$CUSTOM_LOCATION_NAME aepName=$AEP_NAME
The following snippet shows the bicep file that you used to create the asset:
metadata description = 'Media asset that saves snapshots to the file system.'
@description('The name of the custom location you are using.')
param customLocationName string
@description('Specifies the name of the asset endpoint resource to use.')
param aepName string
@description('The name of the asset you are creating.')
param assetName string = 'asset-snapshot-to-fs'
/*****************************************************************************/
/* Asset */
/*****************************************************************************/
resource asset 'Microsoft.DeviceRegistry/assets@2024-11-01' = {
name: assetName
location: resourceGroup().location
extendedLocation: {
type: 'CustomLocation'
name: customLocationName
}
properties: {
assetEndpointProfileRef: aepName
datasets: [
{
name: 'dataset1'
dataPoints: [
{
name: 'snapshot-to-fs'
dataSource: 'snapshot-to-fs'
dataPointConfiguration: '{"taskType":"snapshot-to-fs","autostart":true,"realtime":true,"loop":true,"format":"jpeg","fps":1}'
}
]
}
]
}
}
The files are saved in the file system of the opc-media-1-...
pod. To find the full name of the pod, run the following command. The following command uses the default Azure IoT Operations namespace:
kubectl get pods -n azure-iot-operations
To view the files, run the ls
command in the pod. Use the full name of the pod in the following command:
kubectl exec aio-opc-media-1-... -n azure-iot-operations -- ls /tmp/azure-iot-operations/data/asset-snapshot-to-fs/snapshots/
When you finish testing the asset, you can delete it by running the following command:
az iot ops asset delete -n asset-snapshot-to-fs -g $RESOURCE_GROUP
Clip to file system
To configure an asset that captures clips from a camera and saves them as files:
Set the following environment variables:
SUBSCRIPTION_ID="<YOUR SUBSCRIPTION ID>" RESOURCE_GROUP="<YOUR AZURE IOT OPERATIONS RESOURCE GROUP>" AEP_NAME="contoso-rtsp-aep"
Run the following script:
# Download the Bicep file wget https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/media-connector-bicep/asset-clip-to-fs.bicep -O asset-clip-to-fs.bicep # Find the name of your custom location CUSTOM_LOCATION_NAME=$(az iot ops list -g $RESOURCE_GROUP --query "[0].extendedLocation.name" -o tsv) # Use the Bicep file to deploy the asset az deployment group create --subscription $SUBSCRIPTION_ID --resource-group $RESOURCE_GROUP --template-file asset-clip-to-fs.bicep --parameters customLocationName=$CUSTOM_LOCATION_NAME aepName=$AEP_NAME
The following snippet shows the bicep file that you used to create the asset:
metadata description = 'Media asset that saves clips to the file system.'
@description('The name of the custom location you are using.')
param customLocationName string
@description('Specifies the name of the asset endpoint resource to use.')
param aepName string
@description('The name of the asset you are creating.')
param assetName string = 'asset-clip-to-fs'
/*****************************************************************************/
/* Asset */
/*****************************************************************************/
resource asset 'Microsoft.DeviceRegistry/assets@2024-11-01' = {
name: assetName
location: resourceGroup().location
extendedLocation: {
type: 'CustomLocation'
name: customLocationName
}
properties: {
assetEndpointProfileRef: aepName
datasets: [
{
name: 'dataset1'
dataPoints: [
{
name: 'clip-to-fs'
dataSource: 'clip-to-fs'
dataPointConfiguration: '{"taskType":"clip-to-fs","autostart":true,"realtime":true,"loop":true,"format":"avi","duration":3}'
}
]
}
]
}
}
The files are saved in the file system of the opc-media-1-...
pod. To find the full name of the pod, run the following command. The following command uses the default Azure IoT Operations namespace:
kubectl get pods -n azure-iot-operations
To view the files, run the ls
command in the pod. Use the full name of the pod in the following command:
kubectl exec aio-opc-media-1-... -n azure-iot-operations -- ls /tmp/azure-iot-operations/data/asset-clip-to-fs/clips/
When you finish testing the asset, you can delete it by running the following command:
az iot ops asset delete -n asset-clip-to-fs -g $RESOURCE_GROUP
Stream to RTSP
To configure an asset that forwards video streams from a camera to a media server:
You made a note of the IP address of the media server when you deployed it in a previous step.
Set the following environment variables:
SUBSCRIPTION_ID="<YOUR SUBSCRIPTION ID>" RESOURCE_GROUP="<YOUR AZURE IOT OPERATIONS RESOURCE GROUP>" MEDIA_SERVER_ADDRESS="<YOUR MEDIA SERVER IP ADDRESS>" AEP_NAME="contoso-rtsp-aep"
Run the following script:
# Download the Bicep file wget https://raw.githubusercontent.com/Azure-Samples/explore-iot-operations/main/samples/media-connector-bicep/asset-stream-to-rtsp.bicep -O asset-stream-to-rtsp.bicep # Find the name of your custom location CUSTOM_LOCATION_NAME=$(az iot ops list -g $RESOURCE_GROUP --query "[0].extendedLocation.name" -o tsv) # Use the Bicep file to deploy the asset az deployment group create --subscription $SUBSCRIPTION_ID --resource-group $RESOURCE_GROUP --template-file asset-stream-to-rtsp.bicep --parameters customLocationName=$CUSTOM_LOCATION_NAME aepName=$AEP_NAME mediaServerAddress=$MEDIA_SERVER_ADDRESS
The following snippet shows the bicep file that you used to create the asset:
metadata description = 'Media asset that streams RTSP to a media server.'
@description('The name of the custom location you are using.')
param customLocationName string
@description('Specifies the name of the asset endpoint resource to use.')
param aepName string
@description('The name of the asset you are creating.')
param assetName string = 'asset-stream-to-rtsp'
@description('The IP address of your media server.')
param mediaServerAddress string
/*****************************************************************************/
/* Asset */
/*****************************************************************************/
resource asset 'Microsoft.DeviceRegistry/assets@2024-11-01' = {
name: assetName
location: resourceGroup().location
extendedLocation: {
type: 'CustomLocation'
name: customLocationName
}
properties: {
assetEndpointProfileRef: aepName
datasets: [
{
name: 'dataset1'
dataPoints: [
{
name: 'stream-to-rtsp'
dataSource: 'stream-to-rtsp'
dataPointConfiguration: '{"taskType":"stream-to-rtsp","autostart":true,"realtime":true,"loop":true,"media_server_address":"${mediaServerAddress}"}'
}
]
}
]
}
}
To view the media stream, use a URL that looks like: http://<YOUR KUBERNETES CLUSTER IP ADDRESS>:8888/azure-iot-operations/data/asset-stream-to-rtsp
.
Tip
If you're running Azure IoT Operations in Codespaces, run the following command to port forward the media server to your local machine: kubectl port-forward service/media-server-public 8888:8888 -n media-server
.
Tip
If you're running Azure IoT Operations in a virtual machine, make sure that port 8888 is open for inbound access in your firewall.
The media server logs the connection from the asset and the creation of the stream:
2025/02/20 15:31:10 INF [RTSP] [conn <INTERNAL IP ADDRESS OF ASSET>:41384] opened
2025/02/20 15:31:10 INF [RTSP] [session 180ce9ad] created by <INTERNAL IP ADDRESS OF ASSET>:41384
2025/02/20 15:31:10 INF [RTSP] [session 180ce9ad] is publishing to path 'azure-iot-operations/data/asset-stream-to-rtsp', 2 tracks (H264, LPCM)
2025/02/20 15:31:18 INF [HLS] [muxer azure-iot-operations/data/asset-stream-to-rtsp] created (requested by <IP ADDRESS OF EXTERNAL CLIENT>:16831)
2025/02/20 15:31:18 WAR [HLS] [muxer azure-iot-operations/data/asset-stream-to-rtsp] skipping track 2 (LPCM)
2025/02/20 15:31:18 INF [HLS] [muxer azure-iot-operations/data/asset-stream-to-rtsp] is converting into HLS, 1 track (H264)
When you finish testing the asset, you can delete it by running the following command:
az iot ops asset delete -n asset-stream-to-rtsp -g $RESOURCE_GROUP