1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 from twisted.internet import reactor
19 import gobject
20 import gst
21
22 from flumotion.common import errors, messages, gstreamer
23 from flumotion.common.i18n import N_, gettexter
24 from flumotion.component import feedcomponent
25
26
27 __version__ = "$Rev$"
28 T_ = gettexter()
29
30
32 """
33 I am a GStreamer bin that can scale a video stream from its source pad.
34 """
35 logCategory = "videoscale"
36
37 __gproperties__ = {
38 'width': (gobject.TYPE_UINT, 'width',
39 'Output width',
40 1, 10000, 100, gobject.PARAM_READWRITE),
41 'height': (gobject.TYPE_UINT, 'height',
42 'Output height',
43 1, 10000, 100, gobject.PARAM_READWRITE),
44 'width-correction': (gobject.TYPE_UINT, 'width correction',
45 'Corrects with to be a multiple of this value',
46 0, 64, 8, gobject.PARAM_READWRITE),
47 'height-correction': (gobject.TYPE_UINT, 'height correction',
48 'Corrects height to be a multiple of this value',
49 0, 64, 0, gobject.PARAM_READWRITE),
50 'is-square': (gobject.TYPE_BOOLEAN, 'PAR 1/1',
51 'Output with PAR 1/1',
52 False, gobject.PARAM_READWRITE),
53 'add-borders': (gobject.TYPE_BOOLEAN, 'Add borders',
54 'Add black borders to keep DAR if needed',
55 False, gobject.PARAM_READWRITE)}
56
57 - def __init__(self, width, height, is_square, add_borders,
58 width_correction=8, height_correction=0):
59 gst.Bin.__init__(self)
60 self._width = width
61 self._height = height
62 self._width_correction = width_correction
63 self._height_correction = height_correction
64 self._is_square = is_square
65 self._add_borders = add_borders
66
67 self._inpar = None
68 self._inwidth = None
69 self._inheight = None
70
71 self._identity = gst.element_factory_make("identity")
72 self._videoscaler = gst.element_factory_make("videoscale")
73 self._capsfilter = gst.element_factory_make("capsfilter")
74 self._videobox = gst.element_factory_make("videobox")
75 self.add(self._identity, self._videoscaler, self._capsfilter,
76 self._videobox)
77
78 self._identity.link(self._videoscaler)
79 self._videoscaler.link(self._capsfilter)
80 self._capsfilter.link(self._videobox)
81
82
83 self._sinkPad = gst.GhostPad('sink', self._identity.get_pad('sink'))
84 self._srcPad = gst.GhostPad('src', self._videobox.get_pad('src'))
85 self.add_pad(self._sinkPad)
86 self.add_pad(self._srcPad)
87
88 self._configureOutput()
89
90 self._identity.set_property('silent', True)
91
92 self._sinkPad.set_setcaps_function(self._sinkSetCaps)
93
94
95 self._videoscaler.get_pad('src').connect(
96 'notify::caps', self._applyScaleCorrection)
97
99
100 def unlinkAndReplace(pad, blocked):
101 self._videoscaler.set_state(gst.STATE_NULL)
102 self._capsfilter.set_state(gst.STATE_NULL)
103 self._videobox.set_state(gst.STATE_NULL)
104
105 self._configureOutput()
106
107 self._videobox.set_state(gst.STATE_PLAYING)
108 self._videoscaler.set_state(gst.STATE_PLAYING)
109 self._capsfilter.set_state(gst.STATE_PLAYING)
110
111
112 reactor.callFromThread(blockPad.set_blocked, False)
113
114 self._sinkPad.send_event(gstreamer.flumotion_reset_event())
115
116
117 self.info("Replaced capsfilter")
118 reactor.callFromThread(blockPad.set_blocked_async,
119 True, unlinkAndReplace)
120
136
138
139
140
141
142
143
144
145 c = pad.get_negotiated_caps()
146 if c is None:
147 return
148
149 width = c[0]['width']
150 height = c[0]['height']
151
152 def correctScale(value, correction, isWidth, propIsSet):
153 if correction == 0:
154 return
155
156 name = isWidth and "width" or "height"
157 scale_correction = value % correction
158
159 if scale_correction == 0:
160 return
161
162 if propIsSet:
163 self.warning('%s given, but output is not a '
164 'multiple of %s!' % (name, correction))
165 return
166
167 self.info("Correcting %s with %s pixels to be a multiple "
168 "of %s" % (name, scale_correction, correction))
169 if isWidth:
170 self._videobox.set_property("right", -scale_correction)
171 else:
172 self._videobox.set_property("top", -scale_correction)
173
174 correctScale(width, self._width_correction, True, self._width)
175 correctScale(height, self._height_correction, False, self._height)
176
178 self.info("in:%s" % caps.to_string())
179 if not caps.is_fixed():
180 return
181 struc = caps[0]
182 if struc.has_field('pixel-aspect-ratio'):
183 self._inpar = struc['pixel-aspect-ratio']
184 self._inwidth = struc['width']
185 self._inheight = struc['height']
186 return True
187
189 if property.name == 'width':
190 self._width = value
191 elif property.name == 'height':
192 self._height = value
193 elif property.name == 'width-correction':
194 self._width_correction = value
195 elif property.name == 'height-correction':
196 self._height_correction = value
197 elif property.name == 'add-borders':
198 if not gstreamer.element_has_property(self._videoscaler,
199 'add-borders'):
200 self.warning("Can't add black borders because videoscale\
201 element doesn't have 'add-borders' property.")
202 self._add_borders = value
203 elif property.name == 'is-square':
204 self._is_square = value
205 else:
206 raise AttributeError('unknown property %s' % property.name)
207
209 if property.name == 'width':
210 return self._width or 0
211 elif property.name == 'height':
212 return self._height or 0
213 elif property.name == 'width-correction':
214 return self._width_correction
215 elif property.name == 'height-correction':
216 return self._height_correction
217 elif property.name == 'add-borders':
218 return self._add_borders
219 elif property.name == 'is-square':
220 return self._is_square or False
221 else:
222 raise AttributeError('unknown property %s' % property.name)
223
227
228
230 """
231 I am an effect that can be added to any component that has a video scaler
232 component and a way of changing the size and PAR.
233 """
234 logCategory = "videoscale-effect"
235
236 - def __init__(self, name, component, sourcePad, pipeline,
237 width, height, is_square, add_borders=False,
238 width_correction=8, height_correction=0):
239 """
240 @param element: the video source element on which the post
241 processing effect will be added
242 @param pipeline: the pipeline of the element
243 """
244 feedcomponent.PostProcEffect.__init__(self, name, sourcePad,
245 VideoscaleBin(width, height, is_square, add_borders,
246 width_correction, height_correction), pipeline)
247 self.pipeline = pipeline
248 self.component = component
249
250 vt = gstreamer.get_plugin_version('videoscale')
251 if not vt:
252 raise errors.MissingElementError('videoscale')
253
254
255 if not vt > (0, 10, 29, 0):
256 self.component.addMessage(
257 messages.Warning(T_(N_(
258 "The videoscale element correctly "
259 "works with GStreamer base newer than 0.10.29.1."
260 "You should update your version of GStreamer."))))
261
268
270 self.effectBin.set_property('height', height)
271 self.info('Changing height to %d' % height)
272 self.uiState.set('videoscale-height', height)
273
281
283 return self.effectBin.get_property('height')
284
286 self.effectBin.set_property('width', width)
287 self.info('Changing width to %d' % width)
288 self.uiState.set('videoscale-width', width)
289
291 self._setWidth(width)
292 if self.effect_getIsSquare():
293 self._setHeight(width *
294 (self.effectBin._inheight * self.effectBin._inpar.denom) /
295 (self.effectBin._inwidth * self.effectBin._inpar.num))
296 return width
297
299 return self.effectBin.get_property('width')
300
302 self.effectBin.set_property('is-square', is_square)
303 self.info('Changing is-square to %r' % is_square)
304 self.uiState.set('videoscale-is-square', is_square)
305 return is_square
306
308 return self.effectBin.get_property('is-square')
309
311 self.effectBin.set_property('add-borders', add_borders)
312 self.info('Changing add-borders to %r' % add_borders)
313 self.uiState.set('videoscale-add-borders', add_borders)
314 return add_borders
315
317 return self.effectBin.get_property('add-borders')
318
320 self.par = par
321 self.info('Changing PAR to %s' % str(par))
322
323 return repr(par)
324
327
329 self.info("apply videoscale")
330 self.effectBin.apply()
331 return True
332