How to Implement Adaptive Streaming with video-use: A Complete Guide
You can implement adaptive streaming in video-use by extending the existing ffmpeg wrapper in helpers/render.py to generate multi-bitrate HLS or DASH manifests after the final MP4 render completes.
The video-use repository processes video edits through an automated pipeline that produces a single finalized MP4 at edit/final.mp4. To implement adaptive streaming with video-use, you must generate multiple bitrate renditions and a master playlist that video players can switch between on-the-fly based on network conditions. This guide demonstrates how to extend the existing ffmpeg-based architecture to output HTTP Live Streaming (HLS) or MPEG-DASH assets without modifying the core editing logic.
Understanding the video-use Rendering Architecture
Centralized ffmpeg Execution in render.py
All video processing in video-use funnels through helpers/render.py, where ffmpeg commands are constructed as argument lists and executed via the run() function. This centralized approach ensures consistent handling of cuts, color grading, and loudness normalization throughout the pipeline.
The run() function accepts a command list and handles execution, making it the ideal extension point for post-render adaptive streaming generation.
Reusing Color Grading Filters
The repository applies per-clip color grading through helpers/grade.py, which utilizes ffmpeg's signalstats filter to compute custom filter chains. When implementing adaptive streaming, you can reuse these exact filter strings to ensure each bitrate rendition maintains the same visual grade as the final MP4.
Temporary File Management
Following the pattern established in helpers/timeline_view.py, which uses tempfile.mkdtemp for temporary directories, adaptive streaming generation should write to a dedicated temporary folder (e.g., hls/) that can be safely removed after upload to your CDN.
Creating the Adaptive Streaming Helper
Implementing the make_hls Function
Create a new file at helpers/adaptive_stream.py that imports the existing run() function and generates HLS assets:
# helpers/adaptive_stream.py
import json, pathlib, subprocess
from .render import run
def make_hls(video_path: pathlib.Path, out_dir: pathlib.Path, resolutions=None):
"""Create HLS adaptive-bitrate assets from *video_path*."""
if resolutions is None:
# Default renditions (width, height, bitrate Kbps)
resolutions = [(1920, 1080, 5000),
(1280, 720, 2800),
(854, 480, 1400),
(640, 360, 800)]
out_dir.mkdir(parents=True, exist_ok=True)
variant_playlists = []
for i, (w, h, br) in enumerate(resolutions):
variant = out_dir / f"variant{i}.m3u8"
cmd = [
"ffmpeg", "-y", "-i", str(video_path),
"-vf", f"scale=w={w}:h={h}",
"-c:v", "libx264", "-b:v", f"{br}k",
"-c:a", "aac", "-b:a", "128k",
"-hls_time", "6",
"-hls_playlist_type", "vod",
"-hls_segment_filename", str(out_dir / f"seg{i}_%03d.ts"),
str(variant)
]
run(cmd, quiet=True)
variant_playlists.append((w, h, variant.name))
# Master playlist
master = out_dir / "master.m3u8"
with master.open("w") as fp:
fp.write("#EXTM3U\n")
for w, h, v in variant_playlists:
fp.write(f'#EXT-X-STREAM-INF:BANDWIDTH={int(br*1000)},RESOLUTION={w}x{h}\n{v}\n')
return master
Handling Multiple Bitrate Renditions
The function generates four standard renditions by default: 1080p at 5000 Kbps, 720p at 2800 Kbps, 480p at 1400 Kbps, and 360p at 800 Kbps. Each rendition uses ffmpeg's scale filter to resize the video while maintaining the original aspect ratio, ensuring the adaptive stream covers a full range of device capabilities and network speeds.
Generating the Master Playlist
After creating individual variant playlists, the helper generates a master playlist (master.m3u8) that maps each resolution to its corresponding bandwidth and segment file using the #EXT-X-STREAM-INF tag. This manifest enables players to automatically switch between qualities as network conditions change.
Integrating Adaptive Streaming into the Pipeline
Hooking into the Render Process
Modify helpers/render.py or the top-level script that invokes the render step to call make_hls immediately after the final MP4 production:
from .adaptive_stream import make_hls
# after the normal render finishes:
final_mp4 = output_dir / "final.mp4"
hls_dir = output_dir / "hls"
make_hls(final_mp4, hls_dir)
print(f"✅ Adaptive HLS ready at {hls_dir / 'master.m3u8'}")
Serving HLS Assets
The generated hls/ directory contains the master.m3u8 manifest and segmented .ts files. You can deploy these files to any static web server, CDN, GitHub Pages, or Netlify. Point your video player to https://your-host/hls/master.m3u8 to enable adaptive playback.
For HTML5 playback in browsers without native HLS support, use hls.js:
<video id="vid" controls></video>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource('https://your-host/edit/hls/master.m3u8');
hls.attachMedia(document.getElementById('vid'));
}
</script>
Implementing DASH as an Alternative
MPEG-DASH Configuration
For MPEG-DASH compatibility, replace the HLS-specific ffmpeg arguments with -f dash and adjust the segment filename pattern. The same helper pattern works; just modify the command to generate a manifest.mpd file instead of an m3u8 playlist:
cmd = [
"ffmpeg", "-y", "-i", str(video_path),
"-vf", f"scale=w={w}:h={h}",
"-c:v", "libx264", "-b:v", f"{br}k",
"-f", "dash",
"-dash_segment_filename", str(out_dir / f"seg{i}_%03d.m4s"),
str(out_dir / f"manifest{i}.mpd")
]
Summary
- Extend
helpers/render.pyby creating a newmake_hlsfunction inhelpers/adaptive_stream.pythat leverages the existingrun()wrapper - Generate multiple bitrate renditions (1080p, 720p, 480p, 360p) while preserving color grading consistency from
helpers/grade.py - Create a master playlist that maps bandwidth to resolution for automatic quality switching by video players
- Store temporary HLS files in a dedicated subdirectory following the
tempfile.mkdtemppattern used inhelpers/timeline_view.py - Serve the resulting
.m3u8and.tsfiles via any static web server, CDN, or GitHub Pages
Frequently Asked Questions
Can I use the existing color grades with adaptive streaming?
Yes. The helpers/grade.py module generates ffmpeg filter strings that you can apply to each bitrate rendition during the HLS generation process. This ensures visual consistency across all quality levels by utilizing the same signalstats-based filter chains used in the main render pipeline.
Where should I store the generated HLS files?
Store them in a temporary subdirectory (e.g., hls/ inside the output directory) following the pattern established in helpers/timeline_view.py, which uses tempfile.mkdtemp for temporary file management. You can safely remove these files after uploading to your CDN or streaming server.
How do I serve the adaptive streaming content?
Serve the hls/ folder contents via any static web server, Nginx, GitHub Pages, or Netlify. For HTML5 playback, use hls.js or native HLS support in Safari and mobile devices by pointing the player to master.m3u8.
Does this work with live streaming or only VOD?
The implementation described targets Video on Demand (VOD) using -hls_playlist_type vod. For live streaming, you would need to modify the ffmpeg arguments to use -hls_playlist_type event or -hls_playlist_type live and implement a continuous upload mechanism to your streaming server.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →