Merge remote-tracking branch 'origin/0.10'
[platform/upstream/gstreamer.git] / docs / random / wtay / plugin_guidelines
1
2 This short document gives some guidelines to plugin writers.
3
4
5 chain-based plugins
6 ===================
7
8  - the plugin has a chain function on each sink pad (can be the same function)
9
10
11 ! chain (GstPad *pad, GstBuffer *buffer) 
12 ! {
13 !   MyElement *elem = MY_ELEMENT (gst_pad_get_parent (pad));
14 !   GstBuffer *new_buffer;
15
16 !   ...
17 !   (process the buffer)
18 !   ...
19
20 !   /* you can push as many buffers (0 or more) as you want */
21 !   while (!done) {
22 !     ...
23 !     (process some more
24 !     ...
25 !     gst_pad_push (elem->some_sink_pad, new_buffer);
26 !     ...
27 !     /* if you're like going to send a large amount of buffers
28 !      * it's a good idea to call _yield from time to time after
29 !      * the buffer has been pushed */
30 !     (optional gst_element_yield (GST_ELEMENT (elem)); )
31 !   }
32 ! }
33
34
35  - chain based functions are usually easy and recommended.
36  - use gst_element_yield if you are going to push a lot of buffers.
37
38
39
40 loop-based plugins
41 ==================
42
43  - one loop function for the element.
44  - required for bytestream based plugins.
45
46 simple case
47 -----------
48   
49  - one pull at the start
50
51  - if you do something like this, you really should consider using a 
52    chain function as it can be significantly optimised by the scheduler.
53
54 !
55 ! loop (GstElement *element) 
56 ! {
57 !   MyElement *elem = MY_ELEMENT (element);
58 !   GstBuffer *buffer;
59 !   GstBuffer *new_buffer;
60
61 !   buffer = gst_pad_pull (elem->sinkpad);
62 !  
63 !   ...
64 !   (do something)
65 !   ...
66 !
67 !   /* you can push as many buffers (0 or more) as you want */
68 !   while (!done) {
69 !     ...
70 !     (process some more
71 !     ...
72 !     gst_pad_push (elem->some_sink_pad, new_buffer);
73 !     ...
74 !     /* if you're like going to send a large amount of buffers
75 !      * it's a good idea to call _yield from time to time after
76 !      * the buffer has been pushed */
77 !     (optional gst_element_yield (GST_ELEMENT (elem)); )
78 !   }
79 ! }
80 !
81
82 DONT!!
83
84 - infinite loop
85
86 !
87 ! loop (element)
88 ! {
89 !   while (TRUE) {
90 !     ...
91 !     _pull ()
92 !     ...
93 !
94 !     ...
95 !     _push ()
96 !     ...
97 !   }
98 ! }
99 !
100
101 * you can fix this by either:
102
103  - setting the GST_ELEMENT_INFINITE_LOOP flag on the element. this is
104    not recommended, if all plugins in the pipeline (or depending on the
105    pipeline, some plugins) have this flag, the pipeline will not run.
106  - calling break; from time to time to get out of the loop. (duh, then
107    it's not an infinite loop anymore). beware that the next time the loop
108    function is called, it will be started from the top.
109  - calling gst_element_yield() from time to time (see NOTES). 
110  - this is fine (albeit rather useless, use a chain function):
111   !
112   ! loop (element)
113   ! {
114   !   ...
115   !   _pull ()
116   !   ...
117   !
118   !   ...
119   !   _push ()
120   !   ...
121   ! }
122   !
123  - so is this (albeit rather useless, consider removing the while and the _yield):
124   !
125   ! loop (element)
126   ! {
127   !   while (TRUE) {
128   !     ...
129   !     _pull ()
130   !     ...
131   !
132   !     ...
133   !     _push ()
134   !     ...
135   !     gst_element_yield (element);
136   !   }
137   ! }
138   !
139
140
141 DONT!!
142
143 - allocate data, loop, free data
144
145 !
146 ! loop (element)
147 ! {
148 !   
149 !   (my funky malloc)
150 !
151 !   while (TRUE) {
152 !
153 !     ...
154 !     _pull ()
155 !     ...
156 !
157 !     ...
158 !     _push ()
159 !     ...
160 !
161 !     _yield ()
162 !   }
163 !
164 !   (my funky free)
165 !
166 ! }
167 !
168
169  - the free will NEVER happen!.
170
171 * You can fix this by:
172
173  - allocating/freeing data in the state change function
174  - you could think the following code would work too:
175   !
176   ! (*WRONG* example follows)
177   !
178   ! loop (element)
179   ! {
180   !   
181   !   (my funky malloc)
182   !
183   !   while (TRUE) {
184   !
185   !     ...
186   !     _pull ()
187   !     if (EOS)
188   !       break;
189   !
190   !     ...
191   !     _push ()
192   !     ...
193   !
194   !     _yield ()
195   !   }
196   !
197   !   (my funky free)
198   !
199   ! }
200   !
201    but it'll only free the data if the pipeline was shut down with
202    and EOS so don't try it. Besides, on EOS, a state change will happen
203    anyway so free the data there.
204
205
206 bytestream/multiple pull case
207 -----------------------------
208
209  - same as the simple case, but you can't use a chain based function unless
210    you want to make things a little harder then they should be.
211
212
213 complicated case
214 ----------------
215
216  - push and pull are completely mixed.
217  - the flow is usually something like this:
218
219 !
220 ! loop (element)
221 ! {
222 !   
223 !   while (TRUE) {
224 !
225 !     while (something) {
226 !       ...
227 !       do some _pull ()
228 !       ...
229 !       do some _push ()
230 !       ...
231 !       while (something_else) {
232 !         ...
233 !         if (testing)
234 !           do some _pull ()
235 !         ...
236 !         do some _push ()
237 !       }
238 !     }
239 !
240 !     while (something_useful) {
241 !       ...
242 !       _push ()
243 !       ...
244 !     }
245 !   }
246 ! }
247 !
248 (example made complicated on purpose, but vorbisdec comes close)
249
250  - you cannot call break to avoid infinite loops and there are loops that 
251    take a significant amount of time to execute, possibly pushing/pulling
252    a lot of buffers.
253
254 * You can fix this by:
255
256  - inserting gst_element_yield () in sane places, don't exagerate because
257    every yield can potentially never return so you need to keep track of
258    allocations (see the NOTES below).
259  
260
261
262 NOTES:
263 ======
264
265  - a call to _yield() can never return. if you have data allocated on the
266    stack before the yield, keep a pointer to it in the element struct 
267    and free it in the state change function.
268
269
270 IMPLEMENATION DETAILS
271 =====================
272
273 The infinite loops are only problematic if the scheduler chooses to select
274 the plugin as an entry point in the chain. _yield() will be a nop if this is
275 not the case. The scheduler will not select plugins with the INFINITE_LOOP
276 flag set as entries in a chain.
277
278 A _yield in an entry will hand over control to the main thread context, allowing
279 state changes and other actions to be performed. It will basically exit the 
280 _iterate() function. spending a long time in a loop will degrade app responsiveness
281 because _iterate will take a long time.
282
283 Calling yield, pulling, pushing can potentially never return because a state change
284 might have happened, killing off execution of the plugin. pulling/pushing buffers
285 will cause no leaks in this case because the core will free pending buffers in a
286 state change to READY. The plugin must free allocated data/buffers itself in the state
287 change function if the yield didn't retrun. 
288
289
290
291
292
293
294
295