File indexing completed on 2024-04-21 15:38:05

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