simple_ffmpeg_batch_io.FrameCounter

Create a FrameCounter to follow elapsed time in audio or video file in read/write mode. Static utility function allows to get/format elapsed time.

Authors

Dominique Vaufreydaz

  1"""
  2Create a ``FrameCounter`` to follow elapsed time in audio or video file in read/write mode. Static utility function allows to get/format elapsed time.
  3
  4Authors
  5-------
  6Dominique Vaufreydaz
  7
  8"""
  9
 10__authors__ = ("Dominique Vaufreydaz")
 11
 12from typing import Union
 13
 14# class to count frame, thus time, in audio/video files
 15class FrameCounter:
 16    """
 17    Create a ``FrameCounter`` to follow elapsed time in audio/video file in read or write mode. Static utility functions allow to format elapsed time.
 18    """
 19
 20    class FrameCounterException(Exception):
 21        """
 22        Dedicated exception class for FrameCounter class.
 23        """
 24        def __init__(self, message="Error while setting FrameCounter parameters."):
 25            self.message = message
 26            super().__init__(self.message)
 27
 28    _fps: float # (private)
 29    """ Fps of current stream """
 30
 31    _frame_count: int # (private)
 32    """ Frame count in the current stream """
 33
 34    def __init__(self, fps: Union[int, float]):
 35        """
 36        Create a VideoIO object giving ffmpeg/ffrobe loglevel and defining debug mode
 37
 38        Parameters
 39        ----------
 40        fps: int or float.
 41            Frames per second of the associated stream.
 42        """
 43        # check init fps value
 44        self._fps = float(fps)
 45        if self._fps <= 0.0:
 46            raise FrameCounterException("fps must be > 0.0.")
 47
 48        # 2 modes
 49        self._frame_count = 0       # at 00:00:00.000
 50
 51    # support +=
 52    def __iadd__(self, other: Union[int, float]):
 53        """
 54        Support += operator for FrameCounter. 
 55
 56        Parameters
 57        ----------
 58        other: int or float.
 59            If other is a float, add the number of frame to add 'other' seconds (thus other * self._fps samples).
 60            If other is an int, add the value as a number of samples in the stream.
 61        """        
 62        if isinstance(other,float):
 63            # float means adding time
 64            self._frame_count += int(other * self._fps) # number of second * Nb of elements per seconds
 65        else:
 66            # for int, add number of element
 67            self._frame_count += other
 68        return self
 69    
 70    @property
 71    def frame_count(self):
 72        """
 73        Property to get underlying self._frame_count. Idea is to control setter to valid setting values.
 74        """   
 75        return self._frame_count
 76
 77    @frame_count.setter
 78    def frame_count(self, value: int):
 79        """
 80        Setter for underlying self._frame_count controlling setting value.
 81        """   
 82        if value < 0:
 83            raise FrameCounterException("frame_count must be >= 0")
 84        self._frame_count = value
 85
 86    @property
 87    def fps(self):
 88        """
 89        Property to get underlying self._fps.
 90        """
 91        return self._fps
 92
 93    @staticmethod
 94    def format_time(nb_frames: int, fps: float, show_ms : bool = True, show_days : bool = False) -> str:
 95        """
 96        Static function to format time given by a number of frames and an fps. show_ms value defines if we show milliseconds,
 97        show_days to show days instead of cummulative hour count.
 98
 99        Parameters
100        ----------
101        nb_frames: int.
102            Number of samples already present in the stream.
103            
104        fps: float.
105            Fps of the associated stream.
106            
107        show_ms: bool.
108            Flag to say if we want to show milliseconds in the output str.
109            
110        show_days: bool.
111            Flag to say if we want to show says instead of cumulative hours in the output str.
112
113        Returns
114        -------
115            str representing corresponding time. Either 26:15:00 (show_ms=False, show_days=False), 26:15:00.500 (show_ms=True, show_days=False)
116            or 1 day(s) 02:15:00.500 (show_ms=True, show_days=True)
117        """  
118        # exact time in seconds (float)
119        exact_seconds = nb_frames / fps
120
121        # integer part for days/hours/minutes/seconds
122        total_seconds = int(exact_seconds)
123
124        # milliseconds = decimal part * 1000
125        millis = int(round((exact_seconds - total_seconds) * 1000))
126
127        # handle the case where rounding results in 1000 ms
128        if millis == 1000:
129            millis = 0
130            total_seconds += 1
131
132        if show_days:
133            # compute number of days, hours, minutes, seconds
134            days, mod = divmod(total_seconds, 24 * 3600)
135            hours, mod = divmod(mod, 3600)
136            minutes, seconds = divmod(mod, 60)
137
138            if days > 0:
139                days = f"{days} days(s) "
140            else:
141                days = ""
142        else:
143            days = "" # no day as show_days = False
144            hours, mod = divmod(total_seconds, 3600)
145            minutes, seconds = divmod(mod, 60)
146
147        if show_ms == True:
148            millis = f".{millis:03d}"
149        else:
150            millis = ""
151
152        return f"{days}{hours:02d}:{minutes:02d}:{seconds:02d}{millis}"
153
154    def get_elapsed_time_as_str(self) -> str:
155        """
156        Get elapsed time as string representing a float value rounded to 3 decimals.
157
158        Returns
159        -------
160            str representing corresponding time in float format rounded to 3 decimals.
161        """
162        return f"{float(self._frame_count)/self._fps:.3f}"
163
164    def get_formated_elapsed_time_as_str(self, show_ms : bool = True, show_days : bool = False) -> str:
165        """
166        Get elapsed time as string representing time with different mode (see ``FrameCounter.format_time`` for parameter explanation).
167        Returns
168        -------
169            str representing corresponding time in float format rounded to 3 decimals.
170        """
171        # frame count to time correction is done in format_time
172        return FrameCounter.format_time(self._frame_count, self._fps, show_ms, show_days)
173
174    def get_elapsed_time(self) -> float:
175        """
176        Get elapsed time as float value rounded to 3 decimals.
177
178        Returns
179        -------
180            str representing corresponding time in float format rounded to 3 decimals.
181        """
182        return round(float(self._frame_count)/self._fps,3)
class FrameCounter:
 16class FrameCounter:
 17    """
 18    Create a ``FrameCounter`` to follow elapsed time in audio/video file in read or write mode. Static utility functions allow to format elapsed time.
 19    """
 20
 21    class FrameCounterException(Exception):
 22        """
 23        Dedicated exception class for FrameCounter class.
 24        """
 25        def __init__(self, message="Error while setting FrameCounter parameters."):
 26            self.message = message
 27            super().__init__(self.message)
 28
 29    _fps: float # (private)
 30    """ Fps of current stream """
 31
 32    _frame_count: int # (private)
 33    """ Frame count in the current stream """
 34
 35    def __init__(self, fps: Union[int, float]):
 36        """
 37        Create a VideoIO object giving ffmpeg/ffrobe loglevel and defining debug mode
 38
 39        Parameters
 40        ----------
 41        fps: int or float.
 42            Frames per second of the associated stream.
 43        """
 44        # check init fps value
 45        self._fps = float(fps)
 46        if self._fps <= 0.0:
 47            raise FrameCounterException("fps must be > 0.0.")
 48
 49        # 2 modes
 50        self._frame_count = 0       # at 00:00:00.000
 51
 52    # support +=
 53    def __iadd__(self, other: Union[int, float]):
 54        """
 55        Support += operator for FrameCounter. 
 56
 57        Parameters
 58        ----------
 59        other: int or float.
 60            If other is a float, add the number of frame to add 'other' seconds (thus other * self._fps samples).
 61            If other is an int, add the value as a number of samples in the stream.
 62        """        
 63        if isinstance(other,float):
 64            # float means adding time
 65            self._frame_count += int(other * self._fps) # number of second * Nb of elements per seconds
 66        else:
 67            # for int, add number of element
 68            self._frame_count += other
 69        return self
 70    
 71    @property
 72    def frame_count(self):
 73        """
 74        Property to get underlying self._frame_count. Idea is to control setter to valid setting values.
 75        """   
 76        return self._frame_count
 77
 78    @frame_count.setter
 79    def frame_count(self, value: int):
 80        """
 81        Setter for underlying self._frame_count controlling setting value.
 82        """   
 83        if value < 0:
 84            raise FrameCounterException("frame_count must be >= 0")
 85        self._frame_count = value
 86
 87    @property
 88    def fps(self):
 89        """
 90        Property to get underlying self._fps.
 91        """
 92        return self._fps
 93
 94    @staticmethod
 95    def format_time(nb_frames: int, fps: float, show_ms : bool = True, show_days : bool = False) -> str:
 96        """
 97        Static function to format time given by a number of frames and an fps. show_ms value defines if we show milliseconds,
 98        show_days to show days instead of cummulative hour count.
 99
100        Parameters
101        ----------
102        nb_frames: int.
103            Number of samples already present in the stream.
104            
105        fps: float.
106            Fps of the associated stream.
107            
108        show_ms: bool.
109            Flag to say if we want to show milliseconds in the output str.
110            
111        show_days: bool.
112            Flag to say if we want to show says instead of cumulative hours in the output str.
113
114        Returns
115        -------
116            str representing corresponding time. Either 26:15:00 (show_ms=False, show_days=False), 26:15:00.500 (show_ms=True, show_days=False)
117            or 1 day(s) 02:15:00.500 (show_ms=True, show_days=True)
118        """  
119        # exact time in seconds (float)
120        exact_seconds = nb_frames / fps
121
122        # integer part for days/hours/minutes/seconds
123        total_seconds = int(exact_seconds)
124
125        # milliseconds = decimal part * 1000
126        millis = int(round((exact_seconds - total_seconds) * 1000))
127
128        # handle the case where rounding results in 1000 ms
129        if millis == 1000:
130            millis = 0
131            total_seconds += 1
132
133        if show_days:
134            # compute number of days, hours, minutes, seconds
135            days, mod = divmod(total_seconds, 24 * 3600)
136            hours, mod = divmod(mod, 3600)
137            minutes, seconds = divmod(mod, 60)
138
139            if days > 0:
140                days = f"{days} days(s) "
141            else:
142                days = ""
143        else:
144            days = "" # no day as show_days = False
145            hours, mod = divmod(total_seconds, 3600)
146            minutes, seconds = divmod(mod, 60)
147
148        if show_ms == True:
149            millis = f".{millis:03d}"
150        else:
151            millis = ""
152
153        return f"{days}{hours:02d}:{minutes:02d}:{seconds:02d}{millis}"
154
155    def get_elapsed_time_as_str(self) -> str:
156        """
157        Get elapsed time as string representing a float value rounded to 3 decimals.
158
159        Returns
160        -------
161            str representing corresponding time in float format rounded to 3 decimals.
162        """
163        return f"{float(self._frame_count)/self._fps:.3f}"
164
165    def get_formated_elapsed_time_as_str(self, show_ms : bool = True, show_days : bool = False) -> str:
166        """
167        Get elapsed time as string representing time with different mode (see ``FrameCounter.format_time`` for parameter explanation).
168        Returns
169        -------
170            str representing corresponding time in float format rounded to 3 decimals.
171        """
172        # frame count to time correction is done in format_time
173        return FrameCounter.format_time(self._frame_count, self._fps, show_ms, show_days)
174
175    def get_elapsed_time(self) -> float:
176        """
177        Get elapsed time as float value rounded to 3 decimals.
178
179        Returns
180        -------
181            str representing corresponding time in float format rounded to 3 decimals.
182        """
183        return round(float(self._frame_count)/self._fps,3)

Create a FrameCounter to follow elapsed time in audio/video file in read or write mode. Static utility functions allow to format elapsed time.

FrameCounter(fps: Union[int, float])
35    def __init__(self, fps: Union[int, float]):
36        """
37        Create a VideoIO object giving ffmpeg/ffrobe loglevel and defining debug mode
38
39        Parameters
40        ----------
41        fps: int or float.
42            Frames per second of the associated stream.
43        """
44        # check init fps value
45        self._fps = float(fps)
46        if self._fps <= 0.0:
47            raise FrameCounterException("fps must be > 0.0.")
48
49        # 2 modes
50        self._frame_count = 0       # at 00:00:00.000

Create a VideoIO object giving ffmpeg/ffrobe loglevel and defining debug mode

Parameters

fps: int or float. Frames per second of the associated stream.

frame_count
71    @property
72    def frame_count(self):
73        """
74        Property to get underlying self._frame_count. Idea is to control setter to valid setting values.
75        """   
76        return self._frame_count

Property to get underlying self._frame_count. Idea is to control setter to valid setting values.

fps
87    @property
88    def fps(self):
89        """
90        Property to get underlying self._fps.
91        """
92        return self._fps

Property to get underlying self._fps.

@staticmethod
def format_time( nb_frames: int, fps: float, show_ms: bool = True, show_days: bool = False) -> str:
 94    @staticmethod
 95    def format_time(nb_frames: int, fps: float, show_ms : bool = True, show_days : bool = False) -> str:
 96        """
 97        Static function to format time given by a number of frames and an fps. show_ms value defines if we show milliseconds,
 98        show_days to show days instead of cummulative hour count.
 99
100        Parameters
101        ----------
102        nb_frames: int.
103            Number of samples already present in the stream.
104            
105        fps: float.
106            Fps of the associated stream.
107            
108        show_ms: bool.
109            Flag to say if we want to show milliseconds in the output str.
110            
111        show_days: bool.
112            Flag to say if we want to show says instead of cumulative hours in the output str.
113
114        Returns
115        -------
116            str representing corresponding time. Either 26:15:00 (show_ms=False, show_days=False), 26:15:00.500 (show_ms=True, show_days=False)
117            or 1 day(s) 02:15:00.500 (show_ms=True, show_days=True)
118        """  
119        # exact time in seconds (float)
120        exact_seconds = nb_frames / fps
121
122        # integer part for days/hours/minutes/seconds
123        total_seconds = int(exact_seconds)
124
125        # milliseconds = decimal part * 1000
126        millis = int(round((exact_seconds - total_seconds) * 1000))
127
128        # handle the case where rounding results in 1000 ms
129        if millis == 1000:
130            millis = 0
131            total_seconds += 1
132
133        if show_days:
134            # compute number of days, hours, minutes, seconds
135            days, mod = divmod(total_seconds, 24 * 3600)
136            hours, mod = divmod(mod, 3600)
137            minutes, seconds = divmod(mod, 60)
138
139            if days > 0:
140                days = f"{days} days(s) "
141            else:
142                days = ""
143        else:
144            days = "" # no day as show_days = False
145            hours, mod = divmod(total_seconds, 3600)
146            minutes, seconds = divmod(mod, 60)
147
148        if show_ms == True:
149            millis = f".{millis:03d}"
150        else:
151            millis = ""
152
153        return f"{days}{hours:02d}:{minutes:02d}:{seconds:02d}{millis}"

Static function to format time given by a number of frames and an fps. show_ms value defines if we show milliseconds, show_days to show days instead of cummulative hour count.

Parameters

nb_frames: int. Number of samples already present in the stream.

fps: float. Fps of the associated stream.

show_ms: bool. Flag to say if we want to show milliseconds in the output str.

show_days: bool. Flag to say if we want to show says instead of cumulative hours in the output str.

Returns

str representing corresponding time. Either 26:15:00 (show_ms=False, show_days=False), 26:15:00.500 (show_ms=True, show_days=False)
or 1 day(s) 02:15:00.500 (show_ms=True, show_days=True)
def get_elapsed_time_as_str(self) -> str:
155    def get_elapsed_time_as_str(self) -> str:
156        """
157        Get elapsed time as string representing a float value rounded to 3 decimals.
158
159        Returns
160        -------
161            str representing corresponding time in float format rounded to 3 decimals.
162        """
163        return f"{float(self._frame_count)/self._fps:.3f}"

Get elapsed time as string representing a float value rounded to 3 decimals.

Returns

str representing corresponding time in float format rounded to 3 decimals.
def get_formated_elapsed_time_as_str(self, show_ms: bool = True, show_days: bool = False) -> str:
165    def get_formated_elapsed_time_as_str(self, show_ms : bool = True, show_days : bool = False) -> str:
166        """
167        Get elapsed time as string representing time with different mode (see ``FrameCounter.format_time`` for parameter explanation).
168        Returns
169        -------
170            str representing corresponding time in float format rounded to 3 decimals.
171        """
172        # frame count to time correction is done in format_time
173        return FrameCounter.format_time(self._frame_count, self._fps, show_ms, show_days)

Get elapsed time as string representing time with different mode (see FrameCounter.format_time for parameter explanation).

Returns

str representing corresponding time in float format rounded to 3 decimals.
def get_elapsed_time(self) -> float:
175    def get_elapsed_time(self) -> float:
176        """
177        Get elapsed time as float value rounded to 3 decimals.
178
179        Returns
180        -------
181            str representing corresponding time in float format rounded to 3 decimals.
182        """
183        return round(float(self._frame_count)/self._fps,3)

Get elapsed time as float value rounded to 3 decimals.

Returns

str representing corresponding time in float format rounded to 3 decimals.
class FrameCounter.FrameCounterException(builtins.Exception):
21    class FrameCounterException(Exception):
22        """
23        Dedicated exception class for FrameCounter class.
24        """
25        def __init__(self, message="Error while setting FrameCounter parameters."):
26            self.message = message
27            super().__init__(self.message)

Dedicated exception class for FrameCounter class.

FrameCounter.FrameCounterException(message='Error while setting FrameCounter parameters.')
25        def __init__(self, message="Error while setting FrameCounter parameters."):
26            self.message = message
27            super().__init__(self.message)
message