29
GStreamer Multimedia Framework Part 5

GStreamer - Part 5

Embed Size (px)

DESCRIPTION

GStreamer Multimedia Framework Part 5 Part 5 An Example Application Code Part 1 Introduction to GStreamer Part 2 GStreamer Plugin Internals Part 4 Concepts of GStreamer Application Description 2  gst_is_initialized (void){  return gst_initialized;  }  Use this function to check if GStreamer has been initialized.  static gboolean gst_initialized = FALSE;  This variable will be set to TRUE if the gst is initialized properly. 3

Citation preview

Page 1: GStreamer - Part 5

GStreamer Multimedia Framework

Part 5

Page 2: GStreamer - Part 5

2

Contents

Description

Part 1 Introduction to GStreamer

Part 2 GStreamer Plugin Internals

Part 3 Advanced GStreamer Concepts

Part 4 Concepts of GStreamer Application

Part 5 An Example Application Code

Page 3: GStreamer - Part 5

3

An Example Application Code

static gboolean gst_initialized = FALSE; This variable will be set to TRUE if the gst is initialized properly.

gst_init (int *argc, char **argv[]) Initializes the GStreamer library, setting up internal path lists, registering built-in elements, and loading standard plugins. the registry will be loaded. By default this will also check if the registry cache needs to be updated and rescan all plugins if needed. This function should be called before calling any other GLib functions.

gst_is_initialized (void){ return gst_initialized; } Use this function to check if GStreamer has been initialized.

GstElement * gst_pipeline_new (const gchar * name){ return gst_element_factory_make ("pipeline", name); } Create a new pipeline with the given name. gst_element_factory_make uses gst_element_factory_find() and gst_element_factory_create() functions internally to create

the gst_element of the given type.

Page 4: GStreamer - Part 5

4

An Example Application Code

now it's time to get the decoder decoder = gst_element_factory_make ("mad", "decode"); if (!decoder) { g_print ("could not find plugin \"mad\""); return -1; }

also, we need to add some converters to make sure the audio stream from the decoder is converted into a format the audio sink can understand (if necessary)

conv = gst_element_factory_make ("audioconvert", "audioconvert"); if (!conv) { g_print ("could not create \"audioconvert\" element!"); return -1; } resample = gst_element_factory_make ("audioresample", "audioresample"); if (!resample) { g_print ("could not create \"audioresample\" element!"); return -1; }

Page 5: GStreamer - Part 5

5

An Example Application Code

add an audio sink audiosink = gst_element_factory_make ("alsasink", "play_audio"); g_assert (audiosink);

add objects to the main pipeline gst_bin_add_many (GST_BIN (bin), filesrc, decoder, conv, resample, audiosink, NULL);

Adds a NULL-terminated list of elements to a bin. This function is equivalent to calling gst_bin_add() for each member of the list. The return value of each gst_bin_add() is ignored.

void gst_bin_add_many (GstBin * bin, GstElement * element_1, ...) { va_list args;

g_return_if_fail (GST_IS_BIN (bin)); g_return_if_fail (GST_IS_ELEMENT (element_1));

va_start (args, element_1);

Page 6: GStreamer - Part 5

6

An Example Application Code

while (element_1) { gst_bin_add (bin, element_1);

element_1 = va_arg (args, GstElement *); }

va_end (args); }

gboolean gst_bin_add (GstBin * bin, GstElement * element) { GstBinClass *bclass; gboolean result;

g_return_val_if_fail (GST_IS_BIN (bin), FALSE); g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);

bclass = GST_BIN_GET_CLASS (bin);

Page 7: GStreamer - Part 5

7

An Example Application Code

if (G_UNLIKELY (bclass->add_element == NULL)) goto no_function;

GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element %s to bin %s", GST_STR_NULL (GST_ELEMENT_NAME (element)), GST_STR_NULL (GST_ELEMENT_NAME (bin)));

result = bclass->add_element (bin, element);

return result;

ERROR handling no_function: { g_warning ("adding elements to bin %s is not supported", GST_ELEMENT_NAME (bin)); return FALSE; } }

Adds the given element to the bin. Sets the element's parent, and thus takes ownership of the element. An element can only be added to one bin.

Page 8: GStreamer - Part 5

8

An Example Application Code

If the element's pads are linked to other pads, the pads will be unlinked before the element is added to the bin.

void gst_bin_remove_many (GstBin * bin, GstElement * element_1, ...) { va_list args;

g_return_if_fail (GST_IS_BIN (bin)); g_return_if_fail (GST_IS_ELEMENT (element_1));

va_start (args, element_1);

while (element_1) { gst_bin_remove (bin, element_1);

element_1 = va_arg (args, GstElement *); }

va_end (args); }

Page 9: GStreamer - Part 5

9

An Example Application Code

Chain together a series of elements. Uses gst_element_link(). Make sure you have added your elements to a bin or pipeline with gst_bin_add() before trying to link them.

link the elements gst_element_link_many (filesrc, decoder, conv, resample, audiosink, NULL);

gboolean gst_element_link_many (GstElement * element_1, GstElement * element_2, ...) { gboolean res = TRUE; va_list args;

g_return_val_if_fail (GST_IS_ELEMENT (element_1), FALSE); g_return_val_if_fail (GST_IS_ELEMENT (element_2), FALSE);

va_start (args, element_2);

while (element_2) { if (!gst_element_link (element_1, element_2)) { res = FALSE; break; }

element_1 = element_2; element_2 = va_arg (args, GstElement *); }

Page 10: GStreamer - Part 5

10

An Example Application Code

va_end (args);

return res; }

Make sure you have added your elements to a bin or pipeline with gst_bin_add() before trying to link them. gboolean gst_element_link (GstElement * src, GstElement * dest) { return gst_element_link_pads (src, NULL, dest, NULL); }

gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, GstElement * dest, const gchar * destpadname, GstPadLinkCheck flags) { const GList *srcpads, *destpads, *srctempls, *desttempls, *l; GstPad *srcpad, *destpad; GstPadTemplate *srctempl, *desttempl; GstElementClass *srcclass, *destclass;

checks g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE); g_return_val_if_fail (GST_IS_ELEMENT (dest), FALSE);

Page 11: GStreamer - Part 5

11

An Example Application Code

GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "trying to link element %s:%s to element %s:%s", GST_ELEMENT_NAME (src), srcpadname ? srcpadname : "(any)", GST_ELEMENT_NAME (dest), destpadname ? destpadname : "(any)");

get a src pad if (srcpadname) { name specified, look it up if (!(srcpad = gst_element_get_static_pad (src, srcpadname))) srcpad = gst_element_get_request_pad (src, srcpadname); if (!srcpad) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", GST_ELEMENT_NAME (src), srcpadname); return FALSE; } else { if (!(GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC)) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no src pad", GST_DEBUG_PAD_NAME (srcpad)); gst_object_unref (srcpad); return FALSE; } if (GST_PAD_PEER (srcpad) != NULL) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked", GST_DEBUG_PAD_NAME (srcpad)); gst_object_unref (srcpad); return FALSE; } } srcpads = NULL; } else {

Page 12: GStreamer - Part 5

12

An Example Application Code

no name given, get the first available pad GST_OBJECT_LOCK (src); srcpads = GST_ELEMENT_PADS (src); srcpad = srcpads ? GST_PAD_CAST (srcpads->data) : NULL; if (srcpad) gst_object_ref (srcpad); GST_OBJECT_UNLOCK (src); }

get a destination pad if (destpadname) { name specified, look it up if (!(destpad = gst_element_get_static_pad (dest, destpadname))) destpad = gst_element_get_request_pad (dest, destpadname); if (!destpad) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", GST_ELEMENT_NAME (dest), destpadname); return FALSE; } else { if (!(GST_PAD_DIRECTION (destpad) == GST_PAD_SINK)) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no sink pad", GST_DEBUG_PAD_NAME (destpad)); gst_object_unref (destpad); return FALSE; } if (GST_PAD_PEER (destpad) != NULL) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked", GST_DEBUG_PAD_NAME (destpad)); gst_object_unref (destpad); return FALSE;

Page 13: GStreamer - Part 5

13

An Example Application Code

} } destpads = NULL; } else { no name given, get the first available pad GST_OBJECT_LOCK (dest); destpads = GST_ELEMENT_PADS (dest); destpad = destpads ? GST_PAD_CAST (destpads->data) : NULL; if (destpad) gst_object_ref (destpad); GST_OBJECT_UNLOCK (dest); }

if (srcpad) { loop through the allowed pads in the source, trying to find a compatible destination pad GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "looping through allowed src and dest pads"); do { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying src pad %s:%s", GST_DEBUG_PAD_NAME (srcpad)); if ((GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC) && (GST_PAD_PEER (srcpad) == NULL)) { GstPad *temp;

if (destpadname) { temp = destpad; gst_object_ref (temp);

Page 14: GStreamer - Part 5

14

An Example Application Code

} else { temp = gst_element_get_compatible_pad (dest, srcpad, NULL); }

if (temp && pad_link_maybe_ghosting (srcpad, temp, flags)) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (temp)); if (destpad) gst_object_unref (destpad); gst_object_unref (srcpad); gst_object_unref (temp); return TRUE; }

if (temp) { gst_object_unref (temp); } } if (srcpads) { srcpads = g_list_next (srcpads); if (srcpads) { gst_object_unref (srcpad); srcpad = GST_PAD_CAST (srcpads->data); gst_object_ref (srcpad); } } } while (srcpads); }

Page 15: GStreamer - Part 5

15

An Example Application Code

if (srcpadname) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s:%s to %s", GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (dest)); if (destpad) gst_object_unref (destpad); destpad = NULL; } if (srcpad) gst_object_unref (srcpad); srcpad = NULL;

gst_element_get_compatible_pad function implementation srcclass = GST_ELEMENT_GET_CLASS (src); destclass = GST_ELEMENT_GET_CLASS (dest);

GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, srctempls = gst_element_class_get_pad_template_list (srcclass); desttempls = gst_element_class_get_pad_template_list (destclass);

if (srctempls && desttempls) { while (srctempls) { srctempl = (GstPadTemplate *) srctempls->data; if (srctempl->presence == GST_PAD_REQUEST) { for (l = desttempls; l; l = l->next) { desttempl = (GstPadTemplate *) l->data; if (desttempl->presence == GST_PAD_REQUEST && desttempl->direction != srctempl->direction) { if (gst_caps_is_always_compatible (gst_pad_template_get_caps

Page 16: GStreamer - Part 5

16

An Example Application Code

(srctempl), gst_pad_template_get_caps (desttempl))) { srcpad = gst_element_get_request_pad (src, srctempl->name_template); destpad = gst_element_get_request_pad (dest, desttempl->name_template); if (srcpad && destpad && pad_link_maybe_ghosting (srcpad, destpad, flags)) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (destpad)); gst_object_unref (srcpad); gst_object_unref (destpad); return TRUE; } it failed, so we release the request pads if (srcpad) gst_element_release_request_pad (src, srcpad); if (destpad) gst_element_release_request_pad (dest, destpad); } } } } srctempls = srctempls->next; } }

return TRUE; }

Page 17: GStreamer - Part 5

17

An Example Application Code

start playing gst_element_set_state (bin, GST_STATE_PLAYING);

Sets the state of the element. This function will try to set the requested state by going through all the intermediary states and calling the class's state change function for each. This function can return #GST_STATE_CHANGE_ASYNC, in which case the element will perform the remainder of the state change asynchronously in another thread. An application can use gst_element_get_state() to wait for the completion of the state change or it can wait for a state change message on the bus.

GstStateChangeReturn gst_element_set_state (GstElement * element, GstState state) { GstElementClass *oclass; GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;

g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);

Page 18: GStreamer - Part 5

18

An Example Application Code

oclass = GST_ELEMENT_GET_CLASS (element);

if (oclass->set_state) result = (oclass->set_state) (element, state);

return result; }

Run event loop listening for bus messages until EOS or ERROR event_loop (bin);

A Model software loop to explain the functionality of a loop static void event_loop (GstElement * pipe) { GstBus *bus; GstMessage *message = NULL; gboolean running = TRUE;

bus = gst_element_get_bus (GST_ELEMENT (pipe));

Page 19: GStreamer - Part 5

19

An Example Application Code

while (running) { message = gst_bus_poll (bus, GST_MESSAGE_ANY, -1);

g_assert (message != NULL);

switch (message->type) { case GST_MESSAGE_EOS: running = FALSE; break; case GST_MESSAGE_WARNING:{ GError *gerror; gchar *debug;

gst_message_parse_warning (message, &gerror, &debug); gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); g_error_free (gerror); g_free (debug); break; } case GST_MESSAGE_ERROR:{ GError *gerror;

Page 20: GStreamer - Part 5

20

An Example Application Code

gchar *debug;

gst_message_parse_error (message, &gerror, &debug); gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); g_error_free (gerror); g_free (debug); running = FALSE; break; }

default: break; } gst_message_unref (message); } gst_object_unref (bus); }

stop the bin gst_element_set_state (bin, GST_STATE_NULL);

exit(0);

Page 21: GStreamer - Part 5

21

An Example Application Code

GMainLoop *loop;

loop = g_main_loop_new (NULL, FALSE);

Create gstreamer elements pipeline = gst_pipeline_new ("audio-player"); source = gst_element_factory_make ("filesrc", "file-source"); demuxer = gst_element_factory_make ("oggdemux", "ogg-demuxer"); decoder = gst_element_factory_make ("vorbisdec", "vorbis-decoder"); conv = gst_element_factory_make ("audioconvert", "converter"); sink = gst_element_factory_make ("autoaudiosink", "audio-output");

if (!pipeline || !source || !demuxer || !decoder || !conv || !sink) { g_printerr ("One element could not be created. Exiting.\n"); return -1; }

Set up the pipeline

we set the input filename to the source element g_object_set (G_OBJECT (source), "location", argv[1], NULL);

we add a message handler

bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));

GstBus * gst_pipeline_get_bus (GstPipeline * pipeline)

Page 22: GStreamer - Part 5

22

An Example Application Code

{ return gst_element_get_bus (GST_ELEMENT_CAST (pipeline)); }

GstBus * gst_element_get_bus (GstElement * element) { GstBus *result = NULL;

g_return_val_if_fail (GST_IS_ELEMENT (element), result);

GST_OBJECT_LOCK (element); if ((result = GST_ELEMENT_BUS (element))) gst_object_ref (result); GST_OBJECT_UNLOCK (element);

GST_CAT_DEBUG_OBJECT (GST_CAT_BUS, element, "got bus %" GST_PTR_FORMAT, result);

return result; }

gst_bus_add_watch (bus, bus_call, loop);

guint gst_bus_add_watch (GstBus * bus, GstBusFunc func, gpointer user_data) { return gst_bus_add_watch_full (bus, G_PRIORITY_DEFAULT, func,

Page 23: GStreamer - Part 5

23

An Example Application Code

user_data, NULL); }

guint gst_bus_add_watch_full (GstBus * bus, gint priority, GstBusFunc func, gpointer user_data, GDestroyNotify notify) { guint id;

g_return_val_if_fail (GST_IS_BUS (bus), 0);

GST_OBJECT_LOCK (bus); id = gst_bus_add_watch_full_unlocked (bus, priority, func, user_data, notify); GST_OBJECT_UNLOCK (bus);

return id; }

must be called with the bus OBJECT LOCK static guint gst_bus_add_watch_full_unlocked (GstBus * bus, gint priority, GstBusFunc func, gpointer user_data, GDestroyNotify notify) { guint id; GSource *source;

if (bus->priv->watch_id) { GST_ERROR_OBJECT (bus,

Page 24: GStreamer - Part 5

24

An Example Application Code

"Tried to add new watch while one was already there"); return 0; }

source = gst_bus_create_watch (bus);

if (priority != G_PRIORITY_DEFAULT) g_source_set_priority (source, priority);

g_source_set_callback (source, (GSourceFunc) func, user_data, notify);

Attach the source (bus watch) to the main context and returns a bus watch id id = g_source_attach (source, NULL); g_source_unref (source);

GST_DEBUG_OBJECT (bus, "New source %p with id %u", source, id); return id; }

GSource for the bus typedef struct { GSource source; GstBus *bus; gboolean inited; } GstBusSource;

Page 25: GStreamer - Part 5

25

An Example Application Code

GSource * gst_bus_create_watch (GstBus * bus) { GstBusSource *source;

g_return_val_if_fail (GST_IS_BUS (bus), NULL);

source = (GstBusSource *) g_source_new (&gst_bus_source_funcs, sizeof (GstBusSource)); source->bus = gst_object_ref (bus); source->inited = FALSE;

return (GSource *) source; }

we add all elements into the pipeline file-source | ogg-demuxer | vorbis-decoder | converter | alsa-output gst_bin_add_many (GST_BIN (pipeline), source, demuxer, decoder, conv, sink, NULL);

we link the elements together file-source -> ogg-demuxer ~> vorbis-decoder -> converter -> alsa-output gst_element_link (source, demuxer); gst_element_link_many (decoder, conv, sink, NULL);

note that the demuxer will be linked to the decoder dynamically. The reason is that Ogg may contain various streams (for example audio and video). The source pad(s) will be created at run time,

Page 26: GStreamer - Part 5

26

An Example Application Code

by the demuxer when it detects the amount and nature of streams. Therefore we connect a callback function which will be executed when the "pad-added" is emitted.

g_signal_connect (demuxer, "pad-added", G_CALLBACK (on_pad_added), decoder);

Set the pipeline to "playing" state gst_element_set_state (pipeline, GST_STATE_PLAYING);

Iterate g_main_loop_run (loop);

Out of the main loop, clean up nicely gst_element_set_state (pipeline, GST_STATE_NULL);

gst_object_unref (GST_OBJECT (pipeline));

return 0;

Page 27: GStreamer - Part 5

27

An Example Application Code

static void on_pad_added (GstElement *element, GstPad *pad, gpointer data) { GstPad *sinkpad; GstElement *decoder = (GstElement *) data;

We can now link this pad with the vorbis-decoder sink pad g_print ("Dynamic pad created, linking demuxer/decoder\n");

sinkpad = gst_element_get_static_pad (decoder, "sink");

gst_pad_link (pad, sinkpad);

gst_object_unref (sinkpad); }

static gboolean bus_call (GstBus *bus, GstMessage *msg, gpointer data) { GMainLoop *loop = (GMainLoop *) data;

Page 28: GStreamer - Part 5

28

An Example Application Code

switch (GST_MESSAGE_TYPE (msg)) {

case GST_MESSAGE_EOS: g_print ("End of stream\n"); g_main_loop_quit (loop); break;

case GST_MESSAGE_ERROR: { gchar *debug; GError *error;

gst_message_parse_error (msg, &error, &debug); g_free (debug);

g_printerr ("Error: %s\n", error->message); g_error_free (error);

g_main_loop_quit (loop); break; } default: break; }

return TRUE; }

Page 29: GStreamer - Part 5

Thank You