def try_open_camera(self, open_streaming=False, repeat_loop= -1, sleep_time=1): # param : # int repeat_loop - if -1, it's an infinite loop, else it's the number loop # bool open_streaming - if true, try to start the streaming of seagoat and the firewire # can be use in threading or in init self.loop_try_open_camera = True while self.loop_try_open_camera: # need to wait 1 second if camera just shutdown, else it's crash time.sleep(sleep_time) if self.call_stop: return False # check if can access to the camera if self.open_camera(): if self.initialize(): if open_streaming: if self.open(): logger.debug("Open with success %s" % self.get_name()) self.loop_try_open_camera = False return True else: logger.debug("Finish with initialize") self.loop_try_open_camera = False return True # check if need to continue the loop if not repeat_loop: self.loop_try_open_camera = False return False if repeat_loop > 0: repeat_loop -= 1 log.print_function(logger.error, "Cannot open the camera %s" % self.get_name())
def stop_record(self, media_name): self._post_command_(locals()) media = self._get_media(media_name=media_name) if not media: return False if media.is_media_video(): log.print_function( logger.error, "Cannot stop record to a media media %s." % media_name) return False status = media.stop_record() if status: # {"time": ..., "media_name": ..., "path": ...} record_status = { "time": time.time(), "media_name": media_name, "path": media.get_path_record(), } self.lst_record_historic.append(record_status) self.publisher.publish(keys.get_key_lst_rec_historic(), record_status) else: log.print_function(logger.error, "Error to stop the record %s." % media_name) return status
def start(self, shape, path=None, fps=30, compress=0): # TODO manage multiple record # manage only one record at time if self.writer: self.stop() if not path: path = "%s" % (self.config.get_path_save_record()) if not path: path = "./" elif "/" not in path: path = "%s%s" % (self.config.get_path_save_record(), path) if os.path.isfile(path): log.print_function(logger.error, "File already exist %s" % path) return False self.compress = compress if not os.path.isdir(path): try: os.makedirs(path) except: pass self.file_name = path logger.info("Start record on path: %s", path) self.media.add_observer(self.write) self.writer = self.write return True
def load_media(self): # update list of media dct_media = {} # Create personalize media for conf_media in self.config.get_lst_media_config(): name = conf_media.name o_media = conf_media.media(conf_media) if o_media.is_opened(): if name in dct_media.keys(): log.print_function(logger.error, "Media %s already exist." % name) continue dct_media[name] = o_media logger.info("Media %s detected." % name) else: log.print_function(logger.error, "Camera %s not detected" % name) # Force media_video media_video = media.media_video.MediaVideo( keys.get_media_file_video_name()) dct_media[keys.get_media_file_video_name()] = media_video # TODO this is a hack to remove missing key warning about publisher # Register in publisher # media_video._get_cb_publisher() # Force create empty media from SeaGoatVision.server.media.implementation.empty import Empty dct_media[keys.get_media_empty_name()] = Empty( keys.get_media_empty_name()) self.dct_media = dct_media self.set_all_publisher()
def _get_execution(self, execution_name): dct_execution = self.dct_exec.get(execution_name, {}) if not dct_execution: msg = "Don't find execution %s. List execution name: %s" % (execution_name, self.dct_exec.keys()) log.print_function(logger.warning, msg, last_stack=True) return None return dct_execution
def _is_config_exist(file_name, type_name, ignore_not_exist): if not os.path.isfile(file_name): if not ignore_not_exist: log.print_function(logger.error, "The %s config %s\ not exist." % (file_name, type_name)) return False return True
def load_media(self): # update list of media dct_media = {} # Create personalize media for conf_media in self.config.get_lst_media_config(): name = conf_media.name o_media = conf_media.media(conf_media) if o_media.is_opened(): if name in dct_media.keys(): log.print_function(logger.error, "Media %s already exist." % name) continue dct_media[name] = o_media logger.info("Media %s detected." % name) else: log.print_function(logger.error, "Camera %s not detected" % name) # Force media_video media_video = media.media_video.MediaVideo(keys.get_media_file_video_name()) dct_media[keys.get_media_file_video_name()] = media_video # TODO this is a hack to remove missing key warning about publisher # Register in publisher # media_video._get_cb_publisher() # Force create empty media from SeaGoatVision.server.media.implementation.empty import Empty dct_media[keys.get_media_empty_name()] = Empty(keys.get_media_empty_name()) self.dct_media = dct_media self.set_all_publisher()
def set_image_observer( self, observer, execution_name, filter_name_old, filter_name_new, new_observer=None): """ Inform the server what filter we want to change observer Param : - ref, observer is a reference on method for callback - string, execution_name to select an execution - string, filter_name_old , filter to replace - string, filter_name_new , filter to use """ if new_observer is None: new_observer = observer self._post_command_(locals()) if filter_name_old == filter_name_new: log.print_function( logger.error, "New and old filter_name is equal: %s" % filter_name_old) return False filterchain = self._get_filterchain(execution_name) if not filterchain: return False filterchain.remove_image_observer(observer, filter_name_old) return filterchain.add_image_observer(new_observer, filter_name_new)
def cmd_to_media(self, media_name, cmd, value=None): media = self._get_media(media_name=media_name) if not media: return False if media.is_media_streaming(): log.print_function(logger.error, "Cannot send a command to a streaming media %s." % media_name) return False return media.do_cmd(cmd, value)
def stop_record(self, media_name): media = self._get_media(media_name=media_name) if not media: return False if media.is_media_video(): log.print_function(logger.error, "Cannot stop record to a media media %s." % media_name) return False return media.stop_record()
def _is_config_exist(file_name, type_name, ignore_not_exist): if not os.path.isfile(file_name): if not ignore_not_exist: log.print_function( logger.error, "The %s config %s\ not exist." % (file_name, type_name)) return False return True
def _add_param(self, dct_param, dct_param_manual, param): name = param.get_name() if name in dct_param: log.print_function(logger.error, "This param is already in the list : %s", name) return False dct_param[name] = param dct_param_manual[name] = param return True
def set_global_params(self, dct_global_param): # complete the list and point on it for key, param in self.dct_global_param.items(): if key in dct_global_param: log.print_function(logger.error, "Duplicate key on dct_global_param : %s", key) continue dct_global_param[key] = param self.dct_global_param = dct_global_param self.set_global_params_cpp(self.dct_global_param)
def get_execution_info(self, execution_name): exec_info = self.dct_exec.get(execution_name, None) if not exec_info: log.print_function(logger.error, "Cannot get execution info, it's empty.") return None class Exec_info: pass o_exec_info = Exec_info() setattr(o_exec_info, KEY_MEDIA, exec_info[KEY_MEDIA].get_name()) setattr(o_exec_info, KEY_FILTERCHAIN, exec_info[KEY_FILTERCHAIN].get_name()) return o_exec_info
def _get_param_media(self, media_name, param_name=None): media = self._get_media(media_name=media_name) if not media: return param = media.get_params(param_name=param_name) if not param: log.print_function( logger.error, "Missing param %s on media %s" % (param_name, media_name)) return param
def _get_param_filter(self, execution_name, filter_name, param_name=None): o_filter = self._get_filter(execution_name, filter_name) if not o_filter: return param = o_filter.get_params(param_name=param_name) if not param: log.print_function( logger.error, "Missing param %s on filter %s" % (param_name, filter_name)) return param
def get_execution_info(self, execution_name): self._post_command_(locals()) exec_info = self.dct_exec.get(execution_name, None) if not exec_info: log.print_function( logger.error, "Cannot get execution info, it's empty.") return return {KEY_MEDIA: exec_info[KEY_MEDIA].get_name(), KEY_FILTERCHAIN: exec_info[KEY_FILTERCHAIN].get_name()}
def _get_filterchain(self, execution_name): dct_execution = self._get_execution(execution_name) if dct_execution is None: return None filterchain = dct_execution.get(KEY_FILTERCHAIN, None) if not filterchain: msg = "Execution %s hasn't filterchain." % execution_name log.print_function(logger.critical, msg, last_stack=True) return None return filterchain
def _get_filter(self, execution_name, filter_name): filterchain = self._get_filterchain(execution_name) if not filterchain or not filter_name: return False o_filter = filterchain.get_filter(name=filter_name) if not o_filter: log.print_function( logger.error, "Missing filter %s on filterchain %s" % ( filter_name, filterchain.get_name())) return False return o_filter
def import_all_cpp_filter(cppfiles, cpptimestamps, module, file, extra_link_arg=[], extra_compile_arg=[]): """ This method finds and compile every c++ filters If a c++ file changed, the file must be recompiled in a new .so file """ # param : # module like sys.modules[__name__] # file is __file__ from __init__.py _create_build(cppfiles) dirname = os.path.dirname(file) for f in os.listdir(dirname): if not f.endswith(".cpp"): continue filename, _ = os.path.splitext(f) cppcode = open(os.path.join(dirname, f)).read() code = "cv::Mat execute(cv::Mat " if code not in cppcode: code += "image)" log.print_function( logger.error, "Missing execute function into %s like \"%s\"" % (filename, code)) continue # Verify if there are changes in the c++ code file. If there are # changes, add a timestamp to the filter .so file name to force a # reimportation of the new filter. if filename in cppfiles: if cppcode != cppfiles[filename]: cpptimestamps[filename] = str(int(time.time())) cppfiles[filename] = cppcode if filename in cpptimestamps: modname = filename + cpptimestamps[filename] else: modname = filename mod = _compile_cpp(modname, cppcode, extra_link_arg, extra_compile_arg) _create_python_code(mod, filename, cppcode) _create_module(cpptimestamps, module, filename, mod, reload_mod=modname)
def deserialize(self, data): if not data: return False if type(data) is not dict: log.print_function(logger.error, "Wrong format data, suppose to be dict into camera %s" % self.get_name()) return False res = data.get("resolution", None) if res: self.dct_params.get("resolution").set(res) res = data.get("fps", None) if res: self.dct_params.get("fps").set(res) return True
def _get_media(self, media_name=None, execution_name=None): media = None if media_name: media = self.resource.get_media(media_name) if not media: log.print_function(logger.error, "Cannot found the media %s." % media_name, last_stack=True) return None elif execution_name: dct_execution = self._get_execution(execution_name) if not dct_execution: return None media = dct_execution.get(KEY_MEDIA, None) return media
def cut_video(self, video_name, begin, end, cut_video_name): self._post_command_(locals()) if not os.path.isfile(video_name): log.print_function(logger.error, "File specified %s doesn't exist." % video_name) return if not video_name[-4:] == ".avi": log.print_function(logger.error, "File specified %s is not .avi." % video_name) return video_media = Movie(video_name) if not video_media: return False rec_thread = ThreadRecordCutVideo(video_media, begin, end, cut_video_name, self) rec_thread.start() return True
def cmd_to_media(self, media_name, cmd, value): # don't print when it's command frame_media, because it's spam if cmd != keys.get_key_media_frame(): self._post_command_(locals()) media = self._get_media(media_name=media_name) if not media: return False if media.is_media_streaming(): log.print_function( logger.error, "Cannot send a command to a streaming media %s." % media_name) return False return media.do_cmd(cmd, value)
def run(self): sleep_time_per_fps = self.media.sleep_time self.running = True protection_max_reset = 3 no_reset = 0 first_fps_time = time.time() nb_fps = 0 while self.running: try: image = self.media.next() nb_fps += 1 no_reset = 0 except StopIteration: if self.media.active_loop: self.media.reset() no_reset += 1 if no_reset >= protection_max_reset: self.running = False log.print_function(logger.error, "Max reset - close media %s" % self.media.get_name()) continue else: while not self.media.active_loop: if not self.running: return time.sleep(sleep_time_per_fps) if image is None: time.sleep(sleep_time_per_fps) continue # take a break if in pause while self.pause: if not self.running: return time.sleep(sleep_time_per_fps) start_time = time.time() if start_time - first_fps_time > 1: self.nb_fps = nb_fps nb_fps = 0 first_fps_time = start_time self.media.notify_observer(image) if not self.running: break if sleep_time_per_fps > 0: sleep_time = sleep_time_per_fps - (time.time() - start_time) if sleep_time > 0: time.sleep(sleep_time) self.running = False
def _read_configuration(self, file_name, type_name, ignore_not_exist): if not self._is_config_exist(file_name, type_name, ignore_not_exist): return None f = open(file_name, "r") if not f: log.print_function(logger.error, "Can't open %s %s." % (file_name, type_name)) return None str_value = f.readlines() try: value = json.loads("".join(str_value)) except: log.print_function(logger.error, "The file %s not contain json data." % file_name) value = None f.close() return value
def update_param(self, execution_name, filter_name, param_name, value): filterchain = self._get_filterchain(execution_name) if not filterchain: return False o_filter = filterchain.get_filter(name=filter_name) if not o_filter: log.print_function(logger.error, "Don't find filter %s on filterchain %s" % (filter_name, filterchain.get_name())) return False param = o_filter.get_params(param_name=param_name) if not param: log.print_function(logger.error, "Don't find param %s on filter %s" % (param_name, filter_name)) return False param.set(value) o_filter.configure() return True
def remove_image_observer(self, observer, filter_name): b_original = False if keys.get_filter_original_name() == filter_name: b_original = True lstObserver = self.original_image_observer else: lstObserver = self.image_observers.get(filter_name, []) if lstObserver: if observer in lstObserver: lstObserver.remove(observer) if not lstObserver and not b_original: del self.image_observers[filter_name] return True log.print_function(logger.warning, "This observer is not in observation list for filter %s" % filter_name) return False
def reload_filter(self, filter_name): o_filter = self.dct_filter.get(filter_name, None) if not o_filter: log.print_function(logger.error, "The filter %s not exist in the list." % filter_name) return # reload the module if o_filter.__module__ == "SeaGoatVision.server.cpp.create_module": module = o_filter.__module_init__ else: module = self._module_name(o_filter.__module__) reload(module) filter_class = getattr(module, filter_name) o_filter = filter_class() o_filter.set_name(filter_name) self.dct_filter[filter_name] = filter_class return o_filter
def import_all_cpp_filter( cppfiles, cpptimestamps, module, file, extra_link_arg=[], extra_compile_arg=[]): """ This method finds and compile every c++ filters If a c++ file changed, the file must be recompiled in a new .so file """ # param : # module like sys.modules[__name__] # file is __file__ from __init__.py _create_build(cppfiles) dirname = os.path.dirname(file) for f in os.listdir(dirname): if not f.endswith(".cpp"): continue filename, _ = os.path.splitext(f) cppcode = open(os.path.join(dirname, f)).read() code = "cv::Mat execute(cv::Mat " if code not in cppcode: code += "image)" log.print_function( logger.error, "Missing execute function into %s like \"%s\"" % (filename, code)) continue # Verify if there are changes in the c++ code file. If there are # changes, add a timestamp to the filter .so file name to force a # reimportation of the new filter. if filename in cppfiles: if cppcode != cppfiles[filename]: cpptimestamps[filename] = str(int(time.time())) cppfiles[filename] = cppcode if filename in cpptimestamps: modname = filename + cpptimestamps[filename] else: modname = filename mod = _compile_cpp(modname, cppcode, extra_link_arg, extra_compile_arg) _create_python_code(mod, filename, cppcode) _create_module( cpptimestamps, module, filename, mod, reload_mod=modname)
def reload_filter(self, filter_name): o_filter = self.dct_filter.get(filter_name, None) if not o_filter: log.print_function( logger.error, "The filter %s not exist in the list." % filter_name) return # reload the module if o_filter.__module__ == "SeaGoatVision.server.cpp.create_module": module = o_filter.__module_init__ else: module = self._module_name(o_filter.__module__) reload(module) filter_class = getattr(module, filter_name) o_filter = filter_class() o_filter.set_name(filter_name) self.dct_filter[filter_name] = filter_class return o_filter
def _read_configuration(self, file_name, type_name, ignore_not_exist): if not self._is_config_exist(file_name, type_name, ignore_not_exist): return logger.info("Opening the %s file..." % file_name) f = open(file_name, "r") if not f: log.print_function(logger.error, "Can't open %s %s." % (file_name, type_name)) return str_value = f.readlines() try: value = json.loads("".join(str_value)) except BaseException: log.print_function( logger.error, "The file %s not contain json data." % file_name) value = None f.close() return value
def next(self): if not self.camera or not self.is_streaming: return diff_time = self.last_timestamp - self.actual_timestamp # logger.debug("actual time %s, last time %s, diff %s" % # (self.actual_timestamp, self.last_timestamp, diff_time)) self.actual_timestamp = self.last_timestamp if self.last_timestamp == -1: if not self.buffer_last_timestamp: self.buffer_last_timestamp = True return log.print_function( logger.warning, "No image receive from %s" % self.get_name()) self.count_no_image += 1 if self.count_no_image > self.max_no_image: self.count_no_image = 0 self.camera_closed() return if not diff_time: self.count_not_receive += 1 if self.count_not_receive >= self.max_not_receive: # logger.error("Didn't receive since %d images. Restart the # camera %s??" % (self.count_not_receive, self.id)) logger.error( "Didn't receive since %d images on camera %s" % (self.count_not_receive, self.get_name())) self.actual_timestamp = self.last_timestamp = -1 self.count_not_receive = 0 # ignore if only missing one image if not self.buffer_last_timestamp: self.buffer_last_timestamp = True return self.actual_image else: # logger.warning( # "Receive no more image from %s, timestamp %d" % # (self.get_name(), self.actual_timestamp)) return # reinitilize all protection self.buffer_last_timestamp = False self.count_no_image = 0 self.count_not_receive = 0 return self.actual_image
def remove_image_observer(self, observer, filter_name): b_original = False if keys.get_filter_original_name() == filter_name: b_original = True lst_observer = self.original_image_observer else: lst_observer = self.image_observers.get(filter_name, []) if lst_observer: if observer in lst_observer: lst_observer.remove(observer) if not lst_observer and not b_original: del self.image_observers[filter_name] return True log.print_function( logger.warning, "This observer is not in observation list for filter %s" % filter_name) return False
def deserialize(self, data): if not data: return False if type(data) is dict: dct_params = data elif type(data) is list: dct_params = {param['name']: param for param in data} else: msg = "Wrong format data, suppose to be list in " \ "%s" % self.get_name() log.print_function(logger.error, msg) return False # search param in config status = True for name, param in self.get_params().items(): dct_param = dct_params.get(name) if dct_param: status &= param.deserialize(dct_param) return status
def add_image_observer(self, observer, filter_name): # Exception for original image b_original = False if keys.get_filter_original_name() == filter_name: b_original = True lstObserver = self.original_image_observer else: lstObserver = self.image_observers.get(filter_name, []) if lstObserver: if observer in lstObserver: log.print_function(logger.warning, "This observer already observer the filter %s" % filter_name) return False else: lstObserver.append(observer) elif not b_original: self.image_observers[filter_name] = [observer] else: lstObserver.append(observer) return True
def load_media(self): # update list of media dct_media = {} # Create personalize media for conf_media in self.config.get_lst_media_config(): name = conf_media.name o_media = conf_media.media(conf_media) if o_media.is_opened(): if name in dct_media.keys(): log.print_function(logger.error, "Media %s already exist." % name) continue dct_media[name] = o_media logger.info("Media %s detected." % name) else: log.print_function(logger.error, "Camera %s not detected" % name) # Force media_video dct_media[keys.get_media_file_video_name()] = media.media_video.Media_video(keys.get_media_file_video_name()) self.dct_media = dct_media
def start_record(self, media_name, path, options): self._post_command_(locals()) """ options can be like this {"compress": 0, "format": "avi"} """ media = self._get_media(media_name=media_name) #print "media: " + media.__class__.__name__ if not media: return False if media.is_media_video(): log.print_function( logger.error, "Cannot start record to a media media %s." % media_name) return False if self._is_keep_alive_media: media.add_observer(self._keep_alive_media) return media.start_record(path=path, options=options)
def try_open_camera( self, open_streaming=False, repeat_loop=-1, sleep_time=1): # param : # int repeat_loop - if -1, it's an infinite loop, \ # else it's the number loop # bool open_streaming - if true, try to start the streaming \ # of seagoat and the firewire # can be use in threading or in init self.loop_try_open_camera = True while self.loop_try_open_camera: # need to wait 1 second if camera just shutdown, else it's crash time.sleep(sleep_time) if self.call_stop: return False # check if can access to the camera if self.open_camera(): time.sleep(2) if self.initialize(): time.sleep(2) if open_streaming: time.sleep(2) if self.open(): logger.debug( "Open with success %s" % self.get_name()) self.loop_try_open_camera = False return True else: logger.debug("Finish with initialize") self.loop_try_open_camera = False return True # check if need to continue the loop if not repeat_loop: self.loop_try_open_camera = False return False if repeat_loop > 0: repeat_loop -= 1 log.print_function( logger.error, "Cannot open the camera %s" % self.get_name())
def start(self, shape, path=None, fps=30, compress=0): # TODO manage multiple record # manage only one record at time if self.process: self.stop() add_format_name = False if path: # exception, if not contain /, maybe it's just a filename if "/" not in path: name = "%s%s.avi" % (self.config.get_path_save_record(), path) else: # TODO need to add extension when giving all path with # filename? name = path if os.path.isdir(path): add_format_name = True else: add_format_name = True # TODO mkdir if directory name = self.config.get_path_save_record() if add_format_name: name += "%s.avi" % time.strftime( "%Y_%m_%d_%H_%M_%S", time.gmtime()) if os.path.isfile(name): log.print_function(logger.error, "File already exist %s" % name) return False self.file_name = name logger.info("Start record on path: %s", name) # 1 is best quality, 36 is worse qscale = compress * 0.35 + 1 self.process = Popen( ['ffmpeg', '-y', '-f', 'image2pipe', '-vcodec', 'mjpeg', '-r', '24', '-i', '-', '-vcodec', 'mpeg4', '-qscale', "%s" % qscale, '-r', '24', name], stdin=PIPE) self.media.add_observer(self.add_image) return True
def deserialize(self, name, value): status = True self.filterchain_name = name lst_filter = value.get("lst_filter", []) self.default_media_name = value.get("default_media_name", None) self.filters = [] index = 0 # add default filter self.add_filter(Filter(keys.get_empty_filter_name())) for filter_to_ser in lst_filter: filter_name = filter_to_ser.get("filter_name", None) o_filter = self.resource.create_filter(filter_name, index) index += 1 if not o_filter: log.print_function( logger.warning, "Cannot create filter %s, maybe it not exists." % filter_name) continue status &= o_filter.deserialize(filter_to_ser) self.add_filter(o_filter) if status: log.print_function(logger.info, "Deserialize filterchain %s success." % name) else: log.print_function(logger.warning, "Deserialize filterchain %s failed." % name) return status
def add_image_observer(self, observer, filter_name): # Exception for original image b_original = False if keys.get_filter_original_name() == filter_name: b_original = True lst_observer = self.original_image_observer else: lst_observer = self.image_observers.get(filter_name, []) if lst_observer: if observer in lst_observer: log.print_function( logger.warning, "This observer already observer the filter %s" % filter_name) return False else: lst_observer.append(observer) elif not b_original: self.image_observers[filter_name] = [observer] else: lst_observer.append(observer) return True
def open_camera(self): logger.debug("open camera %s" % self.get_name()) try: ctx = video1394.DC1394Context() except: log.print_function(logger.error, "Libdc1394 is not supported.") return False if self.cam_guid: self.camera = ctx.createCamera(guid=self.cam_guid) self.id = "guid %s" % str(self.cam_guid) else: self.camera = ctx.createCamera(cid=self.cam_no) self.id = "no %s" % str(self.cam_no) if self.camera is not None: return True else: log.print_function( logger.warning, "No Firewire camera detected - %s." % self.id) return False
def run(self): sleep_time_per_fps = self.media.sleep_time self.running = True protection_max_reset = 3 no_reset = 0 nb_busy = 0 first_fps_time = time.time() nb_fps = 0 image = None msg_error = "Max reset - close media %s" % self.media.get_name() self.media.set_status(media.MediaStatus.run) rotate_param = self.rotate_param while self.running: # TODO try to remove this try catch for better performance try: image = self.media.next() no_reset = 0 # apply rotate picture angle = rotate_param.get() if angle: image = np.rot90(image, angle) except StopIteration: if self.media.active_loop: self.media.reset() no_reset += 1 if no_reset >= protection_max_reset: self.running = False log.print_function(logger.error, msg_error) continue else: while not self.media.active_loop: if not self.running: return time.sleep(sleep_time_per_fps) if image is None: # if receive no image since 1 sec, the camera is maybe busy nb_busy += 1 if nb_busy > self.media.fps: self.media.set_status(media.MediaStatus.busy) time.sleep(sleep_time_per_fps) continue nb_busy = 0 nb_fps += 1 # take a break if in pause while self.pause: self.media.set_status(media.MediaStatus.pause) if not self.running: return time.sleep(sleep_time_per_fps) self.media.set_status(media.MediaStatus.run) if not self.running: return start_time = time.time() if start_time - first_fps_time > 1: self.publisher({"fps": nb_fps}) self.nb_fps = nb_fps nb_fps = 0 first_fps_time = start_time self.media.notify_observer(image) if not self.running: break if sleep_time_per_fps > 0: sleep_time = sleep_time_per_fps - (time.time() - start_time) if sleep_time > 0: time.sleep(sleep_time) self.running = False