Integrating Unified Streaming Platform with Bitmovin Live Encoder

Integrating a just-in-time packager in your live (or vod) streaming workflow can make a lot of sense if you plan on:

  • Publishing multiple manifest with different renditions
  • Publishing media with different segment length based on the same encoding
  • Change your DRM configuration on-the-fly
  • Change the DVR window of your live stream on-the-fly
  • Create on-the-fly live-to-vod playback manifests

And although most of this can be done using the Bitmovin Live Encoder and Bitmovin Manifest Generator, some companies still prefer a just-in-time packager.

Bitmovin Live encoder has had an integration with Unified Streaming Platform for many years, and the setup of both Bitmovin Live Encoding and Unified Streaming Platform is a relatively easy task.

In this example, we’ll go through how to deploy the Unified Streaming server on a Nanode 1 GB Ubuntu server running on Linode (https://www.linode.com) (but any cloud vendor will do) in Frankfurt and a Bitmovin Live Encoder running in Google Europe West 1 (Belgium) (but could also have been AWS Stockholm or Azure West Europe (Netherlands)).

Creating and configuring the Unified Streaming Server

First, we need to get a license for Unified Streaming Platform. Unified offers a 7-day trial version, which can be requested here https://www.unified-streaming.com/start.

Next, please go to Linode and create your Nanode 1 GB instance

Once the initial setup has been completed, I suggest you log in and update the virtual server.

Next, we need to install the Unified Streaming Software, by first adding the repository and keys, and next running the installation (based on How to Install — Unified Streaming)

#!/bin/bash
# Choose release name and architecture (note: 'arm64' only available on 'focal-ports' and 'jammy-ports')
release="jammy"
arch="amd64"
repo="stable"
echo "deb [arch=${arch}] https://${repo}.apt.unified-streaming.com ${release} multiverse" sudo tee /etc/apt/sources.list.d/unified-streaming.list

#Downloading and adding key
wget https://stable.apt.unified-streaming.com/unifiedstreaming.pub
apt-key add unifiedstreaming.pub

#Updating repository
apt update

#Installing software
apt install -y mp4split manifest-edit apache2 libapache2-mod-smooth-streaming

Next, we need to do some minor configuration changes (based on https://docs.unified-streaming.com/installation/origin/apache.html#basic-apache-configuration-on-linux)

#Disabling the MPM Event model
a2dismod mpm_event

#Enabling the MPM Worker model
a2enmod mpm_worker

#Adding the module from USP
a2enmod mod_smooth_streaming

#Restarting apache to fetch updated modules
systemctl restart apache2

#Just check that Apache is running
systemctl status apache2

Next we need to store the license received from Unified Streaming. I choose to store it in /var/usp:

-> % mkdir -p /var/usp
-> % echo "the-received-key" > /var/usp/usp.key

If you prefer, you can quickly check the validity of the USP key:

-> % mp4split --license-key=/var/usp/usp.key --show-license
mp4split version=1.12.1 (28247) Copyright 2007-2023 CodeShop B.V.Product name: Unified Streaming Platform
License type: evaluation
License email: -removed-
Starting at: -removed-
Expiring at: -removed-
Supported features:
Packaging: No
Capturing: No
Verifying: No
Streaming: VOD, Live
Remixing: No
Encoding: No
Decoding: No
Metadata: No
DRM: No
Virtual Channel: No

Next, we need to make changes to the configuration of virtual hosts for Apache. For simplicity I just use the default /etc/apache2/sites-available/000-default.conf to setup the virtual host configuration needed for Unified Streaming Platform.

UspLicenseKey /var/usp/usp.key

<VirtualHost *:80>
#ServerName www.example.com

ServerAdmin webmaster@localhost
DocumentRoot /var/www/html

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

<Location />
UspHandleIsm on
</Location>

<LocationMatch ".*\.isml\/(.*\.mpd .*\.m3u8 Manifest)$">
Header setifempty Cache-Control "max-age=2"
</LocationMatch>

<LocationMatch "\.(ismv isma ismt)$">
Require all denied
</LocationMatch>

# Block access to server manifests when the module is loaded
AddHandler smooth-streaming.extensions .ism .isml

# For when the module is not loaded
<IfModule !smooth_streaming_module>
<LocationMatch "\.isml?$">
Require all denied
</LocationMatch>
</IfModule>

# Necessary for Media Source Extensions (MSE)
Header always set Access-Control-Allow-Headers "origin, range"
Header always set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Expose-Headers "Server,range"

</VirtualHost>

After restarting Apache

-> % systemctl restart apache2

We can check for possible errors, and that the USP license key has been found:

[Tue Mar 21 11:20:21.223666 2023] [smooth_streaming:notice] [pid 5586:tid 139717325571968] License key found: /var/usp/usp.key
[Tue Mar 21 11:20:21.230453 2023] [mpm_worker:notice] [pid 5586:tid 139717325571968] AH00292: Apache/2.4.52 (Ubuntu) USP/1.12.1 IISMS/4.0 OpenSSL/3.0.2 configured -- resuming normal operations

Next, we are ready to create the storage folder and USP configuration for our live channel

#Create a folder to store the ingested media
-> % mkdir -p /var/www/html/channel1

#Create the Unified Streaming ingest configuration
-> % mp4split --license-key=/var/usp/usp.key -o /var/www/html/channel1/channel1.isml \
--archiving=true \
--archive_length=3600 \
--archive_segment_length=300 \
--dvr_window_length=60 \
--restart_on_encoder_reconnect \
--hls.client_manifest_version=4 \
--hls.no_multiplex \
--hls.fmp4 \
--fixed_gop=48/25 \
--hls.minimum_fragment_length=48/25 \
--mpd.minimum_fragment_length=48/25 \
--mpd.segment_template=time

mp4split version=1.12.1 (28247) Copyright 2007-2023 CodeShop B.V.

I0.167 Manifest file:///var/www/html/channel1.isml
I0.168 writing 1 buckets for a total of 1075 bytes
Status: 200 FMP4_OK

#Change owner on the folder
-> % chown -R www-data:www-data /var/www/html/channel1/

And we can check the current state of the live channel:

-> % curl http://localhost/channel1/channel1.isml/state
<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Unified Streaming Platform (version=1.12.1-28247) -->
<smil
xmlns="http://www.w3.org/2001/SMIL20/Language">
<head>
<meta
name="updated"
content="2023-03-21T11:24:47.011845Z">
</meta>
<meta
name="state"
content="idle">
</meta>
</head>
</smil>

Preparations on the Unified Streaming Platform server is done, and when you have confirmed that the server can be reached via HTTP, we are ready to create and start the live encoding.

Creating the Bitmovin Live Encoder

To use Bitmovin Live Encoder need a Bitmovin trial (or subscription). You can sign up for a trial here Bitmovin Dashboard

Setting up Live Media Ingest as output from the encoder is only possible via the API, so setting up the Bitmovin Live Encoder will be based on the examples we provide here GitHub - bitmovin/bitmovin-api-sdk-examples: Set of encoding workflow examples highlighting the use of the Bitmovin API SDKs - more specifically I’ll be creating a modified version of https://github.com/bitmovin/bitmovin-api-sdk-examples/blob/main/java/src/main/java/RtmpLiveEncoding.java

The main changes is that we will do is

  1. Create a Live Media Ingest output
  2. Remove the manifests created by the live encoder
  3. Optimize the segment length for lower latency

Instead of using the current S3 output we will create a new Live Media Ingest output:

LiveMediaIngestOutput lmiOutput = new LiveMediaIngestOutput();
lmiOutput.setName("Community Post - usp-01-ingest.domain.tld");
lmiOutput.setDescription("Community Post - output to Unified Streaming Platform");
lmiOutput.setPublishingPoint(
"http://usp-01-ingest.domain.tld/channel1/channel1.isml");

output = bitmovinApi.encoding.outputs.liveMediaIngest.create(lmiOutput);

Naturally, you can also choose to use the IP address of the server. The path to the channel should match your setup on the Unified Streaming server.

In the java example code we also need to remove the manifest from the script - this is done in line 149-156 (https://github.com/bitmovin/bitmovin-api-sdk-examples/blob/af28ad8d347cfc1390ebfcfd93b7b5b1efa3f1f0/java/src/main/java/RtmpLiveEncoding.java#L149) and line 159 and 160 (https://github.com/bitmovin/bitmovin-api-sdk-examples/blob/af28ad8d347cfc1390ebfcfd93b7b5b1efa3f1f0/java/src/main/java/RtmpLiveEncoding.java#L159)

Next, in the FMP4 muxing I will update the segment length to use 1.92 seconds (I will be ingesting 25 FPS and 48kHz audio, and using segment length of 1.92 seconds will align video and audio segments). So instead of 4.0 seconds I specify 1.92 seconds in line 372 https://github.com/bitmovin/bitmovin-api-sdk-examples/blob/af28ad8d347cfc1390ebfcfd93b7b5b1efa3f1f0/java/src/main/java/RtmpLiveEncoding.java#L372

Once these changes have been made, you are ready to run the java code, and once the live encoder is up and running, you should see this information:

[main] INFO RtmpLiveEncodingUSPOrigin - Live encoding is up and ready for ingest. RTMP URL: rtmp://-ip-address-/live StreamKey: livestream

I use OBS to ingest live streams - so entering the information and “Start Streaming”.

Checking the status of the live encoding in the Bitmovin Dashboard shows that it is up and running

Checking the status of the channel on Unified Streaming Server also shows that it is running, and that media is being saved on the server, ready to be streamed

curl http://localhost/channel1/channel1.isml/state
<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Unified Streaming Platform (version=1.12.1-28247) -->
<smil
xmlns="http://www.w3.org/2001/SMIL20/Language">
<head>
<meta
name="updated"
content="2023-03-21T12:27:20.393066Z">
</meta>
<meta
name="state"
content="started">
</meta>
</head>
</smil>
root@localhost:/var/www/html/channel1# ls -ltra
total 236056
drwxr-xr-x 3 root root 4096 Mar 21 11:23 ..
-rw-r--r-- 1 www-data www-data 4380 Mar 21 12:26 channel1.isml
drwxr-xr-x 2 www-data www-data 4096 Mar 21 12:26 .
-rw-r--r-- 1 www-data www-data 9734853 Mar 21 12:29 video-500K.ismv
-rw-r--r-- 1 www-data www-data 16285685 Mar 21 12:29 video-800K.ismv
-rw-r--r-- 1 www-data www-data 23277630 Mar 21 12:29 video-1100K.ismv
-rw-r--r-- 1 www-data www-data 59847325 Mar 21 12:29 video-2500K.ismv
-rw-r--r-- 1 www-data www-data 129536718 Mar 21 12:29 video-5000K.ismv
-rw-r--r-- 1 www-data www-data 69632 Mar 21 12:29 channel1.db3
-rw-r--r-- 1 www-data www-data 2940264 Mar 21 12:29 audio-128K.ismv

And we can check that we can get a manifest from the server:

-> % curl http://usp-01-egress.domain.tld/channel1/channel1.isml/stream.mpd
<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Unified Streaming Platform (version=1.12.1-28247) -->
<MPD
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="urn:mpeg:dash:schema:mpd:2011"
xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
type="dynamic"
availabilityStartTime="1970-01-01T00:00:00Z"
publishTime="2023-03-21T12:30:53.900987Z"
minimumUpdatePeriod="PT2S"
timeShiftBufferDepth="PT1M"
maxSegmentDuration="PT2S"
minBufferTime="PT10S"
profiles="urn:mpeg:dash:profile:isoff-live:2011">
<Period
id="1"
start="PT0S">
<BaseURL>dash/</BaseURL>

And naturally playback is also possible. Glass-to-glass latency in this example (through CDN) is approximately 17 seconds:

Some of the features of using a just-in-time packager is to be able to dynamically modify the manifest/streaming output.

In the above example, the DVR buffer is 60 seconds (as set in the mp4split command --dvr_window_length=60), however, if I would like to have a different DVR window, I can alter my manifest URL to look like:

https://usp-01-egress.bitmovin.domain.tld/channel1/channel1.isml/stream.mpd?dvr_window_length=300

And I would automatically get a DVR window length of 5 mins:

An alternative would be to create a live-to-vod manifest on-the-fly. This could be done by modifying the manifest URL to look like:

https://usp-01-egress.bitmovin.domain.tld/channel1/channel1.isml/stream.mpd?t=1970-01-01T00:03:00.000-1970-01-01T00:10:00.000

Note: Currently Bitmovin Live Encoder does not support sending through timecodes from source or time-of-day. This is on our roadmap, and once done enables the option of live origin redundancy Recommendations for Live — Unified Streaming

Which would give us a 7-minute VOD item ready for playback:

I hope this helps.

If you don’t already have a Bitmovin account, sign up for a free trial on our website to check out this and other cool features of Bitmovin Live and VOD Encoder.

1 Like