File indexing completed on 2024-05-05 04:55:45

0001 /*
0002     This file is part of the KMPlayer application
0003     SPDX-FileCopyrightText: 2004 Koos Vriezen <koos.vriezen@xs4all.nl>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include <cstdio>
0009 #include <cstring>
0010 #include <cmath>
0011 #include "config-kmplayer.h"
0012 #include <dcopclient.h>
0013 #include <QCString>
0014 #include <QTimer>
0015 #include <QFile>
0016 #include <QUrl>
0017 #include <QThread>
0018 #include <QMutex>
0019 #include <QDom>
0020 #include "kmplayer_backend.h"
0021 #include "kmplayer_callback_stub.h"
0022 #include "kmplayer_callback.h"
0023 #include "gstplayer.h"
0024 #include <X11/X.h>
0025 #include <X11/Xlib.h>
0026 #include <X11/Xutil.h>
0027 #include <X11/keysym.h>
0028 #include <X11/Xatom.h>
0029 #include <gst/gst.h>
0030 #include <gst/interfaces/xoverlay.h>
0031 #include <gst/interfaces/colorbalance.h>
0032 
0033 static char                 configfile[2048];
0034 
0035 static Display             *display;
0036 static KGStreamerPlayer   *gstapp;
0037 static KMPlayer::Callback_stub * callback;
0038 static Window               wid;
0039 static QMutex               mutex (true);
0040 static bool                 window_created = true;
0041 static bool                 wants_config;
0042 static bool                 verbose;
0043 static bool                 notified_playing;
0044 static int                  running;
0045 static int                  movie_width;
0046 static int                  movie_height;
0047 static int                  movie_length;
0048 static int                  repeat_count;
0049 static int                  screen;
0050 static const int            event_finished = QEvent::User;
0051 static const int            event_playing = QEvent::User + 1;
0052 static const int            event_size = QEvent::User + 2;
0053 static const int            event_eos = QEvent::User + 3;
0054 static const int            event_progress = QEvent::User + 4;
0055 static const int            event_error = QEvent::User + 5;
0056 static const int            event_video = QEvent::User + 6;
0057 static QString              mrl;
0058 static QString              sub_mrl;
0059 static const char          *ao_driver;
0060 static const char          *vo_driver;
0061 static const char          *playbin_name = "player";
0062 static const char          *dvd_device;
0063 static const char          *vcd_device;
0064 static GstElement          *gst_elm_play;
0065 static GstBus              *gst_bus;
0066 static unsigned int /*GstMessageType*/       ignore_messages_mask;
0067 static GstXOverlay         *xoverlay;
0068 static GstColorBalance     *color_balance;
0069 static gulong               gst_bus_sync;
0070 static gulong               gst_bus_async;
0071 static QString elmentry ("entry");
0072 static QString elmitem ("item");
0073 static QString attname ("NAME");
0074 static QString atttype ("TYPE");
0075 static QString attdefault ("DEFAULT");
0076 static QString attvalue ("VALUE");
0077 static QString attstart ("START");
0078 static QString attend ("END");
0079 static QString valrange ("range");
0080 static QString valnum ("num");
0081 static QString valbool ("bool");
0082 static QString valenum ("enum");
0083 static QString valstring ("string");
0084 
0085 extern "C" {
0086   // nothing yet
0087 } // extern "C"
0088 
0089 
0090 static bool gstPollForStateChange (GstElement *, GstState, gint64=GST_SECOND/2);
0091 
0092 static void
0093 cb_error (GstElement * play,
0094           GstElement * /*src*/,
0095           GError     *err,
0096           const char *debug,
0097           gpointer    /*data*/)
0098 {
0099     fprintf (stderr, "cb_error: %s %s\n", err->message, debug);
0100     if (GST_STATE (play) == GST_STATE_PLAYING)
0101         gst_element_set_state (play, GST_STATE_READY);
0102     QApplication::postEvent (gstapp, new QEvent ((QEvent::Type)event_finished));
0103 }
0104 
0105 // NULL -> READY -> PAUSED -> PLAYING
0106 
0107 static void
0108 gstCapsSet (GstPad *pad,
0109             GParamSpec * /*pspec*/,
0110             gpointer /*data*/)
0111 {
0112     GstCaps *caps = gst_pad_get_negotiated_caps (pad);
0113     if (!caps)
0114         return;
0115     QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_video));
0116     const GstStructure * s = gst_caps_get_structure (caps, 0);
0117     if (s) {
0118         const GValue *par;
0119 
0120         gst_structure_get_int (s, "width", &movie_width);
0121         gst_structure_get_int (s, "height", &movie_height);
0122         if ((par = gst_structure_get_value (s, "pixel-aspect-ratio"))) {
0123             int num = gst_value_get_fraction_numerator (par),
0124                 den = gst_value_get_fraction_denominator (par);
0125 
0126             if (num > den)
0127                 movie_width = (int) ((float) num * movie_width / den);
0128             else
0129                 movie_height = (int) ((float) den * movie_height / num);
0130         }
0131         QApplication::postEvent (gstapp, new GstSizeEvent (movie_length, movie_width, movie_height));
0132     }
0133     gst_caps_unref (caps);
0134 }
0135 
0136 static void gstStreamInfo (GObject *, GParamSpec *, gpointer /*data*/) {
0137     GstPad *videopad = 0L;
0138     GList *streaminfo = 0L;
0139 
0140     fprintf (stderr, "gstStreamInfo\n");
0141     g_object_get (gst_elm_play, "stream-info", &streaminfo, NULL);
0142     streaminfo = g_list_copy (streaminfo);
0143     g_list_foreach (streaminfo, (GFunc) g_object_ref, NULL);
0144     for ( ; streaminfo != NULL; streaminfo = streaminfo->next) {
0145         GObject *info = G_OBJECT (streaminfo->data);
0146         gint type;
0147         GParamSpec *pspec;
0148         GEnumValue *val;
0149 
0150         if (!info)
0151             continue;
0152         g_object_get (info, "type", &type, NULL);
0153         pspec = g_object_class_find_property (G_OBJECT_GET_CLASS(info), "type");
0154         val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
0155 
0156         if (!g_strcasecmp (val->value_nick, "video"))
0157             if (!videopad) {
0158                 g_object_get (info, "object", &videopad, NULL);
0159                 gstCapsSet (GST_PAD (videopad), 0L, 0L);
0160                 g_signal_connect (videopad, "notify::caps", G_CALLBACK (gstCapsSet), 0L);
0161             }
0162     }
0163 
0164     GstMessage * msg = gst_message_new_application (GST_OBJECT (gst_elm_play),
0165             gst_structure_new ("notify-streaminfo", NULL));
0166     gst_element_post_message (gst_elm_play, msg);
0167     g_list_foreach (streaminfo, (GFunc) g_object_unref, NULL);
0168     g_list_free (streaminfo);
0169 }
0170 
0171 static void gstSource (GObject *, GParamSpec *, gpointer /*data*/) {
0172     GObject *source = 0L;
0173     fprintf (stderr, "gstSource\n");
0174     g_object_get (gst_elm_play, "source", &source, NULL);
0175     if (!source)
0176         return;
0177     GObjectClass *klass = G_OBJECT_GET_CLASS (source);
0178     if (mrl.startsWith ("dvd://") && dvd_device) {
0179         if (g_object_class_find_property (klass, "device"))
0180             g_object_set (source, "device", dvd_device, NULL);
0181     } else if (mrl.startsWith ("vcd://") && vcd_device) {
0182         if (g_object_class_find_property (klass, "device"))
0183             g_object_set (source, "device", vcd_device, NULL);
0184     }
0185     g_object_unref (source);
0186 }
0187 
0188 static void gstGetDuration () {
0189     GstFormat fmt = GST_FORMAT_TIME;
0190     gint64 len = -1; // usec
0191     if (gst_element_query_duration (gst_elm_play, &fmt, &len))
0192         if (movie_length != len / (GST_MSECOND * 100)) {
0193             movie_length = len / (GST_MSECOND * 100);
0194             fprintf (stderr, "new length %d\n", movie_length);
0195             QApplication::postEvent (gstapp, new GstSizeEvent (movie_length, movie_width, movie_height));
0196         }
0197 }
0198 
0199 static void gstTag (const GstTagList *, const gchar *tag, gpointer) {
0200     fprintf (stderr, "Tag: %s\n", tag);
0201 }
0202 
0203 //static bool gstStructure (GQuark field_id, const GValue *value, gpointer user_data);
0204 
0205 static void gstBusMessage (GstBus *, GstMessage * message, gpointer) {
0206     GstMessageType msg_type = GST_MESSAGE_TYPE (message);
0207     /* somebody else is handling the message, probably in gstPolForStateChange*/
0208     if (ignore_messages_mask & msg_type)
0209         return;
0210     switch (msg_type) {
0211         case GST_MESSAGE_ERROR:
0212             fprintf (stderr, "error msg\n");
0213             QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_error));
0214             if (gst_elm_play) {
0215                 gst_element_set_state (gst_elm_play, GST_STATE_NULL);
0216                 //gstPollForStateChange (gst_elm_play, GST_STATE_NULL);
0217             }
0218             break;
0219         case GST_MESSAGE_WARNING:
0220             fprintf (stderr, "warning msg\n");
0221             break;
0222         case GST_MESSAGE_TAG: {
0223             GstTagList *tag_list;
0224             //fprintf (stderr, "tag msg\n");
0225             gst_message_parse_tag (message, &tag_list);
0226             gst_tag_list_foreach (tag_list, gstTag, 0L);
0227             gst_tag_list_free (tag_list);
0228             break;
0229         }
0230         case GST_MESSAGE_EOS:
0231             fprintf (stderr, "eos msg\n");
0232             gst_element_set_state (gst_elm_play, GST_STATE_READY);
0233             break;
0234         case GST_MESSAGE_BUFFERING: {
0235             gint percent = 0;
0236             gst_structure_get_int (message->structure, "buffer-percent", &percent);
0237             QApplication::postEvent (gstapp, new GstProgressEvent (percent));
0238             //fprintf (stderr, "Buffering message (%u%%)\n", percent);
0239             break;
0240         }
0241         case GST_MESSAGE_APPLICATION: {
0242             const char * msg = gst_structure_get_name (message->structure);
0243             fprintf (stderr, "app msg %s\n", msg ? msg : "<unknown>");
0244             //gst_structure_foreach (message->structure, gstStructure, 0L);
0245             break;
0246         }
0247         case GST_MESSAGE_STATE_CHANGED: {
0248             GstState old_state, new_state;
0249             //gchar *src_name = gst_object_get_name (message->src);
0250             gst_message_parse_state_changed(message, &old_state, &new_state,0L);
0251             //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));
0252             if (GST_IS_ELEMENT (message->src) &&
0253                     GST_ELEMENT (message->src) == gst_elm_play) {
0254                 if (old_state == GST_STATE_PAUSED &&
0255                         new_state >= GST_STATE_PLAYING) {
0256                     gstGetDuration ();
0257                     QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_playing));
0258                 } else if (old_state >= GST_STATE_PAUSED &&
0259                         new_state <= GST_STATE_READY) {
0260                     if (repeat_count-- > 0 &&
0261                       (gst_element_set_state(gst_elm_play, GST_STATE_PAUSED),
0262                        gstPollForStateChange(gst_elm_play, GST_STATE_PAUSED)))
0263                          gst_element_set_state(gst_elm_play, GST_STATE_PLAYING);
0264                     else
0265                         QApplication::postEvent (gstapp,
0266                                 new QEvent ((QEvent::Type) event_finished));
0267                 }
0268             }
0269             //g_free (src_name);
0270             break;
0271         }
0272         case GST_MESSAGE_DURATION:
0273             gstGetDuration ();
0274             break;
0275         case GST_MESSAGE_CLOCK_PROVIDE:
0276         case GST_MESSAGE_CLOCK_LOST:
0277         case GST_MESSAGE_NEW_CLOCK:
0278         case GST_MESSAGE_STATE_DIRTY:
0279              break;
0280         default:
0281              fprintf (stderr, "Unhandled msg %s (0x%x)\n",
0282                      gst_message_type_get_name (msg_type), msg_type);
0283              break;
0284     }
0285 }
0286 
0287 static void gstMessageElement (GstBus *, GstMessage *msg, gpointer /*data*/) {
0288     if (gst_structure_has_name (msg->structure, "prepare-xwindow-id")) {
0289         fprintf (stderr, "prepare-xwindow-id\n");
0290         if (xoverlay)
0291             gst_x_overlay_set_xwindow_id (xoverlay, wid);
0292     }
0293 }
0294 
0295 static bool gstPollForStateChange (GstElement *element, GstState state, gint64 timeout) {
0296     /*GstMessageType*/ unsigned int events, saved_events;
0297     GstBus *bus = gst_element_get_bus (element);
0298     GError **error = 0L;
0299 
0300     events = GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS;
0301     saved_events = ignore_messages_mask;
0302 
0303     if (element && element == gst_elm_play) {
0304         /* we do want the main handler to process state changed messages for
0305          * playbin as well, otherwise it won't hook up the timeout etc. */
0306         ignore_messages_mask |= (events ^ GST_MESSAGE_STATE_CHANGED);
0307     } else {
0308         ignore_messages_mask |= events;
0309     }
0310 
0311     while (true) {
0312         GstMessage *message;
0313         GstElement *src;
0314 
0315         message = gst_bus_poll (bus, (GstMessageType) events, timeout);
0316         if (!message)
0317             goto timed_out;
0318 
0319         src = (GstElement*)GST_MESSAGE_SRC (message);
0320 
0321         switch (GST_MESSAGE_TYPE (message)) {
0322             case GST_MESSAGE_STATE_CHANGED: {
0323                 GstState olds, news, pending;
0324                 if (src == element) {
0325                     gst_message_parse_state_changed (message, &olds, &news, &pending);
0326                     if (news == state) {
0327                         gst_message_unref (message);
0328                         goto success;
0329                     }
0330                 }
0331                 break;
0332             }
0333             case GST_MESSAGE_ERROR: {
0334                 gchar *debug = NULL;
0335                 GError *gsterror = NULL;
0336                 gst_message_parse_error (message, &gsterror, &debug);
0337                 fprintf (stderr, "Error: %s (%s)\n", gsterror->message, debug);
0338                 gst_message_unref (message);
0339                 g_error_free (gsterror);
0340                 g_free (debug);
0341                 goto error;
0342             }
0343             case GST_MESSAGE_EOS: {
0344                 gst_message_unref (message);
0345                 goto error;
0346             }
0347             default:
0348                 g_assert_not_reached ();
0349                 break;
0350         }
0351         gst_message_unref (message);
0352     }
0353     g_assert_not_reached ();
0354 
0355 success:
0356     /* state change succeeded */
0357     fprintf (stderr, "state change to %s succeeded\n", gst_element_state_get_name (state));
0358     ignore_messages_mask = saved_events;
0359     return true;
0360 
0361 timed_out:
0362     /* it's taking a long time to open -- just tell totem it was ok, this allows
0363      * the user to stop the loading process with the normal stop button */
0364     fprintf (stderr, "state change to %s timed out, returning success and handling errors asynchroneously\n", gst_element_state_get_name (state));
0365     ignore_messages_mask = saved_events;
0366     return true;
0367 
0368 error:
0369     fprintf (stderr, "error while waiting for state change to %s: %s\n",
0370             gst_element_state_get_name (state),
0371             (error && *error) ? (*error)->message : "unknown");
0372     /* already set *error */
0373     ignore_messages_mask = saved_events;
0374     QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_error));
0375     return false;
0376 }
0377 
0378 //-----------------------------------------------------------------------------
0379 
0380 GstSizeEvent::GstSizeEvent (int l, int w, int h)
0381   : QEvent ((QEvent::Type) event_size),
0382     length (l), width (w), height (h) 
0383 {}
0384 
0385 GstProgressEvent::GstProgressEvent (const int p)
0386   : QEvent ((QEvent::Type) event_progress), progress (p) 
0387 {}
0388 
0389 //-----------------------------------------------------------------------------
0390 
0391 using namespace KMPlayer;
0392 
0393 Backend::Backend ()
0394     : DCOPObject (QCString ("Backend")) {
0395 }
0396 
0397 Backend::~Backend () {}
0398 
0399 void Backend::setURL (QString url) {
0400     mrl = url;
0401 }
0402 
0403 void Backend::setSubTitleURL (QString url) {
0404     sub_mrl = url;
0405 }
0406 
0407 void Backend::play (int repeat) {
0408     gstapp->play (repeat);
0409 }
0410 
0411 void Backend::stop () {
0412     QTimer::singleShot (0, gstapp, SLOT (stop ()));
0413 }
0414 
0415 void Backend::pause () {
0416     gstapp->pause ();
0417 }
0418 
0419 void Backend::seek (int v, bool /*absolute*/) {
0420     gstapp->seek (v);
0421 }
0422 
0423 void Backend::hue (int h, bool) {
0424     gstapp->hue (h);
0425 }
0426 
0427 void Backend::saturation (int s, bool) {
0428     gstapp->saturation (s);
0429 }
0430 
0431 void Backend::contrast (int c, bool) {
0432     gstapp->contrast (c);
0433 }
0434 
0435 void Backend::brightness (int b, bool) {
0436     gstapp->brightness (b);
0437 }
0438 
0439 void Backend::volume (int v, bool) {
0440     gstapp->volume (v);
0441 }
0442 
0443 void Backend::frequency (int) {
0444 }
0445 
0446 void Backend::setAudioLang (int, QString) {
0447 }
0448 
0449 void Backend::setSubtitle (int, QString) {
0450 }
0451 
0452 void Backend::quit () {
0453     delete callback;
0454     callback = 0L;
0455     if (running)
0456         stop ();
0457     else
0458         QTimer::singleShot (0, qApp, SLOT (quit ()));
0459 }
0460 
0461 bool updateConfigEntry (const QString & name, const QString & value) {
0462     fprintf (stderr, "%s=%s\n", name.ascii (), (const char *) value.local8Bit ());
0463     return true;
0464 }
0465 
0466 void Backend::setConfig (QByteArray /*data*/) {
0467     /*QString err;
0468     int line, column;
0469     QDomDocument dom;
0470     if (dom.setContent (data, false, &err, &line, &column)) {
0471         if (dom.childNodes().length () == 1) {
0472             for (QDomNode node = dom.firstChild().firstChild();
0473                     !node.isNull ();
0474                     node = node.nextSibling ()) {
0475                 QDomNamedNodeMap attr = node.attributes ();
0476                 updateConfigEntry (attr.namedItem (attname).nodeValue (),
0477                                    attr.namedItem (attvalue).nodeValue ());
0478             }
0479         } else
0480             err = QString ("invalid data");
0481     }
0482     if (callback)
0483         callback->errorMessage (0, err);*/
0484 }
0485 
0486 bool Backend::isPlaying () {
0487     mutex.lock ();
0488     bool b = gst_elm_play && (GST_STATE (gst_elm_play) == GST_STATE_PLAYING);
0489     mutex.unlock ();
0490     return b;
0491 }
0492 
0493 KGStreamerPlayer::KGStreamerPlayer (int _argc, char ** _argv)
0494   : QApplication (_argc, _argv, false) {
0495 }
0496 
0497 void KGStreamerPlayer::init () {
0498     int xpos    = 0;
0499     int ypos    = 0;
0500     int width   = 320;
0501     int height  = 200;
0502 
0503     XLockDisplay(display);
0504     if (window_created)
0505         wid = XCreateSimpleWindow(display, XDefaultRootWindow(display),
0506                 xpos, ypos, width, height, 1, 0, 0);
0507     fprintf (stderr, "init wid %u created:%d\n", wid, window_created);
0508     XSelectInput (display, wid,
0509                   (PointerMotionMask | ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask)); // | SubstructureNotifyMask));
0510 
0511     if (window_created) {
0512         //fprintf (stderr, "map %lu\n", wid);
0513         XMapRaised(display, wid);
0514         XSync(display, False);
0515     }
0516     XUnlockDisplay(display);
0517 }
0518 
0519 KGStreamerPlayer::~KGStreamerPlayer () {
0520     if (window_created) {
0521         XLockDisplay (display);
0522         fprintf (stderr, "unmap %lu\n", wid);
0523         XUnmapWindow (display,  wid);
0524         XDestroyWindow(display,  wid);
0525         XSync (display, False);
0526         XUnlockDisplay (display);
0527     }
0528     gstapp = 0L;
0529 }
0530 
0531 void getConfigEntries (QByteArray & buf) {
0532     QDomDocument doc;
0533     QDomElement root = doc.createElement (QString ("document"));
0534     doc.appendChild (root);
0535     QCString exp = doc.toCString ();
0536     buf = exp;
0537     buf.resize (exp.length ()); // strip terminating \0
0538 }
0539 
0540 void KGStreamerPlayer::play (int repeat) {
0541     GstElement *element;
0542     GstElement *videosink = 0L;
0543     GstElement *audiosink = 0L;
0544     bool success;
0545     fprintf (stderr, "play %s\n", mrl.isEmpty() ? "<empty>" : mrl.ascii ());
0546     if (gst_elm_play) {
0547         if (GST_STATE (gst_elm_play) == GST_STATE_PAUSED) {
0548             gst_element_set_state (gst_elm_play, GST_STATE_PLAYING);
0549             gstPollForStateChange (gst_elm_play, GST_STATE_PLAYING);
0550         }
0551         return;
0552     }
0553     notified_playing = false;
0554     if (mrl.isEmpty ())
0555         return;
0556     gchar *uri, *sub_uri = 0L;
0557     movie_length = movie_width = movie_height = 0;
0558     mutex.lock ();
0559     gst_elm_play = gst_element_factory_make ("playbin", playbin_name);
0560     if (!gst_elm_play) {
0561         fprintf (stderr, "couldn't create playbin\n");
0562         goto fail;
0563     }
0564     ignore_messages_mask = 0;
0565     gst_bus = gst_element_get_bus (gst_elm_play);
0566 
0567     gst_bus_add_signal_watch (gst_bus);
0568 
0569     gst_bus_async = g_signal_connect (gst_bus, "message",
0570                 G_CALLBACK (gstBusMessage), 0L);
0571     if (ao_driver && !strncmp (ao_driver, "alsa", 4))
0572         audiosink = gst_element_factory_make ("alsasink", "audiosink");
0573     else if (ao_driver && !strncmp (ao_driver, "arts", 4))
0574         audiosink = gst_element_factory_make ("artsdsink", "audiosink");
0575     else if (ao_driver && !strncmp (ao_driver, "esd", 3))
0576         audiosink = gst_element_factory_make ("esdsink", "audiosink");
0577     else
0578         audiosink = gst_element_factory_make ("osssink", "audiosink");
0579     if (!audiosink)
0580         goto fail;
0581     if (vo_driver && !strncmp (vo_driver, "xv", 2))
0582         videosink = gst_element_factory_make ("xvimagesink", "videosink");
0583     else
0584         videosink = gst_element_factory_make ("ximagesink", "videosink");
0585     if (!videosink)
0586         goto fail;
0587     if (GST_IS_BIN (videosink))
0588         element = gst_bin_get_by_interface (GST_BIN (videosink),
0589                 GST_TYPE_X_OVERLAY);
0590     else
0591         element = videosink;
0592     if (GST_IS_X_OVERLAY (element)) {
0593         xoverlay = GST_X_OVERLAY (element);
0594         gst_x_overlay_set_xwindow_id (xoverlay, wid);
0595     }
0596     gst_element_set_bus (videosink, gst_bus);
0597     gst_element_set_state (videosink, GST_STATE_READY);
0598     success = gstPollForStateChange (videosink, GST_STATE_READY);
0599     //if (!success) {
0600         /* Drop this video sink */
0601     //    gst_element_set_state (videosink, GST_STATE_NULL);
0602     //    gst_object_unref (videosink);
0603     if (audiosink) {
0604         gst_element_set_bus (audiosink, gst_bus);
0605         gst_element_set_state (audiosink, GST_STATE_READY);
0606         success = gstPollForStateChange (audiosink, GST_STATE_READY);
0607     }
0608     g_object_set (G_OBJECT (gst_elm_play),
0609             "video-sink",  videosink,
0610             "audio-sink",  audiosink,
0611             NULL);
0612     gst_bus_set_sync_handler (gst_bus, gst_bus_sync_signal_handler, 0L);
0613     gst_bus_sync = g_signal_connect (gst_bus, "sync-message::element",
0614             G_CALLBACK (gstMessageElement), 0L);
0615     g_signal_connect (gst_elm_play, "notify::source",
0616             G_CALLBACK (gstSource), 0L);
0617     g_signal_connect (gst_elm_play, "notify::stream-info",
0618             G_CALLBACK (gstStreamInfo), 0L);
0619     if (GST_IS_COLOR_BALANCE (videosink))
0620         color_balance = GST_COLOR_BALANCE (videosink);
0621 
0622     if (GST_STATE (gst_elm_play) > GST_STATE_READY)
0623         gst_element_set_state (gst_elm_play, GST_STATE_READY);
0624 
0625     if (mrl.startsWith (QChar ('/')))
0626         mrl = QString ("file://") + mrl;
0627     uri = g_strdup (mrl.local8Bit ());
0628     g_object_set (gst_elm_play, "uri", uri, NULL);
0629     if (!sub_mrl.isEmpty ()) {
0630         if (sub_mrl.startsWith (QChar ('/')))
0631             sub_mrl = QString ("file://") + sub_mrl;
0632         sub_uri = g_strdup (sub_mrl.local8Bit ());
0633         g_object_set (gst_elm_play, "suburi", sub_uri, NULL);
0634         g_free (sub_uri);
0635     }
0636     repeat_count = repeat;
0637     mutex.unlock ();
0638     gst_element_set_state (gst_elm_play, GST_STATE_PAUSED);
0639     if (gstPollForStateChange (gst_elm_play, GST_STATE_PAUSED)) {
0640         gst_element_set_state (gst_elm_play, GST_STATE_PLAYING);
0641         gstPollForStateChange (gst_elm_play, GST_STATE_PLAYING);
0642     }
0643     g_free (uri);
0644     QTimer::singleShot (500, this, SLOT (updatePosition ()));
0645     return;
0646 fail:
0647     if (videosink) {
0648         gst_element_set_state (videosink, GST_STATE_NULL);
0649         gst_object_unref (videosink);
0650     }
0651     if (audiosink) {
0652         gst_element_set_state (audiosink, GST_STATE_NULL);
0653         gst_object_unref (audiosink);
0654     }
0655     mutex.unlock ();
0656     QApplication::postEvent (gstapp, new QEvent ((QEvent::Type)event_finished));
0657 }
0658 
0659 void KGStreamerPlayer::pause () {
0660     mutex.lock ();
0661     if (gst_elm_play) {
0662         GstState state = GST_STATE (gst_elm_play) == GST_STATE_PLAYING ?
0663             GST_STATE_PAUSED : GST_STATE_PLAYING;
0664         gst_element_set_state (gst_elm_play, state);
0665         gstPollForStateChange (gst_elm_play, state);
0666     }
0667     mutex.unlock ();
0668 }
0669 
0670 void KGStreamerPlayer::stop () {
0671     fprintf (stderr, "stop %s\n", mrl.isEmpty () ? "<empty>" : mrl.ascii ());
0672     mutex.lock ();
0673     repeat_count = 0;
0674     if (gst_elm_play) {
0675         GstState current_state;
0676         gst_element_get_state (gst_elm_play, &current_state, NULL, 0);
0677         if (current_state > GST_STATE_READY) {
0678             gst_element_set_state (gst_elm_play, GST_STATE_READY);
0679             mutex.unlock ();
0680             gstPollForStateChange (gst_elm_play, GST_STATE_READY, -1);
0681             mutex.lock ();
0682         }
0683         gst_element_set_state (gst_elm_play, GST_STATE_NULL);
0684         gst_element_get_state (gst_elm_play, NULL, NULL, -1);
0685     }
0686     mutex.unlock ();
0687     if (!gst_elm_play || (gst_elm_play && !notified_playing))
0688         QApplication::postEvent (gstapp, new QEvent ((QEvent::Type) event_finished));
0689 }
0690 
0691 void KGStreamerPlayer::finished () {
0692     QTimer::singleShot (10, this, SLOT (stop ()));
0693 }
0694 
0695 static void adjustColorSetting (const char * channel, int val) {
0696     //fprintf (stderr, "adjustColorSetting %s\n", channel);
0697     mutex.lock ();
0698     if (color_balance) {
0699         for (const GList *item =gst_color_balance_list_channels (color_balance);
0700                 item != NULL; item = item->next) {
0701             GstColorBalanceChannel *chan = (GstColorBalanceChannel*) item->data;
0702 
0703             if (!strstr (chan->label, channel))
0704                 gst_color_balance_set_value (color_balance, chan,
0705                         ((val + 100) * (chan->max_value - chan->min_value)/200 + chan->min_value));
0706         }
0707     }
0708     mutex.unlock ();
0709 }
0710 
0711 void KGStreamerPlayer::saturation (int s) {
0712     adjustColorSetting ("SATURATION", s);
0713 }
0714 
0715 void KGStreamerPlayer::hue (int h) {
0716     adjustColorSetting ("HUE", h);
0717 }
0718 
0719 void KGStreamerPlayer::contrast (int c) {
0720     adjustColorSetting ("CONTRAST", c);
0721 }
0722 
0723 void KGStreamerPlayer::brightness (int b) {
0724     adjustColorSetting ("BRIGHTNESS", b);
0725 }
0726 
0727 void KGStreamerPlayer::seek (int val /*offset_in_deciseconds*/) {
0728     //fprintf (stderr, "seek %d\n", val);
0729     mutex.lock ();
0730     if (gst_elm_play)
0731         gst_element_seek (gst_elm_play, 1.0, GST_FORMAT_TIME,
0732                 (GstSeekFlags) (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
0733                 GST_SEEK_TYPE_SET, val * GST_MSECOND * 100,
0734                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
0735     mutex.unlock ();
0736 }
0737 
0738 void KGStreamerPlayer::volume (int val) {
0739     //fprintf (stderr, "position %d\n", val);
0740     if (gst_elm_play)
0741         g_object_set (G_OBJECT (gst_elm_play), "volume", 1.0*val/100, 0L);
0742 }
0743 
0744 void KGStreamerPlayer::updatePosition () {
0745     if (gst_elm_play) {
0746         do {
0747             GstMessage * msg = gst_bus_poll (gst_bus, (GstMessageType) (GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_DURATION), GST_MSECOND * 10);
0748             if (!msg)
0749                 break;
0750             gst_message_unref (msg);
0751         } while (gst_bus);
0752 
0753         mutex.lock ();
0754         if (gst_elm_play && callback) {
0755             GstFormat fmt = GST_FORMAT_TIME;
0756             gint64 val = 0; // usec
0757             if (gst_element_query_position (gst_elm_play, &fmt, &val))
0758                 callback->moviePosition (int (val / (GST_MSECOND * 100)));
0759         }
0760         mutex.unlock ();
0761         QTimer::singleShot (500, this, SLOT (updatePosition ()));
0762     }
0763 }
0764 
0765 bool KGStreamerPlayer::event (QEvent * e) {
0766     switch (e->type()) {
0767         case event_finished: {
0768             fprintf (stderr, "event_finished\n");
0769             mutex.lock ();
0770             if (gst_elm_play) {
0771                 gst_bus_set_flushing (gst_bus, true);
0772                 if (gst_bus_sync)
0773                     g_signal_handler_disconnect (gst_bus, gst_bus_sync);
0774                 if (gst_bus_async)
0775                     g_signal_handler_disconnect (gst_bus, gst_bus_async);
0776                 gst_object_unref (gst_bus);
0777                 gst_object_unref (GST_OBJECT (gst_elm_play));
0778                 gst_bus = 0L;
0779                 gst_elm_play = 0L;
0780                 color_balance = 0L;
0781                 gst_bus_sync = gst_bus_async = 0;
0782                 xoverlay = 0L;
0783             }
0784             mutex.unlock ();
0785             if (callback)
0786                 callback->finished ();
0787             else
0788                 QTimer::singleShot (0, this, SLOT (quit ()));
0789             break;
0790         }
0791                 //callback->movieParams (se->length/100, se->width, se->height, se->height ? 1.0*se->width/se->height : 1.0);
0792         case event_size: {
0793             GstSizeEvent * se = static_cast <GstSizeEvent *> (e);                
0794             fprintf (stderr, "movie parms: %d %d %d\n", se->length, se->width, se->height);
0795             if (callback) {
0796                 if (se->length < 0) se->length = 0;
0797                 callback->movieParams (se->length, se->width, se->height, se->height ? 1.0*se->width/se->height : 1.0, QStringList (), QStringList ());
0798             }
0799             if (window_created && movie_width > 0 && movie_height > 0) {
0800                 XLockDisplay (display);
0801                 XResizeWindow (display, wid, movie_width, movie_height);
0802                 XFlush (display);
0803                 XUnlockDisplay (display);
0804             }
0805             // fall through
0806         }
0807         case event_playing:
0808             notified_playing = true;
0809             if (callback)
0810                 callback->playing ();
0811             break;
0812         case event_progress:
0813             if (callback)
0814                 callback->loadingProgress
0815                     (static_cast <GstProgressEvent *> (e)->progress);
0816             break;
0817         case event_eos:
0818         case event_error:
0819             stop ();
0820             break;
0821         case event_video:
0822             if (callback)
0823                 callback->statusMessage ((int) KMPlayer::Callback::stat_hasvideo, QString ());
0824             break;
0825         default:
0826             return false;
0827     }
0828     return true;
0829 }
0830  
0831 void KGStreamerPlayer::saveState (QSessionManager & sm) {
0832     if (callback)
0833         sm.setRestartHint (QSessionManager::RestartNever);
0834 }
0835 
0836 class XEventThread : public QThread {
0837 protected:
0838     void run () {
0839         Time prev_click_time = 0;
0840         int prev_click_x = 0;
0841         int prev_click_y = 0;
0842         while (true) {
0843             XEvent   xevent;
0844             XNextEvent(display, &xevent);
0845             switch(xevent.type) {
0846                 case ClientMessage:
0847                     if (xevent.xclient.format == 8 &&
0848                             !strncmp(xevent.xclient.data.b, "quit_now", 8)) {
0849                         fprintf(stderr, "request quit\n");
0850                         return;
0851                     }
0852                     break;
0853                 case KeyPress: {
0854                     XKeyEvent  kevent;
0855                     KeySym     ksym;
0856                     char       kbuf[256];
0857                     int        len;
0858                     kevent = xevent.xkey;
0859                     XLockDisplay(display);
0860                     len = XLookupString(&kevent, kbuf, sizeof(kbuf), &ksym, NULL);
0861                     XUnlockDisplay(display);
0862                     fprintf(stderr, "keypressed 0x%x 0x%x\n", kevent.keycode, ksym);
0863                     switch (ksym) {
0864                         case XK_q:
0865                         case XK_Q:
0866                             gstapp->lock ();
0867                             gstapp->stop ();
0868                             gstapp->unlock ();
0869                             break;
0870                     }
0871                     break;
0872                 }
0873                 case Expose:
0874                     if (!xevent.xexpose.count && xevent.xexpose.window == wid) {
0875                         mutex.lock ();
0876                         if (gst_elm_play) {
0877                             GstElement *videosink;
0878                             g_object_get (gst_elm_play, "video-sink", &videosink, NULL);
0879                             if (videosink && GST_IS_X_OVERLAY (videosink)) {
0880                                 gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (videosink), wid);
0881                                 gst_x_overlay_expose (GST_X_OVERLAY (videosink));
0882                                 gst_object_unref (videosink);
0883                             }
0884                         }
0885                         mutex.unlock ();
0886                     }
0887                     break;
0888 
0889                 case ConfigureNotify:
0890                     mutex.lock ();
0891                     if (xoverlay && GST_IS_X_OVERLAY (xoverlay))
0892                         gst_x_overlay_expose (xoverlay);
0893                     mutex.unlock ();
0894                     break;
0895                 case ButtonPress: {
0896                     XButtonEvent *bev = (XButtonEvent *) &xevent;
0897                     int dx = prev_click_x - bev->x;
0898                     int dy = prev_click_y - bev->y;
0899                     if (bev->time - prev_click_time < 400 &&
0900                             (dx * dx + dy * dy) < 25) {
0901                         gstapp->lock ();
0902                         if (callback)
0903                             callback->toggleFullScreen ();
0904                         gstapp->unlock ();
0905                     }
0906                     prev_click_time = bev->time;
0907                     prev_click_x = bev->x;
0908                     prev_click_y = bev->y;
0909                     break;
0910                 }
0911                 default:
0912                     ; //if (xevent.type < LASTEvent)
0913                       //  fprintf (stderr, "event %d\n", xevent.type);
0914             }
0915         }
0916     }
0917 };
0918 
0919 int main(int argc, char **argv) {
0920     if (!XInitThreads ()) {
0921         fprintf (stderr, "XInitThreads () failed\n");
0922         return 1;
0923     }
0924     display = XOpenDisplay(NULL);
0925     screen  = XDefaultScreen(display);
0926 
0927     gst_init (NULL, NULL);
0928 
0929     gstapp = new KGStreamerPlayer (argc, argv);
0930 
0931     for(int i = 1; i < argc; i++) {
0932         if (!strcmp (argv [i], "-ao")) {
0933             ao_driver = argv [++i];
0934         } else if (!strcmp (argv [i], "-vo")) {
0935             vo_driver = argv [++i];
0936         } else if (!strcmp (argv [i], "-dvd-device") && ++i < argc) {
0937             dvd_device = argv [i];
0938         } else if (!strcmp (argv [i], "-vcd-device") && ++i < argc) {
0939             vcd_device = argv [i];
0940         } else if (!strcmp (argv [i], "-wid") || !strcmp (argv [i], "-window-id")) {
0941             wid = atol (argv [++i]);
0942             window_created = false;
0943         } else if (!strcmp (argv [i], "-root")) {
0944             wid =  XDefaultRootWindow (display);
0945             window_created = false;
0946         } else if (!strcmp (argv [i], "-window")) {
0947             ;
0948         } else if (!strcmp (argv [i], "-v")) {
0949             verbose = true;
0950         } else if (!strcmp (argv [i], "-c")) {
0951             wants_config = true;
0952         } else if (!strcmp (argv [i], "-f") && i < argc - 1) {
0953             strncpy (configfile, argv [++i], sizeof (configfile));
0954             configfile[sizeof (configfile) - 1] = 0;
0955         } else if (!strcmp (argv [i], "-loop") && i < argc - 1) {
0956             repeat_count = atol (argv [++i]);
0957         } else if (!strcmp (argv [i], "-cb")) {
0958             QString str = argv [++i];
0959             int pos = str.find ('/');
0960             if (pos > -1) {
0961                 fprintf (stderr, "callback is %s %s\n", str.left (pos).ascii (), str.mid (pos + 1).ascii ());
0962                 callback = new KMPlayer::Callback_stub 
0963                     (str.left (pos).ascii (), str.mid (pos + 1).ascii ());
0964             }
0965         } else if (!strncmp (argv [i], "-", 1)) {
0966             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]);
0967             delete gstapp;
0968             return 1;
0969         } else {
0970             mrl = QString::fromLocal8Bit (argv[i]);
0971         }
0972     }
0973 
0974     DCOPClient dcopclient;
0975     dcopclient.registerAs ("kgstreamerplayer");
0976     Backend * backend = new Backend;
0977 
0978     XEventThread * eventThread = new XEventThread;
0979     eventThread->start ();
0980 
0981     gstapp->init ();
0982 
0983     if (callback) {
0984         QByteArray buf;
0985         if (wants_config)
0986             getConfigEntries (buf);
0987         callback->started (dcopclient.appId (), buf);
0988     } else
0989         QTimer::singleShot (10, gstapp, SLOT (play (int)));
0990 
0991     gstapp->exec ();
0992 
0993     XLockDisplay(display);
0994     XClientMessageEvent ev = {
0995         ClientMessage, 0, true, display, wid, 
0996         XInternAtom (display, "XVIDEO", false), 8, {b: "quit_now"}
0997     };
0998     XSendEvent (display, wid, false, StructureNotifyMask, (XEvent *) & ev);
0999     XFlush (display);
1000     XUnlockDisplay(display);
1001     eventThread->wait (500);
1002     delete eventThread;
1003 
1004     gstapp->stop ();
1005     delete backend;
1006     delete gstapp;
1007 
1008     fprintf (stderr, "closing display\n");
1009     XCloseDisplay (display);
1010     fprintf (stderr, "done\n");
1011     fflush (stderr);
1012     return 0;
1013 }
1014 
1015 #include "gstplayer.moc"
1016 
1017 #include "moc_gstplayer.cpp"