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
137 buf.timestamp -= self._syncOffset
138
139 buf.timestamp += self._syncTimestamp
140 duration = 0
141 if buf.duration != gst.CLOCK_TIME_NONE:
142 duration = buf.duration
143 self._totalTime = max(buf.timestamp + duration, self._totalTime)
144
145 self.log("Output %s timestamp: %s, %s" %
146 (srcpad is self.audiosrc and 'audio' or 'video',
147 gst.TIME_ARGS(buf.timestamp),
148 gst.TIME_ARGS(buf.duration)))
149 finally:
150 self._lock.release()
151
152 srcpad.push(buf)
153 return gst.FLOW_OK
154
172
173 gobject.type_register(SyncKeeper)
174 gst.element_register(SyncKeeper, "synckeeper", gst.RANK_MARGINAL)
175
176
178 """
179 Generic decoder component using decodebin2.
180
181 It listen to the custom gstreamer event flumotion-reset,
182 and reset the decoding pipeline by removing the old one
183 and creating a new one.
184
185 Sub-classes must override _get_feeders_info() and return
186 a list of FeederInfo instances that describe the decoder
187 output.
188
189 When reset, if the new decoded pads do not match the
190 previously negotiated caps, feeder will not be connected,
191 and the decoder will go sad.
192 """
193
194 logCategory = "gen-decoder"
195 feeder_tmpl = ("identity name=%(ename)s single-segment=true "
196 "silent=true ! %(caps)s ! @feeder:%(pad)s@")
197
198
199
202
222
229
230
231
233 return 'decodebin2 name=decoder'
234
236 """
237 Must be overridden to returns a tuple of FeederInfo.
238 """
239 return None
240
241
242
244 return "%s-output" % feed_name
245
246
247
254
255
278
279
281
282 logCategory = "avgen-decoder"
283 feeder_tmpl = ("identity name=%(ename)s silent=true ! %(caps)s ! "
284 "sync.%(pad)s-in sync.%(pad)s-out ! @feeder:%(pad)s@")
285
289
291 return 'decodebin2 name=decoder synckeeper name=sync'
292