diff options
Diffstat (limited to 'src/gstplayer.cpp')
-rw-r--r-- | src/gstplayer.cpp | 1027 |
1 files changed, 1027 insertions, 0 deletions
diff --git a/src/gstplayer.cpp b/src/gstplayer.cpp new file mode 100644 index 0000000..092a8b2 --- /dev/null +++ b/src/gstplayer.cpp @@ -0,0 +1,1027 @@ +/* This file is part of the KMPlayer application + Copyright (C) 2004 Koos Vriezen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <config.h> +#include <dcopclient.h> +#include <qcstring.h> +#include <qtimer.h> +#include <qfile.h> +#include <qurl.h> +#include <qthread.h> +#include <qmutex.h> +#include <qdom.h> +#include "kmplayer_backend.h" +#include "kmplayer_callback_stub.h" +#include "kmplayer_callback.h" +#include "gstplayer.h" +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <gst/gst.h> +#include <gst/interfaces/xoverlay.h> +#include <gst/interfaces/colorbalance.h> + +static char configfile[2048]; + +static Display *display; +static KGStreamerPlayer *gstapp; +static KMPlayer::Callback_stub * callback; +static Window wid; +static QMutex mutex (true); +static bool window_created = true; +static bool wants_config; +static bool verbose; +static bool notified_playing; +static int running; +static int movie_width; +static int movie_height; +static int movie_length; +static int repeat_count; +static int screen; +static const int event_finished = QEvent::User; +static const int event_playing = QEvent::User + 1; +static const int event_size = QEvent::User + 2; +static const int event_eos = QEvent::User + 3; +static const int event_progress = QEvent::User + 4; +static const int event_error = QEvent::User + 5; +static const int event_video = QEvent::User + 6; +static QString mrl; +static QString sub_mrl; +static const char *ao_driver; +static const char *vo_driver; +static const char *playbin_name = "player"; +static const char *dvd_device; +static const char *vcd_device; +static GstElement *gst_elm_play; +static GstBus *gst_bus; +static unsigned int /*GstMessageType*/ ignore_messages_mask; +static GstXOverlay *xoverlay; +static GstColorBalance *color_balance; +static gulong gst_bus_sync; +static gulong gst_bus_async; +static QString elmentry ("entry"); +static QString elmitem ("item"); +static QString attname ("NAME"); +static QString atttype ("TYPE"); +static QString attdefault ("DEFAULT"); +static QString attvalue ("VALUE"); +static QString attstart ("START"); +static QString attend ("END"); +static QString valrange ("range"); +static QString valnum ("num"); +static QString valbool ("bool"); +static QString valenum ("enum"); +static QString valstring ("string"); + +extern "C" { + // nothing yet +} // extern "C" + + +static bool gstPollForStateChange (GstElement *, GstState, gint64=GST_SECOND/2); + +static void +cb_error (GstElement * play, + GstElement * /*src*/, + GError *err, + const char *debug, + gpointer /*data*/) +{ + fprintf (stderr, "cb_error: %s %s\n", err->message, debug); + if (GST_STATE (play) == GST_STATE_PLAYING) + gst_element_set_state (play, GST_STATE_READY); + QApplication::postEvent (gstapp, new QEvent ((QEvent::Type)event_finished)); +} + +// NULL -> READY -> PAUSED -> PLAYING + +static void +gstCapsSet (GstPad *pad, + GParamSpec * /*pspec*/, + gpointer /*data*/) +{ + GstCaps *caps = gst_pad_get_negotiated_caps (pad); + if (!caps) + return; + QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_video)); + const GstStructure * s = gst_caps_get_structure (caps, 0); + if (s) { + const GValue *par; + + gst_structure_get_int (s, "width", &movie_width); + gst_structure_get_int (s, "height", &movie_height); + if ((par = gst_structure_get_value (s, "pixel-aspect-ratio"))) { + int num = gst_value_get_fraction_numerator (par), + den = gst_value_get_fraction_denominator (par); + + if (num > den) + movie_width = (int) ((float) num * movie_width / den); + else + movie_height = (int) ((float) den * movie_height / num); + } + QApplication::postEvent (gstapp, new GstSizeEvent (movie_length, movie_width, movie_height)); + } + gst_caps_unref (caps); +} + +static void gstStreamInfo (GObject *, GParamSpec *, gpointer /*data*/) { + GstPad *videopad = 0L; + GList *streaminfo = 0L; + + fprintf (stderr, "gstStreamInfo\n"); + g_object_get (gst_elm_play, "stream-info", &streaminfo, NULL); + streaminfo = g_list_copy (streaminfo); + g_list_foreach (streaminfo, (GFunc) g_object_ref, NULL); + for ( ; streaminfo != NULL; streaminfo = streaminfo->next) { + GObject *info = G_OBJECT (streaminfo->data); + gint type; + GParamSpec *pspec; + GEnumValue *val; + + if (!info) + continue; + g_object_get (info, "type", &type, NULL); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS(info), "type"); + val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type); + + if (!g_strcasecmp (val->value_nick, "video")) + if (!videopad) { + g_object_get (info, "object", &videopad, NULL); + gstCapsSet (GST_PAD (videopad), 0L, 0L); + g_signal_connect (videopad, "notify::caps", G_CALLBACK (gstCapsSet), 0L); + } + } + + GstMessage * msg = gst_message_new_application (GST_OBJECT (gst_elm_play), + gst_structure_new ("notify-streaminfo", NULL)); + gst_element_post_message (gst_elm_play, msg); + g_list_foreach (streaminfo, (GFunc) g_object_unref, NULL); + g_list_free (streaminfo); +} + +static void gstSource (GObject *, GParamSpec *, gpointer /*data*/) { + GObject *source = 0L; + fprintf (stderr, "gstSource\n"); + g_object_get (gst_elm_play, "source", &source, NULL); + if (!source) + return; + GObjectClass *klass = G_OBJECT_GET_CLASS (source); + if (mrl.startsWith ("dvd://") && dvd_device) { + if (g_object_class_find_property (klass, "device")) + g_object_set (source, "device", dvd_device, NULL); + } else if (mrl.startsWith ("vcd://") && vcd_device) { + if (g_object_class_find_property (klass, "device")) + g_object_set (source, "device", vcd_device, NULL); + } + g_object_unref (source); +} + +static void gstGetDuration () { + GstFormat fmt = GST_FORMAT_TIME; + gint64 len = -1; // usec + if (gst_element_query_duration (gst_elm_play, &fmt, &len)) + if (movie_length != len / (GST_MSECOND * 100)) { + movie_length = len / (GST_MSECOND * 100); + fprintf (stderr, "new length %d\n", movie_length); + QApplication::postEvent (gstapp, new GstSizeEvent (movie_length, movie_width, movie_height)); + } +} + +static void gstTag (const GstTagList *, const gchar *tag, gpointer) { + fprintf (stderr, "Tag: %s\n", tag); +} + +//static bool gstStructure (GQuark field_id, const GValue *value, gpointer user_data); + +static void gstBusMessage (GstBus *, GstMessage * message, gpointer) { + GstMessageType msg_type = GST_MESSAGE_TYPE (message); + /* somebody else is handling the message, probably in gstPolForStateChange*/ + if (ignore_messages_mask & msg_type) + return; + switch (msg_type) { + case GST_MESSAGE_ERROR: + fprintf (stderr, "error msg\n"); + QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_error)); + if (gst_elm_play) { + gst_element_set_state (gst_elm_play, GST_STATE_NULL); + //gstPollForStateChange (gst_elm_play, GST_STATE_NULL); + } + break; + case GST_MESSAGE_WARNING: + fprintf (stderr, "warning msg\n"); + break; + case GST_MESSAGE_TAG: { + GstTagList *tag_list; + //fprintf (stderr, "tag msg\n"); + gst_message_parse_tag (message, &tag_list); + gst_tag_list_foreach (tag_list, gstTag, 0L); + gst_tag_list_free (tag_list); + break; + } + case GST_MESSAGE_EOS: + fprintf (stderr, "eos msg\n"); + gst_element_set_state (gst_elm_play, GST_STATE_READY); + break; + case GST_MESSAGE_BUFFERING: { + gint percent = 0; + gst_structure_get_int (message->structure, "buffer-percent", &percent); + QApplication::postEvent (gstapp, new GstProgressEvent (percent)); + //fprintf (stderr, "Buffering message (%u%%)\n", percent); + break; + } + case GST_MESSAGE_APPLICATION: { + const char * msg = gst_structure_get_name (message->structure); + fprintf (stderr, "app msg %s\n", msg ? msg : "<unknown>"); + //gst_structure_foreach (message->structure, gstStructure, 0L); + break; + } + case GST_MESSAGE_STATE_CHANGED: { + GstState old_state, new_state; + //gchar *src_name = gst_object_get_name (message->src); + gst_message_parse_state_changed(message, &old_state, &new_state,0L); + //fprintf (stderr, "%s changed state from %s to %s\n", src_name, gst_element_state_get_name (old_state), gst_element_state_get_name (new_state)); + if (GST_IS_ELEMENT (message->src) && + GST_ELEMENT (message->src) == gst_elm_play) { + if (old_state == GST_STATE_PAUSED && + new_state >= GST_STATE_PLAYING) { + gstGetDuration (); + QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_playing)); + } else if (old_state >= GST_STATE_PAUSED && + new_state <= GST_STATE_READY) { + if (repeat_count-- > 0 && + (gst_element_set_state(gst_elm_play, GST_STATE_PAUSED), + gstPollForStateChange(gst_elm_play, GST_STATE_PAUSED))) + gst_element_set_state(gst_elm_play, GST_STATE_PLAYING); + else + QApplication::postEvent (gstapp, + new QEvent ((QEvent::Type) event_finished)); + } + } + //g_free (src_name); + break; + } + case GST_MESSAGE_DURATION: + gstGetDuration (); + break; + case GST_MESSAGE_CLOCK_PROVIDE: + case GST_MESSAGE_CLOCK_LOST: + case GST_MESSAGE_NEW_CLOCK: + case GST_MESSAGE_STATE_DIRTY: + break; + default: + fprintf (stderr, "Unhandled msg %s (0x%x)\n", + gst_message_type_get_name (msg_type), msg_type); + break; + } +} + +static void gstMessageElement (GstBus *, GstMessage *msg, gpointer /*data*/) { + if (gst_structure_has_name (msg->structure, "prepare-xwindow-id")) { + fprintf (stderr, "prepare-xwindow-id\n"); + if (xoverlay) + gst_x_overlay_set_xwindow_id (xoverlay, wid); + } +} + +static bool gstPollForStateChange (GstElement *element, GstState state, gint64 timeout) { + /*GstMessageType*/ unsigned int events, saved_events; + GstBus *bus = gst_element_get_bus (element); + GError **error = 0L; + + events = GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS; + saved_events = ignore_messages_mask; + + if (element && element == gst_elm_play) { + /* we do want the main handler to process state changed messages for + * playbin as well, otherwise it won't hook up the timeout etc. */ + ignore_messages_mask |= (events ^ GST_MESSAGE_STATE_CHANGED); + } else { + ignore_messages_mask |= events; + } + + while (true) { + GstMessage *message; + GstElement *src; + + message = gst_bus_poll (bus, (GstMessageType) events, timeout); + if (!message) + goto timed_out; + + src = (GstElement*)GST_MESSAGE_SRC (message); + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_STATE_CHANGED: { + GstState olds, news, pending; + if (src == element) { + gst_message_parse_state_changed (message, &olds, &news, &pending); + if (news == state) { + gst_message_unref (message); + goto success; + } + } + break; + } + case GST_MESSAGE_ERROR: { + gchar *debug = NULL; + GError *gsterror = NULL; + gst_message_parse_error (message, &gsterror, &debug); + fprintf (stderr, "Error: %s (%s)\n", gsterror->message, debug); + gst_message_unref (message); + g_error_free (gsterror); + g_free (debug); + goto error; + } + case GST_MESSAGE_EOS: { + gst_message_unref (message); + goto error; + } + default: + g_assert_not_reached (); + break; + } + gst_message_unref (message); + } + g_assert_not_reached (); + +success: + /* state change succeeded */ + fprintf (stderr, "state change to %s succeeded\n", gst_element_state_get_name (state)); + ignore_messages_mask = saved_events; + return true; + +timed_out: + /* it's taking a long time to open -- just tell totem it was ok, this allows + * the user to stop the loading process with the normal stop button */ + fprintf (stderr, "state change to %s timed out, returning success and handling errors asynchroneously\n", gst_element_state_get_name (state)); + ignore_messages_mask = saved_events; + return true; + +error: + fprintf (stderr, "error while waiting for state change to %s: %s\n", + gst_element_state_get_name (state), + (error && *error) ? (*error)->message : "unknown"); + /* already set *error */ + ignore_messages_mask = saved_events; + QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_error)); + return false; +} + +//----------------------------------------------------------------------------- + +GstSizeEvent::GstSizeEvent (int l, int w, int h) + : QEvent ((QEvent::Type) event_size), + length (l), width (w), height (h) +{} + +GstProgressEvent::GstProgressEvent (const int p) + : QEvent ((QEvent::Type) event_progress), progress (p) +{} + +//----------------------------------------------------------------------------- + +using namespace KMPlayer; + +Backend::Backend () + : DCOPObject (QCString ("Backend")) { +} + +Backend::~Backend () {} + +void Backend::setURL (QString url) { + mrl = url; +} + +void Backend::setSubTitleURL (QString url) { + sub_mrl = url; +} + +void Backend::play (int repeat) { + gstapp->play (repeat); +} + +void Backend::stop () { + QTimer::singleShot (0, gstapp, SLOT (stop ())); +} + +void Backend::pause () { + gstapp->pause (); +} + +void Backend::seek (int v, bool /*absolute*/) { + gstapp->seek (v); +} + +void Backend::hue (int h, bool) { + gstapp->hue (h); +} + +void Backend::saturation (int s, bool) { + gstapp->saturation (s); +} + +void Backend::contrast (int c, bool) { + gstapp->contrast (c); +} + +void Backend::brightness (int b, bool) { + gstapp->brightness (b); +} + +void Backend::volume (int v, bool) { + gstapp->volume (v); +} + +void Backend::frequency (int) { +} + +void Backend::setAudioLang (int, QString) { +} + +void Backend::setSubtitle (int, QString) { +} + +void Backend::quit () { + delete callback; + callback = 0L; + if (running) + stop (); + else + QTimer::singleShot (0, qApp, SLOT (quit ())); +} + +bool updateConfigEntry (const QString & name, const QString & value) { + fprintf (stderr, "%s=%s\n", name.ascii (), (const char *) value.local8Bit ()); + return true; +} + +void Backend::setConfig (QByteArray /*data*/) { + /*QString err; + int line, column; + QDomDocument dom; + if (dom.setContent (data, false, &err, &line, &column)) { + if (dom.childNodes().length() == 1) { + for (QDomNode node = dom.firstChild().firstChild(); + !node.isNull (); + node = node.nextSibling ()) { + QDomNamedNodeMap attr = node.attributes (); + updateConfigEntry (attr.namedItem (attname).nodeValue (), + attr.namedItem (attvalue).nodeValue ()); + } + } else + err = QString ("invalid data"); + } + if (callback) + callback->errorMessage (0, err);*/ +} + +bool Backend::isPlaying () { + mutex.lock (); + bool b = gst_elm_play && (GST_STATE (gst_elm_play) == GST_STATE_PLAYING); + mutex.unlock (); + return b; +} + +KGStreamerPlayer::KGStreamerPlayer (int _argc, char ** _argv) + : QApplication (_argc, _argv, false) { +} + +void KGStreamerPlayer::init () { + int xpos = 0; + int ypos = 0; + int width = 320; + int height = 200; + + XLockDisplay(display); + if (window_created) + wid = XCreateSimpleWindow(display, XDefaultRootWindow(display), + xpos, ypos, width, height, 1, 0, 0); + fprintf (stderr, "init wid %u created:%d\n", wid, window_created); + XSelectInput (display, wid, + (PointerMotionMask | ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask)); // | SubstructureNotifyMask)); + + if (window_created) { + //fprintf (stderr, "map %lu\n", wid); + XMapRaised(display, wid); + XSync(display, False); + } + XUnlockDisplay(display); +} + +KGStreamerPlayer::~KGStreamerPlayer () { + if (window_created) { + XLockDisplay (display); + fprintf (stderr, "unmap %lu\n", wid); + XUnmapWindow (display, wid); + XDestroyWindow(display, wid); + XSync (display, False); + XUnlockDisplay (display); + } + gstapp = 0L; +} + +void getConfigEntries (QByteArray & buf) { + QDomDocument doc; + QDomElement root = doc.createElement (QString ("document")); + doc.appendChild (root); + QCString exp = doc.toCString (); + buf = exp; + buf.resize (exp.length ()); // strip terminating \0 +} + +void KGStreamerPlayer::play (int repeat) { + GstElement *element; + GstElement *videosink = 0L; + GstElement *audiosink = 0L; + bool success; + fprintf (stderr, "play %s\n", mrl.isEmpty() ? "<empty>" : mrl.ascii ()); + if (gst_elm_play) { + if (GST_STATE (gst_elm_play) == GST_STATE_PAUSED) { + gst_element_set_state (gst_elm_play, GST_STATE_PLAYING); + gstPollForStateChange (gst_elm_play, GST_STATE_PLAYING); + } + return; + } + notified_playing = false; + if (mrl.isEmpty ()) + return; + gchar *uri, *sub_uri = 0L; + movie_length = movie_width = movie_height = 0; + mutex.lock (); + gst_elm_play = gst_element_factory_make ("playbin", playbin_name); + if (!gst_elm_play) { + fprintf (stderr, "couldn't create playbin\n"); + goto fail; + } + ignore_messages_mask = 0; + gst_bus = gst_element_get_bus (gst_elm_play); + + gst_bus_add_signal_watch (gst_bus); + + gst_bus_async = g_signal_connect (gst_bus, "message", + G_CALLBACK (gstBusMessage), 0L); + if (ao_driver && !strncmp (ao_driver, "alsa", 4)) + audiosink = gst_element_factory_make ("alsasink", "audiosink"); + else if (ao_driver && !strncmp (ao_driver, "arts", 4)) + audiosink = gst_element_factory_make ("artsdsink", "audiosink"); + else if (ao_driver && !strncmp (ao_driver, "esd", 3)) + audiosink = gst_element_factory_make ("esdsink", "audiosink"); + else + audiosink = gst_element_factory_make ("osssink", "audiosink"); + if (!audiosink) + goto fail; + if (vo_driver && !strncmp (vo_driver, "xv", 2)) + videosink = gst_element_factory_make ("xvimagesink", "videosink"); + else + videosink = gst_element_factory_make ("ximagesink", "videosink"); + if (!videosink) + goto fail; + if (GST_IS_BIN (videosink)) + element = gst_bin_get_by_interface (GST_BIN (videosink), + GST_TYPE_X_OVERLAY); + else + element = videosink; + if (GST_IS_X_OVERLAY (element)) { + xoverlay = GST_X_OVERLAY (element); + gst_x_overlay_set_xwindow_id (xoverlay, wid); + } + gst_element_set_bus (videosink, gst_bus); + gst_element_set_state (videosink, GST_STATE_READY); + success = gstPollForStateChange (videosink, GST_STATE_READY); + //if (!success) { + /* Drop this video sink */ + // gst_element_set_state (videosink, GST_STATE_NULL); + // gst_object_unref (videosink); + if (audiosink) { + gst_element_set_bus (audiosink, gst_bus); + gst_element_set_state (audiosink, GST_STATE_READY); + success = gstPollForStateChange (audiosink, GST_STATE_READY); + } + g_object_set (G_OBJECT (gst_elm_play), + "video-sink", videosink, + "audio-sink", audiosink, + NULL); + gst_bus_set_sync_handler (gst_bus, gst_bus_sync_signal_handler, 0L); + gst_bus_sync = g_signal_connect (gst_bus, "sync-message::element", + G_CALLBACK (gstMessageElement), 0L); + g_signal_connect (gst_elm_play, "notify::source", + G_CALLBACK (gstSource), 0L); + g_signal_connect (gst_elm_play, "notify::stream-info", + G_CALLBACK (gstStreamInfo), 0L); + if (GST_IS_COLOR_BALANCE (videosink)) + color_balance = GST_COLOR_BALANCE (videosink); + + if (GST_STATE (gst_elm_play) > GST_STATE_READY) + gst_element_set_state (gst_elm_play, GST_STATE_READY); + + if (mrl.startsWith (QChar ('/'))) + mrl = QString ("file://") + mrl; + uri = g_strdup (mrl.local8Bit ()); + g_object_set (gst_elm_play, "uri", uri, NULL); + if (!sub_mrl.isEmpty ()) { + if (sub_mrl.startsWith (QChar ('/'))) + sub_mrl = QString ("file://") + sub_mrl; + sub_uri = g_strdup (sub_mrl.local8Bit ()); + g_object_set (gst_elm_play, "suburi", sub_uri, NULL); + g_free (sub_uri); + } + repeat_count = repeat; + mutex.unlock (); + gst_element_set_state (gst_elm_play, GST_STATE_PAUSED); + if (gstPollForStateChange (gst_elm_play, GST_STATE_PAUSED)) { + gst_element_set_state (gst_elm_play, GST_STATE_PLAYING); + gstPollForStateChange (gst_elm_play, GST_STATE_PLAYING); + } + g_free (uri); + QTimer::singleShot (500, this, SLOT (updatePosition ())); + return; +fail: + if (videosink) { + gst_element_set_state (videosink, GST_STATE_NULL); + gst_object_unref (videosink); + } + if (audiosink) { + gst_element_set_state (audiosink, GST_STATE_NULL); + gst_object_unref (audiosink); + } + mutex.unlock (); + QApplication::postEvent (gstapp, new QEvent ((QEvent::Type)event_finished)); +} + +void KGStreamerPlayer::pause () { + mutex.lock (); + if (gst_elm_play) { + GstState state = GST_STATE (gst_elm_play) == GST_STATE_PLAYING ? + GST_STATE_PAUSED : GST_STATE_PLAYING; + gst_element_set_state (gst_elm_play, state); + gstPollForStateChange (gst_elm_play, state); + } + mutex.unlock (); +} + +void KGStreamerPlayer::stop () { + fprintf (stderr, "stop %s\n", mrl.isEmpty () ? "<empty>" : mrl.ascii ()); + mutex.lock (); + repeat_count = 0; + if (gst_elm_play) { + GstState current_state; + gst_element_get_state (gst_elm_play, ¤t_state, NULL, 0); + if (current_state > GST_STATE_READY) { + gst_element_set_state (gst_elm_play, GST_STATE_READY); + mutex.unlock (); + gstPollForStateChange (gst_elm_play, GST_STATE_READY, -1); + mutex.lock (); + } + gst_element_set_state (gst_elm_play, GST_STATE_NULL); + gst_element_get_state (gst_elm_play, NULL, NULL, -1); + } + mutex.unlock (); + if (!gst_elm_play || (gst_elm_play && !notified_playing)) + QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_finished)); +} + +void KGStreamerPlayer::finished () { + QTimer::singleShot (10, this, SLOT (stop ())); +} + +static void adjustColorSetting (const char * channel, int val) { + //fprintf (stderr, "adjustColorSetting %s\n", channel); + mutex.lock (); + if (color_balance) { + for (const GList *item =gst_color_balance_list_channels (color_balance); + item != NULL; item = item->next) { + GstColorBalanceChannel *chan = (GstColorBalanceChannel*) item->data; + + if (!strstr (chan->label, channel)) + gst_color_balance_set_value (color_balance, chan, + ((val + 100) * (chan->max_value - chan->min_value)/200 + chan->min_value)); + } + } + mutex.unlock (); +} + +void KGStreamerPlayer::saturation (int s) { + adjustColorSetting ("SATURATION", s); +} + +void KGStreamerPlayer::hue (int h) { + adjustColorSetting ("HUE", h); +} + +void KGStreamerPlayer::contrast (int c) { + adjustColorSetting ("CONTRAST", c); +} + +void KGStreamerPlayer::brightness (int b) { + adjustColorSetting ("BRIGHTNESS", b); +} + +void KGStreamerPlayer::seek (int val /*offset_in_deciseconds*/) { + //fprintf (stderr, "seek %d\n", val); + mutex.lock (); + if (gst_elm_play) + gst_element_seek (gst_elm_play, 1.0, GST_FORMAT_TIME, + (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), + GST_SEEK_TYPE_SET, val * GST_MSECOND * 100, + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + mutex.unlock (); +} + +void KGStreamerPlayer::volume (int val) { + //fprintf (stderr, "position %d\n", val); + if (gst_elm_play) + g_object_set (G_OBJECT (gst_elm_play), "volume", 1.0*val/100, 0L); +} + +void KGStreamerPlayer::updatePosition () { + if (gst_elm_play) { + do { + GstMessage * msg = gst_bus_poll (gst_bus, (GstMessageType) (GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_DURATION), GST_MSECOND * 10); + if (!msg) + break; + gst_message_unref (msg); + } while (gst_bus); + + mutex.lock (); + if (gst_elm_play && callback) { + GstFormat fmt = GST_FORMAT_TIME; + gint64 val = 0; // usec + if (gst_element_query_position (gst_elm_play, &fmt, &val)) + callback->moviePosition (int (val / (GST_MSECOND * 100))); + } + mutex.unlock (); + QTimer::singleShot (500, this, SLOT (updatePosition ())); + } +} + +bool KGStreamerPlayer::event (QEvent * e) { + switch (e->type()) { + case event_finished: { + fprintf (stderr, "event_finished\n"); + mutex.lock (); + if (gst_elm_play) { + gst_bus_set_flushing (gst_bus, true); + if (gst_bus_sync) + g_signal_handler_disconnect (gst_bus, gst_bus_sync); + if (gst_bus_async) + g_signal_handler_disconnect (gst_bus, gst_bus_async); + gst_object_unref (gst_bus); + gst_object_unref (GST_OBJECT (gst_elm_play)); + gst_bus = 0L; + gst_elm_play = 0L; + color_balance = 0L; + gst_bus_sync = gst_bus_async = 0; + xoverlay = 0L; + } + mutex.unlock (); + if (callback) + callback->finished (); + else + QTimer::singleShot (0, this, SLOT (quit ())); + break; + } + //callback->movieParams (se->length/100, se->width, se->height, se->height ? 1.0*se->width/se->height : 1.0); + case event_size: { + GstSizeEvent * se = static_cast <GstSizeEvent *> (e); + fprintf (stderr, "movie parms: %d %d %d\n", se->length, se->width, se->height); + if (callback) { + if (se->length < 0) se->length = 0; + callback->movieParams (se->length, se->width, se->height, se->height ? 1.0*se->width/se->height : 1.0, QStringList (), QStringList ()); + } + if (window_created && movie_width > 0 && movie_height > 0) { + XLockDisplay (display); + XResizeWindow (display, wid, movie_width, movie_height); + XFlush (display); + XUnlockDisplay (display); + } + // fall through + } + case event_playing: + notified_playing = true; + if (callback) + callback->playing (); + break; + case event_progress: + if (callback) + callback->loadingProgress + (static_cast <GstProgressEvent *> (e)->progress); + break; + case event_eos: + case event_error: + stop (); + break; + case event_video: + if (callback) + callback->statusMessage ((int) KMPlayer::Callback::stat_hasvideo, QString ()); + break; + default: + return false; + } + return true; +} + +void KGStreamerPlayer::saveState (QSessionManager & sm) { + if (callback) + sm.setRestartHint (QSessionManager::RestartNever); +} + +class XEventThread : public QThread { +protected: + void run () { + Time prev_click_time = 0; + int prev_click_x = 0; + int prev_click_y = 0; + while (true) { + XEvent xevent; + XNextEvent(display, &xevent); + switch(xevent.type) { + case ClientMessage: + if (xevent.xclient.format == 8 && + !strncmp(xevent.xclient.data.b, "quit_now", 8)) { + fprintf(stderr, "request quit\n"); + return; + } + break; + case KeyPress: { + XKeyEvent kevent; + KeySym ksym; + char kbuf[256]; + int len; + kevent = xevent.xkey; + XLockDisplay(display); + len = XLookupString(&kevent, kbuf, sizeof(kbuf), &ksym, NULL); + XUnlockDisplay(display); + fprintf(stderr, "keypressed 0x%x 0x%x\n", kevent.keycode, ksym); + switch (ksym) { + case XK_q: + case XK_Q: + gstapp->lock (); + gstapp->stop (); + gstapp->unlock (); + break; + } + break; + } + case Expose: + if (!xevent.xexpose.count && xevent.xexpose.window == wid) { + mutex.lock (); + if (gst_elm_play) { + GstElement *videosink; + g_object_get (gst_elm_play, "video-sink", &videosink, NULL); + if (videosink && GST_IS_X_OVERLAY (videosink)) { + gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (videosink), wid); + gst_x_overlay_expose (GST_X_OVERLAY (videosink)); + gst_object_unref (videosink); + } + } + mutex.unlock (); + } + break; + + case ConfigureNotify: + mutex.lock (); + if (xoverlay && GST_IS_X_OVERLAY (xoverlay)) + gst_x_overlay_expose (xoverlay); + mutex.unlock (); + break; + case ButtonPress: { + XButtonEvent *bev = (XButtonEvent *) &xevent; + int dx = prev_click_x - bev->x; + int dy = prev_click_y - bev->y; + if (bev->time - prev_click_time < 400 && + (dx * dx + dy * dy) < 25) { + gstapp->lock (); + if (callback) + callback->toggleFullScreen (); + gstapp->unlock (); + } + prev_click_time = bev->time; + prev_click_x = bev->x; + prev_click_y = bev->y; + break; + } + default: + ; //if (xevent.type < LASTEvent) + // fprintf (stderr, "event %d\n", xevent.type); + } + } + } +}; + +int main(int argc, char **argv) { + if (!XInitThreads ()) { + fprintf (stderr, "XInitThreads () failed\n"); + return 1; + } + display = XOpenDisplay(NULL); + screen = XDefaultScreen(display); + + gst_init (NULL, NULL); + + gstapp = new KGStreamerPlayer (argc, argv); + + for(int i = 1; i < argc; i++) { + if (!strcmp (argv [i], "-ao")) { + ao_driver = argv [++i]; + } else if (!strcmp (argv [i], "-vo")) { + vo_driver = argv [++i]; + } else if (!strcmp (argv [i], "-dvd-device") && ++i < argc) { + dvd_device = argv [i]; + } else if (!strcmp (argv [i], "-vcd-device") && ++i < argc) { + vcd_device = argv [i]; + } else if (!strcmp (argv [i], "-wid") || !strcmp (argv [i], "-window-id")) { + wid = atol (argv [++i]); + window_created = false; + } else if (!strcmp (argv [i], "-root")) { + wid = XDefaultRootWindow (display); + window_created = false; + } else if (!strcmp (argv [i], "-window")) { + ; + } else if (!strcmp (argv [i], "-v")) { + verbose = true; + } else if (!strcmp (argv [i], "-c")) { + wants_config = true; + } else if (!strcmp (argv [i], "-f") && i < argc - 1) { + strncpy (configfile, argv [++i], sizeof (configfile)); + configfile[sizeof (configfile) - 1] = 0; + } else if (!strcmp (argv [i], "-loop") && i < argc - 1) { + repeat_count = atol (argv [++i]); + } else if (!strcmp (argv [i], "-cb")) { + QString str = argv [++i]; + int pos = str.find ('/'); + if (pos > -1) { + fprintf (stderr, "callback is %s %s\n", str.left (pos).ascii (), str.mid (pos + 1).ascii ()); + callback = new KMPlayer::Callback_stub + (str.left (pos).ascii (), str.mid (pos + 1).ascii ()); + } + } else if (!strncmp (argv [i], "-", 1)) { + fprintf (stderr, "usage: %s [-vo (xv|xshm)] [-ao <audio driver>] [-f <config file>] [-dvd-device <device>] [-vcd-device <device>] [-v] [(-wid|-window-id) <window>] [(-root|-window)] [-cb <DCOP callback name> [-c]] [<url>]\n", argv[0]); + delete gstapp; + return 1; + } else { + mrl = QString::fromLocal8Bit (argv[i]); + } + } + + DCOPClient dcopclient; + dcopclient.registerAs ("kgstreamerplayer"); + Backend * backend = new Backend; + + XEventThread * eventThread = new XEventThread; + eventThread->start (); + + gstapp->init (); + + if (callback) { + QByteArray buf; + if (wants_config) + getConfigEntries (buf); + callback->started (dcopclient.appId (), buf); + } else + QTimer::singleShot (10, gstapp, SLOT (play (int))); + + gstapp->exec (); + + XLockDisplay(display); + XClientMessageEvent ev = { + ClientMessage, 0, true, display, wid, + XInternAtom (display, "XVIDEO", false), 8, {b: "quit_now"} + }; + XSendEvent (display, wid, false, StructureNotifyMask, (XEvent *) & ev); + XFlush (display); + XUnlockDisplay(display); + eventThread->wait (500); + delete eventThread; + + gstapp->stop (); + delete backend; + delete gstapp; + + fprintf (stderr, "closing display\n"); + XCloseDisplay (display); + fprintf (stderr, "done\n"); + fflush (stderr); + return 0; +} + +#include "gstplayer.moc" |