diff --git a/packages/config/base.py b/packages/config/base.py index 6d532084b..87f463ef3 100755 --- a/packages/config/base.py +++ b/packages/config/base.py @@ -145,6 +145,13 @@ class SharedSettings(BaseSettings): gpu_encode_relay_base_url: str = Field( default="", validation_alias=AliasChoices("GPU_ENCODE_RELAY_BASE_URL", "gpu_encode_relay_base_url"), + description="P4000 回传结果用的外部 URL(worker 通过该 URL 提供给 P4000 PUT),如 http://100.69.73.60:8092", + ) + # Worker→API 内网直连 URL(Docker DNS),用于 worker 自己下载/清理 relay 文件。 + # 未配置时回退到 relay_base_url(本地开发/单节点)。 + gpu_encode_relay_internal_base_url: str = Field( + default="", + validation_alias=AliasChoices("GPU_ENCODE_RELAY_INTERNAL_BASE_URL", "gpu_encode_relay_internal_base_url"), ) # 同步调用超时(秒):含编码+上传回传,5 分钟足够短视频 gpu_encode_sync_timeout: int = 300 diff --git a/packages/shared/gpu_encoder.py b/packages/shared/gpu_encoder.py index 2a4dcfbe1..c93bca60e 100644 --- a/packages/shared/gpu_encoder.py +++ b/packages/shared/gpu_encoder.py @@ -3,11 +3,13 @@ 完整链路(encode_video_file): 1. CPU 滤镜已在本地生成 mezzanine 中间片(libx264 ultrafast) 2. 上传 mezzanine 到 OSS 临时前缀,拿到签名 GET URL - 3. 生成 relay 一次性 key,构造带 token 的 PUT URL(指向 API 服务 /api/v1/internal/gpu-relay/) - 4. POST P4000 /api/render/sync:inputs={"in.mp4": ""}, output_url="" + 3. 生成 relay 一次性 key,构造两个带 token 的 URL: + - put_url:给 P4000 回传结果,走 relay_base_url(外部可达,通常是 host:port 经 nginx) + - get/del_url:worker 自己下载+清理用,走 relay_internal_base_url(Docker DNS 直连 API) + 4. POST P4000 /api/render/sync:inputs={"in.mp4": ""}, output_url="" ffmpeg_args: -i in.mp4 [-vf ] -c:v h264_nvenc ... -an/-c:a aac -f mp4 pipe:1 - 5. P4000 编码完成后 PUT 最终 mp4 到 relay,API 服务落盘到 /app/generated/gpu_relay/ - 6. 本客户端从 relay GET 下载最终文件到 output_path,然后调用 relay DELETE 清理 + 5. P4000 编码完成后 PUT 最终 mp4 到 put_url,API 服务落盘到 /app/generated/gpu_relay/ + 6. 本客户端通过 get_url(Docker 内网)下载最终文件到 output_path,然后 DELETE 清理 7. 删除 OSS 临时 mezzanine 任何环节失败抛 GpuEncodeError,调用方应 fallback 到 CPU libx264。 @@ -55,6 +57,7 @@ class GpuEncoderClient: endpoint: str, relay_base_url: str, *, + relay_internal_base_url: str = "", sync_timeout: int = 300, health_timeout: float = 3.0, vcodec: str = "h264_nvenc", @@ -66,6 +69,11 @@ class GpuEncoderClient: ) -> None: self.endpoint = endpoint.rstrip("/") self.relay_base_url = relay_base_url.rstrip("/") + # Worker→API 内网访问地址(Docker DNS 直连,如 http://xiaoxia-api-staging:8000)。 + # 未配置时回退到 relay_base_url(本地开发/单节点)。 + self.relay_internal_base_url = ( + relay_internal_base_url.rstrip("/") if relay_internal_base_url else self.relay_base_url + ) self.sync_timeout = sync_timeout self.health_timeout = health_timeout self.vcodec = vcodec @@ -75,6 +83,22 @@ class GpuEncoderClient: self._relay_secret = relay_secret self.oss_tmp_prefix = oss_tmp_prefix.rstrip("/") + "/" if oss_tmp_prefix else "tmp/gpu-mezzanine/" + RELAY_PATH_PREFIX = "/api/v1/internal/gpu-relay" + + # ------------------------------------------------------------------ + # URL builders + # ------------------------------------------------------------------ + def _relay_url_from_base(self, base_url: str, key: str, secret: str) -> str: + return f"{base_url}{self.RELAY_PATH_PREFIX}/{key}?token={urllib.parse.quote(secret, safe='')}" + + def _relay_put_url(self, key: str, secret: str) -> str: + """给 P4000 回传结果用的 URL(外部可达)。""" + return self._relay_url_from_base(self.relay_base_url, key, secret) + + def _relay_internal_url(self, key: str, secret: str) -> str: + """Worker 自己 GET/DELETE 用的 URL(Docker 内网)。""" + return self._relay_url_from_base(self.relay_internal_base_url, key, secret) + # ------------------------------------------------------------------ # Health # ------------------------------------------------------------------ @@ -128,12 +152,12 @@ class GpuEncoderClient: input_url, oss_key = self._upload_mezzanine(mezzanine_path) logger.debug("[gpu-encoder] mezzanine uploaded: oss_key=%s", oss_key) - # 2. prepare relay put/get URLs + # 2. prepare relay URLs (PUT 走外部 URL 给 P4000;GET/DELETE 走内部 Docker 网络) relay_key = uuid.uuid4().hex secret = self._get_relay_secret() - put_url = self._relay_url(relay_key, secret) - get_url = put_url - del_url = put_url # same URL, DELETE method + put_url = self._relay_put_url(relay_key, secret) + get_url = self._relay_internal_url(relay_key, secret) + del_url = get_url # 内部 URL,DELETE method # 3. build ffmpeg args ffmpeg_args = ["-y", "-i", "in.mp4"] @@ -212,9 +236,6 @@ class GpuEncoderClient: raise GpuEncodeError("GPU_ENCODE_RELAY_SECRET not set") return secret - def _relay_url(self, key: str, secret: str) -> str: - return f"{self.relay_base_url}/api/v1/internal/gpu-relay/{key}?token={urllib.parse.quote(secret, safe='')}" - def _post_sync(self, body: dict[str, Any], *, mezzanine_path: Path) -> dict[str, Any]: url = f"{self.endpoint}/api/render/sync" req_timeout = body.get("timeout", self.sync_timeout) + 60 @@ -337,11 +358,13 @@ def _build_client_from_settings() -> Optional[GpuEncoderClient]: return None endpoint = (getattr(settings, "gpu_encode_endpoint", "") or "").strip() relay = (getattr(settings, "gpu_encode_relay_base_url", "") or "").strip() + relay_internal = (getattr(settings, "gpu_encode_relay_internal_base_url", "") or "").strip() if not endpoint or not relay: return None return GpuEncoderClient( endpoint=endpoint, relay_base_url=relay, + relay_internal_base_url=relay_internal, sync_timeout=getattr(settings, "gpu_encode_sync_timeout", 300), health_timeout=getattr(settings, "gpu_encode_health_timeout", 3.0), vcodec=getattr(settings, "gpu_encode_vcodec", "h264_nvenc"),