2000-08-22 21:18:18 +00:00
|
|
|
<chapter id="cha-dynamic">
|
|
|
|
<title>Dynamic pipelines</title>
|
|
|
|
<para>
|
2001-01-06 16:52:44 +00:00
|
|
|
this chapter we will see how you can create a dynamic pipleine. A dynamic
|
|
|
|
pipeline is a pipeline that is updated or created while media is flowing
|
|
|
|
through it. We will create a partial pipeline first and add more elements
|
|
|
|
while the pipeline is playing. Dynamic pipelines cause all sorts of
|
|
|
|
scheduling issues and will remain a topic of research for a long time in
|
|
|
|
GStreamer.
|
|
|
|
</para>
|
|
|
|
<para>
|
2001-01-15 00:53:07 +00:00
|
|
|
We will show how to create an mpeg1 video player using dynamic pipelines.
|
|
|
|
As you have seen in the pad section, we can attach a signal to an element
|
|
|
|
when a pad is created. We will use this to create our MPEG1 player.
|
2000-08-22 21:18:18 +00:00
|
|
|
</para>
|
|
|
|
|
2001-01-16 23:35:22 +00:00
|
|
|
<para>
|
|
|
|
We'll start with a simple main function:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
#include <gnome.h>
|
|
|
|
#include <gst/gst.h>
|
|
|
|
|
|
|
|
void eof(GstElement *src) {
|
|
|
|
g_print("have eos, quitting\n");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
idle_func (gpointer data)
|
|
|
|
{
|
|
|
|
gst_bin_iterate (GST_BIN (data));
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
GstElement *pipeline, *src, *parse;
|
|
|
|
|
|
|
|
gst_init (&argc, &argv);
|
|
|
|
gnome_init ("MPEG1 Video player","0.0.1", argc, argv);
|
|
|
|
|
|
|
|
pipeline = gst_pipeline_new ("pipeline");
|
|
|
|
g_return_val_if_fail (pipeline != NULL, -1);
|
|
|
|
|
|
|
|
src = gst_elementfactory_make ("disksrc", "src");
|
|
|
|
g_return_val_if_fail (src != NULL, -1);
|
2001-07-07 11:34:54 +00:00
|
|
|
g_object_set (G_OBJECT (src), "location", argv[1], NULL);
|
2001-01-16 23:35:22 +00:00
|
|
|
|
|
|
|
parse = gst_elementfactory_make ("mpeg1parse", "parse");
|
|
|
|
g_return_val_if_fail (parse != NULL, -1);
|
|
|
|
|
|
|
|
gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (src));
|
|
|
|
gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (parse));
|
|
|
|
|
2001-08-13 19:00:13 +00:00
|
|
|
g_signal_connect (G_OBJECT (parse), "new_pad",
|
|
|
|
G_CALLBACK (new_pad_created), pipeline);
|
2001-01-16 23:35:22 +00:00
|
|
|
|
2001-08-13 19:00:13 +00:00
|
|
|
g_signal_connect (G_OBJECT (src), "eos",
|
|
|
|
G_CALLBACK (eof), NULL);
|
2001-01-16 23:35:22 +00:00
|
|
|
|
|
|
|
gst_pad_connect (gst_element_get_pad (src, "src"),
|
|
|
|
gst_element_get_pad (parse, "sink"));
|
|
|
|
|
|
|
|
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
|
|
|
|
|
|
|
|
g_idle_add (idle_func, pipeline);
|
|
|
|
|
|
|
|
gdk_threads_enter ();
|
|
|
|
gst_main ();
|
|
|
|
gdk_threads_leave ();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
We create two elements: a disksrc (the element that will read the
|
|
|
|
file from disk) and an mpeg1parser. We also add an EOS (End Of Stream)
|
|
|
|
signal to the disksrc so that we will be notified when the file has ended.
|
|
|
|
There's nothing special about this piece of code except for the signal
|
|
|
|
'new_pad' that we connected to the mpeg1parser using:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
2001-08-13 19:00:13 +00:00
|
|
|
g_signal_connect (G_OBJECT (parse), "new_pad",
|
|
|
|
G_CALLBACK (new_pad_created), pipeline);
|
2001-01-16 23:35:22 +00:00
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
When an elementary stream has been detected in the system stream,
|
|
|
|
mpeg1parse will create a new pad that will provide the data of the
|
|
|
|
elementary stream. A function 'new_pad_created' will be called when
|
|
|
|
the pad is created:
|
|
|
|
</para>
|
|
|
|
<programlisting>
|
|
|
|
void
|
|
|
|
new_pad_created (GstElement *parse, GstPad *pad, GstElement *pipeline)
|
|
|
|
{
|
|
|
|
GstElement *parse_audio, *parse_video, *decode, *decode_video, *play, *videoscale, *show;
|
|
|
|
GstElement *audio_queue, *video_queue;
|
|
|
|
GstElement *audio_thread, *video_thread;
|
|
|
|
|
|
|
|
GtkWidget *appwindow;
|
|
|
|
|
2001-02-16 16:48:54 +00:00
|
|
|
g_print ("***** a new pad %s was created\n", gst_pad_get_name (pad));
|
2001-01-16 23:35:22 +00:00
|
|
|
|
|
|
|
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED);
|
|
|
|
|
|
|
|
// connect to audio pad
|
|
|
|
if (strncmp (gst_pad_get_name (pad), "audio_", 6) == 0) {
|
|
|
|
|
|
|
|
// construct internal pipeline elements
|
|
|
|
parse_audio = gst_elementfactory_make ("mp3parse", "parse_audio");
|
|
|
|
g_return_if_fail (parse_audio != NULL);
|
|
|
|
decode = gst_elementfactory_make ("mpg123", "decode_audio");
|
|
|
|
g_return_if_fail (decode != NULL);
|
|
|
|
play = gst_elementfactory_make ("audiosink", "play_audio");
|
|
|
|
g_return_if_fail (play != NULL);
|
|
|
|
|
|
|
|
// create the thread and pack stuff into it
|
|
|
|
audio_thread = gst_thread_new ("audio_thread");
|
|
|
|
g_return_if_fail (audio_thread != NULL);
|
|
|
|
gst_bin_add (GST_BIN (audio_thread), GST_ELEMENT (parse_audio));
|
|
|
|
gst_bin_add (GST_BIN (audio_thread), GST_ELEMENT (decode));
|
|
|
|
gst_bin_add (GST_BIN (audio_thread), GST_ELEMENT (play));
|
|
|
|
|
|
|
|
// set up pad connections
|
|
|
|
gst_element_add_ghost_pad (GST_ELEMENT (audio_thread),
|
|
|
|
gst_element_get_pad (parse_audio, "sink"));
|
|
|
|
gst_pad_connect (gst_element_get_pad (parse_audio,"src"),
|
|
|
|
gst_element_get_pad (decode,"sink"));
|
|
|
|
gst_pad_connect (gst_element_get_pad (decode,"src"),
|
|
|
|
gst_element_get_pad (play,"sink"));
|
|
|
|
|
|
|
|
// construct queue and connect everything in the main pipelie
|
|
|
|
audio_queue = gst_elementfactory_make ("queue", "audio_queue");
|
|
|
|
|
|
|
|
gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (audio_queue));
|
|
|
|
gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (audio_thread));
|
|
|
|
|
|
|
|
gst_pad_connect (pad,
|
|
|
|
gst_element_get_pad (audio_queue, "sink"));
|
|
|
|
gst_pad_connect (gst_element_get_pad (audio_queue, "src"),
|
|
|
|
gst_element_get_pad (audio_thread, "sink"));
|
|
|
|
|
|
|
|
// set up thread state and kick things off
|
|
|
|
g_print ("setting to READY state\n");
|
|
|
|
gst_element_set_state (GST_ELEMENT (audio_thread), GST_STATE_READY);
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (strncmp (gst_pad_get_name (pad), "video_", 6) == 0) {
|
|
|
|
|
|
|
|
// construct internal pipeline elements
|
|
|
|
parse_video = gst_elementfactory_make ("mp1videoparse", "parse_video");
|
|
|
|
g_return_if_fail (parse_video != NULL);
|
|
|
|
decode_video = gst_elementfactory_make ("mpeg_play", "decode_video");
|
|
|
|
g_return_if_fail (decode_video != NULL);
|
|
|
|
|
|
|
|
show = gst_elementfactory_make ("videosink", "show");
|
|
|
|
g_return_if_fail (show != NULL);
|
|
|
|
|
|
|
|
appwindow = gnome_app_new ("MPEG1 player", "MPEG1 player");
|
|
|
|
gnome_app_set_contents (GNOME_APP (appwindow),
|
|
|
|
gst_util_get_widget_arg (GTK_OBJECT (show), "widget"));
|
|
|
|
gtk_widget_show_all (appwindow);
|
|
|
|
|
|
|
|
// create the thread and pack stuff into it
|
|
|
|
video_thread = gst_thread_new ("video_thread");
|
|
|
|
g_return_if_fail (video_thread != NULL);
|
|
|
|
gst_bin_add (GST_BIN (video_thread), GST_ELEMENT (parse_video));
|
|
|
|
gst_bin_add (GST_BIN (video_thread), GST_ELEMENT (decode_video));
|
|
|
|
gst_bin_add (GST_BIN (video_thread), GST_ELEMENT (show));
|
|
|
|
|
|
|
|
// set up pad connections
|
|
|
|
gst_element_add_ghost_pad (GST_ELEMENT (video_thread),
|
|
|
|
gst_element_get_pad (parse_video, "sink"));
|
|
|
|
gst_pad_connect (gst_element_get_pad (parse_video, "src"),
|
|
|
|
gst_element_get_pad (decode_video, "sink"));
|
|
|
|
gst_pad_connect (gst_element_get_pad (decode_video, "src"),
|
|
|
|
gst_element_get_pad (show, "sink"));
|
|
|
|
|
|
|
|
// construct queue and connect everything in the main pipeline
|
|
|
|
video_queue = gst_elementfactory_make ("queue", "video_queue");
|
|
|
|
|
|
|
|
gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (video_queue));
|
|
|
|
gst_bin_add (GST_BIN (pipeline), GST_ELEMENT (video_thread));
|
|
|
|
|
|
|
|
gst_pad_connect (pad,
|
|
|
|
gst_element_get_pad (video_queue, "sink"));
|
|
|
|
gst_pad_connect (gst_element_get_pad (video_queue, "src"),
|
|
|
|
gst_element_get_pad (video_thread, "sink"));
|
|
|
|
|
|
|
|
// set up thread state and kick things off
|
2001-07-07 11:34:54 +00:00
|
|
|
g_object_set (G_OBJECT (video_thread), "create_thread", TRUE, NULL);
|
2001-01-16 23:35:22 +00:00
|
|
|
g_print ("setting to READY state\n");
|
|
|
|
gst_element_set_state (GST_ELEMENT (video_thread), GST_STATE_READY);
|
|
|
|
}
|
|
|
|
g_print("\n");
|
|
|
|
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
|
|
|
|
}
|
|
|
|
</programlisting>
|
|
|
|
<para>
|
|
|
|
In the above example, we created new elements based on the name of
|
|
|
|
the newly created pad. We added them to a new thread There are other possibilities to check the
|
|
|
|
type of the pad, for example, by using the MIME type and the properties
|
|
|
|
of the pad.
|
|
|
|
</para>
|
2000-08-22 21:18:18 +00:00
|
|
|
</chapter>
|