Fix MP4 mux for web playback: add faststart and genpts
- Add -movflags +faststart to move moov atom to start - Add -fflags +genpts for proper timestamp generation - Fixes jerky playback and video/audio desync Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -402,25 +402,31 @@ async def get_run(
|
||||
# Detect media type using magic bytes, fall back to database item_type
|
||||
output_cid = run["output_cid"]
|
||||
media_type = None
|
||||
try:
|
||||
from ..services.run_service import detect_media_type
|
||||
cache_path = get_cache_manager().get_by_cid(output_cid)
|
||||
if cache_path and cache_path.exists():
|
||||
simple_type = detect_media_type(cache_path)
|
||||
media_type = type_to_mime(simple_type)
|
||||
output_media_type = media_type
|
||||
except Exception:
|
||||
pass
|
||||
# Fall back to database item_type if local detection failed
|
||||
if not media_type:
|
||||
|
||||
# Streaming runs (with ipfs_cid) are always video/mp4
|
||||
if run.get("ipfs_cid"):
|
||||
media_type = "video/mp4"
|
||||
output_media_type = media_type
|
||||
else:
|
||||
try:
|
||||
import database
|
||||
item_types = await database.get_item_types(output_cid, run.get("actor_id"))
|
||||
if item_types:
|
||||
media_type = type_to_mime(item_types[0].get("type"))
|
||||
from ..services.run_service import detect_media_type
|
||||
cache_path = get_cache_manager().get_by_cid(output_cid)
|
||||
if cache_path and cache_path.exists():
|
||||
simple_type = detect_media_type(cache_path)
|
||||
media_type = type_to_mime(simple_type)
|
||||
output_media_type = media_type
|
||||
except Exception:
|
||||
pass
|
||||
# Fall back to database item_type if local detection failed
|
||||
if not media_type:
|
||||
try:
|
||||
import database
|
||||
item_types = await database.get_item_types(output_cid, run.get("actor_id"))
|
||||
if item_types:
|
||||
media_type = type_to_mime(item_types[0].get("type"))
|
||||
output_media_type = media_type
|
||||
except Exception:
|
||||
pass
|
||||
artifacts.append({
|
||||
"cid": output_cid,
|
||||
"step_name": "Output",
|
||||
@@ -568,13 +574,17 @@ async def list_runs(
|
||||
for run in runs:
|
||||
# Add output media info
|
||||
if run.get("output_cid"):
|
||||
try:
|
||||
cache_path = cache_manager.get_by_cid(run["output_cid"])
|
||||
if cache_path and cache_path.exists():
|
||||
simple_type = detect_media_type(cache_path)
|
||||
run["output_media_type"] = type_to_mime(simple_type)
|
||||
except Exception:
|
||||
pass
|
||||
# Streaming runs (with ipfs_cid) are always video/mp4
|
||||
if run.get("ipfs_cid"):
|
||||
run["output_media_type"] = "video/mp4"
|
||||
else:
|
||||
try:
|
||||
cache_path = cache_manager.get_by_cid(run["output_cid"])
|
||||
if cache_path and cache_path.exists():
|
||||
simple_type = detect_media_type(cache_path)
|
||||
run["output_media_type"] = type_to_mime(simple_type)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Add input media info (first 3 inputs)
|
||||
input_previews = []
|
||||
|
||||
@@ -575,7 +575,7 @@
|
||||
{# Inline media preview - prefer IPFS URLs when available #}
|
||||
<div class="mb-4">
|
||||
{% if output_media_type and output_media_type.startswith('image/') %}
|
||||
<a href="/cache/{{ run.output_cid }}" class="block">
|
||||
<a href="{% if run.ipfs_cid %}/ipfs/{{ run.ipfs_cid }}{% else %}/cache/{{ run.output_cid }}{% endif %}" class="block">
|
||||
<img src="{% if run.ipfs_cid %}/ipfs/{{ run.ipfs_cid }}{% else %}/cache/{{ run.output_cid }}/raw{% endif %}" alt="Output"
|
||||
class="max-w-full max-h-96 rounded-lg mx-auto">
|
||||
</a>
|
||||
@@ -593,14 +593,15 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<a href="/cache/{{ run.output_cid }}" class="font-mono text-sm text-blue-400 hover:text-blue-300">
|
||||
{{ run.output_cid }}
|
||||
<a href="{% if run.ipfs_cid %}/ipfs/{{ run.ipfs_cid }}{% else %}/cache/{{ run.output_cid }}{% endif %}"
|
||||
class="font-mono text-sm text-blue-400 hover:text-blue-300">
|
||||
{% if run.ipfs_cid %}{{ run.ipfs_cid }}{% else %}{{ run.output_cid }}{% endif %}
|
||||
</a>
|
||||
{% if run.ipfs_cid %}
|
||||
<a href="https://ipfs.io/ipfs/{{ run.ipfs_cid }}"
|
||||
target="_blank"
|
||||
class="text-gray-400 hover:text-white text-sm">
|
||||
IPFS: {{ run.ipfs_cid[:16] }}...
|
||||
View on IPFS Gateway
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -411,6 +411,8 @@ def run_stream(
|
||||
"ffmpeg", "-y",
|
||||
"-i", str(hls_playlist),
|
||||
"-c", "copy", # Just copy streams, no re-encoding
|
||||
"-movflags", "+faststart", # Move moov atom to start for web playback
|
||||
"-fflags", "+genpts", # Generate proper timestamps
|
||||
str(final_mp4)
|
||||
]
|
||||
logger.info(f"Muxing HLS to MP4: {' '.join(mux_cmd)}")
|
||||
|
||||
Reference in New Issue
Block a user