Configurar Unified Streaming para Live en Microsoft Azure

La semana pasada compartí contigo cómo configurar Unified Streaming Platform para servir video bajo demanda. En este artículo me gustaría contarte cómo desplegar Unified Streaming para Live en Microsoft Azure, ya que la configuración es algo distinta.

Crear un clúster en AKS y un Azure Disk

Al igual que en el escenario anterior, vamos a utilizar Azure Kubernetes Service para hospedar punto de publicación que utilizaremos para la ingesta y el playout del live. Si todavía no tienes un clúster creado puedes montar uno fácilmente siguiendo estos comandos:

#Variables
RESOURCE_GROUP="usp-live-origin"
LOCATION="northeurope"
AKS_NAME="usp-live-origin-cluster"

#Create resource group
az group create -n $RESOURCE_GROUP -l $LOCATION

# Create AKS cluster
az aks create -n $AKS_NAME -g $RESOURCE_GROUP --node-count 1 --generate-ssh-keys

#Get the context for the new AKS
az aks get-credentials -n $AKS_NAME -g $RESOURCE_GROUP

Para el escenario de Live no es recomendable el uso de carpetas compartidas, como usamos en la parte de VoD. En este caso debemos utilizar Azure Disk como volumen persistente, ya que además no es necesario el compartir el punto de montaje entre varios pods. Para ello puedes utilizar los siguientes comandos, para recuperar el grupo de recursos donde tu clúster está alojando el resto de los recursos y para crear uno nuevo del tipo Azure Disk.

#Get the node resource group
NODE_RESOURCE_GROUP=$(az aks show --resource-group $RESOURCE_GROUP --name $AKS_NAME --query nodeResourceGroup -o tsv)

#Create an Azure Disk in the node resource group
az disk create -g $NODE_RESOURCE_GROUP -n live-origin-assets --size-gb 500 --query id --output tsv
#The result, the disk URI, should be used to mount this volume in the Pod

El resultado del último comando nos dará la URI que debemos utilizar en la configuración de nuestro pod.

StatefulSet para el despliegue del pod

Para este escenario haremos uso del recurso StatefulSet y un Service que nos proporcionará la IP pública para realizar la ingesta y consumir el directo. Antes de crearlos necesitas un secreto con la clave de Unified Streaming, en mi caso almacenada en un archivo llamado key:

#Create a secret with the USP Key
kubectl create secret generic live-origin-test-license-key --from-file=key

Esta sería la configuración para el recurso StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: live-origin-test
spec:
  replicas: 1
  serviceName: live-origin-test
  selector:
    matchLabels:
      name: live-origin      
  template:
    metadata:
      labels:
        name: live-origin        
    spec:
      containers:
        - name: live-origin
          image: unifiedstreaming/live:latest
          imagePullPolicy: Always
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /test/test.isml/state
              port: http
          readinessProbe:
            httpGet:
              path: /test/test.isml/state
              port: http
          startupProbe:
            httpGet:
              path: /test/test.isml/state
              port: http
            failureThreshold: 60
            periodSeconds: 10
          resources: {}
          env:
            - name: USP_LICENSE_KEY
              valueFrom:
                secretKeyRef:
                  name: live-origin-test-license-key
                  key: key
            - name: CHANNEL
              value: test
            - name: PUB_POINT_OPTS             
              value: --archiving=1 --archive_length=3600 --archive_segment_length=1800 --dvr_window_length=30 --restart_on_encoder_reconnect --mpd.min_buffer_time=48/25 --mpd.suggested_presentation_delay=48/25 --hls.minimum_fragment_length=48/25 --mpd.minimum_fragment_length=48/25 --mpd.segment_template=time --hls.client_manifest_version=4 --hls.fmp4 --iss.minimum_fragment_length=48/25 --hds.minimum_fragment_length=48/25
          volumeMounts:
            - name: test
              mountPath: /var/www/live/test/
      volumes:
        - name: test
          azureDisk:
            kind: Managed
            diskURI: /subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/MC_usp-live-origin_usp-live-origin-cluster_northeurope/providers/Microsoft.Compute/disks/live-origin-assets
            diskName: live-origin-assets

Como ves, la imagen a utilizar es unifiedstreaming/live:latest, la cual ya está preconfigurada para el modo live. Es importante definir las variables de entorno, donde USP_LICENCE_KEY cogerá el valor del secreto creado anteriormente, el nombre del canal a través de CHANNEL y PUB_POINT_OPTS que son todas las opciones que estamos habilitando para este entorno de pruebas. Puedes obtener más información sobre todas ellas aquí. Por último, montamos el path /var/www/live/test utilizando el Azure Disk que has creado, utilizando la URI que devolvió el comando tras su creación y el nombre del disco, que en este ejemplo es live-origin-assets.

Por otro lado, este sería el servicio de tipo Load Balancer, para que AKS nos proporcione una IP pública:

apiVersion: v1
kind: Service
metadata:
  name: live-origin-test
spec:
  type: LoadBalancer
  selector:
    name: live-origin
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Nota: Este no es el escenario recomendado para un entorno productivo, pero nos sirve para la prueba que estamos realizando ahora mismo.

Para crear ambos recursos, si has descargado el código de mi GitHub puedes hacerlo utilizando este comando:

kubectl apply -f .

Ahora ya tienes todo listo para comenzar a ingestar el contenido que quieres transmitir en directo.

Ingesta de contenido

Para hacer un ejemplo sencillo, nuestros amigos de Unified Streaming Platform han puesto a nuestra disposición este repositorio que permite la ingesta del siguiente contenido:

Live Media Ingest (CMAF)

Este nos permite ver sobre todo el retardo en el Live de una forma súper clara. En mi ejemplo, para ejecutarlo contra mi entorno en AKS me he descargado el código y he modificado el archivo docker-compose.yaml de la siguiente manera:

version: "2.1"
services:
  # live-origin:
  #   image: unifiedstreaming/live:latest
  #   ports:
  #     - 80:80
  #   environment:
  #     - USP_LICENSE_KEY
  #     - CHANNEL=test
  #     - LOG_LEVEL=warn
  #     - PUB_POINT_OPTS=--archiving=1 --archive_length=3600 --archive_segment_length=1800 --dvr_window_length=30 --restart_on_encoder_reconnect --mpd.min_buffer_time=48/25 --mpd.suggested_presentation_delay=48/25 --hls.minimum_fragment_length=48/25 --mpd.minimum_fragment_length=48/25 --mpd.segment_template=time --hls.client_manifest_version=4 --hls.fmp4
  #   healthcheck:
  #     test: kill -0 1
  #     interval: 2s
  #     timeout: 5s
  #     retries: 30
  ffmpeg-1:
    build: ffmpeg
    environment:
      - PUB_POINT_URI=http://<AKS_SERVICE_PUBLIC_IP>/test/test.isml
      - 'TRACKS={ "video": [ { "width": 1024, "height": 576, "bitrate": "500k", "codec": "libx264", "framerate": 25, "gop": 24, "timescale": 50 }, { "width": 1280, "height": 720, "bitrate": "1000k", "codec": "libx264", "framerate": 50, "gop": 48, "timescale": 50 } ], "audio": [ { "samplerate": 48000, "bitrate": "64k", "codec": "aac", "language": "eng", "timescale": 48000 }, { "samplerate": 48000, "bitrate": "64k", "codec": "aac", "language": "dut", "timescale": 48000 } ] }'
  #   depends_on:
  #     live-origin:
  #       condition: service_healthy
  # ffmpeg-2:
  #   build: ffmpeg
  #   environment:
  #     - PUB_POINT_URI=http://live-origin/test/test.isml
  #     - 'TRACKS={ "video": [ { "width": 1024, "height": 576, "bitrate": "500k", "codec": "libx264", "framerate": 25, "gop": 24, "timescale": 50 }, { "width": 1280, "height": 720, "bitrate": "1000k", "codec": "libx264", "framerate": 50, "gop": 48, "timescale": 50 } ], "audio": [ { "samplerate": 48000, "bitrate": "64k", "codec": "aac", "language": "eng", "timescale": 48000 }, { "samplerate": 48000, "bitrate": "64k", "codec": "aac", "language": "dut", "timescale": 48000 } ] }'
  #   depends_on:
  #     live-origin:
  #       condition: service_healthy

De esta forma apunto a mi entorno en AKS y no al live-origin que originalmente viene con ese entorno de pruebas. Para ejecutarlo basta que utilices el siguiente comando (necesitas tener Docker instalado en local):

docker-compose up

Y si todo ha ido bien deberías de ver la siguiente salida:

Ouput de la ingesta a través del repo de live-demo-cmaf

A partir de este momento tu despliegue de USP Live en AKS ya está recibiendo video para la retransmisión en vivo.

Cómo probarlo

Para probar DASH y HLS puedes hacer uso de ffplay ejecutando estos dos comandos:

#MPEG-DASH
ffplay http://<AKS_SERVICE_PUBLIC_IP>:80/test/test.isml/.mpd

#Apple's HLS
ffplay http://<AKS_SERVICE_PUBLIC_IP>:80/test/test.isml/.m3u8

Para Smooth Streaming puedes usar este otro enlace: http://reference.dashif.org/dash.js/v3.2.2/samples/dash-if-reference-player/index.html?url=http://AKS_SERVICE_PUBLIC_IP/test/test.isml/Manifest

Además, tienes tres endpoints para hacer consultas relacionadas con esta publicación:

#State of the publishing endpoint
curl http://<AKS_SERVICE_PUBLIC_IP>/test/test.isml/state

#Stadistics
curl http://<AKS_SERVICE_PUBLIC_IP>/test/test.isml/statistics

#Archive
curl http://<AKS_SERVICE_PUBLIC_IP>/test/test.isml/archive

El código del ejemplo lo tienes en mi GitHub.

¡Saludos!