Loading...

Python FFmpeg Video Streaming Documentation

Create adaptive streaming media packages (DASH & HLS) with DRM support using FFmpeg in Python

Adaptive Streaming

DASH & HLS support with automatic bitrate adaptation

What is Python FFmpeg Video Streaming?

This library leverages FFmpeg to create media packages compatible with online streaming protocols like DASH (Dynamic Adaptive Streaming over HTTP) and HLS (HTTP Live Streaming). It also offers functionalities for DRM (Digital Rights Management) for HLS packaging and managing files stored in cloud storage.

Adaptive Streaming

Create DASH and HLS streams with multiple bitrate representations

DRM Support

AES-128 encryption for HLS with key rotation options

Cloud Integration

Direct integration with S3, Google Cloud, Azure and more

Live Streaming

Support for live streaming from webcams and screen capture

Requirements

1. FFmpeg

This library requires a functioning FFmpeg installation, including both FFMpeg and FFProbe binaries.

Windows Users: For binary detection on Windows, ensure FFmpeg and FFProbe paths are added to your system path environment variable.

2. Python

Compatibility: This library is only compatible with Python 3.9 or higher.

Installation

Package Installation:

Using pip:

pip install python-ffmpeg-video-streaming

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

python-ffmpeg-video-streaming>=0.1
Want to build an OTT platform? The basic version has everything you need. Online Demo Get Now

Usage

Import the Library:

import ffmpeg_streaming

Opening a Resource

There are multiple ways to open a media resource:

1. Local File or Supported Resource:

video = ffmpeg_streaming.input('/var/media/video.mp4')
Refer to the FFmpeg Protocols Documentation for a list of supported resources (http, ftp, etc.).
video = ffmpeg_streaming.input('https://www.quasarstream.com/?"PATH TO A VIDEO FILE" or "PATH TO A LIVE HTTP STREAM"')

2. Cloud Storage:

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 the provided link for examples on accessing files from Amazon S3, Google Cloud Storage, Microsoft Azure Storage, and custom cloud storage solutions.

3. Live Streaming from Webcam or Screen:

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: Consult the FFmpeg documentation for capturing webcams and desktops.

DASH (Dynamic Adaptive Streaming over HTTP)

Dynamic Adaptive Streaming over HTTP (DASH), also known as MPEG-DASH, allows high-quality streaming over the internet by segmenting content into short HTTP-based file downloads. Clients can choose the segment with the highest bitrate suitable for their network conditions, resulting in a seamless playback experience. Learn more

Creating DASH Files:

The library automatically generates Manifest (MPD) and segment files.

from ffmpeg_streaming import Formats

dash = video.dash(Formats.h264())
dash.auto_generate_representations()
dash.output('/var/media/dash.mpd')

Generating Representations Manually:

To specify the exact kilobit rate and size for each stream, manually create Representation objects and add them to the dash or hls instance:

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')

Generating HLS Playlists:

To automatically generate HLS playlists for DASH and HLS, use the auto_generate_representations method:

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:

For live streaming from a camera or screen, create Representation objects and add them to the dash instance:

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: The auto_generate_representations method cannot be used for camera-based media.
See DASH options and DASH WikiPedia article for more information.

HLS

HTTP Live Streaming (also known as HLS) is an adaptive bitrate streaming protocol. It uses HTTP-based file downloads and M3U playlists. Learn more

Creating HLS Files:

To create HLS files, use the hls instance and specify the output filename:

from ffmpeg_streaming import Formats

hls = video.hls(Formats.h264())
hls.auto_generate_representations()
hls.output('/var/media/hls.m3u8')

Generating Representations Manually:

To specify the exact kilobit rate and size for each stream, manually create Representation objects and add them to the dash or hls instance:

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:

For fragmented MP4 format in HLS, use the fragmented_mp4 method:

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:

For live streaming from a camera or screen, use the hls instance and specify the output filename:

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: The auto_generate_representations method cannot be used for camera-based media. HEVC and VP9 formats are not supported for HLS packaging unless using fragmented MP4.
See HLS options and HLS documentation for more information.

Encryption (DRM)

HLS encryption uses the Advanced Encryption Standard (AES) in Cipher Block Chaining (CBC) mode. This means each block is encrypted using the previous block's ciphertext. Learn more

Generating Encryption Keys:

  • Path: Specify a path to save a random key file on your local machine.
  • URL: Provide a URL (or path) on your website where the key file can be accessed. This key file must be accessible from your website.

Single Key Encryption:

The following code generates a single key for all segments:

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:

You can optionally rotate encryption keys periodically by passing a "key rotation period" to the encryption method. This generates a new key after a specified number of segments. For example, with a value of 10, a new key will be created every 10 segments.

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')

Limitations and Alternatives:

While FFmpeg supports AES encryption for HLS, it's not a complete Digital Rights Management (DRM) solution. For a full DRM solution, consider FairPlay Streaming, which offers secure key exchange and playback protection across devices. Other options include Microsoft's PlayReady and Google's Widevine.

Protecting Your Keys:

It's crucial to secure your encryption keys on your website. Here are some methods:

  • Token-based access control: Use a token (via GET/POST methods or authorization headers) to verify if a user is authorized to access the key.
  • Session or cookie-based access control: Implement session or cookie management to restrict access to authorized users.

Transcoding Monitoring

The following code demonstrates how to monitor the transcoding process:

from ffmpeg_streaming import Formats
import sys

def monitor(ffmpeg, duration, time_, time_left, process):
    """
    This function allows you to handle the transcoding process according to your needs.

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

        2. Handling Process object based on specific events
        if "something happened":
            process.terminate()

        3. Sending email notifications about completion time
            if time_left > 3600 and not already_send:
            # Send email if process takes more than an hour
                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. Creating a progress bar or displaying other parameters to 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: video duration
    :param time_: current time of transcoded video
    :param time_left: seconds left to finish transcoding
    :param process: subprocess object
    """
    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)
Tip: The monitor function provides real-time feedback on the transcoding progress.

Output Options

1. Local Path:

You can define the output location by passing a local path to the output method. If the directory doesn't exist, the library will automatically create it for you.

Here's an example using DASH:

from ffmpeg_streaming import Formats

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

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

If you omit the output parameter, the library will save the files to the same directory as the input file by default.

Here's an example using HLS:

from ffmpeg_streaming import Formats

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

hls.output() # Saves to the input file's directory
Important Note: When opening a file from cloud storage without specifying a local path, you must provide an output path using output to save it to your local machine.

2. Cloud Storage (VOD Only):

The library allows you to save packaged files directly to cloud storage providers. This functionality is currently limited to Video On-Demand (VOD) and doesn't support live streaming.

To achieve this, you'll need to create a CloudManager object and configure your cloud storage credentials. Here's an example using Amazon S3:

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)

For configuration details and examples with other cloud providers like Google Cloud Storage, Microsoft Azure Storage, refer to the provided link.

Additionally, you can save a local copy of the files while uploading to cloud storage:

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

Video Transcoding Workflow

An illustration of the end-to-end process.

Input Video File
Output Transcoded File

Input Source

Cloud

Local

Transcoding

HLS
DASH

Output

Cloud

Local

Visit the provided link for examples on accessing files from Amazon S3, Google Cloud Storage, Microsoft Azure Storage, and custom cloud storage solutions.

3. Uploading to Server (Live Streaming)

This method allows you to upload the segmented files and update manifest files directly to an HTTP server or other supported protocols like FTP. This functionality is suitable for live streaming scenarios.

# 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, ensure you've uploaded the master playlist file (/var/media/hls.m3u8) to your server using FTP or another method.
hls.output('ftp://[user[:password]@]server[:port]/var/media/hls.m3u8')
Important Note: For HLS, you need to upload the master playlist file to the server manually before using this method.

Extracting Metadata from Videos

This section explains how to extract information from a video file using our library.

from ffmpeg_streaming import FFProbe

# Specify the video file path
video_path = '/var/media/video.mp4'

# Create an FFProbe object
ffprobe = FFProbe(video_path)

# Save extracted information as JSON
ffprobe.save_as_json('probe.json')

# Access various data points

# All media information
all_media = ffprobe.all()

# Video format details
video_format = ffprobe.format()

# Stream information (all, video, audio)
streams = ffprobe.streams().all()
videos = ffprobe.streams().videos()
audios = ffprobe.streams().audios()

# Access individual streams
first_stream = ffprobe.streams().first_stream()
first_video = ffprobe.streams().video()
first_audio = ffprobe.streams().audio()

# Print extracted information

print("All Media Information:")
print(all_media)

print("\nVideo Format:")
print(video_format)

print("\nStreams:")
print(streams)

print("\nVideo Streams:")
for video in videos:
    print(video)

print("\nAudio Streams:")
for audio in audios:
    print(audio)

print("\nFirst Stream:")
print(first_stream)

print("\nFirst Video Stream:")
print(first_video)

print("\nFirst Audio Stream:")
print(first_audio)

# Calculate and print additional details

duration_seconds = float(video_format.get('duration', 0))
duration = datetime.timedelta(seconds=duration_seconds)
print(f"\nDuration: {duration}")  # f-string for formatted output

size_kb = int(video_format.get('size', 0)) / 1024
print(f"Size: {round(size_kb)} KB")  # f-string with round function

bitrate_kb = int(video_format.get('bit_rate', 0)) / 1024
print(f"Overall Bitrate: {round(bitrate_kb)} KB")  # f-string

dimensions = (
    first_video.get('width', "Unknown"),
    first_video.get('height', "Unknown")
)
print(f"Dimensions: {dimensions[0]}x{dimensions[1]}")  # f-string

video_bitrate_kb = int(first_video.get('bit_rate', 0)) / 1024
print(f"Video Bitrate: {round(video_bitrate_kb)} KB")  # f-string

audio_bitrate_kb = int(first_audio.get('bit_rate', 0)) / 1024
print(f"Audio Bitrate: {round(audio_bitrate_kb)} KB")  # f-string

Stream Conversion

This section demonstrates how to convert streams using our library. You'll provide a manifest URL to the input method.

Supported Conversions:

1. HLS To DASH:

from ffmpeg_streaming import Formats, Bitrate, Representation, Size

# Replace with the actual HLS manifest URL
hls_url = 'https://www.quasarstream.com/?PATH/TO/HLS-MANIFEST.M3U8'

# Define a video representation
_480p = Representation(Size(854, 480), Bitrate(750 * 1024, 192 * 1024))

# Create a DASH output
video = ffmpeg_streaming.input(hls_url)
dash = video.dash(Formats.h264())
dash.representations(_480p)
dash.output('/var/media/dash.mpd')

2. DASH To HLS

from ffmpeg_streaming import Formats

# Replace with the actual DASH manifest URL
dash_url = 'https://www.quasarstream.com/?PATH/TO/DASH-MANIFEST.MPD'

# Create an HLS output
video = ffmpeg_streaming.input(dash_url)
hls = video.hls(Formats.h264())
hls.auto_generate_representations()
hls.output('/var/media/hls.m3u8')

3. Stream (DASH or HLS) to File:

from ffmpeg_streaming import Formats

# Replace with the actual manifest URL (DASH or HLS)
manifest_url = 'https://www.quasarstream.com/?PATH/TO/MANIFEST.MPD or M3U8'

# Create a file output
video = ffmpeg_streaming.input(manifest_url)
stream = video.stream2file(Formats.h264())
stream.output('/var/media/new-video.mp4')

Open-Source Players

Recommended open-source players for playing packaged videos:

Web Players

DASH and HLS Support

DASH Only

HLS Only

Mobile and Desktop Players

Platform Player Supported Formats
Android AndroidX Media DASH & HLS
iOS Native Player HLS
Windows/Linux/macOS FFmpeg (ffplay), VLC media player DASH & HLS
Important Notes:
  • Always pass the URL of the master playlist (manifest) to the players
  • If saving stream content to cloud storage, ensure the content link is public
  • iOS does not have native support for DASH. Libraries like Viblast and MPEGDASH-iOS-Player exist but compatibility may vary

Support Us

If you find this project useful, please support us: