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.py by creating a new make_hls function in helpers/adaptive_stream.py that leverages the existing run() 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.mkdtemp pattern used in helpers/timeline_view.py
  • Serve the resulting .m3u8 and .ts files 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →