├── README.md ├── nginx └── nginx.conf ├── stream.ini ├── stream.py ├── templates └── player.html └── wsgi.py /README.md: -------------------------------------------------------------------------------- 1 | # html5-livestreaming 2 | 3 | Requires https://github.com/dailymotion/hls.js/ 4 | 5 | There's a Flask route to serve static JS content from a subdirectory assumed to be named 'dist' containing either hls.js or hls.min.js, but any method of serving static content would work fine. 6 | 7 | Set the `hostname` variable in `stream.py` to the appropriate value 8 | 9 | Note: this is basically the python part of https://github.com/Nesseref/nginx-rtmp-auth plus a bit more Flask to template pages and load the JavaScript HLS player with the correct media location. 10 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | rtmp { 2 | server { 3 | listen 1935; 4 | chunk_size 4000; 5 | 6 | application hls { 7 | live on; 8 | hls on; 9 | hls_path /tmp/hls; 10 | on_publish http://localhost/auth; 11 | notify_method get; 12 | } 13 | } 14 | } 15 | 16 | http { 17 | server { 18 | listen 80; 19 | 20 | location / { 21 | include uwsgi_params; 22 | uwsgi_pass unix:/tmp/stream.sock; 23 | } 24 | 25 | 26 | location /hls { 27 | types { 28 | application/vnd.apple.mpegurl m3u8; 29 | video/mp2t ts; 30 | } 31 | root /tmp; 32 | add_header Cache-Control no-cache; 33 | } 34 | 35 | } 36 | } 37 | 38 | events { worker_connections 1024; } 39 | -------------------------------------------------------------------------------- /stream.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | module = wsgi 3 | 4 | master = true 5 | processes = 5 6 | socket = /tmp/stream.sock 7 | chmod-socket = 666 8 | vacuum = true 9 | -------------------------------------------------------------------------------- /stream.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, render_template, send_from_directory 2 | from flask.ext.api import status 3 | import psycopg2 4 | import config 5 | import requests 6 | 7 | app = Flask(__name__) 8 | 9 | hostname = 'http://[hostname]/hls/' 10 | 11 | @app.route('/auth') 12 | def auth(): 13 | if request.args.get('name') is None or request.args.get('swfurl') is None: 14 | return 'Malformed request', status.HTTP_400_BAD_REQUEST 15 | 16 | username = request.args.get('name') 17 | idhash = request.args.get('swfurl').split("?")[-1] 18 | 19 | conn = psycopg2.connect(database=config.database, user=config.user, password=config.password, host=config.host) 20 | cur = conn.cursor() 21 | cur.execute("SELECT * FROM users WHERE username=%s AND idhash=%s", (username, idhash)) 22 | 23 | if len(cur.fetchall()) == 0: 24 | return 'Incorrect credentials', status.HTTP_401_UNAUTHORIZED 25 | 26 | return 'OK', status.HTTP_200_OK 27 | 28 | @app.route('/dist/') 29 | def serve_js(path): 30 | return send_from_directory('dist', path) 31 | 32 | @app.route('/') 33 | def serve_player(username): 34 | conn = psycopg2.connect(database=config.database, user=config.user, password=config.password, host=config.host) 35 | cur = conn.cursor() 36 | cur.execute("SELECT * FROM users WHERE username=%s", (username,)) 37 | if len(cur.fetchall()) == 0: 38 | return "No such user!" 39 | else: 40 | r = requests.get(hostname + username + ".m3u8") 41 | if r.status_code == requests.codes.ok: 42 | return render_template('player.html', username=username, hostname=hostname); 43 | else: 44 | return "Stream offline!" 45 | 46 | if __name__== '__main__': 47 | app.run(host='0.0.0.0') 48 | -------------------------------------------------------------------------------- /templates/player.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | html5 rtmp demo 6 | 7 | 8 | 9 |

{{ username }}'s stream

10 | 11 | 12 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | from stream import app as application 2 | 3 | if __name__ == "__main__": 4 | application.run() 5 | --------------------------------------------------------------------------------