More eos handling, bin in bin is handled correctly now.
[platform/upstream/gstreamer.git] / gst / gstthread.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstthread.c: Threaded container object
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <unistd.h>
24
25 //#define GST_DEBUG_ENABLED
26 #include "gst_private.h"
27
28 #include "gstthread.h"
29
30
31 GstElementDetails gst_thread_details = {
32   "Threaded container",
33   "Bin",
34   "Container that creates/manages a thread",
35   VERSION,
36   "Erik Walthinsen <omega@cse.ogi.edu>",
37   "(C) 1999, 2000",
38 };
39
40
41 /* Thread signals and args */
42 enum {
43   /* FILL ME */
44   LAST_SIGNAL
45 };
46
47 enum {
48   ARG_0,
49   ARG_CREATE_THREAD,
50 };
51
52
53 static void                     gst_thread_class_init           (GstThreadClass *klass);
54 static void                     gst_thread_init                 (GstThread *thread);
55
56 static void                     gst_thread_set_arg              (GtkObject *object,GtkArg *arg,guint id);
57 static void                     gst_thread_get_arg              (GtkObject *object,GtkArg *arg,guint id);
58
59 static GstElementStateReturn    gst_thread_change_state         (GstElement *element);
60
61 static xmlNodePtr               gst_thread_save_thyself         (GstElement *element,xmlNodePtr parent);
62 static void                     gst_thread_restore_thyself      (GstElement *element,xmlNodePtr parent,
63                                                                  GHashTable *elements);
64
65 static void                     gst_thread_signal_thread        (GstThread *thread);
66 static void                     gst_thread_wait_thread          (GstThread *thread);
67 static void                     gst_thread_create_plan_dummy    (GstBin *bin);
68 static void                     gst_thread_schedule_dummy       (GstBin *bin);
69
70 static void*                    gst_thread_main_loop            (void *arg);
71
72 static GstBinClass *parent_class = NULL;
73 //static guint gst_thread_signals[LAST_SIGNAL] = { 0 };
74
75 GtkType
76 gst_thread_get_type(void) {
77   static GtkType thread_type = 0;
78
79   if (!thread_type) {
80     static const GtkTypeInfo thread_info = {
81       "GstThread",
82       sizeof(GstThread),
83       sizeof(GstThreadClass),
84       (GtkClassInitFunc)gst_thread_class_init,
85       (GtkObjectInitFunc)gst_thread_init,
86       (GtkArgSetFunc)NULL,
87       (GtkArgGetFunc)NULL,
88       (GtkClassInitFunc)NULL,
89     };
90     thread_type = gtk_type_unique(GST_TYPE_BIN,&thread_info);
91   }
92   return thread_type;
93 }
94
95 static void
96 gst_thread_class_init (GstThreadClass *klass)
97 {
98   GtkObjectClass *gtkobject_class;
99   GstObjectClass *gstobject_class;
100   GstElementClass *gstelement_class;
101   GstBinClass *gstbin_class;
102
103   gtkobject_class =     (GtkObjectClass*)klass;
104   gstobject_class =     (GstObjectClass*)klass;
105   gstelement_class =    (GstElementClass*)klass;
106   gstbin_class =        (GstBinClass*)klass;
107
108   parent_class = gtk_type_class (GST_TYPE_BIN);
109
110   gtk_object_add_arg_type ("GstThread::create_thread", GTK_TYPE_BOOL,
111                            GTK_ARG_READWRITE, ARG_CREATE_THREAD);
112
113   gstelement_class->change_state =      gst_thread_change_state;
114   gstelement_class->save_thyself =      gst_thread_save_thyself;
115   gstelement_class->restore_thyself =   gst_thread_restore_thyself;
116
117   //gstbin_class->create_plan = gst_thread_create_plan_dummy;
118   gstbin_class->schedule = gst_thread_schedule_dummy;
119
120   gtkobject_class->set_arg = gst_thread_set_arg;
121   gtkobject_class->get_arg = gst_thread_get_arg;
122
123 }
124
125 static void
126 gst_thread_init (GstThread *thread)
127 {
128   GST_DEBUG (0,"initializing thread '%s'\n",gst_element_get_name(GST_ELEMENT(thread)));
129
130   // we're a manager by default
131   GST_FLAG_SET (thread, GST_BIN_FLAG_MANAGER);
132
133   // default is to create a thread
134   GST_FLAG_SET (thread, GST_THREAD_CREATE);
135   GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING);
136
137   thread->lock = g_mutex_new();
138   thread->cond = g_cond_new();
139 }
140
141 static void
142 gst_thread_schedule_dummy (GstBin *bin)
143 {
144   g_return_if_fail (GST_IS_THREAD (bin));
145
146   if (!GST_FLAG_IS_SET (GST_THREAD (bin), GST_THREAD_STATE_SPINNING))
147     GST_INFO (GST_CAT_THREAD,"gstthread: scheduling delayed until thread starts");
148 }
149
150 static void
151 gst_thread_create_plan_dummy (GstBin *bin)
152 {
153   g_return_if_fail (GST_IS_THREAD (bin));
154
155   if (!GST_FLAG_IS_SET (GST_THREAD (bin), GST_THREAD_STATE_SPINNING))
156     GST_INFO (GST_CAT_THREAD,"gstthread: create plan delayed until thread starts");
157 }
158
159 static void
160 gst_thread_set_arg (GtkObject *object,
161                     GtkArg *arg,
162                     guint id)
163 {
164   /* it's not null if we got it, but it might not be ours */
165   g_return_if_fail (GST_IS_THREAD (object));
166
167   switch(id) {
168     case ARG_CREATE_THREAD:
169       if (GTK_VALUE_BOOL (*arg)) {
170         GST_INFO (GST_CAT_THREAD,"gstthread: turning ON the creation of the thread");
171         GST_FLAG_SET (object, GST_THREAD_CREATE);
172         GST_DEBUG (0,"gstthread: flags are 0x%08x\n", GST_FLAGS (object));
173       } else {
174         GST_INFO (GST_CAT_THREAD,"gstthread: turning OFF the creation of the thread");
175         GST_FLAG_UNSET (object, GST_THREAD_CREATE);
176         GST_DEBUG (0,"gstthread: flags are 0x%08x\n", GST_FLAGS (object));
177       }
178       break;
179     default:
180       break;
181   }
182 }
183
184 static void
185 gst_thread_get_arg (GtkObject *object,
186                     GtkArg *arg,
187                     guint id)
188 {
189   /* it's not null if we got it, but it might not be ours */
190   g_return_if_fail (GST_IS_THREAD (object));
191
192   switch (id) {
193     case ARG_CREATE_THREAD:
194       GTK_VALUE_BOOL (*arg) = GST_FLAG_IS_SET (object, GST_THREAD_CREATE);
195       break;
196     default:
197       break;
198   }
199 }
200
201
202 /**
203  * gst_thread_new:
204  * @name: the name of the thread
205  *
206  * Create a new thread with the given name.
207  *
208  * Returns: The new thread
209  */
210 GstElement*
211 gst_thread_new (guchar *name)
212 {
213   return gst_elementfactory_make ("thread", name);
214 }
215
216
217
218 static GstElementStateReturn
219 gst_thread_change_state (GstElement *element)
220 {
221   GstThread *thread;
222   gboolean stateset = GST_STATE_SUCCESS;
223   gint pending, transition;
224
225   g_return_val_if_fail (GST_IS_THREAD(element), FALSE);
226   GST_DEBUG_ENTER("(\"%s\")",gst_element_get_name(element));
227
228   thread = GST_THREAD (element);
229
230   GST_INFO (GST_CAT_THREAD,"gstthread: thread \"%s\" change state %d",
231                gst_element_get_name (GST_ELEMENT (element)),
232                GST_STATE_PENDING (element));
233
234   pending = GST_STATE_PENDING (element);
235   transition = GST_STATE_TRANSITION (element);
236
237 //  if (pending == GST_STATE (element)) return GST_STATE_SUCCESS;
238
239   GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
240
241   if (GST_ELEMENT_CLASS (parent_class)->change_state)
242     stateset = GST_ELEMENT_CLASS (parent_class)->change_state (element);
243
244   GST_INFO (GST_CAT_THREAD, "gstthread: stateset %d %d %d %02x", GST_STATE (element), stateset,
245                   GST_STATE_PENDING (element), GST_STATE_TRANSITION (element));
246
247   switch (transition) {
248     case GST_STATE_NULL_TO_READY:
249 //      if (!stateset) return FALSE;
250       // we want to prepare our internal state for doing the iterations
251       GST_INFO (GST_CAT_THREAD, "gstthread: preparing thread \"%s\" for iterations:",
252                gst_element_get_name (GST_ELEMENT (element)));
253
254       // set the state to idle
255       GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
256       // create the thread if that's what we're supposed to do
257       GST_INFO (GST_CAT_THREAD, "gstthread: flags are 0x%08x", GST_FLAGS (thread));
258
259       if (GST_FLAG_IS_SET (thread, GST_THREAD_CREATE)) {
260         GST_INFO (GST_CAT_THREAD, "gstthread: starting thread \"%s\"",
261                  gst_element_get_name (GST_ELEMENT (element)));
262
263         // create the thread
264         pthread_create (&thread->thread_id, NULL,
265                         gst_thread_main_loop, thread);
266
267         // wait for it to 'spin up'
268 //        gst_thread_wait_thread (thread);
269       } else {
270         GST_INFO (GST_CAT_THREAD, "gstthread: NOT starting thread \"%s\"",
271                 gst_element_get_name (GST_ELEMENT (element)));
272       }
273       break;
274     case GST_STATE_PAUSED_TO_PLAYING:
275     case GST_STATE_READY_TO_PLAYING:
276       if (!stateset) return FALSE;
277       GST_INFO (GST_CAT_THREAD, "gstthread: starting thread \"%s\"",
278               gst_element_get_name (GST_ELEMENT (element)));
279
280       GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING);
281       gst_thread_signal_thread (thread);
282       break;
283     case GST_STATE_PLAYING_TO_PAUSED:
284       GST_INFO (GST_CAT_THREAD,"gstthread: pausing thread \"%s\"",
285               gst_element_get_name (GST_ELEMENT (element)));
286
287       //GST_FLAG_UNSET(thread,GST_THREAD_STATE_SPINNING);
288       gst_thread_signal_thread (thread);
289       break;
290     case GST_STATE_READY_TO_NULL:
291       GST_INFO (GST_CAT_THREAD,"gstthread: stopping thread \"%s\"",
292               gst_element_get_name (GST_ELEMENT (element)));
293
294       GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING);
295       gst_thread_signal_thread (thread);
296       break;
297     default:
298       break;
299   }
300
301   return stateset;
302 }
303
304 /**
305  * gst_thread_main_loop:
306  * @arg: the thread to start
307  *
308  * The main loop of the thread. The thread will iterate
309  * while the state is GST_THREAD_STATE_SPINNING
310  */
311 static void *
312 gst_thread_main_loop (void *arg)
313 {
314   GstThread *thread = GST_THREAD (arg);
315
316   GST_INFO (GST_CAT_THREAD,"gstthread: thread \"%s\" is running with PID %d",
317                   gst_element_get_name (GST_ELEMENT (thread)), getpid ());
318
319   // construct the plan and signal back
320   if (GST_BIN_CLASS (parent_class)->schedule)
321     GST_BIN_CLASS (parent_class)->schedule (GST_BIN (thread));
322
323   gst_thread_signal_thread (thread);
324
325   while (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING)) {
326     if (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) {
327       if (!gst_bin_iterate (GST_BIN (thread))) {
328         GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING);
329       }
330     }
331     else {
332       GST_DEBUG (0, "thread \"%s\" waiting\n", gst_element_get_name (GST_ELEMENT (thread)));
333       gst_thread_wait_thread (thread);
334     }
335   }
336
337   GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING);
338   //pthread_join (thread->thread_id, 0);
339
340   GST_INFO (GST_CAT_THREAD, "gstthread: thread \"%s\" is stopped",
341                   gst_element_get_name (GST_ELEMENT (thread)));
342   return NULL;
343 }
344
345 static void
346 gst_thread_signal_thread (GstThread *thread)
347 {
348   GST_DEBUG (0,"signaling thread\n");
349   g_mutex_lock (thread->lock);
350   g_cond_signal (thread->cond);
351   g_mutex_unlock (thread->lock);
352 }
353
354 static void
355 gst_thread_wait_thread (GstThread *thread)
356 {
357   GST_DEBUG (0,"waiting for thread\n");
358   g_mutex_lock (thread->lock);
359   g_cond_wait (thread->cond, thread->lock);
360   g_mutex_unlock (thread->lock);
361 }
362
363
364 static void
365 gst_thread_restore_thyself (GstElement *element,
366                             xmlNodePtr parent,
367                             GHashTable *elements)
368 {
369   GST_DEBUG (0,"gstthread: restore\n");
370
371   if (GST_ELEMENT_CLASS (parent_class)->restore_thyself)
372     GST_ELEMENT_CLASS (parent_class)->restore_thyself (element,parent, elements);
373 }
374
375 static xmlNodePtr
376 gst_thread_save_thyself (GstElement *element,
377                          xmlNodePtr parent)
378 {
379   if (GST_ELEMENT_CLASS (parent_class)->save_thyself)
380     GST_ELEMENT_CLASS (parent_class)->save_thyself (element,parent);
381   return NULL;
382 }