Welcome back to "How to build a movie player". In this third howto, we'll
see how GStreamer should be integrated inside our application.
GStreamer is to multimedia like Gtk is to graphic interfaces. Everything is
a module that should be chained with other modules to be useful. Our
application will use a small bunch of all the available modules of GStreamer.
In particular, our working horse will be the playbin module
which is a full featured demuxer, decoder, subtitle displayer and much more,
all prepackaged for us.
Firs of all, we include a lot of Gtk, Gstreamer and X11 headers to import
library functions and also declare some global symbols in main.c
which will be visible to all other application files. So, outside everything
else, we write:
#include <X11/Xlib.h>
#include <gst/gst.h>
#include <gst/interfaces/xoverlay.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
GMainLoop *loop;
GstElement *playbin, *vsink;
The *loop element is a GLib provided event loop.
*playbin is our everything doing engine and *vsink is
our video output, which will be connected to GtkImage
"video_preview" widget to display video output. Then inside the
function we declare those elements.
loop = g_main_loop_new (NULL, FALSE);
The loop will handle all the events and will be started in the decoding
function.
playbin = gst_element_factory_make("playbin", "playbin");
vsink = gst_element_factory_make("ximagesink", "vsink");
g_object_set(G_OBJECT(playbin), "video-sink", vsink, NULL);
The playbin is extracted from the factory with the same name, while the
video sink vsink is a "ximagesink" module. The third line connects the playbin
output to video sink using an usual GObject statement on playbin
"video-sink" property.
Then we need some code to start audio/video decoding and playing. This is
the start_decode() function depicted here:
void
start_decode()
{
/* if paused just resume */
if (paused) {
paused = 0;
gst_element_set_state (playbin, GST_STATE_PLAYING);
return;
}
if (stream_uri == NULL) return;
/* tell playbin which URI has to play */
g_object_set(G_OBJECT(playbin), "uri", stream_uri, NULL);
/*
* getting the drawing area for video preview
* and setting video sink overlay
*/
GtkWidget *da = lookup_widget(minitheater, "video_preview");
gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(vsink),GDK_WINDOW_XID(da->window));
/* play */
g_timeout_add (500, (GSourceFunc) cb_print_position, playbin);
gst_element_set_state (playbin, GST_STATE_PLAYING);
g_main_loop_run (loop);
}
Not too complex but even not banal. Let's start. First of all, it checks if
the player is paused and should only be continued. paused is a
boolean integer which is defined outside start_decode() function
and is changed by Gtk callbacks.
stream_uri is the URI of the resource to be played. A URI? Why
not a plain filename? Because playbin is so cool that it's able even to play
directly from the net! So, to play a local resource like file
/home/myself/movie.avi we need to convert the path into
file:///home/myself/movie.avi. This function expects the URI to be
already formatted into stream_uri variable. URI formatting is done
inside the callback which Gtk calls when a file is chosen from the file chooser
dialog, as we'll see in a future post. To set the URI inside playbin we need
just a GObject call as in g_object_set(G_OBJECT(playbin), "uri",
stream_uri, NULL);
Then we need to connect the video sink to Gtk widget devoted to video
output. So we first gather the widget using Gtk lookup_widget()
function and then use a function from the X11 overlay interface of GStreamer.
The lines are:
GtkWidget *da = lookup_widget(minitheater, "video_preview");
gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(vsink),GDK_WINDOW_XID(da->window));
GDK_WINDOW_XID() is a GDK macro to extract the X11 window id of
a Gtk widget. GStreamer will later use this id to target video output to the
right area.
Then we connect a timeout function which every 500 milliseconds will update
the interface, using g_timeout_add() facility provided by GLib.
The function cb_print_position() will be shown in a later post.
And finally we start playing, with both GStreamer
gst_element_set_state() function, which put playbin pipeline in
GST_STATE_PLAYING state, and g_main_loop_run() which
starts GLib provided event loop.
That's all the setup we need. In next issue we'll see how this code gets
activated by Gtk callbacks in response to user action on the interface. Stay
tuned.