Video broadcasting and recording with Cisco. Annex 1. HLS

HLS (HTTP Live Streaming) is a live video broadcasting protocol by Apple. It’s current standartization status is IETF draft. It splits incoming stream coded with MPEG (H.264 и AAC) into little chunks (*.ts files) and forms a playlist (*.m3u8) which is served to users. When a users queries for a stream his client (browser) start a simple file download process chunk by chunk. Visit https://developer.apple.com/streaming/ for more information.
This protocol is supported by Safari for MAC and IOS, stock browser and Chrome for Android. Visit http://www.jwplayer.com/html5/hls/ for more info. On a desktop with Windows or Linux you can use VLC player to open m3u8.

Advantages:
– pure HTML5: <video src=’http://<server name>/playlist.m3u8′ width=’640′ height=’360′ controls=’controls’></video> – and that’s it.
– there is no need for a browser plugins, especially so vunerable as flash
– supports AES and adaptive bitrate.

In our case we need another configuration for Wowza and nginx retranslators.

1) For Wowza create a Live origin app (HTTP) and edit /usr/local/WowzaMediaServer/conf/live/Application.xml

<HTTPStreamer>
<Properties>
    <Property> 
        <Name>httpOriginMode</Name> 
        <Value>on</Value> 
    </Property> <!-- Apple HLS: cache control --> 
    <Property> 
        <Name>cupertinoCacheControlPlaylist</Name>
        <Value>max-age=1</Value>
    </Property>
    <Property> 
        <Name>cupertinoCacheControlMediaChunk</Name> 
        <Value>max-age=3600</Value> 
    </Property> <!-- Smooth Streaming: cache control --> 
    <Property> 
        <Name>smoothCacheControlPlaylist</Name> 
        <Value>max-age=1</Value> 
    </Property> 
    <Property> 
        <Name>smoothCacheControlMediaChunk</Name> 
        <Value>max-age=3600</Value> 
    </Property> 
    <Property> 
        <Name>smoothCacheControlDataChunk</Name> 
        <Value>max-age=3600</Value>
    </Property> <!-- Flash HDS: cache control --> 
    <Property> 
        <Name>sanjoseCacheControlPlaylist</Name> 
        <Value>max-age=1</Value> 
    </Property> 
    <Property> 
        <Name>sanjoseCacheControlMediaChunk</Name> 
        <Value>max-age=3600</Value> 
    </Property> 
    <Property> 
        <Name>cupertinoOnChunkStartResetCounter</Name> 
        <Value>true</Value> 
        <Type>Boolean</Type> 
    </Property>
</Properties>
</HTTPStreamer>

2) And convert nginx to a simple proxy server

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
proxy_cache_path /tmp/hls_cache levels=1:2 keys_zone=pcache:1024m max_size=10048m inactive=10d use_temp_path=off;
client_header_timeout 10m;
client_body_timeout 10m;
send_timeout 10m;
connection_pool_size 256;
client_header_buffer_size 1k;
large_client_header_buffers 4 2k;
request_pool_size 4k;
keepalive_timeout  65;
        server {
                listen 1936;
                server_name <server name>:1936;
                location / {
                        types {
                            application/vnd.apple.mpegurl m3u8;
                        }
                        proxy_cache pcache;                        
                        proxy_pass http://<wowza ip>:1935;
                }
                }
}

3) Now you can use your favorite player (flowplayer would work too). And serve users your broadcast.

Video broadcasting and recording with Cisco part 5. flowplayer

Now let’s install web-server which will serve a web-page with flowplayer which will display our live broadcast. We will instruct our users to open link like player.example.com/?stream=1002 in order to start browsing the broadcast.

1) Let’s install Apache and WSGI

# sudo apt-get update
# sudo apt-get upgrade
# sudo apt-get install apache2
# sudo apt-get install libapache2-mod-wsgi

2) Install Flask framework

# wget https://bootstrap.pypa.io/get-pip.py
# python get-pip.py
# python -m pip install flask

3) Inside /var/www/html create wsgi and put player.wsgi inside

#player.wsgi
 import sys
 sys.path.append('/var/www/html/wsgi')
 from player import app as application

4) Inside wsgi folder create player.py

# -*- coding: utf-8 -*-
from flask import Flask, jsonify, render_template, request, redirect, url_for
app = Flask(__name__)
import httplib
import urllib2
import xml.etree.ElementTree as ET


def get_nclients(host):
    try:
        passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
        passman.add_password(None, 'http://'+host+':8080', '', '')
        authhandler = urllib2.HTTPDigestAuthHandler(passman)
        opener = urllib2.build_opener(authhandler)
        res = opener.open('http://'+host+':8080/stat',timeout=2)
        response = res.read()
        res.close()
        summ=0
        tree = ET.fromstring(response)        
        serv=tree.find('server')
        #print serv[0][0].text
        #print serv[1][0].text
        for app in serv:
            for stream in app[1]:
                for clients in stream.findall('nclients'):
                    summ=summ+int(clients.text)
        return summ    
    except:
        return 999

def streams(host):
    result=[]
    try:
        url = 'http://'+host+':8086/connectioncounts'
        mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
        mgr.add_password(None,url,'***','***')
        opener = urllib2.build_opener(urllib2.HTTPBasicAuthHandler(mgr), urllib2.HTTPDigestAuthHandler(mgr))
        urllib2.install_opener(opener)
        f = urllib2.urlopen(url,timeout=2)
        tree = ET.fromstring(f.read())
        vhost = tree.find('VHost')
        for stream in vhost.iter('Stream'):
            result.append(stream.find('Name').text)
    except:
        result=[]
    return result

@app.route("/")
def hello():
    stream=request.args.get('stream')
    rtlrs=['192.168.10.1','192.168.11.1','192.168.12.1']
    rtlr_found=False
    while not rtlr_found:
               temp=[]
               for i in rtlrs:           
                    nclients=get_nclients(i)
                    if nclients

Logic behind this script is following.

a) When users visit our url first of all we get stream number they are looking for with stream=request.args.get(‘stream’) inside hello function

b) Assume we have a number of nginx retranslators [‘192.168.10.1′,’192.168.11.1′,’192.168.12.1’]. Let’s query them for a number of connected users and choose least loaded one. get_nclients function will help us with it. If there are more that 700 users connected to a certain retranslator we won’t use it.

c) We also need to be sure that stream is present as Wowza server, so let’s query it for all it streams with streams function. If there is no stream let’s inform users about it. Do not forget to change Wowza admin passwords inside this function.

d) Finally generate index.html and pass url for a stream to it.

5) Index.html will look like that (don’t forget to delete hashtags in the beginning of every line):

#<!DOCCTYPE html>
#<html><head>
#<meta http-equiv="content-type" content="text/html; charset=UTF-8">
#<meta charset="utf-8">
#<head>
# <link rel="stylesheet" href="//releases.flowplayer.org/6.0.1/skin/minimalist.css">
# //code.jquery.com/jquery-1.11.2.min.js
# //releases.flowplayer.org/6.0.1/flowplayer.min.js
#</head>
#<title>Player</title>
#</head>
#<body>
#
#toggle video #
# # # #
#</div> #</body> #</html>

6) And errors.html like that (don’t forget to delete hashtags in the beginning of every line):

#<!DOCCTYPE html>
#<html><head>
#<meta http-equiv="content-type" content="text/html; charset=UTF-8">
#<meta charset="utf-8">
#<title>Player</title>
#</head>
#<body>
#<span id="debug">{{error}} </span>
#</body>
#</html>

7) Final step is to edit virtual hosts in Apache config at /etc/apache2/sites-enabled/000-default.conf (don’t forget to delete hashtags in the beginning of every line):

     #<VirtualHost *>
     #ServerName 
     #DocumentRoot /var/www/html
     #WSGIDaemonProcess player
     #WSGIScriptAlias / /var/www/html/wsgi/player.wsgi
     #<Directory /var/www/wsgi>
         WSGIProcessGroup player
         WSGIApplicationGroup %{GLOBAL}
         Order deny,allow
          Allow from all
     #</Directory>

8) Restart Apache with service apache2 restart and you are ready to go. In case of ploblems with flowplayer consult http://demos.flowplayer.org/