@@ -141,13 +141,11 @@ def capture(self) -> cv2.VideoCapture:
141141
142142 @property
143143 def frame_rate (self ) -> float :
144- """Framerate in frames/sec."""
145144 assert self ._frame_rate
146145 return self ._frame_rate
147146
148147 @property
149148 def path (self ) -> ty .Union [bytes , str ]:
150- """Video or device path."""
151149 if self ._is_device :
152150 assert isinstance (self ._path_or_device , (int ))
153151 return "Device %d" % self ._path_or_device
@@ -156,7 +154,6 @@ def path(self) -> ty.Union[bytes, str]:
156154
157155 @property
158156 def name (self ) -> str :
159- """Name of the video, without extension, or device."""
160157 if self ._is_device :
161158 return self .path
162159 file_name : str = get_file_name (self .path , include_extension = False )
@@ -205,13 +202,6 @@ def timecode(self) -> Timecode:
205202
206203 @property
207204 def position (self ) -> FrameTimecode :
208- """Current position within stream as FrameTimecode.
209-
210- This can be interpreted as presentation time stamp of the last frame which was
211- decoded by calling `read` with advance=True.
212-
213- This method will always return 0 (e.g. be equal to `base_timecode`) if no frames
214- have been `read`."""
215205 if _USE_PTS_IN_DEVELOPMENT :
216206 return FrameTimecode (timecode = self .timecode , fps = self .frame_rate )
217207 if self .frame_number < 1 :
@@ -220,41 +210,13 @@ def position(self) -> FrameTimecode:
220210
221211 @property
222212 def position_ms (self ) -> float :
223- """Current position within stream as a float of the presentation time in milliseconds.
224- The first frame has a time of 0.0 ms.
225-
226- This method will always return 0.0 if no frames have been `read`."""
227213 return self ._cap .get (cv2 .CAP_PROP_POS_MSEC )
228214
229215 @property
230216 def frame_number (self ) -> int :
231- """Current position within stream in frames as an int.
232-
233- 1 indicates the first frame was just decoded by the last call to `read` with advance=True,
234- whereas 0 indicates that no frames have been `read`.
235-
236- This method will always return 0 if no frames have been `read`."""
237217 return math .trunc (self ._cap .get (cv2 .CAP_PROP_POS_FRAMES ))
238218
239219 def seek (self , target : ty .Union [FrameTimecode , float , int ]):
240- """Seek to the given timecode. If given as a frame number, represents the current seek
241- pointer (e.g. if seeking to 0, the next frame decoded will be the first frame of the video).
242-
243- For 1-based indices (first frame is frame #1), the target frame number needs to be converted
244- to 0-based by subtracting one. For example, if we want to seek to the first frame, we call
245- seek(0) followed by read(). If we want to seek to the 5th frame, we call seek(4) followed
246- by read(), at which point frame_number will be 5.
247-
248- Not supported if the VideoStream is a device/camera. Untested with web streams.
249-
250- Arguments:
251- target: Target position in video stream to seek to.
252- If float, interpreted as time in seconds.
253- If int, interpreted as frame number.
254- Raises:
255- SeekError: An error occurs while seeking, or seeking is not supported.
256- ValueError: `target` is not a valid value (i.e. it is negative).
257- """
258220 if self ._is_device :
259221 raise SeekError ("Cannot seek if input is a device!" )
260222 if target < 0 :
@@ -282,40 +244,27 @@ def reset(self):
282244 self ._cap .release ()
283245 self ._open_capture (self ._frame_rate )
284246
285- def read (self , decode : bool = True , advance : bool = True ) -> ty .Union [np .ndarray , bool ]:
286- """Read and decode the next frame as a np.ndarray. Returns False when video ends,
287- or the maximum number of decode attempts has passed.
288-
289- Arguments:
290- decode: Decode and return the frame.
291- advance: Seek to the next frame. If False, will return the current (last) frame.
292-
293- Returns:
294- If decode = True, the decoded frame (np.ndarray), or False (bool) if end of video.
295- If decode = False, a bool indicating if advancing to the the next frame succeeded.
296- """
247+ def read (self , decode : bool = True ) -> ty .Union [np .ndarray , bool ]:
297248 if not self ._cap .isOpened ():
298249 return False
299- # Grab the next frame if possible.
300- if advance :
301- has_grabbed = self ._cap .grab ()
302- # If we failed to grab the frame, retry a few times if required.
303- if not has_grabbed :
304- if self .duration > 0 and self .position < (self .duration - 1 ):
305- for _ in range (self ._max_decode_attempts ):
306- has_grabbed = self ._cap .grab ()
307- if has_grabbed :
308- break
309- # Report previous failure in debug mode.
310- if has_grabbed :
311- self ._decode_failures += 1
312- logger .debug ("Frame failed to decode." )
313- if not self ._warning_displayed and self ._decode_failures > 1 :
314- logger .warning ("Failed to decode some frames, results may be inaccurate." )
315- # We didn't manage to grab a frame even after retrying, so just return.
316- if not has_grabbed :
317- return False
318- self ._has_grabbed = True
250+ has_grabbed = self ._cap .grab ()
251+ # If we failed to grab the frame, retry a few times if required.
252+ if not has_grabbed :
253+ if self .duration > 0 and self .position < (self .duration - 1 ):
254+ for _ in range (self ._max_decode_attempts ):
255+ has_grabbed = self ._cap .grab ()
256+ if has_grabbed :
257+ break
258+ # Report previous failure in debug mode.
259+ if has_grabbed :
260+ self ._decode_failures += 1
261+ logger .debug ("Frame failed to decode." )
262+ if not self ._warning_displayed and self ._decode_failures > 1 :
263+ logger .warning ("Failed to decode some frames, results may be inaccurate." )
264+ # We didn't manage to grab a frame even after retrying, so just return.
265+ if not has_grabbed :
266+ return False
267+ self ._has_grabbed = True
319268 # Need to make sure we actually grabbed a frame before calling retrieve.
320269 if decode and self ._has_grabbed :
321270 _ , frame = self ._cap .retrieve ()
@@ -490,35 +439,18 @@ def aspect_ratio(self) -> float:
490439
491440 @property
492441 def position (self ) -> FrameTimecode :
493- """Current position within stream as FrameTimecode. Use the :meth:`position_ms`
494- if an accurate duration of elapsed time is required, as `position` is currently
495- based off of the number of frames, and may not be accurate for devicesor live streams.
496-
497- This method will always return 0 (e.g. be equal to `base_timecode`) if no frames
498- have been `read`."""
499-
500442 if self .frame_number < 1 :
501443 return self .base_timecode
502444 return self .base_timecode + (self .frame_number - 1 )
503445
504446 @property
505447 def position_ms (self ) -> float :
506- """Current position within stream as a float of the presentation time in milliseconds.
507- The first frame has a time of 0.0 ms.
508-
509- This method will always return 0.0 if no frames have been `read`."""
510448 if self ._num_frames == 0 :
511449 return 0.0
512450 return self ._cap .get (cv2 .CAP_PROP_POS_MSEC ) - self ._time_base
513451
514452 @property
515453 def frame_number (self ) -> int :
516- """Current position within stream in frames as an int.
517-
518- 1 indicates the first frame was just decoded by the last call to `read` with advance=True,
519- whereas 0 indicates that no frames have been `read`.
520-
521- This method will always return 0 if no frames have been `read`."""
522454 return self ._num_frames
523455
524456 def seek (self , target : ty .Union [FrameTimecode , float , int ]):
@@ -529,41 +461,28 @@ def reset(self):
529461 """Not supported."""
530462 raise NotImplementedError ("Reset is not supported." )
531463
532- def read (self , decode : bool = True , advance : bool = True ) -> ty .Union [np .ndarray , bool ]:
533- """Read and decode the next frame as a np.ndarray. Returns False when video ends,
534- or the maximum number of decode attempts has passed.
535-
536- Arguments:
537- decode: Decode and return the frame.
538- advance: Seek to the next frame. If False, will return the current (last) frame.
539-
540- Returns:
541- If decode = True, the decoded frame (np.ndarray), or False (bool) if end of video.
542- If decode = False, a bool indicating if advancing to the the next frame succeeded.
543- """
464+ def read (self , decode : bool = True ) -> ty .Union [np .ndarray , bool ]:
544465 if not self ._cap .isOpened ():
545466 return False
546- # Grab the next frame if possible.
547- if advance :
548- has_grabbed = self ._cap .grab ()
549- # If we failed to grab the frame, retry a few times if required.
550- if not has_grabbed :
551- for _ in range (self ._max_read_attempts ):
552- has_grabbed = self ._cap .grab ()
553- if has_grabbed :
554- break
555- # Report previous failure in debug mode.
467+ has_grabbed = self ._cap .grab ()
468+ # If we failed to grab the frame, retry a few times if required.
469+ if not has_grabbed :
470+ for _ in range (self ._max_read_attempts ):
471+ has_grabbed = self ._cap .grab ()
556472 if has_grabbed :
557- self ._decode_failures += 1
558- logger .debug ("Frame failed to decode." )
559- if not self ._warning_displayed and self ._decode_failures > 1 :
560- logger .warning ("Failed to decode some frames, results may be inaccurate." )
561- # We didn't manage to grab a frame even after retrying, so just return.
562- if not has_grabbed :
563- return False
564- if self ._num_frames == 0 :
565- self ._time_base = self ._cap .get (cv2 .CAP_PROP_POS_MSEC )
566- self ._num_frames += 1
473+ break
474+ # Report previous failure in debug mode.
475+ if has_grabbed :
476+ self ._decode_failures += 1
477+ logger .debug ("Frame failed to decode." )
478+ if not self ._warning_displayed and self ._decode_failures > 1 :
479+ logger .warning ("Failed to decode some frames, results may be inaccurate." )
480+ # We didn't manage to grab a frame even after retrying, so just return.
481+ if not has_grabbed :
482+ return False
483+ if self ._num_frames == 0 :
484+ self ._time_base = self ._cap .get (cv2 .CAP_PROP_POS_MSEC )
485+ self ._num_frames += 1
567486 # Need to make sure we actually grabbed a frame before calling retrieve.
568487 if decode and self ._num_frames > 0 :
569488 _ , frame = self ._cap .retrieve ()
0 commit comments