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,6 +402,12 @@ async def get_run(
|
|||||||
# Detect media type using magic bytes, fall back to database item_type
|
# Detect media type using magic bytes, fall back to database item_type
|
||||||
output_cid = run["output_cid"]
|
output_cid = run["output_cid"]
|
||||||
media_type = None
|
media_type = None
|
||||||
|
|
||||||
|
# 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:
|
try:
|
||||||
from ..services.run_service import detect_media_type
|
from ..services.run_service import detect_media_type
|
||||||
cache_path = get_cache_manager().get_by_cid(output_cid)
|
cache_path = get_cache_manager().get_by_cid(output_cid)
|
||||||
@@ -568,6 +574,10 @@ async def list_runs(
|
|||||||
for run in runs:
|
for run in runs:
|
||||||
# Add output media info
|
# Add output media info
|
||||||
if run.get("output_cid"):
|
if run.get("output_cid"):
|
||||||
|
# Streaming runs (with ipfs_cid) are always video/mp4
|
||||||
|
if run.get("ipfs_cid"):
|
||||||
|
run["output_media_type"] = "video/mp4"
|
||||||
|
else:
|
||||||
try:
|
try:
|
||||||
cache_path = cache_manager.get_by_cid(run["output_cid"])
|
cache_path = cache_manager.get_by_cid(run["output_cid"])
|
||||||
if cache_path and cache_path.exists():
|
if cache_path and cache_path.exists():
|
||||||
|
|||||||
@@ -575,7 +575,7 @@
|
|||||||
{# Inline media preview - prefer IPFS URLs when available #}
|
{# Inline media preview - prefer IPFS URLs when available #}
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
{% if output_media_type and output_media_type.startswith('image/') %}
|
{% 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"
|
<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">
|
class="max-w-full max-h-96 rounded-lg mx-auto">
|
||||||
</a>
|
</a>
|
||||||
@@ -593,14 +593,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-between">
|
<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">
|
<a href="{% if run.ipfs_cid %}/ipfs/{{ run.ipfs_cid }}{% else %}/cache/{{ run.output_cid }}{% endif %}"
|
||||||
{{ run.output_cid }}
|
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>
|
</a>
|
||||||
{% if run.ipfs_cid %}
|
{% if run.ipfs_cid %}
|
||||||
<a href="https://ipfs.io/ipfs/{{ run.ipfs_cid }}"
|
<a href="https://ipfs.io/ipfs/{{ run.ipfs_cid }}"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="text-gray-400 hover:text-white text-sm">
|
class="text-gray-400 hover:text-white text-sm">
|
||||||
IPFS: {{ run.ipfs_cid[:16] }}...
|
View on IPFS Gateway
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -411,6 +411,8 @@ def run_stream(
|
|||||||
"ffmpeg", "-y",
|
"ffmpeg", "-y",
|
||||||
"-i", str(hls_playlist),
|
"-i", str(hls_playlist),
|
||||||
"-c", "copy", # Just copy streams, no re-encoding
|
"-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)
|
str(final_mp4)
|
||||||
]
|
]
|
||||||
logger.info(f"Muxing HLS to MP4: {' '.join(mux_cmd)}")
|
logger.info(f"Muxing HLS to MP4: {' '.join(mux_cmd)}")
|
||||||
|
|||||||
Reference in New Issue
Block a user