6cda060d6c94a26922d83657c47bb801792326c9
[platform/upstream/gstreamer.git] / docs / design / part-element-sink.txt
1 Sink elements
2 -------------
3
4 Sink elements consume data. They normally have no source pads.
5
6 Typical sink elements include:
7
8  - audio/video renderers
9  - network sinks
10  - filesinks
11
12 Sinks are harder to construct than other element types as they are 
13 treated specially by the GStreamer core.
14
15 state changes
16 ~~~~~~~~~~~~~
17
18 A sink always returns ASYNC from the state change to PAUSED, this 
19 includes a state change from READY->PAUSED and PLAYING->PAUSED. The
20 reason for this is that this way we can detect when the first buffer
21 or event arrives in the sink when the state change completes.
22
23 A sink should block on the first EOS event or buffer received in the
24 READY->PAUSED state before commiting the state to PAUSED.
25
26 FLUSHING events have to be handled out of sync with the buffer flow
27 and take no part in the preroll procedure.
28
29 Events other than EOS do not complete the preroll stage.
30
31 sink overview
32 ~~~~~~~~~~~~~
33
34  - TODO: PREROLL_LOCK can be removed and we can safely use the STREAM_LOCK.
35
36
37
38   # commit the state. We return TRUE if we can continue
39   # streaming, FALSE in the case we go to a READY or NULL state.
40   # if we go to PLAYING, we don't need to block on preroll.
41   commit
42   {
43     LOCK
44     switch (pending)
45       case PLAYING:
46         need_preroll = FALSE
47         break
48       case PAUSED:
49         break
50       case READY:
51       case NULL:
52         return FALSE
53       case VOID:
54         return TRUE
55
56     # update state
57     state = pending
58     next = VOID
59     pending = VOID
60     UNLOCK
61     return TRUE
62   }
63
64   # sync an object. We have to wait for the element to reach
65   # the PLAYING state before we can wait on the clock.
66   # some items do not need synchronisation (most events) so the
67   # get_times method returns FALSE (not syncable)
68   # need_preroll indicates that we are not in the PLAYING state
69   # and therefore need to commit and potentially block on preroll
70   # if our clock_wait got interrupted we commit and block again.
71   # The reason for this is that the current item being rendered is
72   # not yet finished and we can use that item to finish preroll.
73   do_sync (obj)
74   {
75     # get timing information for this object
76     syncable = get_times (obj, &start, &stop)
77     if (!syncable)
78       return OK;
79  again:
80     while (need_preroll)
81       if (need_commit)
82         need_commit = FALSE
83         if (!commit)
84           return FLUSHING
85
86       if (need_preroll)
87         # release PREROLL_LOCK and wait. prerolled can be observed
88         # and will be TRUE
89         prerolled = TRUE
90         PREROLL_WAIT (releasing PREROLL_LOCK)
91         prerolled = FALSE
92         if (flushing)
93           return FLUSHING
94
95     if (valid (start || stop))
96       PREROLL_UNLOCK
97       end_time = stop
98       ret = wait_clock (obj,start)
99       PREROLL_LOCK
100       if (flushing)
101         return FLUSHING 
102       # if the clock was unscheduled, we redo the
103       # preroll
104       if (ret == UNSCHEDULED)
105         goto again          
106   }
107
108   # render a prerollable item (EOS or buffer). It is
109   # always called with the PREROLL_LOCK helt.
110   render_object (obj)
111   {
112     ret = do_sync (obj)
113     if (ret != OK)
114       return ret;
115
116     # preroll and syncing done, now we can render
117     render(obj)
118   }
119                                    | # sinks that sync on buffer contents do like this
120                                    | while (more_to_render)
121                                    |   ret = render 
122                                    |   if (ret == interrupted)
123                                    |     prerolled = TRUE
124     render (buffer)          ----->|     PREROLL_WAIT (releasing PREROLL_LOCK)
125                                    |     prerolled = FALSE
126                                    |     if (flushing)
127                                    |       return FLUSHING
128                                    | 
129
130   # queue a prerollable item (EOS or buffer). It is
131   # always called with the PREROLL_LOCK helt.
132   # This function will commit the state when receiving the
133   # first prerollable item.
134   # items are then added to the rendering queue or rendered
135   # right away if no preroll is needed.
136   queue (obj, prerollable) 
137   {
138     if (need_preroll)
139       if (prerollable)
140         queuelen++
141
142       # first item in the queue while we need preroll
143       # will complete state change and call preroll
144       if (queuelen == 1)
145         preroll (obj)
146         if (need_commit)
147           need_commit = FALSE
148           if (!commit)
149             return FLUSHING
150
151       # then see if we need more preroll items before we
152       # can block
153       if (need_preroll)
154         if (queuelen <= maxqueue)
155           queue.add (obj)
156           return OK
157
158     # now clear the queue and render each item before
159     # rendering the current item.
160     while (queue.hasItem)
161       render_object (queue.remove())
162
163     render_object (obj)
164     queuelen = 0
165   }
166
167   # various event functions
168   event
169     EOS:
170       # events must complete preroll too
171       STREAM_LOCK
172       PREROLL_LOCK
173       if (flushing)
174         return FALSE
175       ret = queue (event, TRUE)
176       if (ret == FLUSHING)
177         return FALSE
178       PREROLL_UNLOCK
179       STREAM_UNLOCK
180       break
181     NEWSEGMENT:
182       # the newsegment must be used to clip incoming
183       # buffers. Then then go into the queue as non-prerollable
184       # items used for syncing the buffers
185       STREAM_LOCK
186       PREROLL_LOCK
187       if (flushing)
188         return FALSE
189       set_clip
190       ret = queue (event, FALSE)
191       if (ret == FLUSHING)
192         return FALSE
193       PREROLL_UNLOCK
194       STREAM_UNLOCK
195       break
196     FLUSH_START:
197       # set flushing and unblock all that is waiting
198       event                   ----> subclasses can interrupt render
199       PREROLL_LOCK
200       flushing = TRUE
201       unlock_clock
202       PREROLL_SIGNAL
203       PREROLL_UNLOCK
204       STREAM_LOCK
205       lost_state
206       STREAM_UNLOCK
207       break
208     FLUSH_END:
209       # unset flushing and clear all data and eos
210       STREAM_LOCK
211       event 
212       PREROLL_LOCK
213       queue.clear
214       queuelen = 0
215       flushing = FALSE
216       eos = FALSE
217       PREROLL_UNLOCK
218       STREAM_UNLOCK
219       break
220
221   # the chain function checks the buffer falls within the
222   # configured segment and queues the buffer for preroll and
223   # rendering
224   chain
225     STREAM_LOCK
226     PREROLL_LOCK
227     if (flushing)
228       return FLUSHING
229     if (clip)
230       queue (buffer, TRUE)
231     PREROLL_UNLOCK
232     STREAM_UNLOCK
233
234   state
235     switch (transition)
236       READY_PAUSED:
237         # no datapassing is going on so we always return ASYNC
238         ret = ASYNC
239         need_commit = TRUE
240         eos = FALSE
241         flushing = FALSE
242         need_preroll = TRUE
243         prerolled = FALSE
244         break
245       PAUSED_PLAYING:
246         # we grab the preroll lock. This we can only do if the
247         # chain function is either doing some clock sync, we are
248         # waiting for preroll or the chain function is not being called.
249         PREROLL_LOCK
250         if (prerolled || eos)
251           ret = OK
252           need_commit = FALSE
253           need_preroll = FALSE
254           if (eos)
255             post_eos
256           else
257             PREROLL_SIGNAL
258         else
259           need_preroll = TRUE
260           need_commit = TRUE
261           ret = ASYNC
262         PREROLL_UNLOCK
263         break
264       PLAYING_PAUSED:
265                            ---> subclass can interrupt render
266         # we grab the preroll lock. This we can only do if the
267         # chain function is either doing some clock sync
268         # or the chain function is not being called. 
269         PREROLL_LOCK
270         need_preroll = TRUE
271         unlock_clock
272         if (prerolled || eos)
273           ret = OK
274         else
275           ret = ASYNC
276         PREROLL_UNLOCK
277         break
278       PAUSED_READY:
279                            ---> subclass can interrupt render
280         # we grab the preroll lock. Set to flushing and unlock
281         # everything. This should exit the chain functions and stop
282         # streaming.
283         PREROLL_LOCK
284         flushing = TRUE
285         unlock_clock
286         queue.clear
287         queuelen = 0
288         PREROLL_SIGNAL
289         ret = OK
290         PREROLL_UNLOCK
291         break
292