From 2d2be4e9a79325ac7c89a2b3b24d8a757ae8313b Mon Sep 17 00:00:00 2001 From: "shuohigh@gmail.com" Date: Fri, 13 Jun 2025 16:08:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AF=B9hls?= =?UTF-8?q?=E7=9B=B4=E6=92=AD=E8=BD=AC=E6=8D=A2mp4=E7=9A=84playlist?= =?UTF-8?q?=E7=BB=93=E5=B0=BE=E6=A3=80=E6=9F=A5=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E7=9B=B4=E6=92=AD=E4=B8=BA=E7=BB=93?= =?UTF-8?q?=E6=9D=9F=E7=9B=B4=E6=92=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BowongModalFunctions/utils/VideoUtils.py | 8 +++++--- src/cluster/ffmpeg_apps/convert_stream.py | 5 +++-- src/cluster/ffmpeg_apps/stream_record_as_hls.py | 12 ++++++++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/BowongModalFunctions/utils/VideoUtils.py b/src/BowongModalFunctions/utils/VideoUtils.py index d29802d..b23d7a6 100644 --- a/src/BowongModalFunctions/utils/VideoUtils.py +++ b/src/BowongModalFunctions/utils/VideoUtils.py @@ -378,7 +378,8 @@ class VideoUtils: raise RuntimeError("输出是空文件") else: if not quiet: - logger.warning(line) + if "SKIP" not in line: + logger.warning(line) return ffmpeg_cmd @@ -754,7 +755,6 @@ class VideoUtils: os.makedirs(os.path.dirname(output_path), exist_ok=True) try: - local_m3u8_path, temp_dir = await VideoUtils.convert_m3u8_to_local_source(media_stream_url) # 使用ffmpeg合并TS片段 ffmpeg_cmd = VideoUtils.async_ffmpeg_init() @@ -1194,8 +1194,9 @@ class VideoUtils: reconnect_at_eof="1", reconnect_streamed="1", reconnect_delay_max="5") + output_playlist = f"{playlist_output_dir}/playlist.m3u8" ffmpeg_cmd.output( - f"{playlist_output_dir}/playlist.m3u8", + output_playlist, f="hls", hls_init_time=first_segment_duration, hls_time=segment_duration, @@ -1210,3 +1211,4 @@ class VideoUtils: ) await ffmpeg_cmd.execute() logger.info(f'停止录制') + return output_playlist diff --git a/src/cluster/ffmpeg_apps/convert_stream.py b/src/cluster/ffmpeg_apps/convert_stream.py index 151d6b5..9e2b84d 100644 --- a/src/cluster/ffmpeg_apps/convert_stream.py +++ b/src/cluster/ffmpeg_apps/convert_stream.py @@ -44,8 +44,9 @@ with ffmpeg_worker_image.imports(): raise NotImplementedError(f"暂不支持使用{media_stream.protocol.value}协议") output_path = f"{output_path_prefix}/{config.modal_environment}/convert_stream/{func_id}/output.mp4" - local_output, metadata = await VideoUtils.ffmpeg_convert_stream_media(media_stream_url=stream_url, - output_path=output_path) + local_output, metadata = await VideoUtils.ffmpeg_convert_stream_media_multithread( + media_stream_url=stream_url, + output_path=output_path) s3_outputs = local_copy_to_s3([local_output]) return FFMPEGResult(urn=s3_outputs[0], metadata=metadata, diff --git a/src/cluster/ffmpeg_apps/stream_record_as_hls.py b/src/cluster/ffmpeg_apps/stream_record_as_hls.py index 5c504f6..7bc91e9 100644 --- a/src/cluster/ffmpeg_apps/stream_record_as_hls.py +++ b/src/cluster/ffmpeg_apps/stream_record_as_hls.py @@ -1,5 +1,7 @@ import os import shutil +import modal + from ..ffmpeg_app import ffmpeg_worker_image, app, config, s3_mount @@ -11,7 +13,7 @@ with ffmpeg_worker_image.imports(): import sentry_sdk import backoff import httpx - import modal + import m3u8 from loguru import logger from typing import Optional, Tuple from modal import current_function_call_id @@ -163,13 +165,19 @@ with ffmpeg_worker_image.imports(): playlist_observer.schedule(playlist_handler, path=volume_output_dir, recursive=False) playlist_observer.start() try: - await VideoUtils.ffmpeg_stream_record_as_hls(stream_url=stream_url, + playlist = await VideoUtils.ffmpeg_stream_record_as_hls(stream_url=stream_url, first_segment_duration=first_segment_duration, segment_duration=segment_duration, stream_content_timeout=recording_timeout, stream_monitor_timeout=monitor_timeout, segments_output_dir=volume_output_dir, playlist_output_dir=volume_output_dir) + # 确保playlist有 #EXT-X-ENDLIST 结尾 + pl = m3u8.load(playlist) + pl.is_endlist = True + pl.dump(playlist) + logger.info(f"[End] playlist = {playlist}") + except Exception as e: logger.exception(e) playlist_observer.stop()