[Misc][Minimax-M3]add default video_processor (#50092)
Signed-off-by: rongfu.leng <lenronfu@gmail.com> Co-authored-by: Cyrus Leung <tlleungac@connect.ust.hk>
rongfu.leng committed
Jul 29, 2026 at 16:24 UTC
df2735ea2e14b377ce71571ed27a63b9ceae2ba3
2 files changed
+44
-7
tests/multimodal/test_video.py
+7
@@ -14,6 +14,7 @@ from transformers import AutoVideoProcessor
14
from transformers.video_utils import VideoMetadata
15
16
from vllm.assets.base import get_vllm_public_assets
17
+from vllm.models.minimax_m3.common.mm_preprocess import MiniMaxM3VideoBackend
18
from vllm.multimodal.video import (
19
PYNVVIDEOCODEC_DECODER_CACHE_SIZE,
20
PYNVVIDEOCODEC_VIDEO_BACKEND,
@@ -388,6 +389,12 @@ def test_cosmos3_edge_uses_qwen3_vl_video_backend():
389
{"fps": 2},
390
id="qwen2_5_vl",
391
),
392
+ pytest.param(
393
+ "MiniMaxAI/MiniMax-M3",
394
+ MiniMaxM3VideoBackend,
395
+ None,
396
+ id="minimax_m3_vl",
397
+ ),
398
],
399
)
400
def test_video_processor_from_model_repo(
vllm/models/minimax_m3/common/mm_preprocess.py
+37
-7
@@ -3,8 +3,9 @@
3
4
import math
5
from collections.abc import Mapping, Sequence
6
-from typing import cast
6
+from typing import Any, Literal, cast
7
8
+import numpy.typing as npt
9
import torch
10
from transformers import BatchFeature
11
from transformers.video_utils import VideoMetadata
@@ -469,10 +470,39 @@ class MiniMaxM3VLMultiModalProcessor(
470
]
471
472
472
-# TODO(Isotr0py): Tie with MinimaxVideoProcessor
473
-# after https://github.com/vllm-project/vllm/pull/44126
474
-@VIDEO_LOADER_REGISTRY.register("minimax_m3_vl")
473
+@VIDEO_LOADER_REGISTRY.register(
474
+ name="minimax_m3_vl",
475
+ video_processor="MiniMaxM3VLVideoProcessor",
476
+)
477
class MiniMaxM3VideoBackend(VideoBackend):
478
+ @classmethod
479
+ def load_bytes(
480
+ cls,
481
+ data: bytes,
482
+ num_frames: int = -1,
483
+ fps: int = 1,
484
+ max_duration: int = 300,
485
+ frame_recovery: bool = False,
486
+ *,
487
+ backend: Literal[
488
+ "opencv",
489
+ "pyav",
490
+ "torchcodec",
491
+ "pynvvideocodec",
492
+ "deepstream",
493
+ ] = "opencv",
494
+ **kwargs,
495
+ ) -> tuple[npt.NDArray, dict[str, Any]]:
496
+ return super().load_bytes(
497
+ data,
498
+ num_frames=num_frames,
499
+ fps=fps,
500
+ max_duration=max_duration,
501
+ frame_recovery=frame_recovery,
502
+ backend=backend,
503
+ **kwargs,
504
+ )
505
+
506
@classmethod
507
def compute_frames_index_to_sample(
508
cls,
@@ -483,7 +513,6 @@ class MiniMaxM3VideoBackend(VideoBackend):
513
total_frames = source.total_frames_num
514
video_fps = source.original_fps
515
fps = target.fps
486
-
516
if total_frames <= 0 or video_fps <= 0 or fps <= 0:
517
return [0] if total_frames > 0 else []
518
@@ -503,8 +532,9 @@ class MiniMaxM3VideoBackend(VideoBackend):
532
break
533
indices.append(target_frame)
534
prev_kept_ts = target_frame / video_fps
506
-
507
- last_frame_idx = total_frames - 1
535
+ # Because HF sample_frames includes the last frame,
536
+ # we will use HF as the standard.
537
+ last_frame_idx = total_frames
538
last_ts = last_frame_idx / video_fps
539
if indices and indices[-1] != last_frame_idx and last_ts - prev_kept_ts > eps:
540
indices.append(last_frame_idx)