diff --git a/tests/multimodal/test_parse.py b/tests/multimodal/test_parse.py index c34c8eec8b..b4d37b0fe8 100644 --- a/tests/multimodal/test_parse.py +++ b/tests/multimodal/test_parse.py @@ -56,6 +56,23 @@ def test_frame_size_hwc_chw(frame): assert items.get_frame_size(0) == (W, H) +@pytest.mark.parametrize( + "frames", + [ + [np.zeros((H, W, 3), dtype=np.uint8) for _ in range(2)], + [torch.zeros((H, W, 3), dtype=torch.uint8) for _ in range(2)], + ], +) +def test_parse_video_frame_list_as_single_video(frames): + """A list of decoded frames must represent one video item.""" + items = MultiModalDataParser().parse_mm_data({"video": frames})["video"] + + assert items.get_count() == 1 + video = items.get(0) + assert isinstance(video, np.ndarray) + np.testing.assert_array_equal(video, np.stack([np.asarray(f) for f in frames])) + + @pytest.mark.parametrize( "modality,processor_cls", [ diff --git a/vllm/multimodal/parse.py b/vllm/multimodal/parse.py index fefaeef2b0..124a8845cc 100644 --- a/vllm/multimodal/parse.py +++ b/vllm/multimodal/parse.py @@ -751,8 +751,14 @@ class MultiModalDataParser: return VideoEmbeddingItems(data, self.expected_hidden_size) data_items: list[VideoItem] - if (is_list_of(data, PILImage.Image) and len(data) > 0) or ( - isinstance(data, (np.ndarray, torch.Tensor)) and data.ndim == 4 + if ( + (is_list_of(data, PILImage.Image) and len(data) > 0) + or ( + is_list_of(data, (np.ndarray, torch.Tensor), check="all") + and len(data) > 0 + and all(item.ndim == 3 for item in data) + ) + or (isinstance(data, (np.ndarray, torch.Tensor)) and data.ndim == 4) ): data_items = [data] elif isinstance(data, (np.ndarray, torch.Tensor)):