1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 from twisted.internet import reactor
23 import gobject
24 import gst
25
26 from flumotion.common import errors, messages, gstreamer
27 from flumotion.common.i18n import N_, gettexter
28 from flumotion.component import feedcomponent
29 from flumotion.component.producers.playlist import smartscale
30
31 __version__ = "$Rev$"
32 T_ = gettexter()
33
34
36 """
37 I am a GStreamer bin that can scale a video stream from its source pad.
38 """
39 logCategory = "videoscale"
40
41 __gproperties__ = {
42 'width': (gobject.TYPE_UINT, 'width',
43 'Output width',
44 1, 10000, 100, gobject.PARAM_READWRITE),
45 'height': (gobject.TYPE_UINT, 'height',
46 'Output height',
47 1, 10000, 100, gobject.PARAM_READWRITE),
48 'is-square': (gobject.TYPE_BOOLEAN, 'PAR 1/1',
49 'Output with PAR 1/1',
50 False, gobject.PARAM_READWRITE),
51 'add-borders': (gobject.TYPE_BOOLEAN, 'Add borders',
52 'Add black borders to keep DAR if needed',
53 False, gobject.PARAM_READWRITE)}
54
55 - def __init__(self, width, height, is_square, add_borders):
56 gst.Bin.__init__(self)
57 self._width = width
58 self._height = height
59 self._is_square = is_square
60 self._add_borders = add_borders
61
62 self._inpar = None
63 self._inwidth = None
64 self._inheight = None
65
66 self._videoscaler = gst.element_factory_make("videoscale")
67 self._capsfilter = gst.element_factory_make("capsfilter")
68 self._videobox = gst.element_factory_make("videobox")
69 self.add(self._videoscaler, self._capsfilter, self._videobox)
70
71 self._videoscaler.link(self._capsfilter)
72 self._capsfilter.link(self._videobox)
73
74
75 self._sinkPad = gst.GhostPad('sink', self._videoscaler.get_pad('sink'))
76 self._srcPad = gst.GhostPad('src', self._videobox.get_pad('src'))
77 self.add_pad(self._sinkPad)
78 self.add_pad(self._srcPad)
79
80 self._configureOutput()
81
82
83 self._sinkPad.set_setcaps_function(self._sinkSetCaps)
84
85
86 self._videoscaler.get_pad('src').connect(
87 'notify::caps', self._scaleCorrectionCallback)
88
90
91 def unlinkAndReplace(pad, blocked):
92 self._videoscaler.set_state(gst.STATE_NULL)
93 self._capsfilter.set_state(gst.STATE_NULL)
94 self._videobox.set_state(gst.STATE_NULL)
95
96 self._configureOutput()
97
98 self._videobox.set_state(gst.STATE_PLAYING)
99 self._videoscaler.set_state(gst.STATE_PLAYING)
100 self._capsfilter.set_state(gst.STATE_PLAYING)
101
102
103 reactor.callFromThread(blockPad.set_blocked, False)
104
105 event = gst.event_new_custom(gst.EVENT_CUSTOM_DOWNSTREAM,
106 gst.Structure('flumotion-reset'))
107 self._sinkPad.send_event(event)
108
109
110 self.info("Replaced capsfilter")
111 reactor.callFromThread(blockPad.set_blocked_async,
112 True, unlinkAndReplace)
113
115
116
117
118 c = pad.get_negotiated_caps()
119 if c is not None:
120 width = c[0]['width']
121 scale_correction = width % 8
122 if scale_correction and self._width:
123 self.info('"width" given, but output is not a multiple of 8!')
124 return
125 self._videobox.set_property("right", -scale_correction)
126
142
144 self.info("in:%s" % caps.to_string())
145 if not caps.is_fixed():
146 return
147 struc = caps[0]
148 if struc.has_field('pixel-aspect-ratio'):
149 self._inpar = struc['pixel-aspect-ratio']
150 self._inwidth = struc['width']
151 self._inheight = struc['height']
152 return True
153
155 if property.name == 'width':
156 self._width = value
157 elif property.name == 'height':
158 self._height = value
159 elif property.name == 'add-borders':
160 if not gstreamer.element_has_property(self._videoscaler,
161 'add-borders'):
162 self.warning("Can't add black borders because videoscale\
163 element doesn't have 'add-borders' property.")
164 self._add_borders = value
165 elif property.name == 'is-square':
166 self._is_square = value
167 else:
168 raise AttributeError('unknown property %s' % property.name)
169
171 if property.name == 'width':
172 return self._width or 0
173 elif property.name == 'height':
174 return self._height or 0
175 elif property.name == 'add-borders':
176 return self._add_borders
177 elif property.name == 'is-square':
178 return self._is_square or False
179 else:
180 raise AttributeError('unknown property %s' % property.name)
181
185
186
188 """
189 I am an effect that can be added to any component that has a video scaler
190 component and a way of changing the size and PAR.
191 """
192 logCategory = "videoscale-effect"
193
194 - def __init__(self, name, component, sourcePad, pipeline,
195 width, height, is_square, add_borders=False):
196 """
197 @param element: the video source element on which the post
198 processing effect will be added
199 @param pipeline: the pipeline of the element
200 """
201 feedcomponent.PostProcEffect.__init__(self, name, sourcePad,
202 VideoscaleBin(width, height, is_square, add_borders), pipeline)
203 self.pipeline = pipeline
204 self.component = component
205
206 vt = gstreamer.get_plugin_version('videoscale')
207 if not vt:
208 raise errors.MissingElementError('videoscale')
209 if not vt > (0, 10, 29, 0):
210 self.component.addMessage(
211 messages.Warning(T_(N_(
212 "The videoscale element correctly "
213 "works with GStreamer base newer than 0.10.29.1."
214 "You should update your version of GStreamer."))))
215
222
224 self.effectBin.set_property('height', height)
225 self.info('Changing height to %d' % height)
226 self.uiState.set('videoscale-height', height)
227
235
237 return self.effectBin.get_property('height')
238
240 self.effectBin.set_property('width', width)
241 self.info('Changing width to %d' % width)
242 self.uiState.set('videoscale-width', width)
243
245 self._setWidth(width)
246 if self.effect_getIsSquare():
247 self._setHeight(width *
248 (self.effectBin._inheight * self.effectBin._inpar.denom) /
249 (self.effectBin._inwidth * self.effectBin._inpar.num))
250 return width
251
253 return self.effectBin.get_property('width')
254
256 self.effectBin.set_property('is-square', is_square)
257 self.info('Changing is-square to %r' % is_square)
258 self.uiState.set('videoscale-is-square', is_square)
259 return is_square
260
262 return self.effectBin.get_property('is-square')
263
265 self.effectBin.set_property('add-borders', add_borders)
266 self.info('Changing add-borders to %r' % add_borders)
267 self.uiState.set('videoscale-add-borders', add_borders)
268 return add_borders
269
271 return self.effectBin.get_property('add-borders')
272
274 self.par = par
275 self.info('Changing PAR to %s' % str(par))
276
277 return repr(par)
278
281
283 self.info("apply videoscale")
284 self.effectBin.apply()
285 return True
286