def setup_streamer(self, width, height): fps = Fraction(FPS) fps_str = fraction_to_str(fps) caps = f'video/x-raw,format={VIDEO_FORMAT},width={width},height={height},framerate={fps_str}' # Converts list of plugins to gst-launch string # ['plugin_1', 'plugin_2', 'plugin_3'] => plugin_1 ! plugin_2 ! plugin_3 default_pipeline = utils.to_gst_string([ f'appsrc caps={caps}', 'videoscale method=1 add-borders=false', 'video/x-raw,width=1280,height=720', 'videoconvert', 'v4l2sink device=/dev/video0 sync=false' ]) self.duration = 10**9 / (fps.numerator / fps.denominator) self.appsrc = self.pts = self.pipeline = None self.context = GstContext() self.context.startup() self.pipeline = GstPipeline(default_pipeline) def on_pipeline_init(other_self): """Setup AppSrc element""" self.appsrc = other_self.get_by_cls(GstApp.AppSrc)[0] # get AppSrc # instructs appsrc that we will be dealing with timed buffer self.appsrc.set_property("format", Gst.Format.TIME) # instructs appsrc to block pushing buffers until ones in queue are preprocessed # allows to avoid huge queue internal queue size in appsrc self.appsrc.set_property("block", True) self.appsrc.set_property("is-live", True) # set input format (caps) self.appsrc.set_caps(Gst.Caps.from_string(caps)) # override on_pipeline_init to set specific properties before launching pipeline self.pipeline._on_pipeline_init = on_pipeline_init.__get__( self.pipeline) # noqa try: self.pipeline.startup() self.appsrc = self.pipeline.get_by_cls( GstApp.AppSrc)[0] # GstApp.AppSrc self.pts = 0 # buffers presentation timestamp except Exception as e: print("Error: ", e)
import traceback import argparse import typing as typ import time import attr import numpy as np from gstreamer import GstContext, GstPipeline, GstApp, Gst, GstVideo import gstreamer.utils as utils # Converts list of plugins to gst-launch string # ['plugin_1', 'plugin_2', 'plugin_3'] => plugin_1 ! plugin_2 ! plugin_3 DEFAULT_PIPELINE = utils.to_gst_string([ "videotestsrc num-buffers=100", "capsfilter caps=video/x-raw,format=GRAY16_LE,width=640,height=480", "queue", "appsink emit-signals=True" ]) ap = argparse.ArgumentParser() ap.add_argument("-p", "--pipeline", required=False, default=DEFAULT_PIPELINE, help="Gstreamer pipeline without gst-launch") args = vars(ap.parse_args()) command = args["pipeline"] def extract_buffer(sample: Gst.Sample) -> np.ndarray: """Extracts Gst.Buffer from Gst.Sample and converts to np.ndarray"""
# typ.List[typ.Tuple[str, str]] caps = [ prop for prop in pipeline.split("!")[0].split(" ") if "caps" in prop ][0] return dict([p.split('=') for p in caps.split(',') if "=" in p]) except IndexError as err: return None DEFAULT_CAPS = f"video/x-raw,format={VIDEO_FORMAT},width={WIDTH},height={HEIGHT},framerate={fraction_to_str(FPS)}" # Converts list of plugins to gst-launch string # ['plugin_1', 'plugin_2', 'plugin_3'] => plugin_1 ! plugin_2 ! plugin_3 DEFAULT_PIPELINE = utils.to_gst_string([ f"appsrc emit-signals=True is-live=True caps={DEFAULT_CAPS}", "queue", "videoconvert", "autovideosink" ]) ap = argparse.ArgumentParser() ap.add_argument("-p", "--pipeline", required=False, default=DEFAULT_PIPELINE, help="Gstreamer pipeline without gst-launch") ap.add_argument("-n", "--num_buffers", required=False, default=100, help="Num buffers to pass")
def __init__(self, shared: bool = False): # https://lazka.github.io/pgi-docs/GstRtspServer-1.0/classes/RTSPServer.html self.server = GstRtspServer.RTSPServer() # https://lazka.github.io/pgi-docs/GstRtspServer-1.0/classes/RTSPMediaFactory.html#GstRtspServer.RTSPMediaFactory.set_shared # f.set_shared(True) # https://lazka.github.io/pgi-docs/GstRtspServer-1.0/classes/RTSPServer.html#GstRtspServer.RTSPServer.get_mount_points # https://lazka.github.io/pgi-docs/GstRtspServer-1.0/classes/RTSPMountPoints.html#GstRtspServer.RTSPMountPoints m = self.server.get_mount_points() # pipeline # Launch test buffers streaming pipeline = utils.to_gst_string([ "videotestsrc num-buffers=1000", "video/x-raw,format={fmt},width={width},height={height}".format(fmt=VIDEO_FORMAT, width=WIDTH, height=HEIGHT), "appsink emit-signals=True" ]) # Launch file streaming # pipeline = [ # "filesrc location=video.mp4", # "decodebin" # "videoconvert n-threads=0", # "video/x-raw,format=RGB", # "appsink emit-signals=True" # ] # Buffers streaming from pipeline (Gst) generator = functools.partial( GstBufferGeneratorFromPipeline, gst_launch=pipeline, loop=True ) # Fake buffers streaming from generator (numpy) # generator = functools.partial(FakeGstBufferGenerator, width=WIDTH, height=HEIGHT, # fps=FPS, video_frmt=GST_VIDEO_FORMAT) # https://lazka.github.io/pgi-docs/GstRtspServer-1.0/classes/RTSPMountPoints.html#GstRtspServer.RTSPMountPoints.add_factory mount_point = "/stream.rtp" factory = RTSPMediaFactoryCustom(generator) # Launch Raw Stream pipeline = [ "appsrc emit-signals=True is-live=True", "queue max-size-buffers=8", "rtpvrawpay name=pay0 pt=96" ] # Launch H264 Stream # pipeline = [ # "appsrc emit-signals=True is-live=True", # "queue max-size-buffers=8", # "videoconvert n-threads=0 ! video/x-raw,format=I420", # "264enc tune=zerolatency", # pass quant # "queue max-size-buffers=8", # "rtph264pay config-interval=1 name=pay0 pt=96" # ] factory.set_launch(utils.to_gst_string(pipeline)) factory.set_shared(shared) m.add_factory(mount_point, factory) # adding streams port = self.server.get_property("service") print(f"rtsp://localhost:{port}/{mount_point}") # https://lazka.github.io/pgi-docs/GstRtspServer-1.0/classes/RTSPServer.html#GstRtspServer.RTSPServer.attach self.server.attach(None)