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