GNOME Bugzilla – Bug 87882
state change in top-level thread not passed down to child bin
Last modified: 2004-12-22 21:47:04 UTC
The following are two files to demonstrate that a bin (containing a filesrc, mad decoder, and volume filter) inside a thread which connects the bin to an osssink, does not stop when the bin is passed GST_STATE_PAUSED. Essentially, it seems the thread's state override's the child's state. Debugging output (and further gdb checking) verifies that the bin is receiving the correct state-change information, but the loop cycle does not seem to recognize this. This example uses gtk for a simple gui --------------------- file with main(...) ------------------------- #include <gtk/gtk.h> #include "mixer.h" #ifndef EXIT_FAILURE #define EXIT_FAILURE -1 #endif gint delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) { gtk_main_quit(); return FALSE; } static void play_src(GtkWidget *widget, gpointer data) { start_channel (); } static void pause_src(GtkWidget *widget, gpointer data) { pause_channel (); } static void quit(void) { quit_everything(); gtk_main_quit(); } int main( int argc, char *argv[]) { GtkWidget *window, *mainvbox; GtkWidget *control_buttons_hbox; GtkWidget *play_button, *pause_button; GtkWidget *quit_button; if (argc == 1) { g_print ("usage: %s <filename1> \n", argv[0]); exit(EXIT_FAILURE); } gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "Tester"); /* When the window is given the "delete_event" signal (this is given * by the window manager, usually by the "close" option, or on the * titlebar), we ask it to call the delete_event () function * as defined above. The data passed to the callback * function is NULL and is ignored in the callback function. */ g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), NULL); gtk_container_set_border_width (GTK_CONTAINER (window), 5); /* main vbox that will hold main hbox (needed so that quit button appears * below everything) */ mainvbox = gtk_vbox_new (FALSE, 5); gtk_container_add (GTK_CONTAINER (window), mainvbox); play_button = gtk_button_new_with_label("play"); g_signal_connect (G_OBJECT (play_button), "clicked", G_CALLBACK (play_src), NULL); pause_button = gtk_button_new_with_label("pause"); g_signal_connect (G_OBJECT (pause_button), "clicked", G_CALLBACK (pause_src), NULL); /* create container for control buttons */ control_buttons_hbox = gtk_hbox_new (TRUE, 5); gtk_box_pack_start (GTK_BOX (mainvbox), control_buttons_hbox, FALSE, FALSE, 5); gtk_container_add (GTK_CONTAINER (control_buttons_hbox), play_button); gtk_container_add (GTK_CONTAINER (control_buttons_hbox), pause_button); gtk_widget_show (play_button); gtk_widget_show (pause_button); gtk_widget_show (control_buttons_hbox); /* create quit button */ quit_button = gtk_button_new_with_label ("quit"); g_signal_connect (G_OBJECT (quit_button), "clicked", G_CALLBACK (quit), NULL); gtk_box_pack_start (GTK_BOX(mainvbox), quit_button, FALSE, FALSE, 5); gtk_widget_show (quit_button); gtk_widget_show (mainvbox); gtk_window_set_default_size (GTK_WINDOW (window), 100, 100); gtk_widget_show (window); init_mixer (argc, argv); gtk_main (); return 0; } --------------------- mixer.c: where elements are created and stored ----- #include <gst/gst.h> static GstElement *main_thread; static GstElement *channel_bin; static GstElement *audiosink; static GstElement *filesrc, *mad_decoder, *volume; init_mixer (int argc, char* argv[]) { GstPad *temp_pad; /* for connecting to adder */ gst_init (&argc, &argv); gst_control_init (&argc, &argv); /* create main thread to process everything. * this thread will hold a bin and an osssink. * the bin will hold a filesrc, mad, and volume. * a ghost pad will connect the volume's output to * the osssink. */ main_thread = gst_thread_new ("mixer_thread"); g_assert (main_thread != NULL); /* create bin to hold filesrc, volume, and mad */ channel_bin = gst_bin_new ("channel_bin"); g_assert (channel_bin != NULL); /* create a filesrc reader (goes into bin) */ filesrc = gst_element_factory_make ("filesrc", "filesrc"); g_assert (filesrc != NULL); g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); /* create a decoder */ mad_decoder = gst_element_factory_make ("mad", "mpg123_decoder"); g_assert (mad_decoder != NULL); /* create a volume controller */ volume = gst_element_factory_make ("volume", "volume"); g_assert (volume != NULL); /* add main part of audio processing to the bin */ gst_bin_add_many (GST_BIN (channel_bin), filesrc, mad_decoder, volume, NULL); /* actually connect the elements */ gst_element_connect_many (filesrc, mad_decoder, volume, NULL); /* add ghost pad to connect bin to osssink which is held * in mixer_thread */ gst_element_add_ghost_pad (channel_bin, gst_element_get_pad (volume, "src"), "channel_bin_output"); /* create our audio sink for final output */ audiosink = gst_element_factory_make ("osssink", "play_audio"); g_assert (audiosink != NULL); temp_pad = gst_element_get_static_pad (audiosink, "sink"); g_assert (temp_pad != NULL); /* try to connect the ghost pad from the channel bin to the * audiosink intake pad */ if (!gst_pad_connect (gst_element_get_pad (channel_bin, "channel_bin_output"), temp_pad)) { g_print ("error connecting pads!\n"); } gst_bin_add (GST_BIN (main_thread), GST_ELEMENT (channel_bin)); gst_bin_add (GST_BIN (main_thread), GST_ELEMENT (audiosink)); /* we turn on all pipelines/bins/threads */ gst_element_set_state (main_thread, GST_STATE_PLAYING); gst_element_set_state (channel_bin, GST_STATE_PLAYING); /* print out the schedule? */ gst_scheduler_show (GST_ELEMENT_SCHED (main_thread)); } void start_channel (void) { g_print ("starting audio\n"); gst_element_set_state (GST_ELEMENT (channel_bin), GST_STATE_PLAYING); } void pause_channel (void) { g_print ("pausing audio\n"); gst_element_set_state (GST_ELEMENT (channel_bin), GST_STATE_PAUSED); } void quit_everything(void) { gst_object_destroy (GST_OBJECT (main_thread)); gst_main_quit(); } ------------- mixer.h, as needed by gainctl.c ------------- #ifndef MIXER_HEADER_ #define MIXER_HEADER_ void start_channel (void); void pause_channel (void); #endif
Created attachment 9813 [details] main(....) part of test program
Created attachment 9814 [details] mixer -- where pipelines are created
doing state changes across threads is not yet supported, you could however change the channel_bin state in the post/pre iterate callback. You also have to make sure, the sinkpad of audiosink is made inactive with gst_pad_set_active (pad, FALSE) so that osssink doesn't pull anymore buffers from the disabled pipeline. This cannot be improved without making a clear design doc about how we do out of thread pipeline manipulation first.