You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmplayer/src/xineplayer.cpp

1242 lines
45 KiB

/* This file is part of the KMPlayer application
Copyright (C) 2003 Koos Vriezen <koos.vriezen@xs4all.nl>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libgen.h>
#include <dcopclient.h>
#include <tqcstring.h>
#include <tqtimer.h>
#include <tqfile.h>
#include <tqurl.h>
#include <tqthread.h>
#include <tqmutex.h>
#include <tqdom.h>
#include "kmplayer_backend.h"
#include "kmplayer_callback_stub.h"
#include "kmplayer_callback.h"
#include "xineplayer.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <xine.h>
#include <xine/xineutils.h>
#ifndef XShmGetEventBase
extern int XShmGetEventBase(Display *);
#endif
#define MWM_HINTS_DECORATIONS (1L << 1)
#define PROP_MWM_HINTS_ELEMENTS 5
typedef struct {
uint32_t flags;
uint32_t functions;
uint32_t decorations;
int32_t input_mode;
uint32_t status;
} MWMHints;
static KXinePlayer * xineapp;
static KMPlayer::Callback_stub * callback;
static TQMutex mutex (true);
static xine_t *xine;
static xine_stream_t *stream;
static xine_stream_t *sub_stream;
static xine_video_port_t *vo_port;
static xine_audio_port_t *ao_port;
static xine_post_t *post_plugin;
static xine_event_queue_t *event_queue;
static xine_cfg_entry_t audio_vis_cfg_entry;
static x11_visual_t vis;
static char configfile[2048];
static Atom quit_atom;
static Display *display;
static Window wid;
static bool window_created;
static bool xine_verbose;
static bool xine_vverbose;
static bool wants_config;
static bool audio_vis;
static int screen;
static int completion_event;
static int repeat_count;
static int xpos, ypos, width, height;
static int movie_width, movie_height, movie_length, movie_pos;
static int movie_brightness = 32767;
static int movie_contrast = 32767;
static int movie_hue = 32767;
static int movie_saturation = 32767;
static int movie_volume = 32767;
static double pixel_aspect;
static int running = 0;
static volatile int firstframe = 0;
static const int event_finished = TQEvent::User;
static const int event_progress = TQEvent::User + 2;
static const int event_url = TQEvent::User + 3;
static const int event_size = TQEvent::User + 4;
static const int event_title = TQEvent::User + 5;
static const int event_video = TQEvent::User + 6;
static TQString mrl;
static TQString sub_mrl;
static TQString rec_mrl;
static TQString alang, slang;
static TQStringList alanglist, slanglist;
static TQString elmentry ("entry");
static TQString elmitem ("item");
static TQString attname ("name");
static TQString atttype ("type");
static TQString attdefault ("DEFAULT");
static TQString attvalue ("value");
static TQString attstart ("START");
static TQString attend ("end");
static TQString valrange ("range");
static TQString valnum ("num");
static TQString valbool ("bool");
static TQString valenum ("enum");
static TQString valstring ("string");
extern "C" {
static void dest_size_cb(void * /*data*/, int /*video_width*/, int /*video_height*/, double /*video_pixel_aspect*/,
int *dest_width, int *dest_height, double *dest_pixel_aspect) {
*dest_width = width;
*dest_height = height;
*dest_pixel_aspect = pixel_aspect;
}
static void frame_output_cb(void * /*data*/, int /*video_width*/, int /*video_height*/,
double /*video_pixel_aspect*/, int *dest_x, int *dest_y,
int *dest_width, int *dest_height,
double *dest_pixel_aspect, int *win_x, int *win_y) {
if (running && firstframe) {
firstframe = 0;
int pos;
fprintf(stderr, "first frame\n");
mutex.lock ();
xine_get_pos_length (stream, 0, &pos, &movie_length);
movie_width = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_WIDTH);
movie_height = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
mutex.unlock ();
TQApplication::postEvent (xineapp, new XineMovieParamEvent (movie_length, movie_width, movie_height, alanglist, slanglist, true));
}
*dest_x = 0;
*dest_y = 0;
*win_x = xpos;
*win_y = ypos;
*dest_width = width;
*dest_height = height;
*dest_pixel_aspect = pixel_aspect;
}
static void xine_config_cb (void * /*user_data*/, xine_cfg_entry_t * entry) {
fprintf (stderr, "xine_config_cb %s\n", entry->enum_values[entry->num_value]);
if (!stream)
return;
mutex.lock ();
if (post_plugin) {
xine_post_wire_audio_port (xine_get_audio_source (stream), ao_port);
xine_post_dispose (xine, post_plugin);
post_plugin = 0L;
}
if (audio_vis && strcmp (entry->enum_values[entry->num_value], "none")) {
post_plugin = xine_post_init (xine, entry->enum_values[entry->num_value], 0, &ao_port, &vo_port);
xine_post_wire (xine_get_audio_source (stream), (xine_post_in_t *) xine_post_input (post_plugin, (char *) "audio in"));
}
mutex.unlock ();
}
static void event_listener(void * /*user_data*/, const xine_event_t *event) {
if (event->stream != stream)
return; // not interested in sub_stream events
switch(event->type) {
case XINE_EVENT_UI_PLAYBACK_FINISHED:
fprintf (stderr, "XINE_EVENT_UI_PLAYBACK_FINISHED\n");
if (repeat_count-- > 0)
xine_play (stream, 0, 0);
else
TQApplication::postEvent (xineapp, new TQEvent ((TQEvent::Type) event_finished));
break;
case XINE_EVENT_PROGRESS:
TQApplication::postEvent (xineapp, new XineProgressEvent (((xine_progress_data_t *) event->data)->percent));
break;
case XINE_EVENT_MRL_REFERENCE:
fprintf(stderr, "XINE_EVENT_MRL_REFERENCE %s\n",
((xine_mrl_reference_data_ext_t*)event->data)->mrl);
TQApplication::postEvent (xineapp, new XineURLEvent (TQString::fromLocal8Bit (((xine_mrl_reference_data_ext_t*)event->data)->mrl)));
break;
case XINE_EVENT_FRAME_FORMAT_CHANGE:
fprintf (stderr, "XINE_EVENT_FRAME_FORMAT_CHANGE\n");
break;
case XINE_EVENT_UI_SET_TITLE:
{
xine_ui_data_t * data = (xine_ui_data_t *) event->data;
TQApplication::postEvent(xineapp, new XineTitleEvent(data->str));
fprintf (stderr, "Set title event %s\n", data->str);
}
break;
case XINE_EVENT_UI_CHANNELS_CHANGED: {
fprintf (stderr, "Channel changed event %d\n", firstframe);
mutex.lock ();
int w = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_WIDTH);
int h = xine_get_stream_info(stream, XINE_STREAM_INFO_VIDEO_HEIGHT);
int pos, l, nr;
xine_get_pos_length (stream, 0, &pos, &l);
char * langstr = new char [66];
alanglist.clear ();
slanglist.clear ();
nr =xine_get_stream_info(stream,XINE_STREAM_INFO_MAX_AUDIO_CHANNEL);
// if nrch > 25) nrch = 25
for (int i = 0; i < nr; ++i) {
if (!xine_get_audio_lang (stream, i, langstr))
continue;
TQString ls = TQString(TQString::fromLocal8Bit (langstr)).stripWhiteSpace();
if (ls.isEmpty ())
continue;
if (!slang.isEmpty () && alang == ls)
xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, i);
alanglist.push_back (ls);
fprintf (stderr, "alang %s\n", langstr);
}
nr = xine_get_stream_info(stream, XINE_STREAM_INFO_MAX_SPU_CHANNEL);
// if nrch > 25) nrch = 25
for (int i = 0; i < nr; ++i) {
if (!xine_get_spu_lang (stream, i, langstr))
continue;
TQString ls = TQString(TQString::fromLocal8Bit (langstr)).stripWhiteSpace();
if (ls.isEmpty ())
continue;
if (!slang.isEmpty () && slang == ls)
xine_set_param (stream, XINE_PARAM_SPU_CHANNEL, i);
slanglist.push_back (ls);
fprintf (stderr, "slang %s\n", langstr);
}
delete langstr;
mutex.unlock ();
movie_width = w;
movie_height = h;
movie_length = l;
TQApplication::postEvent (xineapp, new XineMovieParamEvent (l, w, h, alanglist, slanglist, firstframe));
if (running && firstframe)
firstframe = 0;
if (window_created && w > 0 && h > 0) {
XLockDisplay (display);
XResizeWindow (display, wid, movie_width, movie_height);
XFlush (display);
XUnlockDisplay (display);
}
break;
}
case XINE_EVENT_INPUT_MOUSE_MOVE:
break;
default:
fprintf (stderr, "event_listener %d\n", event->type);
}
}
} // extern "C"
using namespace KMPlayer;
Backend::Backend ()
: DCOPObject (TQCString ("Backend")) {
}
Backend::~Backend () {}
void Backend::setURL (TQString url) {
mrl = url;
}
void Backend::setSubTitleURL (TQString url) {
sub_mrl = url;
}
void Backend::play (int repeat_count) {
xineapp->play (repeat_count);
}
void Backend::stop () {
TQTimer::singleShot (0, xineapp, TQ_SLOT (stop ()));
}
void Backend::pause () {
xineapp->pause ();
}
void Backend::seek (int pos, bool /*absolute*/) {
xineapp->seek (pos);
}
void Backend::hue (int h, bool) {
xineapp->hue (65535 * (h + 100) / 200);
}
void Backend::saturation (int s, bool) {
xineapp->saturation (65535 * (s + 100) / 200);
}
void Backend::contrast (int c, bool) {
xineapp->contrast (65535 * (c + 100) / 200);
}
void Backend::brightness (int b, bool) {
xineapp->brightness (65535 * (b + 100) / 200);
}
void Backend::volume (int v, bool) {
xineapp->volume (v);
}
void Backend::frequency (int) {
}
void Backend::setAudioLang (int id, TQString al) {
xineapp->setAudioLang (id, al);
}
void Backend::setSubtitle (int id, TQString sl) {
xineapp->setSubtitle (id, sl);
}
void Backend::quit () {
delete callback;
callback = 0L;
if (running)
stop ();
else
TQTimer::singleShot (0, tqApp, TQ_SLOT (quit ()));
}
bool updateConfigEntry (const TQString & name, const TQString & value) {
fprintf (stderr, "%s=%s\n", name.ascii (), (const char *) value.local8Bit ());
bool changed = false;
xine_cfg_entry_t cfg_entry;
if (!xine_config_lookup_entry (xine, name.ascii (), &cfg_entry))
return false;
if (cfg_entry.type == XINE_CONFIG_TYPE_STRING ||
cfg_entry.type == XINE_CONFIG_TYPE_UNKNOWN) {
changed = strcmp (cfg_entry.str_value, value.ascii ());
cfg_entry.str_value = (char *) value.ascii ();
} else {
changed = cfg_entry.num_value != value.toInt ();
cfg_entry.num_value = value.toInt ();
}
xine_config_update_entry (xine, &cfg_entry);
return changed;
}
void Backend::setConfig (TQByteArray data) {
TQString err;
int line, column;
TQDomDocument dom;
if (dom.setContent (data, false, &err, &line, &column)) {
if (dom.childNodes().length() == 1) {
for (TQDomNode node = dom.firstChild().firstChild();
!node.isNull ();
node = node.nextSibling ()) {
TQDomNamedNodeMap attr = node.attributes ();
updateConfigEntry (attr.namedItem (attname).nodeValue (),
attr.namedItem (attvalue).nodeValue ());
}
xine_config_save (xine, configfile);
} else
err = TQString ("invalid data");
}
if (callback)
callback->errorMessage (0, err);
}
bool Backend::isPlaying () {
mutex.lock ();
bool b = running &&
(xine_get_status (stream) == XINE_STATUS_PLAY) &&
(xine_get_param (stream, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE);
mutex.unlock ();
return b;
}
KXinePlayer::KXinePlayer (int _argc, char ** _argv)
: TQApplication (_argc, _argv, false) {
}
void KXinePlayer::init () {
xpos = 0;
ypos = 0;
width = 320;
height = 200;
XLockDisplay(display);
if (window_created)
wid = XCreateSimpleWindow(display, XDefaultRootWindow(display),
xpos, ypos, width, height, 1, 0, 0);
XSelectInput (display, wid,
(PointerMotionMask | ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask)); // | SubstructureNotifyMask));
XWindowAttributes attr;
XGetWindowAttributes(display, wid, &attr);
width = attr.width;
height = attr.height;
if (XShmQueryExtension(display) == True)
completion_event = XShmGetEventBase(display) + ShmCompletion;
else
completion_event = -1;
if (window_created) {
fprintf (stderr, "map %lu\n", wid);
XMapRaised(display, wid);
XSync(display, False);
}
//double d->res_h = 1.0 * DisplayWidth(display, screen) / DisplayWidthMM(display, screen);
//double d->res_v = 1.0 * DisplayHeight(display, screen) / DisplayHeightMM(display, screen);
XUnlockDisplay(display);
vis.display = display;
vis.screen = screen;
vis.d = wid;
vis.dest_size_cb = dest_size_cb;
vis.frame_output_cb = frame_output_cb;
vis.user_data = NULL;
//pixel_aspect = d->res_v / d->res_h;
//if(fabs(pixel_aspect - 1.0) < 0.01)
pixel_aspect = 1.0;
const char *const * pp = xine_list_post_plugins_typed (xine, XINE_POST_TYPE_AUDIO_VISUALIZATION);
int i;
for (i = 0; pp[i]; i++);
const char ** options = new const char * [i+2];
options[0] = "none";
for (i = 0; pp[i]; i++)
options[i+1] = pp[i];
options[i+1] = 0L;
xine_config_register_enum (xine, "audio.visualization", 0, (char ** ) options, 0L, 0L, 0, xine_config_cb, 0L);
if (!callback)
TQTimer::singleShot (10, this, TQ_SLOT (play ()));
}
KXinePlayer::~KXinePlayer () {
if (window_created) {
XLockDisplay (display);
fprintf (stderr, "unmap %lu\n", wid);
XUnmapWindow (display, wid);
XDestroyWindow(display, wid);
XSync (display, False);
XUnlockDisplay (display);
}
xineapp = 0L;
}
void getConfigEntries (TQByteArray & buf) {
xine_cfg_entry_t entry;
TQDomDocument doc;
TQDomElement root = doc.createElement (TQString ("document"));
for (int i = xine_config_get_first_entry (xine, &entry);
i;
i = xine_config_get_next_entry (xine, &entry)) {
TQDomElement elm = doc.createElement (elmentry);
elm.setAttribute (attname, TQString (entry.key));
if (entry.type == XINE_CONFIG_TYPE_STRING || entry.type == XINE_CONFIG_TYPE_UNKNOWN) {
elm.setAttribute (atttype, valstring);
elm.setAttribute (attvalue, TQString (entry.str_value));
} else {
elm.setAttribute (attdefault, TQString::number (entry.num_default));
elm.setAttribute (attvalue, TQString::number (entry.num_value));
switch (entry.type) {
case XINE_CONFIG_TYPE_RANGE:
elm.setAttribute (atttype, valrange);
elm.setAttribute (attstart, TQString::number (entry.range_min));
elm.setAttribute (attend, TQString::number (entry.range_max));
break;
case XINE_CONFIG_TYPE_ENUM:
elm.setAttribute (atttype, valenum);
for (int i = 0; entry.enum_values[i]; i++) {
TQDomElement item = doc.createElement (elmitem);
item.setAttribute (attvalue, TQString (entry.enum_values[i]));
elm.appendChild (item);
}
break;
case XINE_CONFIG_TYPE_NUM:
elm.setAttribute (atttype, valnum);
break;
case XINE_CONFIG_TYPE_BOOL:
elm.setAttribute (atttype, valbool);
break;
default:
fprintf (stderr, "unhandled config type: %d\n", entry.type);
}
}
if (entry.help)
elm.appendChild (doc.createTextNode (TQString::fromUtf8 (entry.help)));
root.appendChild (elm);
}
doc.appendChild (root);
TQString exp = doc.toString ();
TQCString cexp = exp.utf8 ();
buf.duplicate (cexp);
buf.resize (cexp.length ()); // strip terminating \0
}
void KXinePlayer::play (int repeat) {
fprintf (stderr, "play mrl: '%s'\n", (const char *) mrl.local8Bit ());
mutex.lock ();
repeat_count = repeat;
if (running) {
if (xine_get_status (stream) == XINE_STATUS_PLAY &&
xine_get_param (stream, XINE_PARAM_SPEED) == XINE_SPEED_PAUSE)
xine_set_param( stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
mutex.unlock ();
return;
}
movie_pos = 0;
movie_width = 0;
movie_height = 0;
if (mrl.startsWith ("cdda://"))
mrl = TQString ("cdda:/") + mrl.mid (7);
stream = xine_stream_new (xine, ao_port, vo_port);
event_queue = xine_event_new_queue (stream);
xine_event_create_listener_thread (event_queue, event_listener, NULL);
if (mrl == "cdda:/") {
int nr;
const char * const* mrls = xine_get_autoplay_mrls (xine, "CD", &nr);
running = 1;
for (int i = 0; i < nr; i++) {
TQString m (mrls[i]);
TQString title;
if (xine_open (stream, mrls[i])) {
const char * t = xine_get_meta_info (stream, XINE_META_INFO_TITLE);
if (t && t[0])
title = TQString::fromUtf8 (t);
xine_close (stream);
}
if (callback)
callback->subMrl (m, title);
else
printf ("track %s\n", m.utf8 ().data ());
}
mutex.unlock ();
finished ();
return;
}
xine_port_send_gui_data(vo_port, XINE_GUI_SEND_VIDEOWIN_VISIBLE, (void *) 1);
running = 1;
TQString mrlsetup = mrl;
if (!rec_mrl.isEmpty ()) {
char * rm = strdup (rec_mrl.local8Bit ());
char *bn = basename (rm);
char *dn = dirname (rm);
if (bn)
updateConfigEntry (TQString ("media.capture.save_dir"), TQString::fromLocal8Bit (dn));
mrlsetup += TQString ("#save:") + TQString::fromLocal8Bit (bn);
free (rm);
}
if (!xine_open (stream, (const char *) mrlsetup.local8Bit ())) {
fprintf(stderr, "Unable to open mrl '%s'\n", (const char *) mrl.local8Bit ());
mutex.unlock ();
finished ();
return;
}
xine_set_param (stream, XINE_PARAM_VO_SATURATION, movie_saturation);
xine_set_param (stream, XINE_PARAM_VO_BRIGHTNESS, movie_brightness);
xine_set_param (stream, XINE_PARAM_VO_CONTRAST, movie_contrast);
xine_set_param (stream, XINE_PARAM_VO_HUE, movie_hue);
if (!sub_mrl.isEmpty ()) {
fprintf(stderr, "Using subtitles from '%s'\n", (const char *) sub_mrl.local8Bit ());
sub_stream = xine_stream_new (xine, NULL, vo_port);
if (xine_open (sub_stream, (const char *) sub_mrl.local8Bit ())) {
xine_stream_master_slave (stream, sub_stream,
XINE_MASTER_SLAVE_PLAY | XINE_MASTER_SLAVE_STOP);
} else {
fprintf(stderr, "Unable to open subtitles from '%s'\n", (const char *) sub_mrl.local8Bit ());
xine_dispose (sub_stream);
sub_stream = 0L;
}
}
if (!xine_play (stream, 0, 0)) {
fprintf(stderr, "Unable to play mrl '%s'\n", (const char *) mrl.local8Bit ());
mutex.unlock ();
finished ();
return;
}
audio_vis = false;
if (xine_get_stream_info (stream, XINE_STREAM_INFO_HAS_VIDEO))
TQApplication::postEvent(xineapp, new TQEvent((TQEvent::Type)event_video));
else
audio_vis = xine_config_lookup_entry
(xine, "audio.visualization", &audio_vis_cfg_entry);
mutex.unlock ();
if (audio_vis)
xine_config_cb (0L, &audio_vis_cfg_entry);
if (callback)
firstframe = 1;
}
void KXinePlayer::stop () {
if (!running) return;
fprintf(stderr, "stop\n");
mutex.lock ();
repeat_count = 0;
if (sub_stream)
xine_stop (sub_stream);
xine_stop (stream);
mutex.unlock ();
TQTimer::singleShot (10, this, TQ_SLOT (postFinished ()));
}
void KXinePlayer::postFinished () {
TQApplication::postEvent (xineapp, new TQEvent ((TQEvent::Type) event_finished));
}
void KXinePlayer::pause () {
if (!running) return;
mutex.lock ();
if (xine_get_status (stream) == XINE_STATUS_PLAY) {
if (xine_get_param (stream, XINE_PARAM_SPEED) == XINE_SPEED_PAUSE)
xine_set_param (stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
else
xine_set_param (stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE);
}
mutex.unlock ();
}
void KXinePlayer::finished () {
TQTimer::singleShot (10, this, TQ_SLOT (stop ()));
}
void KXinePlayer::setAudioLang (int id, const TQString & al) {
alang = al;
mutex.lock ();
if (xine_get_status (stream) == XINE_STATUS_PLAY)
xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, id);
mutex.unlock ();
}
void KXinePlayer::setSubtitle (int id, const TQString & sl) {
slang = sl;
mutex.lock ();
if (xine_get_status (stream) == XINE_STATUS_PLAY)
xine_set_param (stream, XINE_PARAM_SPU_CHANNEL, id);
mutex.unlock ();
}
void KXinePlayer::updatePosition () {
if (!running || !callback) return;
int pos;
mutex.lock ();
xine_get_pos_length (stream, 0, &pos, &movie_length);
mutex.unlock ();
if (movie_pos != pos) {
movie_pos = pos;
callback->moviePosition (pos/100);
}
TQTimer::singleShot (500, this, TQ_SLOT (updatePosition ()));
}
void KXinePlayer::saturation (int val) {
movie_saturation = val;
if (running) {
mutex.lock ();
xine_set_param (stream, XINE_PARAM_VO_SATURATION, val);
mutex.unlock ();
}
}
void KXinePlayer::hue (int val) {
movie_hue = val;
if (running) {
mutex.lock ();
xine_set_param (stream, XINE_PARAM_VO_HUE, val);
mutex.unlock ();
}
}
void KXinePlayer::contrast (int val) {
movie_contrast = val;
if (running) {
mutex.lock ();
xine_set_param (stream, XINE_PARAM_VO_CONTRAST, val);
mutex.unlock ();
}
}
void KXinePlayer::brightness (int val) {
movie_brightness = val;
if (running) {
mutex.lock ();
xine_set_param (stream, XINE_PARAM_VO_BRIGHTNESS, val);
mutex.unlock ();
}
}
void KXinePlayer::volume (int val) {
movie_volume = val;
if (running) {
mutex.lock ();
xine_set_param( stream, XINE_PARAM_AUDIO_VOLUME, val);
mutex.unlock ();
}
}
void KXinePlayer::seek (int val) {
if (running) {
fprintf(stderr, "seek %d\n", val);
mutex.lock ();
if (!xine_play (stream, 0, val * 100)) {
fprintf(stderr, "Unable to seek to %d :-(\n", val);
}
mutex.unlock ();
}
}
bool KXinePlayer::event (TQEvent * e) {
switch (e->type()) {
case event_finished: {
fprintf (stderr, "event_finished\n");
if (audio_vis) {
audio_vis_cfg_entry.num_value = 0;
xine_config_cb (0L, &audio_vis_cfg_entry);
}
mutex.lock ();
running = 0;
firstframe = 0;
if (sub_stream) {
xine_dispose (sub_stream);
sub_stream = 0L;
}
if (stream) {
xine_event_dispose_queue (event_queue);
xine_dispose (stream);
stream = 0L;
}
mutex.unlock ();
//XLockDisplay (display);
//XClearWindow (display, wid);
//XUnlockDisplay (display);
if (callback)
callback->finished ();
else
TQTimer::singleShot (0, this, TQ_SLOT (quit ()));
break;
}
case event_size: {
if (callback) {
XineMovieParamEvent * se = static_cast <XineMovieParamEvent *> (e);
if (se->length < 0) se->length = 0;
callback->movieParams (se->length/100, se->width, se->height, se->height ? 1.0*se->width/se->height : 1.0, se->alang, se->slang);
if (se->first_frame) {
callback->playing ();
TQTimer::singleShot (500, this, TQ_SLOT (updatePosition ()));
}
}
break;
}
case event_progress: {
XineProgressEvent * pe = static_cast <XineProgressEvent *> (e);
if (callback)
callback->loadingProgress (pe->progress);
break;
}
case event_url: {
XineURLEvent * ue = static_cast <XineURLEvent *> (e);
if (callback)
callback->subMrl (ue->url, TQString ());
break;
}
case event_title: {
XineTitleEvent * ue = static_cast <XineTitleEvent *> (e);
if (callback)
callback->statusMessage ((int) KMPlayer::Callback::stat_newtitle, ue->title);
break;
}
case event_video:
if (callback)
callback->statusMessage ((int) KMPlayer::Callback::stat_hasvideo, TQString ());
break;
default:
return false;
}
return true;
}
void KXinePlayer::saveState (TQSessionManager & sm) {
if (callback)
sm.setRestartHint (TQSessionManager::RestartNever);
}
XineMovieParamEvent::XineMovieParamEvent(int l, int w, int h, const TQStringList & a, const TQStringList & s, bool ff)
: TQEvent ((TQEvent::Type) event_size),
length (l), width (w), height (h), alang (a), slang (s) , first_frame (ff)
{}
XineURLEvent::XineURLEvent (const TQString & u)
: TQEvent ((TQEvent::Type) event_url), url (u)
{}
XineTitleEvent::XineTitleEvent (const char * t)
: TQEvent ((TQEvent::Type) event_title), title (TQString::fromUtf8 (t))
{
TQUrl::decode (title);
}
XineProgressEvent::XineProgressEvent (const int p)
: TQEvent ((TQEvent::Type) event_progress), progress (p)
{}
//static bool translateCoordinates (int wx, int wy, int mx, int my) {
// movie_width
class XEventThread : public TQThread {
protected:
void run () {
Time prev_click_time = 0;
int prev_click_x = 0;
int prev_click_y = 0;
while (true) {
XEvent xevent;
XNextEvent(display, &xevent);
switch(xevent.type) {
case ClientMessage:
if (xevent.xclient.message_type == quit_atom) {
fprintf(stderr, "request quit\n");
return;
}
break;
case KeyPress:
{
XKeyEvent kevent;
KeySym ksym;
char kbuf[256];
int len;
kevent = xevent.xkey;
XLockDisplay(display);
len = XLookupString(&kevent, kbuf, sizeof(kbuf), &ksym, NULL);
XUnlockDisplay(display);
fprintf(stderr, "keypressed 0x%x 0x%x\n", kevent.keycode, ksym);
switch (ksym) {
case XK_q:
case XK_Q:
xineapp->lock ();
xineapp->stop ();
xineapp->unlock ();
break;
case XK_p: // previous
mutex.lock ();
if (stream) {
xine_event_t xine_event;
memset(&xine_event, 0, sizeof(xine_event));
xine_event.type = XINE_EVENT_INPUT_PREVIOUS;
xine_event.stream = stream;
xine_event_send (stream, &xine_event);
}
mutex.unlock ();
break;
case XK_n: // next
mutex.lock ();
if (stream) {
xine_event_t xine_event;
memset(&xine_event, 0, sizeof(xine_event));
xine_event.type = XINE_EVENT_INPUT_NEXT;
xine_event.stream = stream;
xine_event_send (stream, &xine_event);
}
mutex.unlock ();
break;
case XK_u: // up menu
mutex.lock ();
if (stream) {
xine_event_t xine_event;
memset(&xine_event, 0, sizeof(xine_event));
xine_event.type = XINE_EVENT_INPUT_MENU1;
xine_event.stream = stream;
xine_event_send (stream, &xine_event);
}
mutex.unlock ();
break;
case XK_r: // root menu
mutex.lock ();
if (stream) {
xine_event_t xine_event;
memset(&xine_event, 0, sizeof(xine_event));
xine_event.type = XINE_EVENT_INPUT_MENU3;
xine_event.stream = stream;
xine_event_send (stream, &xine_event);
}
mutex.unlock ();
break;
case XK_Up:
xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME,
(xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) + 1));
break;
case XK_Down:
xine_set_param(stream, XINE_PARAM_AUDIO_VOLUME,
(xine_get_param(stream, XINE_PARAM_AUDIO_VOLUME) - 1));
break;
case XK_plus:
xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL,
(xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) + 1));
break;
case XK_minus:
xine_set_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL,
(xine_get_param(stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) - 1));
break;
case XK_space:
if(xine_get_param(stream, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE)
xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE);
else
xine_set_param(stream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL);
break;
}
}
break;
case Expose:
if(xevent.xexpose.count != 0 || !stream || xevent.xexpose.window != wid)
break;
mutex.lock ();
xine_port_send_gui_data(vo_port, XINE_GUI_SEND_EXPOSE_EVENT, &xevent);
mutex.unlock ();
break;
case ConfigureNotify:
{
Window tmp_win;
width = xevent.xconfigure.width;
height = xevent.xconfigure.height;
if((xevent.xconfigure.x == 0) && (xevent.xconfigure.y == 0)) {
XLockDisplay(display);
XTranslateCoordinates(display, xevent.xconfigure.window,
DefaultRootWindow(xevent.xconfigure.display),
0, 0, &xpos, &ypos, &tmp_win);
XUnlockDisplay(display);
}
else {
xpos = xevent.xconfigure.x;
ypos = xevent.xconfigure.y;
}
}
break;
case MotionNotify:
if (stream) {
XMotionEvent *mev = (XMotionEvent *) &xevent;
x11_rectangle_t rect = { mev->x, mev->y, 0, 0 };
if (xine_port_send_gui_data(vo_port, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*) &rect) == -1)
break;
xine_input_data_t data;
data.x = rect.x;
data.y = rect.y;
data.button = 0;
xine_event_t xine_event;
memset(&xine_event, 0, sizeof(xine_event));
xine_event.type = XINE_EVENT_INPUT_MOUSE_MOVE;
xine_event.stream = stream;
xine_event.data = &data;
xine_event.data_length = sizeof (xine_input_data_t);
mutex.lock ();
xine_event_send (stream, &xine_event);
mutex.unlock ();
}
break;
case ButtonPress: {
XButtonEvent *bev = (XButtonEvent *) &xevent;
int dx = prev_click_x - bev->x;
int dy = prev_click_y - bev->y;
if (bev->time - prev_click_time < 400 &&
(dx * dx + dy * dy) < 25) {
xineapp->lock ();
if (callback)
callback->toggleFullScreen ();
xineapp->unlock ();
}
prev_click_time = bev->time;
prev_click_x = bev->x;
prev_click_y = bev->y;
if (stream) {
fprintf(stderr, "ButtonPress\n");
XButtonEvent *bev = (XButtonEvent *) &xevent;
x11_rectangle_t rect = { bev->x, bev->y, 0, 0 };
if (xine_port_send_gui_data(vo_port, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*) &rect) == -1)
break;
xine_input_data_t data;
data.x = rect.x;
data.y = rect.y;
data.button = 1;
xine_event_t xine_event;
memset(&xine_event, 0, sizeof(xine_event));
xine_event.type = XINE_EVENT_INPUT_MOUSE_BUTTON;
xine_event.stream = stream;
xine_event.data = &data;
xine_event.data_length = sizeof (xine_input_data_t);
mutex.lock ();
xine_event_send (stream, &xine_event);
mutex.unlock ();
}
break;
}
case NoExpose:
//fprintf (stderr, "NoExpose %lu\n", xevent.xnoexpose.drawable);
break;
case CreateNotify:
fprintf (stderr, "CreateNotify: %lu %lu %d,%d %dx%d\n",
xevent.xcreatewindow.window, xevent.xcreatewindow.parent,
xevent.xcreatewindow.x, xevent.xcreatewindow.y,
xevent.xcreatewindow.width, xevent.xcreatewindow.height);
break;
case DestroyNotify:
fprintf (stderr, "DestroyNotify: %lu\n", xevent.xdestroywindow.window);
break;
default:
if (xevent.type < LASTEvent)
fprintf (stderr, "event %d\n", xevent.type);
}
if(xevent.type == completion_event && stream)
xine_port_send_gui_data(vo_port, XINE_GUI_SEND_COMPLETION_EVENT, &xevent);
}
}
};
int main(int argc, char **argv) {
const char *dvd_device = 0L;
const char *vcd_device = 0L;
const char *grab_device = 0L;
if (!XInitThreads ()) {
fprintf (stderr, "XInitThreads () failed\n");
return 1;
}
display = XOpenDisplay(NULL);
screen = XDefaultScreen(display);
quit_atom = XInternAtom (display, "kxineplayer_quit", false);
snprintf(configfile, sizeof (configfile), "%s%s", xine_get_homedir(), "/.xine/config2");
xineapp = new KXinePlayer (argc, argv);
window_created = true;
TQString vo_driver ("auto");
TQString ao_driver ("auto");
for (int i = 1; i < argc; i++) {
if (!strcmp (argv [i], "-vo") && ++i < argc) {
vo_driver = argv [i];
} else if (!strcmp (argv [i], "-ao") && ++i < argc) {
ao_driver = argv [i];
} else if (!strcmp (argv [i], "-dvd-device") && ++i < argc) {
dvd_device = argv [i];
} else if (!strcmp (argv [i], "-vcd-device") && ++i < argc) {
vcd_device = argv [i];
} else if (!strcmp (argv [i], "-vd") && ++i < argc) {
grab_device = argv [i];
} else if ((!strcmp (argv [i], "-wid") ||
!strcmp (argv [i], "-window-id")) && ++i < argc) {
wid = atol (argv [i]);
window_created = false;
} else if (!strcmp (argv [i], "-root")) {
wid = XDefaultRootWindow (display);
window_created = false;
} else if (!strcmp (argv [i], "-window")) {
;
} else if (!strcmp (argv [i], "-sub") && ++i < argc) {
sub_mrl = TQString (argv [i]);
} else if (!strcmp (argv [i], "-lang") && ++i < argc) {
slang = alang = TQString (argv [i]);
} else if (!strcmp (argv [i], "-v")) {
xine_verbose = true;
} else if (!strcmp (argv [i], "-vv")) {
xine_verbose = xine_vverbose = true;
} else if (!strcmp (argv [i], "-c")) {
wants_config = true;
} else if (!strcmp (argv [i], "-f") && ++i < argc) {
strncpy (configfile, argv [i], sizeof (configfile));
configfile[sizeof (configfile) - 1] = 0;
} else if (!strcmp (argv [i], "-cb") && ++i < argc) {
TQString str = argv [i];
int pos = str.find ('/');
if (pos > -1) {
fprintf (stderr, "callback is %s %s\n", str.left (pos).ascii (), str.mid (pos + 1).ascii ());
callback = new KMPlayer::Callback_stub
(str.left (pos).ascii (), str.mid (pos + 1).ascii ());
}
} else if (!strcmp (argv [i], "-rec") && i < argc - 1) {
rec_mrl = TQString::fromLocal8Bit (argv [++i]);
} else if (!strcmp (argv [i], "-loop") && i < argc - 1) {
repeat_count = atol (argv [++i]);
} else {
if (mrl.startsWith ("-session")) {
delete xineapp;
return 1;
}
mrl = TQString::fromLocal8Bit (argv [i]);
}
}
bool config_changed = !TQFile (configfile).exists ();
if (!callback && mrl.isEmpty ()) {
fprintf (stderr, "usage: %s [-vo (xv|xshm)] [-ao (arts|esd|..)] "
"[-f <xine config file>] [-dvd-device <device>] "
"[-vcd-device <device>] [-vd <video device>] "
"[-wid <X11 Window>|-window-id <X11 Window>|-root] "
"[-sub <subtitle url>] [-lang <lang>] [(-v|-vv)] "
"[-cb <DCOP callback name> [-c]] "
"[-loop <repeat>] [<url>]\n", argv[0]);
delete xineapp;
return 1;
}
XEventThread * eventThread = new XEventThread;
eventThread->start ();
DCOPClient dcopclient;
dcopclient.registerAs ("kxineplayer");
Backend player;
xine = xine_new();
if (xine_verbose)
xine_engine_set_param (xine, XINE_ENGINE_PARAM_VERBOSITY, xine_vverbose ? XINE_VERBOSITY_DEBUG : XINE_VERBOSITY_LOG);
xine_config_load(xine, configfile);
xine_init(xine);
xineapp->init ();
if (dvd_device)
config_changed |= updateConfigEntry (TQString ("input.dvd_device"), TQString (dvd_device));
if (vcd_device)
config_changed |= updateConfigEntry (TQString ("input.vcd_device"), TQString (vcd_device));
if (grab_device)
config_changed |= updateConfigEntry (TQString ("media.video4linux.video_device"), TQString (grab_device));
if (config_changed)
xine_config_save (xine, configfile);
TQStringList vos = TQStringList::split (',', vo_driver);
for (int i = 0; i < vos.size (); i++) {
if (vos[i] == "x11")
vos[i] = "xshm";
else if (vos[i] == "gl")
vos[i] = "opengl";
fprintf (stderr, "trying video driver %s ..\n", vos[i].ascii ());
vo_port = xine_open_video_driver(xine, vos[i].ascii (),
XINE_VISUAL_TYPE_X11, (void *) &vis);
if (vo_port)
break;
}
if (!vo_port)
fprintf (stderr, "no video driver found\n");
TQStringList aos = TQStringList::split (',', ao_driver);
for (int i = 0; i < aos.size (); i++) {
fprintf (stderr, "trying audio driver %s ..\n", aos[i].ascii ());
ao_port = xine_open_audio_driver (xine, aos[i].ascii (), NULL);
if (ao_port)
break;
}
if (!ao_port)
fprintf (stderr, "audio driver initialisation failed\n");
stream = xine_stream_new (xine, ao_port, vo_port);
TQByteArray buf;
if (wants_config) {
/* TODO? Opening the output drivers in front, will add more config
settings. Unfortunately, that also adds a second in startup..
const char *const * aops = xine_list_audio_output_plugins (xine);
for (const char *const* aop = aops; *aop; aop++) {
xine_audio_port_t * ap = xine_open_audio_driver (xine, *aop, 0L);
xine_close_audio_driver (xine, ap);
fprintf (stderr, "audio output: %s\n", *aop);
}
const char *const * vops = xine_list_video_output_plugins (xine);
for (const char *const* vop = vops; *vop; vop++) {
xine_video_port_t * vp = xine_open_video_driver (xine, *vop, XINE_VISUAL_TYPE_NONE, 0L);
xine_close_video_driver (xine, vp);
fprintf (stderr, "vidio output: %s\n", *vop);
}*/
getConfigEntries (buf);
}
if (callback)
callback->started (dcopclient.appId (), buf);
else
;//printf ("%s\n", TQString (buf).ascii ());
xineapp->exec ();
if (sub_stream)
xine_dispose (sub_stream);
if (stream) {
xine_event_dispose_queue (event_queue);
xine_dispose (stream);
}
if (ao_port)
xine_close_audio_driver (xine, ao_port);
if (vo_port)
xine_close_video_driver (xine, vo_port);
XLockDisplay(display);
XEvent ev;
ev.xclient.type = ClientMessage;
ev.xclient.serial = 0;
ev.xclient.send_event = true;
ev.xclient.display = display;
ev.xclient.window = wid;
ev.xclient.message_type = quit_atom;
ev.xclient.format = 8;
ev.xclient.data.b[0] = 0;
XSendEvent (display, wid, false, StructureNotifyMask, &ev);
XFlush (display);
XUnlockDisplay(display);
eventThread->wait (500);
delete eventThread;
xineapp->stop ();
delete xineapp;
xine_exit (xine);
fprintf (stderr, "closing display\n");
XCloseDisplay (display);
fprintf (stderr, "done\n");
return 0;
}
#include "xineplayer.moc"