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.

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

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.

NOTE:

See DASH options and DASH documentation 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.

NOTE:

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)
Output From a Terminal:

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)
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 for Playback

Here are some recommended open-source players for playing packaged videos:




Important Notes:

  • Manifest URL: Always pass the URL of the master playlist (manifest) to the players.
  • Cloud Storage: If saving your stream content to a cloud (e.g., Amazon S3), ensure the content link is public.
  • iOS DASH Support: iOS does not have native support for DASH. While there are libraries like Viblast and MPEGDASH-iOS-Player, their compatibility may vary.

💗 If you find this project helpful, please give it a ⭐️ Star on GitHub.💗

Build Your Own OTT Platform

Need a solution that's perfectly aligned with your unique requirements? Our custom features can handle high volume and complexity. Contact Us to discuss your needs.

Basic

  • Video On-Demand (HLS and DASH)
  • Video Quality Settings
  • Real-Time Progress Monitoring
  • Dark and light theme
Pro

  • Live Streaming
  • Add Subtitles and Audios
  • Monetization
  • Robust DRM Systems