[Bugfix][Multimodal] Parse decoded video frame lists as a single video (#55326)

Signed-off-by: waizuichougou <2082431897@qq.com> Signed-off-by: waizuichougou <105572299+waizuichougou@users.noreply.github.com> Co-authored-by: Isotr0py <2037008807@qq.com> Co-authored-by: Isotr0py <Isotr0py@outlook.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>

waizuichougou committed Sep 11, 2026 at 20:16 UTC 295ac4e52e8a35772b2a63c028f6510e221a65cf
2 files changed +25 -2
tests/multimodal/test_parse.py
+17
@@ -56,6 +56,23 @@ def test_frame_size_hwc_chw(frame):
56 assert items.get_frame_size(0) == (W, H)
57
58
59 +@pytest.mark.parametrize(
60 + "frames",
61 + [
62 + [np.zeros((H, W, 3), dtype=np.uint8) for _ in range(2)],
63 + [torch.zeros((H, W, 3), dtype=torch.uint8) for _ in range(2)],
64 + ],
65 +)
66 +def test_parse_video_frame_list_as_single_video(frames):
67 + """A list of decoded frames must represent one video item."""
68 + items = MultiModalDataParser().parse_mm_data({"video": frames})["video"]
69 +
70 + assert items.get_count() == 1
71 + video = items.get(0)
72 + assert isinstance(video, np.ndarray)
73 + np.testing.assert_array_equal(video, np.stack([np.asarray(f) for f in frames]))
74 +
75 +
76 @pytest.mark.parametrize(
77 "modality,processor_cls",
78 [
vllm/multimodal/parse.py
+8 -2
@@ -751,8 +751,14 @@ class MultiModalDataParser:
751 return VideoEmbeddingItems(data, self.expected_hidden_size)
752
753 data_items: list[VideoItem]
754 - if (is_list_of(data, PILImage.Image) and len(data) > 0) or (
755 - isinstance(data, (np.ndarray, torch.Tensor)) and data.ndim == 4
754 + if (
755 + (is_list_of(data, PILImage.Image) and len(data) > 0)
756 + or (
757 + is_list_of(data, (np.ndarray, torch.Tensor), check="all")
758 + and len(data) > 0
759 + and all(item.ndim == 3 for item in data)
760 + )
761 + or (isinstance(data, (np.ndarray, torch.Tensor)) and data.ndim == 4)
762 ):
763 data_items = [data]
764 elif isinstance(data, (np.ndarray, torch.Tensor)):