Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recommended way to handle KeyboardInterrupt (ctrl-c) properly in apps #4

Closed
yuyou opened this issue Apr 29, 2020 · 2 comments
Closed

Comments

@yuyou
Copy link

yuyou commented Apr 29, 2020

Hi

(sorry this may not be an issue but a question)

I have made a test Python app that use the GstContext. As GstContext runs inside a thread and I could not find a way to "terminate" it properly. The main thread could not catch the KeyboardInterrupt (even I have tried the signal).

Is there a way to handle KeyboardInterrupt at the main thread (app) and shutdown pipeline gracefully?

Thanks.

Yu

except Exception:

@jackersson
Copy link
Owner

jackersson commented May 3, 2020

Hi @yuyou ,

Yes, it is known issue with Glib.MainLoop. It was well discussed in here:
beetbox/audioread#63
https://stackoverflow.com/questions/47647200/python-gobject-mainloop-gobbles-signal-events

I've tried those approaches, but it wasn't perfect. So in most similar cases I use the following pattern for launching many pipelines:

import time
import threading

from gstreamer import GstPipeline, GstContext

DEFAULT_PIPELINE = "videotestsrc ! fakesink"


def launch(stop_event: threading.Event):
    with GstContext(), GstPipeline(DEFAULT_PIPELINE) as pipeline:
        while not pipeline.is_done and not stop_event.is_set():
            time.sleep(.1)


def launch_context(event: threading.Event):
    with GstContext():
        while not event.is_set():
            time.sleep(1)


def launch_pipeline(event: threading.Event):
    with GstPipeline(DEFAULT_PIPELINE) as pipeline:
        while not pipeline.is_done and not event.is_set():
            time.sleep(.1)


if __name__ == '__main__':

    num_pipeline = 3
    num_threads = num_pipeline + 1  # thread for context
    events = [threading.Event() for _ in range(num_threads)]
    for e in events:
        e.clear()

    context = threading.Thread(target=launch_context, args=(events[0],))
    pipelines = [threading.Thread(target=launch_pipeline, args=(e,))
                 for e in events[1:]]

    threads = [context] + pipelines
    for t in threads:
        t.start()

    try:
        while any([t.isAlive() for t in threads[1:]]):
            time.sleep(.1)
    except KeyboardInterrupt as e:
        print("Pressed Ctrl-C")
    finally:
        # reverse, so the context will be stopped the last one
        for e, t in zip(reversed(events), reversed(threads)):
            e.set()
            try:
                t.join(timeout=1)
            except Exception as e:
                pass

Notes:

  • Single thread for GstContext
  • Own thread for GstPipeline
  • Loop in the MainThread

I'd be glad to hear any suggestions on how to make it better ;)

@yuyou
Copy link
Author

yuyou commented May 8, 2020

Thanks @jackersson your solution works nicely.

@yuyou yuyou closed this as completed May 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants