Skip to content

[Bugfix][Frontend] Preserve generated audio in ComfyUI video output - #5905

Closed
Devindan wants to merge 1 commit into
vllm-project:mainfrom
Devindan:fix/comfyui-video-audio
Closed

Devindan wants to merge 1 commit into
vllm-project:mainfrom
Devindan:fix/comfyui-video-audio

Conversation

@Devindan

@Devindan Devindan commented Aug 7, 2026

Copy link
Copy Markdown

Summary

Fix audio handling in the ComfyUI-vLLM-Omni video generation workflow.

This PR addresses two related issues:

  1. Exposes the /v1/videos generate_sound option in the ComfyUI Generate Video node and forwards it to the vLLM-Omni API.
  2. Fixes audio tracks being dropped when converting generated MP4 responses into ComfyUI VIDEO objects.

Problem

Video models such as MiniMax-H3 can generate native audio together with video when generate_sound=true.

However, the current ComfyUI integration has two problems:

  • Generate Video does not expose or forward the generate_sound request parameter.
  • Even when the vLLM-Omni server returns an MP4 containing a valid audio stream, bytes_to_video() may drop the audio during decoding.

The second issue is caused by decoding the streams sequentially from the same PyAV container:

container.decode(video_stream)

followed by:

container.decode(audio_stream)

After the video stream has been fully decoded, the container has already been consumed to EOF, so the subsequent audio decode may return no frames.

As a result, the generated ComfyUI VIDEO object contains the video frames but has no audio.

Changes

Add generate_sound support

Add a generate_sound boolean option to the Generate Video node and forward it through VLLMOmniClient.generate_video() as a top-level multipart field for /v1/videos.

The default remains false, matching the API's existing behavior.

Preserve audio during video decoding

Update bytes_to_video() to decode the selected video and audio streams together in a single pass:

for frame in container.decode(*streams):
    ...

Video and audio frames are then handled according to their frame type.

This avoids consuming the container before the audio stream is decoded.

Testing

Tested end-to-end with:

  • MiniMax-H3 FL2VA
  • vLLM-Omni video serving API
  • ComfyUI-vLLM-Omni
  • Python 3.12
  • 2x NVIDIA H800 80GB

Verified the following cases:

  • generate_sound=true produces a ComfyUI VIDEO object with working generated audio.
  • The MP4 returned directly by the vLLM-Omni server contains both video and audio streams.
  • Audio is preserved after conversion through bytes_to_video().
  • generate_sound=false continues to generate video without audio normally.
  • Video-only responses continue to decode correctly.
  • Ruff and the relevant pre-commit checks pass for the modified Python files.

Scope

The decoding fix is not specific to MiniMax-H3. It should apply to any video generation backend that returns an MP4 containing both video and audio streams through the vLLM-Omni video API.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@hsliuustc0106 hsliuustc0106 added bug Something isn't working frontend code related to entrypoint labels Aug 8, 2026
@hsliuustc0106

Copy link
Copy Markdown
Collaborator

@fhfuih PTAL

@fhfuih fhfuih left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation looks good to me. But I want to double check with your claim

"Video models such as MiniMax-H3 can generate native audio together with video when generate_sound=true."

I cannot find where minimax h3 pipeline recognize this param (that's probably why I didn't pay attention to this param. ) When I tried H3 with ConfyUI, it always generates with sound even without this param.

Now that seeing this PR, I do notice Cosmos recognize this param though

And as that being said, I didn't yet encounter the 2nd problem you mentioned: "bytes_to_video() may drop the audio during decoding.".

So could you help test 2x2=4 cases of generation using (H3, Cosmos) model and with this toggle (on or off), and attach screen recordings---or other test cases that demonstrate both the generate_sound problem and the audio-dropping problem before and after the fix

@Devindan

Copy link
Copy Markdown
Author

@fhfuih

Thanks for the careful review. You're right about MiniMax-H3 — I re-checked the pipeline and my original wording was inaccurate.

MiniMax-H3 currently generates video and audio jointly and does not appear to use generate_sound as a switch. So for H3, I would expect audio to be generated regardless of whether the toggle is on or off.

Cosmos is different: its pipeline does recognize the sound-generation flag, so the expected behavior should be:

Model generate_sound=false generate_sound=true
MiniMax-H3 audio audio
Cosmos no audio audio

I'll update the PR description to avoid implying that generate_sound controls audio generation for H3 specifically. The reason for exposing this option in the ComfyUI node is that it is part of the generic /v1/videos API and is meaningful for models such as Cosmos.

For the second issue, the behavior I observed was reproducible locally with H3: the MP4 returned by the vLLM-Omni server contained a valid audio stream, but the ComfyUI VIDEO object produced by the original bytes_to_video() implementation had no audio. After changing the decoder to consume the video and audio streams together in a single pass, the audio was preserved correctly.

The key difference is:

# before
container.decode(video_stream)
container.decode(audio_stream)

# after
container.decode(video_stream, audio_stream)

My understanding is that after the first decode loop consumes the container to EOF, the second decode may not yield the interleaved audio packets anymore. This explains why the problem may not reproduce for every media layout, but it did reproduce consistently with the H3-generated MP4s in my environment.

I won't be able to provide screen recordings, but I can include the exact configurations and whether an audio stream was present in each generated result.

@fhfuih fhfuih left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the double check. I think it now it makes sense. One last thing: AFAIK if you change a node implementation in ComfyUI, when you import a workflow that references to that node, it still shows the old set of toggles. So could you also

  • copy/rsync your updated integration source code to your local comfyui installation
  • import the example custom workflows that uses video generation
  • delete the main video generation node, re-grag it from the node menu, and set all io connections & fields again like before
  • same the current state of the workflow
  • find the saved workflow JSON in comfyui directory and copy (overwrite) it here, and commit that to this PR

And if you cannot offer screen recording, but still at least have the final video from the comfyui output node, could you upload them here just for everyone's record? (the no sound old video and the new video). I think you can find a download button from the output video preview control, which should be what's outputted by the decoder

@vllm-omni-review-bot

Copy link
Copy Markdown

Omni ReviewBot: no human activity for 20 days

@Devindan this pull request has had no human commit, comment or review since 2026-08-19. Per repository policy it may be closed if it stays inactive.

To keep it moving, any one of these is enough: push an update, reply to the open blocker, or post the current plan and timeline.

height: int,
fps: int,
num_frames: int,
generate_sound: bool,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update the video tests to pass generate_sound; all six existing cases now raise TypeError.

@vllm-omni-review-bot

Copy link
Copy Markdown

Omni ReviewBot: no human activity for 7 days

@Devindan this pull request has had no human commit, comment or review since 2026-09-15. Please confirm the current plan and next step. The author or a maintainer decides whether to change the PR state.

To keep it moving, any one of these is enough: push an update, reply to the open blocker, or post the current plan and timeline.

@vllm-omni-review-bot

Copy link
Copy Markdown

Omni ReviewBot: no human activity for 14 days

@Devindan this pull request has had no human commit, comment or review since 2026-09-15. Please consider marking this PR as draft until work can resume. The author or a maintainer decides whether to change the PR state.

To keep it moving, any one of these is enough: push an update, reply to the open blocker, or post the current plan and timeline.

@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot routing record

Assigned Strict on zcode (GLM-5.3-Flash) under experiment fleet-strict-cursor-grok46-zcode-glm53flash-5050-c5-z10-20261002.

@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (step 'review' (agent.review_diff): unhandled error: RuntimeError: zcode exited 1 without a result event: statusCode: undefined } Error: Turn execution failed (traceId: e71390ce-85f1-47b6-a575-40eec00ca07e) — check zcode login and the CLI version; retrying strict/zcode/GLM-5.3-Flash in 120s (try ).

2 similar comments
@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (step 'review' (agent.review_diff): unhandled error: RuntimeError: zcode exited 1 without a result event: statusCode: undefined } Error: Turn execution failed (traceId: e71390ce-85f1-47b6-a575-40eec00ca07e) — check zcode login and the CLI version; retrying strict/zcode/GLM-5.3-Flash in 120s (try ).

@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (step 'review' (agent.review_diff): unhandled error: RuntimeError: zcode exited 1 without a result event: statusCode: undefined } Error: Turn execution failed (traceId: e71390ce-85f1-47b6-a575-40eec00ca07e) — check zcode login and the CLI version; retrying strict/zcode/GLM-5.3-Flash in 120s (try ).

@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (step 'review' (agent.review_diff): unhandled error: RuntimeError: zcode exited 1 without a result event: statusCode: undefined } Error: Turn execution failed (traceId: 4fbd34e2-eb67-4d68-b02b-39038e167c02) — check zcode login and the CLI version; retrying strict/zcode/GLM-5.3-Flash in 600s (try ).

@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (step 'review' (agent.review_diff): unhandled error: RuntimeError: zcode exited 1 without a result event: statusCode: undefined } Error: Turn execution failed (traceId: 9c005501-f887-4af4-84c8-f5fdeea007e3) — check zcode login and the CLI version; falling back to direct/cursor/auto).

@vllm-omni-review-bot vllm-omni-review-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Omni ReviewBot review

Changes since the previous review

  • 1 new inline finding(s); 1 finding(s) below.

CI at 66f18cbe8cfd (2026-10-09T13:41:20.658594+00:00): required check(s) blocking: buildkite/vllm-omni (missing).

Note: The assigned review arm strict/zcode/GLM-5.3-Flash could not complete this review, so it was produced by the fallback arm direct/cursor/auto. It is excluded from the routing experiment.

Full review analysis

PR description

The ComfyUI Generate Video node gains a generate_sound toggle (default off) and VLLMOmniClient.generate_video() always posts it as a top-level /v1/videos multipart field ("true" or "false"). bytes_to_video() now demuxes the chosen video stream and the last audio stream in one container.decode(...) pass, then builds the ComfyUI VIDEO from those frames, so an MP4 that already contains audio is no longer decoded to EOF on video before audio is read. The flag only changes the request; this diff does not teach MiniMax-H3 to honor it, and audio already present in the response is kept either way.

Change flow

flowchart LR
  Widget["[NEW] generate_sound widget"]:::new
  Client["[CHANGED] generate_video multipart field"]:::changed
  API["[EXISTING] POST /v1/videos MP4"]:::existing
  Decode["[CHANGED] bytes_to_video joint decode"]:::changed
  Out["[EXISTING] ComfyUI VIDEO"]:::existing
  Widget --> Client --> API --> Decode --> Out
  classDef existing fill:#e5e7eb,stroke:#6b7280,color:#111827
  classDef changed fill:#fef3c7,stroke:#d97706,color:#451a03,stroke-width:2px
  classDef new fill:#dcfce7,stroke:#16a34a,color:#052e16,stroke-width:2px
  classDef removed fill:#fee2e2,stroke:#dc2626,color:#450a0a,stroke-width:2px
Loading

Findings

  • [P1] Required generate_sound breaks existing Generate Video callers — apps/ComfyUI-vLLM-Omni/comfyui_vllm_omni/nodes.py:199
    Existing thread: #5905 (comment)
Evidence for Required generate_sound breaks existing Generate Video callers

VLLMOmniGenerateVideo.generate inserts generate_sound: bool with no default ahead of the previous optional arguments. VLLMOmniClient.generate_video already defaults the same flag to False, but the node method does not. Any existing call that still passes negative_prompt, frame, or later arguments by keyword and omits generate_sound now raises TypeError before the request is built. This three-file diff does not update callers or tests. Give the node argument generate_sound: bool = False so it matches the widget default and the client, and update the video-node tests to pass the flag explicitly.


🤖 This review was generated by InferMatrix Copilot, an open-source repo-maintenance agent for PR review, CI debugging and issue triage. Try it on your own repo, and ⭐ star it if it helped!


images = torch.stack(frames, dim=0)
frame_rate = Fraction(video_stream.average_rate) if video_stream.average_rate else Fraction(1)
for frame in container.decode(*streams):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Audio-preserving decode has no regression pin

Evidence and suggested fix

The bugfix replaces sequential container.decode(video_stream) then container.decode(audio_stream) with one container.decode(*streams) loop at this line. The PR testing section only reports a manual MiniMax-H3 / Cosmos run on 2x H800. The changed-file set adds no test that builds an MP4 with both streams, runs bytes_to_video, and asserts a non-empty waveform, nor a video-only control. Without that pin, a later return to two decode passes can drop audio again while ComfyUI unit tests still pass. Add a local PyAV fixture for both layouts on bytes_to_video.

@vllm-omni-review-bot

Copy link
Copy Markdown

Omni ReviewBot: finding feedback

[p1] Required generate_sound breaks existing Generate Video callers — apps/ComfyUI-vLLM-Omni/comfyui_vllm_omni/nodes.py:199

See the review for details. If you are the PR author and disagree, react 👎 here; the maintainer will see your disagreement.

Copy link
Copy Markdown
Collaborator

@Devindan Are you planning to continue work on this PR? Please reply by 2026-10-19 10:42:13 UTC.

A yes or a brief plan is enough; code changes do not need to be finished within these three days. If you are waiting on a review or other maintainer action, please let us know.

If there is no reply, new commit, or maintainer takeover by then, we will recheck and close this PR as inactive. It can be reopened when work resumes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working frontend code related to entrypoint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants