RTMP Tutorial - python rtmp app

As I described in my introduction page I wanted to live stream output from my GoPro, but as I do not have thousands of followers on YouTube and I avoid Facebook like the plague it left me with few other easy and affordable options.

So I did it myself.
In this section of my nginx, rtmp, oauth live streaming server setup I describe the python app I put together to manage the streams, the user authentication and the web pages. It is a python app that uses flask as its web framework and relies upon Nextcloud as its OAuth server. It is not intended to be a standalone application that you can just download and run, but instead is a tutorial/template that you can use to design your own app.

I put it on Github too.

Section 3: Designing an application to glue it together

To glue all the components together nginx needs a little assistance from an application. The role of the application is to

  • provide a web page that lists available streams
  • supply the client with a Javascript player
  • control the user session information and validate the user decryption key requests against an OAuth server
  • provide the URL endpoints that nginx will use to announce the start of a stream, the end of a stream

The application I slapped together is a simple python (version 3) app that uses Flask as its web framework and relies on redis for session management and OAuth for user authentication. The streams published to the app however do not require authorization. I figured anyone publishing a stream would end up with an encrypted stream that only my users could watch. Not too useful or annoying. Clear streams however I restrict to only internal publishers., not rando Internet people. Authorizing the publish point I will leave for someone else to worry about. Perhaps just add a secret token to the https URL.

From a previous post about the nginx config I showed the encrypted publish endpoint, /live, doing three things with the python application:

  1. Tell the python app that a new stream has started via “on_publish http://127.0.0.1:8000/start_stream;”
  2. Prepare to tell the python app when a stream stops via “on_publish_done http://127.0.0.1:8000/stop_stream;”
  3. Validate the user access to the decryption key using the http://127.0.0.1:8000/authorize_key URL.

All told, the application API includes these endpoints as well as some to simply manage the stream lists, log the user in and out via OAuth and a few housekeeping URLs:

    /
        Method:     GET
        Parameters: None
        Response:   Redirect to /home

    /home
        Method:     GET
        Parameters: session data if logged in, otherwise none
        Response:   Rendered html template file with contents dependant upon valid session
                    For example, login button is not logged in, logout otherwise
        
    /login
        Method:     GET
        Parameters: None, but if a valid session exists, redirect to /home
        Response:   Redirect to OAuth server for login validation.  If session already
                    exists, simply redirect to /home endpoint.

    /logout
        Method:     GET
        Parameters: None
        Response:   Invalidates the session redirects to the home page.  Note: this does not
                    invalidate any cookie/session info setup by the OAuth server itself, so
                    do not think of this as logging out of the OAuth server, only logging out 
                    of the python app.  Clicking the login button again will likely automoatically
                    log the user in if the OAuth server information is cached in its own cookie
                    or session information.  Some OAuth servers have an endpoint to invalidate
                    a user login session, but most don't.

    /callback
        Method:     GET
        Parameters: OAuth parameters
        Response:   Process OAuth parameters, validated by the OAuth server, and store token in
                    user session.

    /streams
        Method:     GET
        Parameters: None
        Response:   If not authenticated, list of clear streams, otherwise if authenticated session.
                    return list of all streams.  Response structure is list of JSON data of the format:
                    
                        [{"name":"Stream Name","url":"https://stream_url/index.m3u8"},
                         {"name":"Another Stream (public)","url":"https://clear_stream_url/index.m3u8"}]

                    If the stream is publicly available with no encryption, its name contains (public).

                    This API is used by the Javascript application.

    /start_stream
        Method:     POST
        Parameters: Form with stream key passed as "name"
        Response:   OK
                    Used by nginx to push new stream to list of active streams.

    /stop_stream
        Method:     POST
        Parameters: Form with stream key passed as "name"
        Response:   OK
                    Used by nginx to announce end of stream and update list of active streams.

    /authorize_key
        Method:     GET
        Parameters: User session
        Response:   OK if current user has a valid session as granted by the OAuth server.
                    401 if not authorized.

    /start_clear_stream
        Method:     POST
        Parameters: Form with stream key passed as "name"
        Response:   OK
                    Used by nginx to push new unencrypted stream to list of active streams.

    /version
        Method:     GET
        Parameters: None
        Response:   Application version

as well as static URLs for javascript player, favicon and such.

The source code for my application, along with some nginx config files and a supervisor startup file are located on Github at nginx-rtmp-server. The tv.py file implements the API I described above and the repository includes the redis caching code, web template files and the javascript player app. Feel free to copy/edit/modify/ignore any of the code as you see fit.


In the next section I will discuss my OAuth server setup, including the code in the python app and the Nextcloud server config.