{"id":217,"date":"2025-05-01T14:40:17","date_gmt":"2025-05-01T06:40:17","guid":{"rendered":"https:\/\/knoka.vip\/?p=217"},"modified":"2025-05-01T14:48:45","modified_gmt":"2025-05-01T06:48:45","slug":"%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b","status":"publish","type":"post","link":"https:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/","title":{"rendered":"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\" id=\"header-id-1\">\u670d\u52a1\u5668\u51c6\u5907<\/h3>\n\n\n\n<p>\u9996\u5148\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u670d\u52a1\u5668\u7684\u51c6\u5907\uff0c\u8fd9\u91cc\u51c6\u5907\u7684\u662f RTX-4090 \u670d\u52a1\u5668<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/image-1024x74.png'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"74\" data-original=\"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/image-1024x74.png\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" class=\"wp-image-218\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/div><\/figure>\n\n\n\n<p>\u8fde\u63a5\u6211\u4eec\u5df2\u7ecf\u521b\u5efa\u597d\u7684\u670d\u52a1\u5668\uff0c\u8fd9\u91cc\u53ef\u4f7f\u7528 MobaXterm \u8fdb\u884c ssh \u8fde\u63a5<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ssh funhpc@IP\u5730\u5740<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u73af\u5883\u51c6\u5907<\/h3>\n\n\n\n<p>\u63a5\u7740\u5148\u62c9\u53d6\u5b98\u65b9\u4ee3\u7801\uff0c\u7136\u540e\u521b\u5efa\u4e00\u4e2a\u865a\u62df\u73af\u5883\uff0c\u518d\u5b89\u88c5\u5176\u5bf9\u5e94\u7684\u4f9d\u8d56\u5e93\uff0c\u5982\u679c\u4e0b\u8f7d\u8fc7\u6162\uff0c\u53ef\u91c7\u7528\u4e0b\u9762\u6dfb\u52a0\u4ee3\u7406\u7684\u65b9\u5f0f\u8fdb\u884c\u4e0b\u8f7d<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/bytedance\/LatentSync.git\n# git clone https:\/\/gitproxy.click\/https:\/\/github.com\/bytedance\/LatentSync.git<\/code><\/pre>\n\n\n\n<p>\u63a5\u7740\u53ef\u8fd0\u884c\u5b98\u65b9\u63d0\u4f9b\u7684\u4e00\u4ef6\u8bbe\u7f6e\u73af\u5883\u7684\u811a\u672c\u8fdb\u884c\u73af\u5883\u7684\u51c6\u5907<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>source setup_env.sh<\/code><\/pre>\n\n\n\n<p>\u4f46\u662f\u6ce8\u610f\u8fd9\u91cc\u53ef\u80fd\u4e0b\u8f7d\u901f\u5ea6\u8fc7\u6162\uff0c\u6211\u8fd9\u91cc\u5c06\u6a21\u578b\u4e0b\u8f7d\u806ahuggingface\u66f4\u6362\u6210\u4e86modelscope<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n\n# Create a new conda environment\nconda create -y -n latentsync python=3.10.13\nconda activate latentsync\n\n# Install ffmpeg\nconda install -y -c conda-forge ffmpeg \n\n# Python dependencies\npip install -r requirements.txt -i  https:\/\/repo.huaweicloud.com\/repository\/pypi\/simple\npip install modelscope -i  https:\/\/repo.huaweicloud.com\/repository\/pypi\/simple\n\n# OpenCV dependencies\napt -y install libgl1\n\n# Download the checkpoints required for inference from HuggingFace\nmodelscope download --model ByteDance\/LatentSync-1.5 whisper\/tiny.pt --local_dir checkpoints\nmodelscope download --model ByteDance\/LatentSync-1.5 latentsync_unet.pt --local_dir checkpoints\nmodelscope download --model zhuzhukeji\/sd-vae-ft-mse --local_dir stabilityai\/sd-vae-ft-mse<\/code><\/pre>\n\n\n\n<p>\u5982\u679c\u4e0b\u8f7d\u6210\u529f\uff0ccheckpoints\u00a0\u6587\u4ef6\u5939\u5e94\u5982\u4e0b\u6240\u793a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/checkpoints\/\n|-- latentsync_unet.pt\n|-- whisper\n|   `-- tiny.pt<\/code><\/pre>\n\n\n\n<p>\u5b98\u65b9\u63d0\u4f9b\u7684\u63a8\u7406\u811a\u672c\u4e3a.\/inference.sh\uff0c\u53ef\u76f4\u63a5\u8fd0\u884c\u4f53\u9a8c<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">FastAPI\u90e8\u7f72\u5b9e\u73b0<\/h3>\n\n\n\n<p>\u4f46\u8fd9\u91cc\u6211\u4eec\u4e3a\u5b9e\u73b0FastAPI\u7684\u90e8\u7f72\uff0c\u9700\u8981\u89c2\u5bdf\u5185\u90e8\u4ee3\u7801\uff0c\u53ef\u4ee5\u770b\u5230\u5176\u5185\u90e8\u4e5f\u662f\u6267\u884c\u4e00\u4e2apython\u811a\u672c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python -m scripts.inference \\\n    --unet_config_path \"configs\/unet\/stage2.yaml\" \\\n    --inference_ckpt_path \"checkpoints\/latentsync_unet.pt\" \\\n    --inference_steps 20 \\\n    --guidance_scale 2.0 \\\n    --video_path \"assets\/demo1_video.mp4\" \\\n    --audio_path \"assets\/demo1_audio.wav\" \\\n    --video_out_path \"video_out.mp4\"<\/code><\/pre>\n\n\n\n<p>\u56e0\u6b64\u53bb\u89c2\u5bdfscripts\u6587\u4ef6\u5939\u4e0b\u7684inference\u811a\u672c\uff0c\u8fd9\u4e2a\u5373\u4e3a\u5b98\u65b9\u7684\u6574\u4e2a\u63a8\u7406\u4ee3\u7801\uff0c\u5176\u6309\u7167\u5982\u4e0b\u6d41\u7a0b\u8fdb\u884c<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u8f93\u5165\u68c0\u67e5 <\/strong>\uff1a\u9a8c\u8bc1\u8f93\u5165\u7684\u89c6\u9891\u548c\u97f3\u9891\u6587\u4ef6\u8def\u5f84\u662f\u5426\u5b58\u5728\u3002<\/li>\n\n\n\n<li><strong>\u8bbe\u5907\u4e0e\u7cbe\u5ea6\u8bbe\u7f6e <\/strong>\uff1a\u6839\u636eGPU\u652f\u6301\u60c5\u51b5\u9009\u62e9\u4f7f\u7528 <code>float16<\/code> \u6216 <code>float32<\/code> \u7cbe\u5ea6\u4ee5\u4f18\u5316\u8ba1\u7b97\u6548\u7387\u3002<\/li>\n\n\n\n<li><strong>\u6a21\u578b\u52a0\u8f7d <\/strong>\uff1a\n<ul class=\"wp-block-list\">\n<li>\u52a0\u8f7d <strong>DDIM Scheduler <\/strong>\u7528\u4e8e\u63a8\u7406\u9636\u6bb5\u7684\u53bb\u566a\u8c03\u5ea6\uff1b<\/li>\n\n\n\n<li>\u6839\u636e\u914d\u7f6e\u52a0\u8f7d\u5bf9\u5e94\u7684 <strong>Whisper \u6a21\u578b <\/strong>\u63d0\u53d6\u97f3\u9891\u7279\u5f81\uff1b<\/li>\n\n\n\n<li>\u52a0\u8f7d <strong>VAE \u6a21\u578b <\/strong>\u7528\u4e8e\u56fe\u50cf\u7a7a\u95f4\u4e0e\u6f5c\u5728\u7a7a\u95f4\u4e4b\u95f4\u7684\u7f16\u7801\/\u89e3\u7801\uff1b<\/li>\n\n\n\n<li>\u52a0\u8f7d\u9884\u8bad\u7ec3\u7684 <strong>3D UNet \u6761\u4ef6\u6a21\u578b <\/strong>\u4f5c\u4e3a\u53bb\u566a\u7f51\u7edc\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u521d\u59cb\u5316 Pipeline <\/strong>\uff1a\u5c06\u4e0a\u8ff0\u7ec4\u4ef6\u6784\u5efa\u4e3a\u4e00\u4e2a <code>LipsyncPipeline<\/code>\uff0c\u4e13\u95e8\u7528\u4e8e\u97f3\u9891\u9a71\u52a8\u7684\u89c6\u9891\u751f\u6210\u3002<\/li>\n\n\n\n<li><strong>\u6267\u884c\u63a8\u7406 <\/strong>\uff1a\u901a\u8fc7\u4f20\u5165\u7684\u89c6\u9891\u3001\u97f3\u9891\u8def\u5f84\u7b49\u53c2\u6570\u8fdb\u884c\u63a8\u7406\uff0c\u751f\u6210\u4e0e\u97f3\u9891\u540c\u6b65\u7684\u89c6\u9891\uff0c\u5e76\u4fdd\u5b58\u7ed3\u679c\u3002<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>import argparse\nimport os\nfrom omegaconf import OmegaConf\nimport torch\nfrom diffusers import AutoencoderKL, DDIMScheduler\nfrom latentsync.models.unet import UNet3DConditionModel\nfrom latentsync.pipelines.lipsync_pipeline import LipsyncPipeline\nfrom accelerate.utils import set_seed\nfrom latentsync.whisper.audio2feature import Audio2Feature\n\n\ndef main(config, args):\n    if not os.path.exists(args.video_path):\n        raise RuntimeError(f\"Video path '{args.video_path}' not found\")\n    if not os.path.exists(args.audio_path):\n        raise RuntimeError(f\"Audio path '{args.audio_path}' not found\")\n\n    # Check if the GPU supports float16\n    is_fp16_supported = torch.cuda.is_available() and torch.cuda.get_device_capability()&#91;0] > 7\n    dtype = torch.float16 if is_fp16_supported else torch.float32\n\n    print(f\"Input video path: {args.video_path}\")\n    print(f\"Input audio path: {args.audio_path}\")\n    print(f\"Loaded checkpoint path: {args.inference_ckpt_path}\")\n\n    scheduler = DDIMScheduler.from_pretrained(\"configs\")\n\n    if config.model.cross_attention_dim == 768:\n        whisper_model_path = \"checkpoints\/whisper\/small.pt\"\n    elif config.model.cross_attention_dim == 384:\n        whisper_model_path = \"checkpoints\/whisper\/tiny.pt\"\n    else:\n        raise NotImplementedError(\"cross_attention_dim must be 768 or 384\")\n\n    audio_encoder = Audio2Feature(\n        model_path=whisper_model_path,\n        device=\"cuda\",\n        num_frames=config.data.num_frames,\n        audio_feat_length=config.data.audio_feat_length,\n    )\n\n    vae = AutoencoderKL.from_pretrained(\"stabilityai\/sd-vae-ft-mse\", torch_dtype=dtype)\n    vae.config.scaling_factor = 0.18215\n    vae.config.shift_factor = 0\n\n    denoising_unet, _ = UNet3DConditionModel.from_pretrained(\n        OmegaConf.to_container(config.model),\n        args.inference_ckpt_path,\n        device=\"cpu\",\n    )\n\n    denoising_unet = denoising_unet.to(dtype=dtype)\n\n    pipeline = LipsyncPipeline(\n        vae=vae,\n        audio_encoder=audio_encoder,\n        denoising_unet=denoising_unet,\n        scheduler=scheduler,\n    ).to(\"cuda\")\n\n    if args.seed != -1:\n        set_seed(args.seed)\n    else:\n        torch.seed()\n\n    print(f\"Initial seed: {torch.initial_seed()}\")\n\n    pipeline(\n        video_path=args.video_path,\n        audio_path=args.audio_path,\n        video_out_path=args.video_out_path,\n        video_mask_path=args.video_out_path.replace(\".mp4\", \"_mask.mp4\"),\n        num_frames=config.data.num_frames,\n        num_inference_steps=args.inference_steps,\n        guidance_scale=args.guidance_scale,\n        weight_dtype=dtype,\n        width=config.data.resolution,\n        height=config.data.resolution,\n        mask_image_path=config.data.mask_image_path,\n    )\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--unet_config_path\", type=str, default=\"configs\/unet.yaml\")\n    parser.add_argument(\"--inference_ckpt_path\", type=str, required=True)\n    parser.add_argument(\"--video_path\", type=str, required=True)\n    parser.add_argument(\"--audio_path\", type=str, required=True)\n    parser.add_argument(\"--video_out_path\", type=str, required=True)\n    parser.add_argument(\"--inference_steps\", type=int, default=20)\n    parser.add_argument(\"--guidance_scale\", type=float, default=1.0)\n    parser.add_argument(\"--seed\", type=int, default=1247)\n    args = parser.parse_args()\n\n    config = OmegaConf.load(args.unet_config_path)\n\n    main(config, args)<\/code><\/pre>\n\n\n\n<p>\u68b3\u7406\u5b8c\u6bd5\u8fd9\u4e2a\u4ee3\u7801\u540e\uff0c\u6211\u4eec\u5df2\u7ecf\u53ef\u4ee5\u63d0\u53d6\u51fa\u5176\u6838\u5fc3\u6b65\u9aa4\uff0c\u5728\u8fdb\u884cFastAPI\u7684\u90e8\u7f72\uff0c\u8fd9\u91cc\u76f4\u63a5\u63d0\u4f9b\u7ed9\u5927\u5bb6\u51c6\u5907\u597d\u7684\u811a\u672c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from contextlib import asynccontextmanager\nfrom fastapi import FastAPI, HTTPException, status, Depends, Request\nfrom fastapi.security import HTTPBearer, HTTPAuthorizationCredentials\nfrom fastapi.responses import JSONResponse\nfrom fastapi.exceptions import RequestValidationError\nfrom pydantic import BaseModel, Field\nfrom enum import Enum\nfrom fastapi.staticfiles import StaticFiles\nfrom typing import Optional, Dict, List\nimport uuid\nimport os\nimport asyncio\nimport shutil\nimport logging\nfrom pathlib import Path\nfrom omegaconf import OmegaConf\nimport torch\nimport subprocess\nfrom concurrent.futures import ThreadPoolExecutor\nfrom datetime import datetime\nfrom latentsync.whisper.audio2feature import Audio2Feature\nfrom diffusers import AutoencoderKL, DDIMScheduler\nfrom latentsync.models.unet import UNet3DConditionModel\nfrom latentsync.pipelines.lipsync_pipeline import LipsyncPipeline\nfrom accelerate.utils import set_seed\nimport base64\nimport mimetypes\n\n# \u65e5\u5fd7\u914d\u7f6e\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n# \u914d\u7f6e\u53c2\u6570\nMODEL_CONFIG_PATH = \"configs\/unet\/stage2.yaml\"\nMODEL_CKPT_PATH = \"checkpoints\/latentsync_unet.pt\"\nTEMP_DIR = Path(\"\/data\/coding\/tmp\")\nTEMP_DIR.mkdir(exist_ok=True)\nSTATIC_DIR = Path(\"\/data\/coding\/static\/results\")\nSTATIC_DIR.mkdir(parents=True, exist_ok=True)\nMAX_CONCURRENT_TASKS = 5\nVALID_API_KEYS = {\"\u5bc6\u94a5\"}  # \u4ece\u73af\u5883\u53d8\u91cf\u8bfb\u53d6\u66f4\u5b89\u5168\n\n# \u8ba4\u8bc1\u9a8c\u8bc1\nsecurity = HTTPBearer(auto_error=False)\n\n# \u4efb\u52a1\u72b6\u6001\u679a\u4e3e\nclass TaskStatus(str, Enum):\n    IN_QUEUE = \"InQueue\"\n    IN_PROGRESS = \"InProgress\"\n    SUCCEEDED = \"Succeed\"\n    FAILED = \"Failed\"\n    CANCELLED = \"Cancelled\"\n\n# \u4efb\u52a1\u6570\u636e\u6a21\u578b\nclass TaskData(BaseModel):\n    status: TaskStatus = TaskStatus.IN_QUEUE\n    params: dict\n    created_at: int = Field(default_factory=lambda: int(datetime.now().timestamp()))\n    started_at: Optional&#91;int] = None\n    completed_at: Optional&#91;int] = None\n    download_url: Optional&#91;str] = None\n    reason: Optional&#91;str] = None\n\n# \u8bf7\u6c42\u6a21\u578b\nclass InferenceRequest(BaseModel):\n    video_source: str = Field(..., description=\"Base64 encoded video\")\n    audio_source: str = Field(..., description=\"Base64 encoded audio\")\n    inference_steps: int = 20\n    guidance_scale: float = 2.0\n    seed: int = 1247\n\n# \u72b6\u6001\u67e5\u8be2\u8bf7\u6c42\u6a21\u578b\nclass TaskStatusRequest(BaseModel):\n    request_id: str\n\ndef load_pipeline():\n    try:\n        config = OmegaConf.load(MODEL_CONFIG_PATH)\n        dtype = torch.float32\n\n        whisper_model_path = \"checkpoints\/whisper\/small.pt\" if config.model.cross_attention_dim == 768 else \"checkpoints\/whisper\/tiny.pt\"\n      \n        audio_encoder = Audio2Feature(\n            model_path=whisper_model_path,\n            device=\"cuda\",\n            num_frames=config.data.num_frames,\n            audio_feat_length=config.data.audio_feat_length,\n        )\n\n        vae = AutoencoderKL.from_pretrained(\"stabilityai\/sd-vae-ft-mse\", torch_dtype=dtype)\n        vae.config.scaling_factor = 0.18215\n        vae.config.shift_factor = 0\n\n        denoising_unet, _ = UNet3DConditionModel.from_pretrained(\n            OmegaConf.to_container(config.model),\n            MODEL_CKPT_PATH,\n            device=\"cpu\",\n        )\n        denoising_unet = denoising_unet.to(dtype=dtype).to(\"cpu\")\n\n        pipeline = LipsyncPipeline(\n            vae=vae,\n            audio_encoder=audio_encoder,\n            denoising_unet=denoising_unet,\n            scheduler=DDIMScheduler.from_pretrained(\"configs\"),\n        ).to(\"cuda\")\n\n        return pipeline, config\n\n    except Exception as e:\n        logger.error(\"Model loading failed:\", exc_info=True)\n        raise\n\n# \u751f\u547d\u5468\u671f\u7ba1\u7406\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    try:\n        # \u68c0\u67e5FFmpeg\n        subprocess.run(&#91;\"ffmpeg\", \"-version\"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n        logger.info(\"FFmpeg check passed\")\n        \n        # \u68c0\u67e5\u6a21\u578b\u6587\u4ef6\n        if not Path(MODEL_CKPT_PATH).exists():\n            raise RuntimeError(f\"Checkpoint not found at {MODEL_CKPT_PATH}\")\n        if not Path(MODEL_CONFIG_PATH).exists():\n            raise RuntimeError(f\"Config not found at {MODEL_CONFIG_PATH}\")\n        \n        # \u52a0\u8f7d\u6a21\u578b\n        app.state.pipeline, app.state.config = load_pipeline()\n        logger.info(\"Model loaded successfully\")\n        \n        # \u521d\u59cb\u5316\u4efb\u52a1\u7cfb\u7edf\n        app.state.tasks: Dict&#91;str, TaskData] = {}\n        app.state.pending_queue: List&#91;str] = &#91;]\n        app.state.task_lock = asyncio.Lock()\n        app.state.semaphore = asyncio.Semaphore(MAX_CONCURRENT_TASKS)\n        app.state.base_url = \"IP+\u7aef\u53e3\/static\/results\"  # \u4fee\u6539\u4e3a\u5b9e\u9645\u57df\u540d\n        \n        # \u542f\u52a8\u540e\u53f0\u5904\u7406\u5668\n        asyncio.create_task(task_processor())\n        \n        yield\n    \n    except Exception as e:\n        logger.error(\"Startup failed:\", exc_info=True)\n        raise\n\napp = FastAPI(title=\"Lipsync API\", version=\"1.0.0\", lifespan=lifespan)\n# \u5173\u952e\u914d\u7f6e\uff1a\u6302\u8f7d\u9759\u6001\u6587\u4ef6\u76ee\u5f55\napp.mount(\"\/static\", StaticFiles(directory=\"\/data\/coding\/static\"), name=\"static\")\n\n# \u8ba4\u8bc1\u4f9d\u8d56\nasync def verify_auth(credentials: HTTPAuthorizationCredentials = Depends(security)):\n    if not credentials or credentials.scheme != \"Bearer\":\n        raise HTTPException(401, {\"status\": \"Failed\", \"reason\": \"\u65e0\u6548\u7684\u8ba4\u8bc1\u51ed\u8bc1\"})\n    if credentials.credentials not in VALID_API_KEYS:\n        raise HTTPException(401, {\"status\": \"Failed\", \"reason\": \"\u65e0\u6548\u7684API\u5bc6\u94a5\"})\n    return True\n\n@app.exception_handler(HTTPException)\nasync def http_exception_handler(request, exc: HTTPException):\n    return JSONResponse(\n        status_code=exc.status_code,\n        content=exc.detail\n    )\n\n@app.exception_handler(RequestValidationError)\nasync def validation_exception_handler(request: Request, exc: RequestValidationError):\n    error_msg = exc.errors()&#91;0]&#91;'msg']\n    return JSONResponse(\n        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,\n        content={\"status\": \"Failed\", \"reason\": f\"\u53c2\u6570\u6821\u9a8c\u5931\u8d25: {error_msg}\"}\n    )\n# \u63d0\u4ea4\u4efb\u52a1\u63a5\u53e3\n@app.post(\"\/lipsync\/submit\", response_model=dict)\nasync def submit_task(\n    request: InferenceRequest,\n    auth: bool = Depends(verify_auth)\n):\n    request_id = str(uuid.uuid4())\n    async with app.state.task_lock:\n        app.state.tasks&#91;request_id] = TaskData(params=request.dict())\n        app.state.pending_queue.append(request_id)\n    return {\"request_id\": request_id}\n\n# \u67e5\u8be2\u72b6\u6001\u63a5\u53e3\n@app.post(\"\/lipsync\/status\", response_model=dict)\nasync def get_status(\n    request: TaskStatusRequest,\n    auth: bool = Depends(verify_auth)\n):\n    task = app.state.tasks.get(request.request_id)\n    if not task:\n        raise HTTPException(\n            status_code=404,\n            detail={\"status\": \"Failed\", \"reason\": \"\u65e0\u6548\u7684\u4efb\u52a1ID\"}\n        )\n    \n    # \u9ed8\u8ba4\u54cd\u5e94\u7ed3\u6784\uff08\u5305\u542b results: null\uff09\n    response = {\n        \"status\": task.status.value,\n        \"reason\": task.reason,\n        \"queue_position\": None,  # \u9ed8\u8ba4\u4e3a null\n        \"results\": None          # \u9ed8\u8ba4\u4e3a null\n    }\n    \n    # \u5904\u7406\u961f\u5217\u4f4d\u7f6e\uff08\u4ec5 InQueue \u72b6\u6001\uff09\n    if task.status == TaskStatus.IN_QUEUE:\n        try:\n            queue_pos = app.state.pending_queue.index(request.request_id) + 1\n            response&#91;\"queue_position\"] = queue_pos\n        except ValueError:\n            response&#91;\"queue_position\"] = 0\n    \n    # \u5904\u7406\u6210\u529f\u72b6\u6001\n    if task.status == TaskStatus.SUCCEEDED:\n        response&#91;\"results\"] = {\n            \"video\": &#91;{\"url\": task.download_url}],\n            \"timings\": {\n                \"inference\": task.completed_at - task.started_at\n            }\n        }\n    \n    return response\n\n# \u53d6\u6d88\u4efb\u52a1\u63a5\u53e3\n@app.post(\"\/lipsync\/cancel\", response_model=dict)\nasync def cancel_task(\n    request: TaskStatusRequest,\n    auth: bool = Depends(verify_auth)\n):\n    async with app.state.task_lock:\n        task = app.state.tasks.get(request.request_id)\n        if not task:\n            raise HTTPException(404, {\"status\": \"Failed\", \"reason\": \"\u65e0\u6548\u7684\u4efb\u52a1ID\"})\n        \n        if task.status != TaskStatus.IN_QUEUE:\n            raise HTTPException(400, {\"status\": \"Failed\", \"reason\": f\"\u4ec5\u5141\u8bb8\u53d6\u6d88\u6392\u961f\u4efb\u52a1\uff0c\u5f53\u524d\u72b6\u6001: {task.status}\"})\n        \n        try:\n            app.state.pending_queue.remove(request.request_id)\n        except ValueError:\n            pass\n        \n        task.status = TaskStatus.CANCELLED\n        task.reason = \"\u7528\u6237\u4e3b\u52a8\u53d6\u6d88\"\n    \n    return {\"status\": \"Succeed\", \"reason\": \"\u53d6\u6d88\u6392\u961f\u4efb\u52a1\u6210\u529f\"}\n\n# \u540e\u53f0\u4efb\u52a1\u5904\u7406\u5668\nasync def task_processor():\n    executor = ThreadPoolExecutor(max_workers=MAX_CONCURRENT_TASKS)\n    while True:\n        async with app.state.semaphore:\n            request_id = await get_next_task()\n            if request_id:\n                loop = asyncio.get_event_loop()\n                await loop.run_in_executor(executor, lambda: process_task(request_id))\n        await asyncio.sleep(0.1)\n\nasync def get_next_task():\n    async with app.state.task_lock:\n        if app.state.pending_queue:\n            return app.state.pending_queue.pop(0)\n    return None\n\ndef process_task(request_id: str):\n    task = app.state.tasks.get(request_id)\n    if not task:\n        return\n    \n    try:\n        task.status = TaskStatus.IN_PROGRESS\n        task.started_at = int(datetime.now().timestamp())\n        \n        # \u521b\u5efa\u4e34\u65f6\u76ee\u5f55\n        temp_dir = TEMP_DIR \/ request_id\n        temp_dir.mkdir(parents=True, exist_ok=True)\n        \n        # \u4fdd\u5b58\u5a92\u4f53\u6587\u4ef6\n        video_path = save_media(task.params&#91;'video_source'], temp_dir, 'video')\n        audio_path = save_media(task.params&#91;'audio_source'], temp_dir, 'audio')\n        \n        # \u51c6\u5907\u8f93\u51fa\u8def\u5f84\n        output_filename = f\"output_{request_id}{video_path.suffix}\"\n        output_path = temp_dir \/ output_filename\n        mask_path = temp_dir \/ f\"mask_{output_filename}\"\n        \n        # \u6267\u884c\u63a8\u7406\n        set_seed(task.params&#91;'seed']) if task.params&#91;'seed'] != -1 else torch.seed()\n        \n        app.state.pipeline(\n            video_path=str(video_path),\n            audio_path=str(audio_path),\n            video_out_path=str(output_path),\n            video_mask_path=str(mask_path),\n            num_frames=app.state.config.data.num_frames,\n            num_inference_steps=task.params&#91;'inference_steps'],\n            guidance_scale=task.params&#91;'guidance_scale'],\n            weight_dtype=torch.float32,\n            width=app.state.config.data.resolution,\n            height=app.state.config.data.resolution,\n            mask_image_path=app.state.config.data.mask_image_path,\n        )\n        \n        # \u4fdd\u5b58\u7ed3\u679c\n        result_path = STATIC_DIR \/ output_filename\n        shutil.move(str(output_path), str(result_path))\n        task.download_url = f\"{app.state.base_url}\/{output_filename}\"\n        \n        task.status = TaskStatus.SUCCEEDED\n        task.completed_at = int(datetime.now().timestamp())\n        \n    except Exception as e:\n        task.status = TaskStatus.FAILED\n        task.reason = str(e)\n        task.completed_at = int(datetime.now().timestamp())\n        logger.error(f\"Task {request_id} failed: {str(e)}\")\n    finally:\n        shutil.rmtree(temp_dir, ignore_errors=True)\n\ndef save_media(content: str, temp_dir: Path, media_type: str) -> Path:\n    try:\n        data = base64.b64decode(content)\n    except Exception as e:\n        raise ValueError(f\"Invalid base64 {media_type} content: {str(e)}\")\n\n    ext_map = {'video': '.mp4', 'audio': '.wav'}\n    ext = ext_map&#91;media_type]\n    path = temp_dir \/ f\"input_{media_type}{ext}\"\n    \n    with open(path, 'wb') as f:\n        f.write(data)\n    \n    detected_type, _ = mimetypes.guess_type(path.name)\n    type_whitelist = {\n        'video': &#91;'video\/mp4', 'video\/quicktime', 'video\/x-msvideo'],\n        'audio': &#91;'audio\/wav', 'audio\/x-wav']\n    }\n    \n    if detected_type not in type_whitelist&#91;media_type]:\n        path.unlink()\n        raise ValueError(f\"Unsupported {media_type} format: {detected_type}\")\n    \n    return path\n\nif __name__ == \"__main__\":\n    import uvicorn\n    uvicorn.run(app, host=\"0.0.0.0\", port=8081)<\/code><\/pre>\n\n\n\n<p>\u4e4b\u540e\u518d\u8fdb\u5165latentsync\u865a\u62df\u73af\u5883\u540e\uff0c\u5728\u8fdb\u5165LatentSync\u540e\uff0c\u542f\u52a8\u6b64\u811a\u672c\uff0c\u5373\u53ef\u542f\u52a8\u6a21\u578b\u90e8\u7f72<\/p>\n\n\n\n<p>\u8be5 FastAPI \u670d\u52a1\u63d0\u4f9b\u4e86\u4e00\u4e2a<strong>\u9ad8\u5e76\u53d1\u3001\u5b89\u5168\u53ef\u63a7\u7684\u63a5\u53e3\u7cfb\u7edf <\/strong>\uff0c\u7528\u4e8e\u63a5\u6536\u97f3\u89c6\u9891\u8f93\u5165\uff0c\u4f7f\u7528\u6269\u6563\u6a21\u578b\u751f\u6210\u5bf9\u53e3\u578b\u89c6\u9891\uff0c\u5e76\u652f\u6301\u5f02\u6b65\u4efb\u52a1\u63d0\u4ea4\u3001\u72b6\u6001\u67e5\u8be2\u4e0e\u4efb\u52a1\u53d6\u6d88\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u811a\u672c\u89e3\u6790<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>1. \u521d\u59cb\u5316\u9636\u6bb5<\/strong><\/h4>\n\n\n\n<p><strong>\u6a21\u578b\u52a0\u8f7d <\/strong>\uff1a\u52a0\u8f7d\u914d\u7f6e\u6587\u4ef6\uff08<code>configs\/unet\/stage2.yaml<\/code>\uff09\uff1b\u6839\u636e\u914d\u7f6e\u52a0\u8f7d Whisper \u6a21\u578b\u7528\u4e8e\u97f3\u9891\u7279\u5f81\u63d0\u53d6\uff1b\u52a0\u8f7d VAE \u6a21\u578b\u7528\u4e8e\u6f5c\u5728\u7a7a\u95f4\u4e0e\u56fe\u50cf\u7a7a\u95f4\u7684\u8f6c\u6362\uff1b\u52a0\u8f7d\u9884\u8bad\u7ec3\u7684 <strong>3D UNet \u6a21\u578b <\/strong>\u4f5c\u4e3a\u6269\u6563\u6a21\u578b\u7684\u53bb\u566a\u7f51\u7edc\uff1b\u6784\u5efa <code>LipsyncPipeline<\/code> \u7ba1\u9053\u7528\u4e8e\u540e\u7eed\u63a8\u7406\uff1b<\/p>\n\n\n\n<p><strong>\u73af\u5883\u68c0\u67e5 <\/strong>\uff1a\u68c0\u67e5 FFmpeg \u662f\u5426\u53ef\u7528\uff08\u7528\u4e8e\u97f3\u89c6\u9891\u5904\u7406\uff09\uff1b\u68c0\u67e5\u6a21\u578b\u6743\u91cd\u548c\u914d\u7f6e\u6587\u4ef6\u662f\u5426\u5b58\u5728\uff1b<\/p>\n\n\n\n<p><strong>\u4efb\u52a1\u961f\u5217\u7ba1\u7406\u521d\u59cb\u5316 <\/strong>\uff1a\u8bbe\u7f6e\u6700\u5927\u5e76\u53d1\u4efb\u52a1\u6570\uff08\u9632\u6b62\u8d44\u6e90\u8017\u5c3d\uff09\uff1b\u521b\u5efa\u4efb\u52a1\u72b6\u6001\u5b57\u5178\u3001\u5f85\u5904\u7406\u961f\u5217\u548c\u9501\u673a\u5236\uff0c\u786e\u4fdd\u7ebf\u7a0b\u5b89\u5168\uff1b\u542f\u52a8\u540e\u53f0\u4efb\u52a1\u5904\u7406\u5668 <code>task_processor<\/code> \u6765\u9010\u4e2a\u6267\u884c\u6392\u961f\u7684\u4efb\u52a1\uff1b<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>2. \u63a5\u53e3\u529f\u80fd<\/strong><\/h4>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>(1) \u63d0\u4ea4\u4efb\u52a1 <code>\/lipsync\/submit<\/code><\/strong><\/h5>\n\n\n\n<p>\u63a5\u6536 Base64 \u7f16\u7801\u7684\u89c6\u9891\u548c\u97f3\u9891\u6587\u4ef6\uff1b\u8fd4\u56de\u4e00\u4e2a\u552f\u4e00\u8bf7\u6c42 ID\uff08<code>request_id<\/code>\uff09\uff0c\u7528\u4e8e\u540e\u7eed\u67e5\u8be2\u4efb\u52a1\u7ed3\u679c\uff1b\u5c06\u4efb\u52a1\u52a0\u5165\u7b49\u5f85\u961f\u5217\uff1b<\/p>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>(2) \u67e5\u8be2\u4efb\u52a1\u72b6\u6001 <code>\/lipsync\/status<\/code><\/strong><\/h5>\n\n\n\n<p>\u8f93\u5165\uff1a\u4efb\u52a1 ID\uff1b<\/p>\n\n\n\n<p>\u8f93\u51fa\uff1a\u5f53\u524d\u4efb\u52a1\u72b6\u6001\uff08\u6392\u961f\u4e2d \/ \u8fdb\u884c\u4e2d \/ \u6210\u529f \/ \u5931\u8d25 \/ \u5df2\u53d6\u6d88\uff09\uff1b\u82e5\u6392\u961f\u4e2d\uff1a\u8fd4\u56de\u5f53\u524d\u5728\u961f\u5217\u4e2d\u7684\u4f4d\u7f6e\uff1b\u82e5\u6210\u529f\uff1a\u8fd4\u56de\u751f\u6210\u89c6\u9891\u7684\u4e0b\u8f7d\u94fe\u63a5\u53ca\u63a8\u7406\u65f6\u95f4\uff1b\u82e5\u5931\u8d25\uff1a\u8fd4\u56de\u9519\u8bef\u539f\u56e0\uff1b<\/p>\n\n\n\n<h5 class=\"wp-block-heading\"><strong>(3) \u53d6\u6d88\u4efb\u52a1 <code>\/lipsync\/cancel<\/code><\/strong><\/h5>\n\n\n\n<p>\u5141\u8bb8\u7528\u6237\u53d6\u6d88\u4ecd\u5728\u6392\u961f\u4e2d\u7684\u4efb\u52a1\uff08\u8fdb\u884c\u4e2d\u7684\u4efb\u52a1\u65e0\u6cd5\u53d6\u6d88\uff09\uff1b<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>3. \u540e\u53f0\u4efb\u52a1\u5904\u7406\u903b\u8f91<\/strong><\/h4>\n\n\n\n<p><strong>\u5e76\u53d1\u63a7\u5236 <\/strong>\uff1a\u4f7f\u7528 <code>Semaphore<\/code> \u63a7\u5236\u6700\u591a\u540c\u65f6\u8fd0\u884c\u7684\u4efb\u52a1\u6570\u91cf\uff08\u9ed8\u8ba4 5 \u4e2a\uff09\uff1b<\/p>\n\n\n\n<p><strong>\u4efb\u52a1\u6267\u884c\u6d41\u7a0b <\/strong>\uff1a<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>\u4ece\u961f\u5217\u4e2d\u53d6\u51fa\u4efb\u52a1\uff1b<\/li>\n\n\n\n<li>\u89e3\u7801\u5e76\u4fdd\u5b58 Base64 \u97f3\u89c6\u9891\u4e3a\u4e34\u65f6\u6587\u4ef6\uff1b<\/li>\n\n\n\n<li>\u8c03\u7528 <code>LipsyncPipeline<\/code> \u6267\u884c\u63a8\u7406\uff0c\u751f\u6210\u540c\u6b65\u89c6\u9891\uff1b<\/li>\n\n\n\n<li>\u5c06\u8f93\u51fa\u89c6\u9891\u4fdd\u5b58\u5230\u9759\u6001\u76ee\u5f55\uff0c\u5e76\u66f4\u65b0\u4efb\u52a1\u72b6\u6001\uff1b<\/li>\n\n\n\n<li>\u6e05\u7406\u4e34\u65f6\u6587\u4ef6\uff1b<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>4. \u5b89\u5168\u4e0e\u8ba4\u8bc1<\/strong><\/h4>\n\n\n\n<p>\u4f7f\u7528 <code>HTTPBearer<\/code> \u5b9e\u73b0 Token \u8ba4\u8bc1\uff1b\u652f\u6301\u81ea\u5b9a\u4e49 API Key \u9a8c\u8bc1\uff08\u786c\u7f16\u7801\u5728\u4ee3\u7801\u4e2d\uff0c\u5b9e\u9645\u5e94\u4f7f\u7528\u73af\u5883\u53d8\u91cf\u66f4\u5b89\u5168\uff09\uff1b\u5bf9\u65e0\u6548\u6216\u975e\u6cd5\u8bf7\u6c42\u8fd4\u56de\u7ed3\u6784\u5316 JSON \u9519\u8bef\u4fe1\u606f\uff1b<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>5. \u5f02\u5e38\u5904\u7406<\/strong><\/h4>\n\n\n\n<p>\u81ea\u5b9a\u4e49\u5f02\u5e38\u6355\u83b7\u5668\uff0c\u6355\u83b7\u53c2\u6570\u9a8c\u8bc1\u9519\u8bef\u5e76\u8fd4\u56de\u8be6\u7ec6\u63d0\u793a\uff0c\u6355\u83b7 HTTP \u5f02\u5e38\u5e76\u7edf\u4e00\u683c\u5f0f\u8fd4\u56de\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u670d\u52a1\u5668\u51c6\u5907 \u9996\u5148\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u670d\u52a1\u5668\u7684\u51c6\u5907\uff0c\u8fd9\u91cc\u51c6\u5907\u7684\u662f RTX-4090 \u670d\u52a1\u5668 \u8fde\u63a5\u6211\u4eec\u5df2\u7ecf\u521b\u5efa\u597d\u7684\u670d\u52a1\u5668 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":222,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"class_list":["post-217","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-model-serve"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.8.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b - \u4eca\u5929\u5f00\u59cb\u5b66AI<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/\u4e00\u6587\u5feb\u901f\u5b66\u4f1afastapi\u90e8\u7f72latentsync\u6570\u5b57\u4eba\u6a21\u578b\/\" \/>\n<meta property=\"og:locale\" content=\"zh_CN\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b - \u4eca\u5929\u5f00\u59cb\u5b66AI\" \/>\n<meta property=\"og:description\" content=\"\u670d\u52a1\u5668\u51c6\u5907 \u9996\u5148\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u670d\u52a1\u5668\u7684\u51c6\u5907\uff0c\u8fd9\u91cc\u51c6\u5907\u7684\u662f RTX-4090 \u670d\u52a1\u5668 \u8fde\u63a5\u6211\u4eec\u5df2\u7ecf\u521b\u5efa\u597d\u7684\u670d\u52a1\u5668 [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/\u4e00\u6587\u5feb\u901f\u5b66\u4f1afastapi\u90e8\u7f72latentsync\u6570\u5b57\u4eba\u6a21\u578b\/\" \/>\n<meta property=\"og:site_name\" content=\"\u4eca\u5929\u5f00\u59cb\u5b66AI\" \/>\n<meta property=\"article:published_time\" content=\"2025-05-01T06:40:17+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-05-01T06:48:45+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1344\" \/>\n\t<meta property=\"og:image:height\" content=\"768\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Knoka\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"\u4f5c\u8005\" \/>\n\t<meta name=\"twitter:data1\" content=\"Knoka\" \/>\n\t<meta name=\"twitter:label2\" content=\"\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4\" \/>\n\t<meta name=\"twitter:data2\" content=\"2 \u5206\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#article\",\"isPartOf\":{\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/\"},\"author\":{\"name\":\"Knoka\",\"@id\":\"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84\"},\"headline\":\"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b\",\"datePublished\":\"2025-05-01T06:40:17+00:00\",\"dateModified\":\"2025-05-01T06:48:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/\"},\"wordCount\":41,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84\"},\"image\":{\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg\",\"articleSection\":[\"\u6a21\u578b\u90e8\u7f72\"],\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/\",\"url\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/\",\"name\":\"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b - \u4eca\u5929\u5f00\u59cb\u5b66AI\",\"isPartOf\":{\"@id\":\"https:\/\/knoka.vip\/#website\"},\"primaryImageOfPage\":{\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage\"},\"image\":{\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg\",\"datePublished\":\"2025-05-01T06:40:17+00:00\",\"dateModified\":\"2025-05-01T06:48:45+00:00\",\"breadcrumb\":{\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#breadcrumb\"},\"inLanguage\":\"zh-Hans\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage\",\"url\":\"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg\",\"contentUrl\":\"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg\",\"width\":1344,\"height\":768},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"\u9996\u9875\",\"item\":\"https:\/\/knoka.vip\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/knoka.vip\/#website\",\"url\":\"https:\/\/knoka.vip\/\",\"name\":\"\u4eca\u5929\u5f00\u59cb\u5b66AI\",\"description\":\"\u77e5\u8bc6\u5206\u4eab\",\"publisher\":{\"@id\":\"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/knoka.vip\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"zh-Hans\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84\",\"name\":\"Knoka\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"zh-Hans\",\"@id\":\"https:\/\/knoka.vip\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/gravatar.pho.ink\/avatar\/a81dd1238e551ec6c470995487946913852d59837b557d5166ae60169466da05?s=96&r=g&d=404\",\"contentUrl\":\"https:\/\/gravatar.pho.ink\/avatar\/a81dd1238e551ec6c470995487946913852d59837b557d5166ae60169466da05?s=96&r=g&d=404\",\"caption\":\"Knoka\"},\"logo\":{\"@id\":\"https:\/\/knoka.vip\/#\/schema\/person\/image\/\"},\"sameAs\":[\"http:\/\/118.178.172.39\"],\"url\":\"https:\/\/knoka.vip\/index.php\/author\/admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b - \u4eca\u5929\u5f00\u59cb\u5b66AI","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/\u4e00\u6587\u5feb\u901f\u5b66\u4f1afastapi\u90e8\u7f72latentsync\u6570\u5b57\u4eba\u6a21\u578b\/","og_locale":"zh_CN","og_type":"article","og_title":"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b - \u4eca\u5929\u5f00\u59cb\u5b66AI","og_description":"\u670d\u52a1\u5668\u51c6\u5907 \u9996\u5148\uff0c\u6211\u4eec\u9700\u8981\u8fdb\u884c\u670d\u52a1\u5668\u7684\u51c6\u5907\uff0c\u8fd9\u91cc\u51c6\u5907\u7684\u662f RTX-4090 \u670d\u52a1\u5668 \u8fde\u63a5\u6211\u4eec\u5df2\u7ecf\u521b\u5efa\u597d\u7684\u670d\u52a1\u5668 [&hellip;]","og_url":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/\u4e00\u6587\u5feb\u901f\u5b66\u4f1afastapi\u90e8\u7f72latentsync\u6570\u5b57\u4eba\u6a21\u578b\/","og_site_name":"\u4eca\u5929\u5f00\u59cb\u5b66AI","article_published_time":"2025-05-01T06:40:17+00:00","article_modified_time":"2025-05-01T06:48:45+00:00","og_image":[{"width":1344,"height":768,"url":"http:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg","type":"image\/jpeg"}],"author":"Knoka","twitter_card":"summary_large_image","twitter_misc":{"\u4f5c\u8005":"Knoka","\u9884\u8ba1\u9605\u8bfb\u65f6\u95f4":"2 \u5206"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#article","isPartOf":{"@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/"},"author":{"name":"Knoka","@id":"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84"},"headline":"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b","datePublished":"2025-05-01T06:40:17+00:00","dateModified":"2025-05-01T06:48:45+00:00","mainEntityOfPage":{"@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/"},"wordCount":41,"commentCount":0,"publisher":{"@id":"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84"},"image":{"@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage"},"thumbnailUrl":"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg","articleSection":["\u6a21\u578b\u90e8\u7f72"],"inLanguage":"zh-Hans","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#respond"]}]},{"@type":"WebPage","@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/","url":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/","name":"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b - \u4eca\u5929\u5f00\u59cb\u5b66AI","isPartOf":{"@id":"https:\/\/knoka.vip\/#website"},"primaryImageOfPage":{"@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage"},"image":{"@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage"},"thumbnailUrl":"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg","datePublished":"2025-05-01T06:40:17+00:00","dateModified":"2025-05-01T06:48:45+00:00","breadcrumb":{"@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#breadcrumb"},"inLanguage":"zh-Hans","potentialAction":[{"@type":"ReadAction","target":["http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/"]}]},{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#primaryimage","url":"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg","contentUrl":"https:\/\/knoka.vip\/wp-content\/uploads\/2025\/05\/111.jpg","width":1344,"height":768},{"@type":"BreadcrumbList","@id":"http:\/\/knoka.vip\/index.php\/2025\/05\/01\/%e4%b8%80%e6%96%87%e5%bf%ab%e9%80%9f%e5%ad%a6%e4%bc%9afastapi%e9%83%a8%e7%bd%b2latentsync%e6%95%b0%e5%ad%97%e4%ba%ba%e6%a8%a1%e5%9e%8b\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"\u9996\u9875","item":"https:\/\/knoka.vip\/"},{"@type":"ListItem","position":2,"name":"\u4e00\u6587\u5feb\u901f\u5b66\u4f1aFastAPI\u90e8\u7f72LatentSync\u6570\u5b57\u4eba\u6a21\u578b"}]},{"@type":"WebSite","@id":"https:\/\/knoka.vip\/#website","url":"https:\/\/knoka.vip\/","name":"\u4eca\u5929\u5f00\u59cb\u5b66AI","description":"\u77e5\u8bc6\u5206\u4eab","publisher":{"@id":"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/knoka.vip\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"zh-Hans"},{"@type":["Person","Organization"],"@id":"https:\/\/knoka.vip\/#\/schema\/person\/59bf442e4e4c21f6969b984b8c5bfc84","name":"Knoka","image":{"@type":"ImageObject","inLanguage":"zh-Hans","@id":"https:\/\/knoka.vip\/#\/schema\/person\/image\/","url":"https:\/\/gravatar.pho.ink\/avatar\/a81dd1238e551ec6c470995487946913852d59837b557d5166ae60169466da05?s=96&r=g&d=404","contentUrl":"https:\/\/gravatar.pho.ink\/avatar\/a81dd1238e551ec6c470995487946913852d59837b557d5166ae60169466da05?s=96&r=g&d=404","caption":"Knoka"},"logo":{"@id":"https:\/\/knoka.vip\/#\/schema\/person\/image\/"},"sameAs":["http:\/\/118.178.172.39"],"url":"https:\/\/knoka.vip\/index.php\/author\/admin\/"}]}},"_links":{"self":[{"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/posts\/217","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/comments?post=217"}],"version-history":[{"count":0,"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/posts\/217\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/media\/222"}],"wp:attachment":[{"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/media?parent=217"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/categories?post=217"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/knoka.vip\/index.php\/wp-json\/wp\/v2\/tags?post=217"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}