Create adaptive streaming media packages (DASH & HLS) with DRM support using FFmpeg in Python
DASH & HLS support with automatic bitrate adaptation
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.
Create DASH and HLS streams with multiple bitrate representations
AES-128 encryption for HLS with key rotation options
Direct integration with S3, Google Cloud, Azure and more
Support for live streaming from webcams and screen capture
This library requires a functioning FFmpeg installation, including both FFMpeg and FFProbe binaries.
Compatibility: This library is only compatible with Python 3.9 or higher.
Using pip:
pip install python-ffmpeg-video-streaming
Alternatively, add the dependency to your requirements.txt file:
python-ffmpeg-video-streaming>=0.1
import ffmpeg_streaming
There are multiple ways to open a media resource:
video = ffmpeg_streaming.input('/var/media/video.mp4')
video = ffmpeg_streaming.input('https://www.quasarstream.com/?"PATH TO A VIDEO FILE" or "PATH TO A LIVE HTTP STREAM"')
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")
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)
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
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')
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')
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')
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')
auto_generate_representations
method cannot be used for camera-based media.
HTTP Live Streaming (also known as HLS) is an adaptive bitrate streaming protocol. It uses HTTP-based file downloads and M3U playlists. Learn more
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')
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')
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')
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')
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.
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
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')
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')
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.
It's crucial to secure your encryption keys on your website. Here are some methods:
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)
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
output
to
save it to your local machine.
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)
An illustration of the end-to-end process.
Cloud
Local
Cloud
Local
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')
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
This section demonstrates how to convert streams using our library. You'll provide a manifest URL to the input method.
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')
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')
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')
Recommended open-source players for playing packaged videos:
Platform | Player | Supported Formats |
---|---|---|
Android | AndroidX Media | DASH & HLS |
iOS | Native Player | HLS |
Windows/Linux/macOS | FFmpeg (ffplay), VLC media player | DASH & HLS |
If you find this project useful, please support us: