From 4d281c22d7560c13dc91a9c03d9325d6fb0f4837 Mon Sep 17 00:00:00 2001 From: dirwin31 <83434411+dirwin31@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:38:47 -0700 Subject: [PATCH] Stream Mp4 downloads --- .../the_galaxy/tests/test_dashcam_routes.py | 43 +++++++++++++++++- starpilot/system/the_galaxy/the_galaxy.py | 6 ++- starpilot/system/the_galaxy/utilities.py | 45 +++++++++++++++++++ 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/starpilot/system/the_galaxy/tests/test_dashcam_routes.py b/starpilot/system/the_galaxy/tests/test_dashcam_routes.py index 2f8d4e86f..50f02839f 100644 --- a/starpilot/system/the_galaxy/tests/test_dashcam_routes.py +++ b/starpilot/system/the_galaxy/tests/test_dashcam_routes.py @@ -487,6 +487,46 @@ def test_video_cache_never_evicts_the_entry_being_written(monkeypatch, tmp_path) assert keep.exists() +def test_combined_video_streams_fragmented_mp4_without_a_full_cache_file(monkeypatch, tmp_path): + cache = tmp_path / "video_cache" + first = tmp_path / "first.hevc" + second = tmp_path / "second.hevc" + first.write_bytes(b"first") + second.write_bytes(b"second") + monkeypatch.setattr(utilities, "VIDEO_CACHE_PATH", cache) + captured = {} + + class FakeProcess: + def __init__(self): + self.stdout = io.BytesIO(b"streamed-video") + self.returncode = None + + def wait(self, timeout=None): + self.returncode = 0 + return 0 + + def poll(self): + return self.returncode + + def popen(command, **kwargs): + captured["command"] = command + list_path = Path(command[command.index("-i") + 1]) + captured["list_path"] = list_path + captured["list_contents"] = list_path.read_text() + return FakeProcess() + + monkeypatch.setattr(utilities.subprocess, "Popen", popen) + + payload = b"".join(utilities.ffmpeg_stream_concatenated_mp4([first, second], chunk_size=4)) + + assert payload == b"streamed-video" + assert "frag_keyframe+empty_moov+default_base_moof" in captured["command"] + assert captured["command"][-1] == "pipe:1" + assert captured["list_contents"] == f"file '{first}'\nfile '{second}'\n" + assert not captured["list_path"].exists() + assert not list(cache.glob("*.mp4")) + + def _stub_remux(monkeypatch, tmp_path, payload=b"wrapped-video"): """Stand in for the ffmpeg remux, returning a real file so send_file can stream it.""" wrapped = tmp_path / "wrapped.mp4" @@ -501,7 +541,7 @@ def test_sparse_route_metadata_and_video_downloads(monkeypatch, tmp_path): (segment / "fcamera.hevc").write_bytes(b"hevc") monkeypatch.setattr(utilities, "get_route_start_time", lambda path: datetime(2026, 8, 26, tzinfo=timezone.utc)) _stub_remux(monkeypatch, tmp_path) - monkeypatch.setattr(utilities, "ffmpeg_concat_segments_to_mp4", lambda paths, cache_key=None: io.BytesIO(b"combined-video")) + monkeypatch.setattr(utilities, "ffmpeg_stream_concatenated_mp4", lambda paths: iter((b"combined-", b"video"))) client = _make_client(monkeypatch, tmp_path) metadata = client.get(f"/api/routes/{ROUTE_NAME}") @@ -519,6 +559,7 @@ def test_sparse_route_metadata_and_video_downloads(monkeypatch, tmp_path): assert combined_video.status_code == 200 assert combined_video.mimetype == "video/mp4" assert combined_video.data == b"combined-video" + assert combined_video.headers["X-Accel-Buffering"] == "no" def test_route_metadata_never_probes_segments_with_ffprobe(monkeypatch, tmp_path): diff --git a/starpilot/system/the_galaxy/the_galaxy.py b/starpilot/system/the_galaxy/the_galaxy.py index 92771d6a6..1d781f67a 100644 --- a/starpilot/system/the_galaxy/the_galaxy.py +++ b/starpilot/system/the_galaxy/the_galaxy.py @@ -6482,8 +6482,10 @@ def setup(app): if not input_files: return {"error": "No video files found"}, 404 - mp4_file = utilities.ffmpeg_concat_segments_to_mp4(input_files, cache_key=f"{name}-{camera}") - return send_file(mp4_file, mimetype="video/mp4") + response = Response(utilities.ffmpeg_stream_concatenated_mp4(input_files), mimetype="video/mp4") + response.headers["Cache-Control"] = "no-store" + response.headers["X-Accel-Buffering"] = "no" + return response return {"error": "Route not found"}, 404 diff --git a/starpilot/system/the_galaxy/utilities.py b/starpilot/system/the_galaxy/utilities.py index 7cd952daa..5525eba5e 100644 --- a/starpilot/system/the_galaxy/utilities.py +++ b/starpilot/system/the_galaxy/utilities.py @@ -12,6 +12,7 @@ import signal import socket import subprocess import sys +import tempfile import threading import time @@ -749,6 +750,50 @@ def ffmpeg_concat_segments_to_mp4(input_files, cache_key=None): return open(cache_path, "rb") + +def ffmpeg_stream_concatenated_mp4(input_files, chunk_size=256 * 1024): + """Stream-copy camera segments as fragmented MP4 without building a full cache file.""" + if not input_files: + raise ValueError("No input files provided for concatenation") + + VIDEO_CACHE_PATH.mkdir(exist_ok=True) + with tempfile.NamedTemporaryFile("w", suffix=".txt", prefix="route-download-", dir=VIDEO_CACHE_PATH, delete=False) as list_file: + list_path = Path(list_file.name) + for segment in input_files: + list_file.write(f"file '{Path(segment)}'\n") + + process = None + try: + process = subprocess.Popen( + [FFMPEG_BIN, "-hide_banner", "-loglevel", "error", "-f", "concat", "-safe", "0", + "-i", str(list_path), "-c", "copy", "-movflags", "frag_keyframe+empty_moov+default_base_moof", + "-f", "mp4", "pipe:1"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + ) + while True: + chunk = process.stdout.read(chunk_size) + if not chunk: + break + yield chunk + if process.wait() != 0: + raise ValueError("Could not stream the combined route video") + finally: + if process is not None: + if process.stdout is not None: + process.stdout.close() + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=2) + except subprocess.TimeoutExpired: + process.kill() + process.wait() + try: + list_path.unlink() + except OSError: + pass + def ffmpeg_mp4_wrap_to_path(filename): """Remux one raw .hevc segment to mp4 and return the cache path.