[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)