mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-09-09 02:26:31 +08:00
fix(video): remux HEVC to mp4/mov as hvc1 via hevc_mp4toannexb (#15809)
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from av.bitstream import BitStreamFilterContext
|
||||
from av.container import InputContainer
|
||||
from av.subtitles.stream import SubtitleStream
|
||||
from av.video.reformatter import ColorPrimaries, ColorRange, ColorTrc
|
||||
@@ -96,6 +97,31 @@ def video_stream_bit_depth(stream) -> int:
|
||||
return max(component.bits for component in stream.format.components)
|
||||
|
||||
|
||||
def isobmff_hevc_filter(output_container, stream, out_stream):
|
||||
"""Apple players need the 'hvc1' sample entry, not FFmpeg's default 'hev1'. Annex B input without
|
||||
extradata makes the muxer build hvcC from the first packet and strip in-band parameter sets;
|
||||
'hvc1' sources already have a complete hvcC and only need the tag PyAV reset."""
|
||||
if output_container.format.name not in ("mp4", "mov") or stream.codec.canonical_name != "hevc":
|
||||
return None
|
||||
try:
|
||||
codec_tag = stream.codec_context.codec_tag
|
||||
except UnicodeDecodeError:
|
||||
codec_tag = ""
|
||||
if codec_tag == "hvc1":
|
||||
out_stream.codec_context.codec_tag = "hvc1"
|
||||
return None
|
||||
hevc_filter = BitStreamFilterContext("hevc_mp4toannexb", stream, out_stream)
|
||||
out_stream.codec_context.codec_tag = "hvc1"
|
||||
out_stream.codec_context.extradata = None
|
||||
return hevc_filter
|
||||
|
||||
|
||||
def filter_hevc_packet(hevc_filter, packet):
|
||||
if packet.has_sidedata("new_extradata"):
|
||||
raise ValueError("HEVC with multiple sample descriptions cannot be remuxed as hvc1; re-encode it instead")
|
||||
return hevc_filter.filter(packet)
|
||||
|
||||
|
||||
def last_decodable_audio_stream(container: InputContainer):
|
||||
"""Streams FFmpeg has no decoder for have no codec context, and decoding their
|
||||
packets crashes the process (e.g. APAC spatial-audio track in iPhone)."""
|
||||
@@ -601,19 +627,26 @@ class VideoFromFile(VideoInput):
|
||||
|
||||
# Add streams to the new container. Streams with no codec context cannot be used as an output template.
|
||||
stream_map = {}
|
||||
hevc_filters = {}
|
||||
for stream in streams:
|
||||
if isinstance(stream, (av.VideoStream, av.AudioStream, SubtitleStream)):
|
||||
if stream.codec_context is None:
|
||||
logging.warning("Skipping %s stream %d with unsupported codec", stream.type, stream.index)
|
||||
continue
|
||||
out_stream = output_container.add_stream_from_template(template=stream, opaque=True)
|
||||
hevc_filter = isobmff_hevc_filter(output_container, stream, out_stream)
|
||||
if hevc_filter is not None:
|
||||
hevc_filters[stream] = hevc_filter
|
||||
stream_map[stream] = out_stream
|
||||
|
||||
# Write packets to the new container
|
||||
for packet in container.demux():
|
||||
if packet.stream in stream_map and packet.dts is not None:
|
||||
packet.stream = stream_map[packet.stream]
|
||||
output_container.mux(packet)
|
||||
out_stream = stream_map[packet.stream]
|
||||
hevc_filter = hevc_filters.get(packet.stream)
|
||||
for out_packet in filter_hevc_packet(hevc_filter, packet) if hevc_filter else (packet,):
|
||||
out_packet.stream = out_stream
|
||||
output_container.mux(out_packet)
|
||||
|
||||
def _save_transcoded(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user