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, ¤t_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"