wan_s2v ¶
Wan2.2-S2V-14B: audio-driven video generation.
Ported from the official native implementation (Wan-Video/Wan2.2, wan/modules/s2v/). There is no diffusers implementation of S2V -- the PR that would have added one (huggingface/diffusers#12258) was closed unmerged -- so the official repo is the only reference, and the checkpoint ships in native Wan naming (blocks.0.self_attn.q) rather than diffusers naming.
Three things make S2V structurally different from every other Wan variant here, and none of them can be expressed by configuring WanTransformer3DModel:
- Heterogeneous sequence. Tokens are
[video | reference image | motion]. Only the leadingvideo_lenvideo tokens are denoised and returned; the rest are conditioning context carried through the tower. - Two-segment modulation. With
zero_timestep, video tokens are modulated by the real timestep while ref/motion tokens are modulated by a fixed zero timestep -- they are already clean, so they must not be treated as noisy. Every modulation site in the block therefore splits atseg_idx. - Precomputed heterogeneous RoPE. Each span gets its own positional range (motion frames sit at negative time offsets); frequencies are built once for the whole sequence rather than derived from a single grid.
Sequence parallelism is not wired up in this first version (upstream shards pre_compute_freqs alongside the hidden states); single-GPU and FSDP-style weight sharding work. See the S2V section of the support matrix.
Classes¶
fastvideo.models.dits.wan_s2v.FramePackMotioner ¶
FramePackMotioner(inner_dim: int = 5120, num_heads: int = 40, zip_frame_buckets: tuple[int, int, int] = (1, 2, 16), drop_mode: str = 'drop')
Bases: Module
Compress past motion frames at three temporal scales.
Recent frames keep full detail (proj), older ones are downsampled 2x (proj_2x) and 4x (proj_4x) -- more history for fewer tokens, the same trade a video codec makes. Buckets are [nearest, mid, farthest].
Source code in fastvideo/models/dits/wan_s2v.py
fastvideo.models.dits.wan_s2v.HeadS2V ¶
Bases: Module
Final norm + projection back to patch space.
Source code in fastvideo/models/dits/wan_s2v.py
fastvideo.models.dits.wan_s2v.WanS2VAttentionBlock ¶
WanS2VAttentionBlock(dim: int, ffn_dim: int, num_heads: int, qk_norm: bool = True, cross_attn_norm: bool = True, eps: float = 1e-06)
Bases: Module
One of the 40 blocks.
Cannot inherit WanTransformerBlock: every modulation site here is segment-aware (see the module docstring).
Source code in fastvideo/models/dits/wan_s2v.py
fastvideo.models.dits.wan_s2v.WanS2VCrossAttention ¶
Bases: WanS2VSelfAttention
Text cross-attention: same projections as self-attention, no RoPE.
Source code in fastvideo/models/dits/wan_s2v.py
fastvideo.models.dits.wan_s2v.WanS2VSelfAttention ¶
Bases: Module
Self-attention over the full heterogeneous sequence, with precomputed RoPE.
Source code in fastvideo/models/dits/wan_s2v.py
fastvideo.models.dits.wan_s2v.WanS2VTransformer3DModel ¶
Bases: BaseDiT
Wan2.2-S2V-14B transformer.
Source code in fastvideo/models/dits/wan_s2v.py
Methods:¶
fastvideo.models.dits.wan_s2v.WanS2VTransformer3DModel.forward ¶
forward(hidden_states: Tensor | list[Tensor], encoder_hidden_states: Tensor | list[Tensor], timestep: Tensor, ref_latents: Tensor | list[Tensor] | None = None, motion_latents: Tensor | list[Tensor] | None = None, cond_states: Tensor | list[Tensor] | None = None, audio_input: Tensor | None = None, motion_frames: tuple[int, int] = (17, 5), add_last_motion: int = 2, drop_motion_frames: bool = False, **kwargs) -> list[Tensor]
Denoise one step of an audio-driven video.
hidden_states [B, C, T, H, W] or list of [C, T, H, W] noisy video latents ref_latents reference-image latents; required, the model is image-conditioned motion_latents previously generated frames; None on the first clip cond_states pose/control latents, or None when unused audio_input [B, 25, C_a, T_a] stacked wav2vec2 hidden states; required
Returns a batched [B, C, T, H, W] fp32 tensor when hidden_states came in batched (the DenoisingStage path -- CFG arithmetic and scheduler.step need a tensor), or a list of per-sample tensors when it came in as a list (the reference-implementation path).
Source code in fastvideo/models/dits/wan_s2v.py
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | |
fastvideo.models.dits.wan_s2v.WanS2VTransformer3DModel.materialize_non_persistent_buffers ¶
Rebuild the RoPE tables after meta-device construction.
TransformerLoader builds the DiT under torch.device("meta") and then streams checkpoint weights in. Non-persistent buffers are not in the checkpoint, so they stay on meta until this hook (called by fsdp_load) recreates them with real storage. Complex dtype is deliberate -- these are rotation factors, not activations, and must not follow the model dtype.
Source code in fastvideo/models/dits/wan_s2v.py
Functions:¶
fastvideo.models.dits.wan_s2v.rope_apply ¶
Rotate q/k of shape [B, L, N, D] by precomputed complex frequencies.
Source code in fastvideo/models/dits/wan_s2v.py
fastvideo.models.dits.wan_s2v.rope_freqs ¶
(time, height, width) RoPE tables for one head; time absorbs the remainder of head_dim.
Source code in fastvideo/models/dits/wan_s2v.py
fastvideo.models.dits.wan_s2v.rope_precompute ¶
rope_precompute(x: Tensor, grid_sizes: list, freqs: Tensor) -> Tensor
Build per-token complex RoPE frequencies for a heterogeneous sequence.
grid_sizes is a list of spans laid out back-to-back in sequence order; each span is [start, end, extent] where every element is a [B, 3] tensor of (frame, height, width). A negative start frame means the span sits in the past (motion frames), which is encoded by walking the time axis backwards and conjugating the temporal band rather than by indexing negatively.