mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-08-05 18:05:08 +08:00
Store mp4 metadata at the beginning of the file when possible. (#15195)
This commit is contained in:
@@ -36,13 +36,15 @@ def get_open_write_kwargs(
|
|||||||
dest: str | io.BytesIO, container_format: str, to_format: str | None
|
dest: str | io.BytesIO, container_format: str, to_format: str | None
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Get kwargs for writing a `VideoFromFile` to a file/stream with `av.open`"""
|
"""Get kwargs for writing a `VideoFromFile` to a file/stream with `av.open`"""
|
||||||
|
is_write_to_buffer = isinstance(dest, io.BytesIO)
|
||||||
|
is_mp4_file = not is_write_to_buffer and os.path.splitext(dest)[1].lower() == ".mp4"
|
||||||
|
movflags = "use_metadata_tags+faststart" if is_mp4_file else "use_metadata_tags"
|
||||||
open_kwargs = {
|
open_kwargs = {
|
||||||
"mode": "w",
|
"mode": "w",
|
||||||
# If isobmff, preserve custom metadata tags (workflow, prompt, extra_pnginfo)
|
# If isobmff, preserve custom metadata tags (workflow, prompt, extra_pnginfo)
|
||||||
"options": {"movflags": "use_metadata_tags"},
|
"options": {"movflags": movflags},
|
||||||
}
|
}
|
||||||
|
|
||||||
is_write_to_buffer = isinstance(dest, io.BytesIO)
|
|
||||||
if is_write_to_buffer:
|
if is_write_to_buffer:
|
||||||
# Set output format explicitly, since it cannot be inferred from file extension
|
# Set output format explicitly, since it cannot be inferred from file extension
|
||||||
if to_format == VideoContainer.AUTO:
|
if to_format == VideoContainer.AUTO:
|
||||||
@@ -103,7 +105,9 @@ def mp4_output_open_kwargs(path: str | io.BytesIO, format: VideoContainer, codec
|
|||||||
raise ValueError("Only MP4 format is supported for now")
|
raise ValueError("Only MP4 format is supported for now")
|
||||||
if codec != VideoCodec.AUTO and codec != VideoCodec.H264:
|
if codec != VideoCodec.AUTO and codec != VideoCodec.H264:
|
||||||
raise ValueError("Only H264 codec is supported for now")
|
raise ValueError("Only H264 codec is supported for now")
|
||||||
open_kwargs = {"mode": "w", "options": {"movflags": "use_metadata_tags"}}
|
# FFmpeg's faststart pass reopens the output by filename, so it cannot be used with file-like objects.
|
||||||
|
movflags = "use_metadata_tags+faststart" if isinstance(path, (str, os.PathLike)) else "use_metadata_tags"
|
||||||
|
open_kwargs = {"mode": "w", "options": {"movflags": movflags}}
|
||||||
if isinstance(format, VideoContainer) and format != VideoContainer.AUTO:
|
if isinstance(format, VideoContainer) and format != VideoContainer.AUTO:
|
||||||
open_kwargs["format"] = format.value
|
open_kwargs["format"] = format.value
|
||||||
elif isinstance(path, io.BytesIO):
|
elif isinstance(path, io.BytesIO):
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ def test_get_open_write_kwargs_filepath_no_format():
|
|||||||
kwargs_specific = get_open_write_kwargs("output.avi", "mp4", "avi")
|
kwargs_specific = get_open_write_kwargs("output.avi", "mp4", "avi")
|
||||||
fail_msg = "Format should not be set for file paths (Specific)"
|
fail_msg = "Format should not be set for file paths (Specific)"
|
||||||
assert "format" not in kwargs_specific, fail_msg
|
assert "format" not in kwargs_specific, fail_msg
|
||||||
|
assert kwargs_specific["options"]["movflags"] == "use_metadata_tags"
|
||||||
|
|
||||||
|
|
||||||
def test_get_open_write_kwargs_base_options_mode():
|
def test_get_open_write_kwargs_base_options_mode():
|
||||||
@@ -43,9 +44,9 @@ def test_get_open_write_kwargs_base_options_mode():
|
|||||||
kwargs = get_open_write_kwargs("output.mp4", "mp4", VideoContainer.AUTO)
|
kwargs = get_open_write_kwargs("output.mp4", "mp4", VideoContainer.AUTO)
|
||||||
assert kwargs["mode"] == "w", "mode should be set to write"
|
assert kwargs["mode"] == "w", "mode should be set to write"
|
||||||
|
|
||||||
fail_msg = "movflags should be set to preserve custom metadata tags"
|
fail_msg = "movflags should preserve custom metadata tags and enable faststart for MP4 files"
|
||||||
assert "movflags" in kwargs["options"], fail_msg
|
assert "movflags" in kwargs["options"], fail_msg
|
||||||
assert kwargs["options"]["movflags"] == "use_metadata_tags", fail_msg
|
assert kwargs["options"]["movflags"] == "use_metadata_tags+faststart", fail_msg
|
||||||
|
|
||||||
|
|
||||||
def test_get_open_write_kwargs_bytesio_auto_format():
|
def test_get_open_write_kwargs_bytesio_auto_format():
|
||||||
|
|||||||
@@ -258,6 +258,18 @@ def test_save_to_h264_crf_controls_quality(tmp_path):
|
|||||||
assert os.path.getsize(transcoded) < os.path.getsize(high_quality)
|
assert os.path.getsize(transcoded) < os.path.getsize(high_quality)
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_to_mp4_writes_metadata_before_media(video_components, tmp_path):
|
||||||
|
encoded = tmp_path / "encoded.mp4"
|
||||||
|
remuxed = tmp_path / "remuxed.mp4"
|
||||||
|
|
||||||
|
VideoFromComponents(video_components).save_to(str(encoded), metadata={"prompt": {"test": "value"}})
|
||||||
|
VideoFromFile(str(encoded)).save_to(str(remuxed), metadata={"prompt": {"test": "value"}})
|
||||||
|
|
||||||
|
for path in (encoded, remuxed):
|
||||||
|
data = path.read_bytes()
|
||||||
|
assert data.index(b"moov") < data.index(b"mdat")
|
||||||
|
|
||||||
|
|
||||||
def create_transcode_source(
|
def create_transcode_source(
|
||||||
width=64, height=64, frames=30, fps=30, audio_streams=1, undecodable_audio=0, rotation=False,
|
width=64, height=64, frames=30, fps=30, audio_streams=1, undecodable_audio=0, rotation=False,
|
||||||
container_format="mov", audio_codec="pcm_s16le",
|
container_format="mov", audio_codec="pcm_s16le",
|
||||||
|
|||||||
Reference in New Issue
Block a user