CCCCyx commited on
Commit
da8796b
·
verified ·
1 Parent(s): 1f4da11

Update video_processing_moss_vl.py

Browse files
Files changed (1) hide show
  1. video_processing_moss_vl.py +50 -22
video_processing_moss_vl.py CHANGED
@@ -39,6 +39,39 @@ from transformers.video_utils import VideoMetadata, group_videos_by_shape, reord
39
  logger = logging.get_logger(__name__)
40
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  # -----------------------------------------------------------------------------
43
  # Torchcodec video frame extraction utilities
44
  # -----------------------------------------------------------------------------
@@ -198,7 +231,17 @@ def split_indices(indices: List[Union[int, float]], num_chunks: int) -> List[Lis
198
 
199
  Returns:
200
  List of index chunks.
 
 
 
201
  """
 
 
 
 
 
 
 
202
  chunk_size = len(indices) // num_chunks
203
  chunks = []
204
  for i in range(num_chunks - 1):
@@ -239,7 +282,7 @@ def decode_with_multithreading(indices: List[int], num_threads: int, video_path:
239
  dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
240
  """
241
  chunks = split_indices(indices, num_chunks=num_threads)
242
- results = Parallel(n_jobs=num_threads, prefer="threads", verbose=0)(
243
  delayed(decode_sequentially)(chunk, video_path) for chunk in chunks
244
  )
245
 
@@ -266,22 +309,7 @@ def decode_sequentially_timestamp(timestamp_list: List[float], video_path: str,
266
  try:
267
  metadata = decoder.metadata
268
 
269
- min_pts = metadata.begin_stream_seconds_from_content
270
- if min_pts is None:
271
- min_pts = 0.0
272
-
273
- max_pts = None
274
- if metadata.num_frames_from_content and metadata.average_fps:
275
- max_pts = (metadata.num_frames_from_content - 1) / metadata.average_fps + min_pts
276
- elif metadata.end_stream_seconds_from_content is not None:
277
- max_pts = metadata.end_stream_seconds_from_content
278
- else:
279
- max_pts = metadata.duration_seconds
280
-
281
- if max_pts is not None and max_pts > 0:
282
- timestamp_list = [max(min_pts, min(t, max_pts)) for t in timestamp_list]
283
- elif min_pts > 0:
284
- timestamp_list = [max(min_pts, t) for t in timestamp_list]
285
 
286
  return decoder.get_frames_played_at(timestamp_list)
287
  finally:
@@ -301,7 +329,7 @@ def timestamp_decode_with_multithreading(timestamp_list: List[float], num_thread
301
  dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
302
  """
303
  chunks = split_indices(timestamp_list, num_chunks=num_threads)
304
- results = Parallel(n_jobs=num_threads, prefer="threads", verbose=0)(
305
  delayed(decode_sequentially_timestamp)(chunk, video_path) for chunk in chunks
306
  )
307
 
@@ -702,10 +730,9 @@ class MossVLVideoProcessor(BaseVideoProcessor):
702
 
703
  if len(segment) == 1:
704
  # Single frame at specified time
705
- timestamp = segment[0]
706
- frame_batch = decoder.get_frames_played_at([timestamp])
707
  video_tensor = frame_batch.data
708
- actual_timestamps = [timestamp]
709
  sample_count = 1
710
  else:
711
  # Segment [start, end) - left-closed, right-open interval
@@ -724,6 +751,8 @@ class MossVLVideoProcessor(BaseVideoProcessor):
724
  # Sample uniformly within [start, end), endpoint=False for left-closed right-open
725
  actual_timestamps = np.linspace(start_time, end_time, target_frames, endpoint=False).tolist()
726
 
 
 
727
  # Use multithreading for extraction
728
  result = timestamp_decode_with_multithreading(actual_timestamps, self.num_extract_threads, video_path)
729
  video_tensor = result["data"]
@@ -1129,4 +1158,3 @@ class MossVLVideoProcessor(BaseVideoProcessor):
1129
 
1130
 
1131
  __all__ = ["MossVLVideoProcessor"]
1132
-
 
39
  logger = logging.get_logger(__name__)
40
 
41
 
42
+ TORCHCODEC_TIMESTAMP_EPSILON = 1e-6
43
+
44
+
45
+ def clamp_timestamps_for_torchcodec(timestamps: List[float], torchcodec_metadata) -> List[float]:
46
+ if not timestamps:
47
+ return timestamps
48
+
49
+ min_pts = torchcodec_metadata.begin_stream_seconds_from_content
50
+ if min_pts is None:
51
+ min_pts = 0.0
52
+ # TorchCodec can reject timestamps exactly equal to the reported stream
53
+ # begin due to tiny metadata/decoder precision differences.
54
+ safe_min_pts = min_pts + TORCHCODEC_TIMESTAMP_EPSILON
55
+
56
+ max_pts_candidates = []
57
+ if torchcodec_metadata.num_frames_from_content and torchcodec_metadata.average_fps:
58
+ max_pts_candidates.append(
59
+ (torchcodec_metadata.num_frames_from_content - 1) / torchcodec_metadata.average_fps + min_pts
60
+ )
61
+ if torchcodec_metadata.end_stream_seconds_from_content is not None:
62
+ # TorchCodec requires requested PTS to be strictly smaller than the content end.
63
+ max_pts_candidates.append(torchcodec_metadata.end_stream_seconds_from_content - TORCHCODEC_TIMESTAMP_EPSILON)
64
+ if not max_pts_candidates and torchcodec_metadata.duration_seconds is not None:
65
+ max_pts_candidates.append(torchcodec_metadata.duration_seconds - TORCHCODEC_TIMESTAMP_EPSILON)
66
+
67
+ if max_pts_candidates:
68
+ max_pts = max(safe_min_pts, min(max_pts_candidates))
69
+ return [max(safe_min_pts, min(float(t), max_pts)) for t in timestamps]
70
+ if safe_min_pts > 0:
71
+ return [max(safe_min_pts, float(t)) for t in timestamps]
72
+ return [float(t) for t in timestamps]
73
+
74
+
75
  # -----------------------------------------------------------------------------
76
  # Torchcodec video frame extraction utilities
77
  # -----------------------------------------------------------------------------
 
231
 
232
  Returns:
233
  List of index chunks.
234
+
235
+ Raises:
236
+ ValueError: If indices is empty or num_chunks is not positive.
237
  """
238
+ if len(indices) == 0:
239
+ raise ValueError("indices must not be empty")
240
+ if num_chunks <= 0:
241
+ raise ValueError("num_chunks must be positive")
242
+
243
+ # Never create empty decode jobs when there are fewer frames than workers.
244
+ num_chunks = min(num_chunks, len(indices))
245
  chunk_size = len(indices) // num_chunks
246
  chunks = []
247
  for i in range(num_chunks - 1):
 
282
  dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
283
  """
284
  chunks = split_indices(indices, num_chunks=num_threads)
285
+ results = Parallel(n_jobs=len(chunks), prefer="threads", verbose=0)(
286
  delayed(decode_sequentially)(chunk, video_path) for chunk in chunks
287
  )
288
 
 
309
  try:
310
  metadata = decoder.metadata
311
 
312
+ timestamp_list = clamp_timestamps_for_torchcodec(timestamp_list, metadata)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
 
314
  return decoder.get_frames_played_at(timestamp_list)
315
  finally:
 
329
  dict: Contains 'data', 'duration_seconds', 'pts_seconds' tensors.
330
  """
331
  chunks = split_indices(timestamp_list, num_chunks=num_threads)
332
+ results = Parallel(n_jobs=len(chunks), prefer="threads", verbose=0)(
333
  delayed(decode_sequentially_timestamp)(chunk, video_path) for chunk in chunks
334
  )
335
 
 
730
 
731
  if len(segment) == 1:
732
  # Single frame at specified time
733
+ actual_timestamps = clamp_timestamps_for_torchcodec([segment[0]], torchcodec_metadata)
734
+ frame_batch = decoder.get_frames_played_at(actual_timestamps)
735
  video_tensor = frame_batch.data
 
736
  sample_count = 1
737
  else:
738
  # Segment [start, end) - left-closed, right-open interval
 
751
  # Sample uniformly within [start, end), endpoint=False for left-closed right-open
752
  actual_timestamps = np.linspace(start_time, end_time, target_frames, endpoint=False).tolist()
753
 
754
+ actual_timestamps = clamp_timestamps_for_torchcodec(actual_timestamps, torchcodec_metadata)
755
+
756
  # Use multithreading for extraction
757
  result = timestamp_decode_with_multithreading(actual_timestamps, self.num_extract_threads, video_path)
758
  video_tensor = result["data"]
 
1158
 
1159
 
1160
  __all__ = ["MossVLVideoProcessor"]