From 554d72facd4c73f9e06baa90c2845a2005ef03f2 Mon Sep 17 00:00:00 2001 From: Santosh R Date: Tue, 7 Mar 2023 13:39:05 +0530 Subject: [PATCH] Added a Python example for source switching and added a README.md file explaining how it's done. --- example/python_example/README.md | 55 ++++ .../img_files/Source Switched.drawio.png | Bin 0 -> 37532 bytes .../img_files/Source Switching.drawio.png | Bin 0 -> 37696 bytes example/python_example/test_pipeline.py | 273 ++++++++++++++++++ example/python_example/test_utils.py | 223 ++++++++++++++ 5 files changed, 551 insertions(+) create mode 100644 example/python_example/README.md create mode 100644 example/python_example/img_files/Source Switched.drawio.png create mode 100644 example/python_example/img_files/Source Switching.drawio.png create mode 100644 example/python_example/test_pipeline.py create mode 100644 example/python_example/test_utils.py diff --git a/example/python_example/README.md b/example/python_example/README.md new file mode 100644 index 0000000..0d2fe60 --- /dev/null +++ b/example/python_example/README.md @@ -0,0 +1,55 @@ +# Dynamic source switching with interpipes + +## Introduction + +In this example, we attempt to show dynamic switching of interpipes when an `rtsp` source goes down and a dummy source takes it's place. + +NOTE: Rtsp source is used here, but this can be replicated to any other source as well. + +## Usage + +1. The following command is used to run the pipeline: + + ```bash + python3 test_pipeline.py [rtsp_url_2] ... [rtsp_url_N] + + ``` + * For eg: + * ```python3 test_pipeline.py "rtsp://127.x.x.1:8554/stream1" "rtsp://127.x.x.2:8554/stream2"``` + +# Explanation + +The example code ingests rtsp stream(s) and saves every frame as a jpg image. + +This functionality is achieved by splitting the pipeline into 3:- + +* Original Source Pipeline(s)- (OSP) -> This pipeline consists of the rtsp source and which is decoded to raw buffers. + +* Main Pipeline -> Ingests decoded raw buffers from various source pipelines, and saves them as a jpg image. + +* Dummy Pipeline(s) -> Acts as a substitute and sends in frames, when any of the OSP is down. + +### Initial Flow of buffers +![Initial Pipeline](/gst-interpipe/example/python_example/img_files/Source%20Switching.drawio.png) + +### Flow of buffers when source has changed +![Pipeline with Source switched](/gst-interpipe/example/python_example/img_files/Source%20Switched.drawio.png) + + +## How is this achieved? + +A buffer probe is attached to the decoding element and the triggers `source_stream_pad_buffer_probe` function. This keeps updating the most recent buffer timestamps that pass through the decoding element. + +The variable `PERMISSIBLE_TIMEOUT` holds the timelimit in seconds an OSP can be down before switching to a dummy is initiated. + +The function `handle_source_downtime` is running in a separate thread which is executed every `PERMISSIBLE_TIMEOUT/4` seconds. + +### How does it work? + +* This function checks if a source pipeline is alive by checking if the recent buffer timestamp has exceeded the `PERMISSIBLE_TIMEOUT` value. If yes, the interpipesrc _`listen-to`_ property is changed to the corresponding dummy source pipeline. + +* Since `handle_source_downtime` runs every _`PERMISSIBLE_TIMEOUT/4`_ seconds, list of interpipesrc(s) listening to a dummy source are stored in `current_dummies`. We iterate through the recent timestamp stored in the list `timers2` and check if any of the previously "dead" source pipelines, are pushing out buffers. + +* If a new buffer hasn't arrived in the OSP _(the timestamp would still exceed the _`PERMISSIBLE_TIMEOUT/4`_ threshold)_ it is restarted. + +* If a source is back online(_the difference between the current time and latest timestamp is less than `PERMISSIBLE_TIMEOUT/4` seconds_) the interpipsrc _`listen-to`_ property is changed back to the correspondingh source interpipesink. \ No newline at end of file diff --git a/example/python_example/img_files/Source Switched.drawio.png b/example/python_example/img_files/Source Switched.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..0f1543f15b1667e1db5815bf13c26ebe534348cf GIT binary patch literal 37532 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 literal 0 HcmV?d00001 diff --git a/example/python_example/img_files/Source Switching.drawio.png b/example/python_example/img_files/Source Switching.drawio.png new file mode 100644 index 0000000000000000000000000000000000000000..5d00101feed63b24415d57d20c33dc52547baf3c GIT binary patch literal 37696 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 [rtsp_url_2] ... [rtsp_url_N]\n" % args[0]) + sys.exit() + + global perf_data + number_sources = len(args) - 1 + sources_urls = args[1:] + + # Standard GStreamer initialization + Gst.init(None) + logger.debug("Creating Pipeline \n ") + + global dummy_pipeline, main_pipeline + main_pipeline = Gst.Pipeline() + if not main_pipeline: + logger.error("Unable to create Pipeline") + dummy_pipeline = Gst.Pipeline.new(f"dummy-pipeline") + if not dummy_pipeline: + logger.error("Unable to create dummy-pipeline") + + + logger.debug("Creating element \n ") + for index, stream_url in enumerate(sources_urls): + create_source_pipeline(index, stream_url) + create_dummy_pipeline(index, dummy_pipeline) + create_main_pipeline(index, main_pipeline) + + # create an event loop and feed gstreamer bus mesages to it + loop = GLib.MainLoop() + for src_pipeline in SRC_PIPELINES: + src_bus = src_pipeline.get_bus() + src_bus.add_signal_watch() + src_bus.connect("message", test_utils.bus_call_src_pipeline, loop, src_pipeline) + + + bus = main_pipeline.get_bus() + bus.add_signal_watch() + bus.connect("message", test_utils.bus_call, loop, main_pipeline) + + logger.debug("Starting pipeline \n") + # start play back and listed to events + dummy_pipeline.set_state(Gst.State.PLAYING) + for src_pipeline1 in SRC_PIPELINES: + src_pipeline1.set_state(Gst.State.PLAYING) + time.sleep(number_sources) + main_pipeline.set_state(Gst.State.PLAYING) + + repeated_timer = RepeatedTimer( + PERMISSIBLE_TIMEOUT / 4, + test_utils.handle_source_downtime, + VIDEO_SOURCE_TIMER, + INTERPIPESRC, + INTERPIPESINK, + DUMMYSINKS, + SRC_PIPELINES, + PERMISSIBLE_TIMEOUT, +) + try: + loop.run() + except: + pass + + # cleanup + logger.debug("Exiting app\n") + repeated_timer.stop() + main_pipeline.set_state(Gst.State.NULL) + for src_pipeline2 in SRC_PIPELINES: + src_pipeline2.set_state(Gst.State.NULL) + dummy_pipeline.set_state(Gst.State.NULL) + +if __name__ == '__main__': + main(sys.argv) diff --git a/example/python_example/test_utils.py b/example/python_example/test_utils.py new file mode 100644 index 0000000..06403eb --- /dev/null +++ b/example/python_example/test_utils.py @@ -0,0 +1,223 @@ +import time +import gi + +gi.require_version("Gst", "1.0") +from gi.repository import Gst +from logzero import logger + + +def handle_source_downtime(timers2, interpipesrcs, interpipesinks, dummysink, src_pipelines, permissible_cam_timeout): + """ + Change to dummy source, if buffers stop flowing in original source pipeline. + + Args: + timers2 (list): Timestamps of when buffers were recently updated + interpipesrcs (list): List of interpipesources in mainpipeline + interpipesinks (list): List of interpipesinks which send buffers to mainpipeline + dummysink (list): List of dummy interpipesinks in dummypipeline + src_pipelines (list): List containing source pipelines + permissible_cam_timeout (float): Maximum time for which a video source can be without fresh data. + """ + time_1 = time.time() + current_dummies = [ + i for i, _ in enumerate(timers2) if interpipesrcs[i].get_property("listen-to").startswith("dummy_source_") + ] + # Iterate through all the sources' latest buffer timestamps + for i, time_2 in enumerate(timers2): + # If the first buffer for said video source hasn't flown yet, then the dummy source will not be + # able to negotiate a proper shape, etc to send videotestsrc buffers in + # NOTE: Lines 157-159 are commented out as caps aren't set(or required) for dummy buffers from original source. + # if data_started[i] is False: + # logger.debug(f"Missing first contact from source {i+1} to gst core pipeline") + # continue + + # If said video source is currently linked to a dummy source + if i in current_dummies: + # Restart source if actual video source hasn't sent a buffer in the recent past + if time_1 - time_2 > permissible_cam_timeout: + src_pipelines[i].set_state(Gst.State.NULL) + src_pipelines[i].set_state(Gst.State.PLAYING) + + # Else switch the dummy input back to the actual video source + else: + original_sink = interpipesinks[i].get_property("name") + interpipesrcs[i].set_property("listen-to", original_sink) + current_dummies.remove(i) + logger.debug(f"Input {i} is listening back to {original_sink}\n") + + # If said video source is linked to a actual video source albeit a zombie (no buffers for a while) + elif (i not in current_dummies) and time_1 - time_2 > permissible_cam_timeout: + logger.debug(f"No buffers are recieved from actual source: {i}\n") + dummy = Gst.Object.get_name(dummysink[i]) + interpipesrcs[i].set_property("listen-to", dummy) + current_dummies.append(i) + logger.debug(f"Input: {i} has been changed to its dummy input\n") + # If no switch is to be made for said video source, then move on to next cam + else: + continue + logger.debug(f"Current dummies: {current_dummies}\n") + + +def source_stream_pad_buffer_probe(_pad, info, vid_source_timer, index, _source): + gst_buffer = info.get_buffer() + # logger.info(f"buffers coming through for source_pipeline:{_source.name}-{index} \n") + if not gst_buffer: + logger.error("Unable to get GstBuffer ") + return Gst.PadProbeReturn.DROP + time_2 = time.time() + vid_source_timer[index] = time_2 + return Gst.PadProbeReturn.OK + + +def create_gst_ele(element, name=""): + """ + Create GStreamer Element along with catching any failures in the making. + + Args: + element (str): Type of element to be made + name (str): Name to be assigned internal to GStreamer for that element + + Returns: + Gstreamer Element: Element that was requested + """ + if name: + ele = Gst.ElementFactory.make(element, name) + else: + ele = Gst.ElementFactory.make(element) + if not ele: + logger.error(f"Unable to create Element: {element}, with name {name}") + return ele + + +def bus_call(_bus, message, loop, gst_pipeline): + """ + Handle all messages coming through to the core pipeline. + + Args: + bus (Gst.Bus): Reference to bus of pipeline + message (Gst.Message): Type of message that is to be handled + loop (GObject.MainLoop): Main event loop + gst_pipeline (Gst.Pipeline): reference to main pipeline + uris: contains list of input uris + Returns: + True, if messages handled correctly, else throws error + """ + type_ = message.type + if type_ == Gst.MessageType.EOS: + logger.debug(f"End-of-stream from: {Gst.Object.get_name(message.src)}\n") + loop.quit() + elif type_ == Gst.MessageType.WARNING: + err, debug = message.parse_warning() + if "Impossible to configure latency" not in debug: + # errors from flvmuxer in the case of rtmp output are ignored + logger.debug(f"{err}:- {debug}\n") + elif type_ == Gst.MessageType.ERROR: + err, debug = message.parse_error() + if "watchdog" in Gst.Object.get_name(message.src): + logger.debug("GStreamer Mux watchdog triggered") + gst_pipeline.pipeline.set_state(Gst.State.NULL) + gst_pipeline.pipeline.send_event(Gst.Event.new_eos()) + logger.debug("Sent EoS") + gst_pipeline.cleanup() + logger.debug("Restart completed") + elif "interpipesrc" in str(Gst.Object.get_name(message.src)).lower(): + if "Internal data stream error" in str(err): + logger.debug(f"Internal data stream error in {Gst.Object.get_name(message.src)}\n") + else: + logger.debug(f"{err}:- {debug}\n") + else: + logger.debug(f"{err}:- {debug}\n") + loop.quit() + elif type_ in ( + Gst.MessageType.STATE_CHANGED, + Gst.MessageType.STREAM_STATUS, + Gst.MessageType.ELEMENT, + Gst.MessageType.TAG, + Gst.MessageType.DURATION_CHANGED, + Gst.MessageType.ASYNC_DONE, + Gst.MessageType.NEW_CLOCK, + Gst.MessageType.PROGRESS, + Gst.MessageType.BUFFERING, + Gst.MessageType.QOS, + Gst.MessageType.LATENCY, + ): + pass + elif type_ == Gst.MessageType.STREAM_START: + logger.debug("New Data Stream started\n") + else: + logger.debug(f"Unknown message type {type_}: {message}\n") + return True + + +def bus_call_src_pipeline(_bus, message, loop, gst_pipeline): + """ + Handle all messages coming through to the source pipeline. + + Args: + bus (Gst.Bus): Reference to bus of pipeline + message (Gst.Message): Type of message that is to be handled + loop (GObject.MainLoop): Main event loop + gst_pipeline (Gst.Pipeline): reference to the source pipeline + index (int): index + uri_name (str): input uri fed to src pipeline + files_list (list): list containing the files matching wildcard pattern + input_datatype (string): input datatype of source uri + + Returns: + True, if messages handled correctly, else throws error + """ + type_ = message.type + if type_ == Gst.MessageType.EOS: + logger.debug("EoS from URI\n") + loop.quit() + elif type_ == Gst.MessageType.WARNING: + err, debug = message.parse_warning() + logger.debug(f"{err}:- {debug}\n") + elif type_ == Gst.MessageType.ERROR: + parse_error_message(message, gst_pipeline, loop) + elif type_ in ( + Gst.MessageType.STATE_CHANGED, + Gst.MessageType.STREAM_STATUS, + Gst.MessageType.ELEMENT, + Gst.MessageType.TAG, + Gst.MessageType.DURATION_CHANGED, + Gst.MessageType.ASYNC_DONE, + Gst.MessageType.NEW_CLOCK, + Gst.MessageType.PROGRESS, + Gst.MessageType.BUFFERING, + Gst.MessageType.QOS, + Gst.MessageType.LATENCY, + ): + pass + elif type_ == Gst.MessageType.ELEMENT: + logger.debug(f"{message} and {message.src}\n") + elif type_ == Gst.MessageType.STREAM_START: + logger.debug(f"New Data Stream started at source {Gst.Object.get_name(gst_pipeline)}\n") + else: + if "tsdemux" in str(Gst.Object.get_name(message.src)): + return True + logger.debug(f"Unknown message type {type_}: {message}, source: {Gst.Object.get_name(message.src)}\n") + return True + + +def parse_error_message(message, gst_pipeline, loop): + """ + Parse error messages sent from bus and processes them accordingly. + + Args: + message (Gst.MessageType.ERROR): Error message from bus + gst_pipeline (Gst.Pipeline): reference to the source pipeline + loop (GObject.MainLoop): Main event loop + """ + err, debug = message.parse_error() + if any(domain in err.domain for domain in ("stream", "resource")): + logger.debug(f"Stream/Resource Error. Pausing the pipeline {Gst.Object.get_name(gst_pipeline)}\n") + try: + gst_pipeline.set_state(Gst.State.NULL) + + # Suppose it occurs on first run, the gst_pipeline.pipeline object itself hasn't been created yet + except AttributeError: + pass + else: + logger.error(f"{err}:- {debug}\n") + loop.quit()