1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import gst
19 import gobject
20 import threading
21
22 from flumotion.component import decodercomponent as dc
23 from flumotion.common import messages, gstreamer
24 from flumotion.common.i18n import N_, gettexter
25
26 T_ = gettexter()
27
28 __version__ = "$Rev: 7162 $"
29
30 BASIC_AUDIO_CAPS = "audio/x-raw-int;audio/x-raw-float"
31 BASIC_VIDEO_CAPS = "video/x-raw-yuv;video/x-raw-rgb"
32
33
34
35
36 GST_AUTOPLUG_SELECT_TRY = 0
37 GST_AUTOPLUG_SELECT_SKIP = 2
38
39
41
42 - def __init__(self, name, caps, linked=False):
45
46
48 __gstdetails__ = ('SyncKeeper', 'Generic',
49 'Retimestamp the output to be contiguous and maintain '
50 'the sync', 'Xavier Queralt')
51 _audiosink = gst.PadTemplate("audio-in",
52 gst.PAD_SINK,
53 gst.PAD_ALWAYS,
54 gst.caps_from_string(BASIC_AUDIO_CAPS))
55 _videosink = gst.PadTemplate("video-in",
56 gst.PAD_SINK,
57 gst.PAD_ALWAYS,
58 gst.caps_from_string(BASIC_VIDEO_CAPS))
59 _audiosrc = gst.PadTemplate("audio-out",
60 gst.PAD_SRC,
61 gst.PAD_ALWAYS,
62 gst.caps_from_string(BASIC_AUDIO_CAPS))
63 _videosrc = gst.PadTemplate("video-out",
64 gst.PAD_SRC,
65 gst.PAD_ALWAYS,
66 gst.caps_from_string(BASIC_VIDEO_CAPS))
67
69 gst.Element.__init__(self)
70
71
72 self.audiosrc = gst.Pad(self._audiosrc, "audio-out")
73 self.add_pad(self.audiosrc)
74 self.videosrc = gst.Pad(self._videosrc, "video-out")
75 self.add_pad(self.videosrc)
76
77
78 self.audiosink = gst.Pad(self._audiosink, "audio-in")
79 self.audiosink.set_chain_function(lambda pad, buffer:
80 self.chainfunc(pad, buffer, self.audiosrc))
81 self.audiosink.set_event_function(lambda pad, buffer:
82 self.eventfunc(pad, buffer, self.audiosrc))
83 self.add_pad(self.audiosink)
84 self.videosink = gst.Pad(self._videosink, "video-in")
85 self.videosink.set_chain_function(lambda pad, buffer:
86 self.chainfunc(pad, buffer, self.videosrc))
87 self.videosink.set_event_function(lambda pad, buffer:
88 self.eventfunc(pad, buffer, self.videosrc))
89 self.add_pad(self.videosink)
90
91
92 self._lock = threading.Lock()
93 self._totalTime = 0L
94 self._syncTimestamp = 0L
95 self._syncOffset = 0L
96 self._resetReceived = True
97 self._sendNewSegment = True
98
100 for pad in [self.videosrc, self.audiosrc]:
101 pad.push_event(
102 gst.event_new_new_segment(True, 1.0, gst.FORMAT_TIME,
103 self._syncTimestamp, -1, 0))
104 self._sendNewSegment = False
105
107
108
109 if not self._totalTime and not self._resetReceived:
110 return
111 self._syncTimestamp = self._totalTime
112 if position >= start:
113 self._syncOffset = start + (position - start)
114 else:
115 self._syncOffset = start
116 self._resetReceived = False
117 self.info("Update sync point to % r, offset to %r" %
118 (gst.TIME_ARGS(self._syncTimestamp),
119 (gst.TIME_ARGS(self._syncOffset))))
120
122 self.log("Input %s timestamp: %s, %s" %
123 (srcpad is self.audiosrc and 'audio' or 'video',
124 gst.TIME_ARGS(buf.timestamp),
125 gst.TIME_ARGS(buf.duration)))
126
127 if not self._sendNewSegment:
128 self._send_new_segment()
129
130 try:
131 self._lock.acquire()
132
133 if buf.timestamp < self._syncOffset:
134 self.warning("Could not clip buffer to segment")
135 return gst.FLOW_OK
136 if buf.timestamp == gst.CLOCK_TIME_NONE:
137 return gst.FLOW_OK
138
139 buf.timestamp -= self._syncOffset
140
141 buf.timestamp += self._syncTimestamp
142 duration = 0
143 if buf.duration != gst.CLOCK_TIME_NONE:
144 duration = buf.duration
145 self._totalTime = max(buf.timestamp + duration, self._totalTime)
146
147 self.log("Output %s timestamp: %s, %s" %
148 (srcpad is self.audiosrc and 'audio' or 'video',
149 gst.TIME_ARGS(buf.timestamp),
150 gst.TIME_ARGS(buf.duration)))
151 finally:
152 self._lock.release()
153
154 srcpad.push(buf)
155 return gst.FLOW_OK
156
174
175 gobject.type_register(SyncKeeper)
176 gst.element_register(SyncKeeper, "synckeeper", gst.RANK_MARGINAL)
177
178
180 """
181 Generic decoder component using decodebin2.
182
183 It listen to the custom gstreamer event flumotion-reset,
184 and reset the decoding pipeline by removing the old one
185 and creating a new one.
186
187 Sub-classes must override _get_feeders_info() and return
188 a list of FeederInfo instances that describe the decoder
189 output.
190
191 When reset, if the new decoded pads do not match the
192 previously negotiated caps, feeder will not be connected,
193 and the decoder will go sad.
194 """
195
196 logCategory = "gen-decoder"
197 feeder_tmpl = ("identity name=%(ename)s single-segment=true "
198 "silent=true ! %(caps)s ! @feeder:%(pad)s@")
199
200
201
204
224
231
232
233
235 return 'decodebin2 name=decoder'
236
238 """
239 Must be overridden to returns a tuple of FeederInfo.
240 """
241 return None
242
243
244
246 return "%s-output" % feed_name
247
248
249
256
257
280
281
283
284 logCategory = "avgen-decoder"
285 feeder_tmpl = ("identity name=%(ename)s silent=true ! %(caps)s ! "
286 "sync.%(pad)s-in sync.%(pad)s-out ! @feeder:%(pad)s@")
287
291
293 return 'decodebin2 name=decoder synckeeper name=sync'
294