/* * kxinewidget.cpp - a kde / qt api for xine-lib * * Copyright (C) 2003-2005 Jürgen Kofler * Copyright (C) 2005-2006 Christophe Thommeret * * 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; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "kxinewidget.h" #ifdef HAVE_XINERAMA #include #endif #ifndef USE_TQT_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_NEW_VOLUME_LEVEL 109 #define TIMER_EVENT_RESTART_PLAYBACK 200 #define TIMER_EVENT_RESIZE_PARENT 300 bool KXineWidget::s_logarithmicVolume = false; KXineWidget::KXineWidget(TQWidget* parent, const char* name, const TQString& pathToConfigFile, const TQString& pathToLogoFile, const TQString& audioDriver, const TQString& videoDriver, bool startManual, bool verbose) : TQWidget(parent,name), m_startXineManual(startManual), m_xineReady(false), m_logoFile(pathToLogoFile), m_preferedAudio(audioDriver), m_preferedVideo(videoDriver), m_xineVerbose(verbose), m_xineEngine(NULL), m_audioDriver(NULL), m_videoDriver(NULL), m_xineStream(NULL), connection(NULL), m_eventQueue(NULL), m_osd(NULL), m_osdUnscaled(false), m_osdShow(false), m_osdSize(0), m_osdFont(NULL), m_audioChoices(NULL), m_audioInfo(NULL), m_videoChoices(NULL), m_videoInfo(NULL), m_mixerInfo(NULL), m_osdShowInfo(NULL), m_osdSizeOptions(NULL), m_osdSizeInfo(NULL), m_osdFontInfo(NULL), #ifndef USE_TQT_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(TQString()), m_currentSpeed(Normal), m_softwareMixer(false), m_volumeGain(false), m_currentZoom(100), m_currentZoomX(100), m_currentZoomY(100), m_currentAudio(0), m_currentSub(0), m_savedPos(0), m_autoresizeEnabled(false) { setMinimumSize(TQSize(20,20)); // set a size hint setPaletteBackgroundColor(TQColor(0,0,0)); //black /* dvb */ TimeShiftFilename = ""; dvbHaveVideo = 0; dvbOSD = 0; dvbColor[0] = 0; connect( &dvbOSDHideTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(dvbHideOSD()) ); if (pathToConfigFile.isNull()) { debugOut("Using default config file ~/.xine/config"); m_configFilePath = TQDir::homeDirPath(); m_configFilePath.append("/.xine/config"); } else m_configFilePath = pathToConfigFile; if (!m_logoFile.isNull()) appendToQueue(m_logoFile); #ifndef USE_TQT_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, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotSendPosition())); connect(&m_lengthInfoTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotEmitLengthInfo())); connect(&m_mouseHideTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotHideMouse())); connect(&m_osdTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotOSDHide())); connect(&m_recentMessagesTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotNoRecentMessage())); setUpdatesEnabled(false); setMouseTracking(true); // Detect xine version, this is used for volume adjustment. // Xine versions prior to 1.2.13 use linear volume, so the engine uses logarithmic volume. // Xine versions starting from 1.2.13 use logarithmic volume, so the engine uses linear volume. int xinemajor = 0, xineminor = 0, xinemaint = 0; xine_get_version(&xinemajor, &xineminor, &xinemaint); s_logarithmicVolume = (xinemajor * 1000000 + xineminor * 1000 + xinemaint < 1002013); } KXineWidget::~KXineWidget() { /* "careful" shutdown, maybe xine initialization was not successful */ m_xineReady = false; /* stop all timers */ m_posTimer.stop(); m_mouseHideTimer.stop(); #ifndef USE_TQT_ONLY slotRemoveAllAudioFilters(); slotRemoveAllVideoFilters(); #endif if (m_osd) xine_osd_free(m_osd); if (m_xineStream) xine_close(m_xineStream); debugOut("Shut down xine engine"); #ifndef USE_TQT_ONLY if (m_deinterlaceFilter) { debugOut("Unwire video filters"); unwireVideoFilters(); delete m_deinterlaceFilter; m_deinterlaceFilter = NULL; } if (m_visualPlugin) { debugOut("Unwire audio filters"); unwireAudioFilters(); debugOut(TQString("Dispose visual plugin: %1").arg(m_visualPluginName )); delete m_visualPlugin; m_visualPlugin = NULL; } #else if (m_xinePost) { debugOut(TQString("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; /* free xine config strings */ if (m_osdShowInfo) free(m_osdShowInfo); if (m_osdFontInfo) free(m_osdFontInfo); if (m_osdFont) free(m_osdFont); if (m_osdSizeInfo) free(m_osdSizeInfo); if (m_osdSizeOptions) { int i=0; while (m_osdSizeOptions[i]) { free(m_osdSizeOptions[i]); i++; } delete [] m_osdSizeOptions; } if (m_mixerInfo) free(m_mixerInfo); if (m_videoInfo) free(m_videoInfo); if (m_videoChoices) { int i=0; while (m_videoChoices[i]) { free(m_videoChoices[i]); i++; } delete [] m_videoChoices; } if (m_audioInfo) free(m_audioInfo); if (m_audioChoices) { int i=0; while (m_audioChoices[i]) { free(m_audioChoices[i]); i++; } delete [] m_audioChoices; } if (connection) { debugOut("Close xine display"); #ifndef HAVE_XCB XCloseDisplay(connection); /* close xine display */ #else xcb_disconnect(connection); /* close xine display */ #endif } connection = 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(TQString("Save xine config to: %1").arg(m_configFilePath)); xine_config_save(m_xineEngine, m_configFilePath.ascii()); } /*************************************************** * 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; /* give false aspect for audio visualization*/ if ( !vw->hasVideo() ) { *dest_aspect = (video_width*video_aspect)/((vw->width()*video_height/vw->height())-0.5); } /* 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(TQString("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; TQApplication::postEvent(vw, new TQTimerEvent(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() - TQSize((vw->width() - video_width), vw->height() - video_height); debugOut(TQString("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 */ TQApplication::postEvent(vw, new TQTimerEvent(TIMER_EVENT_RESIZE_PARENT)); } } } /* * XINE EVENT THREAD * only the QT event thread should do GUI operations, * we use TQApplication::postEvent() and a reimplementation of TQObject::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; switch (xineEvent->type) { case XINE_EVENT_UI_PLAYBACK_FINISHED: { debugOut("xine event: playback finished"); TQApplication::postEvent(vw, new TQTimerEvent(TIMER_EVENT_PLAYBACK_FINISHED )); break; } case XINE_EVENT_UI_CHANNELS_CHANGED: /* new channel informations */ { debugOut("xine event: channels changed"); int i,channels; char* lang = new char[128]; TQString slang; int num; TQStringList tmp; bool update=false, sk; /*** get audio channels ***/ tmp.append(i18n("auto")); channels = xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_MAX_AUDIO_CHANNEL); for(i = 0; i < channels; i++) { slang = TQString("%1.").arg(i+1); if (xine_get_audio_lang(vw->m_xineStream, i, lang)) slang += lang; tmp << slang; } if ( tmp!=vw->m_audioCh ) { update = true; vw->m_audioCh = tmp; } num = xine_get_param(vw->m_xineStream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL); if ( vw->m_currentAudio!=num ) { update = true; if ( num>channels ) vw->m_currentAudio = -1; else vw->m_currentAudio = num; } /*** get subtitle channels ***/ tmp.clear(); tmp.append(i18n("off")); channels = xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_MAX_SPU_CHANNEL); for(i = 0; im_xineStream, i, lang)) slang += lang; tmp << slang; } if ( tmp!=vw->m_subCh ) { update = true; vw->m_subCh = tmp; } num = xine_get_param(vw->m_xineStream, XINE_PARAM_SPU_CHANNEL); if ( vw->m_currentSub!=num ) { update = true; if ( num>channels ) vw->m_currentSub = -1; else vw->m_currentSub = num; } delete [] lang; //check if stream is seekable sk = (bool)xine_get_stream_info(vw->m_xineStream, XINE_STREAM_INFO_SEEKABLE); if ( vw->m_trackIsSeekable!=sk ) { update = true; vw->m_trackIsSeekable = sk; } if ( update ) TQApplication::postEvent(vw, new TQTimerEvent(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 = TQString::fromLocal8Bit( (char*)xd->str ); vw->m_lengthInfoTries = 0; vw->m_lengthInfoTimer.start(1000); /* May be new Length on Changing DVD/VCD titles */ TQApplication::postEvent(vw, new TQTimerEvent(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 = TQString::fromLocal8Bit(pd->description) + " " + TQString::number(pd->percent) + "%"; TQApplication::postEvent(vw, new TQTimerEvent(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(TQString("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 left button"); vw->m_DVDButtonEntered = false; } TQApplication::postEvent(vw, new TQTimerEvent(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; TQApplication::postEvent(vw, new TQTimerEvent(TIMER_EVENT_NEW_MRL_REFERENCE)); break; } case XINE_EVENT_FRAME_FORMAT_CHANGE: { // debugOut("xine event: frame format change"); // TQApplication::postEvent(vw, new TQTimerEvent(TIMER_EVENT_FRAME_FORMAT_CHANGE)); break; } case XINE_EVENT_AUDIO_LEVEL: { TQApplication::postEvent(vw, new TQTimerEvent(TIMER_EVENT_NEW_VOLUME_LEVEL)); break; } case XINE_EVENT_UI_MESSAGE: { debugOut("xine event: xine message"); xine_ui_message_data_t *data = (xine_ui_message_data_t *)xineEvent->data; TQString message; 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 + "(" + TQString::fromLocal8Bit((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; TQApplication::postEvent(vw, new TQTimerEvent(TIMER_EVENT_NEW_XINE_MESSAGE)); break; } default: { //debugOut("xine event: unhandled type "); break; } } } void KXineWidget::timerEvent( TQTimerEvent* tevent ) { switch ( tevent->timerId() ) { case TIMER_EVENT_PLAYBACK_FINISHED: { if ( !TimeShiftFilename.isEmpty() ) { TQTimer::singleShot(0, this, TQ_SLOT(slotPlayTimeShift())); break; } if ( m_trackURL=="DVB" || m_trackURL.contains(".kaxtv") ) break; #ifdef XINE_PARAM_GAPLESS_SWITCH if ( xine_check_version(1,1,1) ) xine_set_param( m_xineStream, XINE_PARAM_GAPLESS_SWITCH, 1); #endif if (isQueueEmpty()) { if (m_trackURL != m_logoFile) emit signalPlaybackFinished(); else xine_stop(m_xineStream); } else TQTimer::singleShot(0, this, TQ_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(TQCursor(TQt::PointingHandCursor)); else setCursor(TQCursor(TQt::ArrowCursor)); break; } case TIMER_EVENT_NEW_MRL_REFERENCE: { m_queue.prepend(m_newMRLReference ); break; } case TIMER_EVENT_NEW_VOLUME_LEVEL: { emit signalSyncVolume(); break; } case TIMER_EVENT_NEW_XINE_MESSAGE: { if (!m_recentMessagesTimer.isActive()) { m_recentMessagesTimer.start(1500); emit signalXineMessage(m_xineMessage); } else { //restart warningOut(TQString("Message: '%1' was blocked!").arg(m_xineMessage)); m_recentMessagesTimer.start(1500); } break; } case TIMER_EVENT_NEW_XINE_ERROR: { emit signalXineError(m_xineError); break; } case TIMER_EVENT_RESTART_PLAYBACK: { appendToQueue(m_trackURL); slotPlay(); break; } case TIMER_EVENT_RESIZE_PARENT: { parentWidget()->resize(m_newParentSize); break; } default: break; } } void KXineWidget::slotNoRecentMessage() { m_recentMessagesTimer.stop(); } /******************* new video driver *********************/ void KXineWidget::videoDriverChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; if (entry == NULL) return; #ifndef USE_TQT_ONLY KXineWidget* vw = (KXineWidget*) p; xine_video_port_t* oldVideoDriver = vw->m_videoDriver; xine_video_port_t* noneVideoDriver; int pos, time, length; debugOut(TQString("New video driver: %1").arg(entry->enum_values[entry->num_value])); if (vw->m_osd) { xine_osd_free(vw->m_osd); vw->m_osd = NULL; } 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; int t = 0, ret = 0; while(((ret = xine_get_pos_length(vw->m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) xine_usec_sleep(100000); if ( ret != 0 ) 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(TQString("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, #ifndef HAVE_XCB entry->enum_values[entry->num_value], XINE_VISUAL_TYPE_X11, #else entry->enum_values[entry->num_value], XINE_VISUAL_TYPE_XCB, #endif (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); TQApplication::postEvent(vw, new TQTimerEvent( TIMER_EVENT_NEW_XINE_ERROR)); playing = false; vw->m_videoDriver = xine_open_video_driver(vw->m_xineEngine, #ifndef HAVE_XCB vw->m_videoDriverName.ascii(), XINE_VISUAL_TYPE_X11, #else vw->m_videoDriverName.ascii(), XINE_VISUAL_TYPE_XCB, #endif (void *) &(vw->m_x11Visual)); } else { vw->m_videoDriverName = entry->enum_values[entry->num_value]; vw->m_statusString = i18n("Using Video Driver: %1").arg(vw->m_videoDriverName); TQApplication::postEvent(vw, new TQTimerEvent(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) TQApplication::postEvent(vw, new TQTimerEvent(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_TQT_ONLY KXineWidget* vw = (KXineWidget*) p; int pos, time, length; debugOut(TQString("New audio driver: %1").arg(entry->enum_values[entry->num_value])); if (vw->m_osd) { xine_osd_free(vw->m_osd); vw->m_osd = NULL; } vw->unwireVideoFilters(); bool playing = false; if (vw->isPlaying()) { playing = true; vw->m_savedPos = 0; int t = 0, ret = 0; while(((ret = xine_get_pos_length(vw->m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) xine_usec_sleep(100000); if ( ret != 0 ) vw->m_savedPos = pos; } xine_close(vw->m_xineStream); vw->unwireAudioFilters(); if (vw->m_visualPlugin) { debugOut(TQString("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); TQApplication::postEvent(vw, new TQTimerEvent( TIMER_EVENT_NEW_XINE_ERROR)); playing = false; vw->m_audioDriver = xine_open_audio_driver(vw->m_xineEngine, vw->m_audioDriverName.ascii(), NULL); } else { vw->m_audioDriverName = entry->enum_values[entry->num_value]; vw->m_statusString = i18n("Using Audio Driver: %1").arg(vw->m_audioDriverName); TQApplication::postEvent(vw, new TQTimerEvent(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) TQApplication::postEvent(vw, new TQTimerEvent(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; } /******** Callback for OSD configuration ****************/ 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; } void KXineWidget::sizeForOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; const int fontsizetable[] = { 16,20,24,32,48,64 }; if (entry->num_value >= 6) { debugOut("Font size not defined: Shouldn't have happened"); return; } if (vw->m_osd) { vw->m_osdSize = entry->num_value; xine_osd_set_font(vw->m_osd, vw->m_osdFont, fontsizetable[vw->m_osdSize]); } } void KXineWidget::fontForOSDMessagesChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; const int fontsizetable[] = { 16,20,24,32,48,64 }; if (vw->m_osd) if (entry->str_value) { free(vw->m_osdFont); vw->m_osdFont = strdup(entry->str_value); if (!xine_osd_set_font(vw->m_osd, vw->m_osdFont, fontsizetable[vw->m_osdSize])) { free(vw->m_osdFont); vw->m_osdFont = strdup("sans"); if (!xine_osd_set_font(vw->m_osd, vw->m_osdFont, fontsizetable[vw->m_osdSize])) warningOut("Default SANS font not found: shouldn't have happened."); } } } void KXineWidget::monitorXResChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; vw->monitorXRes = (double)entry->num_value; double m_displayRatio = vw->monitorYRes / vw->monitorXRes; if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) m_displayRatio = 1; vw->m_displayRatio = m_displayRatio; } void KXineWidget::monitorYResChangedCallback(void* p, xine_cfg_entry_t* entry) { if (p == NULL) return; KXineWidget* vw = (KXineWidget*) p; vw->monitorYRes = (double)entry->num_value; double m_displayRatio = vw->monitorYRes / vw->monitorXRes; if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) m_displayRatio = 1; vw->m_displayRatio = m_displayRatio; } /********************************************** * EVENT LOOP *********************************************/ #ifndef HAVE_XCB bool KXineWidget::x11Event(XEvent *event) { if (isXineReady()) if (event->type == Expose) if (event->xexpose.count == 0) xine_port_send_gui_data(m_videoDriver, XINE_GUI_SEND_EXPOSE_EVENT, event); return false; } #else void KXineWidget::paintEvent(TQPaintEvent *event) { if (isXineReady()) { const TQRect &rect = event->rect(); xcb_expose_event_t xcb_event; memset(&xcb_event, 0, sizeof(xcb_event)); xcb_event.window = winId(); xcb_event.x = rect.x(); xcb_event.y = rect.y(); xcb_event.width = rect.width(); xcb_event.height = rect.height(); xcb_event.count = 0; xine_port_send_gui_data(m_videoDriver, XINE_GUI_SEND_EXPOSE_EVENT, &xcb_event); } TQWidget::paintEvent(event); } #endif /********************************************************** * 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 ****/ #ifndef HAVE_XCB XInitThreads(); connection = XOpenDisplay(NULL); #else int screen_nbr = 0; connection = xcb_connect(NULL, &screen_nbr); #endif if (!connection) { emit signalXineFatal(i18n("Failed to connect to X-Server!")); return false; } int m_xineWindow = winId(); /* // determine display aspect ratio double resHor = ((double) DisplayWidth(x11Display(), x11Screen())) / DisplayWidthMM(x11Display(), x11Screen()); double resVer = ((double) DisplayHeight(x11Display(), x11Screen())) / DisplayHeightMM(x11Display(), x11Screen()); m_displayRatio = resVer / resHor; if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) m_displayRatio = 1; #ifdef HAVE_XINERAMA int dummy_event, dummy_error; if (XineramaQueryExtension(x11Display(), &dummy_event, &dummy_error)) { int count = 1; debugOut("Xinerama extension present"); XineramaQueryScreens(x11Display(), &count); debugOut(TQString("%1 screens detected").arg(count)); if (count > 1) // multihead -> assuming square pixels m_displayRatio = 1.0; } #endif debugOut(TQString("Display aspect ratio (v/h): %1").arg(m_displayRatio));*/ /**** INIT XINE ENGINE ****/ debugOut(TQString("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 (!TQFile::exists(m_configFilePath)) warningOut("No config file found, will create one..."); else xine_config_load(m_xineEngine, TQFile::encodeName(m_configFilePath)); debugOut("Post-init xine engine"); xine_init(m_xineEngine); /** set xine parameters **/ const char* const* drivers = NULL; drivers = xine_list_audio_output_plugins(m_xineEngine); int i = 0; while (drivers[i] != NULL) i++; m_audioChoices = new char*[i+2]; m_audioChoices[0] = strdup("auto"); m_audioDriverList << m_audioChoices[0]; i = 0; while(drivers[i]) { m_audioChoices[i+1] = strdup(drivers[i]); m_audioDriverList << m_audioChoices[i+1]; i++; } m_audioChoices[i+1] = NULL; m_audioInfo = strdup(i18n("Audiodriver to use (default: auto)").local8Bit()); i = xine_config_register_enum(m_xineEngine, "audio.driver", 0, m_audioChoices, m_audioInfo, NULL, 10, &KXineWidget::audioDriverChangedCallback, this); if (m_audioDriverList.contains(m_preferedAudio)) m_audioDriverName = m_preferedAudio; else m_audioDriverName = m_audioChoices[i]; debugOut(TQString("Use audio driver %1").arg(m_audioDriverName)); drivers = xine_list_video_output_plugins(m_xineEngine); i = 0; while (drivers[i] != NULL) i++; m_videoChoices = new char*[i+2]; m_videoChoices[0] = strdup("auto"); m_videoDriverList << m_videoChoices[0]; i = 0; while(drivers[i]) { m_videoChoices[i+1] = strdup(drivers[i]); m_videoDriverList << m_videoChoices[i+1]; i++; } m_videoChoices[i+1] = NULL; m_videoInfo = strdup(i18n("Videodriver to use (default: auto)").local8Bit()); i = xine_config_register_enum(m_xineEngine, "video.driver", 0, m_videoChoices, m_videoInfo, NULL, 10, &KXineWidget::videoDriverChangedCallback, this); if (m_videoDriverList.contains(m_preferedVideo)) m_videoDriverName = m_preferedVideo; else m_videoDriverName = m_videoChoices[i]; debugOut(TQString("Use video driver %1").arg(m_videoDriverName)); m_mixerInfo = strdup(i18n("Use software audio mixer").local8Bit()); m_softwareMixer = (bool)xine_config_register_bool(m_xineEngine, "audio.mixer_software", 1, m_mixerInfo, NULL, 10, &KXineWidget::audioMixerMethodChangedCallback, this); m_osdShowInfo = strdup(i18n("Show OSD Messages").local8Bit()); m_osdShow = (bool)xine_config_register_bool(m_xineEngine, "osd.osd_messages", 1, m_osdShowInfo, NULL, 10, &KXineWidget::showOSDMessagesChangedCallback, this); m_osdSizeOptions = new char*[7]; m_osdSizeOptions[0] = strdup("tiny"); m_osdSizeOptions[1] = strdup("small"); m_osdSizeOptions[2] = strdup("medium"); m_osdSizeOptions[3] = strdup("large"); m_osdSizeOptions[4] = strdup("very large"); m_osdSizeOptions[5] = strdup("huge"); m_osdSizeOptions[6] = NULL; m_osdSizeInfo = strdup(i18n("Size of OSD text").local8Bit()); m_osdSize = (int)xine_config_register_enum(m_xineEngine, "osd.osd_size", 1 /*small - 20P*/, m_osdSizeOptions, m_osdSizeInfo, NULL, 10, &KXineWidget::sizeForOSDMessagesChangedCallback, this); m_osdFontInfo = strdup(i18n("Font for OSD Messages").local8Bit()); m_osdFont = strdup((char*)xine_config_register_string(m_xineEngine, "osd.osd_font", "sans", m_osdFontInfo, NULL, 10, &KXineWidget::fontForOSDMessagesChangedCallback, this)); xResInfo = strdup(i18n("Monitor horizontal resolution (dpi).").local8Bit()); monitorXRes = (bool)xine_config_register_range(m_xineEngine, "video.screen_x_res", 78, 1, 200, xResInfo, NULL, 10, &KXineWidget::monitorXResChangedCallback, this); yResInfo = strdup(i18n("Monitor vertical resolution (dpi).").local8Bit()); monitorYRes = (bool)xine_config_register_range(m_xineEngine, "video.screen_y_res", 78, 1, 200, yResInfo, NULL, 10, &KXineWidget::monitorYResChangedCallback, this); double resHor = (double)monitorXRes; double resVer = (double)monitorYRes; m_displayRatio = resVer / resHor; if ((m_displayRatio >= 0.98) && (m_displayRatio <= 1.02)) m_displayRatio = 1; /* init video driver */ debugOut("Init video driver"); #ifndef HAVE_XCB m_x11Visual.display = connection; m_x11Visual.screen = DefaultScreen(connection); m_x11Visual.d = m_xineWindow; #else xcb_screen_iterator_t screen_it = xcb_setup_roots_iterator(xcb_get_setup(connection)); while ((screen_it.rem > 1) && (screen_nbr > 0)) { xcb_screen_next(&screen_it); --screen_nbr; } m_x11Visual.connection = connection; m_x11Visual.screen = screen_it.data; m_x11Visual.window = m_xineWindow; #endif 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, #ifndef HAVE_XCB m_videoDriverName.ascii(), XINE_VISUAL_TYPE_X11, #else m_videoDriverName.ascii(), XINE_VISUAL_TYPE_XCB, #endif (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, #ifndef HAVE_XCB m_videoDriverName.ascii(), XINE_VISUAL_TYPE_X11, #else m_videoDriverName.ascii(), XINE_VISUAL_TYPE_XCB, #endif (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.ascii(), 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.ascii(), 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; } #ifdef XINE_PARAM_EARLY_FINISHED_EVENT if ( xine_check_version(1,1,1) ) { // enable gapless playback xine_set_param(m_xineStream, XINE_PARAM_EARLY_FINISHED_EVENT, 1 ); } #endif /*** OSD ***/ initOSD(); /** event handling **/ m_eventQueue = xine_event_new_queue (m_xineStream); xine_event_create_listener_thread(m_eventQueue, &KXineWidget::xineEventListener, (void*)this); //maybe user closed player in muted state if (m_softwareMixer) xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_MUTE, 0); else xine_set_param(m_xineStream, XINE_PARAM_AUDIO_MUTE, 0); m_xineReady = true; debugOut("xine init successful"); emit signalXineStatus(i18n("Ready")); emit signalXineReady(); /** something to play? **/ slotPlay(); return true; } void KXineWidget::initOSD() { debugOut("Init OSD"); const int fontsizetable[] = { 16,20,24,32,48,64 }; m_osd = xine_osd_new(m_xineStream, 10, 10, 1000, 200); if (m_osd) { if (!xine_osd_set_font(m_osd, m_osdFont, fontsizetable[m_osdSize])) { debugOut(TQString("Font ->%1<- specified for OSD doesn't exists.").arg(m_osdFont)); free(m_osdFont); m_osdFont = strdup("sans"); xine_osd_set_font(m_osd, m_osdFont, fontsizetable[m_osdSize]); } debugOut(TQString("Font for OSD: %1").arg(m_osdFont)); 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"); } else warningOut("Initialisation of xine OSD failed."); } /************************************************ * PLAY MRL ************************************************/ bool KXineWidget::playDvb() { #ifndef USE_TQT_ONLY unwireAudioFilters(); TQPtrList activeList; if (m_audioFilterList.count() && m_audioFiltersEnabled) activeList = m_audioFilterList; if ( !dvbHaveVideo ) { if (!m_visualPlugin) { debugOut(TQString("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(TQString("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 if (!xine_play(m_xineStream, 0,0)) { sendXineError(); return false; } m_currentSpeed = Normal; m_trackHasChapters = false; m_trackArtist = TQString(); m_trackAlbum = TQString(); m_trackNumber = TQString(); m_trackYear = TQString(); m_trackComment = TQString(); m_trackIsSeekable = false; if ( !dvbHaveVideo ) m_trackHasVideo = false; else 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 = TQString(); 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 = TQString(); m_trackAudioBitrate = 0; } m_trackLength = getLengthInfo(); slotSetAudioChannel(0); //refresh channel info m_posTimer.start(1000); emit signalXinePlaying(); emit signalXineStatus(i18n("Playing")); return true; } #ifndef TQ_BYTE_ORDER #error TQ_BYTE_ORDER is not defined! #endif #ifndef TQ_LITTLE_ENDIAN #error TQ_LITTLE_ENDIAN is not defined! #endif #if TQ_BYTE_ORDER == TQ_LITTLE_ENDIAN #define rgb2yuv(R,G,B) ((((((66*R+129*G+25*B+128)>>8)+16)<<8)|(((112*R-94*G-18*B+128)>>8)+128))<<8|(((-38*R-74*G+112*B+128)>>8)+128)) #else #define rgb2yuv(R,G,B) (unsigned int)(((((((-38*R-74*G+112*B+128)>>8)+128)<<8)| (((112*R-94*G-18*B+128)>>8)+128))<<8|(((66*R+129*G+25*B+128)>>8)+16))<<8) #endif void KXineWidget::initDvbPalette() { if ( dvbColor[0] ) return; memset( dvbColor, 0, sizeof(dvbColor) ); memset( dvbTrans, 0, sizeof(dvbTrans) ); dvbColor[0]=1; unsigned int blueText[11] = { rgb2yuv(0,0,0), /* 0 : not used */ rgb2yuv(0,0,0), /* 1 : font bg */ rgb2yuv(10,50,40), /* 2 : transition bg->border */ rgb2yuv(30,100,85), /* 3 */ rgb2yuv(50,150,130), /* 4 */ rgb2yuv(70,200,175), /* 5 */ rgb2yuv(90,255,220), /* 6 : border */ rgb2yuv(90,255,220), /* 7 : transition border->fg */ rgb2yuv(90,255,220), /* 8 */ rgb2yuv(90,255,220), /* 9 */ rgb2yuv(90,255,220), /* 10 : font fg */ }; unsigned int whiteText[11] = { rgb2yuv(0,0,0), rgb2yuv(0,0,0), rgb2yuv(50,50,50), rgb2yuv(100,100,100), rgb2yuv(150,150,150), rgb2yuv(200,200,200), rgb2yuv(255,255,255), rgb2yuv(255,255,255), rgb2yuv(255,255,255), rgb2yuv(255,255,255), rgb2yuv(255,255,255), }; unsigned int greenText[11] = { rgb2yuv(0,0,0), rgb2yuv(0,0,0), rgb2yuv(30,50,30), rgb2yuv(60,100,30), rgb2yuv(90,150,90), rgb2yuv(120,200,120), rgb2yuv(150,255,150), rgb2yuv(150,255,150), rgb2yuv(150,255,150), rgb2yuv(150,255,150), rgb2yuv(150,255,150), }; unsigned char textAlpha[11] = { 0, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, }; #define DVB_TEXT_WHITE 100 #define DVB_TEXT_BLUE 111 #define DVB_TEXT_GREEN 122 int a; for ( a=DVB_TEXT_BLUE; aw ) { wrap = true; break; } if ( pos==-1 ) break; prevPos = pos; pos = source.find(" ",pos+1); dest = source.left( pos ); } if ( wrap ) { dest = source.left( prevPos ); source = source.right( source.length()-dest.length()-1 ); } else { dest = source; source = ""; } } void KXineWidget::dvbShowOSD() { if ( m_trackURL!="DVB" ) return; if ( xine_get_status(m_xineStream)!=XINE_STATUS_PLAY ) return; if ( !dvbHaveVideo ) m_trackHasVideo = false; else 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 = TQString(); 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 = TQString(); m_trackAudioBitrate = 0; } if ( dvbOSD ) { xine_osd_free( dvbOSD ); dvbOSD = 0; } int border=40; int w = m_videoFrameWidth; int h = m_videoFrameHeight; if ( !w || !h ) return; if ( w<1921 ) { if ( dvbCurrentNext[0]=="E" ) { dvbOSDHideTimer.stop(); dvbOSD = xine_osd_new( m_xineStream, border, border, w-(2*border), h-(2/border) ); } else dvbOSD = xine_osd_new( m_xineStream, border, h-border-100, w-(2*border), 100 ); } if ( dvbOSD ) { TQCString ct, cs; if ( !dvbColor[0] ) initDvbPalette(); xine_osd_set_palette( dvbOSD, dvbColor, dvbTrans ); xine_osd_set_font( dvbOSD, m_osdFont, 16 ); xine_osd_set_encoding( dvbOSD, "utf-8" ); if ( dvbCurrentNext[0]=="E" ) xine_osd_draw_rect( dvbOSD, 0, 0, w-(2*border), h-(2*border), DVB_TEXT_WHITE+1, 1 ); else xine_osd_draw_rect( dvbOSD, 0, 0, w-(2*border), 100, DVB_TEXT_WHITE+1, 1 ); TQString t = TQTime::currentTime().toString( "hh:mm" ); int tw, th, len; xine_osd_get_text_size(dvbOSD, t.utf8(), &tw, &th); len = tw; int offset = 5; xine_osd_draw_text( dvbOSD, w-(2*border)-tw-offset, 5, t.utf8(), DVB_TEXT_BLUE ); int i; for ( i=0; i<(int)dvbCurrentNext.count(); i++ ) { if ( dvbCurrentNext[i]=="R" ) { xine_osd_draw_rect( dvbOSD, offset, 5, offset+16, 21, DVB_COLOR_RED, 1 ); offset+=21; } else if ( dvbCurrentNext[i]=="T" ) { xine_osd_draw_rect( dvbOSD, offset, 5, offset+16, 21, DVB_COLOR_GREEN, 1 ); offset+=21; } } if (m_dvbChannelName == "") t = m_trackTitle; else t = m_dvbChannelName; i=0; ct = t.utf8(); while ( i<(int)t.length() ) { xine_osd_get_text_size( dvbOSD, ct, &tw, &th ); if ( tw<=(w-(2*border)-offset-5-len) ) break; ct = ct.remove( ct.length()-1, ct.length() ); i++; } xine_osd_draw_text( dvbOSD, offset, 5, ct, DVB_TEXT_BLUE ); xine_osd_draw_line( dvbOSD, 5, 10+18, w-(2*border)-5, 10+18, DVB_COLOR_MAGENTA ); TQString s, c; int y=43; int pos; if ( dvbCurrentNext[0]=="E" ) { if ( dvbCurrentNext.count()<2 ) { xine_osd_show( dvbOSD, 0 ); return; } if ( !dvbCurrentNext[1].isEmpty() ) { s = dvbCurrentNext[1]; pos = s.find("-"); c = s.left( pos+1 ); s = s.right( s.length()-pos-1 ); t = s; xine_osd_draw_text( dvbOSD, 10, y, c.utf8(), DVB_TEXT_GREEN ); xine_osd_get_text_size( dvbOSD, c.utf8(), &offset, &th ); i=0; cs = s.utf8(); while ( i<(int)t.length() ) { ct = cs.remove( cs.length()-i, cs.length() ); xine_osd_get_text_size( dvbOSD, ct, &tw, &th ); if ( tw<=(w-(2*border)-20-offset) ) break; i++; } xine_osd_draw_text( dvbOSD, 10+offset, y, ct, DVB_TEXT_WHITE ); y+= 40; } if ( !dvbCurrentNext[2].isEmpty() ) { cs = dvbCurrentNext[2].utf8(); while ( y<(h-(2*border)-23) ) { getOSDLine( dvbOSD, (w-(2*border)-20), ct, cs ); xine_osd_draw_text( dvbOSD, 10, y, ct, DVB_TEXT_BLUE ); y+= 28; if ( !cs.length() ) break; } y+= 40; } if ( !dvbCurrentNext[3].isEmpty() ) { cs = dvbCurrentNext[3].utf8(); while ( y<(h-(2*border)-23) ) { getOSDLine( dvbOSD, (w-(2*border)-20), ct, cs ); xine_osd_draw_text( dvbOSD, 10, y, ct, DVB_TEXT_WHITE ); y+= 28; if ( !cs.length() ) break; } } xine_osd_show( dvbOSD, 0 ); return; } int bar=-1; int barOffset=0; for ( int j=0; j<(int)dvbCurrentNext.count(); j++ ) { if ( (dvbCurrentNext[ j ]=="T") || (dvbCurrentNext[ j ]=="R") ) continue; if ( dvbCurrentNext[ j ].startsWith("BAR") ) { s = dvbCurrentNext[ j ]; s = s.remove("BAR"); bar = s.toInt(); //fprintf( stderr, "BAR : %d\n", bar ); continue; } s = dvbCurrentNext[ j ]; pos = s.find("-"); c = s.left( pos+1 ); s = s.right( s.length()-pos-1 ); ct = cs = s.utf8(); xine_osd_draw_text( dvbOSD, 10, y, c.utf8(), DVB_TEXT_GREEN ); xine_osd_get_text_size( dvbOSD, c.utf8(), &offset, &th ); i=0; while ( i<(int)t.length() ) { ct = cs.remove( cs.length()-i, cs.length() ); xine_osd_get_text_size( dvbOSD, ct, &tw, &th ); if ( tw<=(w-(2*border)-20-offset-35) ) break; i++; } if ( !barOffset ) barOffset = tw; xine_osd_draw_text( dvbOSD, 10+offset, y, ct, DVB_TEXT_WHITE ); y+= 28; if ( bar>-1 ) { int x1=10+offset+barOffset+10; int x2=w-(2*border)-5; if ( (x1+30)>x2 ) x1 = x2 - 30; int x3=x1+((bar*(x2-x1))/100); //fprintf( stderr, "x1=%d - x2=%d - x3=%d\n", x1, x2, x3 ); int y1=46; int y2=60; xine_osd_draw_line( dvbOSD, x1, y1, x2, y1, DVB_COLOR_BAR ); xine_osd_draw_line( dvbOSD, x1, y2, x2, y2, DVB_COLOR_BAR ); xine_osd_draw_line( dvbOSD, x1, y1, x1, y2, DVB_COLOR_BAR ); xine_osd_draw_line( dvbOSD, x2, y1, x2, y2, DVB_COLOR_BAR ); xine_osd_draw_rect( dvbOSD, x1, y1, x3, y2, DVB_COLOR_BAR, 1 ); } } xine_osd_show( dvbOSD, 0 ); dvbOSDHideTimer.start( 5000, true ); } } void KXineWidget::dvbHideOSD() { if ( dvbOSD ) { xine_osd_hide( dvbOSD, 0 ); xine_osd_free( dvbOSD ); dvbOSD = 0; if (m_dvbChannelName != "") m_dvbChannelName = ""; emit signalDvbOSDHidden(); } } void KXineWidget::setDvbCurrentNext( const TQString &channelName, const TQStringList &list ) { if ( list[0]=="STOP" ) { dvbHideOSD(); return; } dvbCurrentNext = list; m_dvbChannelName = channelName; TQTimer::singleShot( 0, this, TQ_SLOT(dvbShowOSD()) ); } void KXineWidget::setDvb( const TQString &pipeName, const TQString &chanName, int haveVideo ) { m_trackURL = /*"fifo://"+*/pipeName; m_trackTitle = chanName; dvbHaveVideo = haveVideo; } bool KXineWidget::openDvb() { if ( dvbOSD ) { dvbOSDHideTimer.stop(); xine_osd_hide( dvbOSD, 0 ); xine_osd_free( dvbOSD ); dvbOSD = 0; } clearQueue(); m_lengthInfoTimer.stop(); m_posTimer.stop(); xine_set_param( m_xineStream, XINE_PARAM_METRONOM_PREBUFFER, 180000); if (!xine_open(m_xineStream, TQFile::encodeName(m_trackURL))) { sendXineError(); return false; } else fprintf(stderr,"xine pipe opened %s\n", m_trackURL.ascii()); m_trackURL = "DVB"; emit signalXineStatus(i18n("DVB: opening...")); TQTimer::singleShot( 0, this, TQ_SLOT(playDvb()) ); return true; } void KXineWidget::slotPlayTimeShift() { m_lengthInfoTimer.stop(); m_posTimer.stop(); xine_set_param( m_xineStream, XINE_PARAM_METRONOM_PREBUFFER, 0); if (!xine_open(m_xineStream, TQFile::encodeName(TimeShiftFilename))) { sendXineError(); #ifdef XINE_PARAM_GAPLESS_SWITCH if ( xine_check_version(1,1,1) ) xine_set_param( m_xineStream, XINE_PARAM_GAPLESS_SWITCH, 0); #endif return; } if (!xine_play(m_xineStream, 0,0)) { sendXineError(); return; } m_trackIsSeekable = true; m_lengthInfoTimer.start(1000); m_posTimer.start(1000); } void KXineWidget::slotPlay() { if ((!isXineReady()) || (isQueueEmpty())) return; if (m_logoFile != NULL && m_trackURL == m_logoFile && isPlaying()) return; /* dvb */ if ( dvbOSD ) { dvbOSDHideTimer.stop(); xine_osd_hide( dvbOSD, 0 ); xine_osd_free( dvbOSD ); dvbOSD = 0; } m_lengthInfoTimer.stop(); m_posTimer.stop(); m_currentSpeed = Normal; setCursor(TQCursor(TQt::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 = TQString(); m_trackSaveURL = TQString(); /*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); }*/ TQString turl; int pos; if ( (pos=m_trackURL.find("#subtitle:"))>-1 ) { turl = m_trackURL.left(pos); m_trackSubtitleURL = m_trackURL.right( m_trackURL.length()-pos ); if ( (pos=m_trackSubtitleURL.find("#save:"))>-1 ) { m_trackSaveURL = m_trackSubtitleURL.right( m_trackSubtitleURL.length()-pos ); m_trackSubtitleURL = m_trackSubtitleURL.left(pos); } } else if ( (pos=m_trackURL.find("#save:"))>-1 ) { turl = m_trackURL.left(pos); m_trackSaveURL = m_trackURL.right( m_trackURL.length()-pos ); } else turl = m_trackURL; m_trackSubtitleURL.remove("#subtitle:"); m_trackSaveURL.remove("#save:"); turl = turl.replace( "%", "%25" ).replace( "#", "%23" ).replace( ";", "%3b" ).replace( " ", "%20" ); if ( !m_trackSubtitleURL.isEmpty() ) turl = turl + "#subtitle:" + m_trackSubtitleURL.replace( "%", "%25" ).replace( "#", "%23" ).replace( ";", "%3b" ).replace( " ", "%20" ); if ( !m_trackSaveURL.isEmpty() ) turl = turl + "#save:" + m_trackSaveURL.replace( "%", "%25" ).replace( "#", "%23" ).replace( ";", "%3b" ).replace( " ", "%20" ); if ( turl.startsWith("/") ) turl.prepend("file://"); debugOut(TQString("Playing: %1").arg(turl.local8Bit().data())); xine_set_param( m_xineStream, XINE_PARAM_METRONOM_PREBUFFER, 12000 ); if (!xine_open(m_xineStream, TQFile::encodeName(turl))) { sendXineError(); setCursor(TQCursor(TQt::ArrowCursor)); #ifdef XINE_PARAM_GAPLESS_SWITCH if ( xine_check_version(1,1,1) ) xine_set_param( m_xineStream, XINE_PARAM_GAPLESS_SWITCH, 0); #endif return; } /**** use visualization ? ****/ #ifndef USE_TQT_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(TQString("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(TQString("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 ***/ int savedPos = m_savedPos; m_savedPos = 0; if (!xine_play(m_xineStream, savedPos, 0)) { sendXineError(); setCursor(TQCursor(TQt::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 = TQString(); bool currentUtf8Locale; TQTextCodec* testUtf8Local = TQTextCodec::codecForLocale(); if (!strcmp(testUtf8Local->name(),"UTF-8")) currentUtf8Locale = true; else currentUtf8Locale = false; TQTextCodec *CodecUtf8; CodecUtf8 = TQTextCodec::codecForName("UTF-8"); TQString infotag; infotag = TQString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_TITLE)); if (currentUtf8Locale) m_trackTitle = infotag; else m_trackTitle = TQString::fromLocal8Bit(infotag.ascii()); if (CodecUtf8->heuristicContentMatch(infotag.ascii(), infotag.length()) >= 0) m_trackTitle = TQString::fromUtf8(infotag.ascii()); if ((!m_trackTitle.isNull()) && (!m_trackTitle.isEmpty())) /* no meta? */ { TQString trackArtist=NULL; TQString trackAlbum=NULL; TQString trackComment=NULL; trackArtist = TQString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_ARTIST)); trackAlbum = TQString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_ALBUM)); trackComment = TQString::fromLatin1(xine_get_meta_info(m_xineStream, XINE_META_INFO_COMMENT)); if (currentUtf8Locale) { m_trackArtist = trackArtist; m_trackAlbum = trackAlbum; m_trackComment = trackComment; } else { m_trackArtist = TQString::fromLocal8Bit(trackArtist.ascii()); m_trackAlbum = TQString::fromLocal8Bit(trackAlbum.ascii()); m_trackComment = TQString::fromLocal8Bit(trackComment.ascii()); } if (CodecUtf8->heuristicContentMatch(trackArtist.ascii(), trackArtist.length()) >= 0) m_trackArtist = TQString::fromUtf8(trackArtist.ascii()); if (CodecUtf8->heuristicContentMatch(trackAlbum.ascii(), trackAlbum.length()) >= 0) m_trackAlbum = TQString::fromUtf8(trackAlbum.ascii()); if (CodecUtf8->heuristicContentMatch(trackComment.ascii(), trackComment.length()) >= 0) m_trackComment = TQString::fromUtf8(trackComment.ascii()); m_trackYear = xine_get_meta_info(m_xineStream, XINE_META_INFO_YEAR); m_trackNumber = xine_get_meta_info(m_xineStream, XINE_META_INFO_TRACK_NUMBER); } else { m_trackArtist = TQString(); m_trackAlbum = TQString(); m_trackNumber = TQString(); m_trackYear = TQString(); m_trackComment = TQString(); } 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 = TQString(); 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 = TQString(); m_trackAudioBitrate = 0; } /*** we need a little delay for some meta info ***/ TQTimer::singleShot(1000, this, TQ_SLOT(slotGetInfoDelayed())); m_trackLength = getLengthInfo(); if ((m_trackLength.isNull()) && (m_trackURL != m_logoFile)) { debugOut("Wait for valid length information"); m_lengthInfoTries = 0; 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(200); setCursor(TQCursor(TQt::ArrowCursor)); if (m_trackURL != m_logoFile) { emit signalXinePlaying(); if (hasSaveURL()) emit signalXineStatus(i18n("Recording")); else emit signalXineStatus(i18n("Playing")); } } int KXineWidget::getVideoWidth() { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_WIDTH); } int KXineWidget::getVideoHeight() { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_VIDEO_HEIGHT); } void KXineWidget::slotGetInfoDelayed() { if (!m_xineStream) return; if (m_trackHasVideo) m_trackVideoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); if (m_trackHasAudio) m_trackAudioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); } /****** error processing ****/ void KXineWidget::sendXineError() { TQString error; int errCode = xine_get_error(m_xineStream); TQString addInfo; TQString audioCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_AUDIOCODEC); TQString videoCodec = xine_get_meta_info(m_xineStream, XINE_META_INFO_VIDEOCODEC); if ((!audioCodec.isEmpty()) || (!videoCodec.isEmpty())) { if (!audioCodec.isEmpty()) addInfo.append(TQString("(") + i18n("Audio Codec") + ": " + audioCodec + ")"); if (!videoCodec.isEmpty()) addInfo.append(TQString("(") + i18n("Video Codec") + ": " + videoCodec + ")"); } else addInfo.append(TQString("(") + 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(TQString("Can't play: %1 - trying next").arg(m_trackURL)); TQTimer::singleShot(0, this, TQ_SLOT(slotPlay())); } } bool KXineWidget::isPlaying() const { if (isXineReady()) return ((xine_get_status(m_xineStream) == XINE_STATUS_PLAY) && (m_trackURL != m_logoFile)); else return false; } TQString KXineWidget::getXineLog() const { TQString logStr; int i = 0; TQTextStream ts(&logStr, IO_WriteOnly); const char* const* log = xine_get_log(m_xineEngine, /* XINE_LOG_MSG*/ 0); if (log == NULL) return TQString(); while(log[i]) { ts << TQString::fromLocal8Bit(log[i]); i++; } return logStr; } void KXineWidget::showOSDMessage(const TQString& message, uint duration, int priority) { if ((!m_osd) || (!m_osdShow) || (isHidden())) return; static int prevOsdPriority = 0; if (m_osdTimer.isActive() && prevOsdPriority > priority) return; prevOsdPriority = priority; //debugOut(TQString("OSD: draw text: %1").arg(message)); xine_osd_clear(m_osd); xine_osd_draw_text(m_osd, 0, 0, message.local8Bit(), 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_TQT_ONLY /****************** postprocessing filter management ****************/ TQStringList KXineWidget::getVideoFilterNames() const { TQStringList 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; } TQStringList KXineWidget::getAudioFilterNames() const { TQStringList 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 TQString& name, TQWidget* parent) { #ifndef USE_TQT_ONLY unwireVideoFilters(); PostFilter* filter = new PostFilter(name, m_xineEngine, m_audioDriver, m_videoDriver, parent); connect(filter, TQ_SIGNAL(signalDeleteMe(PostFilter*)), this, TQ_SLOT(slotDeleteVideoFilter(PostFilter*))); m_videoFilterList.append(filter); wireVideoFilters(); #else parent = parent; warningOut(TQString("Not implemented [CreateVideoFilter %1]").arg(name)); #endif } void KXineWidget::slotCreateAudioFilter(const TQString& name, TQWidget* parent) { #ifndef USE_TQT_ONLY unwireAudioFilters(); PostFilter* filter = new PostFilter(name, m_xineEngine, m_audioDriver, m_videoDriver, parent); connect(filter, TQ_SIGNAL(signalDeleteMe(PostFilter*)), this, TQ_SLOT(slotDeleteAudioFilter(PostFilter*))); m_audioFilterList.append(filter); wireAudioFilters(); #else parent = parent; warningOut(TQString("Not implemented [CreateAudioFilter %1]").arg(name)); #endif } void KXineWidget::slotRemoveAllVideoFilters() { #ifndef USE_TQT_ONLY unwireVideoFilters(); while (m_videoFilterList.count()) m_videoFilterList.removeLast(); wireVideoFilters(); #else warningOut("Not implemented!"); #endif } void KXineWidget::slotRemoveAllAudioFilters() { #ifndef USE_TQT_ONLY unwireAudioFilters(); while (m_audioFilterList.count()) m_audioFilterList.removeLast(); wireAudioFilters(); #else warningOut("Not implemented!"); #endif } void KXineWidget::slotDeleteVideoFilter(PostFilter* filter) { #ifndef USE_TQT_ONLY unwireVideoFilters(); m_videoFilterList.remove(filter); wireVideoFilters(); #else filter = filter; warningOut("Not implemented!"); #endif } void KXineWidget::slotDeleteAudioFilter(PostFilter* filter) { #ifndef USE_TQT_ONLY unwireAudioFilters(); m_audioFilterList.remove(filter); wireAudioFilters(); #else filter = filter; warningOut("Not implemented!"); #endif } #ifndef USE_TQT_ONLY void KXineWidget::unwireVideoFilters() { if (m_xineStream && m_videoDriver) xine_post_wire_video_port(xine_get_video_source(m_xineStream), m_videoDriver); } void KXineWidget::wireVideoFilters() { if (!m_xineStream) { debugOut("wireVideoFilters() - xine stream not initialized, nothing happend."); return; } TQPtrList 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() { if (m_xineStream && m_audioDriver) xine_post_wire_audio_port( xine_get_audio_source (m_xineStream), m_audioDriver); } void KXineWidget::wireAudioFilters() { if (!m_xineStream) { debugOut("wireAudioFilters() - xine stream not initialized, nothing happend."); return; } TQPtrList 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.ascii()) { if (!m_visualPlugin) { debugOut(TQString("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(TQString("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_TQT_ONLY m_videoFiltersEnabled = enable; unwireVideoFilters(); wireVideoFilters(); #else enable = enable; warningOut("Not implemented!"); #endif } void KXineWidget::slotEnableAudioFilters(bool enable) { #ifndef USE_TQT_ONLY m_audioFiltersEnabled = enable; unwireAudioFilters(); wireAudioFilters(); #else enable = enable; warningOut("Not implemented!"); #endif } #ifndef USE_TQT_ONLY TQStringList KXineWidget::getAudioFilterConfig() { TQStringList configStrings; for (uint i=0; igetConfig(); return configStrings; } TQStringList KXineWidget::getVideoFilterConfig() { TQStringList configStrings; for (uint i=0; igetConfig(); return configStrings; } #endif /**** visual plugin **********/ TQStringList KXineWidget::getVisualPlugins() const { TQStringList 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 TQString& visual) { if (m_visualPluginName == visual) return; debugOut(TQString("New visualization plugin: %1").arg(visual)); #ifndef USE_TQT_ONLY unwireAudioFilters(); if(m_visualPlugin) { delete m_visualPlugin; m_visualPlugin = NULL; } if (visual == "none") m_visualPluginName = TQString(); else m_visualPluginName = visual; wireAudioFilters(); #else if (visual == "none") m_visualPluginName = TQString(); 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(TQStringList& 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 TQString& plugin, TQStringList& list) { #if XINE_MAJOR_VERSION > 1 || ( XINE_MAJOR_VERSION == 1 && XINE_MINOR_VERSION >= 2 ) const char* const* urls = NULL; #else char** urls = NULL; #endif int num; int i = 0; urls = xine_get_autoplay_mrls(m_xineEngine, plugin.ascii(), &num); if (urls) { while (urls[i]) { list << urls[i]; i++; } return true; } else { return false; } } int KXineWidget::makeVolumeLogarithmic(int volume) { // We're using a logarithmic function to make the volume ramp more natural. return static_cast( 100 - 100.0 * std::log10( ( 100 - volume ) * 0.09 + 1.0 ) ); } void KXineWidget::slotSetVolume(int vol) { if (!isXineReady()) return; if (m_softwareMixer) { int xine_vol = vol; if (s_logarithmicVolume) { xine_vol = makeVolumeLogarithmic(vol); } //debugOut(TQString("Set software amplification level: %1").arg(vol)); if (m_volumeGain) xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL, xine_vol*2); else xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL, xine_vol); } else { //debugOut(TQString("Set audio mixer volume: %1").arg(vol)); xine_set_param(m_xineStream, XINE_PARAM_AUDIO_VOLUME, vol); } emit signalXineStatus(i18n("Volume") + ": " + TQString::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); if (vol > 200) { // when amp is high > 100, xine_get_param sometimes returns incorrect amp level errorOut("Amp level returned weird results, set Amp to 200"); vol = 200; } if (m_volumeGain) vol = vol/2; } else { vol = xine_get_param(m_xineStream, XINE_PARAM_AUDIO_VOLUME); } return vol; } void KXineWidget::slotToggleMute() { int muteParam; if (m_softwareMixer) muteParam = XINE_PARAM_AUDIO_AMP_MUTE; else muteParam = XINE_PARAM_AUDIO_MUTE; if (xine_get_param(m_xineStream, muteParam)) { xine_set_param(m_xineStream, muteParam, 0); /* mute off */ emit signalXineStatus(i18n("Mute Off")); } else { xine_set_param(m_xineStream, muteParam, 1); /* mute on */ emit signalXineStatus(i18n("Mute On")); } } bool KXineWidget::SoftwareMixing() const { if (m_softwareMixer) return true; else return false; } void KXineWidget::mouseMoveEvent(TQMouseEvent* mev) { if (!m_xineReady) return; if (cursor().shape() == TQt::BlankCursor) { setCursor(TQCursor(TQt::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_port_send_gui_data (m_videoDriver, 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); mev->ignore(); } void KXineWidget::mousePressEvent(TQMouseEvent* mev) { if (!m_xineReady) return; int cur = cursor().shape(); if (mev->button() == TQt::MidButton) { emit signalMiddleClick(); mev->ignore(); return; } if (mev->button() == TQt::RightButton) { if ( (cur == TQt::ArrowCursor) || (cur == TQt::BlankCursor) ) { emit signalRightClick(mev->globalPos()); mev->accept(); return; } } if (mev->button() == TQt::LeftButton) { if ( (cur == TQt::ArrowCursor) || (cur == TQt::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_port_send_gui_data(m_videoDriver, 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(TQMouseEvent* mev) { emit signalDoubleClick(); mev->ignore(); } void KXineWidget::wheelEvent(TQWheelEvent* e) { int oldVal = getPosition(); if (oldVal == 0) // no valid position return; float offset = log10( TQABS(e->delta()) ) / 0.002; int newVal = 0; if (e->delta()>0) newVal = oldVal - int(offset); else newVal = oldVal + int(offset); if (newVal < 0) newVal = 0; 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(); if ( m_lengthInfoTimer.isActive() ) m_lengthInfoTimer.stop(); //emit signalNewPosition(0, TQTime()); if ((m_logoFile.isNull()) && (isPlaying())) xine_stop(m_xineStream); else { appendToQueue(m_logoFile); TQTimer::singleShot(0, this, TQ_SLOT(slotPlay())); } emit signalXineStatus(i18n("Stop")); } void KXineWidget::slotSetAudiocdDevice(const TQString& device) { debugOut(TQString("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 = TQFile::encodeName(device).data(); xine_config_update_entry (m_xineEngine, &config); } void KXineWidget::slotSetVcdDevice(const TQString& device) { debugOut(TQString("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 = TQFile::encodeName(device).data(); xine_config_update_entry (m_xineEngine, &config); } void KXineWidget::slotSetDvdDevice(const TQString& device) { debugOut(TQString("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 = TQFile::encodeName(device).data(); xine_config_update_entry (m_xineEngine, &config); } TQString KXineWidget::audiocdDevice() const { xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.cdda_device", &config); return TQFile::decodeName(config.str_value); } TQString KXineWidget::vcdDevice() const { xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.vcd_device", &config); return TQFile::decodeName(config.str_value); } TQString KXineWidget::dvdDevice() const { xine_cfg_entry_t config; xine_config_lookup_entry(m_xineEngine, "input.dvd_device", &config); return TQFile::decodeName(config.str_value); } uint KXineWidget::currentDVDTitleNumber() const { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_TITLE_NUMBER); } uint KXineWidget::getDVDTitleCount() const { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_TITLE_COUNT); } uint KXineWidget::currentDVDChapterNumber() const { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_CHAPTER_NUMBER); } uint KXineWidget::getDVDChapterCount() const { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_CHAPTER_COUNT); } uint KXineWidget::currentDVDAngleNumber() const { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_ANGLE_NUMBER); } uint KXineWidget::getDVDAngleCount() const { return xine_get_stream_info(m_xineStream, XINE_STREAM_INFO_DVD_ANGLE_COUNT); } void KXineWidget::setStreamSaveDir(const TQString& dir) { xine_cfg_entry_t config; if (!xine_config_lookup_entry(m_xineEngine, "misc.save_dir", &config)) return; /* older xine-lib */ debugOut(TQString("Set misc.save_dir to: %1").arg(dir)); config.str_value = TQFile::encodeName(dir).data(); xine_config_update_entry (m_xineEngine, &config); } const TQString KXineWidget::getStreamSaveDir() { xine_cfg_entry_t config; if (!xine_config_lookup_entry(m_xineEngine, "misc.save_dir", &config)) return TQString(); /* older xine-lib */ return TQFile::decodeName(config.str_value); } void KXineWidget::setBroadcasterPort(const uint port) { debugOut(TQString("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 if (m_trackURL != m_logoFile) // don't pause logo { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_PAUSE); m_posTimer.stop(); if (m_currentSpeed != Undefined) emit signalXineStatus(i18n("Pause")); m_currentSpeed = Pause; } } void KXineWidget::slotSpeedNormal() { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_NORMAL); m_posTimer.start(200); 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; } case Slow1: { slotSpeedNormal(); break; } case Slow2: { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_SLOW_2); m_currentSpeed = Slow1; emit signalXineStatus(i18n("Slow Motion %1").arg("x1")); 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; } case Fast1: { slotSpeedNormal(); break; } case Fast2: { xine_set_param(m_xineStream, XINE_PARAM_SPEED, XINE_SPEED_FAST_2); m_currentSpeed = Fast1; emit signalXineStatus(i18n("Fast Forward %1").arg("x1")); 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; } } } TQString KXineWidget::getSupportedExtensions() const { return xine_get_file_extensions(m_xineEngine); } void KXineWidget::slotSetAudioChannel(int ch) { debugOut(TQString("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(TQString("Switch to subtitle channel %1").arg(ch-1)); xine_set_param(m_xineStream, XINE_PARAM_SPU_CHANNEL, ch-1); } void KXineWidget::slotSetFileSubtitles(TQString url) { int pos; int time; int length; m_queue.prepend(url); int t = 0, ret = 0; while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) xine_usec_sleep(100000); if ( ret == 0 ) { 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; int t = 0, ret = 0; while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) xine_usec_sleep(100000); if ( ret == 0 ) { debugOut("No valid stream position information"); return 0; } return (uint)pos; } TQTime KXineWidget::getPlaytime() const { if (!m_xineReady) return TQTime(); int pos, time, length; int t = 0, ret = 0; while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) xine_usec_sleep(100000); if ( ret == 0 ) { debugOut("No valid stream position information"); return TQTime(); } return msToTime(time); } void KXineWidget::slotSendPosition() { if (!m_xineReady) return; int pos, time, length; int t = 0, ret = 0; while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) xine_usec_sleep(100000); if ( ret == 0 ) { debugOut("No valid stream position information"); return; } emit signalNewPosition(pos, msToTime(time)); } void KXineWidget::slotStartSeeking() { debugOut("Seeking started"); m_posTimer.stop(); } void KXineWidget::run() { if ( seekThreadPos ) xine_play(m_xineStream, seekThreadPos, 0); else if ( seekThreadTime ) xine_play(m_xineStream, 0, seekThreadTime); else xine_play(m_xineStream, seekThreadPos, 0); if ( seekThreadPause ) { m_currentSpeed = Undefined; slotSpeedPause(); } } void KXineWidget::slotSeekToPositionBlocking(int pos) { while ( running() ) usleep( 1000 ); if ( pos!=seekThreadPos ) slotSeekToPosition(pos); } void KXineWidget::slotSeekToPosition(int pos) { if ( running() ) { return; } if ((!isXineReady()) || (!isPlaying()) || (!isSeekable())) return; seekThreadPause = false; if (m_currentSpeed == Pause) seekThreadPause = true; seekThreadPos = pos; seekThreadTime = 0; start(); } void KXineWidget::slotSeekToTime(const TQTime& postime) { if ( running() ) return; if ((!isXineReady()) || (!isPlaying()) || (!isSeekable())) return; seekThreadPause = false; if (m_currentSpeed == Pause) seekThreadPause = true; seekThreadPos = 0; seekThreadTime = TQTime().msecsTo(postime); start(); } void KXineWidget::slotStopSeeking() { debugOut("Seeking stopped"); m_posTimer.start(200, 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_TQT_ONLY void KXineWidget::createDeinterlacePlugin(const TQString& config, TQWidget* 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 TQString KXineWidget::getDeinterlaceConfig() const { if (m_deinterlaceFilter) return m_deinterlaceFilter->getConfig(); return DEFAULT_TVTIME_CONFIG; } #endif void KXineWidget::slotSetDeinterlaceConfig(const TQString& config) { #ifndef USE_TQT_ONLY if (m_deinterlaceFilter) m_deinterlaceFilter->setConfig(config); #else warningOut(TQString ("Not implemented [SetDeinterlaceConfig %1]").arg(config)); #endif } void KXineWidget::slotToggleDeinterlace() { #ifndef USE_TQT_ONLY TQString s; if (m_deinterlaceFilter) { m_deinterlaceEnabled = !m_deinterlaceEnabled; debugOut(TQString("Deinterlace enabled: %1").arg(m_deinterlaceEnabled)); if ( m_deinterlaceEnabled ) s = i18n("Deinterlace: on"); else s = i18n("Deinterlace: off"); showOSDMessage( s, 2000 ); 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::slotZoomOutX() { if ((m_currentZoomX - 5) >= 100) { m_currentZoomX -= 5; xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoomX); emit signalXineStatus(i18n("Zoom X") + ": " + TQString::number(m_currentZoomX) + "%"); } } void KXineWidget::slotZoomInX() { if ((m_currentZoomX + 5) <= XINE_VO_ZOOM_MAX) { m_currentZoomX += 5; xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_X, m_currentZoomX); emit signalXineStatus(i18n("Zoom X") + ": " + TQString::number(m_currentZoomX) + "%"); } } void KXineWidget::slotZoomOutY() { if ((m_currentZoomY - 5) >= 100) { m_currentZoomY -= 5; xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoomY); emit signalXineStatus(i18n("Zoom Y") + ": " + TQString::number(m_currentZoomY) + "%"); } } void KXineWidget::slotZoomInY() { if ((m_currentZoomY + 5) <= XINE_VO_ZOOM_MAX) { m_currentZoomY += 5; xine_set_param(m_xineStream, XINE_PARAM_VO_ZOOM_Y, m_currentZoomY); emit signalXineStatus(i18n("Zoom Y") + ": " + TQString::number(m_currentZoomY) + "%"); } } void KXineWidget::slotZoomOut() { if ((m_currentZoom - 5) >= 100) { m_currentZoom -= 5; m_currentZoomX = m_currentZoomY = m_currentZoom; 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") + ": " + TQString::number(m_currentZoom) + "%"); } } void KXineWidget::slotZoomIn() { if ((m_currentZoom + 5) <= XINE_VO_ZOOM_MAX) { m_currentZoom += 5; m_currentZoomX = m_currentZoomY = m_currentZoom; 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") + ": " + TQString::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; m_currentZoomX = m_currentZoomY = m_currentZoom; emit signalXineStatus(i18n("Zoom") + ": " + TQString::number(m_currentZoom) + "%"); } TQTime KXineWidget::getLengthInfo() { int pos, time, length; int t = 0, ret = 0; while(((ret = xine_get_pos_length(m_xineStream, &pos, &time, &length)) == 0) && (++t < 5)) xine_usec_sleep(100000); if ( (ret != 0) && (length > 0) ) { return msToTime(length); } return TQTime(); } void KXineWidget::slotEmitLengthInfo() { TQTime length = getLengthInfo(); if (!(length.isNull())) { if ( m_trackURL!="DVB" ) m_lengthInfoTimer.stop(); m_trackLength = length; emit signalLengthChanged(); } else { if (m_lengthInfoTries > 10) // wait 10 seconds m_lengthInfoTimer.stop(); else { debugOut("Wait for valid length information"); m_lengthInfoTries ++; } } } void KXineWidget::globalPosChanged() { TQPoint g = mapToGlobal(TQPoint(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::getspuOffset(int& spuOffset) const { 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") + ": " + TQString::number((hue*100)/65535) + "%"); } void KXineWidget::slotSetSaturation(int sat) { xine_set_param(m_xineStream, XINE_PARAM_VO_SATURATION, sat); emit signalXineStatus(i18n("Saturation") + ": " + TQString::number((sat*100)/65535) + "%"); } void KXineWidget::slotSetContrast(int contrast) { xine_set_param(m_xineStream, XINE_PARAM_VO_CONTRAST, contrast); emit signalXineStatus(i18n("Contrast") + ": " + TQString::number((contrast*100)/65535) + "%"); } void KXineWidget::slotSetBrightness(int bright) { xine_set_param(m_xineStream, XINE_PARAM_VO_BRIGHTNESS, bright); emit signalXineStatus(i18n("Brightness") + ": " + TQString::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") + ": " + TQString::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") + ": " + TQString::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); } void KXineWidget::slotSetVolumeGain(bool gain) { int amp = 0; if (gain) { if (m_softwareMixer) amp = getVolume()*2; else amp = 200; } else { if (m_softwareMixer) amp = getVolume(); else amp = 100; } xine_set_param(m_xineStream, XINE_PARAM_AUDIO_AMP_LEVEL, amp); m_volumeGain = gain; } /*************** 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() == TQt::ArrowCursor) { setCursor(TQCursor(TQt::BlankCursor)); } } /************************************************************ * Take a Screenshot * ************************************************************/ TQImage KXineWidget::getScreenshot() const { uchar *rgbPile = NULL; int width, height; double scaleFactor; getScreenshot(rgbPile, width, height, scaleFactor); if (!rgbPile) return TQImage(); TQImage screenShot(rgbPile, width, height, 32, 0, 0, TQImage::IgnoreEndian); if (scaleFactor >= 1.0) width = (int)((double) width * scaleFactor + 0.5); else height = (int)((double) height / scaleFactor + 0.5); debugOut(TQString("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(TQString("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(TQString("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(TQString("Screenshot: using scale factor: %1").arg(m_videoAspect)); scaleFactor = m_videoAspect; delete [] yuv; } /************************************************ * HELPER FUNCTIONS * ************************************************/ TQTime KXineWidget::msToTime(int msec) { TQTime t; t = t.addMSecs(msec); return t; } #ifdef USE_TQT_ONLY TQString KXineWidget::i18n(const char *text) { return TQApplication::tr(text); } #endif void KXineWidget::debugOut (TQString qsDebug) { #ifdef USE_TQT_ONLY TQString qsDebugging = TQString ("Debug: ") + qsDebug +"\n"; printf ((const char *)qsDebugging); #else kdDebug() << "KXineWidget: " << (const char *)qsDebug.ascii() << "\n"; #endif } void KXineWidget::errorOut (TQString qsError) { #ifdef USE_TQT_ONLY TQString qsErroring = TQString ("Error: ") + qsError+ "\n"; printf ((const char *)qsErroring); #else kdError() << "KXineWidget: " << (const char *)qsError.ascii() << "\n"; #endif } void KXineWidget::warningOut (TQString qsWarning) { #ifdef USE_TQT_ONLY TQString qsWarninging = TQString ("Warning: ") + qsWarning + "\n"; printf ((const char *)qsWarninging); #else kdWarning() << "KXineWidget: " << (const char *)qsWarning.ascii() << "\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); #if TQ_BYTE_ORDER == TQ_LITTLE_ENDIAN 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; #else rgb[(i * width + j) * 4 + 3] = b; rgb[(i * width + j) * 4 + 2] = g; rgb[(i * width + j) * 4 + 1] = r; rgb[(i * width + j) * 4 + 0] = 0; #endif } } return rgb; }