예제 #1
0
파일: config.py 프로젝트: tspannhw/maskcam
def print_config_overrides():
    # Leave prints separated so that it can be executed on demand
    # by one single process instead of each import
    for env_var, config_param in ENV_CONFIG_OVERRIDES:
        override_value = os.environ.get(env_var, None)
        if override_value is not None:
            print(f"\nConfig override {env_var}={override_value}")
예제 #2
0
def handle_file_saving(video_period,
                       video_duration,
                       ram_dir,
                       hdd_dir,
                       force_save,
                       mqtt_client=None):
    period = timedelta(seconds=video_period)
    duration = timedelta(seconds=video_duration)
    latest_start = None
    latest_number = 0

    # Handle termination of previous file-saving processes and move files RAM->HDD
    terminated_idxs = []
    for idx, active_process in enumerate(active_filesave_processes):
        if datetime.now() - active_process["started"] >= duration:
            finish_filesave_process(active_process,
                                    hdd_dir,
                                    force_save,
                                    mqtt_client=mqtt_client)
            terminated_idxs.append(idx)
        if latest_start is None or active_process["started"] > latest_start:
            latest_start = active_process["started"]
            latest_number = active_process["number"]

    # Remove terminated processes from list in a separated loop
    for idx in sorted(terminated_idxs, reverse=True):
        del active_filesave_processes[idx]

    # Start new file-saving process if time has elapsed
    if latest_start is None or (datetime.now() - latest_start >= period):
        print("[green]Time to start a new video file [/green]"
              f" (latest started at: {format_tdelta(latest_start)})")
        new_process_number = latest_number + 1
        new_process_name = f"{P_FILESAVE_PREFIX}{new_process_number}"
        new_filename = f"{datetime.today().strftime('%Y%m%d_%H%M%S')}_{new_process_number}.mp4"
        new_filepath = f"{ram_dir}/{new_filename}"
        new_udp_port = allocate_free_udp_port()
        process_handler, e_interrupt_process = start_process(
            new_process_name,
            filesave_main,
            config,
            output_filename=new_filepath,
            udp_port=new_udp_port,
        )
        active_filesave_processes.append(
            dict(
                number=new_process_number,
                name=new_process_name,
                filepath=new_filepath,
                filename=new_filename,
                started=datetime.now(),
                process_handler=process_handler,
                e_interrupt=e_interrupt_process,
                flag_keep_file=False,
                udp_port=new_udp_port,
            ))
예제 #3
0
def start_process(name, target_function, config, **kwargs):
    e_interrupt_process = mp.Event()
    process = mp.Process(
        name=name,
        target=target_function,
        kwargs=dict(
            e_external_interrupt=e_interrupt_process,
            config=config,
            **kwargs,
        ),
    )
    processes_info[name] = {"started": datetime.now(), "running": True}
    process.start()
    print(f"Process [yellow]{name}[/yellow] started with PID: {process.pid}")
    return process, e_interrupt_process
예제 #4
0
def is_alert_condition(statistics, config):
    # Thresholds config
    max_total_people = int(config["maskcam"]["alert-max-total-people"])
    min_visible_people = int(config["maskcam"]["alert-min-visible-people"])
    max_no_mask = float(config["maskcam"]["alert-no-mask-fraction"])

    # Calculate visible people
    without_mask = int(statistics["people_without_mask"])
    with_mask = int(statistics["people_with_mask"])
    visible_people = with_mask + without_mask
    is_alert = False
    if statistics["people_total"] > max_total_people:
        is_alert = True
    elif visible_people >= min_visible_people:
        no_mask_fraction = float(
            statistics["people_without_mask"]) / visible_people
        is_alert = no_mask_fraction > max_no_mask

    print(f"[yellow]ALERT condition: {is_alert}[/yellow]")
    return is_alert
예제 #5
0
def finish_filesave_process(active_process,
                            hdd_dir,
                            force_filesave,
                            mqtt_client=None):
    terminate_process(
        active_process["name"],
        active_process["process_handler"],
        active_process["e_interrupt"],
        delete_info=True,
    )
    release_udp_port(active_process["udp_port"])

    # Move file to its definitive place if flagged, otherwise remove it
    if active_process["flag_keep_file"] or force_filesave:
        definitive_filepath = f"{hdd_dir}/{active_process['filename']}"
        print(f"Force file saving: {bool(force_filesave)}")
        print(
            f"Permanent video file created: [green]{definitive_filepath}[/green]"
        )
        # Must use shutil here to move RAM->HDD
        shutil.move(active_process["filepath"], definitive_filepath)
        # Send updated file list via MQTT (prints ignore if mqtt_client is None)
        mqtt_send_file_list(mqtt_client)
    else:
        print(f"Removing RAM video file: {active_process['filepath']}")
        os.remove(active_process["filepath"])
예제 #6
0
def terminate_process(name, process, e_interrupt_process, delete_info=False):
    print(f"Sending interrupt to {name} process")
    e_interrupt_process.set()
    print(f"Waiting for process [yellow]{name}[/yellow] to terminate...")
    process.join(timeout=10)
    if process.is_alive():
        print(
            f"[red]Forcing termination of process:[/red] [bold]{name}[/bold]",
            warning=True,
        )
        process.terminate()
    if name in processes_info:
        if delete_info:
            del processes_info[
                name]  # Sequential processes, avoid filling memory
        else:
            processes_info[name].update({
                "ended": datetime.now(),
                "running": False
            })
    print(f"Process terminated: [yellow]{name}[/yellow]\n")
예제 #7
0
def mqtt_init(config):
    if MQTT_BROKER_IP is None or MQTT_DEVICE_NAME is None:
        print(
            "[red]MQTT is DISABLED[/red]"
            " since MQTT_BROKER_IP or MQTT_DEVICE_NAME env vars are not defined\n",
            warning=True,
        )
        mqtt_client = None
    else:
        print(f"Connecting to MQTT server {MQTT_BROKER_IP}:{MQTT_BROKER_PORT}")
        print(f"Device name: [green]{MQTT_DEVICE_NAME}[/green]\n\n")
        mqtt_client = mqtt_connect_broker(
            client_id=MQTT_DEVICE_NAME,
            broker_ip=MQTT_BROKER_IP,
            broker_port=MQTT_BROKER_PORT,
            subscribe_to=[(MQTT_TOPIC_COMMANDS, 2)],  # handles re-subscription
            cb_success=mqtt_on_connect,
        )
        mqtt_client.on_message = mqtt_process_message

        return mqtt_client
예제 #8
0
def sigint_handler(sig, frame):
    print("[red]Ctrl+C pressed. Interrupting all processes...[/red]")
    e_interrupt.set()
예제 #9
0
def flag_keep_current_files():
    print("Request to [green]save current video files[/green]")
    for process in active_filesave_processes:
        print(f"Set flag to keep: [green]{process['filename']}[/green]")
        process["flag_keep_file"] = True
예제 #10
0
def release_udp_port(port_number):
    print(f"Releasing UDP port: {port_number}")
    udp_ports_pool.add(port_number)
예제 #11
0
def allocate_free_udp_port():
    new_port = udp_ports_pool.pop()
    print(f"Allocating UDP port: {new_port}")
    return new_port
예제 #12
0
def new_command(command):
    if q_commands.full():
        print(f"Command {command} IGNORED. Queue is full.", error=True)
        return
    print(f"Received command: [yellow]{command}[/yellow]")
    q_commands.put_nowait(command)
예제 #13
0
def flag_keep_current_files():
    print("Request to [green]save current video files[/green]")
    for process in active_filesave_processes:
        print(f"Set flag to keep: [green]{process['filename']}[/green]")
        process["flag_keep_file"] = True


if __name__ == "__main__":
    if len(sys.argv) > 2:
        print("""Usage: python3 maskcam_run.py [ URI ]
        Examples:
        \t$ python3 maskcam_run.py
        \t$ python3 maskcam_run.py file:///absolute/path/to/file.mp4
        \t$ python3 maskcam_run.py v4l2:///dev/video1
        \t$ python3 maskcam_run.py argus://0

        Notes:
        \t - If no URI is provided, will use default-input defined in config_maskcam.txt
        \t - If a file:///path/file.mp4 is provided, the output will be ./output_file.mp4
        \t - If the input is a live camera, the output will be consecutive
        \t   video files under /dev/shm/date_time.mp4
        \t   according to the time interval defined in output-chunks-duration in config_maskcam.txt.
        """)
        sys.exit(0)
    try:
        # Print any ENV var config override to avoid confusions
        print_config_overrides()

        # Input source
        if len(sys.argv) > 1:
            input_filename = sys.argv[1]
            print(f"Provided input source: {input_filename}")