/*************************************************************************** kxinewidget.cpp A KDE/QT API for the xine-lib. Define USE_QT_ONLY if you use QT only. ------------------- begin : Fre Apr 18 2003 revision : $Revision: 1.1 $ last modified : $Date: 2004/11/24 00:52:07 $ by $Author: cookiem2 $ copyright : (C) 2003-2004 by Jürgen Kofler email : kaffeine@gmx.net ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kxinewidget.h" #ifndef USE_QT_ONLY #include "kxinewidget.moc" #include #include #endif #define TIMER_EVENT_PLAYBACK_FINISHED 100 #define TIMER_EVENT_NEW_CHANNELS 101 #define TIMER_EVENT_NEW_TITLE 102 #define TIMER_EVENT_NEW_STATUS 103 #define TIMER_EVENT_CHANGE_CURSOR 104 #define TIMER_EVENT_NEW_MRL_REFERENCE 105 #define TIMER_EVENT_NEW_XINE_MESSAGE 106 #define TIMER_EVENT_NEW_XINE_ERROR 107 #define TIMER_EVENT_FRAME_FORMAT_CHANGE 108 #define TIMER_EVENT_RESTART_PLAYBACK 200 #define TIMER_EVENT_RESIZE_PARENT 300 KXineWidget::KXineWidget(QWidget* parent, const char* name, const QString& pathToConfigFile, const QString& pathToLogoFile, const QString& audioDriver, const QString& videoDriver, bool startManual, bool verbose) : QWidget(parent,name), QThread(), m_startXineManual(startManual), m_xineReady(false), m_logoFile(pathToLogoFile), m_preferedAudio(audioDriver), m_preferedVideo(videoDriver), m_xineVerbose(verbose), m_xineDisplay(NULL), m_xineEngine(NULL), m_audioDriver(NULL), m_videoDriver(NULL), m_xineStream(NULL), m_eventQueue(NULL), m_osd(NULL), m_osdUnscaled(false), m_osdShow(false), #ifndef USE_QT_ONLY m_videoFiltersEnabled(true), m_audioFiltersEnabled(true), m_deinterlaceFilter(NULL), m_deinterlaceEnabled(false), m_visualPlugin(NULL), #else m_xinePost(NULL), m_postAudioSource(NULL), m_postInput(NULL), #endif m_visualPluginName(QString::null), m_currentSpeed(Normal), m_softwareMixer(false), m_currentZoom(100), m_currentAudio(0), m_currentSub(0), m_autoresizeEnabled(false) { setMinimumSize(QSize(20,20)); // set a size hint setPaletteBackgroundColor(QColor(0,0,0)); //black if (pathToConfigFile.isNull()) { debugOut("Using default config file ~/.xine/config"); m_configFilePath = QDir::homeDirPath(); m_configFilePath.append("/.xine/config"); } else m_configFilePath = pathToConfigFile; if (!m_logoFile.isNull()) appendToQueue(m_logoFile); #ifndef USE_QT_ONLY m_videoFilterList.setAutoDelete(true); /*** delete post plugin on removing from list ***/ m_audioFilterList.setAutoDelete(true); /*** delete post plugin on removing from list ***/ #endif connect(&m_posTimer, SIGNAL(timeout()), this, SLOT(slotSendPosition())); connect(&m_lengthInfoTimer, SIGNAL(timeout()), this, SLOT(slotEmitLengthInfo())); connect(&m_mouseHideTimer, SIGNAL(timeout()), this, SLOT(slotHideMouse())); connect(&m_osdTimer, SIGNAL(timeout()), this, SLOT(slotOSDHide())); setUpdatesEnabled(false); setMouseTracking(true); } KXineWidget::~KXineWidget() { /* "careful" shutdown, maybe xine initialization was not successful */ m_xineReady = false; /* stop all timers */ m_posTimer.stop(); m_mouseHideTimer.stop(); #ifdef USE_QT_ONLY slotRemoveAllAudioFilters(); slotRemoveAllVideoFilters(); #endif if (m_osd) xine_osd_free(m_osd); if (m_xineStream) xine_close(m_xineStream); if (running()) { XEvent ev; ev.type = Expose; ev.xexpose.display = m_xineDisplay; ev.xexpose.window = winId(); ev.xexpose.x = x(); ev.xexpose.y = y(); ev.xexpose.width = width(); ev.xexpose.height = height(); ev.xexpose.count = 0; XSendEvent( x11Display(), winId(), False, ExposureMask, &ev ); /* send a fake expose event */ XFlush(x11Display()); if( !wait(1000) ) /* wait a second for thread exiting */ { warningOut("XEvent thread don't exit. Terminating it..."); terminate(); } } debugOut("Shut down xine engine"); #ifndef USE_QT_ONLY if (m_deinterlaceFilter) { debugOut("Unwire video filters"); unwireVideoFilters(); delete m_deinterlaceFilter; m_deinterlaceFilter = NULL; } if (m_visualPlugin) { debugOut("Unwire audio filters"); unwireAudioFilters(); debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName )); delete m_visualPlugin; m_visualPlugin = NULL; } #else if (m_xinePost) { debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName)); m_postAudioSource = xine_get_audio_source(m_xineStream); xine_post_wire_audio_port(m_postAudioSource, m_audioDriver); xine_post_dispose(m_xineEngine, m_xinePost); } #endif if (m_eventQueue) { debugOut("Dispose event queue"); xine_event_dispose_queue(m_eventQueue); } if (m_xineStream) { debugOut("Dispose stream"); xine_dispose(m_xineStream); } if (m_audioDriver) { debugOut("Close audio driver"); xine_close_audio_driver(m_xineEngine, m_audioDriver); } if (m_videoDriver) { debugOut("Close video driver"); xine_close_video_driver(m_xineEngine, m_videoDriver); } if (m_xineEngine) { saveXineConfig(); debugOut("Close xine engine"); xine_exit(m_xineEngine); } m_xineEngine = NULL; if (m_xineDisplay) { debugOut("Close xine display"); XCloseDisplay(m_xineDisplay); /* close xine display */ } m_xineDisplay = NULL; debugOut("xine closed"); } void KXineWidget::saveXineConfig() { debugOut("Set CD/VCD/DVD path back"); xine_cfg_entry_t config; if (!m_cachedCDPath.isNull()) { xine_config_lookup_entry (m_xineEngine, "input.cdda_device", &config); config.str_value = (char*)m_cachedCDPath.latin1(); xine_config_update_entry (m_xineEngine, &config); } if (!m_cachedVCDPath.isNull()) { xine_config_lookup_entry (m_xineEngine, "input.vcd_device", &config); config.str_value = (char*)m_cachedVCDPath.latin1(); xine_config_update_entry (m_xineEngine, &config); } if (!m_cachedDVDPath.isNull()) { xine_config_lookup_entry (m_xineEngine, "input.dvd_device", &config); config.str_value = (char*)m_cachedDVDPath.latin1(); xine_config_update_entry (m_xineEngine, &config); } debugOut(QString("Save xine config to: %1").arg(m_configFilePath)); xine_config_save(m_xineEngine, m_configFilePath); } /*************************************************** * CALLBACKS ***************************************************/ void KXineWidget::destSizeCallback(void* p, int /*video_width*/, int /*video_height*/, double /*video_aspect*/, int* dest_width, int* dest_height, double* dest_aspect) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; *dest_width = vw->width(); *dest_height = vw->height(); *dest_aspect = vw->m_displayRatio; } void KXineWidget::frameOutputCallback(void* p, int video_width, int video_height, double video_aspect, int* dest_x, int* dest_y, int* dest_width, int* dest_height, double* dest_aspect, int* win_x, int* win_y) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; *dest_x = 0; *dest_y = 0 ; *dest_width = vw->width(); *dest_height = vw->height(); *win_x = vw->m_globalX; *win_y = vw->m_globalY; *dest_aspect = vw->m_displayRatio; /* correct size with video aspect */ if (video_aspect >= vw->m_displayRatio) video_width = (int) ( (double) (video_width * video_aspect / vw->m_displayRatio + 0.5) ); else video_height = (int) ( (double) (video_height * vw->m_displayRatio / video_aspect) + 0.5); /* frame size changed */ if ( (video_width != vw->m_videoFrameWidth) || (video_height != vw->m_videoFrameHeight) ) { debugOut(QString("New video frame size: %1x%2 - aspect ratio: %3").arg(video_width).arg(video_height).arg(video_aspect)); vw->m_videoFrameWidth = video_width; vw->m_videoFrameHeight = video_height; vw->m_videoAspect = video_aspect; QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_FRAME_FORMAT_CHANGE)); /* auto-resize parent widget */ if ((vw->m_autoresizeEnabled) && (vw->parentWidget()) && (vw->m_posTimer.isActive()) && (!vw->parentWidget()->isFullScreen()) && (video_width > 0) && (video_height > 0)) { vw->m_newParentSize = vw->parentWidget()->size() - QSize((vw->width() - video_width), vw->height() - video_height); debugOut(QString("Resize video window to: %1x%2").arg(vw->m_newParentSize.width()).arg(vw->m_newParentSize.height())); /* we should not do a resize() inside a xine thread, but post an event to the main thread */ QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_RESIZE_PARENT)); } } } /* * XINE EVENT THREAD * only the QT event thread should do GUI operations, * we use QApplication::postEvent() and a reimplementation of QObject::timerEvent() to * make sure all critical jobs are done within the QT main thread context * * for more information see http://doc.trolltech.com/3.1/threads.html */ void KXineWidget::xineEventListener(void *p, const xine_event_t* xineEvent) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; // KApplication::kApplication()->lock(); /* lock QT event thread */ switch (xineEvent->type) { case XINE_EVENT_UI_PLAYBACK_FINISHED: { debugOut("xine event: playback finished"); QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_PLAYBACK_FINISHED )); break; } case XINE_EVENT_UI_CHANNELS_CHANGED: /* new channel informations */ { debugOut("xine event: channels changed"); int i,j,channels; char* lang = new char[128]; /* more than enough */ QString num; vw->m_audioCh.clear(); vw->m_audioCh.append(i18n("auto")); vw->m_subCh.clear(); vw->m_subCh.append(i18n("off")); /*** get audio channels ***/ channels = QMAX(10, xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_MAX_AUDIO_CHANNEL)); for(i = 0; i < channels; i++) { if (xine_get_audio_lang(vw->m_xineStream, i, lang)) vw->m_audioCh << lang; else vw->m_audioCh << i18n("Ch ") + num.setNum(i+1); } /*** get subtitle channels ***/ channels = QMAX(10, xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_MAX_SPU_CHANNEL)); for(j = 0; j < channels; j++) { if (xine_get_spu_lang(vw->m_xineStream, j, lang)) vw->m_subCh << lang; else vw->m_subCh << i18n("Ch ") + num.setNum(j+1); } delete [] lang; vw->m_currentAudio = xine_get_param(vw->m_xineStream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL) + 1; vw->m_currentSub = xine_get_param(vw->m_xineStream, XINE_PARAM_SPU_CHANNEL) + 1; if (vw->m_currentAudio > (i-1)) { vw->slotSetAudioChannel(0); vw->m_currentAudio = -1; } if (vw->m_currentSub > (j-1)) { vw->slotSetSubtitleChannel(0); vw->m_currentSub = -1; } QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_CHANNELS)); break; } case XINE_EVENT_UI_SET_TITLE: /* set new title */ { debugOut("xine event: ui set title"); xine_ui_data_t* xd = (xine_ui_data_t*)xineEvent->data; vw->m_trackTitle = QString::fromLocal8Bit( (char*)xd->str ); QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_TITLE)); break; } case XINE_EVENT_PROGRESS: { debugOut("xine event: progress info"); xine_progress_data_t* pd = (xine_progress_data_t*)xineEvent->data; vw->m_statusString = QString::fromLocal8Bit(pd->description) + " " + QString::number(pd->percent) + "%"; QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_STATUS)); break; } case XINE_EVENT_DROPPED_FRAMES: { debugOut("xine event: dropped frames"); xine_dropped_frames_t* dropped = (xine_dropped_frames_t*)xineEvent->data; warningOut(QString("Skipped frames: %1 - discarded frames: %2").arg(dropped->skipped_frames/10).arg(dropped->discarded_frames/10)); break; } case XINE_EVENT_SPU_BUTTON: { debugOut("xine event: spu button"); xine_spu_button_t* button = (xine_spu_button_t*)xineEvent->data; if (button->direction == 1) /* enter a button */ { debugOut("DVD Menu: Mouse entered button"); vw->m_DVDButtonEntered = true; } else { debugOut("DVD Menu: Mouse leaved button"); vw->m_DVDButtonEntered = false; } QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_CHANGE_CURSOR)); break; } case XINE_EVENT_UI_NUM_BUTTONS: { debugOut("xine event: ui num buttons"); break; } case XINE_EVENT_MRL_REFERENCE: { debugOut("xine event: mrl reference"); xine_mrl_reference_data_t* mrldata = (xine_mrl_reference_data_t*)xineEvent->data; vw->m_newMRLReference = mrldata->mrl; QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_MRL_REFERENCE)); break; } case XINE_EVENT_FRAME_FORMAT_CHANGE: { // debugOut("xine event: frame format change"); // QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_FRAME_FORMAT_CHANGE)); break; } case XINE_EVENT_UI_MESSAGE: { debugOut("xine event: xine message"); xine_ui_message_data_t *data = (xine_ui_message_data_t *)xineEvent->data; QString message; /* some code take from the xine-ui - Copyright (C) 2000-2003 the xine project */ switch(data->type) { case XINE_MSG_NO_ERROR: { /* copy strings, and replace '\0' separators by '\n' */ char* s = data->messages; char* d = new char[2000]; while(s && (*s != '\0') && ((*s + 1) != '\0')) { switch(*s) { case '\0': { *d = '\n'; break; } default: { *d = *s; break; } } s++; d++; } *++d = '\0'; message = d; delete [] d; break; } case XINE_MSG_GENERAL_WARNING: { message = i18n("General Warning: \n"); if(data->explanation) message = message + ((char *) data + data->explanation) + " " + ((char *) data + data->parameters); else message = message + i18n("No Informations available."); break; } case XINE_MSG_SECURITY: { message = i18n("Security Warning: \n"); if(data->explanation) message = message + ((char *) data + data->explanation) + " " + ((char *) data + data->parameters); break; } case XINE_MSG_UNKNOWN_HOST: { message = i18n("The host you're trying to connect is unknown.\nCheck the validity of the specified hostname. "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_UNKNOWN_DEVICE: { message = i18n("The device name you specified seems invalid. "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_NETWORK_UNREACHABLE: { message = i18n("The network looks unreachable.\nCheck your network setup and the server name. "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_AUDIO_OUT_UNAVAILABLE: { message = i18n("Audio output unavailable. Device is busy. "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_CONNECTION_REFUSED: { message = i18n("The connection was refused.\nCheck the host name. "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_FILE_NOT_FOUND: { message = i18n("The specified file or url was not found. Please check it. "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_PERMISSION_ERROR: { message = i18n("Permission to this source was denied. "); // if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_READ_ERROR: { message = i18n("The source can't be read.\nMaybe you don't have enough rights for this, or source doesn't contain data (e.g: no disc in drive). "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } case XINE_MSG_LIBRARY_LOAD_ERROR: { message = i18n("A problem occur while loading a library or a decoder: "); if(data->explanation) message = message + ((char *) data + data->parameters); break; } case XINE_MSG_ENCRYPTED_SOURCE: { message = i18n("The source seems encrypted, and can't be read. "); if (vw->m_trackURL.contains("dvd:/")) message = message + i18n("\nYour DVD is probably crypted. According to your country laws, you can or can't use libdvdcss to be able to read this disc. "); if(data->explanation) message = message + "(" + ((char *) data + data->parameters) + ")"; break; } default: { message = i18n("Unknown error: \n"); if(data->explanation) message = message + ((char *) data + data->explanation) + " " + ((char *) data + data->parameters); break; } } vw->m_xineMessage = message; QApplication::postEvent(vw, new QTimerEvent( TIMER_EVENT_NEW_XINE_MESSAGE)); break; } default: { //debugOut("xine event: unhandled type "); break; } } } void KXineWidget::timerEvent( QTimerEvent* tevent ) { switch ( tevent->timerId() ) { case TIMER_EVENT_PLAYBACK_FINISHED: { if (isQueueEmpty()) { if (m_trackURL != m_logoFile) emit signalPlaybackFinished(); else xine_stop(m_xineStream); } else QTimer::singleShot(0, this, SLOT(slotPlay())); break; } case TIMER_EVENT_NEW_CHANNELS: { emit signalNewChannels(m_audioCh, m_subCh, m_currentAudio, m_currentSub); break; } case TIMER_EVENT_NEW_TITLE: { emit signalTitleChanged(); break; } case TIMER_EVENT_FRAME_FORMAT_CHANGE: { if ((m_trackHasVideo) && (m_trackURL != m_logoFile)) emit signalVideoSizeChanged(); break; } case TIMER_EVENT_NEW_STATUS: { emit signalXineStatus(m_statusString); break; } case TIMER_EVENT_CHANGE_CURSOR: { if (m_DVDButtonEntered) setCursor(QCursor(Qt::PointingHandCursor)); else setCursor(QCursor(Qt::ArrowCursor)); break; } case TIMER_EVENT_NEW_MRL_REFERENCE: { m_queue.prepend(m_newMRLReference ); break; } case TIMER_EVENT_NEW_XINE_MESSAGE: { emit signalXineMessage(m_xineMessage); break; } case TIMER_EVENT_NEW_XINE_ERROR: { emit signalXineError(m_xineError); break; } case TIMER_EVENT_RESTART_PLAYBACK: { appendToQueue(m_trackURL); slotPlay(); slotSeekToPosition(m_savedPos); break; } case TIMER_EVENT_RESIZE_PARENT: { parentWidget()->resize(m_newParentSize); break; } default: break; } } /******************* new video driver *********************/ void KXineWidget::videoDriverChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; if (entry == NULL) return; #ifndef USE_QT_ONLY KXineWidget* vw = (KXineWidget*) p; xine_video_port_t* oldVideoDriver = vw->m_videoDriver; xine_video_port_t* noneVideoDriver; int pos, time, length; debugOut(QString("New video driver: %1").arg(entry->enum_values[entry->num_value])); if (vw->m_osd) xine_osd_free(vw->m_osd); noneVideoDriver = xine_open_video_driver(vw->m_xineEngine, "none", XINE_VISUAL_TYPE_NONE, NULL); if (!noneVideoDriver) { errorOut("Can't init Video Driver 'none', operation aborted."); return; } bool playing = false; if (vw->isPlaying()) { playing = true; vw->m_savedPos = 0; if (xine_get_pos_length(vw->m_xineStream, &pos, &time, &length)) vw->m_savedPos = pos; } xine_close(vw->m_xineStream); /* wire filters to "none" driver so the old one can be safely disposed */ vw->m_videoDriver = noneVideoDriver; vw->unwireVideoFilters(); vw->wireVideoFilters(); vw->unwireAudioFilters(); if (vw->m_visualPlugin) { debugOut(QString("Dispose visual plugin: %1").arg(vw->m_visualPluginName)); delete vw->m_visualPlugin; vw->m_visualPlugin = NULL; } xine_event_dispose_queue(vw->m_eventQueue); xine_dispose(vw->m_xineStream); xine_close_video_driver(vw->m_xineEngine, oldVideoDriver); vw->m_videoDriver = xine_open_video_driver(vw->m_xineEngine, entry->enum_values[entry->num_value], XINE_VISUAL_TYPE_X11, (void *) &(vw->m_x11Visual)); if (!vw->m_videoDriver) { vw->m_xineError = i18n("Error: Can't init new Video Driver %1 - using %2!").arg(entry->enum_values[entry->num_value]).arg(vw->m_videoDriverName); QApplication::postEvent(vw, new QTimerEvent( TIMER_EVENT_NEW_XINE_ERROR)); playing = false; vw->m_videoDriver = xine_open_video_driver(vw->m_xineEngine, vw->m_videoDriverName, XINE_VISUAL_TYPE_X11, (void *) &(vw->m_x11Visual)); } else { vw->m_videoDriverName = entry->enum_values[entry->num_value]; vw->m_statusString = i18n("Using Video Driver: ") + vw->m_videoDriverName; QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_STATUS)); } vw->m_xineStream = xine_stream_new(vw->m_xineEngine, vw->m_audioDriver, vw->m_videoDriver); vw->m_eventQueue = xine_event_new_queue (vw->m_xineStream); xine_event_create_listener_thread(vw->m_eventQueue, &KXineWidget::xineEventListener, p); /* rewire filters to the new driver */ vw->unwireVideoFilters(); vw->wireVideoFilters(); /* "none" can now be disposed too */ xine_close_video_driver(vw->m_xineEngine, noneVideoDriver); vw->initOSD(); if (playing) QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_RESTART_PLAYBACK)); #endif } /*********************** new audio driver *************************/ void KXineWidget::audioDriverChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; if (entry == NULL) return; #ifndef USE_QT_ONLY KXineWidget* vw = (KXineWidget*) p; int pos, time, length; debugOut(QString("New audio driver: %1").arg(entry->enum_values[entry->num_value])); if (vw->m_osd) xine_osd_free(vw->m_osd); vw->unwireVideoFilters(); bool playing = false; if (vw->isPlaying()) { playing = true; vw->m_savedPos = 0; if (xine_get_pos_length(vw->m_xineStream, &pos, &time, &length)) vw->m_savedPos = pos; } xine_close(vw->m_xineStream); vw->unwireAudioFilters(); if (vw->m_visualPlugin) { debugOut(QString("Dispose visual plugin: %1").arg(vw->m_visualPluginName)); delete vw->m_visualPlugin; vw->m_visualPlugin = NULL; } xine_event_dispose_queue(vw->m_eventQueue); xine_dispose(vw->m_xineStream); xine_close_audio_driver(vw->m_xineEngine, vw->m_audioDriver); vw->m_audioDriver = NULL; vw->m_audioDriver = xine_open_audio_driver(vw->m_xineEngine, entry->enum_values[entry->num_value], NULL); if (!vw->m_audioDriver) { vw->m_xineError = i18n("Error: Can't init new Audio Driver %1 - using %2!").arg(entry->enum_values[entry->num_value]).arg(vw->m_audioDriverName); QApplication::postEvent(vw, new QTimerEvent( TIMER_EVENT_NEW_XINE_ERROR)); playing = false; vw->m_audioDriver = xine_open_audio_driver(vw->m_xineEngine, vw->m_audioDriverName, NULL); } else { vw->m_audioDriverName = entry->enum_values[entry->num_value]; vw->m_statusString = i18n("Using Audio Driver: ") + vw->m_audioDriverName; QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_NEW_STATUS)); } vw->m_xineStream = xine_stream_new(vw->m_xineEngine, vw->m_audioDriver, vw->m_videoDriver); vw->m_eventQueue = xine_event_new_queue (vw->m_xineStream); xine_event_create_listener_thread(vw->m_eventQueue, &KXineWidget::xineEventListener, p); vw->wireVideoFilters(); vw->initOSD(); if (playing) QApplication::postEvent(vw, new QTimerEvent(TIMER_EVENT_RESTART_PLAYBACK)); #endif } /******** change audio mixer method ****************/ void KXineWidget::audioMixerMethodChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; vw->m_softwareMixer = (bool)entry->num_value; } void KXineWidget::showOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; if (vw->m_osd) vw->m_osdShow = (bool)entry->num_value; } /********************************************** * EVENT LOOP *********************************************/ void KXineWidget::run() { debugOut("Start event loop..."); XEvent event; while (isXineReady()) { XNextEvent (m_xineDisplay, &event); XLockDisplay(m_xineDisplay); if (event.type == Expose) { if (event.xexpose.count == 0) { xine_gui_send_vo_data (m_xineStream, XINE_GUI_SEND_EXPOSE_EVENT, &event); } } XUnlockDisplay(m_xineDisplay); } debugOut("Exiting event loop..."); } /********************************************************** * INIT XINE ENGINE *********************************************************/ void KXineWidget::polish() { if ((!m_startXineManual) && (!isXineReady())) /* start xine engine automatically? */ { initXine(); } } bool KXineWidget::initXine() { if (isXineReady()) return true; emit signalXineStatus(i18n("Init xine...")); globalPosChanged(); /* get global pos of the window */ /**** INIT XINE DISPLAY ****/ /* * xfree 4.4/x.org 6.7: XInitThreads() should be in main() - see main.cpp for more info */ XInitThreads(); m_xineDisplay = XOpenDisplay( getenv("DISPLAY") ); if (!m_xineDisplay) { emit signalXineFatal(i18n("Failed to connect to X-Server!")); return false; } m_xineScreen = DefaultScreen(m_xineDisplay); m_xineWindow = winId(); XLockDisplay(m_xineDisplay); XSelectInput(m_xineDisplay, m_xineWindow, ExposureMask); /* determine display aspect ratio */ double resHor = DisplayWidth(m_xineDisplay, m_xineScreen)*1000 / DisplayWidthMM(m_xineDisplay, m_xineScreen); double resVer = DisplayHeight(m_xineDisplay, m_xineScreen)*1000 / DisplayHeightMM(m_xineDisplay, m_xineScreen); m_displayRatio = resVer / resHor; //if (QABS(m_displayRatio - 1.0) < 0.01) m_displayRatio = 1.0; debugOut(QString("Display aspect ratio (v/h): %1").arg(m_displayRatio)); XUnlockDisplay(m_xineDisplay); /**** INIT XINE ENGINE ****/ debugOut(QString("Using xine version %1").arg(xine_get_version_string())); m_xineEngine = xine_new(); if (!m_xineEngine) { emit signalXineFatal(i18n("Can't init xine Engine!")); return false; } if (m_xineVerbose) xine_engine_set_param(m_xineEngine, XINE_ENGINE_PARAM_VERBOSITY, 99); /* load configuration */ if (!QFile::exists(m_configFilePath)) warningOut("No config file found, will create one..."); else xine_config_load(m_xineEngine, m_configFilePath); debugOut("Post-init xine engine"); xine_init(m_xineEngine); /** set xine parameters **/ const char* const* drivers = NULL; char** audioChoices = NULL; int i = 0; drivers = xine_list_audio_output_plugins(m_xineEngine); audioChoices = new char*[15]; for(i = 0; i < 15; i++) audioChoices[i] = new char[10]; audioChoices[0] = const_cast("auto"); m_audioDriverList << audioChoices[0]; i = 0; while(drivers[i]) { audioChoices[i+1] = (char*)drivers[i]; m_audioDriverList << audioChoices[i+1]; i++; } audioChoices[i+1] = NULL; char* audioInfo = new char[200]; strcpy(audioInfo, i18n("Audiodriver to use (default: auto)").utf8()); i = xine_config_register_enum(m_xineEngine, "audio.driver", 0, audioChoices, audioInfo, NULL, 10, &KXineWidget::audioDriverChangedCallback, this); if (m_audioDriverList.contains(m_preferedAudio)) m_audioDriverName = m_preferedAudio; else m_audioDriverName = audioChoices[i]; debugOut(QString("Use audio driver %1").arg(m_audioDriverName)); i = 0; char** videoChoices = NULL; drivers = xine_list_video_output_plugins(m_xineEngine); videoChoices = new char*[15]; for(i = 0; i < 15; i++) videoChoices[i] = new char[10]; i = 0; videoChoices[0] = const_cast("auto"); m_videoDriverList << videoChoices[0]; while(drivers[i]) { videoChoices[i+1] = (char*)drivers[i]; m_videoDriverList << videoChoices[i+1]; i++; } videoChoices[i+1] = NULL; char* videoInfo = new char[200]; strcpy(videoInfo, i18n("Videodriver to use (default: auto)").utf8()); i = xine_config_register_enum(m_xineEngine, "video.driver", 0, videoChoices, videoInfo, NULL, 10, &KXineWidget::videoDriverChangedCallback, this); if (m_videoDriverList.contains(m_preferedVideo)) m_videoDriverName = m_preferedVideo; else m_videoDriverName = videoChoices[i]; debugOut(QString("Use video driver %1").arg(m_videoDriverName)); char* mixerInfo = new char[200]; strcpy(mixerInfo, i18n("Use software audio mixer").utf8()); m_softwareMixer = (bool)xine_config_register_bool(m_xineEngine, "audio.mixer_software", 1, mixerInfo, NULL, 10, &KXineWidget::audioMixerMethodChangedCallback, this); char* osdInfo = new char[200]; strcpy(osdInfo, i18n("Show OSD Messages").utf8()); m_osdShow = (bool)xine_config_register_bool(m_xineEngine, "misc.osd_messages", 1, osdInfo, NULL, 10, &KXineWidget::showOSDMessagesChangedCallback, this); /* init video driver */ debugOut("Init video driver"); m_x11Visual.display = m_xineDisplay; m_x11Visual.screen = m_xineScreen; m_x11Visual.d = m_xineWindow; m_x11Visual.dest_size_cb = &KXineWidget::destSizeCallback; m_x11Visual.frame_output_cb = &KXineWidget::frameOutputCallback; m_x11Visual.user_data = (void*)this; m_videoDriver = xine_open_video_driver(m_xineEngine, m_videoDriverName, XINE_VISUAL_TYPE_X11, (void *) &(m_x11Visual)); if (!m_videoDriver && m_videoDriverName != "auto") { emit signalXineError(i18n("Can't init Video Driver '%1' - trying 'auto'...").arg(m_videoDriverName)); m_videoDriverName = "auto"; m_videoDriver = xine_open_video_driver(m_xineEngine, m_videoDriverName, XINE_VISUAL_TYPE_X11, (void *) &(m_x11Visual)); } if (!m_videoDriver) { emit signalXineFatal(i18n("All Video Drivers failed to initialize!")); return false; } /* init audio driver */ debugOut("Init audio driver"); m_audioDriver = xine_open_audio_driver(m_xineEngine, m_audioDriverName, NULL); if (!m_audioDriver && m_audioDriverName != "auto") { emit signalXineError(i18n("Can't init Audio Driver '%1' - trying 'auto'...").arg(m_audioDriverName)); m_audioDriverName = "auto"; m_audioDriver = xine_open_audio_driver (m_xineEngine, m_audioDriverName, NULL); } if (!m_audioDriver) { emit signalXineFatal(i18n("All Audio Drivers failed to initialize!")); return false; } //debugOut("Open xine stream"); m_xineStream = xine_stream_new(m_xineEngine, m_audioDriver, m_videoDriver); if (!m_xineStream) { emit signalXineFatal(i18n("Can't create a new xine Stream!")); return false; } /*** OSD ***/ initOSD(); /** event handling **/ m_eventQueue = xine_event_new_queue (m_xineStream); xine_event_create_listener_thread(m_eventQueue, &KXineWidget::xineEventListener, (void*)this); //xine_set_param(m_xineStream, XINE_PARAM_AUDIO_MUTE, 0); //maybe user closed player in muted state m_xineReady = true; start(); debugOut("xine init successful"); /** something to play? **/ QTimer::singleShot(0, this, SLOT(slotPlay())); emit signalXineStatus(i18n("Ready")); emit signalXineReady(); return true; } void KXineWidget::initOSD() { debugOut("Init OSD"); m_osd = xine_osd_new(m_xineStream, 10, 10, 1000, DEFAULT_OSD_FONT_SIZE * 6 + 10); if (m_osd) { xine_cfg_entry_t config; char* font = NULL; if (xine_config_lookup_entry(m_xineEngine, "misc.spu_font", &config)) { font = new char[strlen(config.str_value)+1]; strcpy(font, config.str_value); } else { font = new char[5]; strcpy(font, "sans"); } if (!xine_osd_set_font(m_osd, font, DEFAULT_OSD_FONT_SIZE)) { debugOut(QString("Font for OSD specified not exist: %1").arg(font)); delete [] font; font = new char[5]; strcpy(font, "sans"); xine_osd_set_font(m_osd, font, DEFAULT_OSD_FONT_SIZE); } debugOut(QString("Font for OSD: %1").arg(font)); xine_osd_set_text_palette(m_osd, XINE_TEXTPALETTE_WHITE_BLACK_TRANSPARENT, XINE_OSD_TEXT1); m_osdUnscaled = (xine_osd_get_capabilities(m_osd) & XINE_OSD_CAP_UNSCALED); if (m_osdUnscaled) debugOut("Unscaled OSD available"); delete [] font; } else warningOut("Initialisation of xine OSD failed."); } /************************************************ * PLAY MRL ************************************************/ void KXineWidget::slotPlay() { if ((!isXineReady()) || (isQueueEmpty())) return; m_lengthInfoTimer.stop(); m_posTimer.stop(); m_currentSpeed = Normal; setCursor(QCursor(Qt::WaitCursor)); m_trackURL = m_queue.first(); m_queue.remove(m_queue.find(m_trackURL)); if (m_trackURL != m_logoFile) emit signalXineStatus(i18n("Opening...")); /* check for external subtitle file or save url */ m_trackSubtitleURL = QString::null; m_trackSaveURL = QString::null; QString ref; for (int i = 1; i <= m_trackURL.contains('#'); i++) { ref = m_trackURL.section('#', i, i); if (ref.section(':', 0, 0) == "subtitle") m_trackSubtitleURL = ref.section(':', 1); if (ref.section(':', 0, 0) == "save") m_trackSaveURL = ref.section(':', 1); } debugOut(QString("Playing: %1").arg(m_trackURL.local8Bit())); if (!xine_open(m_xineStream, m_trackURL.local8Bit())) /** pass mrl local 8Bit encoded **/ { sendXineError(); setCursor(QCursor(Qt::ArrowCursor)); return; } /**** use visualization ? ****/ #ifndef USE_QT_ONLY unwireAudioFilters(); wireAudioFilters(); #else if ((xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_AUDIO)) && (!xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO))) { if (m_visualPluginName && (!m_xinePost)) { debugOut(QString("Init visual plugin: %1").arg(m_visualPluginName)); m_xinePost = xine_post_init(m_xineEngine, m_visualPluginName, 0, &m_audioDriver, &m_videoDriver); m_postAudioSource = xine_get_audio_source(m_xineStream); m_postInput = (xine_post_in_t*)xine_post_input (m_xinePost, const_cast("audio in")); xine_post_wire(m_postAudioSource, m_postInput); } } else { if (m_xinePost) { debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName)); m_postAudioSource = xine_get_audio_source(m_xineStream); xine_post_wire_audio_port(m_postAudioSource, m_audioDriver); xine_post_dispose(m_xineEngine, m_xinePost); m_xinePost = NULL; } } #endif /*** play ***/ if (!xine_play(m_xineStream, 0,0)) { sendXineError(); setCursor(QCursor(Qt::ArrowCursor)); return; } /* do the stream have chapters ? */ m_trackHasChapters = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_CHAPTERS); /** information requirement **/ m_trackTitle = QString::null; bool useLatin = false; QTextCodec* testUtf8Local = QTextCodec::codecForLocale(); if (!strcmp(testUtf8Local->name(),"UTF-8")) useLatin = true; if (useLatin) m_trackTitle = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_TITLE)); else m_trackTitle = QString::fromLocal8Bit(xine_get_meta_info(m_xineStream, XINE_META_INFO_TITLE)); if ((!m_trackTitle.isNull()) && (!m_trackTitle.isEmpty())) /* no meta? */ { if (useLatin) { m_trackArtist = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_ARTIST)); m_trackAlbum = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_ALBUM)); m_trackComment = QString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_COMMENT)); } else { m_trackArtist = QString::fromLocal8Bit(xine_get_meta_info(m_xineStream, XINE_META_INFO_ARTIST)); m_trackAlbum = QString::fromLocal8Bit(xine_get_meta_info(m_xineStream, XINE_META_INFO_ALBUM)); m_trackComment = QString::fromLocal8Bit(xine_get_meta_info(m_xineStream, XINE_META_INFO_COMMENT)); } m_trackYear = xine_get_meta_info(m_xineStream, XINE_META_INFO_YEAR); } else { m_trackArtist = QString::null; m_trackAlbum = QString::null; m_trackYear = QString::null; m_trackComment = QString::null; } m_trackIsSeekable = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_SEEKABLE); m_trackHasVideo = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO); if (m_trackHasVideo) { m_trackVideoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); m_videoFrameWidth = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_WIDTH); m_videoFrameHeight = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_HEIGHT); m_trackVideoBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_BITRATE); } else { m_trackVideoCodec = QString::null; m_videoFrameWidth = 0; m_videoFrameHeight = 0; m_trackVideoBitrate = 0; } m_trackHasAudio = (bool)xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_AUDIO); if (m_trackHasAudio) { m_trackAudioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); m_trackAudioBitrate = xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_AUDIO_BITRATE); } else { m_trackAudioCodec = QString::null; m_trackAudioBitrate = 0; } m_trackLength = getLengthInfo(); if ((m_trackLength.isNull()) && (m_trackURL != m_logoFile)) { debugOut("Wait for valid length information"); m_lengthInfoTimer.start(1000); /* wait for available track length info */ } if (m_trackTitle.isNull() || m_trackTitle.isEmpty()) { /* no meta info */ m_trackTitle = m_trackURL; } slotSetAudioChannel(0); //refresh channel info if (m_trackURL != m_logoFile) m_posTimer.start(500); setCursor(QCursor(Qt::ArrowCursor)); if (m_trackURL != m_logoFile) { emit signalXinePlaying(); if (hasSaveURL()) emit signalXineStatus(i18n("Recording")); else emit signalXineStatus(i18n("Playing")); } } /****** error processing ****/ void KXineWidget::sendXineError() { QString error; int errCode = xine_get_error(m_xineStream); QString addInfo; QString audioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); QString videoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); if ((!audioCodec.isEmpty()) || (!videoCodec.isEmpty())) { if (!audioCodec.isEmpty()) addInfo.append(QString("(") + i18n("Audio Codec") + ": " + audioCodec + ")"); if (!videoCodec.isEmpty()) addInfo.append(QString("(") + i18n("Video Codec") + ": " + videoCodec + ")"); } else addInfo.append(QString("(") + m_trackURL + ")"); switch (errCode) { case XINE_ERROR_NO_INPUT_PLUGIN: case XINE_ERROR_NO_DEMUX_PLUGIN: { error = i18n("No plugin found to handle this resource") + " " + addInfo; break; } case XINE_ERROR_DEMUX_FAILED: { error = i18n("Resource seems to be broken") + " (" + m_trackURL + ")"; break; } case XINE_ERROR_MALFORMED_MRL: { error = i18n("Requested resource does not exist") + " (" + m_trackURL + ")"; break; } case XINE_ERROR_INPUT_FAILED: { error = i18n("Resource can not be opened") + " (" + m_trackURL + ")"; break; } default: { error = i18n("Generic error") + " (" + m_trackURL + ")"; break; } } if (isQueueEmpty()) { if (m_trackURL != m_logoFile) { emit signalXineStatus(i18n("Error")); emit signalXineError(error); } else errorOut("Can't find/play logo file!"); } else { errorOut(error); errorOut(QString("Can't play: %1 - trying next").arg(m_trackURL)); QTimer::singleShot(0, this, SLOT(slotPlay())); } } bool KXineWidget::isPlaying() const { if (isXineReady()) return xine_get_status(m_xineStream) == XINE_STATUS_PLAY; else return false; } QString KXineWidget::getXineLog() const { QString logStr; int i = 0; QTextStream ts(&logStr, IO_WriteOnly); const char* const* log = xine_get_log(m_xineEngine, /* XINE_LOG_MSG*/ 0); while(log[i]) { ts << QString::fromUtf8(log[i]); i++; } return logStr; } void KXineWidget::showOSDMessage(const QString& message, uint duration) { if ((!m_osd) || (!m_osdShow) || (isHidden())) return; //debugOut(QString("OSD: draw text: %1").arg(message)); xine_osd_clear(m_osd); xine_osd_draw_text(m_osd, 0, 0, message.utf8(), XINE_OSD_TEXT1); if (m_osdUnscaled) xine_osd_show_unscaled(m_osd, 0); else xine_osd_show(m_osd, 0); m_osdTimer.start(duration); } void KXineWidget::slotOSDHide() { xine_osd_hide(m_osd, 0); m_osdTimer.stop(); } #ifndef USE_QT_ONLY /****************** postprocessing filter management ****************/ QStringList KXineWidget::getVideoFilterNames() const { QStringList filters; const char* const* plugins = xine_list_post_plugins_typed(m_xineEngine, XINE_POST_TYPE_VIDEO_FILTER); for (int i = 0; plugins[i]; i++) { filters << plugins[i]; } return filters; } QStringList KXineWidget::getAudioFilterNames() const { QStringList filters; const char* const* plugins = xine_list_post_plugins_typed(m_xineEngine, XINE_POST_TYPE_AUDIO_FILTER); for (int i = 0; plugins[i]; i++) { filters << plugins[i]; } return filters; } #endif void KXineWidget::slotCreateVideoFilter(const QString& name, QWidget* parent) { #ifndef USE_QT_ONLY unwireVideoFilters(); PostFilter* filter = new PostFilter(name, m_xineEngine, m_audioDriver, m_videoDriver, parent); connect(filter, SIGNAL(signalDeleteMe(PostFilter*)), this, SLOT(slotDeleteVideoFilter(PostFilter*))); m_videoFilterList.append(filter); wireVideoFilters(); #else parent = parent; warningOut(QString("Not implemented [CreateVideoFilter %1]").arg(name)); #endif } void KXineWidget::slotCreateAudioFilter(const QString& name, QWidget* parent) { #ifndef USE_QT_ONLY unwireAudioFilters(); PostFilter* filter = new PostFilter(name, m_xineEngine, m_audioDriver, m_videoDriver, parent); connect(filter, SIGNAL(signalDeleteMe(PostFilter*)), this, SLOT(slotDeleteAudioFilter(PostFilter*))); m_audioFilterList.append(filter); wireAudioFilters(); #else parent = parent; warningOut(QString("Not implemented [CreateAudioFilter %1]").arg(name)); #endif } void KXineWidget::slotRemoveAllVideoFilters() { #ifndef USE_QT_ONLY unwireVideoFilters(); while (m_videoFilterList.count()) m_videoFilterList.removeLast(); wireVideoFilters(); #else warningOut("Not implemented!"); #endif } void KXineWidget::slotRemoveAllAudioFilters() { #ifndef USE_QT_ONLY unwireAudioFilters(); while (m_audioFilterList.count()) m_audioFilterList.removeLast(); wireAudioFilters(); #else warningOut("Not implemented!"); #endif } void KXineWidget::slotDeleteVideoFilter(PostFilter* filter) { #ifndef USE_QT_ONLY unwireVideoFilters(); m_videoFilterList.remove(filter); wireVideoFilters(); #else filter = filter; warningOut("Not implemented!"); #endif } void KXineWidget::slotDeleteAudioFilter(PostFilter* filter) { #ifndef USE_QT_ONLY unwireAudioFilters(); m_audioFilterList.remove(filter); wireAudioFilters(); #else filter = filter; warningOut("Not implemented!"); #endif } #ifndef USE_QT_ONLY void KXineWidget::unwireVideoFilters() { xine_post_wire_video_port(xine_get_video_source(m_xineStream), m_videoDriver); } void KXineWidget::wireVideoFilters() { QPtrList activeList; if (m_videoFilterList.count() && m_videoFiltersEnabled) activeList = m_videoFilterList; if (m_deinterlaceFilter && m_deinterlaceEnabled ) activeList.insert (0, m_deinterlaceFilter); if (activeList.count()) { xine_post_wire_video_port(activeList.at(activeList.count()-1)->getOutput(), m_videoDriver); for (uint i = activeList.count()-1; i >0; i--) { xine_post_wire(activeList.at( i-1 )->getOutput(), activeList.at(i)->getInput()); } xine_post_wire( xine_get_video_source(m_xineStream), activeList.at(0)->getInput()); } } void KXineWidget::unwireAudioFilters() { xine_post_wire_audio_port( xine_get_audio_source (m_xineStream), m_audioDriver); } void KXineWidget::wireAudioFilters() { QPtrList activeList; if (m_audioFilterList.count() && m_audioFiltersEnabled) activeList = m_audioFilterList; if ( (xine_get_stream_info (m_xineStream, XINE_STREAM_INFO_HAS_AUDIO)) && (!xine_get_stream_info (m_xineStream, XINE_STREAM_INFO_HAS_VIDEO)) && m_visualPluginName ) { if (!m_visualPlugin) { debugOut(QString("Init visual plugin: %1").arg(m_visualPluginName)); m_visualPlugin = new PostFilter(m_visualPluginName, m_xineEngine, m_audioDriver, m_videoDriver, NULL); } activeList.insert (0, m_visualPlugin); } else { if (m_visualPlugin) { debugOut(QString("Dispose visual plugin: %1").arg(m_visualPluginName)); delete m_visualPlugin; m_visualPlugin = NULL; } } if (activeList.count()) { xine_post_wire_audio_port(activeList.at(activeList.count()-1)->getOutput(), m_audioDriver); for (uint i = activeList.count()-1; i >0; i--) { xine_post_wire(activeList.at(i-1)->getOutput(), activeList.at(i)->getInput()); } xine_post_wire( xine_get_audio_source(m_xineStream), activeList.at(0)->getInput()); } } #endif void KXineWidget::slotEnableVideoFilters(bool enable) { #ifndef USE_QT_ONLY m_videoFiltersEnabled = enable; unwireVideoFilters(); wireVideoFilters(); #else enable = enable; warningOut("Not implemented!"); #endif } void KXineWidget::slotEnableAudioFilters(bool enable) { #ifndef USE_QT_ONLY m_audioFiltersEnabled = enable; unwireAudioFilters(); wireAudioFilters(); #else enable = enable; warningOut("Not implemented!"); #endif } #ifndef USE_QT_ONLY QStringList KXineWidget::getAudioFilterConfig() { QStringList configStrings; for (uint i=0; igetConfig(); return configStrings; } QStringList KXineWidget::getVideoFilterConfig() { QStringList configStrings; for (uint i=0; igetConfig(); return configStrings; } #endif /**** visual plugin **********/ QStringList KXineWidget::getVisualPlugins() const { QStringList visuals; const char* const* plugins = xine_list_post_plugins_typed(m_xineEngine, XINE_POST_TYPE_AUDIO_VISUALIZATION); for (int i = 0; plugins[i]; i++) { visuals << plugins[i]; } return visuals; } /**************** change visualization plugin *****************/ void KXineWidget::slotSetVisualPlugin(const QString& visual) { if (m_visualPluginName == visual) return; debugOut(QString("New visualization plugin: %1").arg(visual)); #ifndef USE_QT_ONLY unwireAudioFilters(); if(m_visualPlugin) { delete m_visualPlugin; m_visualPlugin = NULL; } if (visual == "none") m_visualPluginName = QString::null; else m_visualPluginName = visual; wireAudioFilters(); #else if (visual == "none") m_visualPluginName = QString::null; else m_visualPluginName = visual; if (m_xinePost) { xine_post_out_t *pp; pp = xine_get_audio_source(m_xineStream); xine_post_wire_audio_port(pp, m_audioDriver); xine_post_dispose(m_xineEngine, m_xinePost); m_xinePost = NULL; } if ( (xine_get_status(m_xineStream ) == XINE_STATUS_PLAY) && (!xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_HAS_VIDEO)) && (m_visualPluginName) ) { m_xinePost = xine_post_init(m_xineEngine, m_visualPluginName, 0, &m_audioDriver, &m_videoDriver); m_postAudioSource = xine_get_audio_source(m_xineStream); m_postInput = (xine_post_in_t*)xine_post_input(m_xinePost, const_cast("audio in")); xine_post_wire(m_postAudioSource, m_postInput); } #endif } /*****/ void KXineWidget::getAutoplayPlugins(QStringList& autoPlayList) const { char** pluginIds = NULL; int i = 0; pluginIds = (char**)xine_get_autoplay_input_plugin_ids(m_xineEngine); while(pluginIds[i]) { autoPlayList << pluginIds[i]; autoPlayList << xine_get_input_plugin_description(m_xineEngine, pluginIds[i]); i++; } } bool KXineWidget::getAutoplayPluginURLS(const QString& plugin, QStringList& list) { char** urls = NULL; int num; int i = 0; urls = xine_get_autoplay_mrls(m_xineEngine, plugin, &num); if (urls) { while (urls[i]) { list << urls[i]; i++; } return true; } else { return false; } } void KXineWidget::slotSetVolume(int vol) { if (!isXineReady()) return; if (m_softwareMixer) { //debugOut(QString("Set software amplification level: %1").arg(vol)); xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL, vol); } else { //debugOut(QString("Set audio mixer volume: %1").arg(vol)); xine_set_param(m_xineStream, XINE_PARAM_AUDIO_VOLUME, vol); } emit signalXineStatus(i18n("Volume") + ": " + QString::number(vol) +"%"); } uint KXineWidget::getVolume() const { if (!isXineReady()) return 0; uint vol; if (m_softwareMixer) { vol = xine_get_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL); } else { vol = xine_get_param(m_xineStream, XINE_PARAM_AUDIO_VOLUME); } return vol; } void KXineWidget::slotToggleMute() { if (xine_get_param(m_xineStream, XINE_PARAM_AUDIO_MUTE)) { xine_set_param(m_xineStream, XINE_PARAM_AUDIO_MUTE, 0); /* mute off */ emit signalXineStatus(i18n("Mute") + ": " + i18n("Off")); } else { xine_set_param(m_xineStream,XINE_PARAM_AUDIO_MUTE, 1); /* mute on */ emit signalXineStatus(i18n("Mute") + ": " + i18n("On")); } } void KXineWidget::mouseMoveEvent(QMouseEvent* mev) { if (!m_xineReady) return; if (cursor().shape() == Qt::BlankCursor) { setCursor(QCursor(Qt::ArrowCursor)); } x11_rectangle_t rect; xine_event_t event; xine_input_data_t input; rect.x = mev->x(); rect.y = mev->y(); rect.w = 0; rect.h = 0; xine_gui_send_vo_data(m_xineStream, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&rect); event.type = XINE_EVENT_INPUT_MOUSE_MOVE; event.data = &input; event.data_length = sizeof(input); input.button = 0; input.x = rect.x; input.y = rect.y; xine_event_send(m_xineStream, &event); } void KXineWidget::mousePressEvent(QMouseEvent* mev) { if (!m_xineReady) return; int cur = cursor().shape(); if (mev->button() == Qt::MidButton) { emit signalMiddleClick(); mev->ignore(); return; } if (mev->button() == Qt::RightButton) { if ( (cur == Qt::ArrowCursor) || (cur == Qt::BlankCursor) ) { emit signalRightClick(mev->globalPos()); mev->accept(); return; } } if (mev->button() == Qt::LeftButton) { if ( (cur == Qt::ArrowCursor) || (cur == Qt::BlankCursor) ) { emit signalLeftClick(mev->globalPos()); mev->ignore(); return; } x11_rectangle_t rect; xine_event_t event; xine_input_data_t input; rect.x = mev->x(); rect.y = mev->y(); rect.w = 0; rect.h = 0; xine_gui_send_vo_data (m_xineStream, XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO, (void*)&rect); event.type = XINE_EVENT_INPUT_MOUSE_BUTTON; event.data = &input; event.data_length = sizeof(input); input.button = 1; input.x = rect.x; input.y = rect.y; xine_event_send (m_xineStream, &event); mev->accept(); /* don't send event to parent */ } } void KXineWidget::mouseDoubleClickEvent(QMouseEvent* mev) { emit signalDoubleClick(); mev->ignore(); } void KXineWidget::wheelEvent(QWheelEvent* e) { int oldVal = getPosition(); if (oldVal == 0) // no valid position return; float offset = log10( QABS(e->delta()) ) / 0.002; int newVal = 0; if (e->delta()>0) newVal = oldVal + int(offset); else newVal = oldVal - int(offset); slotSeekToPosition(newVal); e->accept(); } void KXineWidget::playNextChapter() const { xine_event_t xev; xev.type = XINE_EVENT_INPUT_NEXT; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::playPreviousChapter() const { xine_event_t xev; xev.type = XINE_EVENT_INPUT_PREVIOUS; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotStop() { m_posTimer.stop(); //emit signalNewPosition(0, QTime()); if ((m_logoFile.isNull()) && (isPlaying())) xine_stop(m_xineStream); else { appendToQueue(m_logoFile); QTimer::singleShot(0, this, SLOT(slotPlay())); } emit signalXineStatus(i18n("Stop")); } void KXineWidget::slotSetAudiocdDevice(const QString& device) { debugOut(QString("Set AudioCD device to %1").arg(device)); xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.cdda_device", &config); if (m_cachedCDPath.isNull()) m_cachedCDPath = config.str_value; config.str_value = (char*)device.latin1(); xine_config_update_entry (m_xineEngine, &config); } void KXineWidget::slotSetVcdDevice(const QString& device) { debugOut(QString("Set VCD device to %1").arg(device)); xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.vcd_device", &config); if (m_cachedVCDPath.isNull()) m_cachedVCDPath = config.str_value; config.str_value = (char*)device.latin1(); xine_config_update_entry (m_xineEngine, &config); } void KXineWidget::slotSetDvdDevice(const QString& device) { debugOut(QString("Set DVD device to %1").arg(device)); xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.dvd_device", &config); if (m_cachedDVDPath.isNull()) m_cachedDVDPath = config.str_value; config.str_value = (char*)device.latin1(); xine_config_update_entry (m_xineEngine, &config); } QString KXineWidget::audiocdDevice() const { xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.cdda_device", &config); return QString(config.str_value); } QString KXineWidget::vcdDevice() const { xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.vcd_device", &config); return QString(config.str_value); } QString KXineWidget::dvdDevice() const { xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.dvd_device", &config); return QString(config.str_value); } void KXineWidget::setStreamSaveDir(const QString& dir) { xine_cfg_entry_t config; if (!xine_config_lookup_entry(m_xineEngine, "misc.save_dir", &config)) return; /* older xine-lib */ debugOut(QString("Set misc.save_dir to: %1").arg(dir)); config.str_value = (char*)dir.latin1(); xine_config_update_entry (m_xineEngine, &config); } const QString KXineWidget::getStreamSaveDir() { xine_cfg_entry_t config; if (!xine_config_lookup_entry(m_xineEngine, "misc.save_dir", &config)) return QString::null; /* older xine-lib */ return QString(config.str_value); } void KXineWidget::setBroadcasterPort(const uint port) { debugOut(QString("Set broadcaster port to %1").arg(port)); xine_set_param(m_xineStream, XINE_PARAM_BROADCASTER_PORT, port); } void KXineWidget::slotSpeedPause() { if (m_currentSpeed == Pause) { slotSpeedNormal(); } else { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); m_posTimer.stop(); m_currentSpeed = Pause; emit signalXineStatus(i18n("Pause")); } } void KXineWidget::slotSpeedNormal() { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); m_posTimer.start(500); m_currentSpeed = Normal; emit signalXineStatus(i18n("Playing") + " "); } void KXineWidget::slotSpeedFaster() { switch (m_currentSpeed) { case Fast1: { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_FAST_4); m_currentSpeed = Fast2; emit signalXineStatus(i18n("Fast Forward %1").arg("x2")); break; } case Fast2: { slotSpeedNormal(); break; } default: { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_FAST_2); m_currentSpeed = Fast1; emit signalXineStatus(i18n("Fast Forward %1").arg("x1")); break; } } } void KXineWidget::slotSpeedSlower() { switch (m_currentSpeed) { case Slow1: { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_4); m_currentSpeed = Slow2; emit signalXineStatus(i18n("Slow Motion %1").arg("x2")); break; } case Slow2: { slotSpeedNormal(); break; } default: { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_2); m_currentSpeed = Slow1; emit signalXineStatus(i18n("Slow Motion %1").arg("x1")); break; } } } QString KXineWidget::getSupportedExtensions() const { return xine_get_file_extensions(m_xineEngine); } void KXineWidget::slotSetAudioChannel(int ch) { debugOut(QString("Switch to audio channel %1").arg(ch-1)); xine_set_param(m_xineStream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, ch-1); } void KXineWidget::slotSetSubtitleChannel(int ch) { debugOut(QString("Switch to subtitle channel %1").arg(ch-1)); xine_set_param(m_xineStream, XINE_PARAM_SPU_CHANNEL, ch-1); } void KXineWidget::slotSetFileSubtitles(QString url) { int pos; int time; int length; m_queue.prepend(url); if ( !xine_get_pos_length(m_xineStream, &pos, &time, &length) ) { debugOut("No valid stream position information"); return; } if (isPlaying()) xine_stop(m_xineStream); m_posTimer.stop(); slotPlay(); slotSeekToPosition(pos); } uint KXineWidget::getPosition() const { if (!m_xineReady) return 0; int pos, time, length; if (!xine_get_pos_length(m_xineStream, &pos, &time, &length)) { debugOut("No valid stream position information"); return 0; } return (uint)pos; } QTime KXineWidget::getPlaytime() const { if (!m_xineReady) return QTime(); int pos, time, length; if (!xine_get_pos_length(m_xineStream, &pos, &time, &length)) { debugOut("No valid stream position information"); return QTime(); } return msToTime(time); } void KXineWidget::slotSendPosition() { if (!m_xineReady) return; int pos, time, length; if (!xine_get_pos_length(m_xineStream, &pos, &time, &length)) { debugOut("No valid stream position information"); return; } emit signalNewPosition(pos, msToTime(time)); } void KXineWidget::slotStartSeeking() { debugOut("Seeking started"); m_posTimer.stop(); } void KXineWidget::slotSeekToPosition(int pos) { if ((!isXineReady()) || (!isPlaying()) || (!isSeekable())) return; bool pause = false; if (m_currentSpeed == Pause) pause = true; xine_play(m_xineStream, pos, 0); double length = QTime().msecsTo(getLength()); length = (length * pos)/65535; QTime time = QTime().addMSecs((int)length); emit signalNewPosition(pos, time); emit signalXineStatus(i18n("Position") + ": " + time.toString("h:mm:ss")); if (pause) slotSpeedPause(); } void KXineWidget::slotSeekToTime(const QTime& pos) { if ((!isXineReady()) || (!isPlaying()) || (!isSeekable())) return; bool pause = false; if (m_currentSpeed == Pause) pause = true; xine_play(m_xineStream, 0, QTime().msecsTo(pos)); emit signalXineStatus(i18n("Position") + ": " + getPlaytime().toString("h:mm:ss")); if (pause) slotSpeedPause(); } void KXineWidget::slotStopSeeking() { debugOut("Seeking stopped"); m_posTimer.start(500, false); } void KXineWidget::slotEject() { xine_eject(m_xineStream); } void KXineWidget::slotEnableAutoresize(bool enable) { m_autoresizeEnabled = enable; if (!m_autoresizeEnabled) { m_videoFrameHeight = 0; m_videoFrameWidth = 0; } } /*************************************** * tvtime deinterlacer plugin * ***************************************/ #ifndef USE_QT_ONLY void KXineWidget::createDeinterlacePlugin(const QString& config, QWidget* parent) { m_deinterlaceFilter = new PostFilter(config.section(':',0,0), m_xineEngine, m_audioDriver, m_videoDriver, parent); if( !m_deinterlaceFilter->getInput() || !m_deinterlaceFilter->getOutput() ) { delete m_deinterlaceFilter; m_deinterlaceFilter = NULL; } slotSetDeinterlaceConfig(config); } const QString KXineWidget::getDeinterlaceConfig() const { if (m_deinterlaceFilter) return m_deinterlaceFilter->getConfig(); return DEFAULT_TVTIME_CONFIG; } #endif void KXineWidget::slotSetDeinterlaceConfig(const QString& config) { #ifndef USE_QT_ONLY if (m_deinterlaceFilter) m_deinterlaceFilter->setConfig(config); #else warningOut(QString ("Not implemented [SetDeinterlaceConfig %1]").arg(config)); #endif } void KXineWidget::slotToggleDeinterlace() { #ifndef USE_QT_ONLY if (m_deinterlaceFilter) { m_deinterlaceEnabled = !m_deinterlaceEnabled; debugOut(QString("Deinterlace enabled: %1").arg(m_deinterlaceEnabled)); unwireVideoFilters(); wireVideoFilters(); } else { /* fallback - this method is deprecated */ if (xine_get_param(m_xineStream, XINE_PARAM_VO_DEINTERLACE)) xine_set_param(m_xineStream, XINE_PARAM_VO_DEINTERLACE, false); else xine_set_param(m_xineStream, XINE_PARAM_VO_DEINTERLACE, true); } #else warningOut("Not implemented!"); #endif } /**************************/ void KXineWidget::slotAspectRatioAuto() { xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_AUTO); emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("Auto")); } void KXineWidget::slotAspectRatio4_3() { xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_4_3); emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("4:3")); } void KXineWidget::slotAspectRatioAnamorphic() { xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_ANAMORPHIC); emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("16:9")); } void KXineWidget::slotAspectRatioSquare() { xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_SQUARE); emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("1:1")); } void KXineWidget::slotAspectRatioDVB() { xine_set_param(m_xineStream, XINE_PARAM_VO_ASPECT_RATIO, XINE_VO_ASPECT_DVB); emit signalXineStatus(i18n("Aspect Ratio") + ": " + i18n("2.11:1")); } void KXineWidget::slotZoomOut() { if ((m_currentZoom - 5) >= 100) { m_currentZoom -= 5; xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoom); xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoom); emit signalXineStatus(i18n("Zoom") + ": " + QString::number(m_currentZoom) + "%"); } } void KXineWidget::slotZoomIn() { if ((m_currentZoom + 5) <= XINE_VO_ZOOM_MAX) { m_currentZoom += 5; xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoom); xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoom); emit signalXineStatus(i18n("Zoom") + ": " + QString::number(m_currentZoom) + "%"); } } void KXineWidget::slotZoomOff() { xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, 100); xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, 100); m_currentZoom = 100; emit signalXineStatus(i18n("Zoom") + ": " + QString::number(m_currentZoom) + "%"); } QTime KXineWidget::getLengthInfo() { int pos, time, length; bool ok = xine_get_pos_length(m_xineStream, &pos, &time, &length); if ( (ok) && (length > 0) ) { return msToTime(length); } return QTime(); } void KXineWidget::slotEmitLengthInfo() { QTime length = getLengthInfo(); if (!(length.isNull())) { m_lengthInfoTimer.stop(); m_trackLength = length; emit signalLengthChanged(); } else debugOut("Wait for valid length information"); } void KXineWidget::globalPosChanged() { QPoint g = mapToGlobal(QPoint(0,0)); m_globalX = g.x(); m_globalY = g.y(); } const xine_t* const KXineWidget::getXineEngine()const { return m_xineEngine; } /************ video settings ****************/ void KXineWidget::getVideoSettings(int& hue, int& sat, int& contrast, int& bright, int& avOffset, int& spuOffset) const { hue = xine_get_param(m_xineStream, XINE_PARAM_VO_HUE); sat = xine_get_param(m_xineStream, XINE_PARAM_VO_SATURATION); contrast = xine_get_param(m_xineStream, XINE_PARAM_VO_CONTRAST); bright = xine_get_param(m_xineStream, XINE_PARAM_VO_BRIGHTNESS); avOffset = xine_get_param(m_xineStream, XINE_PARAM_AV_OFFSET); spuOffset = xine_get_param(m_xineStream, XINE_PARAM_SPU_OFFSET); } void KXineWidget::slotSetHue(int hue) { xine_set_param(m_xineStream, XINE_PARAM_VO_HUE, hue); emit signalXineStatus(i18n("Hue") + ": " + QString::number((hue*100)/65535) + "%"); } void KXineWidget::slotSetSaturation(int sat) { xine_set_param(m_xineStream, XINE_PARAM_VO_SATURATION, sat); emit signalXineStatus(i18n("Saturation") + ": " + QString::number((sat*100)/65535) + "%"); } void KXineWidget::slotSetContrast(int contrast) { xine_set_param(m_xineStream, XINE_PARAM_VO_CONTRAST, contrast); emit signalXineStatus(i18n("Contrast") + ": " + QString::number((contrast*100)/65535) + "%"); } void KXineWidget::slotSetBrightness(int bright) { xine_set_param(m_xineStream, XINE_PARAM_VO_BRIGHTNESS, bright); emit signalXineStatus(i18n("Brightness") + ": " + QString::number((bright*100)/65535) + "%"); } void KXineWidget::slotSetAVOffset(int av) { xine_set_param(m_xineStream, XINE_PARAM_AV_OFFSET, av); emit signalXineStatus(i18n("Audio/Video Offset") + ": " + QString::number(av/90) + i18n("msec")); } void KXineWidget::slotSetSpuOffset(int spu) { xine_set_param(m_xineStream, XINE_PARAM_SPU_OFFSET, spu); emit signalXineStatus(i18n("Subtitle Offset") + ": " + QString::number(spu/90) + i18n("msec")); } /**************** equalizer *****************/ void KXineWidget::slotSetEq30(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_30HZ, -val); } void KXineWidget::slotSetEq60(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_60HZ, -val); } void KXineWidget::slotSetEq125(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_125HZ, -val); } void KXineWidget::slotSetEq250(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_250HZ, -val); } void KXineWidget::slotSetEq500(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_500HZ, -val); } void KXineWidget::slotSetEq1k(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_1000HZ, -val); } void KXineWidget::slotSetEq2k(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_2000HZ, -val); } void KXineWidget::slotSetEq4k(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_4000HZ, -val); } void KXineWidget::slotSetEq8k(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_8000HZ, -val); } void KXineWidget::slotSetEq16k(int val) { xine_set_param(m_xineStream, XINE_PARAM_EQ_16000HZ, -val); } /*************** dvd menus ******************/ void KXineWidget::slotMenuToggle() { xine_event_t xev; xev.type = XINE_EVENT_INPUT_MENU1; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotMenuTitle() { xine_event_t xev; xev.type = XINE_EVENT_INPUT_MENU2; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotMenuRoot() { xine_event_t xev; xev.type = XINE_EVENT_INPUT_MENU3; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotMenuSubpicture() { xine_event_t xev; xev.type = XINE_EVENT_INPUT_MENU4; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotMenuAudio() { xine_event_t xev; xev.type = XINE_EVENT_INPUT_MENU5; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotMenuAngle() { xine_event_t xev; xev.type = XINE_EVENT_INPUT_MENU6; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotMenuPart() { xine_event_t xev; xev.type = XINE_EVENT_INPUT_MENU7; xev.data = NULL; xev.data_length = 0; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotDVDMenuLeft() { xine_event_t xev; xev.data = NULL; xev.data_length = 0; xev.type = XINE_EVENT_INPUT_LEFT; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotDVDMenuRight() { xine_event_t xev; xev.data = NULL; xev.data_length = 0; xev.type = XINE_EVENT_INPUT_RIGHT; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotDVDMenuUp() { xine_event_t xev; xev.data = NULL; xev.data_length = 0; xev.type = XINE_EVENT_INPUT_UP; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotDVDMenuDown() { xine_event_t xev; xev.data = NULL; xev.data_length = 0; xev.type = XINE_EVENT_INPUT_DOWN; xine_event_send(m_xineStream, &xev); } void KXineWidget::slotDVDMenuSelect() { xine_event_t xev; xev.data = NULL; xev.data_length = 0; xev.type = XINE_EVENT_INPUT_SELECT; xine_event_send(m_xineStream, &xev); } /******** mouse hideing at fullscreen ****/ void KXineWidget::startMouseHideTimer() { m_mouseHideTimer.start(5000); } void KXineWidget::stopMouseHideTimer() { m_mouseHideTimer.stop(); } void KXineWidget::slotHideMouse() { if (cursor().shape() == Qt::ArrowCursor) { setCursor(QCursor(Qt::BlankCursor)); } } /************************************************************ * Take a Screenshot * ************************************************************/ QImage KXineWidget::getScreenshot() const { uchar *rgbPile = NULL; int width, height; double scaleFactor; getScreenshot(rgbPile, width, height, scaleFactor); if (!rgbPile) return QImage(); QImage screenShot(rgbPile, width, height, 32, 0, 0, QImage::IgnoreEndian); if (scaleFactor >= 1.0) width = (int)((double) width * scaleFactor + 0.5); else height = (int)((double) height / scaleFactor + 0.5); debugOut(QString("Screenshot: scale picture from %1x%2 to %3x%4").arg(screenShot.width()).arg(screenShot.height()).arg(width).arg(height)); screenShot = screenShot.smoothScale(width, height); delete []rgbPile; return screenShot; } void KXineWidget::getScreenshot(uchar*& rgb32BitData, int& videoWidth, int& videoHeight, double& scaleFactor) const { uint8_t *yuv = NULL, *y = NULL, *u = NULL, *v =NULL; int width, height, ratio, format; // double desired_ratio, image_ratio; if (!xine_get_current_frame(m_xineStream, &width, &height, &ratio, &format, NULL)) return; yuv = new uint8_t[((width+8) * (height+1) * 2)]; if (yuv == NULL) { errorOut("Not enough memory to make screenshot!"); return; } xine_get_current_frame(m_xineStream, &width, &height, &ratio, &format, yuv); videoWidth = width; videoHeight = height; /* * convert to yv12 if necessary */ switch (format) { case XINE_IMGFMT_YUY2: { uint8_t *yuy2 = yuv; yuv = new uint8_t[(width * height * 2)]; if (yuv == NULL) { errorOut("Not enough memory to make screenshot!"); return; } y = yuv; u = yuv + width * height; v = yuv + width * height * 5 / 4; yuy2Toyv12 (y, u, v, yuy2, width, height); delete [] yuy2; } break; case XINE_IMGFMT_YV12: y = yuv; u = yuv + width * height; v = yuv + width * height * 5 / 4; break; default: { warningOut(QString("Screenshot: Format %1 not supportet!").arg((char*)&format)); delete [] yuv; return; } } /* * convert to rgb */ rgb32BitData = yv12ToRgb (y, u, v, width, height); // image_ratio = (double) height / (double) width; /* switch (ratio) { case XINE_VO_ASPECT_ANAMORPHIC: debugOut("Screenshot: got video aspect: anamorphic"); desired_ratio = 16.0 /9.0; break; case XINE_VO_ASPECT_DVB: debugOut("Screenshot: got video aspect: 2.11:1"); desired_ratio = 2.11/1.0; break; case XINE_VO_ASPECT_4_3: debugOut("Screenshot: got video aspect: 4:3"); desired_ratio = 4.0 / 3.0; break; default: warningOut(QString("Screenshot: Unknown aspect ratio: %1 - using 4:3").arg(ratio)); case XINE_VO_ASPECT_SQUARE: debugOut("Screenshot: got video aspect: 1:1"); desired_ratio = image_ratio; break; } */ debugOut(QString("Screenshot: using scale factor: %1").arg(m_videoAspect)); scaleFactor = m_videoAspect; delete [] yuv; } /************************************************ * HELPER FUNCTIONS * ************************************************/ QTime KXineWidget::msToTime(int msec) { QTime t; t = t.addMSecs(msec); return t; } #ifdef USE_QT_ONLY QString KXineWidget::i18n(const char *text) { return QApplication::tr(text); } #endif void KXineWidget::debugOut (QString qsDebug) { #ifdef USE_QT_ONLY QString qsDebugging = QString ("Debug: ") + qsDebug +"\n"; printf ((const char *)qsDebugging); #else kdDebug() << "KXineWidget: " << (const char *)qsDebug << "\n"; #endif } void KXineWidget::errorOut (QString qsError) { #ifdef USE_QT_ONLY QString qsErroring = QString ("Error: ") + qsError+ "\n"; printf ((const char *)qsErroring); #else kdError() << "KXineWidget: " << (const char *)qsError << "\n"; #endif } void KXineWidget::warningOut (QString qsWarning) { #ifdef USE_QT_ONLY QString qsWarninging = QString ("Warning: ") + qsWarning + "\n"; printf ((const char *)qsWarninging); #else kdWarning() << "KXineWidget: " << (const char *)qsWarning << "\n"; #endif } /************************************************************ * Helpers to convert yuy and yv12 frames to rgb * * code from gxine modified for 32bit output * * Copyright (C) 2000-2003 the xine project * ************************************************************/ void KXineWidget::yuy2Toyv12 (uint8_t *y, uint8_t *u, uint8_t *v, uint8_t *input, int width, int height) { int i, j, w2; w2 = width / 2; for (i = 0; i < height; i += 2) { for (j = 0; j < w2; j++) { /* * packed YUV 422 is: Y[i] U[i] Y[i+1] V[i] */ *(y++) = *(input++); *(u++) = *(input++); *(y++) = *(input++); *(v++) = *(input++); } /* * down sampling */ for (j = 0; j < w2; j++) { /* * skip every second line for U and V */ *(y++) = *(input++); input++; *(y++) = *(input++); input++; } } } uchar* KXineWidget::yv12ToRgb (uint8_t *src_y, uint8_t *src_u, uint8_t *src_v, int width, int height) { /* * Create rgb data from yv12 */ #define clip_8_bit(val) \ { \ if (val < 0) \ val = 0; \ else \ if (val > 255) val = 255; \ } int i, j; int y, u, v; int r, g, b; int sub_i_uv; int sub_j_uv; int uv_width, uv_height; uchar *rgb; uv_width = width / 2; uv_height = height / 2; rgb = new uchar[(width * height * 4)]; //qt needs a 32bit align if (!rgb) { // kdError(555) << "Not enough memory!" << endl; return NULL; } for (i = 0; i < height; ++i) { /* * calculate u & v rows */ sub_i_uv = ((i * uv_height) / height); for (j = 0; j < width; ++j) { /* * calculate u & v columns */ sub_j_uv = ((j * uv_width) / width); /*************************************************** * * Colour conversion from * http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30 * * Thanks to Billy Biggs * for the pointer and the following conversion. * * R' = [ 1.1644 0 1.5960 ] ([ Y' ] [ 16 ]) * G' = [ 1.1644 -0.3918 -0.8130 ] * ([ Cb ] - [ 128 ]) * B' = [ 1.1644 2.0172 0 ] ([ Cr ] [ 128 ]) * * Where in xine the above values are represented as * * Y' == image->y * Cb == image->u * Cr == image->v * ***************************************************/ y = src_y[(i * width) + j] - 16; u = src_u[(sub_i_uv * uv_width) + sub_j_uv] - 128; v = src_v[(sub_i_uv * uv_width) + sub_j_uv] - 128; r = (int)((1.1644 * (double)y) + (1.5960 * (double)v)); g = (int)((1.1644 * (double)y) - (0.3918 * (double)u) - (0.8130 * (double)v)); b = (int)((1.1644 * (double)y) + (2.0172 * (double)u)); clip_8_bit (r); clip_8_bit (g); clip_8_bit (b); rgb[(i * width + j) * 4 + 0] = b; rgb[(i * width + j) * 4 + 1] = g; rgb[(i * width + j) * 4 + 2] = r; rgb[(i * width + j) * 4 + 3] = 0; } } return rgb; }