What is the Python FFmpeg Video Streaming?

This package uses the FFmpeg to package media content for online streaming such as DASH and HLS. You can also use DRM for HLS packaging. There are several options to open a file from a cloud and save files to clouds as well.

Github Repository: https://github.com/quasarstream/python-ffmpeg-video-streaming

Bug Tracker and Issues: https://github.com/quasarstream/python-ffmpeg-video-streaming/issues

Installation

Step One: Requirement

    • FFMpeg

    • This Library requires a working FFmpeg. You will need both FFMpeg and FFProbe binaries to use it.

      Getting FFMpeg

      Useful Tip For Windows Users:

      For binary detection, you need to add FFmpeg and FFprobe paths to your system path

Step Two: Installing the Package

This version of the package is only compatible with Python 3.7 or higher.

Install the package via pip:

pip install python-ffmpeg-video-streaming

Alternatively, add the dependency directly to your requirements.txt file:

python-ffmpeg-video-streaming>=0.1

Save 200 hours of development for just $78. Focus on features of your OTT platform instead of writing boilerplate code.
Included Features:
  • User Authentication
  • User Access Control
  • Video On-Demand
  • HLS Encryption
  • Video Quality Settings
  • Real-Time Progress Monitoring
  • User Management
  • Video Player with quality selector
  • Theme(dark and light)
Project information
  • Backend Python - Django v5
  • Frontend Javascript ES6 - React v18
  • Container Docker
  • Get it now

Usage

First of all, you need to import the package in your code:

import ffmpeg_streaming

Opening a Resource

There are several ways to open a resource.

1. From an FFmpeg supported resource

You can pass a local path of video(or a supported resource) to the input method:

video = ffmpeg_streaming.input('/var/media/video.mp4')

See FFmpeg Protocols Documentation for more information about supported resources such as http, ftp, and etc.

For example:

video = ffmpeg_streaming.input('https://www.quasarstream.com/?"PATH TO A VIDEO FILE" or "PATH TO A LIVE HTTP STREAM"')
2. From a Cloud

You can open a file from a cloud by passing an instance of the Cloud object to the `input` method.

from ffmpeg_streaming import S3
s3 = S3(aws_access_key_id='YOUR_KEY_ID', aws_secret_access_key='YOUR_KEY_SECRET', region_name='YOUR_REGION')

video = ffmpeg_streaming.input(s3, bucket_name="bucket-name", key="video.mp4")

Visit this page to see some examples of opening a file from Amazon S3, Google Cloud Storage, Microsoft Azure Storage, and a custom cloud.

3. Capture Webcam or Screen (Live Streaming)

You can pass the name of a supported, connected capture device(i.e. the name of a webcam, camera, screen and etc) to the input method to stream a live media over the network from your connected device.

video = ffmpeg_streaming.input('CAMERA NAME OR SCREEN NAME', capture=True)

NOTE:

To list the supported, connected capture devices, see FFmpeg Capture Webcam and FFmpeg Capture Desktop

DASH

Dynamic Adaptive Streaming over HTTP (DASH), also known as MPEG-DASH, is an adaptive bitrate streaming technique that enables high quality streaming of media content over the Internet delivered from conventional HTTP web servers. Similar to Apple's HTTP Live Streaming (HLS) solution, MPEG-DASH works by breaking the content into a sequence of small HTTP-based file segments, each segment containing a short interval of playback time of content that is potentially many hours in duration, such as a movie or the live broadcast of a sports event. The content is made available at a variety of different bit rates, i.e., alternative segments encoded at different bit rates covering aligned short intervals of playback time. While the content is being played back by an MPEG-DASH client, the client uses a bit rate adaptation (ABR) algorithm to automatically select the segment with the highest bit rate possible that can be downloaded in time for playback without causing stalls or re-buffering events in the playback. The current MPEG-DASH reference client dash.js offers both buffer-based (BOLA) and hybrid (DYNAMIC) bit rate adaptation algorithms. Thus, an MPEG-DASH client can seamlessly adapt to changing network conditions and provide high quality playback with fewer stalls or re-buffering events.

MPEG-DASH is the first adaptive bit-rate HTTP-based streaming solution that is an international standard. MPEG-DASH should not be confused with a transport protocol — the transport protocol that MPEG-DASH uses is TCP. MPEG-DASH uses existing HTTP web server infrastructure that is used for delivery of essentially all World Wide Web content. It allows devices like Internet-connected televisions, TV set-top boxes, desktop computers, smartphones, tablets, etc. to consume multimedia content (video, TV, radio, etc.) delivered via the Internet, coping with variable Internet receiving conditions. Standardizing an adaptive streaming solution is meant to provide confidence to the market that the solution can be adopted for universal deployment, compared to similar but more proprietary solutions like Smooth Streaming by Microsoft, or HDS by Adobe. Unlike HLS, HDS, or Smooth Streaming, DASH is codec-agnostic, which means it can use content encoded with any coding format, such as H.265, H.264, VP9, etc. Learn more

Create DASH files:

It creates a MPD manifest file and segment files for streams automatically.

from ffmpeg_streaming import Formats

dash = video.dash(Formats.h264())
dash.auto_generate_representations()
dash.output('/var/media/dash.mpd')
Generate representations manually:

Specify the value of kilo bite rate and size for each stream explicitly.

from ffmpeg_streaming import Formats, Bitrate, Representation, Size

_144p  = Representation(Size(256, 144), Bitrate(95 * 1024, 64 * 1024))
_240p  = Representation(Size(426, 240), Bitrate(150 * 1024, 94 * 1024))
_360p  = Representation(Size(640, 360), Bitrate(276 * 1024, 128 * 1024))
_480p  = Representation(Size(854, 480), Bitrate(750 * 1024, 192 * 1024))
_720p  = Representation(Size(1280, 720), Bitrate(2048 * 1024, 320 * 1024))
_1080p = Representation(Size(1920, 1080), Bitrate(4096 * 1024, 320 * 1024))
_2k    = Representation(Size(2560, 1440), Bitrate(6144 * 1024, 320 * 1024))
_4k    = Representation(Size(3840, 2160), Bitrate(17408 * 1024, 320 * 1024))

dash = video.dash(Formats.h264())
dash.representations(_144p, _240p, _360p, _480p, _720p, _1080p, _2k, _4k)
dash.output('/var/media/dash.mpd')
Generate HLS Playlist(DASH and HLS):

Publish master playlist repeatedly every after specified number of segment intervals.

from ffmpeg_streaming import Formats

dash = video.dash(Formats.h264())
dash.auto_generate_representations([1080, 720, 480])
dash.generate_hls_playlist()
dash.output('/var/media/dash.mpd')
DASH from Camera/Screen:

You can go on live video streaming using a supported, connected capture devices such as a camera/webcam or screen. This uses the camera on your local machine not a webcam from the web.

from ffmpeg_streaming import Formats, Bitrate, Representation, Size

_240p  = Representation(Size(426, 240), Bitrate(150 * 1024, 94 * 1024))
_360p  = Representation(Size(640, 360), Bitrate(276 * 1024, 128 * 1024))

dash = video.dash(Formats.h264())
dash.representations(_240p, _360p)
dash.output('/var/media/dash.mpd')

NOTE:

You cannot use the "autoGenerateRepresentations()" method for opening media a from camera

NOTE:

See DASH options and DASH documentation for more information.

HLS

HTTP Live Streaming (also known as HLS) is an HTTP-based adaptive bitrate streaming communications protocol implemented by Apple Inc. as part of its QuickTime, Safari, OS X, and iOS software. Client implementations are also available in Microsoft Edge, Firefox and some versions of Google Chrome. Support is widespread in streaming media servers.

HLS resembles MPEG-DASH in that it works by breaking the overall stream into a sequence of small HTTP-based file downloads, each download loading one short chunk of an overall potentially unbounded transport stream. A list of available streams, encoded at different bit rates, is sent to the client using an extended M3U playlist.

Based on standard HTTP transactions, HTTP Live Streaming can traverse any firewall or proxy server that lets through standard HTTP traffic, unlike UDP-based protocols such as RTP. This also allows content to be offered from conventional HTTP servers and delivered over widely available HTTP-based content delivery networks. The standard also includes a standard encryption mechanism and secure-key distribution using HTTPS, which together provide a simple DRM system. Later versions of the protocol also provide for trick-mode fast-forward and rewind and for integration of subtitles.

Apple has documented HTTP Live Streaming as an Internet Draft (Individual Submission), the first stage in the process of publishing it as a Request for Comments (RFC). As of December 2015, the authors of that document have requested the RFC Independent Stream Editor (ISE) to publish the document as an informational (non-standard) RFC outside of the IETF consensus process. In August 2017, RFC8216 was published to describe version 7 of the protocol. Learn more

Create HLS files

It creates a playlist file, and one or more segment files automatically. The output filename specifies the playlist filename.

from ffmpeg_streaming import Formats

hls = video.hls(Formats.h264())
hls.auto_generate_representations()
hls.output('/var/media/hls.m3u8')
Generate representations manually:

Specify the value of kilo bite rate and size for each stream explicitly.

from ffmpeg_streaming import Formats, Bitrate, Representation, Size

_360p  = Representation(Size(640, 360), Bitrate(276 * 1024, 128 * 1024))
_480p  = Representation(Size(854, 480), Bitrate(750 * 1024, 192 * 1024))
_720p  = Representation(Size(1280, 720), Bitrate(2048 * 1024, 320 * 1024))

hls = video.hls(Formats.h264())
hls.representations(_360p, _480p, _720p)
hls.output('/var/media/hls.m3u8')
Fragmented Mp4:

Output segment files in fragmented MP4 format, similar to MPEG-DASH. fmp4 files may be used in HLS version 7 and above.

from ffmpeg_streaming import Formats

hls = video.hls(Formats.h264())
hls.auto_generate_representations([360, 240])
hls.fragmented_mp4()
hls.output('/var/media/hls.m3u8')
HLS Live Streaming from Camera/Screen:

You can go on live video streaming using a supported, connected capture devices such as a camera/webcam or screen. This uses the camera on your local machine not a webcam from the web.

from ffmpeg_streaming import Formats, Bitrate, Representation, Size

_480p  = Representation(Size(854, 480), Bitrate(750 * 1024, 192 * 1024))

hls = video.hls(Formats.h264(), hls_list_size=10, hls_time=5)
hls.flags('delete_segments')
hls.representations(_480p)
hls.output('/var/media/hls.m3u8')

NOTE:

You cannot use the "autoGenerateRepresentations()" method for opening a media from a camera

NOTE:

You cannot use HEVC and VP9 formats for HLS packaging except you are using HLS in fragmented MP4 format, then you can only use the HEVC format.

NOTE:

See HLS options and HLS documentation for more information.

Encryption(DRM)

The encryption process requires some kind of secret (key) together with an encryption algorithm. HLS uses AES in cipher block chaining (CBC) mode. This means each block is encrypted using the ciphertext of the preceding block. Learn more

You must specify a path to save a random key to your local machine and also specify a URL(or a path) to access the key on your website(the key you will save must be accessible from your website). You must pass both these parameters to the encryption method:

Single Key

The following code generates a key for all segment files.

from ffmpeg_streaming import Formats

#A path you want to save a random key to your local machine
save_to = '/home/public_html/"PATH TO THE KEY DIRECTORY"/key'

#A URL (or a path) to access the key on your website
url = 'https://www.quasarstream.com/?"PATH TO THE KEY DIRECTORY"/key'
# or url = '/"PATH TO THE KEY DIRECTORY"/key';

hls = video.hls(Formats.h264())
hls.encryption(save_to, url)
hls.auto_generate_representations()
hls.output('/var/media/hls.m3u8')


Key Rotation

An integer as a "key rotation period" can also be passed to the encryption method (i.e. encryption(save_to, url, 10)) to use a different key for each set of segments, rotating to a new key after this many segments. For example, if 10 segment files have been generated then it will generate a new key. If you set this value to 1, each segment file will be encrypted with a new encryption key. This can improve security and allows for more flexibility.

from ffmpeg_streaming import Formats

#A path you want to save a random key to your server
save_to = '/home/public_html/"PATH TO THE KEY DIRECTORY"/key'

#A URL (or a path) to access the key on your website
url = 'https://www.quasarstream.com/?"PATH TO THE KEY DIRECTORY"/key'
# or url = '/PATH TO THE KEY DIRECTORY/key';

hls = video.hls(Formats.h264())
hls.encryption(save_to, url, 10)
hls.auto_generate_representations([])
hls.output('/var/media/hls.m3u8')


DRM

However FFmpeg supports AES encryption for HLS packaging, which you can encrypt your content, it is not a full DRM solution. If you want to use a full DRM solution, I recommend trying FairPlay Streaming solution which then securely exchange keys, and protect playback on devices.

Besides Apple’s FairPlay, you can also use other DRM systems such as Microsoft's PlayReady and Google’s Widevine.

IMPORTANT:

It is very important to protect your key(s) on your website. For example, you can use a token(using a Get/Post HTTP method or add a token to authorization header) to check if the user is eligible to access the key or not. You can also use a session(or cookie) on your website to restrict access to the key(s)(It is highly recommended).

Transcoding

You can get realtime information about the transcoding using the following code.

from ffmpeg_streaming import Formats
import sys

def monitor(ffmpeg, duration, time_, time_left, process):
    """
    Handling proccess.

    Examples:
    1. Logging or printing ffmpeg command
    logging.info(ffmpeg) or print(ffmpeg)

    2. Handling Process object
    if "something happened":
        process.terminate()

    3. Email someone to inform about the time of finishing process
    if time_left > 3600 and not already_send:  # if it takes more than one hour and you have not emailed them already
        ready_time = time_left + time.time()
        Email.send(
            email='[email protected]',
            subject='Your video will be ready by %s' % datetime.timedelta(seconds=ready_time),
            message='Your video takes more than %s hour(s) ...' % round(time_left / 3600)
        )
       already_send = True

    4. Create a socket connection and show a progress bar(or other parameters) to your users
    Socket.broadcast(
        address=127.0.0.1
        port=5050
        data={
            percentage = per,
            time_left = datetime.timedelta(seconds=int(time_left))
        }
    )

    :param ffmpeg: ffmpeg command line
    :param duration: duration of the video
    :param time_: current time of transcoded video
    :param time_left: seconds left to finish the video process
    :param process: subprocess object
    :return: None
    """
    per = round(time_ / duration * 100)
    sys.stdout.write(
        "\rTranscoding...(%s%%) %s left [%s%s]" %
        (per, datetime.timedelta(seconds=int(time_left)), '#' * per, '-' * (100 - per))
    )
    sys.stdout.flush()

hls = video.hls(Formats.h264())
hls.auto_generate_representations([1080, 720, 480])
hls.output('/var/media/hls.m3u8', monitor=monitor)
Output From a Terminal:

Saving Files

There are several ways to save files.

1. To a Local Path

You can pass a local path to the `output` method. If there is no directory, then the package will create it.

from ffmpeg_streaming import Formats

dash = video.dash(Formats.h264())
dash.auto_generate_representations()

dash.output('/var/media/dash.mpd')

It can also be None. The default path to save files is the input directory.

from ffmpeg_streaming import Formats

hls = video.hls(Formats.h264())
hls.auto_generate_representations()

hls.output()

NOTE:

If you open a file from a cloud and do not pass a path to save the file to your local machine, you will have to pass a local path to the `output` method.

2. To a Cloud:

You can save your files to a cloud by passing an instance of the `CloudManager` object to the `output` method.

from ffmpeg_streaming import  S3, CloudManager

s3 = S3(aws_access_key_id='YOUR_KEY_ID', aws_secret_access_key='YOUR_KEY_SECRET', region_name='YOUR_REGION')
save_to_s3 = CloudManager().add(s3, bucket_name="bucket-name")

hls.output(clouds=save_to_s3)

Visit this page to see some examples of saving files to Amazon S3, Google Cloud Storage, Microsoft Azure Storage, and a custom cloud.

A path can also be passed to save a copy of files to your local machine.

hls.output('/var/media/hls.m3u8', clouds=save_to_s3)

NOTE:

This option(Save To Clouds) is only valid for VOD (it does not support live streaming).

Schema:

The relation is one-to-many

3. To a Server Instantly

You can pass a url(or a supported resource like `ftp`) to the `output` method to upload all the segments files to the HTTP server(or other protocols) using the HTTP PUT method, and update the manifest files every refresh times.

# DASH
dash.output('http://YOUR-WEBSITE.COM/live-stream/out.mpd')

# HLS
hls.save_master_playlist('/var/media/hls.m3u8')
#Before running the following code, you should upload the master playlist to the server. For example upload the '/var/media/hls.m3u8' to 'ftp://[user[:password]@]server[:port]/var/media/hls.m3u8'
hls.output('ftp://[user[:password]@]server[:port]/var/media/hls.m3u8')

NOTE:

In the HLS method, you must upload the master playlist to the server manually.

Metadata Extraction

You can get information from the video file using the following code.

from ffmpeg_streaming import FFProbe

ffprobe = FFProbe('/var/media/video.mp4')
current_dir = os.path.dirname(os.path.abspath(__file__))
ffprobe.save_as_json(os.path.join(current_dir, 'probe.json'))

all_media = ffprobe.all()

video_format = ffprobe.format()

streams = ffprobe.streams().all()
videos = ffprobe.streams().videos()
audios = ffprobe.streams().audios()

first_stream = ffprobe.streams().first_stream()
first_video = ffprobe.streams().video()
first_audio = ffprobe.streams().audio()

print("all:")
print(all_media)

print("format:")
print(video_format)

print("streams:")
print(streams)

print("videos:")
for video in videos:
    print(video)

print("audios:")
for audio in audios:
    print(audio)

print("first stream:")
print(first_stream)

print("first video:")
print(first_video)

print("first audio:")
print(first_audio)

print("duration: {}".format(str(datetime.timedelta(seconds=float(video_format.get('duration', 0))))))
# duration: 00:00:10.496

print("size: {}k".format(round(int(video_format.get('size', 0)) / 1024)))
# size: 290k

print("overall bitrate: {}k".format(round(int(video_format.get('bit_rate', 0)) / 1024)))
# overall bitrate: 221k

print("dimensions: {}x{}".format(first_video.get('width', "Unknown"), first_video.get('height', "Unknown")))
# dimensions: 480x270

print("video bitrate: {}k".format(round(int(first_video.get('bit_rate', 0)) / 1024)))
# video bitrate: 149k

print("audio bitrate: {}k".format(round(int(first_audio.get('bit_rate', 0)) / 1024)))
# audio bitrate: 64k

Conversion

You can convert your stream to a file or to another stream protocols. You should pass a manifest of the stream to the `input` method:

1. HLS To DASH
from ffmpeg_streaming import Formats, Bitrate, Representation, Size

video = ffmpeg_streaming.input('https://www.quasarstream.com/?PATH/TO/HLS-MANIFEST.M3U8')

_480p  = Representation(Size(854, 480), Bitrate(750 * 1024, 192 * 1024))

dash = video.dash(Formats.h264())
dash.representations(_480p)
dash.output('/var/media/dash.mpd')
2. DASH To HLS
video = ffmpeg_streaming.input('https://www.quasarstream.com/?PATH/TO/DASH-MANIFEST.MPD')

hls = video.hls(Formats.h264())
hls.auto_generate_representations()
hls.output('/var/media/hls.m3u8')
3. Stream(DASH or HLS) To File:
video = ffmpeg_streaming.input('https://www.quasarstream.com/?PATH/TO/MANIFEST.MPD or M3U8')

stream = video.stream2file(Formats.h264())
stream.output('/var/media/new-video.mp4')

Several Open Source Players

You can use these players to play your packaged videos.




NOTE:

You must pass a link of the master playlist(manifest)(i.e. https://www.quasarstream.com/?"PATH TO STREAM DIRECTORY"/dash-stream.mpd or /PATH_TO_STREAM_DIRECTORY/hls-stream.m3u8 ) to these players.

NOTE:

If you save your stream content to a cloud(i.e. Amazon S3), the link of content MUST BE PUBLIC.

NOTE:

As you may know, IOS does not have native support for DASH. Although there are some libraries such as Viblast and MPEGDASH-iOS-Player to support this technique, I have never tested them. So maybe som of them will not work correctly.

If you like this project, please ⭐️ Star it on Github.