gstlal 1.13.0
Loading...
Searching...
No Matches
gstpipetools.py
1"""Miscellaneous utilities for working with Gstreamer Pipelines
2
3References:
4 [1] 1.0 API: https://lazka.github.io/pgi-docs/Gst-1.0/index.html
5"""
6from typing import Any, Union, Tuple, Iterable, Optional, Callable
7
8import gi
9
10gi.require_version('Gst', '1.0')
11from gi.repository import GObject
12from gi.repository import Gst
13
14GObject.threads_init()
15Gst.init(None)
16
17from lal import LIGOTimeGPS
18from gstlal import simplehandler
19from gstlal import pipeio
20
21LIVE_SEGMENT = (None, None)
22Time = Union[int, float, LIGOTimeGPS]
23
24
25def is_element(x: Any) -> bool:
26 """Test whether an object is a Gst Element. TODO: do these belong somewhere more general?
27
28 Args:
29 x:
30 Any, the object to test
31
32 Returns:
33 bool, True if x is a Gst Element, false otherwise
34 """
35 return isinstance(x, Gst.Element)
36
37
38def is_pad(x: Any) -> bool:
39 """Test whether an object is a Gst Pad. TODO: do these belong somewhere more general?
40
41 Args:
42 x:
43 Any, the object to test
44
45 Returns:
46 bool, True if x is a Gst Pad, false otherwise
47 """
48 return isinstance(x, Gst.Pad)
49
50
51def to_caps(x: Union[str, Gst.Caps]) -> Gst.Caps:
52 """Create a Caps object from a string, or pass thru if already is Caps
53
54 Args:
55 x:
56 str or Caps, if a string construct Caps instance from x, else pass thru as Caps
57
58 Returns:
59 Caps
60 """
61 if isinstance(x, str):
62 return Gst.Caps.from_string(x)
63 if not isinstance(x, Gst.Caps):
64 raise ValueError('Cannot coerce type {} to Caps: {}'.format(type(x), str(x)))
65 return x
66
67
68def make_element(type_name: str, name: str = None, **properties: dict) -> Gst.Element:
69 """Create a new element of the type defined by the given element factory. If name is None, then the element will
70 receive a guaranteed unique name, consisting of the element factory name and a number. If name is given, it will
71 be given the name supplied.
72
73 Args:
74 type_name:
75 str, the name of the type of element
76 elem_name:
77 str, default None, the name of the element instance
78 properties:
79 dict, keyword arguments to set as properties of the element
80
81 References:
82 [1] ElementFactor.make: https://lazka.github.io/pgi-docs/Gst-1.0/classes/ElementFactory.html#Gst.ElementFactory.make
83
84 Returns:
85 Gst.Element
86 """
87 elem = Gst.ElementFactory.make(type_name, name)
88 if elem is None:
89 raise RuntimeError("Unknown failure creating {} element: confirm that the correct plugins are being loaded".format(type_name))
90
91 # Set element properties
92 for k, v in properties.items():
93 elem.set_property(k, pipeio.format_property(v))
94
95 return elem
96
97
98def make_pipeline(name: str) -> Gst.Pipeline:
99 """Create a Pipeline, which is the macroscopic container for Gstreamer elements and is necessary
100 prior to creating any elements
101
102 Args:
103 name:
104 str, the name of the pipeline
105
106 References:
107 [1] https://gstreamer.freedesktop.org/documentation/application-development/introduction/basics.html?gi-language=c#bins-and-pipelines
108
109 Returns:
110 Pipeline
111 """
112 return Gst.Pipeline(name)
113
114
115def run_pipeline(pipeline: Gst.Pipeline, segment: Tuple[Time, Time] = LIVE_SEGMENT, handlers: Optional[Iterable[Callable]] = None):
116 """Run a pipeline using the main event loop
117
118 Args:
119 pipeline:
120 Gst.Pipeline, the pipeline to run
121 segment:
122 Tuple[Time, Time], a playback segment
123 handlers:
124 Iterable[Callable], default None, an optional list of functions that return Handlers. Each must
125 accept only a "mainloop" and "pipeline" argument.
126
127 Returns:
128 None
129 """
130 if handlers is None:
131 handlers = (simplehandler.Handler,)
132
133 mainloop = GObject.MainLoop()
134
135 # Set handlers
136 for handler in handlers:
137 _ = handler(mainloop=mainloop, pipeline=pipeline)
138
139 if pipeline.set_state(Gst.State.READY) != Gst.StateChangeReturn.SUCCESS:
140 raise RuntimeError("pipeline did not enter ready state")
141
142 seek(pipeline, segment)
143
144 if pipeline.set_state(Gst.State.PLAYING) != Gst.StateChangeReturn.SUCCESS:
145 raise RuntimeError("pipeline did not enter playing state")
146
147 mainloop.run()
148
149
150def seek(pipeline, segment: Tuple[Time, Time], flags=Gst.SeekFlags.FLUSH):
151 """Create a new seek event, i.e., Gst.Event.new_seek() for a given
152 gps_start_time and gps_end_time, with optional flags.
153
154 Args:
155 pipeline:
156 gps_start_time:
157 start time as LIGOTimeGPS, float
158 gps_end_time:
159 end time as LIGOTimeGPS, float
160 flags:
161 Optional flags, see [2] for options
162
163 Notes:
164 In contrast to the documentation, we set the seek event directly on the pipeline sources. This is because of implementation
165 issues in framecpp demux that prevent the full backward propagation of the seek event from sink -> source (as is done in the
166 gstreamer documentation [1]).
167
168 References:
169 [1] https://gstreamer.freedesktop.org/documentation/additional/design/seeking.html?gi-language=python
170 [2] Flags https://gstreamer.freedesktop.org/documentation/gstreamer/gstsegment.html?gi-language=python#GstSeekFlags
171
172 Returns:
173 None
174 """
175 start, end = segment
176
177 start_type, start_time = seek_args(start)
178 stop_type, stop_time = seek_args(end)
179
180 if pipeline.current_state != Gst.State.READY:
181 raise ValueError("pipeline must be in READY state")
182
183 pipeline.seek(rate=1.0,
184 format=Gst.Format(Gst.Format.TIME),
185 flags=flags,
186 start_type=start_type,
187 start=start_time,
188 stop_type=stop_type,
189 stop=stop_time)
190
191 for elem in pipeline.iterate_sources():
192 elem.seek(rate=1.0,
193 format=Gst.Format(Gst.Format.TIME),
194 flags=flags,
195 start_type=start_type,
196 start=start_time,
197 stop_type=stop_type,
198 stop=stop_time)
199
200
201def seek_args(time: Time) -> Tuple[Gst.SeekType, int]:
202 """Convenience function for determining the type of arguments to seek for a given time input
203
204 Args:
205 time:
206 Time, either a float or LIGOTimeGPS
207
208 Returns:
209 Tuple[Gst.SeekType, int]
210 """
211 if time is None or time == -1:
212 return (Gst.SeekType.NONE, -1) # -1 == Gst.CLOCK_TIME_NONE
213 elif isinstance(time, LIGOTimeGPS):
214 return (Gst.SeekType.SET, time.ns())
215 else:
216 return (Gst.SeekType.SET, int(float(time) * Gst.SECOND))
Tuple[Gst.SeekType, int] seek_args(Time time)
run_pipeline(Gst.Pipeline pipeline, Tuple[Time, Time] segment=LIVE_SEGMENT, Optional[Iterable[Callable]] handlers=None)
Gst.Pipeline make_pipeline(str name)
Gst.Caps to_caps(Union[str, Gst.Caps] x)
Gst.Element make_element(str type_name, str name=None, **dict properties)
bool is_element(Any x)
seek(pipeline, Tuple[Time, Time] segment, flags=Gst.SeekFlags.FLUSH)