xine-0.9.4/0000755000000000000000000000000011540204431011157 5ustar rootrootxine-0.9.4/xineSettings.h0000644000000000000000000001617511210726441014032 0ustar rootroot #ifndef __XINESETTINGS_H #define __XINESETTINGS_H #include "xineCommon.h" namespace PluginXine { class cXineSetupPage; class cXineSettings { public: enum eUsageMode { modeLiveTV , modeReplay }; enum eOsdMode { osdOverlay , osdBlendClipped , osdBlendScaledLQ , osdBlendScaledHQ , osdBlendScaledSHQ , osdBlendScaledAuto }; enum eAudioMode { audioDolbyOff , audioDolbyOn }; enum eVolumeMode { volumeIgnore , volumeChangeHW , volumeChangeSW }; enum eMuteMode { muteIgnore , muteExecute , muteSimulate }; enum eOsdExtentWidth { osdExtentWidthMin = 320 , osdExtentWidthDefault = 720 , osdExtentWidthMax = 1920 }; enum eOsdExtentHeight { osdExtentHeightMin = 240 , osdExtentHeightDefault = 576 , osdExtentHeightMax = 1080 }; enum eMonitorGamma { monitorGammaBase = 100 , monitorGammaMin = 100 , monitorGammaDefault = 123 , monitorGammaMax = 250 }; enum eImage { image4_3 = 0 , image16_9 = 1 }; enum eImageZoom { imageZoomBase = 100 , imageZoomMin = 25 , imageZoomDefault = 100 , imageZoomMax = 400 }; enum eAutoPrimaryDeviceMode { autoPrimaryDeviceOff , autoPrimaryDeviceOn }; enum eTransparencyMode { transparencyOff , transparencyOn }; enum eMonitoringMode { monitoringOnce , monitoringContinuous }; enum eInteractWithEitScannerMode { interactWithEitScannerOff , interactWithEitScannerOn }; private: bool m_switchSkin; bool m_beQuiet; int m_defaultGrabSizeX; int m_defaultGrabSizeY; eOsdMode m_osdMode; eUsageMode m_usageMode /* , m_usageModeDefault */; eAudioMode m_audioMode; eVolumeMode m_volumeMode; eMuteMode m_muteMode; eMonitorGamma m_crtGamma; eAutoPrimaryDeviceMode m_autoPrimaryDeviceMode; eTransparencyMode m_transparencyMode; eInteractWithEitScannerMode m_interactWithEitScannerMode; template static T clip(T a, T x, T b) { if (x < a) return a; if (x > b) return b; return x; } class cModeParams { bool SetupParse(const char *optionName, int &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eMonitoringMode &optionValue, const char *Name, const char *Value); public: cModeParams(); int m_prebufferFramesVideoSD; int m_prebufferFramesVideoHD; int m_prebufferFramesAudio; int m_prebufferHysteresis; int m_monitoringDuration; eMonitoringMode m_monitoringMode; bool SetupParse(const char *prefix, const char *Name, const char *Value); bool MonitoringContinuous() const; } m_modeParams[ 2 ]; class cZoomParams { bool SetupParse(const char *optionName, eImageZoom &optionValue, const char *Name, const char *Value); public: cZoomParams(); eImageZoom m_zoomX; eImageZoom m_zoomY; eImageZoom GetZoomX() const { return clip(imageZoomMin, m_zoomX, imageZoomMax); } eImageZoom GetZoomY() const { return clip(imageZoomMin, m_zoomY, imageZoomMax); } bool SetupParse(const char *prefix, const char *Name, const char *Value); bool operator !=(const cZoomParams &rhs) const; } m_zoomParams[ 2 ]; class cOsdExtentParams { bool SetupParse(const char *optionName, eOsdExtentWidth &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eOsdExtentHeight &optionValue, const char *Name, const char *Value); public: cOsdExtentParams(); eOsdExtentWidth m_osdExtentWidth; eOsdExtentHeight m_osdExtentHeight; eOsdExtentWidth GetOsdExtentWidth() const { return clip(osdExtentWidthMin, m_osdExtentWidth, osdExtentWidthMax); } eOsdExtentHeight GetOsdExtentHeight() const { return clip(osdExtentHeightMin, m_osdExtentHeight, osdExtentHeightMax); } bool SetupParse(const char *prefix, const char *Name, const char *Value); bool operator !=(const cOsdExtentParams &rhs) const; } m_osdExtentParams; bool SetupParse(const char *optionName, eUsageMode &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eOsdMode &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eAudioMode &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eVolumeMode &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eMuteMode &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eMonitorGamma &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eAutoPrimaryDeviceMode &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eTransparencyMode &optionValue, const char *Name, const char *Value); bool SetupParse(const char *optionName, eInteractWithEitScannerMode &optionValue, const char *Name, const char *Value); public: cXineSettings(); bool SetupParse(const char *Name, const char *Value); const cModeParams *GetModeParams() const; eOsdMode OsdMode() const; bool AudioDolbyOn() const; eVolumeMode VolumeMode() const; bool SupportTransparency() const; eMuteMode MuteMode() const; void Create(cXineSetupPage *const setupPage); void Store(cXineSetupPage *const setupPage); void SelectReplayPrebufferMode(const bool select = true); void TogglePrebufferMode(); const char *GetMainMenuEntry(); bool LiveTV() const; bool AutoPrimaryDevice() const; bool InteractWithEitScanner() const; bool setupDiffers(const cXineSettings &rhs) const { return m_osdMode != rhs.m_osdMode || m_transparencyMode != rhs.m_transparencyMode || m_volumeMode != rhs.m_volumeMode || m_muteMode != rhs.m_muteMode || m_zoomParams[ image4_3 ] != rhs.m_zoomParams[ image4_3 ] || m_zoomParams[ image16_9 ] != rhs.m_zoomParams[ image16_9 ] || m_osdExtentParams != rhs.m_osdExtentParams; } int GetCrtGamma() const { return clip(monitorGammaMin, m_crtGamma, monitorGammaMax); } const cZoomParams &ZoomParams(eImage image) const { return m_zoomParams[ image ]; } const cOsdExtentParams &OsdExtentParams() const { return m_osdExtentParams; } void SetSwitchSkin(const bool switchSkin); bool ShallSwitchSkin() const; void SetBeQuiet(const bool beQuiet); bool ShallBeQuiet() const; void SetDefaultGrabSizeX(const int defaultGrabSizeX); int DefaultGrabSizeX() const; void SetDefaultGrabSizeY(const int defaultGrabSizeY); int DefaultGrabSizeY() const; }; }; #endif //__XINESETTINGS_H xine-0.9.4/xineCommon.h0000644000000000000000000000220111537222003013440 0ustar rootroot #ifndef __XINECOMMON_H #define __XINECOMMON_H #include #include #include #define __STDC_FORMAT_MACROS #include #include using namespace std; #include #include #define MIN_XINE_VDR_VERSION 901 #if !defined(XINE_VDR_VERSION) || XINE_VDR_VERSION < MIN_XINE_VDR_VERSION #error xine/vdr.h does not match. Please solve this issue by reading section XINE VDR VERSION MISMATCH in INSTALL! #endif #include // poisened #ifndef APIVERSNUM #define APIVERSNUM VDRVERSNUM #endif namespace PluginXine { extern bool beQuiet; template DST_TYPE &alias_cast(SRC_TYPE &rhs) { union hlp { SRC_TYPE src; DST_TYPE dst; }; return ((hlp &)rhs).dst; } }; #define xfprintf(fh, fmt, args...) \ while (!PluginXine::beQuiet) \ { \ fprintf(fh, fmt, ##args); \ /* char xfmt[ 500 ]; */ \ /* sprintf(xfmt, "%s", fmt); */ \ /* fprintf(fh, xfmt, ##args); */ \ break; \ } #endif //__XINECOMMON_H xine-0.9.4/xineExternal.c0000644000000000000000000001157011132432616014002 0ustar rootroot #include "xineCommon.h" #include "xineExternal.h" #include "xineLib.h" namespace PluginXine { cXineExternal::cXineExternal() : cThread() , m_fdControl(-1) , m_fdResult(-1) , m_shutdown(false) , m_connected(false) , m_xineLib(0) , m_enabled(false) { } cXineExternal::~cXineExternal() { StopExternal(); } void cXineExternal::StartExternal() { cThread::Start(); } void cXineExternal::StopExternal() { cMutexLock shutdownMutexLock(&m_shutdownMutex); m_shutdown = true; m_shutdownCondVar.Broadcast(); disconnect(); } bool cXineExternal::readCommand() { int len = 0; while (!m_shutdown && len < EXTERNAL_COMMAND_MAX_LEN) { cPoller poller(m_fdControl); if (poller.Poll(100)) { void (* const sigPipeHandler)(int) = ::signal(SIGPIPE, SIG_IGN); int r = ::read(m_fdControl, &m_command[ len ], 1); int myErrno = errno; ::signal(SIGPIPE, sigPipeHandler); if (r < 0) { if (EAGAIN == myErrno) return false; if (m_connected) { errno = myErrno; ::perror("vdr-xine: reading external command failed"); } } if (r <= 0) { disconnect(); return false; } if ('\n' == m_command[ len ]) break; len += r; } } if (m_shutdown) return false; if (len >= EXTERNAL_COMMAND_MAX_LEN && m_command[ EXTERNAL_COMMAND_MAX_LEN - 1 ] != '\n') { ::fprintf(stderr, "vdr-xine: external command length exceeded!\n"); disconnect(); return false; } m_command[ len ] = '\0'; return (len > 0); } bool cXineExternal::writeResult(const char *result) { int l = ::strlen(result); while (l > 0) { void (* const sigPipeHandler)(int) = ::signal(SIGPIPE, SIG_IGN); int r = ::write(m_fdResult, &result, l); int myErrno = errno; ::signal(SIGPIPE, sigPipeHandler); if (r < 0) { if (EAGAIN == myErrno) continue; errno = myErrno; ::perror("vdr-xine: writing external result failed"); } if (r <= 0) { disconnect(); return false; } result += r; l -= r; } return true; } void cXineExternal::cmdPlay(const char *const mrl) { if (m_xineLib) { m_xineLib->execFuncPlayExternal(mrl); m_xineLib->execFuncWait(); } } void cXineExternal::enable(const bool enable) { cMutexLock lock(&m_enabledMutex); if (m_enabled && !enable) cmdPlay(0); m_enabled = enable; } void cXineExternal::externalStreamFinished() { ::fprintf(stderr, "vdr-xine: external stream finished\n"); cmdPlay(0); writeResult("play finished\n"); } void cXineExternal::Action(void) { while (!m_shutdown) { if (!isConnected()) { // ::fprintf(stderr, "?"); checkConnect(); } else { // ::fprintf(stderr, "!"); if (readCommand()) { cMutexLock lock(&m_enabledMutex); if (!m_enabled) { ::fprintf(stderr, "vdr-xine: external commands not allowed!\n"); disconnect(); } else { if (0 == strncmp(m_command, "play ", 5)) { cmdPlay(m_command + 5); } else { ::fprintf(stderr, "vdr-xine: external command '%s' unknown!\n", m_command); disconnect(); } } } } if (!m_shutdown) { cMutexLock shutdownMutexLock(&m_shutdownMutex); if (!m_shutdown) m_shutdownCondVar.TimedWait(m_shutdownMutex, 100); } } } bool cXineExternal::isConnected() { return (-1 != m_fdControl); } bool cXineExternal::checkConnect() { m_fdResult = ::open(m_xineLib->m_fifoNameExtResult.c_str() , O_WRONLY | O_NONBLOCK); if (-1 == m_fdResult) return false; ::fprintf(stderr, "vdr-xine: external connecting ...\n"); m_fdControl = ::open(m_xineLib->m_fifoNameExtControl.c_str(), O_RDONLY); ::fcntl(m_fdResult, F_SETFL, ~O_NONBLOCK & ::fcntl(m_fdResult, F_GETFL, 0)); if (isConnected()) { ::fprintf(stderr, "vdr-xine: external connected!\n"); m_connected = true; return true; } ::fprintf(stderr, "vdr-xine: external connect failed!\n"); return false; } void cXineExternal::disconnect() { m_connected = false; cmdPlay(0); if (-1 != m_fdControl) { ::close(m_fdControl); m_fdControl = -1; } if (-1 != m_fdResult) { ::close(m_fdResult); m_fdResult = -1; } } }; xine-0.9.4/patches/0000755000000000000000000000000011216247766012630 5ustar rootrootxine-0.9.4/patches/xine-ui.patch0000644000000000000000000000000010736466664015223 0ustar rootrootxine-0.9.4/patches/xine-lib.patch0000644000000000000000000044052411216244712015355 0ustar rootrootdiff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -2801,6 +2801,7 @@ src/video_out/vidix/drivers/Makefile src/video_out/vidix/drivers/Makefile src/xine-utils/Makefile src/xine-engine/Makefile +src/vdr/Makefile win32/Makefile win32/include/Makefile]) AC_CONFIG_COMMANDS([default],[[chmod +x ./misc/SlackBuild ./misc/build_rpms.sh ./misc/relchk.sh]],[[]]) @@ -2843,7 +2844,7 @@ echo " - stdin_fifo - rtp" echo " - stdin_fifo - rtp" echo " - http - mms" echo " - pnm - rtsp" -echo " - dvb" +echo " - dvb - vdr" if test "x$external_dvdnav" = "xyes"; then echo " - dvd (external libs)" else @@ -3048,6 +3049,7 @@ echo " - eq - eq2" echo " - eq - eq2" echo " - boxblur - denoise3d" echo " - unsharp - tvtime" +echo " - vdr" echo " * SFX:" echo " - goom - oscope" echo " - fftscope - mosaico" diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,4 +27,5 @@ SUBDIRS = \ libfaad \ libmusepack \ post \ - combined + combined \ + vdr diff --git a/src/vdr/Makefile.am b/src/vdr/Makefile.am new file mode 100644 --- /dev/null +++ b/src/vdr/Makefile.am @@ -0,0 +1,13 @@ +include $(top_srcdir)/misc/Makefile.common + +AM_CFLAGS = -D_LARGEFILE64_SOURCE + +xineplug_LTLIBRARIES = \ + xineplug_vdr.la + +xineplug_vdr_la_SOURCES = combined_vdr.c input_vdr.c post_vdr_video.c post_vdr_audio.c +xineplug_vdr_la_LIBADD = $(XINE_LIB) +xineplug_vdr_la_LDFLAGS = -avoid-version -module @IMPURE_TEXT_LDFLAGS@ + +xineinclude_HEADERS = vdr.h +noinst_HEADERS = combined_vdr.h diff --git a/src/vdr/combined_vdr.c b/src/vdr/combined_vdr.c new file mode 100644 --- /dev/null +++ b/src/vdr/combined_vdr.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + */ + +/* + * plugins for VDR + */ + +#include "xine_internal.h" +#include "post.h" +#include "combined_vdr.h" + + + +static const post_info_t vdr_video_special_info = { XINE_POST_TYPE_VIDEO_FILTER }; +static const post_info_t vdr_audio_special_info = { XINE_POST_TYPE_AUDIO_FILTER }; + +/* exported plugin catalog entry */ +const plugin_info_t xine_plugin_info[] EXPORTED = +{ + /* type , API, "name" , version , special_info , init_function */ + { PLUGIN_INPUT, 17, "VDR" , XINE_VERSION_CODE, NULL , &vdr_input_init_plugin }, + { PLUGIN_POST , 9, "vdr" , XINE_VERSION_CODE, &vdr_video_special_info, &vdr_video_init_plugin }, + { PLUGIN_POST , 9, "vdr_video", XINE_VERSION_CODE, &vdr_video_special_info, &vdr_video_init_plugin }, + { PLUGIN_POST , 9, "vdr_audio", XINE_VERSION_CODE, &vdr_audio_special_info, &vdr_audio_init_plugin }, + { PLUGIN_NONE , 0, "" , 0 , NULL , NULL } +}; + diff --git a/src/vdr/combined_vdr.h b/src/vdr/combined_vdr.h new file mode 100644 --- /dev/null +++ b/src/vdr/combined_vdr.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + */ + +#ifndef __COMBINED_VDR_H +#define __COMBINED_VDR_H + + + +typedef struct vdr_set_video_window_data_s { + int32_t x; + int32_t y; + int32_t w; + int32_t h; + int32_t w_ref; + int32_t h_ref; + +} vdr_set_video_window_data_t; + + + +typedef struct vdr_frame_size_changed_data_s { + int32_t x; + int32_t y; + int32_t w; + int32_t h; + double r; + +} vdr_frame_size_changed_data_t; + + + +typedef struct vdr_select_audio_data_s { + uint8_t channels; + +} vdr_select_audio_data_t; + + + +inline static int vdr_is_vdr_stream(xine_stream_t *stream) +{ + if (!stream + || !stream->input_plugin + || !stream->input_plugin->input_class) + { + return 0; + } + + { + input_class_t *input_class = stream->input_plugin->input_class; + + if (input_class->get_identifier) + { + const char *identifier = input_class->get_identifier(input_class); + if (identifier + && 0 == strcmp(identifier, "VDR")) + { + return 1; + } + } + } + + return 0; +} + + + +/* plugin class initialization function */ +void *vdr_input_init_plugin(xine_t *xine, void *data); +void *vdr_video_init_plugin(xine_t *xine, void *data); +void *vdr_audio_init_plugin(xine_t *xine, void *data); + + + +#endif /* __COMBINED_VDR_H */ + diff --git a/src/vdr/input_vdr.c b/src/vdr/input_vdr.c new file mode 100644 --- /dev/null +++ b/src/vdr/input_vdr.c @@ -0,0 +1,2665 @@ +/* + * Copyright (C) 2003-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOG_MODULE "input_vdr" +#define LOG_VERBOSE +/* +#define LOG +*/ +#include "xine_internal.h" +#include "xineutils.h" +#include "input_plugin.h" + +#include "vdr.h" +#include "combined_vdr.h" + + + +#define VDR_MAX_NUM_WINDOWS 16 +#define VDR_ABS_FIFO_DIR "/tmp/vdr-xine" + +#define BUF_SIZE 1024 + +#define LOG_OSD(x) +/* +#define LOG_OSD(x) x +*/ + + +typedef struct vdr_input_plugin_s vdr_input_plugin_t; + +typedef struct +{ + metronom_t metronom; + metronom_t *stream_metronom; + vdr_input_plugin_t *input; +} +vdr_metronom_t; + + +typedef struct vdr_osd_s +{ + xine_osd_t *window; + uint8_t *argb_buffer; + int width; + int height; +} +vdr_osd_t; + + +typedef struct vdr_vpts_offset_s vdr_vpts_offset_t; + +struct vdr_vpts_offset_s +{ + vdr_vpts_offset_t *next; + int64_t vpts; + int64_t offset; +}; + + +struct vdr_input_plugin_s +{ + input_plugin_t input_plugin; + + xine_stream_t *stream; + xine_stream_t *stream_external; + + int fh; + int fh_control; + int fh_result; + int fh_event; + + char *mrl; + + off_t curpos; + char seek_buf[ BUF_SIZE ]; + + char *preview; + off_t preview_size; + + enum funcs cur_func; + off_t cur_size; + off_t cur_done; + + vdr_osd_t osd[ VDR_MAX_NUM_WINDOWS ]; + uint8_t *osd_buffer; + uint32_t osd_buffer_size; + uint8_t osd_unscaled_blending; + uint8_t osd_supports_custom_extent; + uint8_t osd_supports_argb_layer; + + uint8_t audio_channels; + uint8_t trick_speed_mode; + uint8_t mute_mode; + uint8_t volume_mode; + int last_volume; + vdr_frame_size_changed_data_t frame_size; + + pthread_t rpc_thread; + int rpc_thread_shutdown; + pthread_mutex_t rpc_thread_shutdown_lock; + pthread_cond_t rpc_thread_shutdown_cond; + int startup_phase; + + pthread_t metronom_thread; + pthread_mutex_t metronom_thread_lock; + int64_t metronom_thread_request; + int metronom_thread_reply; + pthread_cond_t metronom_thread_request_cond; + pthread_cond_t metronom_thread_reply_cond; + pthread_mutex_t metronom_thread_call_lock; + + xine_event_queue_t *event_queue; + xine_event_queue_t *event_queue_external; + + pthread_mutex_t adjust_zoom_lock; + uint16_t image4_3_zoom_x; + uint16_t image4_3_zoom_y; + uint16_t image16_9_zoom_x; + uint16_t image16_9_zoom_y; + + uint8_t find_sync_point; + pthread_mutex_t find_sync_point_lock; + + vdr_metronom_t metronom; + int last_disc_type; + + vdr_vpts_offset_t *vpts_offset_queue; + vdr_vpts_offset_t *vpts_offset_queue_tail; + pthread_mutex_t vpts_offset_queue_lock; + pthread_cond_t vpts_offset_queue_changed_cond; + int vpts_offset_queue_changes; + + int video_window_active; + vdr_set_video_window_data_t video_window_event_data; +}; + + +typedef struct +{ + input_class_t input_class; + xine_t *xine; + const char *mrls[ 2 ]; +} +vdr_input_class_t; + + + +static int vdr_write(int f, void *b, int n) +{ + int t = 0, r; + + while (t < n) + { + /* + * System calls are not a thread cancellation point in Linux + * pthreads. However, the RT signal sent to cancel the thread + * will cause recv() to return with EINTR, and we can manually + * check cancellation. + */ + pthread_testcancel(); + r = write(f, ((char *)b) + t, n - t); + pthread_testcancel(); + + if (r < 0 + && (errno == EINTR + || errno == EAGAIN)) + { + continue; + } + + if (r < 0) + return r; + + t += r; + } + + return t; +} + + + +static int internal_write_event_play_external(vdr_input_plugin_t *this, uint32_t key); + +static void event_handler_external(void *user_data, const xine_event_t *event) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)user_data; + uint32_t key = key_none; +/* + printf("event_handler_external(): event->type: %d\n", event->type); +*/ + switch (event->type) + { + case XINE_EVENT_UI_PLAYBACK_FINISHED: + break; + + default: + return; + } + + if (0 != internal_write_event_play_external(this, key)) + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: input event write: %s.\n"), LOG_MODULE, strerror(errno)); +} + +static void external_stream_stop(vdr_input_plugin_t *this) +{ + if (this->stream_external) + { + xine_stop(this->stream_external); + xine_close(this->stream_external); + + if (this->event_queue_external) + { + xine_event_dispose_queue(this->event_queue_external); + this->event_queue_external = 0; + } + + _x_demux_flush_engine(this->stream_external); + + xine_dispose(this->stream_external); + this->stream_external = 0; + } +} + +static void external_stream_play(vdr_input_plugin_t *this, char *file_name) +{ + external_stream_stop(this); + + this->stream_external = xine_stream_new(this->stream->xine, this->stream->audio_out, this->stream->video_out); + + this->event_queue_external = xine_event_new_queue(this->stream_external); + + xine_event_create_listener_thread(this->event_queue_external, event_handler_external, this); + + if (!xine_open(this->stream_external, file_name) + || !xine_play(this->stream_external, 0, 0)) + { + uint32_t key = key_none; + + if ( 0 != internal_write_event_play_external(this, key)) + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: input event write: %s.\n"), LOG_MODULE, strerror(errno)); + } +} + +static off_t vdr_read_abort(xine_stream_t *stream, int fd, char *buf, off_t todo) +{ + off_t ret; + + while (1) + { + /* + * System calls are not a thread cancellation point in Linux + * pthreads. However, the RT signal sent to cancel the thread + * will cause recv() to return with EINTR, and we can manually + * check cancellation. + */ + pthread_testcancel(); + ret = _x_read_abort(stream, fd, buf, todo); + pthread_testcancel(); + + if (ret < 0 + && (errno == EINTR + || errno == EAGAIN)) + { + continue; + } + + break; + } + + return ret; +} + +#define READ_DATA_OR_FAIL(kind, log) \ + data_##kind##_t *data = &data_union.kind; \ + { \ + log; \ + n = vdr_read_abort(this->stream, this->fh_control, (char *)data + sizeof (data->header), sizeof (*data) - sizeof (data->header)); \ + if (n != sizeof (*data) - sizeof (data->header)) \ + return -1; \ + \ + this->cur_size -= n; \ + } + +static double _now() +{ + struct timeval tv; + + gettimeofday(&tv, 0); + + return (tv.tv_sec * 1000000.0 + tv.tv_usec) / 1000.0; +} + +static void adjust_zoom(vdr_input_plugin_t *this) +{ + pthread_mutex_lock(&this->adjust_zoom_lock); + + if (this->image4_3_zoom_x && this->image4_3_zoom_y + && this->image16_9_zoom_x && this->image16_9_zoom_y) + { + int ratio = (int)(10000 * this->frame_size.r + 0.5); + /* fprintf(stderr, "ratio: %d\n", ratio); */ + if (13332 <= ratio && ratio <= 13334) + { + xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, this->image4_3_zoom_x); + xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_Y, this->image4_3_zoom_y); + } + else /* if (17777 <= ratio && ratio <= 17779) */ + { + xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_X, this->image16_9_zoom_x); + xine_set_param(this->stream, XINE_PARAM_VO_ZOOM_Y, this->image16_9_zoom_y); + } + } + + pthread_mutex_unlock(&this->adjust_zoom_lock); +} + + +static void vdr_vpts_offset_queue_process(vdr_input_plugin_t *this, int64_t vpts) +{ + while (this->vpts_offset_queue + && this->vpts_offset_queue->vpts <= vpts) + { + vdr_vpts_offset_t *curr = this->vpts_offset_queue; + this->vpts_offset_queue = curr->next; + + free(curr); + } + + if (!this->vpts_offset_queue) + this->vpts_offset_queue_tail = 0; +} + + +static void vdr_vpts_offset_queue_purge(vdr_input_plugin_t *this) +{ + vdr_vpts_offset_queue_process(this, 1ll << 62); +} + + +static off_t vdr_execute_rpc_command(vdr_input_plugin_t *this) +{ + data_union_t data_union; + off_t n; + + n = vdr_read_abort(this->stream, this->fh_control, (char *)&data_union, sizeof (data_union.header)); + if (n != sizeof (data_union.header)) + return -1; + + this->cur_func = data_union.header.func; + this->cur_size = data_union.header.len - sizeof (data_union.header); + this->cur_done = 0; + + switch (this->cur_func) + { + case func_nop: + { + READ_DATA_OR_FAIL(nop, lprintf("got NOP\n")); + } + break; + + case func_osd_new: + { + READ_DATA_OR_FAIL(osd_new, LOG_OSD(lprintf("got OSDNEW\n"))); +/* + LOG_OSD(lprintf("... (%d,%d)-(%d,%d)@(%d,%d)\n", data->x, data->y, data->width, data->height, data->w_ref, data->h_ref)); + + fprintf(stderr, "vdr: osdnew %d\n", data->window); +*/ + if (data->window >= VDR_MAX_NUM_WINDOWS) + return -1; + + if (0 != this->osd[ data->window ].window) + return -1; + + this->osd[ data->window ].window = xine_osd_new(this->stream + , data->x + , data->y + , data->width + , data->height); + + this->osd[ data->window ].width = data->width; + this->osd[ data->window ].height = data->height; + + if (0 == this->osd[ data->window ].window) + return -1; + +#ifdef XINE_OSD_CAP_CUSTOM_EXTENT + if (this->osd_supports_custom_extent && data->w_ref > 0 && data->h_ref > 0) + xine_osd_set_extent(this->osd[ data->window ].window, data->w_ref, data->h_ref); +#endif + } + break; + + case func_osd_free: + { + READ_DATA_OR_FAIL(osd_free, LOG_OSD(lprintf("got OSDFREE\n"))); +/* + fprintf(stderr, "vdr: osdfree %d\n", data->window); +*/ + if (data->window >= VDR_MAX_NUM_WINDOWS) + return -1; + + if (0 != this->osd[ data->window ].window) + xine_osd_free(this->osd[ data->window ].window); + + this->osd[ data->window ].window = 0; + + free(this->osd[ data->window ].argb_buffer); + this->osd[ data->window ].argb_buffer = 0; + } + break; + + case func_osd_show: + { + READ_DATA_OR_FAIL(osd_show, LOG_OSD(lprintf("got OSDSHOW\n"))); +/* + fprintf(stderr, "vdr: osdshow %d\n", data->window); +*/ + if (data->window >= VDR_MAX_NUM_WINDOWS) + return -1; + + if (0 != this->osd[ data->window ].window) + { +#ifdef XINE_OSD_CAP_VIDEO_WINDOW + xine_osd_set_video_window(this->osd[ data->window ].window + , this->video_window_active ? this->video_window_event_data.x : 0 + , this->video_window_active ? this->video_window_event_data.y : 0 + , this->video_window_active ? this->video_window_event_data.w : 0 + , this->video_window_active ? this->video_window_event_data.h : 0); +#endif + if (this->osd_unscaled_blending) + xine_osd_show_unscaled(this->osd[ data->window ].window, 0); + else + xine_osd_show(this->osd[ data->window ].window, 0); + } + } + break; + + case func_osd_hide: + { + READ_DATA_OR_FAIL(osd_hide, LOG_OSD(lprintf("got OSDHIDE\n"))); +/* + fprintf(stderr, "vdr: osdhide %d\n", data->window); +*/ + if (data->window >= VDR_MAX_NUM_WINDOWS) + return -1; + + if (0 != this->osd[ data->window ].window) + xine_osd_hide(this->osd[ data->window ].window, 0); + } + break; + + case func_osd_flush: + { + double _t1, _t2; + int _n = 0; + int _to = 0; + int r = 0; + + READ_DATA_OR_FAIL(osd_flush, LOG_OSD(lprintf("got OSDFLUSH\n"))); +/* + fprintf(stderr, "vdr: osdflush +\n"); +*/ + _t1 = _now(); + + while ((r = _x_query_unprocessed_osd_events(this->stream))) + { + if ((_now() - _t1) > 200) + { + _to = 1; + break; + } +/* + fprintf(stderr, "redraw_needed: 1\n"); +*/ +/* sched_yield(); */ + xine_usec_sleep(5000); + _n++; + } + + _t2 = _now(); + fprintf(stderr, "vdr: osdflush: n: %d, %.1lf, timeout: %d, result: %d\n", _n, _t2 - _t1, _to, r); +/* + fprintf(stderr, "redraw_needed: 0\n"); + + fprintf(stderr, "vdr: osdflush -\n"); +*/ + } + break; + + case func_osd_set_position: + { + READ_DATA_OR_FAIL(osd_set_position, LOG_OSD(lprintf("got OSDSETPOSITION\n"))); +/* + fprintf(stderr, "vdr: osdsetposition %d\n", data->window); +*/ + if (data->window >= VDR_MAX_NUM_WINDOWS) + return -1; + + if (0 != this->osd[ data->window ].window) + xine_osd_set_position(this->osd[ data->window ].window, data->x, data->y); + } + break; + + case func_osd_draw_bitmap: + { + READ_DATA_OR_FAIL(osd_draw_bitmap, LOG_OSD(lprintf("got OSDDRAWBITMAP\n"))); +/* + fprintf(stderr, "vdr: osddrawbitmap %d, %d, %d, %d, %d, %d\n", data->window, data->x, data->y, data->width, data->height, data->argb); +*/ + if (this->osd_buffer_size < this->cur_size) + { + if (this->osd_buffer) + free(this->osd_buffer); + + this->osd_buffer_size = 0; + + this->osd_buffer = xine_xmalloc(this->cur_size); + if (!this->osd_buffer) + return -1; + + this->osd_buffer_size = this->cur_size; + } + + n = vdr_read_abort (this->stream, this->fh_control, (char *)this->osd_buffer, this->cur_size); + if (n != this->cur_size) + return -1; + + this->cur_size -= n; + + if (data->window >= VDR_MAX_NUM_WINDOWS) + return -1; + + if (0 != this->osd[ data->window ].window) + { + vdr_osd_t *osd = &this->osd[ data->window ]; + + if (data->argb) + { + if (!osd->argb_buffer) + osd->argb_buffer = calloc(4 * osd->width, osd->height); + + { + int src_stride = 4 * data->width; + int dst_stride = 4 * osd->width; + + uint8_t *src = this->osd_buffer; + uint8_t *dst = osd->argb_buffer + data->y * dst_stride + data->x * 4; + int y; + + if (src_stride == dst_stride) + xine_fast_memcpy(dst, src, src_stride * data->height); + else + { + for (y = 0; y < data->height; y++) + { + xine_fast_memcpy(dst, src, src_stride); + dst += dst_stride; + src += src_stride; + } + } + } + +#ifdef XINE_OSD_CAP_ARGB_LAYER + xine_osd_set_argb_buffer(osd->window, (uint32_t *)osd->argb_buffer, data->x, data->y, data->width, data->height); +#endif + } + else + xine_osd_draw_bitmap(osd->window, this->osd_buffer, data->x, data->y, data->width, data->height, 0); + } + } + break; + + case func_set_color: + { + uint32_t vdr_color[ 256 ]; + + READ_DATA_OR_FAIL(set_color, lprintf("got SETCOLOR\n")); + + if (((data->num + 1) * sizeof (uint32_t)) != this->cur_size) + return -1; + + n = vdr_read_abort (this->stream, this->fh_control, (char *)&vdr_color[ data->index ], this->cur_size); + if (n != this->cur_size) + return -1; + + this->cur_size -= n; + + if (data->window >= VDR_MAX_NUM_WINDOWS) + return -1; + + if (0 != this->osd[ data->window ].window) + { + uint32_t color[ 256 ]; + uint8_t trans[ 256 ]; + + xine_osd_get_palette(this->osd[ data->window ].window, color, trans); + + { + int i; + + for (i = data->index; i <= (data->index + data->num); i++) + { + int a = (vdr_color[ i ] & 0xff000000) >> 0x18; + int r = (vdr_color[ i ] & 0x00ff0000) >> 0x10; + int g = (vdr_color[ i ] & 0x0000ff00) >> 0x08; + int b = (vdr_color[ i ] & 0x000000ff) >> 0x00; + + int y = (( 66 * r + 129 * g + 25 * b + 128) >> 8) + 16; + int cr = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; + int cb = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; + + uint8_t *dst = (uint8_t *)&color[ i ]; + *dst++ = cb; + *dst++ = cr; + *dst++ = y; + *dst++ = 0; + + trans[ i ] = a >> 4; + } + } + + xine_osd_set_palette(this->osd[ data->window ].window, color, trans); + } + } + break; + + case func_play_external: + { + char file_name[ 1024 ]; + int file_name_len = 0; + + READ_DATA_OR_FAIL(play_external, lprintf("got PLAYEXTERNAL\n")); + + file_name_len = this->cur_size; + + if (0 != file_name_len) + { + if (file_name_len <= 1 + || file_name_len > sizeof (file_name)) + { + return -1; + } + + n = vdr_read_abort (this->stream, this->fh_control, file_name, file_name_len); + if (n != file_name_len) + return -1; + + if (file_name[ file_name_len - 1 ] != '\0') + return -1; + + this->cur_size -= n; + } + + lprintf((file_name_len > 0) ? "----------- play external: %s\n" : "---------- stop external\n", file_name); + + if (file_name_len > 0) + external_stream_play(this, file_name); + else + external_stream_stop(this); + } + break; + + case func_clear: + { + READ_DATA_OR_FAIL(clear, lprintf("got CLEAR\n")); + + { + int orig_speed = xine_get_param(this->stream, XINE_PARAM_FINE_SPEED); + if (orig_speed <= 0) + xine_set_param(this->stream, XINE_PARAM_FINE_SPEED, XINE_FINE_SPEED_NORMAL); +/* fprintf(stderr, "+++ CLEAR(%d%c): sync point: %02x\n", data->n, data->s ? 'b' : 'a', data->i); */ + if (!data->s) + { + pthread_mutex_lock(&this->find_sync_point_lock); + this->find_sync_point = data->i; + pthread_mutex_unlock(&this->find_sync_point_lock); + } +/* + if (!this->dont_change_xine_volume) + xine_set_param(this->stream, XINE_PARAM_AUDIO_VOLUME, 0); +*/ + _x_demux_flush_engine(this->stream); +/* fprintf(stderr, "=== CLEAR(%d.1)\n", data->n); */ + _x_demux_control_start(this->stream); +/* fprintf(stderr, "=== CLEAR(%d.2)\n", data->n); */ + _x_demux_seek(this->stream, 0, 0, 0); +/* fprintf(stderr, "=== CLEAR(%d.3)\n", data->n); */ + + _x_stream_info_reset(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE); +/* fprintf(stderr, "=== CLEAR(%d.4)\n", data->n); */ + _x_meta_info_reset(this->stream, XINE_META_INFO_AUDIOCODEC); +/* fprintf(stderr, "=== CLEAR(%d.5)\n", data->n); */ + + _x_trigger_relaxed_frame_drop_mode(this->stream); +/* _x_reset_relaxed_frame_drop_mode(this->stream); */ +/* + if (!this->dont_change_xine_volume) + xine_set_param(this->stream, XINE_PARAM_AUDIO_VOLUME, this->last_volume); +*/ +/* fprintf(stderr, "--- CLEAR(%d%c)\n", data->n, data->s ? 'b' : 'a'); */ + if (orig_speed <= 0) + xine_set_param(this->stream, XINE_PARAM_FINE_SPEED, orig_speed); + } + } + break; + + case func_first_frame: + { + READ_DATA_OR_FAIL(first_frame, lprintf("got FIRST FRAME\n")); + + _x_trigger_relaxed_frame_drop_mode(this->stream); +/* _x_reset_relaxed_frame_drop_mode(this->stream); */ + } + break; + + case func_still_frame: + { + READ_DATA_OR_FAIL(still_frame, lprintf("got STILL FRAME\n")); + + _x_reset_relaxed_frame_drop_mode(this->stream); + } + break; + + case func_set_video_window: + { + READ_DATA_OR_FAIL(set_video_window, lprintf("got SET VIDEO WINDOW\n")); +/* + fprintf(stderr, "svw: (%d, %d)x(%d, %d), (%d, %d)\n", data->x, data->y, data->w, data->h, data->wRef, data->hRef); +*/ + { + xine_event_t event; + + this->video_window_active = (data->x != 0 + || data->y != 0 + || data->w != data->w_ref + || data->h != data->h_ref); + + this->video_window_event_data.x = data->x; + this->video_window_event_data.y = data->y; + this->video_window_event_data.w = data->w; + this->video_window_event_data.h = data->h; + this->video_window_event_data.w_ref = data->w_ref; + this->video_window_event_data.h_ref = data->h_ref; + + event.type = XINE_EVENT_VDR_SETVIDEOWINDOW; + event.data = &this->video_window_event_data; + event.data_length = sizeof (this->video_window_event_data); + + xine_event_send(this->stream, &event); + } + } + break; + + case func_select_audio: + { + READ_DATA_OR_FAIL(select_audio, lprintf("got SELECT AUDIO\n")); + + this->audio_channels = data->channels; + + { + xine_event_t event; + vdr_select_audio_data_t event_data; + + event_data.channels = this->audio_channels; + + event.type = XINE_EVENT_VDR_SELECTAUDIO; + event.data = &event_data; + event.data_length = sizeof (event_data); + + xine_event_send(this->stream, &event); + } + } + break; + + case func_trick_speed_mode: + { + READ_DATA_OR_FAIL(trick_speed_mode, lprintf("got TRICK SPEED MODE\n")); + + if (this->trick_speed_mode != data->on) + { + this->trick_speed_mode = data->on; + + _x_demux_seek(this->stream, 0, 0, 0); + + { + xine_event_t event; + + event.type = XINE_EVENT_VDR_TRICKSPEEDMODE; + event.data = 0; + event.data_length = 0; /* this->trick_speed_mode; */ +/* fprintf(stderr, "************************: %p, %d\n", event.data, event.data_length); */ + xine_event_send(this->stream, &event); + } + } + } + break; + + case func_flush: + { + READ_DATA_OR_FAIL(flush, lprintf("got FLUSH\n")); + + if (!data->just_wait) + { + if (this->stream->video_fifo) + { + buf_element_t *buf = this->stream->video_fifo->buffer_pool_alloc(this->stream->video_fifo); + if (!buf) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: buffer_pool_alloc() failed!\n"), LOG_MODULE); + return -1; + } + + buf->type = BUF_CONTROL_FLUSH_DECODER; + + this->stream->video_fifo->put(this->stream->video_fifo, buf); + } + } + + { + double _t1, _t2; + int _n = 0; + + int vb = -1, ab = -1, vf = -1, af = -1; + + uint8_t timed_out = 0; + + struct timeval now, then; + + if (data->ms_timeout >= 0) + { + gettimeofday(&now, 0); + + then = now; + then.tv_usec += (data->ms_timeout % 1000) * 1000; + then.tv_sec += (data->ms_timeout / 1000); + + if (then.tv_usec >= 1000000) + { + then.tv_usec -= 1000000; + then.tv_sec += 1; + } + } + else + { + then.tv_usec = 0; + then.tv_sec = 0; + } + + _t1 = _now(); + + while (1) + { + _x_query_buffer_usage(this->stream, &vb, &ab, &vf, &af); + + if (vb <= 0 && ab <= 0 && vf <= 0 && af <= 0) + break; + + if (data->ms_timeout >= 0 + && timercmp(&now, &then, >=)) + { + timed_out++; + break; + } + +/* sched_yield(); */ + xine_usec_sleep(5000); + _n++; + + if (data->ms_timeout >= 0) + gettimeofday(&now, 0); + } + + _t2 = _now(); + /* fprintf(stderr, "vdr: flush: n: %d, %.1lf\n", _n, _t2 - _t1); */ + + xprintf(this->stream->xine + , XINE_VERBOSITY_LOG + , _("%s: flush buffers (vb: %d, ab: %d, vf: %d, af: %d) %s.\n") + , LOG_MODULE, vb, ab, vf, af + , (timed_out ? "timed out" : "done")); + + { + result_flush_t result_flush; + result_flush.header.func = data->header.func; + result_flush.header.len = sizeof (result_flush); + + result_flush.timed_out = timed_out; + + if (sizeof (result_flush) != vdr_write(this->fh_result, &result_flush, sizeof (result_flush))) + return -1; + } + } + } + break; + + case func_mute: + { + READ_DATA_OR_FAIL(mute, lprintf("got MUTE\n")); + + { + int param_mute = (this->volume_mode == XINE_VDR_VOLUME_CHANGE_SW) ? XINE_PARAM_AUDIO_AMP_MUTE : XINE_PARAM_AUDIO_MUTE; + xine_set_param(this->stream, param_mute, data->mute); + } + } + break; + + case func_set_volume: + { +double t3, t2, t1, t0; + READ_DATA_OR_FAIL(set_volume, lprintf("got SETVOLUME\n")); +t0 = _now(); + { + int change_volume = (this->volume_mode != XINE_VDR_VOLUME_IGNORE); + int do_mute = (this->last_volume != 0 && 0 == data->volume); + int do_unmute = (this->last_volume <= 0 && 0 != data->volume); + int report_change = 0; + + int param_mute = (this->volume_mode == XINE_VDR_VOLUME_CHANGE_SW) ? XINE_PARAM_AUDIO_AMP_MUTE : XINE_PARAM_AUDIO_MUTE; + int param_volume = (this->volume_mode == XINE_VDR_VOLUME_CHANGE_SW) ? XINE_PARAM_AUDIO_AMP_LEVEL : XINE_PARAM_AUDIO_VOLUME; + + this->last_volume = data->volume; + + if (do_mute || do_unmute) + { + switch (this->mute_mode) + { + case XINE_VDR_MUTE_EXECUTE: + report_change = 1; + xine_set_param(this->stream, param_mute, do_mute); + + case XINE_VDR_MUTE_IGNORE: + if (do_mute) + change_volume = 0; + break; + + case XINE_VDR_MUTE_SIMULATE: + change_volume = 1; + break; + + default: + return -1; + }; + } +t1 = _now(); + + if (change_volume) + { + report_change = 1; + xine_set_param(this->stream, param_volume, this->last_volume); + } +t2 = _now(); + + if (report_change && this->volume_mode != XINE_VDR_VOLUME_CHANGE_SW) + { + xine_event_t event; + xine_audio_level_data_t data; + + data.left + = data.right + = xine_get_param(this->stream, param_volume); + data.mute + = xine_get_param(this->stream, param_mute); +t3 = _now(); + + event.type = XINE_EVENT_AUDIO_LEVEL; + event.data = &data; + event.data_length = sizeof (data); + + xine_event_send(this->stream, &event); + } + } +/* fprintf(stderr, "volume: %6.3lf ms, %6.3lf ms, %6.3lf ms\n", t1 - t0, t2 - t1, t3 - t2); */ + } + break; + + case func_set_speed: + { + READ_DATA_OR_FAIL(set_speed, lprintf("got SETSPEED\n")); + + lprintf("... got SETSPEED %d\n", data->speed); + + if (data->speed != xine_get_param(this->stream, XINE_PARAM_FINE_SPEED)) + xine_set_param(this->stream, XINE_PARAM_FINE_SPEED, data->speed); + } + break; + + case func_set_prebuffer: + { + READ_DATA_OR_FAIL(set_prebuffer, lprintf("got SETPREBUFFER\n")); + + xine_set_param(this->stream, XINE_PARAM_METRONOM_PREBUFFER, data->prebuffer); + } + break; + + case func_metronom: + { + READ_DATA_OR_FAIL(metronom, lprintf("got METRONOM\n")); + + _x_demux_control_newpts(this->stream, data->pts, data->flags); + } + break; + + case func_start: + { + READ_DATA_OR_FAIL(start, lprintf("got START\n")); + + _x_demux_control_start(this->stream); + _x_demux_seek(this->stream, 0, 0, 0); + } + break; + + case func_wait: + { + READ_DATA_OR_FAIL(wait, lprintf("got WAIT\n")); + + { + result_wait_t result_wait; + result_wait.header.func = data->header.func; + result_wait.header.len = sizeof (result_wait); + + if (sizeof (result_wait) != vdr_write(this->fh_result, &result_wait, sizeof (result_wait))) + return -1; + + if (data->id == 1) + this->startup_phase = 0; + } + } + break; + + case func_setup: + { + READ_DATA_OR_FAIL(setup, lprintf("got SETUP\n")); + + this->osd_unscaled_blending = data->osd_unscaled_blending; + this->volume_mode = data->volume_mode; + this->mute_mode = data->mute_mode; + this->image4_3_zoom_x = data->image4_3_zoom_x; + this->image4_3_zoom_y = data->image4_3_zoom_y; + this->image16_9_zoom_x = data->image16_9_zoom_x; + this->image16_9_zoom_y = data->image16_9_zoom_y; + + adjust_zoom(this); + } + break; + + case func_grab_image: + { + READ_DATA_OR_FAIL(grab_image, lprintf("got GRABIMAGE\n")); + + { + off_t ret_val = -1; + + xine_current_frame_data_t frame_data; + memset(&frame_data, 0, sizeof (frame_data)); + + if (xine_get_current_frame_data(this->stream, &frame_data, XINE_FRAME_DATA_ALLOCATE_IMG)) + { + if (frame_data.ratio_code == XINE_VO_ASPECT_SQUARE) + frame_data.ratio_code = 10000; + else if (frame_data.ratio_code == XINE_VO_ASPECT_4_3) + frame_data.ratio_code = 13333; + else if (frame_data.ratio_code == XINE_VO_ASPECT_ANAMORPHIC) + frame_data.ratio_code = 17778; + else if (frame_data.ratio_code == XINE_VO_ASPECT_DVB) + frame_data.ratio_code = 21100; + } + + if (!frame_data.img) + memset(&frame_data, 0, sizeof (frame_data)); + + { + result_grab_image_t result_grab_image; + result_grab_image.header.func = data->header.func; + result_grab_image.header.len = sizeof (result_grab_image) + frame_data.img_size; + + result_grab_image.width = frame_data.width; + result_grab_image.height = frame_data.height; + result_grab_image.ratio = frame_data.ratio_code; + result_grab_image.format = frame_data.format; + result_grab_image.interlaced = frame_data.interlaced; + result_grab_image.crop_left = frame_data.crop_left; + result_grab_image.crop_right = frame_data.crop_right; + result_grab_image.crop_top = frame_data.crop_top; + result_grab_image.crop_bottom = frame_data.crop_bottom; + + if (sizeof (result_grab_image) == vdr_write(this->fh_result, &result_grab_image, sizeof (result_grab_image))) + { + if (!frame_data.img_size || (frame_data.img_size == vdr_write(this->fh_result, frame_data.img, frame_data.img_size))) + ret_val = 0; + } + } + + free(frame_data.img); + + if (ret_val != 0) + return ret_val; + } + } + break; + + case func_get_pts: + { + READ_DATA_OR_FAIL(get_pts, lprintf("got GETPTS\n")); + + { + result_get_pts_t result_get_pts; + result_get_pts.header.func = data->header.func; + result_get_pts.header.len = sizeof (result_get_pts); + + pthread_mutex_lock(&this->vpts_offset_queue_lock); + + while (this->vpts_offset_queue_changes) + pthread_cond_wait(&this->vpts_offset_queue_changed_cond, &this->vpts_offset_queue_lock); + + if (this->last_disc_type == DISC_STREAMSTART + && data->ms_timeout > 0) + { + struct timespec abstime; + { + struct timeval now; + gettimeofday(&now, 0); + + abstime.tv_sec = now.tv_sec + data->ms_timeout / 1000; + abstime.tv_nsec = now.tv_usec * 1000 + (data->ms_timeout % 1000) * 1e6; + + if (abstime.tv_nsec > 1e9) + { + abstime.tv_nsec -= 1e9; + abstime.tv_sec++; + } + } + + while (this->last_disc_type == DISC_STREAMSTART + || this->vpts_offset_queue_changes) + { + if (0 != pthread_cond_timedwait(&this->vpts_offset_queue_changed_cond, &this->vpts_offset_queue_lock, &abstime)) + break; + } + } + + if (this->last_disc_type == DISC_STREAMSTART + || this->vpts_offset_queue_changes) + { + result_get_pts.pts = -1; + result_get_pts.queued = 0; + } + else + { + int64_t offset, vpts = xine_get_current_vpts(this->stream); + + vdr_vpts_offset_queue_process(this, vpts); + +/* if(this->vpts_offset_queue) */ +if(0) + { +fprintf(stderr, "C ---------------------------------------------\n"); + vdr_vpts_offset_t *p = this->vpts_offset_queue; + while (p) + { + fprintf(stderr, "C now: %12"PRId64", vpts: %12"PRId64", offset: %12"PRId64"\n", xine_get_current_vpts(this->stream), p->vpts, p->offset); + p = p->next; + } +fprintf(stderr, "C =============================================\n"); + } + + if (this->vpts_offset_queue) + offset = this->vpts_offset_queue->offset; + else + offset = this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET); + + result_get_pts.pts = (vpts - offset) & ((1ll << 33) - 1); + result_get_pts.queued = !!this->vpts_offset_queue; +/* fprintf(stderr, "vpts: %12ld, stc: %12ld, offset: %12ld\n", vpts, result_get_pts.pts, offset); */ + } + + pthread_mutex_unlock(&this->vpts_offset_queue_lock); + + if (sizeof (result_get_pts) != vdr_write(this->fh_result, &result_get_pts, sizeof (result_get_pts))) + return -1; + } + } + break; + + case func_get_version: + { + READ_DATA_OR_FAIL(get_version, lprintf("got GETVERSION\n")); + + { + result_get_version_t result_get_version; + result_get_version.header.func = data->header.func; + result_get_version.header.len = sizeof (result_get_version); + + result_get_version.version = XINE_VDR_VERSION; + + if (sizeof (result_get_version) != vdr_write(this->fh_result, &result_get_version, sizeof (result_get_version))) + return -1; + } + } + break; + + case func_video_size: + { + READ_DATA_OR_FAIL(video_size, lprintf("got VIDEO SIZE\n")); + + { + int format; + + result_video_size_t result_video_size; + result_video_size.header.func = data->header.func; + result_video_size.header.len = sizeof (result_video_size); + + result_video_size.top = -1; + result_video_size.left = -1; + result_video_size.width = -1; + result_video_size.height = -1; + result_video_size.ratio = 0; + + xine_get_current_frame(this->stream, &result_video_size.width, &result_video_size.height, &result_video_size.ratio, &format, 0); + + if (result_video_size.ratio == XINE_VO_ASPECT_SQUARE) + result_video_size.ratio = 10000; + else if (result_video_size.ratio == XINE_VO_ASPECT_4_3) + result_video_size.ratio = 13333; + else if (result_video_size.ratio == XINE_VO_ASPECT_ANAMORPHIC) + result_video_size.ratio = 17778; + else if (result_video_size.ratio == XINE_VO_ASPECT_DVB) + result_video_size.ratio = 21100; + + if (0 != this->frame_size.x + || 0 != this->frame_size.y + || 0 != this->frame_size.w + || 0 != this->frame_size.h) + { + result_video_size.left = this->frame_size.x; + result_video_size.top = this->frame_size.y; + result_video_size.width = this->frame_size.w; + result_video_size.height = this->frame_size.h; + } +/* fprintf(stderr, "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n"); */ + result_video_size.zoom_x = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_X); + result_video_size.zoom_y = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_Y); +/* fprintf(stderr, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n"); */ + if (sizeof (result_video_size) != vdr_write(this->fh_result, &result_video_size, sizeof (result_video_size))) + return -1; +/* fprintf(stderr, "GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG\n"); */ + } + } + break; + + case func_reset_audio: + { + double _t1, _t2; + int _n = 0; + + READ_DATA_OR_FAIL(reset_audio, lprintf("got RESET AUDIO\n")); + + if (this->stream->audio_fifo) + { + xine_set_param(this->stream, XINE_PARAM_IGNORE_AUDIO, 1); + xine_set_param(this->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, -2); + + _t1 = _now(); + + while (1) + { + int n = xine_get_stream_info(this->stream, XINE_STREAM_INFO_MAX_AUDIO_CHANNEL); + if (n <= 0) + break; + + /* keep the decoder running */ + if (this->stream->audio_fifo) + { + buf_element_t *buf = this->stream->audio_fifo->buffer_pool_alloc(this->stream->audio_fifo); + if (!buf) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: buffer_pool_alloc() failed!\n"), LOG_MODULE); + return -1; + } + + buf->type = BUF_CONTROL_RESET_TRACK_MAP; + + this->stream->audio_fifo->put(this->stream->audio_fifo, buf); + } + +/* sched_yield(); */ + xine_usec_sleep(5000); + _n++; + } + + _t2 = _now(); + /* fprintf(stderr, "vdr: reset_audio: n: %d, %.1lf\n", _n, _t2 - _t1); */ + + xine_set_param(this->stream, XINE_PARAM_AUDIO_CHANNEL_LOGICAL, -1); + + _x_stream_info_reset(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE); + _x_meta_info_reset(this->stream, XINE_META_INFO_AUDIOCODEC); + + xine_set_param(this->stream, XINE_PARAM_IGNORE_AUDIO, 0); + } + } + break; + + case func_query_capabilities: + { + READ_DATA_OR_FAIL(query_capabilities, lprintf("got QUERYCAPABILITIES\n")); + + { + result_query_capabilities_t result_query_capabilities; + result_query_capabilities.header.func = data->header.func; + result_query_capabilities.header.len = sizeof (result_query_capabilities); + + result_query_capabilities.osd_max_num_windows = MAX_SHOWING; + result_query_capabilities.osd_palette_max_depth = 8; + result_query_capabilities.osd_palette_is_shared = 0; + result_query_capabilities.osd_supports_argb_layer = this->osd_supports_argb_layer; + result_query_capabilities.osd_supports_custom_extent = this->osd_supports_custom_extent; + + if (sizeof (result_query_capabilities) != vdr_write(this->fh_result, &result_query_capabilities, sizeof (result_query_capabilities))) + return -1; + } + } + break; + + default: + lprintf("unknown function: %d\n", this->cur_func); + } + + if (this->cur_size != this->cur_done) + { + off_t skip = this->cur_size - this->cur_done; + + lprintf("func: %d, skipping: %lld\n", this->cur_func, skip); + + while (skip > BUF_SIZE) + { + n = vdr_read_abort(this->stream, this->fh_control, this->seek_buf, BUF_SIZE); + if (n != BUF_SIZE) + return -1; + + skip -= BUF_SIZE; + } + + n = vdr_read_abort(this->stream, this->fh_control, this->seek_buf, skip); + if (n != skip) + return -1; + + this->cur_done = this->cur_size; + + return -1; + } + + return 0; +} + +static void *vdr_rpc_thread_loop(void *arg) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)arg; + int frontend_lock_failures = 0; + int failed = 0; + int was_startup_phase = this->startup_phase; + + while (!failed + && !this->rpc_thread_shutdown + && was_startup_phase == this->startup_phase) + { + struct timeval timeout; + fd_set rset; + + FD_ZERO(&rset); + FD_SET(this->fh_control, &rset); + + timeout.tv_sec = 0; + timeout.tv_usec = 50000; + + if (select(this->fh_control + 1, &rset, NULL, NULL, &timeout) > 0) + { + if (!_x_lock_frontend(this->stream, 100)) + { + if (++frontend_lock_failures > 50) + { + failed = 1; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE ": locking frontend for rpc command execution failed, exiting ...\n"); + } + } + else + { + frontend_lock_failures = 0; + + if (_x_lock_port_rewiring(this->stream->xine, 100)) + { + if (vdr_execute_rpc_command(this) < 0) + { + failed = 1; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE ": execution of rpc command %d (%s) failed, exiting ...\n", this->cur_func, ""); + } + + _x_unlock_port_rewiring(this->stream->xine); + } + + _x_unlock_frontend(this->stream); + } + } + } + + if (!failed && was_startup_phase) + return (void *)1; + + /* close control and result channel here to have vdr-xine initiate a disconnect for the above error case ... */ + close(this->fh_control); + this->fh_control = -1; + + close(this->fh_result); + this->fh_result = -1; + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + LOG_MODULE ": rpc thread done.\n"); + + pthread_mutex_lock(&this->rpc_thread_shutdown_lock); + this->rpc_thread_shutdown = -1; + pthread_cond_broadcast(&this->rpc_thread_shutdown_cond); + pthread_mutex_unlock(&this->rpc_thread_shutdown_lock); + + return 0; +} + +static int internal_write_event_key(vdr_input_plugin_t *this, uint32_t key) +{ + event_key_t event; + event.header.func = func_key; + event.header.len = sizeof (event); + + event.key = key; + + if (sizeof (event) != vdr_write(this->fh_event, &event, sizeof (event))) + return -1; + + return 0; +} + +static int internal_write_event_frame_size(vdr_input_plugin_t *this) +{ + event_frame_size_t event; + event.header.func = func_frame_size; + event.header.len = sizeof (event); + + event.left = this->frame_size.x; + event.top = this->frame_size.y; + event.width = this->frame_size.w, + event.height = this->frame_size.h; + event.zoom_x = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_X); + event.zoom_y = xine_get_param(this->stream, XINE_PARAM_VO_ZOOM_Y); + + if (sizeof (event) != vdr_write(this->fh_event, &event, sizeof (event))) + return -1; + + return 0; +} + +static int internal_write_event_play_external(vdr_input_plugin_t *this, uint32_t key) +{ + event_play_external_t event; + event.header.func = func_play_external; + event.header.len = sizeof (event); + + event.key = key; + + if (sizeof (event) != vdr_write(this->fh_event, &event, sizeof (event))) + return -1; + + return 0; +} + +static int internal_write_event_discontinuity(vdr_input_plugin_t *this, int32_t type) +{ + event_discontinuity_t event; + event.header.func = func_discontinuity; + event.header.len = sizeof (event); + + event.type = type; + + if (sizeof (event) != vdr_write(this->fh_event, &event, sizeof (event))) + return -1; + + return 0; +} + +static off_t vdr_plugin_read(input_plugin_t *this_gen, + char *buf_gen, off_t len) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *) this_gen; + uint8_t *buf = (uint8_t *)buf_gen; + off_t n, total; +#ifdef LOG_READ + lprintf ("reading %lld bytes...\n", len); +#endif + total=0; + if (this->curpos < this->preview_size) + { + n = this->preview_size - this->curpos; + if (n > (len - total)) + n = len - total; +#ifdef LOG_READ + lprintf ("%lld bytes from preview (which has %lld bytes)\n", + n, this->preview_size); +#endif + memcpy (&buf[total], &this->preview[this->curpos], n); + this->curpos += n; + total += n; + } + + if( (len-total) > 0 ) + { + int retries = 0; + do + { + n = vdr_read_abort (this->stream, this->fh, (char *)&buf[total], len-total); + if (0 == n) + lprintf("read 0, retries: %d\n", retries); + } + while (0 == n + && !this->stream_external + && _x_continue_stream_processing(this->stream) + && 200 > retries++); /* 200 * 50ms */ +#ifdef LOG_READ + lprintf ("got %lld bytes (%lld/%lld bytes read)\n", + n,total,len); +#endif + if (n < 0) + { + _x_message(this->stream, XINE_MSG_READ_ERROR, NULL); + return 0; + } + + this->curpos += n; + total += n; + } + + if (this->find_sync_point + && total == 6) + { + pthread_mutex_lock(&this->find_sync_point_lock); + + while (this->find_sync_point + && total == 6 + && buf[0] == 0x00 + && buf[1] == 0x00 + && buf[2] == 0x01) + { + int l, sp; + + if (buf[3] == 0xbe + && buf[4] == 0xff) + { +/* fprintf(stderr, "------- seen sync point: %02x, waiting for: %02x\n", buf[5], this->find_sync_point); */ + if (buf[5] == this->find_sync_point) + { + this->find_sync_point = 0; + break; + } + } + + if ((buf[3] & 0xf0) != 0xe0 + && (buf[3] & 0xe0) != 0xc0 + && buf[3] != 0xbd + && buf[3] != 0xbe) + { + break; + } + + l = buf[4] * 256 + buf[5]; + if (l <= 0) + break; + + sp = this->find_sync_point; + this->find_sync_point = 0; + this_gen->seek(this_gen, l, SEEK_CUR); + total = this_gen->read(this_gen, buf, 6); + this->find_sync_point = sp; + } + + pthread_mutex_unlock(&this->find_sync_point_lock); + } + + return total; +} + +static buf_element_t *vdr_plugin_read_block(input_plugin_t *this_gen, fifo_buffer_t *fifo, + off_t todo) +{ + off_t total_bytes; + buf_element_t *buf = fifo->buffer_pool_alloc(fifo); + + buf->content = buf->mem; + buf->type = BUF_DEMUX_BLOCK; + + total_bytes = vdr_plugin_read(this_gen, (char *)buf->content, todo); + + if (total_bytes != todo) + { + buf->free_buffer(buf); + return NULL; + } + + buf->size = total_bytes; + + return buf; +} + +/* forward reference */ +static off_t vdr_plugin_get_current_pos(input_plugin_t *this_gen); + +static off_t vdr_plugin_seek(input_plugin_t *this_gen, off_t offset, int origin) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + + lprintf("seek %lld offset, %d origin...\n", + offset, origin); + + if ((origin == SEEK_CUR) && (offset >= 0)) + { + for ( ; ((int)offset) - BUF_SIZE > 0; offset -= BUF_SIZE) + { + if (!this_gen->read(this_gen, this->seek_buf, BUF_SIZE)) + return this->curpos; + } + + this_gen->read (this_gen, this->seek_buf, offset); + } + + if (origin == SEEK_SET) + { + if (offset < this->curpos) + { + if (this->curpos <= this->preview_size) + this->curpos = offset; + else + lprintf("cannot seek back! (%lld > %lld)\n", this->curpos, offset); + } + else + { + offset -= this->curpos; + + for ( ; ((int)offset) - BUF_SIZE > 0; offset -= BUF_SIZE) + { + if (!this_gen->read(this_gen, this->seek_buf, BUF_SIZE)) + return this->curpos; + } + + this_gen->read(this_gen, this->seek_buf, offset); + } + } + + return this->curpos; +} + +static off_t vdr_plugin_get_length(input_plugin_t *this_gen) +{ + return 0; +} + +static uint32_t vdr_plugin_get_capabilities(input_plugin_t *this_gen) +{ + return INPUT_CAP_PREVIEW; +} + +static uint32_t vdr_plugin_get_blocksize(input_plugin_t *this_gen) +{ + return 0; +} + +static off_t vdr_plugin_get_current_pos(input_plugin_t *this_gen) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + + return this->curpos; +} + +static const char *vdr_plugin_get_mrl(input_plugin_t *this_gen) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + + return this->mrl; +} + +static void vdr_plugin_dispose(input_plugin_t *this_gen) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + int i; + + external_stream_stop(this); + + if (this->event_queue) + xine_event_dispose_queue(this->event_queue); + + if (this->rpc_thread) + { + struct timespec abstime; + int ms_to_time_out = 10000; + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: shutting down rpc thread (timeout: %d ms) ...\n"), LOG_MODULE, ms_to_time_out); + + pthread_mutex_lock(&this->rpc_thread_shutdown_lock); + + if (this->rpc_thread_shutdown > -1) + { + this->rpc_thread_shutdown = 1; + + { + struct timeval now; + gettimeofday(&now, 0); + + abstime.tv_sec = now.tv_sec + ms_to_time_out / 1000; + abstime.tv_nsec = now.tv_usec * 1000 + (ms_to_time_out % 1000) * 1e6; + + if (abstime.tv_nsec > 1e9) + { + abstime.tv_nsec -= 1e9; + abstime.tv_sec++; + } + } + + if (0 != pthread_cond_timedwait(&this->rpc_thread_shutdown_cond, &this->rpc_thread_shutdown_lock, &abstime)) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: cancelling rpc thread in function %d...\n"), LOG_MODULE, this->cur_func); + pthread_cancel(this->rpc_thread); + } + } + + pthread_mutex_unlock(&this->rpc_thread_shutdown_lock); + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: joining rpc thread ...\n"), LOG_MODULE); + pthread_join(this->rpc_thread, 0); + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: rpc thread joined.\n"), LOG_MODULE); + } + + pthread_cond_destroy(&this->rpc_thread_shutdown_cond); + pthread_mutex_destroy(&this->rpc_thread_shutdown_lock); + + if (this->metronom_thread) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: joining metronom thread ...\n"), LOG_MODULE); + + pthread_mutex_lock(&this->metronom_thread_call_lock); + + pthread_mutex_lock(&this->metronom_thread_lock); + this->metronom_thread_request = -1; + this->metronom_thread_reply = 0; + pthread_cond_broadcast(&this->metronom_thread_request_cond); +/* + pthread_mutex_unlock(&this->metronom_thread_lock); + + pthread_mutex_lock(&this->metronom_thread_lock); + if (!this->metronom_thread_reply) +*/ + pthread_cond_wait(&this->metronom_thread_reply_cond, &this->metronom_thread_lock); + pthread_mutex_unlock(&this->metronom_thread_lock); + + pthread_mutex_unlock(&this->metronom_thread_call_lock); + + pthread_join(this->metronom_thread, 0); + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, _("%s: metronom thread joined.\n"), LOG_MODULE); + } + + pthread_mutex_destroy(&this->metronom_thread_lock); + pthread_cond_destroy(&this->metronom_thread_request_cond); + pthread_cond_destroy(&this->metronom_thread_reply_cond); + + pthread_mutex_destroy(&this->find_sync_point_lock); + pthread_mutex_destroy(&this->adjust_zoom_lock); + + if (this->fh_result != -1) + close(this->fh_result); + + if (this->fh_control != -1) + close(this->fh_control); + + if (this->fh_event != -1) + close(this->fh_event); + + for (i = 0; i < VDR_MAX_NUM_WINDOWS; i++) + { + if (0 == this->osd[ i ].window) + continue; + + xine_osd_hide(this->osd[ i ].window, 0); + xine_osd_free(this->osd[ i ].window); + + free(this->osd[ i ].argb_buffer); + } + + if (this->osd_buffer) + free(this->osd_buffer); + + if ((this->fh != STDIN_FILENO) && (this->fh != -1)) + close(this->fh); + + free(this->mrl); + + this->stream->metronom = this->metronom.stream_metronom; + this->metronom.stream_metronom = 0; + + vdr_vpts_offset_queue_purge(this); + pthread_cond_destroy(&this->vpts_offset_queue_changed_cond); + pthread_mutex_destroy(&this->vpts_offset_queue_lock); + + free(this); +} + +static int vdr_plugin_get_optional_data(input_plugin_t *this_gen, + void *data, int data_type) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + (void)this; + switch (data_type) + { + case INPUT_OPTIONAL_DATA_PREVIEW: + /* just fake what mpeg_pes demuxer expects */ + memcpy (data, "\x00\x00\x01\xe0\x00\x03\x80\x00\x00", 9); + return 9; + } + return INPUT_OPTIONAL_UNSUPPORTED; +} + +static inline const char *mrl_to_fifo (const char *mrl) +{ + /* vdr://foo -> /foo */ + return mrl + 3 + strspn (mrl + 4, "/"); +} + +static inline const char *mrl_to_host (const char *mrl) +{ + /* netvdr://host:port -> host:port */ + return strrchr (mrl, '/') + 1; +} + +static int vdr_plugin_open_fifo_mrl(input_plugin_t *this_gen) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + char *filename = (char *)mrl_to_fifo (this->mrl); + + if(!strcmp(filename, "/")) { + filename = (char *)VDR_ABS_FIFO_DIR "/stream"; + } + + filename = strdup(filename); + + _x_mrl_unescape (filename); + this->fh = open(filename, O_RDONLY | O_NONBLOCK); + + lprintf("filename '%s'\n", filename); + + if (this->fh == -1) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to open '%s' (%s)\n"), LOG_MODULE, + filename, + strerror(errno)); + free (filename); + return 0; + } + + { + struct pollfd poll_fh = { this->fh, POLLIN, 0 }; + + int r = poll(&poll_fh, 1, 300); + if (1 != r) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to open '%s' (%s)\n"), LOG_MODULE, + filename, + _("timeout expired during setup phase")); + free (filename); + return 0; + } + } + + fcntl(this->fh, F_SETFL, ~O_NONBLOCK & fcntl(this->fh, F_GETFL, 0)); + + /* eat initial handshake byte */ + { + char b; + read(this->fh, &b, 1); + } + + { + char *filename_control = 0; + asprintf(&filename_control, "%s.control", filename); + + this->fh_control = open(filename_control, O_RDONLY); + + if (this->fh_control == -1) { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to open '%s' (%s)\n"), LOG_MODULE, + filename_control, + strerror(errno)); + + free(filename_control); + free (filename); + return 0; + } + + free(filename_control); + } + + { + char *filename_result = 0; + asprintf(&filename_result, "%s.result", filename); + + this->fh_result = open(filename_result, O_WRONLY); + + if (this->fh_result == -1) { + perror("failed"); + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to open '%s' (%s)\n"), LOG_MODULE, + filename_result, + strerror(errno)); + + free(filename_result); + free (filename); + return 0; + } + + free(filename_result); + } + + { + char *filename_event = 0; + asprintf(&filename_event, "%s.event", filename); + + this->fh_event = open(filename_event, O_WRONLY); + + if (this->fh_event == -1) { + perror("failed"); + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to open '%s' (%s)\n"), LOG_MODULE, + filename_event, + strerror(errno)); + + free(filename_event); + free (filename); + return 0; + } + + free(filename_event); + } + + free (filename); + return 1; +} + +static int vdr_plugin_open_socket(vdr_input_plugin_t *this, struct hostent *host, unsigned short port) +{ + int fd; + struct sockaddr_in sain; + struct in_addr iaddr; + + if ((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to create socket for port %d (%s)\n"), LOG_MODULE, + port, strerror(errno)); + return -1; + } + + iaddr.s_addr = *((unsigned int *)host->h_addr_list[0]); + + sain.sin_port = htons(port); + sain.sin_family = AF_INET; + sain.sin_addr = iaddr; + + if (connect(fd, (struct sockaddr *)&sain, sizeof (sain)) < 0) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to connect to port %d (%s)\n"), LOG_MODULE, port, + strerror(errno)); + + return -1; + } + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: socket opening (port %d) successful, fd = %d\n"), LOG_MODULE, port, fd); + + return fd; +} + +static int vdr_plugin_open_sockets(vdr_input_plugin_t *this) +{ + struct hostent *host; + char *mrl_host = strdup (mrl_to_host (this->mrl)); + char *mrl_port; + int port = 18701; + + mrl_port = strchr(mrl_host, '#'); + if (mrl_port) + *mrl_port = 0; /* strip off things like '#demux:mpeg_pes' */ + + _x_mrl_unescape (mrl_host); + + mrl_port = strchr(mrl_host, ':'); + if (mrl_port) + { + port = atoi(mrl_port + 1); + *mrl_port = 0; + } + + host = gethostbyname(mrl_host); + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: connecting to vdr.\n"), LOG_MODULE); + + if (!host) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: failed to resolve hostname '%s' (%s)\n"), LOG_MODULE, + mrl_host, + strerror(errno)); + free (mrl_host); + return 0; + } + free (mrl_host); + + if ((this->fh = vdr_plugin_open_socket(this, host, port + 0)) == -1) + return 0; + + fcntl(this->fh, F_SETFL, ~O_NONBLOCK & fcntl(this->fh, F_GETFL, 0)); + + if ((this->fh_control = vdr_plugin_open_socket(this, host, port + 1)) == -1) + return 0; + + if ((this->fh_result = vdr_plugin_open_socket(this, host, port + 2)) == -1) + return 0; + + if ((this->fh_event = vdr_plugin_open_socket(this, host, port + 3)) == -1) + return 0; + + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: connecting to all sockets (port %d .. %d) was successful.\n"), LOG_MODULE, port, port + 3); + + return 1; +} + +static int vdr_plugin_open_socket_mrl(input_plugin_t *this_gen) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + + lprintf("input_vdr: connecting to vdr-xine-server...\n"); + + if (!vdr_plugin_open_sockets(this)) + return 0; + + return 1; +} + +static void *vdr_metronom_thread_loop(void *arg) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)arg; + int run = 1; + + pthread_mutex_lock(&this->metronom_thread_lock); + + while (run) + { + if (this->metronom_thread_request == 0) + pthread_cond_wait(&this->metronom_thread_request_cond, &this->metronom_thread_lock); + + if (this->metronom_thread_request == -1) + run = 0; + else + this->metronom.metronom.handle_audio_discontinuity(&this->metronom.metronom, DISC_ABSOLUTE, this->metronom_thread_request); + + this->metronom_thread_request = 0; + this->metronom_thread_reply = 1; + pthread_cond_broadcast(&this->metronom_thread_reply_cond); + } + + pthread_mutex_unlock(&this->metronom_thread_lock); + + return 0; +} + +static int vdr_plugin_open(input_plugin_t *this_gen) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)this_gen; + + lprintf("trying to open '%s'...\n", this->mrl); + + if (this->fh == -1) + { + int err = 0; + + if (!strncasecmp(&this->mrl[0], "vdr:/", 5)) + { + if (!vdr_plugin_open_fifo_mrl(this_gen)) + return 0; + } + else if (!strncasecmp(&this->mrl[0], "netvdr:/", 8)) + { + if (!vdr_plugin_open_socket_mrl(this_gen)) + return 0; + } + else + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: MRL (%s) invalid! MRL should start with vdr://path/to/fifo/stream or netvdr://host:port where ':port' is optional.\n"), LOG_MODULE, + strerror(err)); + return 0; + } + + if ((err = pthread_create(&this->metronom_thread, NULL, + vdr_metronom_thread_loop, (void *)this)) != 0) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: can't create new thread (%s)\n"), LOG_MODULE, + strerror(err)); + + return 0; + } + + this->rpc_thread_shutdown = 0; + + /* let this thread handle rpc commands in startup phase */ + this->startup_phase = 1; + if (0 == vdr_rpc_thread_loop(this)) + return 0; +/* fprintf(stderr, "####################################################\n"); */ + if ((err = pthread_create(&this->rpc_thread, NULL, + vdr_rpc_thread_loop, (void *)this)) != 0) + { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: can't create new thread (%s)\n"), LOG_MODULE, + strerror(err)); + + return 0; + } + } + + /* + * mrl accepted and opened successfully at this point + * + * => create plugin instance + */ + + this->preview = NULL; + this->preview_size = 0; + this->curpos = 0; + + return 1; +} + +static void event_handler(void *user_data, const xine_event_t *event) +{ + vdr_input_plugin_t *this = (vdr_input_plugin_t *)user_data; + uint32_t key = key_none; + + lprintf("eventHandler(): event->type: %d\n", event->type); + + if (XINE_EVENT_VDR_FRAMESIZECHANGED == event->type) + { + memcpy(&this->frame_size, event->data, event->data_length); + + if (0 != internal_write_event_frame_size(this)) + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: input event write: %s.\n"), LOG_MODULE, strerror(errno)); + + adjust_zoom(this); + return; + } + + if (XINE_EVENT_VDR_DISCONTINUITY == event->type) + { + if (0 != internal_write_event_discontinuity(this, event->data_length)) + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: input event write: %s.\n"), LOG_MODULE, strerror(errno)); + + return; + } + + if (XINE_EVENT_VDR_PLUGINSTARTED == event->type) + { + if (0 == event->data_length) /* vdr_video */ + { + xine_event_t event; + + event.type = XINE_EVENT_VDR_TRICKSPEEDMODE; + event.data = 0; + event.data_length = 0; /* this->trick_speed_mode; */ + + xine_event_send(this->stream, &event); + } + else if (1 == event->data_length) /* vdr_audio */ + { + xine_event_t event; + vdr_select_audio_data_t event_data; + + event_data.channels = this->audio_channels; + + event.type = XINE_EVENT_VDR_SELECTAUDIO; + event.data = &event_data; + event.data_length = sizeof (event_data); + + xine_event_send(this->stream, &event); + } + else + { + fprintf(stderr, "input_vdr: illegal XINE_EVENT_VDR_PLUGINSTARTED: %d\n", event->data_length); + } + + return; + } + + switch (event->type) + { + case XINE_EVENT_INPUT_UP: key = key_up; break; + case XINE_EVENT_INPUT_DOWN: key = key_down; break; + case XINE_EVENT_INPUT_LEFT: key = key_left; break; + case XINE_EVENT_INPUT_RIGHT: key = key_right; break; + case XINE_EVENT_INPUT_SELECT: key = key_ok; break; + case XINE_EVENT_VDR_BACK: key = key_back; break; + case XINE_EVENT_VDR_CHANNELPLUS: key = key_channel_plus; break; + case XINE_EVENT_VDR_CHANNELMINUS: key = key_channel_minus; break; + case XINE_EVENT_VDR_RED: key = key_red; break; + case XINE_EVENT_VDR_GREEN: key = key_green; break; + case XINE_EVENT_VDR_YELLOW: key = key_yellow; break; + case XINE_EVENT_VDR_BLUE: key = key_blue; break; + case XINE_EVENT_VDR_PLAY: key = key_play; break; + case XINE_EVENT_VDR_PAUSE: key = key_pause; break; + case XINE_EVENT_VDR_STOP: key = key_stop; break; + case XINE_EVENT_VDR_RECORD: key = key_record; break; + case XINE_EVENT_VDR_FASTFWD: key = key_fast_fwd; break; + case XINE_EVENT_VDR_FASTREW: key = key_fast_rew; break; + case XINE_EVENT_VDR_POWER: key = key_power; break; + case XINE_EVENT_VDR_SCHEDULE: key = key_schedule; break; + case XINE_EVENT_VDR_CHANNELS: key = key_channels; break; + case XINE_EVENT_VDR_TIMERS: key = key_timers; break; + case XINE_EVENT_VDR_RECORDINGS: key = key_recordings; break; + case XINE_EVENT_INPUT_MENU1: key = key_menu; break; + case XINE_EVENT_VDR_SETUP: key = key_setup; break; + case XINE_EVENT_VDR_COMMANDS: key = key_commands; break; + case XINE_EVENT_INPUT_NUMBER_0: key = key_0; break; + case XINE_EVENT_INPUT_NUMBER_1: key = key_1; break; + case XINE_EVENT_INPUT_NUMBER_2: key = key_2; break; + case XINE_EVENT_INPUT_NUMBER_3: key = key_3; break; + case XINE_EVENT_INPUT_NUMBER_4: key = key_4; break; + case XINE_EVENT_INPUT_NUMBER_5: key = key_5; break; + case XINE_EVENT_INPUT_NUMBER_6: key = key_6; break; + case XINE_EVENT_INPUT_NUMBER_7: key = key_7; break; + case XINE_EVENT_INPUT_NUMBER_8: key = key_8; break; + case XINE_EVENT_INPUT_NUMBER_9: key = key_9; break; + case XINE_EVENT_VDR_USER1: key = key_user1; break; + case XINE_EVENT_VDR_USER2: key = key_user2; break; + case XINE_EVENT_VDR_USER3: key = key_user3; break; + case XINE_EVENT_VDR_USER4: key = key_user4; break; + case XINE_EVENT_VDR_USER5: key = key_user5; break; + case XINE_EVENT_VDR_USER6: key = key_user6; break; + case XINE_EVENT_VDR_USER7: key = key_user7; break; + case XINE_EVENT_VDR_USER8: key = key_user8; break; + case XINE_EVENT_VDR_USER9: key = key_user9; break; + case XINE_EVENT_VDR_VOLPLUS: key = key_volume_plus; break; + case XINE_EVENT_VDR_VOLMINUS: key = key_volume_minus; break; + case XINE_EVENT_VDR_MUTE: key = key_mute; break; + case XINE_EVENT_VDR_AUDIO: key = key_audio; break; + case XINE_EVENT_VDR_INFO: key = key_info; break; + case XINE_EVENT_VDR_CHANNELPREVIOUS: key = key_channel_previous; break; + case XINE_EVENT_INPUT_NEXT: key = key_next; break; + case XINE_EVENT_INPUT_PREVIOUS: key = key_previous; break; + case XINE_EVENT_VDR_SUBTITLES: key = key_subtitles; break; + default: + return; + } + + if (0 != internal_write_event_key(this, key)) + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("%s: input event write: %s.\n"), LOG_MODULE, strerror(errno)); +} + + +static int64_t vdr_vpts_offset_queue_change_begin(vdr_input_plugin_t *this, int type) +{ + pthread_mutex_lock(&this->vpts_offset_queue_lock); + this->vpts_offset_queue_changes++; + pthread_mutex_unlock(&this->vpts_offset_queue_lock); + + return this->metronom.metronom.get_option(&this->metronom.metronom, METRONOM_VPTS_OFFSET); +} + +static void vdr_vpts_offset_queue_change_end(vdr_input_plugin_t *this, int type, int64_t disc_off, int64_t vpts_offset) +{ + pthread_mutex_lock(&this->vpts_offset_queue_lock); + +if(0) + { +fprintf(stderr, "A ---------------------------------------------\n"); + vdr_vpts_offset_t *p = this->vpts_offset_queue; + while (p) + { + fprintf(stderr, "A now: %12"PRId64", vpts: %12"PRId64", offset: %12"PRId64"\n", xine_get_current_vpts(this->stream), p->vpts, p->offset); + p = p->next; + } +fprintf(stderr, "A =============================================\n"); + } + + if (type == DISC_ABSOLUTE) + { + int64_t vpts = this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET) + disc_off; + + if (!this->vpts_offset_queue + || this->vpts_offset_queue_tail->vpts < vpts) + { + vdr_vpts_offset_t *curr = (vdr_vpts_offset_t *)calloc(1, sizeof (vdr_vpts_offset_t)); + curr->vpts = vpts; + curr->offset = vpts_offset; + + if (!this->vpts_offset_queue) + this->vpts_offset_queue = this->vpts_offset_queue_tail = curr; + else + { + this->vpts_offset_queue_tail->next = curr; + this->vpts_offset_queue_tail = curr; + } + } + } + else + vdr_vpts_offset_queue_purge(this); + +if(0) + { +fprintf(stderr, "B ---------------------------------------------\n"); + vdr_vpts_offset_t *p = this->vpts_offset_queue; + while (p) + { + fprintf(stderr, "B now: %12"PRId64", vpts: %12"PRId64", offset: %12"PRId64"\n", xine_get_current_vpts(this->stream), p->vpts, p->offset); + p = p->next; + } +fprintf(stderr, "B =============================================\n"); + } + + this->vpts_offset_queue_changes--; + pthread_cond_broadcast(&this->vpts_offset_queue_changed_cond); + + this->last_disc_type = type; + + pthread_mutex_unlock(&this->vpts_offset_queue_lock); + + if (!this->trick_speed_mode) + { + xine_event_t event; + + event.type = XINE_EVENT_VDR_DISCONTINUITY; + event.data = 0; + event.data_length = type; + + xine_event_send(this->stream, &event); + } +} + +static void vdr_metronom_handle_audio_discontinuity(metronom_t *self, int type, int64_t disc_off) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + int64_t vpts_offset = vdr_vpts_offset_queue_change_begin(this->input, type); +/* fprintf(stderr, "had A: %d, %"PRId64", %"PRId64", %"PRId64"\n", type, disc_off, xine_get_current_vpts(this->input->stream), this->stream_metronom->get_option(this->stream_metronom, METRONOM_VPTS_OFFSET)); */ + this->stream_metronom->handle_audio_discontinuity(this->stream_metronom, type, disc_off); +/* fprintf(stderr, "had B: %d, %"PRId64", %"PRId64", %"PRId64"\n", type, disc_off, xine_get_current_vpts(this->input->stream), this->stream_metronom->get_option(this->stream_metronom, METRONOM_VPTS_OFFSET)); */ + vdr_vpts_offset_queue_change_end(this->input, type, disc_off, vpts_offset); +} + +static void vdr_metronom_handle_video_discontinuity(metronom_t *self, int type, int64_t disc_off) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + int64_t vpts_offset = vdr_vpts_offset_queue_change_begin(this->input, type); +/* fprintf(stderr, "hvd A: %d, %"PRId64", %"PRId64", %"PRId64"\n", type, disc_off, xine_get_current_vpts(this->input->stream), this->stream_metronom->get_option(this->stream_metronom, METRONOM_VPTS_OFFSET)); */ + this->stream_metronom->handle_video_discontinuity(this->stream_metronom, type, disc_off); +/* fprintf(stderr, "hvd B: %d, %"PRId64", %"PRId64", %"PRId64"\n", type, disc_off, xine_get_current_vpts(this->input->stream), this->stream_metronom->get_option(this->stream_metronom, METRONOM_VPTS_OFFSET)); */ + vdr_vpts_offset_queue_change_end(this->input, type, disc_off, vpts_offset); +} + +static void vdr_metronom_got_video_frame(metronom_t *self, vo_frame_t *frame) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + + if (this->input->trick_speed_mode && frame->pts) + { + pthread_mutex_lock(&this->input->metronom_thread_call_lock); + + pthread_mutex_lock(&this->input->metronom_thread_lock); + this->input->metronom_thread_request = frame->pts; + this->input->metronom_thread_reply = 0; + pthread_cond_broadcast(&this->input->metronom_thread_request_cond); + pthread_mutex_unlock(&this->input->metronom_thread_lock); + + vdr_metronom_handle_video_discontinuity(self, DISC_ABSOLUTE, frame->pts); + + pthread_mutex_lock(&this->input->metronom_thread_lock); + if (!this->input->metronom_thread_reply) + pthread_cond_wait(&this->input->metronom_thread_reply_cond, &this->input->metronom_thread_lock); + pthread_mutex_unlock(&this->input->metronom_thread_lock); + + pthread_mutex_unlock(&this->input->metronom_thread_call_lock); + } + + this->stream_metronom->got_video_frame(this->stream_metronom, frame); + + if (this->input->trick_speed_mode && frame->pts) + { +/* fprintf(stderr, "vpts: %12ld, pts: %12ld, offset: %12ld\n", frame->vpts, frame->pts, this->stream_metronom->get_option(this->stream_metronom, METRONOM_VPTS_OFFSET)); */ + } +} + +static int64_t vdr_metronom_got_audio_samples(metronom_t *self, int64_t pts, int nsamples) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + return this->stream_metronom->got_audio_samples(this->stream_metronom, pts, nsamples); +} + +static int64_t vdr_metronom_got_spu_packet(metronom_t *self, int64_t pts) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + return this->stream_metronom->got_spu_packet(this->stream_metronom, pts); +} + +static void vdr_metronom_set_audio_rate(metronom_t *self, int64_t pts_per_smpls) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + this->stream_metronom->set_audio_rate(this->stream_metronom, pts_per_smpls); +} + +static void vdr_metronom_set_option(metronom_t *self, int option, int64_t value) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + this->stream_metronom->set_option(this->stream_metronom, option, value); +} + +static int64_t vdr_metronom_get_option(metronom_t *self, int option) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + return this->stream_metronom->get_option(this->stream_metronom, option); +} + +static void vdr_metronom_set_master(metronom_t *self, metronom_t *master) +{ + vdr_metronom_t *this = (vdr_metronom_t *)self; + this->stream_metronom->set_master(this->stream_metronom, master); +} + +static void vdr_metronom_exit(metronom_t *self) +{ + _x_abort(); +} + + +static input_plugin_t *vdr_class_get_instance(input_class_t *cls_gen, xine_stream_t *stream, + const char *data) +{ + vdr_input_plugin_t *this; + char *mrl = strdup(data); + + if (!strncasecmp(mrl, "vdr:/", 5)) + lprintf("filename '%s'\n", mrl_to_path (mrl)); + else if (!strncasecmp(mrl, "netvdr:/", 5)) + lprintf("host '%s'\n", mrl_to_socket (mrl)); + else + { + free(mrl); + return NULL; + } + + /* + * mrl accepted and opened successfully at this point + * + * => create plugin instance + */ + + this = (vdr_input_plugin_t *)xine_xmalloc(sizeof (vdr_input_plugin_t)); + + this->stream = stream; + this->curpos = 0; + this->mrl = mrl; + this->fh = -1; + this->fh_control = -1; + this->fh_result = -1; + this->fh_event = -1; + + this->input_plugin.open = vdr_plugin_open; + this->input_plugin.get_capabilities = vdr_plugin_get_capabilities; + this->input_plugin.read = vdr_plugin_read; + this->input_plugin.read_block = vdr_plugin_read_block; + this->input_plugin.seek = vdr_plugin_seek; + this->input_plugin.get_current_pos = vdr_plugin_get_current_pos; + this->input_plugin.get_length = vdr_plugin_get_length; + this->input_plugin.get_blocksize = vdr_plugin_get_blocksize; + this->input_plugin.get_mrl = vdr_plugin_get_mrl; + this->input_plugin.dispose = vdr_plugin_dispose; + this->input_plugin.get_optional_data = vdr_plugin_get_optional_data; + this->input_plugin.input_class = cls_gen; + + this->cur_func = func_unknown; + this->cur_size = 0; + this->cur_done = 0; + + memset(this->osd, 0, sizeof (this->osd)); + + { + xine_osd_t *osd = xine_osd_new(this->stream, 0, 0, 16, 16); + uint32_t caps = xine_osd_get_capabilities(osd); + xine_osd_free(osd); + +#ifdef XINE_OSD_CAP_ARGB_LAYER + this->osd_supports_argb_layer = !!(caps & XINE_OSD_CAP_ARGB_LAYER); +#endif +#ifdef XINE_OSD_CAP_CUSTOM_EXTENT + this->osd_supports_custom_extent = !!(caps & XINE_OSD_CAP_CUSTOM_EXTENT); +#endif + } + + this->osd_buffer = 0; + this->osd_buffer_size = 0; + this->osd_unscaled_blending = 0; + this->trick_speed_mode = 0; + this->audio_channels = 0; + this->mute_mode = XINE_VDR_MUTE_SIMULATE; + this->volume_mode = XINE_VDR_VOLUME_CHANGE_HW; + this->last_volume = -1; + this->frame_size.x = 0; + this->frame_size.y = 0; + this->frame_size.w = 0; + this->frame_size.h = 0; + this->frame_size.r = 0; + + this->stream_external = 0; + this->event_queue_external = 0; + + pthread_mutex_init(&this->rpc_thread_shutdown_lock, 0); + pthread_cond_init(&this->rpc_thread_shutdown_cond, 0); + + pthread_mutex_init(&this->metronom_thread_lock, 0); + pthread_cond_init(&this->metronom_thread_request_cond, 0); + pthread_cond_init(&this->metronom_thread_reply_cond, 0); + pthread_mutex_init(&this->metronom_thread_call_lock, 0); + + pthread_mutex_init(&this->find_sync_point_lock, 0); + pthread_mutex_init(&this->adjust_zoom_lock, 0); + this->image4_3_zoom_x = 0; + this->image4_3_zoom_y = 0; + this->image16_9_zoom_x = 0; + this->image16_9_zoom_y = 0; + + this->event_queue = xine_event_new_queue(this->stream); + if (this->event_queue) + xine_event_create_listener_thread(this->event_queue, event_handler, this); + + this->metronom.input = this; + this->metronom.metronom.set_audio_rate = vdr_metronom_set_audio_rate; + this->metronom.metronom.got_video_frame = vdr_metronom_got_video_frame; + this->metronom.metronom.got_audio_samples = vdr_metronom_got_audio_samples; + this->metronom.metronom.got_spu_packet = vdr_metronom_got_spu_packet; + this->metronom.metronom.handle_audio_discontinuity = vdr_metronom_handle_audio_discontinuity; + this->metronom.metronom.handle_video_discontinuity = vdr_metronom_handle_video_discontinuity; + this->metronom.metronom.set_option = vdr_metronom_set_option; + this->metronom.metronom.get_option = vdr_metronom_get_option; + this->metronom.metronom.set_master = vdr_metronom_set_master; + this->metronom.metronom.exit = vdr_metronom_exit; + + this->metronom.stream_metronom = stream->metronom; + stream->metronom = &this->metronom.metronom; + + pthread_mutex_init(&this->vpts_offset_queue_lock, 0); + pthread_cond_init(&this->vpts_offset_queue_changed_cond, 0); + + return &this->input_plugin; +} + +/* + * vdr input plugin class stuff + */ + +static const char *vdr_class_get_description(input_class_t *this_gen) +{ + return _("VDR display device plugin"); +} + +static const char *vdr_class_get_identifier (input_class_t *this_gen) +{ + return "VDR"; +} + +static void vdr_class_dispose (input_class_t *this_gen) +{ + vdr_input_class_t *this = (vdr_input_class_t *)this_gen; + + free(this); +} + +static char **vdr_class_get_autoplay_list(input_class_t *this_gen, + int *num_files) +{ + vdr_input_class_t *class = (vdr_input_class_t *)this_gen; + + *num_files = 1; + return (char **)class->mrls; +} + +void *vdr_input_init_plugin(xine_t *xine, void *data) +{ + vdr_input_class_t *this; + + lprintf("init_class\n"); + + this = (vdr_input_class_t *)xine_xmalloc(sizeof (vdr_input_class_t)); + + this->xine = xine; + + this->mrls[ 0 ] = "vdr:/" VDR_ABS_FIFO_DIR "/stream#demux:mpeg_pes"; + this->mrls[ 1 ] = 0; + + this->input_class.get_instance = vdr_class_get_instance; + this->input_class.get_identifier = vdr_class_get_identifier; + this->input_class.get_description = vdr_class_get_description; + this->input_class.get_dir = NULL; + this->input_class.get_autoplay_list = vdr_class_get_autoplay_list; + this->input_class.dispose = vdr_class_dispose; + this->input_class.eject_media = NULL; + + return this; +} diff --git a/src/vdr/post_vdr_audio.c b/src/vdr/post_vdr_audio.c new file mode 100644 --- /dev/null +++ b/src/vdr/post_vdr_audio.c @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + */ + +/* + * select audio channel plugin for VDR + */ + +#define LOG_MODULE "vdr_audio" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include "xine_internal.h" +#include "post.h" +#include "combined_vdr.h" + + + +typedef struct vdr_audio_post_plugin_s +{ + post_plugin_t post_plugin; + + xine_event_queue_t *event_queue; + xine_stream_t *vdr_stream; + + uint8_t audio_channels; + int num_channels; + +} +vdr_audio_post_plugin_t; + + +static void vdr_audio_select_audio(vdr_audio_post_plugin_t *this, uint8_t channels) +{ + this->audio_channels = channels; +} + + +/* plugin class functions */ +static post_plugin_t *vdr_audio_open_plugin(post_class_t *class_gen, int inputs, + xine_audio_port_t **audio_target, + xine_video_port_t **video_target); +static char *vdr_audio_get_identifier(post_class_t *class_gen); +static char *vdr_audio_get_description(post_class_t *class_gen); +static void vdr_audio_class_dispose(post_class_t *class_gen); + +/* plugin instance functions */ +static void vdr_audio_dispose(post_plugin_t *this_gen); + +/* replaced ao_port functions */ +static int vdr_audio_port_open(xine_audio_port_t *port_gen, xine_stream_t *stream, + uint32_t bits, uint32_t rate, int mode); +static void vdr_audio_port_put_buffer(xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_stream_t *stream); + + + +void *vdr_audio_init_plugin(xine_t *xine, void *data) +{ + post_class_t *class = (post_class_t *)xine_xmalloc(sizeof (post_class_t)); + + if (!class) + return NULL; + + class->open_plugin = vdr_audio_open_plugin; + class->get_identifier = vdr_audio_get_identifier; + class->get_description = vdr_audio_get_description; + class->dispose = vdr_audio_class_dispose; + + return class; +} + +static post_plugin_t *vdr_audio_open_plugin(post_class_t *class_gen, int inputs, + xine_audio_port_t **audio_target, + xine_video_port_t **video_target) +{ + vdr_audio_post_plugin_t *this = (vdr_audio_post_plugin_t *)xine_xmalloc(sizeof (vdr_audio_post_plugin_t)); + post_in_t *input; + post_out_t *output; + post_audio_port_t *port; +/* +fprintf(stderr, "~~~~~~~~~~ vdr open plugin\n"); +*/ + if (!this || !audio_target || !audio_target[ 0 ]) + { + free(this); + return NULL; + } + + _x_post_init(&this->post_plugin, 1, 0); + this->post_plugin.dispose = vdr_audio_dispose; + + port = _x_post_intercept_audio_port(&this->post_plugin, audio_target[ 0 ], &input, &output); + port->new_port.open = vdr_audio_port_open; + port->new_port.put_buffer = vdr_audio_port_put_buffer; + + this->post_plugin.xine_post.audio_input[ 0 ] = &port->new_port; + + + + this->audio_channels = 0; + + return &this->post_plugin; +} + +static char *vdr_audio_get_identifier(post_class_t *class_gen) +{ + return "vdr_audio"; +} + +static char *vdr_audio_get_description(post_class_t *class_gen) +{ + return "modifies every audio frame as requested by VDR"; +} + +static void vdr_audio_class_dispose(post_class_t *class_gen) +{ + free(class_gen); +} + + +static void vdr_audio_dispose(post_plugin_t *this_gen) +{ +/* +fprintf(stderr, "~~~~~~~~~~ vdr dispose\n"); +*/ + if (_x_post_dispose(this_gen)) + { + vdr_audio_post_plugin_t *this = (vdr_audio_post_plugin_t *)this_gen; + + if (this->vdr_stream) + xine_event_dispose_queue(this->event_queue); + + free(this_gen); + } +} + +static int vdr_audio_port_open(xine_audio_port_t *port_gen, xine_stream_t *stream, + uint32_t bits, uint32_t rate, int mode) { + + post_audio_port_t *port = (post_audio_port_t *)port_gen; + vdr_audio_post_plugin_t *this = (vdr_audio_post_plugin_t *)port->post; + + _x_post_rewire(&this->post_plugin); + _x_post_inc_usage(port); +/* +fprintf(stderr, "~~~~~~~~~~ vdr port open\n"); +*/ + port->stream = stream; + port->bits = bits; + port->rate = rate; + port->mode = mode; + + this->num_channels = _x_ao_mode2channels(mode); + + return (port->original_port->open) (port->original_port, stream, bits, rate, mode ); +} + + +static void vdr_audio_port_put_buffer(xine_audio_port_t *port_gen, audio_buffer_t *buf, xine_stream_t *stream) +{ + post_audio_port_t *port = (post_audio_port_t *)port_gen; + vdr_audio_post_plugin_t *this = (vdr_audio_post_plugin_t *)port->post; + xine_event_t *event; +/* +fprintf(stderr, "~~~~~~ vdr_audio\n"); +*/ + if (this->vdr_stream + && !_x_continue_stream_processing(this->vdr_stream)) + { + this->vdr_stream = 0; + + xine_event_dispose_queue(this->event_queue); + this->event_queue = 0; + + this->audio_channels = 0; + } + + if (!this->vdr_stream + && vdr_is_vdr_stream(stream)) + { + this->event_queue = xine_event_new_queue(stream); + if (this->event_queue) + { + this->vdr_stream = stream; + + { + xine_event_t event; + + event.type = XINE_EVENT_VDR_PLUGINSTARTED; + event.data = 0; + event.data_length = 1; /* vdr_audio */ + + xine_event_send(this->vdr_stream, &event); + } + } + } + + if (this->event_queue) + { + while ((event = xine_event_get(this->event_queue))) + { + if (event->type == XINE_EVENT_VDR_SELECTAUDIO) + { + vdr_select_audio_data_t *data = (vdr_select_audio_data_t *)event->data; + + vdr_audio_select_audio(this, data->channels); + } + + xine_event_free(event); + } + } + + if (this->num_channels == 2 + && this->audio_channels != 0 + && this->audio_channels != 3) + { + audio_buffer_t *vdr_buf = port->original_port->get_buffer(port->original_port); + vdr_buf->num_frames = buf->num_frames; + vdr_buf->vpts = buf->vpts; + vdr_buf->frame_header_count = buf->frame_header_count; + vdr_buf->first_access_unit = buf->first_access_unit; + /* FIXME: The audio buffer should contain this info. + * We should not have to get it from the open call. + */ + vdr_buf->format.bits = buf->format.bits; + vdr_buf->format.rate = buf->format.rate; + vdr_buf->format.mode = buf->format.mode; + _x_extra_info_merge(vdr_buf->extra_info, buf->extra_info); + + { + int step = buf->format.bits / 8; + uint8_t *src = (uint8_t *)buf->mem; + uint8_t *dst = (uint8_t *)vdr_buf->mem; + + if (this->audio_channels == 2) + src += step; +/* + fprintf(stderr, "~~~~~~~~~~ vdr port put buffer: channels: %d, %d\n" + , this->audio_channels + , buf->format.bits); +*/ + int i, k; + for (i = 0; i < buf->num_frames; i++) + { + for (k = 0; k < step; k++) + *dst++ = *src++; + + src -= step; + + for (k = 0; k < step; k++) + *dst++ = *src++; + + src += step; + } + } + + /* pass data to original port */ + port->original_port->put_buffer(port->original_port, vdr_buf, stream); + + /* free data from origial buffer */ + buf->num_frames = 0; /* UNDOCUMENTED, but hey, it works! Force old audio_out buffer free. */ + } + + port->original_port->put_buffer(port->original_port, buf, stream); + + return; +} diff --git a/src/vdr/post_vdr_video.c b/src/vdr/post_vdr_video.c new file mode 100644 --- /dev/null +++ b/src/vdr/post_vdr_video.c @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + */ + +/* + * frame scaler plugin for VDR + */ + +#define LOG_MODULE "vdr_video" +/* +#define LOG +#define LOG_VERBOSE +*/ + +#include "xine_internal.h" +#include "post.h" +#include "combined_vdr.h" + + + +typedef struct vdr_video_post_plugin_s +{ + post_plugin_t post_plugin; + + xine_event_queue_t *event_queue; + xine_stream_t *vdr_stream; + + int8_t trick_speed_mode; + int8_t enabled; + + int32_t x; + int32_t y; + int32_t w; + int32_t h; + int32_t w_ref; + int32_t h_ref; + + int32_t old_frame_left; + int32_t old_frame_top; + int32_t old_frame_width; + int32_t old_frame_height; + double old_frame_ratio; + +} +vdr_video_post_plugin_t; + + +static void vdr_video_set_video_window(vdr_video_post_plugin_t *this, int32_t x, int32_t y, int32_t w, int32_t h, int32_t w_ref, int32_t h_ref) +{ + this->enabled = 0; + + this->x = x; + this->y = y; + this->w = w; + this->h = h; + this->w_ref = w_ref; + this->h_ref = h_ref; + + if (w != w_ref || h != h_ref) + this->enabled = 1; +} + + +/* plugin class functions */ +static post_plugin_t *vdr_video_open_plugin(post_class_t *class_gen, int inputs, + xine_audio_port_t **audio_target, + xine_video_port_t **video_target); +static char *vdr_video_get_identifier(post_class_t *class_gen); +static char *vdr_video_get_description(post_class_t *class_gen); +static void vdr_video_class_dispose(post_class_t *class_gen); + +/* plugin instance functions */ +static void vdr_video_dispose(post_plugin_t *this_gen); + +/* route preprocessing functions check */ +static int vdr_video_route_preprocessing_procs(post_video_port_t *port, vo_frame_t *frame); + +/* replaced vo_frame functions */ +static int vdr_video_draw(vo_frame_t *frame, xine_stream_t *stream); + + +void *vdr_video_init_plugin(xine_t *xine, void *data) +{ + post_class_t *class = (post_class_t *)xine_xmalloc(sizeof (post_class_t)); + + if (!class) + return NULL; + + class->open_plugin = vdr_video_open_plugin; + class->get_identifier = vdr_video_get_identifier; + class->get_description = vdr_video_get_description; + class->dispose = vdr_video_class_dispose; + + return class; +} + +static post_plugin_t *vdr_video_open_plugin(post_class_t *class_gen, int inputs, + xine_audio_port_t **audio_target, + xine_video_port_t **video_target) +{ + vdr_video_post_plugin_t *this = (vdr_video_post_plugin_t *)xine_xmalloc(sizeof (vdr_video_post_plugin_t)); + post_in_t *input; + post_out_t *output; + post_video_port_t *port; + + if (!this || !video_target || !video_target[ 0 ]) + { + free(this); + return NULL; + } + + _x_post_init(&this->post_plugin, 0, 1); + this->post_plugin.dispose = vdr_video_dispose; + + port = _x_post_intercept_video_port(&this->post_plugin, video_target[ 0 ], &input, &output); + port->route_preprocessing_procs = vdr_video_route_preprocessing_procs; + port->new_frame->draw = vdr_video_draw; + this->post_plugin.xine_post.video_input[ 0 ] = &port->new_port; + + this->enabled = 0; + this->vdr_stream = 0; + this->event_queue = 0; + this->old_frame_left = 0; + this->old_frame_top = 0; + this->old_frame_width = 0; + this->old_frame_height = 0; + this->old_frame_ratio = 0; + this->trick_speed_mode = 0; + + return &this->post_plugin; +} + +static char *vdr_video_get_identifier(post_class_t *class_gen) +{ + return "vdr"; +} + +static char *vdr_video_get_description(post_class_t *class_gen) +{ + return "modifies every video frame as requested by VDR"; +} + +static void vdr_video_class_dispose(post_class_t *class_gen) +{ + free(class_gen); +} + + +static void vdr_video_dispose(post_plugin_t *this_gen) +{ + if (_x_post_dispose(this_gen)) + { + vdr_video_post_plugin_t *this = (vdr_video_post_plugin_t *)this_gen; + + if (this->vdr_stream) + { + xine_event_t event; + vdr_frame_size_changed_data_t event_data; + + event_data.x = 0; + event_data.y = 0; + event_data.w = 0; + event_data.h = 0; + + event.type = XINE_EVENT_VDR_FRAMESIZECHANGED; + event.data = &event_data; + event.data_length = sizeof (event_data); + + xine_event_send(this->vdr_stream, &event); + + xine_event_dispose_queue(this->event_queue); + } + + free(this_gen); + } +} + +static int vdr_video_route_preprocessing_procs(post_video_port_t *port, vo_frame_t *frame) +{ + vdr_video_post_plugin_t *this = (vdr_video_post_plugin_t *)port->post; + return !this->enabled + || (frame->format != XINE_IMGFMT_YUY2 + && frame->format != XINE_IMGFMT_YV12); +} + + +static inline void vdr_video_scale(uint8_t *src, uint8_t *dst, int y_inc, int x_inc, int w_dst, int h_dst, int x, int y, int w, int h, int w_ref, int h_ref, int init) +{ + int x0 = x * w_dst / w_ref; + int y0 = y * h_dst / h_ref; + + int x1 = ((x + w) * w_dst - 1 + w_ref) / w_ref; + int y1 = ((y + h) * h_dst - 1 + h_ref) / h_ref; + + int dx = x1 - x0; + int dy = y1 - y0; + + int yy, xx; + + int dy2 = dy + dy; + int h_dst2 = h_dst + h_dst; + int y_eps = h_dst - dy2; + + int dx2 = dx + dx; + int w_dst2 = w_dst + w_dst; + int x_eps0 = w_dst - dx2; + + for (yy = 0; yy < y0; yy++) + { + uint8_t *dst0 = dst; + + for (xx = 0; xx < w_dst; xx++) + { + *dst0 = init; + dst0 += x_inc; + } + + dst += y_inc; + } + + for (yy = y0; yy < y1; yy++) + { + uint8_t *dst0 = dst; + uint8_t *src0 = src; + + int x_eps = x_eps0; + + for (xx = 0; xx < x0; xx++) + { + *dst0 = init; + dst0 += x_inc; + } + + for (xx = x0; xx < x1; xx++) + { + *dst0 = *src0; + dst0 += x_inc; + + x_eps += w_dst2; + while (x_eps >= 0) + { + src0 += x_inc; + x_eps -= dx2; + } + } + + for (xx = x1; xx < w_dst; xx++) + { + *dst0 = init; + dst0 += x_inc; + } + + dst += y_inc; + + y_eps += h_dst2; + while (y_eps >= 0) + { + src += y_inc; + y_eps -= dy2; + } + } + + for (yy = y1; yy < h_dst; yy++) + { + uint8_t *dst0 = dst; + + for (xx = 0; xx < w_dst; xx++) + { + *dst0 = init; + dst0 += x_inc; + } + + dst += y_inc; + } +} + +static void vdr_video_scale_YUY2(vdr_video_post_plugin_t *this, vo_frame_t *src, vo_frame_t *dst) +{ + int w = dst->width - dst->crop_left - dst->crop_right; + int h = dst->height - dst->crop_top - dst->crop_bottom; + int offset; + + if (w < 0) + w = 0; + + if (h < 0) + h = 0; + + offset = dst->pitches[ 0 ] * dst->crop_top + 2 * dst->crop_left; + vdr_video_scale(&src->base[ 0 ][ 0 ] + offset, &dst->base[ 0 ][ 0 ] + offset, dst->pitches[ 0 ], 2, w , h, this->x, this->y, this->w, this->h, this->w_ref, this->h_ref, 0x00); + offset = dst->pitches[ 0 ] * dst->crop_top + 4 * ((dst->crop_left + 1) / 2); + vdr_video_scale(&src->base[ 0 ][ 1 ] + offset, &dst->base[ 0 ][ 1 ] + offset, dst->pitches[ 0 ], 4, (w + 1) / 2, h, this->x, this->y, this->w, this->h, this->w_ref, this->h_ref, 0x80); + offset = dst->pitches[ 0 ] * dst->crop_top + 4 * ((dst->crop_left + 1) / 2); + vdr_video_scale(&src->base[ 0 ][ 3 ] + offset, &dst->base[ 0 ][ 3 ] + offset, dst->pitches[ 0 ], 4, (w + 1) / 2, h, this->x, this->y, this->w, this->h, this->w_ref, this->h_ref, 0x80); +} + +static void vdr_video_scale_YV12(vdr_video_post_plugin_t *this, vo_frame_t *src, vo_frame_t *dst) +{ + int w = dst->width - dst->crop_left - dst->crop_right; + int h = dst->height - dst->crop_top - dst->crop_bottom; + int offset; + + if (w < 0) + w = 0; + + if (h < 0) + h = 0; + + offset = dst->pitches[ 0 ] * dst->crop_top + 1 * dst->crop_left; + vdr_video_scale(&src->base[ 0 ][ 0 ] + offset, &dst->base[ 0 ][ 0 ] + offset, dst->pitches[ 0 ], 1, w , h , this->x, this->y, this->w, this->h, this->w_ref, this->h_ref, 0x00); + offset = dst->pitches[ 1 ] * ((dst->crop_top + 1) / 2) + 1 * ((dst->crop_left + 1) / 2); + vdr_video_scale(&src->base[ 1 ][ 0 ] + offset, &dst->base[ 1 ][ 0 ] + offset, dst->pitches[ 1 ], 1, (w + 1) / 2, (h + 1) / 2, this->x, this->y, this->w, this->h, this->w_ref, this->h_ref, 0x80); + offset = dst->pitches[ 2 ] * ((dst->crop_top + 1) / 2) + 1 * ((dst->crop_left + 1) / 2); + vdr_video_scale(&src->base[ 2 ][ 0 ] + offset, &dst->base[ 2 ][ 0 ] + offset, dst->pitches[ 2 ], 1, (w + 1) / 2, (h + 1) / 2, this->x, this->y, this->w, this->h, this->w_ref, this->h_ref, 0x80); +} + + +static int vdr_video_draw(vo_frame_t *frame, xine_stream_t *stream) +{ + post_video_port_t *port = (post_video_port_t *)frame->port; + vdr_video_post_plugin_t *this = (vdr_video_post_plugin_t *)port->post; + vo_frame_t *vdr_frame; + xine_event_t *event; + int skip; + + if (this->vdr_stream + && !_x_continue_stream_processing(this->vdr_stream)) + { + this->vdr_stream = 0; + + xine_event_dispose_queue(this->event_queue); + this->event_queue = 0; + + this->old_frame_left = 0; + this->old_frame_top = 0; + this->old_frame_width = 0; + this->old_frame_height = 0; + this->old_frame_ratio = 0; + } + + if (!this->vdr_stream + && vdr_is_vdr_stream(stream)) + { + this->event_queue = xine_event_new_queue(stream); + if (this->event_queue) + { + this->vdr_stream = stream; + + { + xine_event_t event; + + event.type = XINE_EVENT_VDR_PLUGINSTARTED; + event.data = 0; + event.data_length = 0; /* vdr_video */ + + xine_event_send(this->vdr_stream, &event); + } + } + } + + if (this->event_queue) + { + while ((event = xine_event_get(this->event_queue))) + { + if (event->type == XINE_EVENT_VDR_SETVIDEOWINDOW) + { + vdr_set_video_window_data_t *data = (vdr_set_video_window_data_t *)event->data; + + vdr_video_set_video_window(this, data->x, data->y, data->w, data->h, data->w_ref, data->h_ref); + } + else if (event->type == XINE_EVENT_VDR_TRICKSPEEDMODE) + { +/* + fprintf(stderr, "###############################: %p, %d\n", event->data, event->data_length); + this->trick_speed_mode = (0 != event->data_length); +*/ + } + + xine_event_free(event); + } + } + + { + int32_t frame_left = frame->crop_left; + int32_t frame_width = frame->width - frame->crop_left - frame->crop_right; + int32_t frame_top = frame->crop_top; + int32_t frame_height = frame->height - frame->crop_top - frame->crop_bottom; + double frame_ratio = frame->ratio; + + if (frame_left < 0) + frame_left = 0; + if (frame_width > frame->width) + frame_width = frame->width; + if (frame_top < 0) + frame_top = 0; + if (frame_height > frame->height) + frame_height = frame->height; + + if (this->vdr_stream + && frame_width != 0 + && frame_height != 0 + && (this->old_frame_left != frame_left + || this->old_frame_top != frame_top + || this->old_frame_width != frame_width + || this->old_frame_height != frame_height + || this->old_frame_ratio != frame_ratio)) + { + xine_event_t event; + vdr_frame_size_changed_data_t event_data; + + event_data.x = frame_left; + event_data.y = frame_top; + event_data.w = frame_width; + event_data.h = frame_height; + event_data.r = frame_ratio; + + xprintf(this->vdr_stream->xine, XINE_VERBOSITY_LOG, + _(LOG_MODULE ": osd: (%d, %d)-(%d, %d)@%lg\n"), frame_left, frame_top, frame_width, frame_height, frame_ratio); + + event.type = XINE_EVENT_VDR_FRAMESIZECHANGED; + event.data = &event_data; + event.data_length = sizeof (event_data); + + xine_event_send(this->vdr_stream, &event); + + this->old_frame_left = frame_left; + this->old_frame_top = frame_top; + this->old_frame_width = frame_width; + this->old_frame_height = frame_height; + this->old_frame_ratio = frame_ratio; + } + } +/* + fprintf(stderr, "~~~~~~~~~~~~ trickspeedmode: %d\n", this->trick_speed_mode); + + if (this->vdr_stream + && this->trick_speed_mode) + { + frame->pts = 0; + frame->next->pts = 0; + } +*/ +#if defined(LOG) && defined(LOG_VERBOSE) + { + int a = 0, b = 0, c = 0, d = 0; + if (stream) + _x_query_buffer_usage(stream, &a, &b, &c, &d); + lprintf("buffer usage: %3d, %2d, %2d, %2d, %p\n", a, b, c, d, stream); + } +#endif + + if (!this->enabled + || frame->bad_frame + || (frame->format != XINE_IMGFMT_YUY2 + && frame->format != XINE_IMGFMT_YV12) + || frame->proc_frame + || frame->proc_slice) + { + _x_post_frame_copy_down(frame, frame->next); + skip = frame->next->draw(frame->next, stream); + _x_post_frame_copy_up(frame, frame->next); + return skip; + } + + vdr_frame = port->original_port->get_frame(port->original_port, + frame->width, frame->height, frame->ratio, frame->format, frame->flags | VO_BOTH_FIELDS); + + _x_post_frame_copy_down(frame, vdr_frame); + + switch (vdr_frame->format) + { + case XINE_IMGFMT_YUY2: + vdr_video_scale_YUY2(this, frame, vdr_frame); + break; + + case XINE_IMGFMT_YV12: + vdr_video_scale_YV12(this, frame, vdr_frame); + break; + } + + skip = vdr_frame->draw(vdr_frame, stream); + _x_post_frame_copy_up(frame, vdr_frame); + vdr_frame->free(vdr_frame); + + return skip; +} diff --git a/src/vdr/vdr.h b/src/vdr/vdr.h new file mode 100644 --- /dev/null +++ b/src/vdr/vdr.h @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2000-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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, USA + */ + +#ifndef __VDR_H +#define __VDR_H + + +#define XINE_VDR_VERSION 901 + + +enum funcs +{ + func_unknown = -1 + , func_nop + , func_osd_new + , func_osd_free + , func_osd_show + , func_osd_hide + , func_osd_set_position + , func_osd_draw_bitmap + , func_set_color + , func_clear + , func_mute + , func_set_volume + , func_set_speed + , func_set_prebuffer + , func_metronom + , func_start + , func_wait + , func_setup + , func_grab_image + , func_get_pts + , func_flush + , func_first_frame + , func_still_frame + , func_video_size + , func_set_video_window + , func_osd_flush + , func_play_external + , func_key + , func_frame_size + , func_reset_audio + , func_select_audio + , func_trick_speed_mode + , func_get_version + , func_discontinuity + , func_query_capabilities +}; + +enum keys +{ + key_none, + key_up, + key_down, + key_menu, + key_ok, + key_back, + key_left, + key_right, + key_red, + key_green, + key_yellow, + key_blue, + key_0, + key_1, + key_2, + key_3, + key_4, + key_5, + key_6, + key_7, + key_8, + key_9, + key_play, + key_pause, + key_stop, + key_record, + key_fast_fwd, + key_fast_rew, + key_power, + key_channel_plus, + key_channel_minus, + key_volume_plus, + key_volume_minus, + key_mute, + key_schedule, + key_channels, + key_timers, + key_recordings, + key_setup, + key_commands, + key_user1, + key_user2, + key_user3, + key_user4, + key_user5, + key_user6, + key_user7, + key_user8, + key_user9, + key_audio, + key_info, + key_channel_previous, + key_next, + key_previous, + key_subtitles, +}; + + + +typedef struct __attribute__((packed)) data_header_s +{ + uint32_t func:8; + uint32_t len:24; +} +data_header_t; + + + +typedef data_header_t result_header_t; +typedef data_header_t event_header_t; + + + +typedef struct __attribute__((packed)) data_nop_s +{ + data_header_t header; +} +data_nop_t; + + + +typedef struct __attribute__((packed)) data_osd_new_s +{ + data_header_t header; + + uint8_t window; + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; + uint16_t w_ref; + uint16_t h_ref; +} +data_osd_new_t; + + + +typedef struct __attribute__((packed)) data_osd_free_s +{ + data_header_t header; + + uint8_t window; +} +data_osd_free_t; + + + +typedef struct __attribute__((packed)) data_osd_show_s +{ + data_header_t header; + + uint8_t window; +} +data_osd_show_t; + + + +typedef struct __attribute__((packed)) data_osd_hide_s +{ + data_header_t header; + + uint8_t window; +} +data_osd_hide_t; + + + +typedef struct __attribute__((packed)) data_osd_flush_s +{ + data_header_t header; +} +data_osd_flush_t; + + + +typedef struct __attribute__((packed)) data_play_external_s +{ + data_header_t header; +} +data_play_external_t; + + + +typedef struct __attribute__((packed)) data_osd_set_position_s +{ + data_header_t header; + + uint8_t window; + int16_t x; + int16_t y; +} +data_osd_set_position_t; + + + +typedef struct __attribute__((packed)) data_osd_draw_bitmap_s +{ + data_header_t header; + + uint8_t window; + int16_t x; + int16_t y; + uint16_t width; + uint16_t height; + uint8_t argb; +} +data_osd_draw_bitmap_t; + + + +typedef struct __attribute__((packed)) data_set_color_s +{ + data_header_t header; + + uint8_t window; + uint8_t index; + uint8_t num; +} +data_set_color_t; + + + +typedef struct __attribute__((packed)) data_flush_s +{ + data_header_t header; + + int32_t ms_timeout; + uint8_t just_wait; +} +data_flush_t; + + + +typedef struct __attribute__((packed)) result_flush_s +{ + result_header_t header; + + uint8_t timed_out; +} +result_flush_t; + + + +typedef struct __attribute__((packed)) data_clear_s +{ + data_header_t header; + + int32_t n; + int8_t s; + uint8_t i; +} +data_clear_t; + + + +typedef struct __attribute__((packed)) data_mute_s +{ + data_header_t header; + + uint8_t mute; +} +data_mute_t; + + + +typedef struct __attribute__((packed)) data_set_volume_s +{ + data_header_t header; + + uint8_t volume; +} +data_set_volume_t; + + + +typedef struct __attribute__((packed)) data_set_speed_s +{ + data_header_t header; + + int32_t speed; +} +data_set_speed_t; + + + +typedef struct __attribute__((packed)) data_set_prebuffer_s +{ + data_header_t header; + + uint32_t prebuffer; +} +data_set_prebuffer_t; + + + +typedef struct __attribute__((packed)) data_metronom_s +{ + data_header_t header; + + int64_t pts; + uint32_t flags; +} +data_metronom_t; + + + +typedef struct __attribute__((packed)) data_start_s +{ + data_header_t header; +} +data_start_t; + + + +typedef struct __attribute__((packed)) data_wait_s +{ + data_header_t header; + uint8_t id; +} +data_wait_t; + + + +typedef struct __attribute__((packed)) result_wait_s +{ + result_header_t header; +} +result_wait_t; + + + +#define XINE_VDR_VOLUME_IGNORE 0 +#define XINE_VDR_VOLUME_CHANGE_HW 1 +#define XINE_VDR_VOLUME_CHANGE_SW 2 + +#define XINE_VDR_MUTE_IGNORE 0 +#define XINE_VDR_MUTE_EXECUTE 1 +#define XINE_VDR_MUTE_SIMULATE 2 + +typedef struct __attribute__((packed)) data_setup_s +{ + data_header_t header; + + uint8_t osd_unscaled_blending; + uint8_t volume_mode; + uint8_t mute_mode; + uint16_t image4_3_zoom_x; + uint16_t image4_3_zoom_y; + uint16_t image16_9_zoom_x; + uint16_t image16_9_zoom_y; +} +data_setup_t; + + + +typedef struct __attribute__((packed)) data_first_frame_s +{ + data_header_t header; +} +data_first_frame_t; + + + +typedef struct __attribute__((packed)) data_still_frame_s +{ + data_header_t header; +} +data_still_frame_t; + + + +typedef struct __attribute__((packed)) data_set_video_window_s +{ + data_header_t header; + + uint32_t x; + uint32_t y; + uint32_t w; + uint32_t h; + uint32_t w_ref; + uint32_t h_ref; +} +data_set_video_window_t; + + + +typedef struct __attribute__((packed)) data_grab_image_s +{ + data_header_t header; +} +data_grab_image_t; + + + +typedef struct __attribute__((packed)) result_grab_image_s +{ + result_header_t header; + + int32_t width; + int32_t height; + int32_t ratio; + int32_t format; + int32_t interlaced; + int32_t crop_left; + int32_t crop_right; + int32_t crop_top; + int32_t crop_bottom; +} +result_grab_image_t; + + + +typedef struct __attribute__((packed)) data_get_pts_s +{ + data_header_t header; + int32_t ms_timeout; +} +data_get_pts_t; + + + +typedef struct __attribute__((packed)) result_get_pts_s +{ + result_header_t header; + + int64_t pts; + int8_t queued; +} +result_get_pts_t; + + + +typedef struct __attribute__((packed)) data_get_version_s +{ + data_header_t header; +} +data_get_version_t; + + + +typedef struct __attribute__((packed)) result_get_version_s +{ + result_header_t header; + + int32_t version; +} +result_get_version_t; + + + +typedef struct __attribute__((packed)) data_video_size_s +{ + data_header_t header; +} +data_video_size_t; + + + +typedef struct __attribute__((packed)) result_video_size_s +{ + result_header_t header; + + int32_t left; + int32_t top; + int32_t width; + int32_t height; + int32_t ratio; + int32_t zoom_x; + int32_t zoom_y; +} +result_video_size_t; + + + +typedef struct __attribute__((packed)) data_reset_audio_s +{ + data_header_t header; +} +data_reset_audio_t; + + + +typedef struct __attribute__((packed)) event_key_s +{ + event_header_t header; + + uint32_t key; +} +event_key_t; + + + +typedef struct __attribute__((packed)) event_frame_size_s +{ + event_header_t header; + + int32_t left; + int32_t top; + int32_t width; + int32_t height; + int32_t zoom_x; + int32_t zoom_y; +} +event_frame_size_t; + + + +typedef struct __attribute__((packed)) event_play_external_s +{ + event_header_t header; + + uint32_t key; +} +event_play_external_t; + + + +typedef struct __attribute__((packed)) data_select_audio_s +{ + data_header_t header; + + uint8_t channels; +} +data_select_audio_t; + + + +typedef struct __attribute__((packed)) data_trick_speed_mode_s +{ + data_header_t header; + + uint8_t on; +} +data_trick_speed_mode_t; + + + +typedef struct __attribute__((packed)) event_discontinuity_s +{ + event_header_t header; + + int32_t type; +} +event_discontinuity_t; + + + +typedef struct __attribute__((packed)) data_query_capabilities_s +{ + data_header_t header; +} +data_query_capabilities_t; + + + +typedef struct __attribute__((packed)) result_query_capabilities_s +{ + result_header_t header; + + uint8_t osd_max_num_windows; + uint8_t osd_palette_max_depth; + uint8_t osd_palette_is_shared; + uint8_t osd_supports_argb_layer; + uint8_t osd_supports_custom_extent; +} +result_query_capabilities_t; + + + +typedef union __attribute__((packed)) data_union_u +{ + data_header_t header; + data_nop_t nop; + data_osd_new_t osd_new; + data_osd_free_t osd_free; + data_osd_show_t osd_show; + data_osd_hide_t osd_hide; + data_osd_set_position_t osd_set_position; + data_osd_draw_bitmap_t osd_draw_bitmap; + data_set_color_t set_color; + data_flush_t flush; + data_clear_t clear; + data_mute_t mute; + data_set_volume_t set_volume; + data_set_speed_t set_speed; + data_set_prebuffer_t set_prebuffer; + data_metronom_t metronom; + data_start_t start; + data_wait_t wait; + data_setup_t setup; + data_grab_image_t grab_image; + data_get_pts_t get_pts; + data_first_frame_t first_frame; + data_still_frame_t still_frame; + data_video_size_t video_size; + data_set_video_window_t set_video_window; + data_osd_flush_t osd_flush; + data_play_external_t play_external; + data_reset_audio_t reset_audio; + data_select_audio_t select_audio; + data_trick_speed_mode_t trick_speed_mode; + data_get_version_t get_version; + data_query_capabilities_t query_capabilities; +} +data_union_t; + + + +typedef union __attribute__((packed)) result_union_u +{ + result_header_t header; + result_grab_image_t grab_image; + result_get_pts_t get_pts; + result_flush_t flush; + result_video_size_t video_size; + result_get_version_t get_version; + result_wait_t wait; + result_query_capabilities_t query_capabilities; +} +result_union_t; + + + +typedef union __attribute__((packed)) event_union_u +{ + event_header_t header; + event_key_t key; + event_frame_size_t frame_size; + event_play_external_t play_external; + event_discontinuity_t discontinuity; +} +event_union_t; + + + +#endif /* __VDR_H */ + diff --git a/src/video_out/video_out_xvmc.c b/src/video_out/video_out_xvmc.c --- a/src/video_out/video_out_xvmc.c +++ b/src/video_out/video_out_xvmc.c @@ -486,9 +486,9 @@ static void xvmc_render_macro_blocks(vo_ int second_field, xvmc_macroblocks_t *macroblocks) { xvmc_driver_t *this = (xvmc_driver_t *) current_image->driver; - xvmc_frame_t *current_frame = (xvmc_frame_t *) current_image; - xvmc_frame_t *forward_frame = (xvmc_frame_t *) forward_ref_image; - xvmc_frame_t *backward_frame = (xvmc_frame_t *) backward_ref_image; + xvmc_frame_t *current_frame = XVMC_FRAME(current_image); + xvmc_frame_t *forward_frame = XVMC_FRAME(forward_ref_image); + xvmc_frame_t *backward_frame = XVMC_FRAME(backward_ref_image); int flags; lprintf ("xvmc_render_macro_blocks\n"); @@ -561,6 +561,7 @@ static vo_frame_t *xvmc_alloc_frame (vo_ return NULL; frame->vo_frame.accel_data = &frame->xvmc_data; + frame->xvmc_data.vo_frame = &frame->vo_frame; /* keep track of frames and how many frames alocated. */ this->frames[this->num_frame_buffers++] = frame; diff --git a/src/video_out/video_out_xxmc.c b/src/video_out/video_out_xxmc.c --- a/src/video_out/video_out_xxmc.c +++ b/src/video_out/video_out_xxmc.c @@ -365,15 +365,15 @@ static int xxmc_lock_and_validate_surfac switch(pc_type) { case XINE_PICT_B_TYPE: - frame = (xxmc_frame_t *) bw_frame; + frame = XXMC_FRAME(bw_frame); if (!xxmc_xvmc_surface_valid( driver, frame->xvmc_surf)) break; /* fall through */ case XINE_PICT_P_TYPE: - frame = (xxmc_frame_t *) fw_frame; + frame = XXMC_FRAME(fw_frame); if (!xxmc_xvmc_surface_valid( driver, frame->xvmc_surf)) break; /* fall through */ default: - frame = (xxmc_frame_t *) cur_frame; + frame = XXMC_FRAME(cur_frame); if (!xxmc_xvmc_surface_valid( driver, frame->xvmc_surf)) break; return 0; } @@ -404,7 +404,7 @@ static void xvmc_flush(vo_frame_t *this_ { xxmc_frame_t - *frame = (xxmc_frame_t *) this_gen; + *frame = XXMC_FRAME(this_gen); xxmc_driver_t *driver = (xxmc_driver_t *) this_gen->driver; @@ -452,6 +452,7 @@ static void xxmc_duplicate_frame_data(vo return; } this->xxmc_data = *xxmc; + this->xxmc_data.xvmc.vo_frame = &this->vo_frame; this->width = original->width; this->height = original->height; this->format = original->format; @@ -566,6 +567,7 @@ static vo_frame_t *xxmc_alloc_frame (vo_ frame->vo_frame.driver = this_gen; frame->last_sw_format = 0; frame->vo_frame.accel_data = &frame->xxmc_data; + frame->xxmc_data.xvmc.vo_frame = &frame->vo_frame; frame->image = NULL; xprintf (this->xine, XINE_VERBOSITY_DEBUG, "Allocating frame\n"); @@ -1213,10 +1215,17 @@ static void xxmc_do_update_frame(vo_driv double ratio, int format, int flags) { xxmc_driver_t *this = (xxmc_driver_t *) this_gen; - xxmc_frame_t *frame = (xxmc_frame_t *) frame_gen; + xxmc_frame_t *frame = XXMC_FRAME(frame_gen); if ( XINE_IMGFMT_XXMC == format ) { xine_xxmc_t *xxmc = &frame->xxmc_data; + vo_frame_t orig_frame_content; + + if (frame_gen != &frame->vo_frame) { + /* this is an intercepted frame, so we need to detect and propagate any + * changes on the original vo_frame to all the intercepted frames */ + xine_fast_memcpy(&orig_frame_content, &frame->vo_frame, sizeof (vo_frame_t)); + } xvmc_context_writer_lock( &this->xvmc_lock); if (xxmc_accel_update(this, this->last_accel_request, xxmc->acceleration) || @@ -1232,7 +1241,7 @@ static void xxmc_do_update_frame(vo_driv if (this->contextActive) xxmc_frame_updates(this, frame, 1); - xxmc_do_update_frame_xv(this_gen, frame_gen, width, height, ratio, + xxmc_do_update_frame_xv(this_gen, &frame->vo_frame, width, height, ratio, xxmc->fallback_format, flags); if (!this->contextActive) { @@ -1246,6 +1255,33 @@ static void xxmc_do_update_frame(vo_driv xvmc_context_writer_unlock( &this->xvmc_lock); + if (frame_gen != &frame->vo_frame) { + /* this is an intercepted frame, so we need to detect and propagate any + * changes on the original vo_frame to all the intercepted frames */ + unsigned char *p0 = (unsigned char *)&orig_frame_content; + unsigned char *p1 = (unsigned char *)&frame->vo_frame; + int i; + for (i = 0; i < sizeof (vo_frame_t); i++) { + if (*p0 != *p1) { + /* propagate the change */ + vo_frame_t *f = frame_gen; + while (f->next) { + /* serveral restrictions apply when intercepting XXMC frames. So let's check + * the intercepted frames before modifing them and fail otherwise. */ + unsigned char *p = (unsigned char *)f + i; + if (*p != *p0) { + xprintf(this->xine, XINE_VERBOSITY_DEBUG, "xxmc_do_update_frame: a post plugin violates the restrictions on intercepting XXMC frames\n"); + _x_abort(); + } + + *p = *p1; + f = f->next; + } + } + p0++; + p1++; + } + } } else { /* switch back to an unaccelerated context */ if (this->last_accel_request != 0xFFFFFFFF) { @@ -1253,7 +1289,7 @@ static void xxmc_do_update_frame(vo_driv xxmc_xvmc_update_context(this, frame, width, height, 0); } frame->vo_frame.proc_duplicate_frame_data = NULL; - xxmc_do_update_frame_xv(this_gen, frame_gen, width, height, ratio, + xxmc_do_update_frame_xv(this_gen, &frame->vo_frame, width, height, ratio, format, flags); } } diff --git a/src/video_out/xvmc_mocomp.c b/src/video_out/xvmc_mocomp.c --- a/src/video_out/xvmc_mocomp.c +++ b/src/video_out/xvmc_mocomp.c @@ -70,9 +70,9 @@ static void xvmc_render_macro_blocks(vo_ int second_field, xvmc_macroblocks_t *macroblocks) { xxmc_driver_t *this = (xxmc_driver_t *) current_image->driver; - xxmc_frame_t *current_frame = (xxmc_frame_t *) current_image; - xxmc_frame_t *forward_frame = (xxmc_frame_t *) forward_ref_image; - xxmc_frame_t *backward_frame = (xxmc_frame_t *) backward_ref_image; + xxmc_frame_t *current_frame = XXMC_FRAME(current_image); + xxmc_frame_t *forward_frame = XXMC_FRAME(forward_ref_image); + xxmc_frame_t *backward_frame = XXMC_FRAME(backward_ref_image); int flags; lprintf ("xvmc_render_macro_blocks\n"); diff --git a/src/video_out/xvmc_vld.c b/src/video_out/xvmc_vld.c --- a/src/video_out/xvmc_vld.c +++ b/src/video_out/xvmc_vld.c @@ -32,12 +32,12 @@ void xvmc_vld_frame(struct vo_frame_s *t { vo_frame_t *this = (vo_frame_t *) this_gen; xxmc_frame_t - *cf = (xxmc_frame_t *) this; + *cf = XXMC_FRAME(this); xine_vld_frame_t *vft = &(cf->xxmc_data.vld_frame); xxmc_frame_t - *ff = (xxmc_frame_t *) vft->forward_reference_frame, - *bf = (xxmc_frame_t *) vft->backward_reference_frame; + *ff = XXMC_FRAME(vft->forward_reference_frame), + *bf = XXMC_FRAME(vft->backward_reference_frame); XvMCMpegControl ctl; xxmc_driver_t *driver = (xxmc_driver_t *) cf->vo_frame.driver; @@ -104,7 +104,7 @@ void xvmc_vld_slice(vo_frame_t *this_gen void xvmc_vld_slice(vo_frame_t *this_gen) { xxmc_frame_t - *cf = (xxmc_frame_t *) this_gen; + *cf = XXMC_FRAME(this_gen); xxmc_driver_t *driver = (xxmc_driver_t *) cf->vo_frame.driver; diff --git a/src/xine-engine/accel_xvmc.h b/src/xine-engine/accel_xvmc.h --- a/src/xine-engine/accel_xvmc.h +++ b/src/xine-engine/accel_xvmc.h @@ -65,6 +65,7 @@ typedef struct xine_vld_frame_s { typedef struct xine_xvmc_s { + vo_frame_t *vo_frame; xine_macroblocks_t *macroblocks; void (*proc_macro_block)(int x,int y,int mb_type, int motion_type,int (*mv_field_sel)[2], @@ -73,6 +74,9 @@ typedef struct xine_xvmc_s { vo_frame_t *backward_ref_frame,int picture_structure, int second_field,int (*f_mot_pmv)[2],int (*b_mot_pmv)[2]); } xine_xvmc_t ; + +#define XVMC_DATA(frame_gen) ((frame_gen) ? (xine_xvmc_t *)(frame_gen)->accel_data : (xine_xvmc_t *)0) +#define XVMC_FRAME(frame_gen) ((frame_gen) ? (xvmc_frame_t *)XVMC_DATA(frame_gen)->vo_frame : (xvmc_frame_t *)0) typedef struct xine_xxmc_s { @@ -108,6 +112,9 @@ typedef struct xine_xxmc_s { void (*proc_xxmc_unlock) (vo_driver_t *this_gen); } xine_xxmc_t; +#define XXMC_DATA(frame_gen) ((frame_gen) ? (xine_xxmc_t *)(frame_gen)->accel_data : (xine_xxmc_t *)0) +#define XXMC_FRAME(frame_gen) ((frame_gen) ? (xxmc_frame_t *)XXMC_DATA(frame_gen)->xvmc.vo_frame : (xxmc_frame_t *)0) + /* * Register XvMC stream types here. */ diff --git a/src/xine-engine/post.c b/src/xine-engine/post.c --- a/src/xine-engine/post.c +++ b/src/xine-engine/post.c @@ -144,6 +144,14 @@ static void post_video_flush(xine_video_ if (port->port_lock) pthread_mutex_unlock(port->port_lock); } +static void post_video_trigger_drawing(xine_video_port_t *port_gen) { + post_video_port_t *port = (post_video_port_t *)port_gen; + + if (port->port_lock) pthread_mutex_lock(port->port_lock); + port->original_port->trigger_drawing(port->original_port); + if (port->port_lock) pthread_mutex_unlock(port->port_lock); +} + static int post_video_status(xine_video_port_t *port_gen, xine_stream_t *stream, int *width, int *height, int64_t *img_duration) { post_video_port_t *port = (post_video_port_t *)port_gen; @@ -187,6 +195,7 @@ static int post_video_rewire(xine_post_o if (!new_port) return 0; + this->running_ticket->lock_port_rewiring(this->running_ticket, -1); this->running_ticket->revoke(this->running_ticket, 1); if (input_port->original_port->status(input_port->original_port, input_port->stream, @@ -197,6 +206,7 @@ static int post_video_rewire(xine_post_o input_port->original_port = new_port; this->running_ticket->issue(this->running_ticket, 1); + this->running_ticket->unlock_port_rewiring(this->running_ticket); return 1; } @@ -218,6 +228,7 @@ post_video_port_t *_x_post_intercept_vid port->new_port.exit = post_video_exit; port->new_port.get_overlay_manager = post_video_get_overlay_manager; port->new_port.flush = post_video_flush; + port->new_port.trigger_drawing = post_video_trigger_drawing; port->new_port.status = post_video_status; port->new_port.get_property = post_video_get_property; port->new_port.set_property = post_video_set_property; @@ -377,10 +388,11 @@ vo_frame_t *_x_post_intercept_video_fram port->new_frame->free ? port->new_frame->free : post_frame_free; new_frame->dispose = port->new_frame->dispose ? port->new_frame->dispose : post_frame_dispose; - - if (!port->new_frame->draw) { + + if (!port->new_frame->draw || (port->route_preprocessing_procs && port->route_preprocessing_procs(port, frame))) { /* draw will most likely modify the frame, so the decoder - * should only request preprocessing when there is no new draw */ + * should only request preprocessing when there is no new draw + * but route_preprocessing_procs() can override this decision */ if (frame->proc_frame && !new_frame->proc_frame) new_frame->proc_frame = post_frame_proc_frame; if (frame->proc_slice && !new_frame->proc_slice) @@ -697,6 +709,7 @@ static int post_audio_rewire(xine_post_o if (!new_port) return 0; + this->running_ticket->lock_port_rewiring(this->running_ticket, -1); this->running_ticket->revoke(this->running_ticket, 1); if (input_port->original_port->status(input_port->original_port, input_port->stream, @@ -707,6 +720,7 @@ static int post_audio_rewire(xine_post_o input_port->original_port = new_port; this->running_ticket->issue(this->running_ticket, 1); + this->running_ticket->unlock_port_rewiring(this->running_ticket); return 1; } diff --git a/src/xine-engine/post.h b/src/xine-engine/post.h --- a/src/xine-engine/post.h +++ b/src/xine-engine/post.h @@ -177,6 +177,13 @@ struct post_video_port_s { /* the new frame function pointers */ vo_frame_t *new_frame; + /* if you want to decide yourself, whether the preprocessing functions + * should still be routed when draw is intercepted, fill in this + * function; _x_post_intercept_video_frame() acts as a template method + * and asks your function; return a boolean; the default is _not_ to + * route preprocessing functions when draw is intercepted */ + int (*route_preprocessing_procs)(post_video_port_t *self, vo_frame_t *frame); + /* if you want to decide yourself, whether the overlay manager should * be intercepted, fill in this function; get_overlay_manager() acts as * a template method and asks your function; return a boolean; diff --git a/src/xine-engine/video_out.c b/src/xine-engine/video_out.c --- a/src/xine-engine/video_out.c +++ b/src/xine-engine/video_out.c @@ -132,6 +132,9 @@ typedef struct { int frame_drop_cpt; int frame_drop_suggested; int crop_left, crop_right, crop_top, crop_bottom; + pthread_mutex_t trigger_drawing_mutex; + pthread_cond_t trigger_drawing_cond; + int trigger_drawing; } vos_t; @@ -1068,6 +1071,32 @@ static void check_redraw_needed (vos_t * this->redraw_needed = 1; } +static int interruptable_sleep(vos_t *this, int usec_to_sleep) +{ + int timedout = 0; + + struct timeval now; + gettimeofday(&now, 0); + + pthread_mutex_lock (&this->trigger_drawing_mutex); + if (!this->trigger_drawing) { + struct timespec abstime; + abstime.tv_sec = now.tv_sec + usec_to_sleep / 1000000; + abstime.tv_nsec = now.tv_usec * 1000 + (usec_to_sleep % 1000000) * 1000; + + if (abstime.tv_nsec > 1000000000) { + abstime.tv_nsec -= 1000000000; + abstime.tv_sec++; + } + + timedout = pthread_cond_timedwait(&this->trigger_drawing_cond, &this->trigger_drawing_mutex, &abstime); + } + this->trigger_drawing = 0; + pthread_mutex_unlock (&this->trigger_drawing_mutex); + + return timedout; +} + /* special loop for paused mode * needed to update screen due overlay changes, resize, window * movement, brightness adjusting etc. @@ -1113,7 +1142,7 @@ static void paused_loop( vos_t *this, in } pthread_mutex_unlock( &this->free_img_buf_queue->mutex ); - xine_usec_sleep (20000); + interruptable_sleep(this, 20000); pthread_mutex_lock( &this->free_img_buf_queue->mutex ); } @@ -1243,7 +1272,10 @@ static void *video_out_loop (void *this_ "video_out: vpts/clock error, next_vpts=%" PRId64 " cur_vpts=%" PRId64 "\n", next_frame_vpts,vpts); if (usec_to_sleep > 0) - xine_usec_sleep (usec_to_sleep); + { + if (0 == interruptable_sleep(this, usec_to_sleep)) + break; + } if (this->discard_frames) break; @@ -1626,6 +1658,9 @@ static void vo_exit (xine_video_port_t * free (this->free_img_buf_queue); free (this->display_img_buf_queue); + pthread_cond_destroy(&this->trigger_drawing_cond); + pthread_mutex_destroy(&this->trigger_drawing_mutex); + free (this); } @@ -1693,6 +1728,15 @@ static void vo_flush (xine_video_port_t this->discard_frames--; pthread_mutex_unlock(&this->display_img_buf_queue->mutex); } +} + +static void vo_trigger_drawing (xine_video_port_t *this_gen) { + vos_t *this = (vos_t *) this_gen; + + pthread_mutex_lock (&this->trigger_drawing_mutex); + this->trigger_drawing = 1; + pthread_cond_signal (&this->trigger_drawing_cond); + pthread_mutex_unlock (&this->trigger_drawing_mutex); } /* crop_frame() will allocate a new frame to copy in the given image @@ -1790,6 +1834,7 @@ xine_video_port_t *_x_vo_new_port (xine_ this->vo.enable_ovl = vo_enable_overlay; this->vo.get_overlay_manager = vo_get_overlay_manager; this->vo.flush = vo_flush; + this->vo.trigger_drawing = vo_trigger_drawing; this->vo.get_property = vo_get_property; this->vo.set_property = vo_set_property; this->vo.status = vo_status; @@ -1883,6 +1928,9 @@ xine_video_port_t *_x_vo_new_port (xine_ "were not scheduled for display in time, xine sends a notification."), 20, NULL, NULL); + pthread_mutex_init(&this->trigger_drawing_mutex, NULL); + pthread_cond_init(&this->trigger_drawing_cond, NULL); + this->trigger_drawing = 0; if (grabonly) { diff --git a/src/xine-engine/video_out.h b/src/xine-engine/video_out.h --- a/src/xine-engine/video_out.h +++ b/src/xine-engine/video_out.h @@ -198,6 +198,9 @@ struct xine_video_port_s { /* flush video_out fifo */ void (*flush) (xine_video_port_t *self); + /* trigger immediate drawing */ + void (*trigger_drawing) (xine_video_port_t *self); + /* Get/Set video property * * See VO_PROP_* bellow diff --git a/src/xine-engine/video_overlay.h b/src/xine-engine/video_overlay.h --- a/src/xine-engine/video_overlay.h +++ b/src/xine-engine/video_overlay.h @@ -35,7 +35,7 @@ #define MAX_OBJECTS 50 #define MAX_EVENTS 50 -#define MAX_SHOWING 16 +#define MAX_SHOWING (5 + 16) #define OVERLAY_EVENT_NULL 0 #define OVERLAY_EVENT_SHOW 1 diff --git a/src/xine-engine/xine.c b/src/xine-engine/xine.c --- a/src/xine-engine/xine.c +++ b/src/xine-engine/xine.c @@ -294,8 +294,37 @@ static void ticket_revoke(xine_ticket_t pthread_mutex_unlock(&this->revoke_lock); } +static int ticket_lock_port_rewiring(xine_ticket_t *this, int ms_timeout) { + + if (ms_timeout >= 0) { + struct timespec abstime; + + struct timeval now; + gettimeofday(&now, 0); + + abstime.tv_sec = now.tv_sec + ms_timeout / 1000; + abstime.tv_nsec = now.tv_usec * 1000 + (ms_timeout % 1000) * 1e6; + + if (abstime.tv_nsec > 1e9) { + abstime.tv_nsec -= 1e9; + abstime.tv_sec++; + } + + return (0 == pthread_mutex_timedlock(&this->port_rewiring_lock, &abstime)); + } + + pthread_mutex_lock(&this->port_rewiring_lock); + return 1; +} + +static void ticket_unlock_port_rewiring(xine_ticket_t *this) { + + pthread_mutex_unlock(&this->port_rewiring_lock); +} + static void ticket_dispose(xine_ticket_t *this) { + pthread_mutex_destroy(&this->port_rewiring_lock); pthread_mutex_destroy(&this->lock); pthread_mutex_destroy(&this->revoke_lock); pthread_cond_destroy(&this->issued); @@ -316,12 +345,15 @@ static xine_ticket_t *XINE_MALLOC ticket port_ticket->renew = ticket_renew; port_ticket->issue = ticket_issue; port_ticket->revoke = ticket_revoke; + port_ticket->lock_port_rewiring = ticket_lock_port_rewiring; + port_ticket->unlock_port_rewiring = ticket_unlock_port_rewiring; port_ticket->dispose = ticket_dispose; port_ticket->holder_thread_count = XINE_MAX_TICKET_HOLDER_THREADS; port_ticket->holder_threads = calloc(XINE_MAX_TICKET_HOLDER_THREADS,sizeof(*port_ticket->holder_threads)); pthread_mutex_init(&port_ticket->lock, NULL); pthread_mutex_init(&port_ticket->revoke_lock, NULL); + pthread_mutex_init(&port_ticket->port_rewiring_lock, NULL); pthread_cond_init(&port_ticket->issued, NULL); pthread_cond_init(&port_ticket->revoked, NULL); @@ -523,6 +555,7 @@ static int stream_rewire_audio(xine_post if (!data) return 0; + stream->xine->port_ticket->lock_port_rewiring(stream->xine->port_ticket, -1); stream->xine->port_ticket->revoke(stream->xine->port_ticket, 1); if (stream->audio_out->status(stream->audio_out, stream, &bits, &rate, &mode)) { @@ -533,6 +566,7 @@ static int stream_rewire_audio(xine_post stream->audio_out = new_port; stream->xine->port_ticket->issue(stream->xine->port_ticket, 1); + stream->xine->port_ticket->unlock_port_rewiring(stream->xine->port_ticket); return 1; } @@ -547,6 +581,7 @@ static int stream_rewire_video(xine_post if (!data) return 0; + stream->xine->port_ticket->lock_port_rewiring(stream->xine->port_ticket, -1); stream->xine->port_ticket->revoke(stream->xine->port_ticket, 1); if (stream->video_out->status(stream->video_out, stream, &width, &height, &img_duration)) { @@ -557,6 +592,7 @@ static int stream_rewire_video(xine_post stream->video_out = new_port; stream->xine->port_ticket->issue(stream->xine->port_ticket, 1); + stream->xine->port_ticket->unlock_port_rewiring(stream->xine->port_ticket); return 1; } @@ -2339,3 +2375,83 @@ int _x_query_buffer_usage(xine_stream_t return ticket_acquired != 0; } + +int _x_lock_port_rewiring(xine_t *xine, int ms_timeout) +{ + return xine->port_ticket->lock_port_rewiring(xine->port_ticket, ms_timeout); +} + +void _x_unlock_port_rewiring(xine_t *xine) +{ + xine->port_ticket->unlock_port_rewiring(xine->port_ticket); +} + +int _x_lock_frontend(xine_stream_t *stream, int ms_to_time_out) +{ + if (ms_to_time_out >= 0) { + struct timespec abstime; + + struct timeval now; + gettimeofday(&now, 0); + + abstime.tv_sec = now.tv_sec + ms_to_time_out / 1000; + abstime.tv_nsec = now.tv_usec * 1000 + (ms_to_time_out % 1000) * 1e6; + + if (abstime.tv_nsec > 1e9) { + abstime.tv_nsec -= 1e9; + abstime.tv_sec++; + } + + return (0 == pthread_mutex_timedlock(&stream->frontend_lock, &abstime)); + } + + pthread_mutex_lock(&stream->frontend_lock); + return 1; +} + +void _x_unlock_frontend(xine_stream_t *stream) +{ + pthread_mutex_unlock(&stream->frontend_lock); +} + +int _x_query_unprocessed_osd_events(xine_stream_t *stream) +{ + video_overlay_manager_t *ovl; + int redraw_needed; + + if (!stream->xine->port_ticket->acquire_nonblocking(stream->xine->port_ticket, 1)) + return -1; + + ovl = stream->video_out->get_overlay_manager(stream->video_out); + redraw_needed = ovl->redraw_needed(ovl, 0); + + if (redraw_needed) + stream->video_out->trigger_drawing(stream->video_out); + + stream->xine->port_ticket->release_nonblocking(stream->xine->port_ticket, 1); + + return redraw_needed; +} + +int _x_demux_seek(xine_stream_t *stream, off_t start_pos, int start_time, int playing) +{ + if (!stream->demux_plugin) + return -1; + return stream->demux_plugin->seek(stream->demux_plugin, start_pos, start_time, playing); +} + +int _x_continue_stream_processing(xine_stream_t *stream) +{ + return stream->status != XINE_STATUS_STOP + && stream->status != XINE_STATUS_QUIT; +} + +void _x_trigger_relaxed_frame_drop_mode(xine_stream_t *stream) +{ + stream->first_frame_flag = 2; +} + +void _x_reset_relaxed_frame_drop_mode(xine_stream_t *stream) +{ + stream->first_frame_flag = 1; +} diff --git a/src/xine-engine/xine_internal.h b/src/xine-engine/xine_internal.h --- a/src/xine-engine/xine_internal.h +++ b/src/xine-engine/xine_internal.h @@ -169,6 +169,9 @@ struct xine_ticket_s { * be used in combination with acquire_nonblocking() */ void (*release_nonblocking)(xine_ticket_t *self, int irrevocable); + int (*lock_port_rewiring)(xine_ticket_t *self, int ms_timeout); + void (*unlock_port_rewiring)(xine_ticket_t *self); + void (*dispose)(xine_ticket_t *self); pthread_mutex_t lock; @@ -185,6 +188,7 @@ struct xine_ticket_s { pthread_t holder; } *holder_threads; unsigned holder_thread_count; + pthread_mutex_t port_rewiring_lock; #endif }; @@ -375,6 +379,15 @@ struct xine_stream_s { */ int _x_query_buffer_usage(xine_stream_t *stream, int *num_video_buffers, int *num_audio_buffers, int *num_video_frames, int *num_audio_frames) XINE_PROTECTED; +int _x_lock_port_rewiring(xine_t *xine, int ms_to_time_out) XINE_PROTECTED; +void _x_unlock_port_rewiring(xine_t *xine) XINE_PROTECTED; +int _x_lock_frontend(xine_stream_t *stream, int ms_to_time_out) XINE_PROTECTED; +void _x_unlock_frontend(xine_stream_t *stream) XINE_PROTECTED; +int _x_query_unprocessed_osd_events(xine_stream_t *stream) XINE_PROTECTED; +int _x_demux_seek(xine_stream_t *stream, off_t start_pos, int start_time, int playing) XINE_PROTECTED; +int _x_continue_stream_processing(xine_stream_t *stream) XINE_PROTECTED; +void _x_trigger_relaxed_frame_drop_mode(xine_stream_t *stream) XINE_PROTECTED; +void _x_reset_relaxed_frame_drop_mode(xine_stream_t *stream) XINE_PROTECTED; void _x_handle_stream_end (xine_stream_t *stream, int non_user) XINE_PROTECTED; xine-0.9.4/INSTALL0000644000000000000000000004044711537763153012242 0ustar rootroot OVERVIEW ~~~~~~~~ Introduction A note about VDPAU Getting a recent xine Patching xine's source Building xine Testing xine Patching VDR Makefile settings Building vdr-xine Xine VDR version mismatch Testing vdr-xine Using budget cards Remote learning mode Using full featured cards Additional software Last but not least INTRODUCTION ~~~~~~~~~~~~ As you can read these lines, you've successfully extracted the tar archive of VDR plugin xine. The plugin supplies VDR with an output device in software by using xine. It's most useful if you don't own a DVB card which can be directly connected to a TV set, or if you want to watch HDTV (MPEG2/H.264) content which typically cannot be displayed by such a card. Usually, the contents of this package should be below the PLUGINS subdirectory of VDR. I've installed the source of VDR in directory "/soft/src/VDR", so the files of this package reside in directory "/soft/src/VDR/PLUGINS/src/xine". A NOTE ABOUT VDPAU ~~~~~~~~~~~~~~~~~~ VDPAU is a new API for hardware assisted video decoding. If you plan to use VDPAU, you will need to choose different sources for xine-lib. To stay with xine-lib-1.1 you can choose the VDPAU development fork named xine-vdpau which is available via svn and go on with patching it for vdr-xine: svn co svn://jusst.de/xine-vdpau In case you would like to use xine-lib-1.2 then you need to patch in VDPAU support. You'll find updated patches against xine-lib-1.2 here: http://jusst.de/vdpau The patches are named like xine-lib-1.2-vdpau-r157.diff.bz2 where r157 for example indicates the vdpau svn revision this patch is based on. GETTING A RECENT XINE ~~~~~~~~~~~~~~~~~~~~~ To compile and use this plugin, you must get a recent Hg/CVS version of xine. When you are going to use xine-lib-1.2 then you can most often find binary packages for your distribution. Additionally you'll have to install a suitable devel-package too. After installing these packages, just read on in section PATCHING VDR. To retrieve xine via Hg/CVS, please go to the project homepage of xine (which is http://xine.sf.net). On the main page, look for downloads and then for CVS or Mercurial (Hg) respectively. Follow the instructions on this page and checkout module "xine-lib". You also need a recent version of a GUI for xine, so checkout module "xine-ui". I've checked out both modules and they reside in directory "/soft/src/xine-lib" and "/soft/src/xine-ui" respectively. My Hg/CVS commands were: hg clone http://hg.debian.org/hg/xine-lib/xine-lib cvs -d:pserver:anonymous@xine.cvs.sourceforge.net:/cvsroot/xine login cvs -z9 -d:pserver:anonymous@xine.cvs.sourceforge.net:/cvsroot/xine co xine-ui NOTE: As space permits you'll also find archives of both modules on my homepage which you might use if you don't have access to CVS servers. Just choose the most recent archives for the most recent vdr-xine plugin. NOTE: The above command and my archives on the homepage supply xine-lib-1.1.x, which needs to be patched as mentioned below. If you like, you may want to try xine-lib-1.2, which contains already everything regarding vdr-xine and therefore doesn't need to be patched. PATCHING XINE'S SOURCE ~~~~~~~~~~~~~~~~~~~~~~ Next, you have to extend xine-lib to support a media resource location (mrl) type named "vdr:". Change to the parent directory of module "xine-lib", e. g. in my case "/soft/src". Then apply the patches supplied with this distribution (in case you are using xine-lib-1.2, there is no need for patching). The patches will add new source files to xine-lib's source tree and will add keybindings to xine-ui for supporting my plugin's remote functionality. I use the following commands for patching: patch -dxine-lib -p1 < /soft/src/VDR/PLUGINS/src/xine/patches/xine-lib.patch patch -dxine-ui -p1 < /soft/src/VDR/PLUGINS/src/xine/patches/xine-ui.patch NOTE: It might well be that some of the patch files are empty if my changes have been ported back into xine's CVS repository. But I didn't want to remove those empty files as it might break some build scripts which people created to build xine. BUILDING XINE ~~~~~~~~~~~~~ Now, it's time to build xine-lib. Change back to the directory of module "xine-lib", e. g. "/soft/src/xine-lib", and run "./autogen.sh". Please don't call "configure" directly at this stage, even if you don't use the CVS source. Doing so will miss necessary modifications to the Makefiles and finally lead to not building the new plugins! "./autogen.sh" will need some additional tools which must have at least a certain minimum version to successfully build xine. If you look into the head of "./autogen.sh" then you'll find the following information: # Minimum value required to build WANT_AUTOMAKE_1_8=1 export WANT_AUTOMAKE_1_8 WANT_AUTOMAKE=1.8 export WANT_AUTOMAKE AUTOMAKE_MIN=1.8.0 AUTOCONF_MIN=2.59 LIBTOOL_MIN=1.4.0 On my openSUSE 11.1 I use the following versions: automake-1.10.1-4.286 autoconf-2.63-1.136 libtool-2.2.6-1.35 If you have trouble running "./autogen.sh" respectively the hereby called configure please consider upgrading the above mentioned tools. Calling "./autogen.sh" will create all the files which configure needs, and will finally run configure. If you need special options for configure, you can specify them as command arguments to autogen.sh. If you intend to use vdr-xine for watching H.264 channels, I highly recommend to install a recent FFmpeg and to add the switch "--with-external-ffmpeg" when compiling xine-lib (it's the default since xine-lib-1.1.15). When building xine-ui I suggest the following configure options: --enable-vdr-keys Adds VDR's commands to xine's keybinding dialog. You're then able to assign keys in xine to VDR's commands and therefore control VDR by pressing the appropriate keys in xine's window. For example I run the following command for building xine-ui: ./autogen.sh --prefix=/soft/xine-ui-cvs --enable-vdr-keys As xine supports a lot of media, it also requires a lot of libraries to compile all modules. Please see the file "config.log" and the xine documentation to get all the required components for the plugins you want to build. After configure has been run (automatically from autogen.sh), the commands "make" and "make install" should provide you with a recent version of module "xine-lib". After that, you can go to the directory of module "xine-ui" and repeat the steps for building the standard GUI for xine. TESTING XINE ~~~~~~~~~~~~ Please test the newly compiled xine with a sample MPEG file, to see whether it works. Be careful, that not any previously versions of xine respectively plugins get loaded. Use the option "--verbose=2" and verify that the output of xine contains a line which indicates loading "xineplug_vdr.so". PATCHING VDR ~~~~~~~~~~~~ There may be further patches available on my homepage that enhance VDR for best cooperation with vdr-xine. For further details about the patches please have a look into the corresponding README file on my homepage. I highly recommend to apply at least some of them. MAKEFILE SETTINGS ~~~~~~~~~~~~~~~~~ Before you go on to compile vdr-xine, please make sure that you've installed xine-lib already. Otherwise you'll get a lot of errors due to missing header files respectively missing structure members if there are already old versions of some header files available! Then please have a look at my plugin's "Makefile" and locate "INCLUDES", "VDR_XINE_FIFO_DIR" and "VDR_XINE_SET_VIDEO_WINDOW". As the plugin depends on xine's data structures of it's companion, it is necessary to include xine's header files. For this to work, INCLUDES must contain the include path of xine's header files. Therefore, pkg-config is asked to supply the path as it was set when xine was compiled. Make sure that pkg-config reports the include directory of your previously installed xine-lib by running the following command in a shell: pkg-config --cflags libxine Depending on your setup, you may need to adapt the environment variable PKG_CONFIG_PATH to change the order in which pkgconfig directories are queried to achive the goal. One reason for getting a wrong directory reported is that a xine-lib-devel package is still installed. Simply uninstall it and try building vdr-xine again. The plugin will create its fifos (which are used for data exchange with xine) below directory "VDR_XINE_FIFO_DIR" (e. g. "/tmp/vdr-xine"). It's up to you to create and maintain any parent directories of the directory you specify here (in this case "/tmp"); the plugin will itself try to create and remove the final directory (in this case "vdr-xine"). So you may need to create the vdr-xine directy too, if the plugin is not allowed to do it on its own at runtime. NOTE: xine's autoscan button "VDR" currently expects "VDR_XINE_FIFO_DIR" to be set to directory "/tmp/vdr-xine"! If you are using the yaepg plugin and have patched VDR to support it, then you might also enable VDR_XINE_SET_VIDEO_WINDOW. vdr-xine will then access the new OSD member vidWin to extract position and size of the video window and ask xine to position the video accordingly within the original frame. BUILDING VDR-XINE ~~~~~~~~~~~~~~~~~ Just go to VDR's source directory and type "make plugins". This should compile my plugin and "xineplayer". For more information about xineplayer please have a look into MANUAL. XINE VDR VERSION MISMATCH ~~~~~~~~~~~~~~~~~~~~~~~~~ In the case you've got a compilation error which asked you to read this section, please try the following to solve this issue. Otherwise skip this section. - Make sure that the above mentioned pkg-config command shows you the correct location where xine-lib's include files reside. - Make sure that there exists xine/vdr.h below this directory. In the case that it does not exist and you are building xine-lib-1.1.x please check whether you've applied the xine-lib.patch as mentioned above. - When the file exists and you still get this error then you're most likely trying to mix different versions of vdr-xine and xine-lib. Make sure that you've installed a matching version of xine-lib. You may also need to upgrade vdr-xine to use a recent version of xine-lib. - Try to clean the build if the error persists after any changes like that: make plugins-clean TESTING VDR-XINE ~~~~~~~~~~~~~~~~ For a first run, go to the VDR directory (e. g. "/soft/src/VDR") and issue the following command: ./runvdr "'-Pxine -r'" This should start VDR and have it load my plugin, but if this is the first time that you use my plugin it will also abort immediately with an error message like this: vdr-xine: error: couldn't open '/video/plugins/xine/noSignal4x3.mpg'! vdr-xine: error: couldn't open '/video/plugins/xine/noSignal.mpg'! vdr-xine: error: couldn't open '/video/plugins/xine/noSignal16x9.mpg'! vdr-xine: error: couldn't open '/video/plugins/xine/noSignal.mpg'! The exact path depends on VDR's directory for config files (typically '/video' but see VDR's manual). To get vdr-xine working just create the directory "xine" below "plugins" and copy the contents of the directory "data" (located in my plugin's source directory) into the new directory. After that retry starting VDR again as mentioned above. If no errors are reported, six fifos should now exist below "VDR_XINE_FIFO_DIR" (e. g. "/tmp/vdr-xine"), named "stream", "stream.control", "stream.result" and "stream.event" as well as "external.control" and "external.result". All of them have a size of 0 bytes. At any time, you can use xine to connect to the above mentioned fifo "stream" (while the "external" fifos are used by xineplayer). The MRL may look like this, depending on the "VDR_XINE_FIFO_DIR" at compile time: vdr://tmp/vdr-xine/stream So either type something like xine vdr://tmp/vdr-xine/stream or just hit the autoscan button "VDR" in xine. You should now see a beautiful "vdr-xine" logo on screen! USING BUDGET CARDS ~~~~~~~~~~~~~~~~~~ If your system only has budget DVB cards (i. e. cards that don't supply an MPEG decoder and rely on the CPU to decode the MPEG stream), then loading vdr-xine will supply VDR with a "software device" with a software MPEG decoder. VDR will choose this device as output device by default. Depending on your previous usage of VDR, it might well be that VDR has entered remote learning mode and you may see an OSD which invites you to press keys. Please see the VDR manual for more information. Where VDR's configuration suits your receiving equipment, it is most likely that VDR enters transfer mode and you'll see live TV in xine! REMOTE LEARNING MODE ~~~~~~~~~~~~~~~~~~~~ vdr-xine's internal remote (enabled by the "-r" switch) behaves different to VDR's other remotes as there is no key learning necessary within VDR as the keys have to be assinged in xine's keybinding dialog. NOTE: Please don't worry about entries like the following in VDR's logfile as there is currently no other way to skip key learning for a still unknown remote: "ERROR: remote control XineRemote not ready!" Any other remote (e. g. VDR's built in keyboard remote) will make VDR start in remote learning mode if there are still no keys learnt in remote.conf for that kind of remote or when no remote.conf exists so far. To successfully enter remote learning mode, you'll have to connect xine to VDR immediately after starting VDR. Otherwise you'll miss VDR's first prompt (which is visible for about 10 seconds per remote) and VDR will either abort learning mode or go on to learn the next remote. NOTE: It may take about 10 seconds after pressing a remote key in "Phase 1" of remote learning mode until VDR goes on to "Phase 2". USING FULL FEATURED CARDS ~~~~~~~~~~~~~~~~~~~~~~~~~ If your system is equipped with a full featured card (i. e. a card that has its own MPEG decoder), you'll see in xine nothing more than "vdr-xine". This is because VDR has chosen the hardware MPEG decoder over the software MPEG decoder, so the output device is your full featured card. To get the output to xine, you'll have to go to VDR's OSD setup menu and choose the highest device available, which should be vdr-xine's software device. ADDITIONAL SOFTWARE ~~~~~~~~~~~~~~~~~~~ To be able to have VDR grab the image currently displayed in xine, you'll need the programs "y4mscaler", "y4mtoppm" and "pnmtojpeg". On my system I use the following versions: y4mscaler 9.0 y4mtoppm part of mjpegtools-1.8.0 pnmtojpeg part of netpbm-10.26.44-98.16 By default, vdr-xine assumes to find these utilities on your PATH. But you can specify the absolute path to them by setting the Makefile variables VDR_XINE_Y4MSCALER, VDR_XINE_Y4MTOPPM and VDR_XINE_PNMTOJPEG. LAST BUT NOT LEAST ~~~~~~~~~~~~~~~~~~ After you can see VDR's OSD in xine, you've done everything well. Now go on and setup xine and my plugin to suit your needs. See the file "MANUAL" for more information, e. g. how to bind keys in xine to VDR commands. As mentioned above you've copied some data files to ".../plugins/xine". Some of them were named "noSignal*x*.mpg" and is responsible for displaying "vdr-xine" on screen. After you've setup everything properly you might be annoyed of this message whenever you switch channels or when listening to radio stations. Therefore I've supplied additional files "noSignal*x*-completelyBlack.mpg" which show a completely black screen instead. If you prefer a black screen instead of "vdr-xine" just move "noSignal*x*-completelyBlack.mpg" over the files named "noSignal*x*.mpg". Alternatively you may try files like "noSignal4x3-smallTextAtBottom.mpg" or "noSignal4x3-old.mpg". BTW: the ...4x3... or ...16x9... indicate which aspect ratio should be assumed for the files. vdr-xine tries to load both specific files and falls back to the file name "noSignal.mpg" when a specific file does not exist. vdr-xine will then choose the file to display depending on VDR's DVB setup option video format. In case none of the files suits, you may create your own files. Just provide a suitable PNG image named noSignal*x*.png and run mkNoSignal.sh. Keep in mind that the PNG image must be true color even if it just shows grey content and it should be of a size which matches your TV channels e. g. something like 720x576 or 704x576. Furthermore it shouldn't be a too complex image as the resulting MPG files must not exceed 64 kB. BTW: mkNoSignal.sh requires the following tools on your path: pngtopnm part of netpbm-10.26.44-98.16 ppmtoy4m part of mjpegtools-1.8.0 mpeg2enc part of mjpegtools-1.8.0 Have fun! xine-0.9.4/Makefile0000644000000000000000000001103311537763213012633 0ustar rootroot# # Makefile for a Video Disk Recorder plugin # # $Id$ # The official name of this plugin. # This name will be used in the '-P...' option of VDR to load the plugin. # By default the main source file also carries this name. # IMPORTANT: the presence of this macro is important for the Make.config # file. So it must be defined, even if it is not used here! # PLUGIN = xine ### The version number of this plugin (taken from the main source file): VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') ### The C++ compiler and options: CXX ?= g++ #CXX = /soft/gcc-4.1-20060113/bin/g++ -I/soft/include -L/soft/lib CXXFLAGS ?= -g -O3 -Wall -Woverloaded-virtual #CXXFLAGS ?= -g3 -O0 -Wall -Woverloaded-virtual #CXXFLAGS ?= -g3 -O0 -Wall -Woverloaded-virtual -Wformat=2 -Wextra ### The directory environment: VDRDIR = ../../.. LIBDIR = ../../lib TMPDIR = /tmp ### Make sure that necessary options are included: -include $(VDRDIR)/Make.global ### Allow user defined options to overwrite defaults: -include $(VDRDIR)/Make.config INCLUDES += `pkg-config --cflags libxine` # where to create fifos (xine expects them at /tmp/vdr-xine) VDR_XINE_FIFO_DIR ?= /tmp/vdr-xine # can be used to detect inefficient OSD drawing # 0 - do not verify whether the dirty area of a bitmap is really dirty # 1 - verify that bitmap is really dirty and print a message on console when it is not # 2 - additionally fail with a core dump VDR_XINE_VERIFY_BITMAP_DIRTY ?= 0 # enable to fully support yaepg plugin #VDR_XINE_SET_VIDEO_WINDOW = 1 # where are these utilities for image grabbing? (default: anywhere on your PATH) #VDR_XINE_Y4MSCALER = /usr/bin/y4mscaler #VDR_XINE_Y4MTOPPM = /usr/bin/y4mtoppm #VDR_XINE_PNMTOJPEG = /usr/bin/pnmtojpeg ### The version number of VDR's plugin API (taken from VDR's "config.h"): APIVERSION = $(shell (sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h ; sed -ne '/define VDRVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) | sed -ne 1p) ### The name of the distribution archive: ARCHIVE = $(PLUGIN)-$(VERSION) PACKAGE = vdr-$(ARCHIVE) ### Includes and Defines (add further entries here): INCLUDES += -I$(VDRDIR)/include DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' DEFINES += -DFIFO_DIR=\"$(VDR_XINE_FIFO_DIR)\" DEFINES += -DVERIFY_BITMAP_DIRTY=$(VDR_XINE_VERIFY_BITMAP_DIRTY) ifdef VDR_XINE_SET_VIDEO_WINDOW DEFINES += -DSET_VIDEO_WINDOW endif ifdef VDR_XINE_Y4MSCALER DEFINES += -DY4MSCALER=\"$(VDR_XINE_Y4MSCALER)\" endif ifdef VDR_XINE_Y4MTOPPM DEFINES += -DY4MTOPPM=\"$(VDR_XINE_Y4MTOPPM)\" endif ifdef VDR_XINE_PNMTOJPEG DEFINES += -DPNMTOJPEG=\"$(VDR_XINE_PNMTOJPEG)\" endif ### The object files (add further files here): OBJS = $(PLUGIN).o xineDevice.o xineLib.o xineOsd.o xineSettings.o xineSetupPage.o xineRemote.o xineExternal.o vdr172remux.o vdr172h264parser.o ### The main target: all: libvdr-$(PLUGIN).so i18n xineplayer ### Implicit rules: %.o: %.c Makefile $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< ### Dependencies: MAKEDEP = $(CXX) -MM -MG DEPFILE = .dependencies $(DEPFILE): Makefile @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) xineplayer.c > $@ -include $(DEPFILE) ### Internationalization (I18N): PODIR = po LOCALEDIR = $(VDRDIR)/locale I18Npo = $(wildcard $(PODIR)/*.po) I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) I18Npot = $(PODIR)/$(PLUGIN).pot %.mo: %.po msgfmt -c -o $@ $< $(I18Npot): $(wildcard *.c) xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --package-name=vdr-$(PLUGIN) --package-version=$(VERSION) --msgid-bugs-address='Reinhard Nissl ' -o $@ $^ %.po: $(I18Npot) msgmerge -U --no-wrap --no-location --backup=none -q $@ $< @touch $@ $(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo @mkdir -p $(dir $@) cp $< $@ .PHONY: i18n i18n: $(I18Nmsgs) $(I18Npot) ### Targets: libvdr-$(PLUGIN).so: $(OBJS) Makefile $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) -o $@ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) xineplayer: xineplayer.o Makefile $(CXX) $(CXXFLAGS) $(LDFLAGS) xineplayer.o -o $@ dist: $(I18Npo) clean @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) @cp -a * $(TMPDIR)/$(ARCHIVE) @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @echo Distribution package created as $(PACKAGE).tgz clean: @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot xineplayer xineplayer.o xine-0.9.4/xineLib.h0000644000000000000000000003073211537734461012750 0ustar rootroot #ifndef __XINELIB_H #define __XINELIB_H #include "xineCommon.h" #include #if APIVERSNUM < 10307 #include typedef uchar tIndex; typedef eDvbColor tColor; typedef pid_t tThreadId; #include #else #include #define MAXNUMWINDOWS MAXOSDAREAS #endif #include #include "xineExternal.h" #include "xineSettings.h" namespace PluginXine { class cXineLibEvents { protected: virtual ~cXineLibEvents() {} public: virtual void OnClientConnect() = 0; virtual void OnClientDisconnect() = 0; virtual void ReshowCurrentOSD(const int frameLeft, const int frameTop, const int frameWidth, const int frameHeight, const int frameZoomX, const int frameZoomY, const bool unlockOsdUpdate) = 0; virtual void LockOsdUpdate() = 0; virtual bool OsdUpdateLocked() = 0; virtual void DiscontinuityDetected() = 0; virtual bool DeviceReplayingOrTransferring() = 0; }; class cXineLib; // This class performs the adaption from Xine events // to VDR's remote control events. class cXineRemote : public cRemote , private cThread { private: bool m_active; cMutex m_activeMutex; cCondVar m_activeCondVar; const bool m_remoteOn; cXineLib *m_xineLib; virtual void Action (void); bool Ready(void) { return false; }; bool isConnected(); public: cXineRemote(const bool remoteOn); virtual ~cXineRemote(); void setXineLib(cXineLib *const xineLib); }; class cXineOsd; class cXineSettings; class cXineAdapter { public: virtual int X0(void) const = 0; virtual int Y0(void) const = 0; virtual int Width(void) const = 0; virtual int Height(void) const = 0; virtual bool Dirty(int &x1, int &y1, int &x2, int &y2) = 0; virtual void Clean(void) = 0; virtual const tColor *Colors(int &NumColors) const = 0; virtual const uint8_t *Data(int x, int y) const = 0; virtual int SizeOfPixel() const = 0; virtual int SizeOfStride() const = 0; }; class cXineBitmapAdapter : public cXineAdapter { cBitmap *const m_pBitmap; public: cXineBitmapAdapter(cBitmap *const pBitmap) : m_pBitmap(pBitmap) { } virtual int X0(void) const; virtual int Y0(void) const; virtual int Width(void) const; virtual int Height(void) const; virtual bool Dirty(int &x1, int &y1, int &x2, int &y2); virtual void Clean(void); virtual const tColor *Colors(int &NumColors) const; virtual const uint8_t *Data(int x, int y) const; virtual int SizeOfPixel() const; virtual int SizeOfStride() const; operator cXineAdapter *() { if (m_pBitmap) return this; return 0; } }; #if APIVERSNUM >= 10717 class cXinePixmapMemoryAdapter : public cXineAdapter { cPixmapMemory *const m_pPixmapMemory; public: cXinePixmapMemoryAdapter(const cPixmapMemory *const pPixmapMemory); virtual int X0(void) const; virtual int Y0(void) const; virtual int Width(void) const; virtual int Height(void) const; virtual bool Dirty(int &x1, int &y1, int &x2, int &y2); virtual void Clean(void); virtual const tColor *Colors(int &NumColors) const; virtual const uint8_t *Data(int x, int y) const; virtual int SizeOfPixel() const; virtual int SizeOfStride() const; operator cXineAdapter *() { if (m_pPixmapMemory) return this; return 0; } }; #endif class cXineLib : public cThread { public: enum eNeedsScaling { no = 0 , yes , shq }; string m_fifoDir; string m_fifoNameControl; string m_fifoNameResult; string m_fifoNameRemote; string m_fifoNameStream; string m_fifoNameExtControl; string m_fifoNameExtResult; in_addr_t m_bindIp; int m_bindPort; private: cPlugin *const m_plugin; cXineExternal m_external; void internalPaused(const bool paused); cMutex m_pausedMutex; cCondVar m_pausedCondVar; cMutex m_shutdownMutex; cCondVar m_shutdownCondVar; const cXineSettings &m_settings; bool osdUpdateLocked(const char *const funcName); bool m_osdWindowVisible[ MAXNUMWINDOWS ]; #if APIVERSNUM >= 10307 int m_osdWindowVideoLeft[ MAXNUMWINDOWS ]; int m_osdWindowVideoTop[ MAXNUMWINDOWS ]; int m_osdWindowVideoWidth[ MAXNUMWINDOWS ]; int m_osdWindowVideoHeight[ MAXNUMWINDOWS ]; int m_osdWindowVideoZoomX[ MAXNUMWINDOWS ]; int m_osdWindowVideoZoomY[ MAXNUMWINDOWS ]; int m_osdWindowLeft[ MAXNUMWINDOWS ]; int m_osdWindowTop[ MAXNUMWINDOWS ]; int m_osdWindowWidth[ MAXNUMWINDOWS ]; int m_osdWindowHeight[ MAXNUMWINDOWS ]; int m_osdWindowColorsNum[ MAXNUMWINDOWS ]; tColor m_osdWindowColors[ MAXNUMWINDOWS ][ MAXNUMCOLORS ]; int m_scaledWindowColorsNum[ MAXNUMWINDOWS ]; tColor m_scaledWindowColors[ MAXNUMWINDOWS ][ MAXNUMCOLORS ]; cXineSettings::eOsdMode m_osdWindowMode[ MAXNUMWINDOWS ]; int m_osdWindowGamma[ MAXNUMWINDOWS ]; bool m_osdWindowSupportTransparency[ MAXNUMWINDOWS ]; int m_osdWindowBufferSize[ MAXNUMWINDOWS ]; uint8_t *m_osdWindowBuffer[ MAXNUMWINDOWS ]; tArea m_vidWin; #endif result_query_capabilities_t m_capabilities; void cloneBitmap(const int windowNum, cXineAdapter *bitmap, int x1, int y1, int x2, int y2); bool bitmapDiffers(const int windowNum, cXineOsd *xineOsd, cXineAdapter *bitmap, int &x1, int &y1, int &x2, int &y2, const int osdX, const int osdY, const int osdW, const int osdH); bool clipBitmap(cXineOsd *xineOsd, cXineAdapter *bitmap, int &x1, int &y1, int &x2, int &y2, const int osdX, const int osdY, const int osdW, const int osdH); bool m_osdFlushRequired; public: int getRemoteFD() const { return fd_remote; } private: int CreateServerSocket(unsigned short port); int SocketAcceptHelper(int fd); int fd_fifo0_serv, fd_result_serv, fd_control_serv, fd_remote_serv; int fd_fifo0, fd_result, fd_control, fd_remote; cMutex m_ioMutex, m_dataMutex, m_disconnectMutex; cMutex &m_osdMutex; bool m_paused; bool m_frozen; bool m_ignore; bool m_shutdown; bool m_muted; int m_volume; int m_audioChannel; bool m_trickSpeedMode; public: bool isTrickSpeedMode() const { return m_trickSpeedMode; } bool isConnected(); void disconnect(); int xwrite(int f, const void *b, int n); int xread(int f, void *b, int n); private: bool m_noSignalStream16x9; char m_noSignalStreamData[2][ 6 + 0xffff ]; long m_noSignalStreamSize[2]; bool readNoSignalStream(const int index, const string &suffix); cXineLibEvents *m_eventSink; eNeedsScaling NeedsScaling(const int maxOsdWidth, const int maxOsdHeight, const int videoWidth, const int videoHeight); bool pushOut(const uchar id = 0xff); public: bool hasNoSignalStream() const { return m_noSignalStreamSize[0] > 0 && m_noSignalStreamSize[1] > 0; } bool SupportsTrueColorOSD() const; void selectNoSignalStream(const bool stream16x9) { m_noSignalStream16x9 = stream16x9; } virtual void Action(void); bool checkXineVersion(); bool checkConnect(); void enableExternal(const bool enable = true); void ExternalStreamFinished(); public: cXineLib(cPlugin *const plugin, const cXineSettings &settings, cMutex &osdMutex, cXineRemote *const remote); virtual ~cXineLib(); void SetEventSink(cXineLibEvents *const eventSink); bool Open(); void Close(); bool Poll(cPoller &Poller, int TimeoutMs = 0, const bool special = false); #if APIVERSNUM < 10307 bool OpenWindow(cXineOsd *const xineOsd, cWindow *Window, const int maxOsdWidth, const int maxOsdHeight); void CommitWindow(cXineOsd *const xineOsd, cWindow *Window, const bool optimize = true); void ShowWindow(cXineOsd *const xineOsd, cWindow *Window); void HideWindow(cXineOsd *const xineOsd, cWindow *Window, bool Hide); void MoveWindow(cXineOsd *const xineOsd, cWindow *Window, int x, int y); void CloseWindow(cXineOsd *const xineOsd, cWindow *Window); void CloseWindow(cXineOsd *const xineOsd, int Window); #else void sendWindow(const int maxOsdWidth, const int maxOsdHeight, cXineOsd *const xineOsd, const int windowNum, cXineAdapter *bitmap = 0, const int videoLeft = -1, const int videoTop = -1, const int videoWidth = -1, const int videoHeight = -1, const int videoZoomX = -1, const int videoZoomY = -1, const bool dontOptimize = false); void SendWindow(const int maxOsdWidth, const int maxOsdHeight, cXineOsd *const xineOsd, const int windowNum, cXineAdapter *bitmap = 0, const int videoLeft = -1, const int videoTop = -1, const int videoWidth = -1, const int videoHeight = -1, const int videoZoomX = -1, const int videoZoomY = -1, const bool dontOptimize = false); bool execFuncOsdNew(const int maxOsdWidth, const int maxOsdHeight, const eNeedsScaling needsScaling, const int videoLeft, const int videoTop, const int videoWidth, const int videoHeight, int window, int x, int y, int width, int height); bool execFuncOsdDrawBitmap(const int maxOsdWidth, const int maxOsdHeight, const eNeedsScaling needsScaling, const int videoWidth, const int videoHeight, cXineOsd *const xineOsd, int window, cXineAdapter *const bitmap, int x, int y, int width, int height, int stride); void SetVideoWindow(const int maxOsdWidth, const int maxOsdHeight, tArea vidWin, const bool dontOptimize = false); #endif int execFuncStream1(const uchar *Data, int Length); int execFuncStream(const uchar *Data, int Length); bool execFuncStream0(const uchar *Data, int Length); bool execFuncStart(); bool execFuncEnd(); bool execFuncWait(const bool id = false); bool execFuncSetup(); bool execFuncDiscontinuity(); bool execFuncOsdFlush(); bool execFuncOsdNew(int window, int x, int y, int width, int height, int w_ref, int h_ref); bool execFuncOsdFree(int window); bool execFuncOsdShow(int window); bool execFuncOsdHide(int window); bool execFuncOsdSetPosition(int window, int x, int y); bool execFuncOsdDrawBitmap(int window, const uint8_t *const bitmap, const int sizeOfPixel, int x, int y, int width, int height, int stride, const tColor *const colors); #if APIVERSNUM < 10307 bool execFuncSetColor(int window, int index, eDvbColor color); bool execFuncSetColor(int window, int index, eDvbColor *const colors, int numColors); eDvbColor filterAlpha(eDvbColor color); #else bool execFuncSetColor(int window, int index, tColor color); bool execFuncSetColor(int window, int index, tColor *const colors, int numColors); tColor filterAlpha(tColor color); #endif bool execFuncSetColor(int window, int index, int numColors, uint32_t *const colors); bool execFuncMute(bool mute = true); bool execFuncClear(int n); bool execFuncResetAudio(); bool execFuncSelectAudio(int channel); bool execFuncFirstFrame(); bool execFuncStillFrame(); bool execFuncFlush(int TimeoutMs = -1, const bool justWait = false); bool execFuncSetVolume(int volume); bool execFuncSetSpeed(double speed); bool execFuncTrickSpeedMode(bool on); bool execFuncDelay(int usDelay); bool execFuncMetronom(int64_t pts, uint32_t flags = 0); bool execFuncNop(); bool execFuncSetPrebuffer(int frames); bool execFuncGetPTS(int64_t &pts, const int TimeoutMs = 0, bool *const pQueued = 0); bool execFuncVideoSize(int &videoLeft, int &videoTop, int &videoWidth, int &videoHeight, int &videoZoomX, int &videoZoomY, double *const pVideoAspect = 0); bool execFuncGetVersion(int32_t &version); bool execFuncQueryCapabilities(result_query_capabilities_t &capabilities); uchar *execFuncGrabImage(const char *FileName, int &Size, bool Jpeg, int Quality, int SizeX, int SizeY); bool execFuncSetVideoWindow(int x, int y, int w, int h, int wRef, int hRef); bool execFuncPlayExternal(const char *const fileName = 0); bool showNoSignal(); void pause(bool doPause = true); void freeze(bool doFreeze = true); void ignore(bool doIgnore = true); void flush(); void LockOsdUpdate(); void ReshowCurrentOSD(const int frameLeft, const int frameTop, const int frameWidth, const int frameHeight, const int frameZoomX, const int frameZoomY, const bool unlockOsdUpdate = false); void ReshowCurrentOSD(const bool unlockOsdUpdate = false) { ReshowCurrentOSD(-1, -1, -1, -1, -1, -1, unlockOsdUpdate); } void DiscontinuityDetected(); }; }; #endif //__XINELIB_H xine-0.9.4/vdr172remux.c0000644000000000000000000035341411246320600013443 0ustar rootroot #include #ifndef APIVERSNUM #define APIVERSNUM VDRVERSNUM #endif #if APIVERSNUM >= 10703 /* * remux.c: A streaming MPEG2 remultiplexer * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * The parts of this code that implement cTS2PES have been taken from * the Linux DVB driver's 'tuxplayer' example and were rewritten to suit * VDR's needs. * * The cRepacker family's code was originally written by Reinhard Nissl , * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de. * * $Id: remux.c 2.2 2008/12/13 14:30:15 kls Exp $ */ #include "vdr172remux.h" #include #include #include #if 0 #include "libsi/si.h" #include "libsi/section.h" #include "libsi/descriptor.h" #endif #include #include #include #include "vdr172h264parser.h" #if APIVERSNUM >= 10703 #define FRAMESPERSEC ((int)(DEFAULTFRAMESPERSECOND)) #endif namespace vdr172 { #if 0 // Set this to 'true' for debug output: static bool DebugPatPmt = false; #define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a) #endif ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader) { if (Count < 7) return phNeedMoreData; // too short if ((Data[6] & 0xC0) == 0x80) { // MPEG 2 if (Count < 9) return phNeedMoreData; // too short PesPayloadOffset = 6 + 3 + Data[8]; if (Count < PesPayloadOffset) return phNeedMoreData; // too short if (ContinuationHeader) *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]); return phMPEG2; // MPEG 2 } // check for MPEG 1 ... PesPayloadOffset = 6; // skip up to 16 stuffing bytes for (int i = 0; i < 16; i++) { if (Data[PesPayloadOffset] != 0xFF) break; if (Count <= ++PesPayloadOffset) return phNeedMoreData; // too short } // skip STD_buffer_scale/size if ((Data[PesPayloadOffset] & 0xC0) == 0x40) { PesPayloadOffset += 2; if (Count <= PesPayloadOffset) return phNeedMoreData; // too short } if (ContinuationHeader) *ContinuationHeader = false; if ((Data[PesPayloadOffset] & 0xF0) == 0x20) { // skip PTS only PesPayloadOffset += 5; } else if ((Data[PesPayloadOffset] & 0xF0) == 0x30) { // skip PTS and DTS PesPayloadOffset += 10; } else if (Data[PesPayloadOffset] == 0x0F) { // continuation header PesPayloadOffset++; if (ContinuationHeader) *ContinuationHeader = true; } else return phInvalid; // unknown if (Count < PesPayloadOffset) return phNeedMoreData; // too short return phMPEG1; // MPEG 1 } // --- cRepacker ------------------------------------------------------------- #define MIN_LOG_INTERVAL 10 // min. # of seconds between two consecutive log messages of a cRepacker #define LOG(a...) (LogAllowed() && (esyslog(a), true)) class cRepacker { protected: bool initiallySyncing; int maxPacketSize; uint8_t subStreamId; time_t lastLog; int suppressedLogMessages; bool LogAllowed(void); void DroppedData(const char *Reason, int Count) { LOG("%s (dropped %d bytes)", Reason, Count); } virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded); public: static int PutAllOrNothing(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded); cRepacker(void); virtual ~cRepacker() {} virtual void Reset(void) { initiallySyncing = true; } virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0; virtual int BreakAt(const uchar *Data, int Count) = 0; virtual int QuerySnoopSize(void) { return 0; } void SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; } void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; } }; cRepacker::cRepacker(void) { initiallySyncing = true; maxPacketSize = 6 + 65535; subStreamId = 0; suppressedLogMessages = 0;; lastLog = 0; } bool cRepacker::LogAllowed(void) { bool Allowed = time(NULL) - lastLog >= MIN_LOG_INTERVAL; lastLog = time(NULL); if (Allowed) { if (suppressedLogMessages) { esyslog("%d cRepacker messages suppressed", suppressedLogMessages); suppressedLogMessages = 0; } } else suppressedLogMessages++; return Allowed; } int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded) { return PutAllOrNothing(ResultBuffer, Data, Count, CapacityNeeded); } int cRepacker::PutAllOrNothing(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded) { if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) { esyslog("ERROR: possible result buffer overflow, dropped %d out of %d byte", CapacityNeeded, CapacityNeeded); return 0; } int n = ResultBuffer->Put(Data, Count); if (n != Count) esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count); return n; } // --- cCommonRepacker ------------------------------------------------------- class cCommonRepacker : public cRepacker { protected: int skippedBytes; int packetTodo; uchar fragmentData[6 + 65535 + 3]; int fragmentLen; uchar pesHeader[6 + 3 + 255 + 5 + 3]; // 5: H.264 AUD int pesHeaderLen; uchar pesHeaderBackup[6 + 3 + 255]; int pesHeaderBackupLen; uint32_t scanner; uint32_t localScanner; int localStart; bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count); virtual int QuerySnoopSize(void) { return 4; } virtual void Reset(void); }; void cCommonRepacker::Reset(void) { cRepacker::Reset(); skippedBytes = 0; packetTodo = 0; fragmentLen = 0; pesHeaderLen = 0; pesHeaderBackupLen = 0; localStart = -1; } bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) { // enter packet length into PES header ... if (fragmentLen > 0) { // ... which is contained in the fragment buffer // determine PES packet payload int PacketLen = fragmentLen + Count - 6; fragmentData[ 4 ] = PacketLen >> 8; fragmentData[ 5 ] = PacketLen & 0xFF; // just skip packets with no payload int PesPayloadOffset = 0; if (AnalyzePesHeader(fragmentData, fragmentLen, PesPayloadOffset) <= phInvalid) LOG("cCommonRepacker: invalid PES packet encountered in fragment buffer!"); else if (6 + PacketLen <= PesPayloadOffset) { fragmentLen = 0; return true; // skip empty packet } // amount of data to put into result buffer: a negative Count value means // to strip off any partially contained start code. int Bite = fragmentLen + (Count >= 0 ? 0 : Count); // put data into result buffer int n = Put(ResultBuffer, fragmentData, Bite, 6 + PacketLen); fragmentLen = 0; if (n != Bite) return false; } else if (pesHeaderLen > 0) { // ... which is contained in the PES header buffer int PacketLen = pesHeaderLen + Count - 6; pesHeader[ 4 ] = PacketLen >> 8; pesHeader[ 5 ] = PacketLen & 0xFF; // just skip packets with no payload int PesPayloadOffset = 0; if (AnalyzePesHeader(pesHeader, pesHeaderLen, PesPayloadOffset) <= phInvalid) LOG("cCommonRepacker: invalid PES packet encountered in header buffer!"); else if (6 + PacketLen <= PesPayloadOffset) { pesHeaderLen = 0; return true; // skip empty packet } // amount of data to put into result buffer: a negative Count value means // to strip off any partially contained start code. int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count); // put data into result buffer int n = Put(ResultBuffer, pesHeader, Bite, 6 + PacketLen); pesHeaderLen = 0; if (n != Bite) return false; } // append further payload if (Count > 0) { // amount of data to put into result buffer int Bite = Count; // put data into result buffer int n = Put(ResultBuffer, Data, Bite, Bite); if (n != Bite) return false; } // we did it ;-) return true; } // --- cAudGenerator --------------------------------------------------------- class cAudGenerator { private: H264::cSimpleBuffer buffer; int overflowByteCount; H264::cSliceHeader::eAccessUnitType accessUnitType; int sliceTypes; public: cAudGenerator(void); void CollectSliceType(const H264::cSliceHeader *SH); int CollectData(const uchar *Data, int Count); void Generate(cRingBufferLinear *const ResultBuffer); }; cAudGenerator::cAudGenerator() : buffer(MAXFRAMESIZE) { overflowByteCount = 0; accessUnitType = H264::cSliceHeader::Frame; sliceTypes = 0; } int cAudGenerator::CollectData(const uchar *Data, int Count) { // buffer frame data until AUD can be generated int n = buffer.Put(Data, Count); overflowByteCount += (Count - n); // always report "success" as an error message will be shown in Generate() return Count; } void cAudGenerator::CollectSliceType(const H264::cSliceHeader *SH) { if (!SH) return; // remember type of current access unit accessUnitType = SH->GetAccessUnitType(); // translate slice_type into part of primary_pic_type and merge them switch (SH->slice_type) { case 2: // I case 7: // I only => I sliceTypes |= 0x10000; break; case 0: // P case 5: // P only => I, P sliceTypes |= 0x11000; break; case 1: // B case 6: // B only => I, P, B sliceTypes |= 0x11100; break; case 4: // SI case 9: // SI only => SI sliceTypes |= 0x00010; break; case 3: // SP case 8: // SP only => SI, SP sliceTypes |= 0x00011; break; } } void cAudGenerator::Generate(cRingBufferLinear *const ResultBuffer) { int primary_pic_type; // translate the merged primary_pic_type parts into primary_pic_type switch (sliceTypes) { case 0x10000: // I primary_pic_type = 0; break; case 0x11000: // I, P primary_pic_type = 1; break; case 0x11100: // I, P, B primary_pic_type = 2; break; case 0x00010: // SI primary_pic_type = 3; break; case 0x00011: // SI, SP primary_pic_type = 4; break; case 0x10010: // I, SI primary_pic_type = 5; break; case 0x11011: // I, SI, P, SP case 0x10011: // I, SI, SP case 0x11010: // I, SI, P primary_pic_type = 6; break; case 0x11111: // I, SI, P, SP, B case 0x11110: // I, SI, P, B primary_pic_type = 7; break; default: primary_pic_type = -1; // frame without slices? } // drop an incorrect frame if (primary_pic_type < 0) esyslog("ERROR: cAudGenerator::Generate(): dropping frame without slices"); else { // drop a partitial frame if (overflowByteCount > 0) esyslog("ERROR: cAudGenerator::Generate(): frame exceeds MAXFRAMESIZE bytes (required size: %d bytes), dropping frame", buffer.Size() + overflowByteCount); else { int Count; uchar *Data = buffer.Get(Count); int PesPayloadOffset = 0; AnalyzePesHeader(Data, Count, PesPayloadOffset); // enter primary_pic_type into AUD Data[ PesPayloadOffset + 4 ] |= primary_pic_type << 5; // mangle the "start code" to pass the information that this access unit is a // bottom field to ScanVideoPacket() where this modification will be reverted. if (accessUnitType == H264::cSliceHeader::BottomField) Data[ PesPayloadOffset + 3 ] |= 0x80; // store the buffered frame cRepacker::PutAllOrNothing(ResultBuffer, Data, Count, Count); } } // prepare for next run buffer.Clear(); overflowByteCount = 0; sliceTypes = 0; } // --- cVideoRepacker -------------------------------------------------------- #define IPACKS 2048 #define SC_SEQUENCE 0xB3 // "sequence header code" #define SC_GROUP 0xB8 // "group start code" #define SC_PICTURE 0x00 // "picture start code" class cVideoRepacker : public cCommonRepacker { private: enum eState { syncing, findPicture, scanPicture }; int state; bool framePicture; bool pictureExtensionAhead; bool collectChunkData; H264::cSimpleBuffer chunkData; H264::cParser *h264Parser; bool &h264; int sliceSeen; bool audSeen; cAudGenerator *audGenerator; const uchar *startCodeLocations[(IPACKS + 3) / 4]; int startCodeLocationCount; int startCodeLocationIndex; bool startCodeLocationsPrepared; void CheckAudGeneration(bool SliceNalUnitType, bool SyncPoint, const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel); void PushOutCurrentFrameAndStartNewPacket(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel); void HandleNalUnit(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&NalPayload); void HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&ChunkPayload); inline bool ScanDataForStartCodeSlow(const uchar *const Data); inline bool ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit); inline bool ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo, int PesPayloadOffset); inline void AdjustCounters(const int Delta, int &Done, int &Todo); inline bool ScanForEndOfPictureSlow(const uchar *&Data); inline bool ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit); inline bool ScanForEndOfPicture(const uchar *&Data, const uchar *Limit); inline void PushStartCodeLocation(const uchar *Data); inline const uchar *PeekStartCodeLocation(void); inline const uchar *PullStartCodeLocation(void); void CollectData(const uchar *Data, int Count); void BeginCollectingPictureExtension(void); void EndCollectingPictureExtension(void); bool DetermineFramePicture(void); void GenerateFieldPicturesHint(bool FramePicture, const uchar *const Data, const uchar AndMask, const uchar OrMask); void SwitchToMpeg12(void); protected: virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded); public: cVideoRepacker(bool &H264); ~cVideoRepacker(); virtual void Reset(void); virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count); virtual int BreakAt(const uchar *Data, int Count); }; cVideoRepacker::cVideoRepacker(bool &H264) : chunkData(1024) , h264(H264) { // assume H.264 -- we'll fallback to MPEG1/2 when necessary h264 = true; h264Parser = (H264 ? new H264::cParser() : 0); audGenerator = 0; Reset(); } cVideoRepacker::~cVideoRepacker() { delete h264Parser; delete audGenerator; } void cVideoRepacker::Reset(void) { cCommonRepacker::Reset(); if (h264Parser) h264Parser->Reset(); scanner = 0xFFFFFFFF; state = syncing; framePicture = true; pictureExtensionAhead = false; collectChunkData = false; chunkData.Clear(); sliceSeen = -1; audSeen = false; delete audGenerator; audGenerator = 0; startCodeLocationCount = 0; startCodeLocationsPrepared = false; } void cVideoRepacker::SwitchToMpeg12(void) { if (!h264Parser) return; dsyslog("cVideoRepacker: switching to MPEG1/2 mode"); delete h264Parser; h264Parser = 0; delete audGenerator; audGenerator = 0; h264 = false; } int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded) { if (!audGenerator) return cCommonRepacker::Put(ResultBuffer, Data, Count, CapacityNeeded); return audGenerator->CollectData(Data, Count); } void cVideoRepacker::CollectData(const uchar *Data, int Count) { if (h264Parser) h264Parser->PutNalUnitData(Data, Count); else if (collectChunkData) chunkData.Put(Data, Count); } void cVideoRepacker::HandleNalUnit(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&NalPayload) { // check whether we need to fall back to MPEG1/2 if (initiallySyncing) { switch (*Data) { case SC_SEQUENCE: case SC_GROUP: case SC_PICTURE: // the above start codes do not appear in H.264 so let's switch to MPEG1/2 SwitchToMpeg12(); // delegate startcode to appropriate handler HandleStartCode(Data, ResultBuffer, Payload, StreamID, MpegLevel, NalPayload); return; } } // valid NAL units start with a zero bit if (*Data & 0x80) { LOG("cVideoRepacker: found invalid NAL unit: stream seems to be scrambled or not demultiplexed"); return; } // collect NAL unit's remaining data and process it CollectData(NalPayload, Data - 3 - NalPayload); h264Parser->Process(); // which kind of NAL unit have we got? const int nal_unit_type = *Data & 0x1F; switch (nal_unit_type) { case 1: // coded slice of a non-IDR picture case 2: // coded slice data partition A case 5: // coded slice of an IDR picture CheckAudGeneration(true, false, Data, ResultBuffer, Payload, StreamID, MpegLevel); break; case 3: // coded slice data partition B case 4: // coded slice data partition C case 19: // coded slice of an auxiliary coded picture without partitioning break; case 6: // supplemental enhancement information (SEI) case 7: // sequence parameter set case 8: // picture parameter set case 10: // end of sequence case 11: // end of stream case 13: // sequence parameter set extension CheckAudGeneration(false, nal_unit_type == 7, Data, ResultBuffer, Payload, StreamID, MpegLevel); break; case 12: // filler data break; case 14 ... 18: // reserved CheckAudGeneration(false, false, Data, ResultBuffer, Payload, StreamID, MpegLevel); case 20 ... 23: // reserved LOG("cVideoRepacker: found reserved NAL unit type: stream seems to be scrambled"); break; case 0: // unspecified case 24 ... 31: // unspecified LOG("cVideoRepacker: found unspecified NAL unit type: stream seems to be scrambled"); break; case 9: { // access unit delimiter audSeen = true; CheckAudGeneration(false, true, Data, ResultBuffer, Payload, StreamID, MpegLevel); // mangle the "start code" to pass the information "the next access unit will be the // second field of the current frame" to ScanVideoPacket() where this modification // will be reverted. const H264::cSliceHeader *SH = h264Parser->Context().CurrentSlice(); GenerateFieldPicturesHint(!SH || SH->GetAccessUnitType() == H264::cSliceHeader::Frame, Data, 0xFF, 0x80); } break; } // collect 0x00 0x00 0x01 for current NAL unit static const uchar InitPayload[3] = { 0x00, 0x00, 0x01 }; CollectData(InitPayload, sizeof (InitPayload)); NalPayload = Data; } void cVideoRepacker::GenerateFieldPicturesHint(bool FramePicture, const uchar *const Data, const uchar AndMask, const uchar OrMask) { if (FramePicture) framePicture = true; else { framePicture ^= true; // toggle between frame/first field and second field if (!framePicture) { // the last picture was a field so set a hint for this second field *(uchar *)Data &= AndMask; *(uchar *)Data |= OrMask; } } } void cVideoRepacker::CheckAudGeneration(bool SliceNalUnitType, bool SyncPoint, const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel) { // we cannot generate anything until we have reached the synchronisation point if (sliceSeen < 0 && !SyncPoint) return; // detect transition from slice to non-slice NAL units const bool WasSliceSeen = (sliceSeen != false); const bool IsSliceSeen = SliceNalUnitType; sliceSeen = IsSliceSeen; // collect slice types for AUD generation if (WasSliceSeen && audGenerator) audGenerator->CollectSliceType(h264Parser->Context().CurrentSlice()); // handle access unit delimiter at the transition from slice to non-slice NAL units if (WasSliceSeen && !IsSliceSeen) { // an Access Unit Delimiter indicates that the current picture is done. So let's // push out the current frame to start a new packet for the next picture. PushOutCurrentFrameAndStartNewPacket(Data, ResultBuffer, Payload, StreamID, MpegLevel); if (state == findPicture) { // go on with scanning the picture data state++; } // generate the AUD and push out the buffered frame if (audGenerator) { audGenerator->Generate(ResultBuffer); if (audSeen) { // we nolonger need to generate AUDs as they are part of the stream delete audGenerator; audGenerator = 0; } } else if (!audSeen) // we do need to generate AUDs audGenerator = new cAudGenerator; } } void cVideoRepacker::HandleStartCode(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel, const uchar *&ChunkPayload) { // collect chunk's remaining data CollectData(ChunkPayload, Data - 3 - ChunkPayload); // currently, only picture extension is collected, so end collecting now EndCollectingPictureExtension(); // which kind of start code have we got? switch (*Data) { case 0xB9 ... 0xFF: // system start codes LOG("cVideoRepacker: found system start code: stream seems to be scrambled or not demultiplexed"); break; case 0xB0 ... 0xB1: // reserved start codes case 0xB6: LOG("cVideoRepacker: found reserved start code: stream seems to be scrambled"); break; case 0xB4: // sequence error code LOG("cVideoRepacker: found sequence error code: stream seems to be damaged"); case 0xB2: // user data start code break; case 0xB5: // extension start code if (pictureExtensionAhead) { pictureExtensionAhead = false; // mangle the "start code" to pass the information "the next access unit will be the // second field of the current frame" to ScanVideoPacket() where this modification // will be reverted. GenerateFieldPicturesHint(DetermineFramePicture(), Data, 0x00, 0xB9); BeginCollectingPictureExtension(); } break; case 0x00: // picture start code pictureExtensionAhead = true; case 0xB8: // group start code case 0xB3: // sequence header code case 0xB7: // sequence end code // the above start codes indicate that the current picture is done. So let's // push out the current frame to start a new packet for the next picture. PushOutCurrentFrameAndStartNewPacket(Data, ResultBuffer, Payload, StreamID, MpegLevel); break; case 0x01 ... 0xAF: // slice start codes if (state == findPicture) { // go on with scanning the picture data state++; } break; } // collect 0x00 0x00 0x01 for current chunk static const uchar InitPayload[3] = { 0x00, 0x00, 0x01 }; CollectData(InitPayload, sizeof (InitPayload)); ChunkPayload = Data; } void cVideoRepacker::BeginCollectingPictureExtension(void) { chunkData.Clear(); collectChunkData = true; } void cVideoRepacker::EndCollectingPictureExtension(void) { collectChunkData = false; } bool cVideoRepacker::DetermineFramePicture(void) { bool FieldPicture = false; int Count; uchar *Data = chunkData.Get(Count); if (Data && Count >= 7) { if (!Data[0] && !Data[1] && Data[2] == 0x01 && (Data[3] == 0xB5 || Data[3] == 0xB9)) { // extension startcode or hint if ((Data[4] & 0xF0) == 0x80) { // picture coding extension int picture_structure = Data[6] & 0x03; FieldPicture = picture_structure == 0x01 || picture_structure == 0x02; } } } if (FieldPicture) fprintf(stderr, "----- MPEG2 field picture detected ---------------------------\n"); return !FieldPicture; } void cVideoRepacker::PushOutCurrentFrameAndStartNewPacket(const uchar *const Data, cRingBufferLinear *const ResultBuffer, const uchar *&Payload, const uchar StreamID, const ePesHeader MpegLevel) { // synchronisation is detected some bytes after frame start. const int SkippedBytesLimit = 4; if (state == scanPicture) { // picture data has been found so let's push out the current frame. // If the byte count get's negative then the current buffer ends in a // partitial start code that must be stripped off, as it shall be put // in the next packet. PushOutPacket(ResultBuffer, Payload, Data - 3 - Payload); // go on with syncing to the next picture state = syncing; } // when already synced to a picture, just go on collecting data if (state != syncing) return; // we're synced to a picture so prepare a new packet if (initiallySyncing) { // omit report for the typical initial case initiallySyncing = false; isyslog("cVideoRepacker: operating in %s mode", h264Parser ? "H.264" : "MPEG1/2"); } else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes LOG("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit); skippedBytes = 0; // if there is a PES header available, then use it ... if (pesHeaderBackupLen > 0) { // ISO 13818-1 says: // In the case of video, if a PTS is present in a PES packet header // it shall refer to the access unit containing the first picture start // code that commences in this PES packet. A picture start code commences // in PES packet if the first byte of the picture start code is present // in the PES packet. memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen); pesHeaderLen = pesHeaderBackupLen; pesHeaderBackupLen = 0; } else { // ... otherwise create a continuation PES header pesHeaderLen = 0; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x01; pesHeader[pesHeaderLen++] = StreamID; // video stream ID pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown if (MpegLevel == phMPEG2) { pesHeader[pesHeaderLen++] = 0x80; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; } else pesHeader[pesHeaderLen++] = 0x0F; } // add an AUD in H.264 mode when not present in stream if (h264Parser && !audSeen) { pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x01; pesHeader[pesHeaderLen++] = 0x09; // access unit delimiter pesHeader[pesHeaderLen++] = 0x10; // will be filled later } // append the first three bytes of the start code pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x01; // the next packet's payload will begin with the fourth byte of // the start code (= the actual code) Payload = Data; // as there is no length information available, assume the // maximum we can hold in one PES packet packetTodo = maxPacketSize - pesHeaderLen; // go on with finding the picture data state++; } bool cVideoRepacker::ScanDataForStartCodeSlow(const uchar *const Data) { scanner <<= 8; bool FoundStartCode = (scanner == 0x00000100); scanner |= *Data; return FoundStartCode; } bool cVideoRepacker::ScanDataForStartCodeFast(const uchar *&Data, const uchar *Limit) { // We enter here when it is safe to access at least 3 bytes before Data (e. g. // the tail of a previous run) and one byte after Data (xx yy zz [aa] bb {cc}). // On return, Data shall either point to the last valid byte of the block or to // the byte qualifying the start code (00 00 01 [ss]). // As we are searching for 0x01, we've to move pointers (xx yy [zz] aa {bb} cc) // to find start code "aa" for example after a packet boundery. Data--; Limit--; while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) { if (Data[-2] || Data[-1]) Data += 3; else { scanner = 0x00000100 | *++Data; return true; } } Data = Limit; uint32_t *Scanner = (uint32_t *)(Data - 3); scanner = ntohl(*Scanner); return false; } bool cVideoRepacker::ScanDataForStartCode(const uchar *&Data, int &Done, int &Todo, int PesPayloadOffset) { const int ReasonableDataSizeForFastScanning = 12; if (!startCodeLocationsPrepared) { // use slow scanning until it is safe to use fast scanning if (Done < PesPayloadOffset + 3) return ScanDataForStartCodeSlow(Data); // process available data but not more than needed for the current packet int Limit = Todo; if (state != syncing && Limit > packetTodo) Limit = packetTodo; // use slow scanning when there is not enough data left if (Limit < ReasonableDataSizeForFastScanning) return ScanDataForStartCodeSlow(Data); // it's reasonable to use fast scanning const uchar *const DataOrig = Data; bool FoundStartCode = ScanDataForStartCodeFast(Data, Data + Limit); AdjustCounters(Data - DataOrig, Done, Todo); return FoundStartCode; } // process available data but not more than needed for the current packet const uchar *Limit = Data + Todo - 1; if (state != syncing && Todo > packetTodo) { if (packetTodo <= 0) // overfill phase Limit = Data; // do a single ScanDataForStartCodeSlow() below else Limit = Data + packetTodo - 1; } // prepare the "not found" case bool FoundStartCode = false; const uchar *const DataOrig = Data; Data = Limit; // get the next start code location which fits into the limit const uchar *p = PeekStartCodeLocation(); if (p && p <= Limit) { Data = PullStartCodeLocation(); FoundStartCode = true; } // setup the scanner variable int bite = Data - DataOrig; if (bite <= 3) { // to few data, need to do byte shifting for (int i = 0; i <= bite; i++) ScanDataForStartCodeSlow(DataOrig + i); } else { // it's safe to access the last 4 bytes directly uint32_t *Scanner = (uint32_t *)(Data - 3); scanner = ntohl(*Scanner); } AdjustCounters(bite, Done, Todo); return FoundStartCode; } void cVideoRepacker::AdjustCounters(const int Delta, int &Done, int &Todo) { Done += Delta; Todo -= Delta; if (state <= syncing) skippedBytes += Delta; else packetTodo -= Delta; } void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) { // synchronisation is detected some bytes after frame start. const int SkippedBytesLimit = 4; // reset local scanner localStart = -1; int pesPayloadOffset = 0; bool continuationHeader = false; ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader); if (mpegLevel <= phInvalid) { DroppedData("cVideoRepacker: no valid PES packet header found", Count); return; } if (!continuationHeader) { // backup PES header pesHeaderBackupLen = pesPayloadOffset; memcpy(pesHeaderBackup, Data, pesHeaderBackupLen); } // skip PES header int done = pesPayloadOffset; int todo = Count - done; const uchar *data = Data + done; // remember start of the data const uchar *payload = data; const uchar *ChunkPayload = payload; startCodeLocationIndex = 0; while (todo > 0) { // collect number of skipped bytes while syncing if (state <= syncing) skippedBytes++; // did we reach a start code? if (ScanDataForStartCode(data, done, todo, pesPayloadOffset)) { if (h264Parser) HandleNalUnit(data, ResultBuffer, payload, Data[3], mpegLevel, ChunkPayload); else HandleStartCode(data, ResultBuffer, payload, Data[3], mpegLevel, ChunkPayload); } // move on data++; done++; todo--; // do we have to start a new packet as there is no more space left? if (state != syncing && --packetTodo <= 0) { // we connot start a new packet here if the current might end in a start // code and this start code shall possibly be put in the next packet. So // overfill the current packet until we can safely detect that we won't // break a start code into pieces: // // A) the last four bytes were a start code. // B) the current byte introduces a start code. // C) the last three bytes begin a start code. // // Todo : Data : Rule : Result // -----:-------------------------------:------:------- // : XX 00 00 00 01 YY|YY YY YY YY : : // 0 : ^^| : A : push // -----:-------------------------------:------:------- // : XX XX 00 00 00 01|YY YY YY YY : : // 0 : ^^| : B : wait // -1 : |^^ : A : push // -----:-------------------------------:------:------- // : XX XX XX 00 00 00|01 YY YY YY : : // 0 : ^^| : C : wait // -1 : |^^ : B : wait // -2 : | ^^ : A : push // -----:-------------------------------:------:------- // : XX XX XX XX 00 00|00 01 YY YY : : // 0 : ^^| : C : wait // -1 : |^^ : C : wait // -2 : | ^^ : B : wait // -3 : | ^^ : A : push // -----:-------------------------------:------:------- // : XX XX XX XX XX 00|00 00 01 YY : : // 0 : ^^| : C : wait // -1 : |^^ : C : wait // -2 : | ^^ : : push // -----:-------------------------------:------:------- bool A = ((scanner & 0xFFFFFF00) == 0x00000100); bool B = ((scanner & 0xFFFFFF) == 0x000001); bool C = ((scanner & 0xFF) == 0x00) && (packetTodo >= -1); if (A || (!B && !C)) { // actually we cannot push out an overfull packet. So we'll have to // adjust the byte count and payload start as necessary. If the byte // count get's negative we'll have to append the excess from fragment's // tail to the next PES header. int bite = data + packetTodo - payload; const uchar *excessData = fragmentData + fragmentLen + bite; // a negative byte count means to drop some bytes from the current // fragment's tail, to not exceed the maximum packet size. PushOutPacket(ResultBuffer, payload, bite); // create a continuation PES header pesHeaderLen = 0; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x01; pesHeader[pesHeaderLen++] = Data[3]; // video stream ID pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown if (mpegLevel == phMPEG2) { pesHeader[pesHeaderLen++] = 0x80; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; } else pesHeader[pesHeaderLen++] = 0x0F; // copy any excess data while (bite++ < 0) { // append the excess data here pesHeader[pesHeaderLen++] = *excessData++; packetTodo++; } // the next packet's payload will begin here payload = data + packetTodo; // as there is no length information available, assume the // maximum we can hold in one PES packet packetTodo += maxPacketSize - pesHeaderLen; } } } // the packet is done. Now store any remaining data into fragment buffer // if we are no longer syncing. if (state != syncing) { // append the PES header ... int bite = pesHeaderLen; pesHeaderLen = 0; if (bite > 0) { memcpy(fragmentData + fragmentLen, pesHeader, bite); fragmentLen += bite; } // append payload. It may contain part of a start code at it's end, // which will be removed when the next packet gets processed. bite = data - payload; if (bite > 0) { memcpy(fragmentData + fragmentLen, payload, bite); fragmentLen += bite; } } // collect data as needed CollectData(ChunkPayload, data - ChunkPayload); // report that syncing dropped some bytes if (skippedBytes > SkippedBytesLimit) { if (!initiallySyncing) // omit report for the typical initial case LOG("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit); skippedBytes = SkippedBytesLimit; } startCodeLocationCount = 0; } void cVideoRepacker::PushStartCodeLocation(const uchar *Data) { startCodeLocations[startCodeLocationCount++] = Data; } const uchar *cVideoRepacker::PeekStartCodeLocation() { if (startCodeLocationIndex < startCodeLocationCount) return startCodeLocations[startCodeLocationIndex]; return 0; } const uchar *cVideoRepacker::PullStartCodeLocation() { const uchar *p = PeekStartCodeLocation(); if (p != 0) startCodeLocationIndex++; return p; } bool cVideoRepacker::ScanForEndOfPictureSlow(const uchar *&Data) { localScanner <<= 8; if (localScanner != 0x00000100) { localScanner |= *Data++; return false; } PushStartCodeLocation(Data); localScanner |= *Data++; // check start codes which follow picture data if (h264Parser) { int nal_unit_type = localScanner & 0x1F; switch (nal_unit_type) { case 9: // access unit delimiter return true; } } else { switch (localScanner) { case 0x00000100: // picture start code case 0x000001B8: // group start code case 0x000001B3: // sequence header code case 0x000001B7: // sequence end code return true; } } return false; } bool cVideoRepacker::ScanForEndOfPictureFast(const uchar *&Data, const uchar *Limit) { // We enter here when it is safe to access at least 3 bytes before Data (e. g. // the tail of a previous run) and one byte after Data (xx yy zz [aa] bb {cc}). // On return, Data shall either point to the first byte outside of the block or // to the byte following the start code (00 00 01 ss [tt]). // As we are searching for 0x01, we've to move pointers (xx yy [zz] aa {bb} cc) // to find start code "aa" for example after a packet boundery. Data--; Limit--; while (Data < Limit && (Data = (const uchar *)memchr(Data, 0x01, Limit - Data))) { if (Data[-2] || Data[-1]) Data += 3; else { localScanner = 0x00000100 | *++Data; PushStartCodeLocation(Data); // check start codes which follow picture data if (h264Parser) { int nal_unit_type = localScanner & 0x1F; switch (nal_unit_type) { case 9: // access unit delimiter Data++; return true; default: Data += 3; } } else { switch (localScanner) { case 0x00000100: // picture start code case 0x000001B8: // group start code case 0x000001B3: // sequence header code case 0x000001B7: // sequence end code Data++; return true; default: Data += 3; } } } } Data = Limit + 1; uint32_t *LocalScanner = (uint32_t *)(Data - 4); localScanner = ntohl(*LocalScanner); return false; } bool cVideoRepacker::ScanForEndOfPicture(const uchar *&Data, const uchar *Limit) { const int ReasonableDataSizeForFastScanning = 12; const uchar *const ReasonableLimit = Limit - ReasonableDataSizeForFastScanning; bool FoundEndOfPicture = false; while (Data < Limit) { // use slow scanning until it is safe or reasonable to use fast scanning if (localStart < 3 || Data >= ReasonableLimit) { localStart++; if (ScanForEndOfPictureSlow(Data)) { FoundEndOfPicture = true; break; } } else { const uchar *const DataOrig = Data; FoundEndOfPicture = ScanForEndOfPictureFast(Data, Limit); localStart += (Data - DataOrig); break; } } return FoundEndOfPicture; } int cVideoRepacker::BreakAt(const uchar *Data, int Count) { int PesPayloadOffset = 0; if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid) return -1; // not enough data for test // setup local scanner if (localStart < 0) { localScanner = scanner; localStart = 0; } // start where we've stopped at the last run const uchar *data = Data + PesPayloadOffset + localStart; const uchar *limit = Data + Count; // scan data startCodeLocationsPrepared = true; if (ScanForEndOfPicture(data, limit)) { // just detect end of picture if (state == scanPicture && !initiallySyncing) return data - Data; } if (initiallySyncing) return -1; // fill the packet buffer completely until we have synced once // just fill up packet and append next start code return PesPayloadOffset + packetTodo + 4; } // --- cAudioRepacker -------------------------------------------------------- class cAudioRepacker : public cCommonRepacker { private: static int bitRates[2][3][16]; enum eState { syncing, scanFrame }; int state; int frameTodo; int frameSize; int cid; static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = NULL, int *FrameDuration = NULL); public: cAudioRepacker(int Cid); virtual void Reset(void); virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count); virtual int BreakAt(const uchar *Data, int Count); static int GetFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL); }; int cAudioRepacker::bitRates[2][3][16] = { // all values are specified as kbits/s { { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, // MPEG 1, Layer I { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, // MPEG 1, Layer II { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 } // MPEG 1, Layer III }, { { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // MPEG 2, Layer I { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // MPEG 2, Layer II/III { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // MPEG 2, Layer II/III } }; int cAudioRepacker::GetFrameDuration(const uchar *Data, int Count, int *TrackIndex) { int PesPayloadOffset = 0; ePesHeader PH = AnalyzePesHeader(Data, Count, PesPayloadOffset); if (PH < phMPEG1) return -1; const uchar *Payload = Data + PesPayloadOffset; const int PayloadCount = Count - PesPayloadOffset; int FrameDuration = -1; if ((Data[3] & 0xE0) == 0xC0 && PayloadCount >= 4) { if (IsValidAudioHeader(((Payload[0] << 8 | Payload[1]) << 8 | Payload[2]) << 8 | Payload[3], PH == phMPEG2, NULL, &FrameDuration) && TrackIndex) *TrackIndex = Data[3] - 0xC0; } return FrameDuration; } cAudioRepacker::cAudioRepacker(int Cid) { cid = Cid; Reset(); } void cAudioRepacker::Reset(void) { cCommonRepacker::Reset(); scanner = 0; state = syncing; frameTodo = 0; frameSize = 0; } bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize, int *FrameDuration) { int syncword = (Header & 0xFFF00000) >> 20; int id = (Header & 0x00080000) >> 19; int layer = (Header & 0x00060000) >> 17; //int protection_bit = (Header & 0x00010000) >> 16; int bitrate_index = (Header & 0x0000F000) >> 12; int sampling_frequency = (Header & 0x00000C00) >> 10; int padding_bit = (Header & 0x00000200) >> 9; //int private_bit = (Header & 0x00000100) >> 8; //int mode = (Header & 0x000000C0) >> 6; //int mode_extension = (Header & 0x00000030) >> 4; //int copyright = (Header & 0x00000008) >> 3; //int orignal_copy = (Header & 0x00000004) >> 2; int emphasis = (Header & 0x00000003); if (syncword != 0xFFF) return false; if (id == 0 && !Mpeg2) // reserved in MPEG 1 return false; if (layer == 0) // reserved return false; if (bitrate_index == 0xF) // forbidden return false; if (sampling_frequency == 3) // reserved return false; if (emphasis == 2) // reserved return false; if (FrameSize || FrameDuration) { static int samplingFrequencies[2][4] = { // all values are specified in Hz { 44100, 48000, 32000, -1 }, // MPEG 1 { 22050, 24000, 16000, -1 } // MPEG 2 }; static int slots_per_frame[2][3] = { { 12, 144, 144 }, // MPEG 1, Layer I, II, III { 12, 144, 72 } // MPEG 2, Layer I, II, III }; int mpegIndex = 1 - id; int layerIndex = 3 - layer; // Layer I (i. e., layerIndex == 0) has a larger slot size int slotSize = (layerIndex == 0) ? 4 : 1; // bytes int sf = samplingFrequencies[mpegIndex][sampling_frequency]; if (FrameDuration) *FrameDuration = 90000 * 8 * slotSize * slots_per_frame[mpegIndex][layerIndex] / sf; if (FrameSize) { if (bitrate_index == 0) *FrameSize = 0; else { int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots *FrameSize = (N + padding_bit) * slotSize; // bytes } } } return true; } void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) { // synchronisation is detected some bytes after frame start. const int SkippedBytesLimit = 4; // reset local scanner localStart = -1; int pesPayloadOffset = 0; bool continuationHeader = false; ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader); if (mpegLevel <= phInvalid) { DroppedData("cAudioRepacker: no valid PES packet header found", Count); return; } if (!continuationHeader) { // backup PES header pesHeaderBackupLen = pesPayloadOffset; memcpy(pesHeaderBackup, Data, pesHeaderBackupLen); } // skip PES header int done = pesPayloadOffset; int todo = Count - done; const uchar *data = Data + done; // remember start of the data const uchar *payload = data; while (todo > 0) { // collect number of skipped bytes while syncing if (state <= syncing) skippedBytes++; // did we reach an audio frame header? scanner <<= 8; scanner |= *data; if ((scanner & 0xFFF00000) == 0xFFF00000) { if (frameTodo <= 0 && (frameSize == 0 || skippedBytes >= 4) && IsValidAudioHeader(scanner, mpegLevel == phMPEG2, &frameSize)) { if (state == scanFrame) { // As a new audio frame starts here, the previous one is done. So push // out the packet to start a new packet for the next audio frame. If // the byte count gets negative then the current buffer ends in a // partitial audio frame header that must be stripped off, as it shall // be put in the next packet. PushOutPacket(ResultBuffer, payload, data - 3 - payload); // go on with syncing to the next audio frame state = syncing; } if (state == syncing) { if (initiallySyncing) // omit report for the typical initial case initiallySyncing = false; else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes LOG("cAudioRepacker(0x%02X): skipped %d bytes to sync on next audio frame", cid, skippedBytes - SkippedBytesLimit); skippedBytes = 0; // if there is a PES header available, then use it ... if (pesHeaderBackupLen > 0) { // ISO 13818-1 says: // In the case of audio, if a PTS is present in a PES packet header // it shall refer to the access unit commencing in the PES packet. An // audio access unit commences in a PES packet if the first byte of // the audio access unit is present in the PES packet. memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen); pesHeaderLen = pesHeaderBackupLen; pesHeaderBackupLen = 0; } else { // ... otherwise create a continuation PES header pesHeaderLen = 0; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x01; pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown if (mpegLevel == phMPEG2) { pesHeader[pesHeaderLen++] = 0x80; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; } else pesHeader[pesHeaderLen++] = 0x0F; } // append the first three bytes of the audio frame header pesHeader[pesHeaderLen++] = 0xFF; pesHeader[pesHeaderLen++] = (scanner >> 16) & 0xFF; pesHeader[pesHeaderLen++] = (scanner >> 8) & 0xFF; // the next packet's payload will begin with the fourth byte of // the audio frame header (= the actual byte) payload = data; // maximum we can hold in one PES packet packetTodo = maxPacketSize - pesHeaderLen; // expected remainder of audio frame: so far we have read 3 bytes from the frame header frameTodo = frameSize - 3; // go on with collecting the frame's data state++; } } } data++; done++; todo--; // do we have to start a new packet as the current is done? if (frameTodo > 0) { // try to skip most loops for continuous memory int bite = frameTodo; // jump to next audio frame if (bite > packetTodo) bite = packetTodo; // jump only to next output packet if (--bite > todo) bite = todo; // jump only to next input packet // is there enough payload available to load the scanner? if (bite > 0 && done + bite - pesPayloadOffset >= 4) { data += bite; done += bite; todo -= bite; frameTodo -= bite; packetTodo -= bite; uint32_t *Scanner = (uint32_t *)(data - 4); scanner = ntohl(*Scanner); } if (--frameTodo == 0) { // the current audio frame is is done now. So push out the packet to // start a new packet for the next audio frame. PushOutPacket(ResultBuffer, payload, data - payload); // go on with syncing to the next audio frame state = syncing; } } // do we have to start a new packet as there is no more space left? if (state != syncing && --packetTodo <= 0) { // We connot start a new packet here if the current might end in an audio // frame header and this header shall possibly be put in the next packet. So // overfill the current packet until we can safely detect that we won't // break an audio frame header into pieces: // // A) the last four bytes were an audio frame header. // B) the last three bytes introduce an audio frame header. // C) the last two bytes introduce an audio frame header. // D) the last byte introduces an audio frame header. // // Todo : Data : Rule : Result // -----:-------------------------------:------:------- // : XX XX FF Fz zz zz|YY YY YY YY : : // 0 : ^^| : A : push // -----:-------------------------------:------:------- // : XX XX XX FF Fz zz|zz YY YY YY : : // 0 : ^^| : B : wait // -1 : |^^ : A : push // -----:-------------------------------:------:------- // : XX XX XX XX FF Fz|zz zz YY YY : : // 0 : ^^| : C : wait // -1 : |^^ : B : wait // -2 : | ^^ : A : push // -----:-------------------------------:------:------- // : XX XX XX XX XX FF|Fz zz zz YY : : // 0 : ^^| : D : wait // -1 : |^^ : C : wait // -2 : | ^^ : B : wait // -3 : | ^^ : A : push // -----:-------------------------------:------:------- bool A = ((scanner & 0xFFF00000) == 0xFFF00000); bool B = ((scanner & 0xFFF000) == 0xFFF000); bool C = ((scanner & 0xFFF0) == 0xFFF0); bool D = ((scanner & 0xFF) == 0xFF); if (A || (!B && !C && !D)) { // Actually we cannot push out an overfull packet. So we'll have to // adjust the byte count and payload start as necessary. If the byte // count gets negative we'll have to append the excess from fragment's // tail to the next PES header. int bite = data + packetTodo - payload; const uchar *excessData = fragmentData + fragmentLen + bite; // A negative byte count means to drop some bytes from the current // fragment's tail, to not exceed the maximum packet size. PushOutPacket(ResultBuffer, payload, bite); // create a continuation PES header pesHeaderLen = 0; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x01; pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID pesHeader[pesHeaderLen++] = 0x00; // length still unknown pesHeader[pesHeaderLen++] = 0x00; // length still unknown if (mpegLevel == phMPEG2) { pesHeader[pesHeaderLen++] = 0x80; pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = 0x00; } else pesHeader[pesHeaderLen++] = 0x0F; // copy any excess data while (bite++ < 0) { // append the excess data here pesHeader[pesHeaderLen++] = *excessData++; packetTodo++; } // the next packet's payload will begin here payload = data + packetTodo; // as there is no length information available, assume the // maximum we can hold in one PES packet packetTodo += maxPacketSize - pesHeaderLen; } } } // The packet is done. Now store any remaining data into fragment buffer // if we are no longer syncing. if (state != syncing) { // append the PES header ... int bite = pesHeaderLen; pesHeaderLen = 0; if (bite > 0) { memcpy(fragmentData + fragmentLen, pesHeader, bite); fragmentLen += bite; } // append payload. It may contain part of an audio frame header at it's // end, which will be removed when the next packet gets processed. bite = data - payload; if (bite > 0) { memcpy(fragmentData + fragmentLen, payload, bite); fragmentLen += bite; } } // report that syncing dropped some bytes if (skippedBytes > SkippedBytesLimit) { if (!initiallySyncing) // omit report for the typical initial case LOG("cAudioRepacker(0x%02X): skipped %d bytes while syncing on next audio frame", cid, skippedBytes - SkippedBytesLimit); skippedBytes = SkippedBytesLimit; } } int cAudioRepacker::BreakAt(const uchar *Data, int Count) { if (initiallySyncing) return -1; // fill the packet buffer completely until we have synced once int PesPayloadOffset = 0; ePesHeader MpegLevel = AnalyzePesHeader(Data, Count, PesPayloadOffset); if (MpegLevel <= phInvalid) return -1; // not enough data for test // determine amount of data to fill up packet and to append next audio frame header int packetRemainder = PesPayloadOffset + packetTodo + 4; // just detect end of an audio frame if (state == scanFrame) { // when remaining audio frame size is known, then omit scanning if (frameTodo > 0) { // determine amount of data to fill up audio frame and to append next audio frame header int remaining = PesPayloadOffset + frameTodo + 4; if (remaining < packetRemainder) return remaining; return packetRemainder; } // setup local scanner if (localStart < 0) { localScanner = scanner; localStart = 0; } // start where we've stopped at the last run const uchar *data = Data + PesPayloadOffset + localStart; const uchar *limit = Data + Count; // scan data while (data < limit) { localStart++; localScanner <<= 8; localScanner |= *data++; // check whether the next audio frame follows if (((localScanner & 0xFFF00000) == 0xFFF00000) && IsValidAudioHeader(localScanner, MpegLevel == phMPEG2)) return data - Data; } } // just fill up packet and append next audio frame header return packetRemainder; } // --- cDolbyRepacker -------------------------------------------------------- class cDolbyRepacker : public cRepacker { private: static int frameSizes[]; uchar fragmentData[6 + 65535]; int fragmentLen; int fragmentTodo; uchar pesHeader[6 + 3 + 255 + 4 + 4]; int pesHeaderLen; uchar pesHeaderBackup[6 + 3 + 255]; int pesHeaderBackupLen; uchar chk1; uchar chk2; int ac3todo; enum eState { find_0b, find_77, store_chk1, store_chk2, get_length, output_packet }; int state; int skippedBytes; void ResetPesHeader(bool ContinuationFrame = false); void AppendSubStreamID(bool ContinuationFrame = false); bool FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite); bool StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite); public: cDolbyRepacker(void); virtual void Reset(void); virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count); virtual int BreakAt(const uchar *Data, int Count); static int GetFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL); }; // frameSizes are in words, i. e. multiply them by 2 to get bytes int cDolbyRepacker::frameSizes[] = { // fs = 48 kHz 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, 224, 224, 256, 256, 320, 320, 384, 384, 448, 448, 512, 512, 640, 640, 768, 768, 896, 896, 1024, 1024, 1152, 1152, 1280, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // fs = 44.1 kHz 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, 243, 244, 278, 279, 348, 349, 417, 418, 487, 488, 557, 558, 696, 697, 835, 836, 975, 976, 1114, 1115, 1253, 1254, 1393, 1394, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // fs = 32 kHz 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, 288, 336, 336, 384, 384, 480, 480, 576, 576, 672, 672, 768, 768, 960, 960, 1152, 1152, 1344, 1344, 1536, 1536, 1728, 1728, 1920, 1920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; int cDolbyRepacker::GetFrameDuration(const uchar *Data, int Count, int *TrackIndex) { int PesPayloadOffset = 0; ePesHeader PH = AnalyzePesHeader(Data, Count, PesPayloadOffset); if (PH < phMPEG1) return -1; const uchar *Payload = Data + PesPayloadOffset; const int PayloadCount = Count - PesPayloadOffset; if (Data[3] == 0xBD && PayloadCount >= 9 && ((Payload[0] & 0xF0) == 0x80) && Payload[4] == 0x0B && Payload[5] == 0x77 && frameSizes[Payload[8]] > 0) { if (TrackIndex) *TrackIndex = Payload[0] - 0x80; static int samplingFrequencies[4] = { // all values are specified in Hz 48000, 44100, 32000, -1 }; return 90000 * 1536 / samplingFrequencies[Payload[8] >> 6]; } return -1; } cDolbyRepacker::cDolbyRepacker(void) { pesHeader[0] = 0x00; pesHeader[1] = 0x00; pesHeader[2] = 0x01; pesHeader[3] = 0xBD; pesHeader[4] = 0x00; pesHeader[5] = 0x00; Reset(); } void cDolbyRepacker::AppendSubStreamID(bool ContinuationFrame) { if (subStreamId) { pesHeader[pesHeaderLen++] = subStreamId; // number of ac3 frames "starting" in this packet (1 by design). pesHeader[pesHeaderLen++] = 0x01; // offset to start of first ac3 frame (0 means "no ac3 frame starting" // so 1 (by design) addresses the first byte after the next two bytes). pesHeader[pesHeaderLen++] = 0x00; pesHeader[pesHeaderLen++] = (ContinuationFrame ? 0x00 : 0x01); } } void cDolbyRepacker::ResetPesHeader(bool ContinuationFrame) { pesHeader[6] = 0x80; pesHeader[7] = 0x00; pesHeader[8] = 0x00; pesHeaderLen = 9; AppendSubStreamID(ContinuationFrame); } void cDolbyRepacker::Reset(void) { cRepacker::Reset(); ResetPesHeader(); state = find_0b; ac3todo = 0; chk1 = 0; chk2 = 0; fragmentLen = 0; fragmentTodo = 0; pesHeaderBackupLen = 0; skippedBytes = 0; } bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite) { bool success = true; // enough data available to put PES packet into buffer? if (fragmentTodo <= Todo) { // output a previous fragment first if (fragmentLen > 0) { Bite = fragmentLen; int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo); if (Bite != n) success = false; fragmentLen = 0; } Bite = fragmentTodo; if (success) { int n = Put(ResultBuffer, Data, Bite, Bite); if (Bite != n) success = false; } fragmentTodo = 0; // ac3 frame completely processed? if (Bite >= ac3todo) state = find_0b; // go on with finding start of next packet } else { // copy the fragment into separate buffer for later processing Bite = Todo; memcpy(fragmentData + fragmentLen, Data, Bite); fragmentLen += Bite; fragmentTodo -= Bite; } return success; } bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite) { bool success = true; int packetLen = pesHeaderLen + ac3todo; // limit packet to maximum size if (packetLen > maxPacketSize) packetLen = maxPacketSize; pesHeader[4] = (packetLen - 6) >> 8; pesHeader[5] = (packetLen - 6) & 0xFF; Bite = pesHeaderLen; // enough data available to put PES packet into buffer? if (packetLen - pesHeaderLen <= Todo) { int n = Put(ResultBuffer, pesHeader, Bite, packetLen); if (Bite != n) success = false; Bite = packetLen - pesHeaderLen; if (success) { n = Put(ResultBuffer, Data, Bite, Bite); if (Bite != n) success = false; } // ac3 frame completely processed? if (Bite >= ac3todo) state = find_0b; // go on with finding start of next packet } else { fragmentTodo = packetLen; // copy the pesheader into separate buffer for later processing memcpy(fragmentData + fragmentLen, pesHeader, Bite); fragmentLen += Bite; fragmentTodo -= Bite; // copy the fragment into separate buffer for later processing Bite = Todo; memcpy(fragmentData + fragmentLen, Data, Bite); fragmentLen += Bite; fragmentTodo -= Bite; } return success; } void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) { // synchronisation is detected some bytes after frame start. const int SkippedBytesLimit = 4; // check for MPEG 2 if ((Data[6] & 0xC0) != 0x80) { DroppedData("cDolbyRepacker: MPEG 2 PES header expected", Count); return; } // backup PES header if (Data[6] != 0x80 || Data[7] != 0x00 || Data[8] != 0x00) { pesHeaderBackupLen = 6 + 3 + Data[8]; memcpy(pesHeaderBackup, Data, pesHeaderBackupLen); } // skip PES header int done = 6 + 3 + Data[8]; int todo = Count - done; const uchar *data = Data + done; // look for 0x0B 0x77 while (todo > 0) { switch (state) { case find_0b: if (*data == 0x0B) { state++; // copy header information once for later use if (pesHeaderBackupLen > 0) { pesHeaderLen = pesHeaderBackupLen; pesHeaderBackupLen = 0; memcpy(pesHeader, pesHeaderBackup, pesHeaderLen); AppendSubStreamID(); } } data++; done++; todo--; skippedBytes++; // collect number of skipped bytes while syncing continue; case find_77: if (*data != 0x77) { state = find_0b; continue; } data++; done++; todo--; skippedBytes++; // collect number of skipped bytes while syncing state++; continue; case store_chk1: chk1 = *data++; done++; todo--; skippedBytes++; // collect number of skipped bytes while syncing state++; continue; case store_chk2: chk2 = *data++; done++; todo--; skippedBytes++; // collect number of skipped bytes while syncing state++; continue; case get_length: ac3todo = 2 * frameSizes[*data]; // frameSizeCode was invalid => restart searching if (ac3todo <= 0) { // reset PES header instead of using a wrong one ResetPesHeader(); if (chk1 == 0x0B) { if (chk2 == 0x77) { state = store_chk1; continue; } if (chk2 == 0x0B) { state = find_77; continue; } state = find_0b; continue; } if (chk2 == 0x0B) { state = find_77; continue; } state = find_0b; continue; } if (initiallySyncing) // omit report for the typical initial case initiallySyncing = false; else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes { //{ char cmd[500]; sprintf(cmd, "ddd /soft/vdr-" APIVERSION "/bin/vdr %d", getpid()); system(cmd); sleep(10); } //should never happen! LOG("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit); } skippedBytes = 0; // append read data to header for common output processing pesHeader[pesHeaderLen++] = 0x0B; pesHeader[pesHeaderLen++] = 0x77; pesHeader[pesHeaderLen++] = chk1; pesHeader[pesHeaderLen++] = chk2; ac3todo -= 4; state++; // fall through to output case output_packet: { int bite = 0; // finish remainder of ac3 frame? if (fragmentTodo > 0) FinishRemainder(ResultBuffer, data, todo, bite); else { // start a new packet StartNewPacket(ResultBuffer, data, todo, bite); // prepare for next (continuation) packet ResetPesHeader(state == output_packet); } data += bite; done += bite; todo -= bite; ac3todo -= bite; } } } // report that syncing dropped some bytes if (skippedBytes > SkippedBytesLimit) { if (!initiallySyncing) // omit report for the typical initial case LOG("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4); skippedBytes = SkippedBytesLimit; } } int cDolbyRepacker::BreakAt(const uchar *Data, int Count) { if (initiallySyncing) return -1; // fill the packet buffer completely until we have synced once // enough data for test? if (Count < 6 + 3) return -1; // check for MPEG 2 if ((Data[6] & 0xC0) != 0x80) return -1; int headerLen = Data[8] + 6 + 3; // break after fragment tail? if (ac3todo > 0) return headerLen + ac3todo; // enough data for test? if (Count < headerLen + 5) return -1; const uchar *data = Data + headerLen; // break after ac3 frame? if (data[0] == 0x0B && data[1] == 0x77 && frameSizes[data[4]] > 0) return headerLen + 2 * frameSizes[data[4]]; return -1; } // --- cTS2PES --------------------------------------------------------------- #include //XXX TODO: these should really be available in some driver header file! #define PROG_STREAM_MAP 0xBC #ifndef PRIVATE_STREAM1 #define PRIVATE_STREAM1 0xBD #endif #define PADDING_STREAM 0xBE #ifndef PRIVATE_STREAM2 #define PRIVATE_STREAM2 0xBF #endif #define AUDIO_STREAM_S 0xC0 #define AUDIO_STREAM_E 0xDF #define VIDEO_STREAM_S 0xE0 #define VIDEO_STREAM_E 0xEF #define ECM_STREAM 0xF0 #define EMM_STREAM 0xF1 #define DSM_CC_STREAM 0xF2 #define ISO13522_STREAM 0xF3 #define PROG_STREAM_DIR 0xFF //pts_dts flags #define PTS_ONLY 0x80 #define PID_MASK_HI 0x1F #define CONT_CNT_MASK 0x0F // Flags: #define PAY_LOAD 0x10 #define ADAPT_FIELD 0x20 #define PAY_START 0x40 #define TS_ERROR 0x80 #define MAX_PLENGTH 0xFFFF // the maximum PES packet length (theoretically) #define MMAX_PLENGTH (64*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland or HDTV 1920x1080 #define IPACKS 2048 // Start codes: #define SC_SEQUENCE 0xB3 // "sequence header code" #define SC_GROUP 0xB8 // "group start code" #define SC_PICTURE 0x00 // "picture start code" #define MAXNONUSEFULDATA (10*1024*1024) #define MAXNUMUPTERRORS 10 class cTS2PES { private: int pid; int size; int found; int count; uint8_t *buf; uint8_t cid; uint8_t rewriteCid; uint8_t subStreamId; int plength; uint8_t plen[2]; uint8_t flag1; uint8_t flag2; uint8_t hlength; int mpeg; uint8_t check; int mpeg1_required; int mpeg1_stuffing; bool done; cRingBufferLinear *resultBuffer; int tsErrors; int ccErrors; int ccCounter; cRepacker *repacker; static uint8_t headr[]; void store(uint8_t *Data, int Count); void reset_ipack(void); void send_ipack(void); void write_ipack(const uint8_t *Data, int Count); void instant_repack(const uint8_t *Buf, int Count); public: cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid = 0x00, uint8_t SubStreamId = 0x00, cRepacker *Repacker = NULL); ~cTS2PES(); int Pid(void) { return pid; } void ts_to_pes(const uint8_t *Buf); // don't need count (=188) void Clear(void); }; uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 }; cTS2PES::cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t RewriteCid, uint8_t SubStreamId, cRepacker *Repacker) { pid = Pid; resultBuffer = ResultBuffer; size = Size; rewriteCid = RewriteCid; subStreamId = SubStreamId; repacker = Repacker; if (repacker) { repacker->SetMaxPacketSize(size); repacker->SetSubStreamId(subStreamId); size += repacker->QuerySnoopSize(); } tsErrors = 0; ccErrors = 0; ccCounter = -1; if (!(buf = MALLOC(uint8_t, size))) esyslog("Not enough memory for ts_transform"); reset_ipack(); } cTS2PES::~cTS2PES() { if (tsErrors || ccErrors) dsyslog("cTS2PES got %d TS errors, %d TS continuity errors", tsErrors, ccErrors); free(buf); delete repacker; } void cTS2PES::Clear(void) { reset_ipack(); if (repacker) repacker->Reset(); } void cTS2PES::store(uint8_t *Data, int Count) { if (repacker) repacker->Repack(resultBuffer, Data, Count); else cRepacker::PutAllOrNothing(resultBuffer, Data, Count, Count); } void cTS2PES::reset_ipack(void) { found = 0; cid = 0; plength = 0; flag1 = 0; flag2 = 0; hlength = 0; mpeg = 0; check = 0; mpeg1_required = 0; mpeg1_stuffing = 0; done = false; count = 0; } void cTS2PES::send_ipack(void) { if (count <= ((mpeg == 2) ? 9 : 7)) // skip empty packets return; buf[3] = rewriteCid ? rewriteCid : cid; buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8); buf[5] = (uint8_t)((count - 6) & 0x00FF); store(buf, count); switch (mpeg) { case 2: buf[6] = 0x80; buf[7] = 0x00; buf[8] = 0x00; count = 9; if (!repacker && subStreamId) { buf[9] = subStreamId; buf[10] = 1; buf[11] = 0; buf[12] = 1; count = 13; } break; case 1: buf[6] = 0x0F; count = 7; break; } } void cTS2PES::write_ipack(const uint8_t *Data, int Count) { if (count < 6) { memcpy(buf, headr, 3); count = 6; } // determine amount of data to process int bite = Count; if (count + bite > size) bite = size - count; if (repacker) { int breakAt = repacker->BreakAt(buf, count); // avoid memcpy of data after break location if (0 <= breakAt && breakAt < count + bite) { bite = breakAt - count; if (bite < 0) // should never happen bite = 0; } } memcpy(buf + count, Data, bite); count += bite; if (repacker) { // determine break location int breakAt = repacker->BreakAt(buf, count); if (breakAt > size) // won't fit into packet? breakAt = -1; if (breakAt > count) // not enough data? breakAt = -1; // push out data before break location if (breakAt > 0) { // adjust bite if above memcpy was to large bite -= count - breakAt; count = breakAt; send_ipack(); // recurse for data after break location if (Count - bite > 0) write_ipack(Data + bite, Count - bite); } } // push out data when buffer is full if (count >= size) { send_ipack(); // recurse for remaining data if (Count - bite > 0) write_ipack(Data + bite, Count - bite); } } void cTS2PES::instant_repack(const uint8_t *Buf, int Count) { int c = 0; while (c < Count && (mpeg == 0 || (mpeg == 1 && found < mpeg1_required) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) { switch (found ) { case 0: case 1: if (Buf[c] == 0x00) found++; else found = 0; c++; break; case 2: if (Buf[c] == 0x01) found++; else if (Buf[c] != 0) found = 0; c++; break; case 3: cid = 0; switch (Buf[c]) { case PROG_STREAM_MAP: case PRIVATE_STREAM2: case PROG_STREAM_DIR: case ECM_STREAM : case EMM_STREAM : case PADDING_STREAM : case DSM_CC_STREAM : case ISO13522_STREAM: done = true; case PRIVATE_STREAM1: case VIDEO_STREAM_S ... VIDEO_STREAM_E: case AUDIO_STREAM_S ... AUDIO_STREAM_E: found++; cid = Buf[c++]; break; default: found = 0; break; } break; case 4: if (Count - c > 1) { unsigned short *pl = (unsigned short *)(Buf + c); plength = ntohs(*pl); c += 2; found += 2; mpeg1_stuffing = 0; } else { plen[0] = Buf[c]; found++; return; } break; case 5: { plen[1] = Buf[c++]; unsigned short *pl = (unsigned short *)plen; plength = ntohs(*pl); found++; mpeg1_stuffing = 0; } break; case 6: if (!done) { flag1 = Buf[c++]; found++; if (mpeg1_stuffing == 0) { // first stuffing iteration: determine MPEG level if ((flag1 & 0xC0) == 0x80) mpeg = 2; else { mpeg = 1; mpeg1_required = 7; } } if (mpeg == 1) { if (flag1 == 0xFF) { // MPEG1 stuffing if (++mpeg1_stuffing > 16) found = 0; // invalid MPEG1 header else { // ignore stuffing found--; if (plength > 0) plength--; } } else if ((flag1 & 0xC0) == 0x40) // STD_buffer_scale/size mpeg1_required += 2; else if (flag1 != 0x0F && (flag1 & 0xF0) != 0x20 && (flag1 & 0xF0) != 0x30) found = 0; // invalid MPEG1 header else { flag2 = 0; hlength = 0; } } } break; case 7: if (!done && (mpeg == 2 || mpeg1_required > 7)) { flag2 = Buf[c++]; found++; } break; case 8: if (!done && (mpeg == 2 || mpeg1_required > 7)) { hlength = Buf[c++]; found++; if (mpeg == 1 && hlength != 0x0F && (hlength & 0xF0) != 0x20 && (hlength & 0xF0) != 0x30) found = 0; // invalid MPEG1 header } break; default: break; } } if (!plength) plength = MMAX_PLENGTH - 6; if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= mpeg1_required))) { switch (cid) { case AUDIO_STREAM_S ... AUDIO_STREAM_E: case VIDEO_STREAM_S ... VIDEO_STREAM_E: case PRIVATE_STREAM1: if (mpeg == 2 && found == 9 && count < found) { // make sure to not write the data twice by looking at count write_ipack(&flag1, 1); write_ipack(&flag2, 1); write_ipack(&hlength, 1); } if (mpeg == 1 && found == mpeg1_required && count < found) { // make sure to not write the data twice by looking at count write_ipack(&flag1, 1); if (mpeg1_required > 7) { write_ipack(&flag2, 1); write_ipack(&hlength, 1); } } if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) { while (c < Count && found < 14) { write_ipack(Buf + c, 1); c++; found++; } if (c == Count) return; } if (!repacker && subStreamId) { while (c < Count && found < (hlength + 9) && found < plength + 6) { write_ipack(Buf + c, 1); c++; found++; } if (found == (hlength + 9)) { uchar sbuf[] = { 0x01, 0x00, 0x00 }; write_ipack(&subStreamId, 1); write_ipack(sbuf, 3); } } while (c < Count && found < plength + 6) { int l = Count - c; if (l + found > plength + 6) l = plength + 6 - found; write_ipack(Buf + c, l); found += l; c += l; } break; } if (done) { if (found + Count - c < plength + 6) { found += Count - c; c = Count; } else { c += plength + 6 - found; found = plength + 6; } } if (plength && found == plength + 6) { if (plength == MMAX_PLENGTH - 6) esyslog("ERROR: PES packet length overflow in remuxer (stream corruption)"); send_ipack(); reset_ipack(); if (c < Count) instant_repack(Buf + c, Count - c); } } return; } void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188) { if (!Buf) return; if (Buf[1] & TS_ERROR) tsErrors++; if (!(Buf[3] & (ADAPT_FIELD | PAY_LOAD))) return; // discard TS packet with adaption_field_control set to '00'. if ((Buf[3] & PAY_LOAD) && ((Buf[3] ^ ccCounter) & CONT_CNT_MASK)) { // This should check duplicates and packets which do not increase the counter. // But as the errors usually come in bursts this should be enough to // show you there is something wrong with signal quality. if (ccCounter != -1 && ((Buf[3] ^ (ccCounter + 1)) & CONT_CNT_MASK)) { ccErrors++; // Enable this if you are having problems with signal quality. // These are the errors I used to get with Nova-T when antenna // was not positioned correcly (not transport errors). //tvr dsyslog("TS continuity error (%d)", ccCounter); } ccCounter = Buf[3] & CONT_CNT_MASK; } if (Buf[1] & PAY_START) { if (found > 6) { if (plength != MMAX_PLENGTH - 6 && plength != found - 6) dsyslog("PES packet shortened to %d bytes (expected: %d bytes)", found, plength + 6); plength = found - 6; send_ipack(); } reset_ipack(); } uint8_t off = 0; if (Buf[3] & ADAPT_FIELD) { // adaptation field? off = Buf[4] + 1; if (off + 4 > 187) return; } if (Buf[3] & PAY_LOAD) instant_repack(Buf + 4 + off, TS_SIZE - 4 - off); } // --- cAudioIndexer --------------------------------------------------------- class cAudioIndexer { private: int frameTrack; int frameDuration; int64_t trackTime[MAXAPIDS + MAXDPIDS]; int64_t nextIndexTime; public: cAudioIndexer(void); void Clear(void); void PrepareFrame(const uchar *Data, int Count, int Offset, uchar &PictureType); void ProcessFrame(void); }; cAudioIndexer::cAudioIndexer(void) { Clear(); } void cAudioIndexer::Clear(void) { memset(trackTime, 0, sizeof (trackTime)); nextIndexTime = 0; frameTrack = -1; } void cAudioIndexer::PrepareFrame(const uchar *Data, int Count, int Offset, uchar &PictureType) { frameDuration = cRemux::GetAudioFrameDuration(Data + Offset, Count - Offset, &frameTrack); if (frameDuration <= 0) return; if (Data[Offset + 3] == 0xBD) frameTrack += MAXAPIDS; PictureType = (trackTime[frameTrack] >= nextIndexTime) ? I_FRAME : NO_PICTURE; } void cAudioIndexer::ProcessFrame(void) { if (frameTrack < 0) return; if (trackTime[frameTrack] >= nextIndexTime) nextIndexTime += 90000 / FRAMESPERSEC; trackTime[frameTrack] += frameDuration; frameTrack = -1; } // --- cRingBufferLinearPes -------------------------------------------------- class cRingBufferLinearPes : public cRingBufferLinear { protected: virtual int DataReady(const uchar *Data, int Count); public: cRingBufferLinearPes(int Size, int Margin = 0, bool Statistics = false, const char *Description = NULL) :cRingBufferLinear(Size, Margin, Statistics, Description) {} }; int cRingBufferLinearPes::DataReady(const uchar *Data, int Count) { int c = cRingBufferLinear::DataReady(Data, Count); if (!c && Count >= 6) { if (!Data[0] && !Data[1] && Data[2] == 0x01) { int Length = 6 + Data[4] * 256 + Data[5]; if (Length <= Count) return Length; } } return c; } // --- cRemux ---------------------------------------------------------------- #define RESULTBUFFERSIZE KILOBYTE(256) cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure, bool SyncEarly) { h264 = false; exitOnFailure = ExitOnFailure; noVideo = VPid == 0 || VPid == 1 || VPid == 0x1FFF; numUPTerrors = 0; synced = false; syncEarly = SyncEarly; skipped = 0; numTracks = 0; resultSkipped = 0; resultBuffer = new cRingBufferLinearPes(RESULTBUFFERSIZE, IPACKS, false, "Result"); resultBuffer->SetTimeouts(0, 100); if (VPid) #define TEST_cVideoRepacker #ifdef TEST_cVideoRepacker ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0, 0x00, new cVideoRepacker(h264)); #else ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0xE0); #endif if (APids) { int n = 0; while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS) { #define TEST_cAudioRepacker #ifdef TEST_cAudioRepacker ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n, 0x00, new cAudioRepacker(0xC0 + n)); n++; #else ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++); #endif } } if (DPids) { int n = 0; while (*DPids && numTracks < MAXTRACKS && n < MAXDPIDS) ts2pes[numTracks++] = new cTS2PES(*DPids++, resultBuffer, IPACKS, 0x00, 0x80 + n++, new cDolbyRepacker); } if (SPids) { int n = 0; while (*SPids && numTracks < MAXTRACKS && n < MAXSPIDS) ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x20 + n++); } audioIndexer = (noVideo ? new cAudioIndexer : NULL); } cRemux::~cRemux() { for (int t = 0; t < numTracks; t++) delete ts2pes[t]; delete resultBuffer; delete audioIndexer; } int cRemux::GetAudioFrameDuration(const uchar *Data, int Count, int *TrackIndex) { if (Count <= 4) return -1; if (Data[3] == 0xBD) return cDolbyRepacker::GetFrameDuration(Data, Count, TrackIndex); return cAudioRepacker::GetFrameDuration(Data, Count, TrackIndex); } int cRemux::GetPid(const uchar *Data) { return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF); } int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset) { // Returns the length of the packet starting at Offset, or -1 if Count is // too small to contain the entire packet. int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1; if (Length > 0 && Offset + Length <= Count) return Length; return -1; } bool cRemux::IsFrameH264(const uchar *Data, int Length) { int PesPayloadOffset; const uchar *limit = Data + Length; if (AnalyzePesHeader(Data, Length, PesPayloadOffset) <= phInvalid) return false; // neither MPEG1 nor MPEG2 Data += PesPayloadOffset + 3; // move to video payload and skip 00 00 01 if (Data < limit) { // cVideoRepacker ensures that in case of H264 we will see an access unit delimiter here if (0x01 == Data[-1] && 9 == Data[0] && 0x00 == Data[-2] && 0x00 == Data[-3]) return true; } return false; } int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType) { // Scans the video packet starting at Offset and returns its length. // If the return value is -1 the packet was not completely in the buffer. int Length = GetPacketLength(Data, Count, Offset); if (Length > 0) { #ifdef TEST_cVideoRepacker bool FoundPicture = false; bool FoundPictureCodingExtension = false; #endif int PesPayloadOffset = 0; if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) { const uchar *p = Data + Offset + PesPayloadOffset + 2; const uchar *pLimit = Data + Offset + Length - 3; #ifdef TEST_cVideoRepacker // cVideoRepacker ensures that a new PES packet is started for a new sequence, // group or picture which allows us to easily skip scanning through a huge // amount of video data. if (p < pLimit) { if (p[-2] || p[-1] || p[0] != 0x01) pLimit = 0; // skip scanning: packet doesn't start with 0x000001 else { if (h264) { int nal_unit_type = p[1] & 0x1F; switch (nal_unit_type) { case 9: // access unit delimiter // when the MSB in p[1] is set (which violates H.264) then this is a hint // from cVideoRepacker that this second field of the current frame shall // not be reported as picture. if (p[1] & 0x80) ((uchar *)p)[1] &= ~0x80; // revert the hint and fall through else break; default: // skip scanning: packet doesn't start a new picture pLimit = 0; } } else { switch (p[1]) { case SC_SEQUENCE: case SC_GROUP: case SC_PICTURE: break; default: // skip scanning: packet doesn't start a new sequence, group or picture pLimit = 0; } } } } #endif while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) { if (!p[-2] && !p[-1]) { // found 0x000001 if (h264) { int nal_unit_type = p[1] & 0x1F; switch (nal_unit_type) { case 9: { // access unit delimiter int primary_pic_type = p[2] >> 5; switch (primary_pic_type) { case 0: // I case 3: // SI case 5: // I, SI PictureType = I_FRAME; break; case 1: // I, P case 4: // SI, SP case 6: // I, SI, P, SP PictureType = P_FRAME; break; case 2: // I, P, B case 7: // I, SI, P, SP, B PictureType = B_FRAME; break; } return Length; } } } else { switch (p[1]) { case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07; #ifndef TEST_cVideoRepacker return Length; #else FoundPicture = true; break; case 0x01 ... 0xAF: // slice startcodes break; case 0xB5: // extension startcode case 0xB9: // hint from cVideoRepacker if (FoundPicture && p + 2 < pLimit && (p[2] & 0xF0) == 0x80) { // picture coding extension FoundPictureCodingExtension = true; // using 0xB9 instead of 0xB5 for an expected picture coding extension // is a hint from cVideoRepacker that this second field of the current // frame shall not be reported as picture. if (p[1] == 0xB9) { ((uchar *)p)[1] = 0xB5; // revert the hint pLimit = 0; // return with NO_PICTURE below } else return Length; } break; #endif } } p += 4; // continue scanning after 0x01ssxxyy } else p += 3; // continue scanning after 0x01xxyy } } #ifdef TEST_cVideoRepacker if (!FoundPicture || FoundPictureCodingExtension) #endif PictureType = NO_PICTURE; return Length; } return -1; } int cRemux::Put(const uchar *Data, int Count) { int used = 0; // Make sure we are looking at a TS packet: while (Count > TS_SIZE) { if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE) break; Data++; Count--; used++; } if (used) esyslog("ERROR: skipped %d byte to sync on TS packet", used); // Convert incoming TS data into multiplexed PES: for (int i = 0; i < Count; i += TS_SIZE) { if (Count - i < TS_SIZE) break; if (Data[i] != TS_SYNC_BYTE) break; if (resultBuffer->Free() < 2 * IPACKS) break; // A cTS2PES might write one full packet and also a small rest int pid = GetPid(Data + i + 1); if (Data[i + 3] & 0x10) { // got payload for (int t = 0; t < numTracks; t++) { if (ts2pes[t]->Pid() == pid) { ts2pes[t]->ts_to_pes(Data + i); break; } } } used += TS_SIZE; } // Check if we're getting anywhere here: if (!synced && skipped >= 0) { if (skipped > MAXNONUSEFULDATA) { esyslog("ERROR: no useful data seen within %d byte of video stream", skipped); skipped = -1; if (exitOnFailure) ShutdownHandler.RequestEmergencyExit(); } else skipped += used; } return used; } uchar *cRemux::Get(int &Count, uchar *PictureType) { // Remove any previously skipped data from the result buffer: if (resultSkipped > 0) { resultBuffer->Del(resultSkipped); resultSkipped = 0; } #if 0 // Test recording without determining the real frame borders: if (PictureType) *PictureType = I_FRAME; return resultBuffer->Get(Count); #endif // Check for frame borders: if (PictureType) *PictureType = NO_PICTURE; Count = 0; uchar *resultData = NULL; int resultCount = 0; uchar *data = resultBuffer->Get(resultCount); if (data) { for (int i = 0; i < resultCount - 3; i++) { if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) { int l = 0; uchar StreamType = data[i + 3]; if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) { uchar pt = NO_PICTURE; l = ScanVideoPacket(data, resultCount, i, pt); if (l < 0) return resultData; if (pt != NO_PICTURE) { if (pt < I_FRAME || B_FRAME < pt) { esyslog("ERROR: unknown picture type '%d'", pt); if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure) { ShutdownHandler.RequestEmergencyExit(); numUPTerrors = 0; } } else if (!synced) { if (pt == I_FRAME || syncEarly) { if (PictureType) *PictureType = pt; resultSkipped = i; // will drop everything before this position synced = true; if (pt == I_FRAME) // syncEarly: it's ok but there is no need to call SetBrokenLink() SetBrokenLink(data + i, l); else fprintf(stderr, "video: synced early\n"); } } else if (Count) return resultData; else if (PictureType) *PictureType = pt; } } else { //if (AUDIO_STREAM_S <= StreamType && StreamType <= AUDIO_STREAM_E || StreamType == PRIVATE_STREAM1) { l = GetPacketLength(data, resultCount, i); if (l < 0) return resultData; if (noVideo || (!synced && syncEarly)) { uchar pt = NO_PICTURE; if (audioIndexer && !Count) audioIndexer->PrepareFrame(data, resultCount, i, pt); if (!synced) { if (PictureType && noVideo) *PictureType = pt; resultSkipped = i; // will drop everything before this position synced = true; if (!noVideo) fprintf(stderr, "audio: synced early\n"); } else if (Count) return resultData; else if (PictureType) *PictureType = pt; } } if (synced) { if (!Count) resultData = data + i; Count += l; } else resultSkipped = i + l; if (l > 0) i += l - 1; // the loop increments, too } } } return resultData; } void cRemux::Del(int Count) { resultBuffer->Del(Count); if (audioIndexer && Count > 0) audioIndexer->ProcessFrame(); } void cRemux::Clear(void) { for (int t = 0; t < numTracks; t++) ts2pes[t]->Clear(); resultBuffer->Clear(); if (audioIndexer) audioIndexer->Clear(); synced = false; skipped = 0; resultSkipped = 0; } void cRemux::SetBrokenLink(uchar *Data, int Length) { int PesPayloadOffset = 0; if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) { for (int i = PesPayloadOffset; i < Length - 7; i++) { if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) { if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed Data[i + 7] |= 0x20; return; } } dsyslog("SetBrokenLink: no GOP header found in video packet"); } else dsyslog("SetBrokenLink: no video packet in frame"); } #if 0 // --- cPatPmtGenerator ------------------------------------------------------ cPatPmtGenerator::cPatPmtGenerator(void) { numPmtPackets = 0; patCounter = pmtCounter = 0; patVersion = pmtVersion = 0; esInfoLength = NULL; GeneratePat(); } void cPatPmtGenerator::IncCounter(int &Counter, uchar *TsPacket) { TsPacket[3] = (TsPacket[3] & 0xF0) | Counter; if (++Counter > 0x0F) Counter = 0x00; } void cPatPmtGenerator::IncVersion(int &Version) { if (++Version > 0x1F) Version = 0x00; } void cPatPmtGenerator::IncEsInfoLength(int Length) { if (esInfoLength) { Length += ((*esInfoLength & 0x0F) << 8) | *(esInfoLength + 1); *esInfoLength = 0xF0 | (Length >> 8); *(esInfoLength + 1) = Length; } } int cPatPmtGenerator::MakeStream(uchar *Target, uchar Type, int Pid) { int i = 0; Target[i++] = Type; // stream type Target[i++] = 0xE0 | (Pid >> 8); // dummy (3), pid hi (5) Target[i++] = Pid; // pid lo esInfoLength = &Target[i]; Target[i++] = 0xF0; // dummy (4), ES info length hi Target[i++] = 0x00; // ES info length lo return i; } int cPatPmtGenerator::MakeAC3Descriptor(uchar *Target) { int i = 0; Target[i++] = SI::AC3DescriptorTag; Target[i++] = 0x01; // length Target[i++] = 0x00; IncEsInfoLength(i); return i; } int cPatPmtGenerator::MakeSubtitlingDescriptor(uchar *Target, const char *Language) { int i = 0; Target[i++] = SI::SubtitlingDescriptorTag; Target[i++] = 0x08; // length Target[i++] = *Language++; Target[i++] = *Language++; Target[i++] = *Language++; Target[i++] = 0x00; // subtitling type Target[i++] = 0x00; // composition page id hi Target[i++] = 0x01; // composition page id lo Target[i++] = 0x00; // ancillary page id hi Target[i++] = 0x01; // ancillary page id lo IncEsInfoLength(i); return i; } int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language) { int i = 0; Target[i++] = SI::ISO639LanguageDescriptorTag; Target[i++] = 0x04; // length Target[i++] = *Language++; Target[i++] = *Language++; Target[i++] = *Language++; Target[i++] = 0x01; // audio type IncEsInfoLength(i); return i; } int cPatPmtGenerator::MakeCRC(uchar *Target, const uchar *Data, int Length) { int crc = SI::CRC32::crc32((const char *)Data, Length, 0xFFFFFFFF); int i = 0; Target[i++] = crc >> 24; Target[i++] = crc >> 16; Target[i++] = crc >> 8; Target[i++] = crc; return i; } #define P_TSID 0x8008 // pseudo TS ID #define P_PNR 0x0084 // pseudo Program Number #define P_PMT_PID 0x0084 // pseudo PMT pid void cPatPmtGenerator::GeneratePat(void) { memset(pat, 0xFF, sizeof(pat)); uchar *p = pat; int i = 0; p[i++] = 0x47; // TS indicator p[i++] = 0x40; // flags (3), pid hi (5) p[i++] = 0x00; // pid lo p[i++] = 0x10; // flags (4), continuity counter (4) int PayloadStart = i; p[i++] = 0x00; // table id p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4) int SectionLength = i; p[i++] = 0x00; // section length lo (filled in later) p[i++] = P_TSID >> 8; // TS id hi p[i++] = P_TSID & 0xFF; // TS id lo p[i++] = 0xC1 | (patVersion << 1); // dummy (2), version number (5), current/next indicator (1) p[i++] = 0x00; // section number p[i++] = 0x00; // last section number p[i++] = P_PNR >> 8; // program number hi p[i++] = P_PNR & 0xFF; // program number lo p[i++] = 0xE0 | (P_PMT_PID >> 8); // dummy (3), PMT pid hi (5) p[i++] = P_PMT_PID & 0xFF; // PMT pid lo pat[SectionLength] = i - SectionLength - 1 + 4; // -2 = SectionLength storage, +4 = length of CRC MakeCRC(pat + i, pat + PayloadStart, i - PayloadStart); IncVersion(patVersion); } void cPatPmtGenerator::GeneratePmt(tChannelID ChannelID) { // generate the complete PMT section: uchar buf[MAX_SECTION_SIZE]; memset(buf, 0xFF, sizeof(buf)); numPmtPackets = 0; cChannel *Channel = Channels.GetByChannelID(ChannelID); if (Channel) { int Vpid = Channel->Vpid(); uchar *p = buf; int i = 0; p[i++] = 0x02; // table id int SectionLength = i; p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4) p[i++] = 0x00; // section length lo (filled in later) p[i++] = P_PNR >> 8; // program number hi p[i++] = P_PNR & 0xFF; // program number lo p[i++] = 0xC1 | (pmtVersion << 1); // dummy (2), version number (5), current/next indicator (1) p[i++] = 0x00; // section number p[i++] = 0x00; // last section number p[i++] = 0xE0 | (Vpid >> 8); // dummy (3), PCR pid hi (5) p[i++] = Vpid; // PCR pid lo p[i++] = 0xF0; // dummy (4), program info length hi (4) p[i++] = 0x00; // program info length lo if (Vpid) i += MakeStream(buf + i, Channel->Vtype(), Vpid); for (int n = 0; Channel->Apid(n); n++) { i += MakeStream(buf + i, 0x04, Channel->Apid(n)); const char *Alang = Channel->Alang(n); i += MakeLanguageDescriptor(buf + i, Alang); if (Alang[3] == '+') i += MakeLanguageDescriptor(buf + i, Alang + 3); } for (int n = 0; Channel->Dpid(n); n++) { i += MakeStream(buf + i, 0x06, Channel->Dpid(n)); i += MakeAC3Descriptor(buf + i); i += MakeLanguageDescriptor(buf + i, Channel->Dlang(n)); } for (int n = 0; Channel->Spid(n); n++) { i += MakeStream(buf + i, 0x06, Channel->Spid(n)); i += MakeSubtitlingDescriptor(buf + i, Channel->Slang(n)); } int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC buf[SectionLength] |= (sl >> 8) & 0x0F; buf[SectionLength + 1] = sl; MakeCRC(buf + i, buf, i); // split the PMT section into several TS packets: uchar *q = buf; while (i > 0) { uchar *p = pmt[numPmtPackets++]; int j = 0; p[j++] = 0x47; // TS indicator p[j++] = 0x40 | (P_PNR >> 8); // flags (3), pid hi (5) p[j++] = P_PNR & 0xFF; // pid lo p[j++] = 0x10; // flags (4), continuity counter (4) int l = TS_SIZE - j; memcpy(p + j, q, l); q += l; i -= l; } IncVersion(pmtVersion); } else esyslog("ERROR: can't find channel %s", *ChannelID.ToString()); } uchar *cPatPmtGenerator::GetPat(void) { IncCounter(patCounter, pat); return pat; } uchar *cPatPmtGenerator::GetPmt(int &Index) { if (Index < numPmtPackets) { IncCounter(patCounter, pmt[Index]); return pmt[Index++]; } return NULL; } // --- cPatPmtParser --------------------------------------------------------- cPatPmtParser::cPatPmtParser(void) { pmtSize = 0; pmtPid = -1; vpid = vtype = 0; } void cPatPmtParser::ParsePat(const uchar *Data, int Length) { // The PAT is always assumed to fit into a single TS packet SI::PAT Pat(Data, false); if (Pat.CheckCRCAndParse()) { dbgpatpmt("PAT: TSid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pat.getTransportStreamId(), Pat.getCurrentNextIndicator(), Pat.getVersionNumber(), Pat.getSectionNumber(), Pat.getLastSectionNumber()); SI::PAT::Association assoc; for (SI::Loop::Iterator it; Pat.associationLoop.getNext(assoc, it); ) { dbgpatpmt(" isNITPid = %d\n", assoc.isNITPid()); if (!assoc.isNITPid()) { pmtPid = assoc.getPid(); dbgpatpmt(" service id = %d, pid = %d\n", assoc.getServiceId(), assoc.getPid()); } } } else esyslog("ERROR: can't parse PAT"); } void cPatPmtParser::ParsePmt(const uchar *Data, int Length) { // The PMT may extend over several TS packets, so we need to assemble them if (pmtSize == 0) { // this is the first packet if (SectionLength(Data, Length) > Length) { if (Length <= int(sizeof(pmt))) { memcpy(pmt, Data, Length); pmtSize = Length; } else esyslog("ERROR: PMT packet length too big (%d byte)!", Length); return; } // the packet contains the entire PMT section, so we run into the actual parsing } else { // this is a following packet, so we add it to the pmt storage if (Length <= int(sizeof(pmt)) - pmtSize) { memcpy(pmt + pmtSize, Data, Length); pmtSize += Length; } else { esyslog("ERROR: PMT section length too big (%d byte)!", pmtSize + Length); pmtSize = 0; } if (SectionLength(pmt, pmtSize) > pmtSize) return; // more packets to come // the PMT section is now complete, so we run into the actual parsing Data = pmt; } SI::PMT Pmt(Data, false); if (Pmt.CheckCRCAndParse()) { dbgpatpmt("PMT: sid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pmt.getServiceId(), Pmt.getCurrentNextIndicator(), Pmt.getVersionNumber(), Pmt.getSectionNumber(), Pmt.getLastSectionNumber()); dbgpatpmt(" pcr = %d\n", Pmt.getPCRPid()); cDevice::PrimaryDevice()->ClrAvailableTracks(false, true); int NumApids = 0; int NumDpids = 0; int NumSpids = 0; vpid = vtype = 0; SI::PMT::Stream stream; for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) { dbgpatpmt(" stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid()); switch (stream.getStreamType()) { case 0x02: // STREAMTYPE_13818_VIDEO case 0x1B: // MPEG4 vpid = stream.getPid(); vtype = stream.getStreamType(); break; case 0x04: // STREAMTYPE_13818_AUDIO { if (NumApids < MAXAPIDS) { char ALangs[MAXLANGCODE2] = ""; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; SI::ISO639LanguageDescriptor::Language l; char *s = ALangs; int n = 0; for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) { if (*ld->languageCode != '-') { // some use "---" to indicate "none" dbgpatpmt(" '%s'", l.languageCode); if (n > 0) *s++ = '+'; strn0cpy(s, I18nNormalizeLanguageCode(l.languageCode), MAXLANGCODE1); s += strlen(s); if (n++ > 1) break; } } } break; default: ; } delete d; } cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, NumApids, stream.getPid(), ALangs); NumApids++; } } break; case 0x06: // STREAMTYPE_13818_PES_PRIVATE { int dpid = 0; char lang[MAXLANGCODE1] = ""; SI::Descriptor *d; for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) { switch (d->getDescriptorTag()) { case SI::AC3DescriptorTag: dbgpatpmt(" AC3"); dpid = stream.getPid(); break; case SI::SubtitlingDescriptorTag: dbgpatpmt(" subtitling"); if (NumSpids < MAXSPIDS) { SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d; SI::SubtitlingDescriptor::Subtitling sub; char SLangs[MAXLANGCODE2] = ""; char *s = SLangs; int n = 0; for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) { if (sub.languageCode[0]) { dbgpatpmt(" '%s'", sub.languageCode); if (n > 0) *s++ = '+'; strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1); s += strlen(s); if (n++ > 1) break; } } cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), SLangs); NumSpids++; } break; case SI::ISO639LanguageDescriptorTag: { SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d; dbgpatpmt(" '%s'", ld->languageCode); strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1); } break; default: ; } delete d; } if (dpid) { if (NumDpids < MAXDPIDS) { cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, dpid, lang); NumDpids++; } } } break; } dbgpatpmt("\n"); cDevice::PrimaryDevice()->EnsureAudioTrack(true); cDevice::PrimaryDevice()->EnsureSubtitleTrack(); } } else esyslog("ERROR: can't parse PMT"); pmtSize = 0; } #endif // --- cTsToPes -------------------------------------------------------------- cTsToPes::cTsToPes(void) { data = NULL; size = length = offset = 0; synced = false; } cTsToPes::~cTsToPes() { free(data); } void cTsToPes::PutTs(const uchar *Data, int Length) { if (TsPayloadStart(Data)) Reset(); else if (!size) return; // skip everything before the first payload start Length = TsGetPayload(&Data); if (length + Length > size) { size = max(KILOBYTE(2), length + Length); data = (uchar *)realloc(data, size); } memcpy(data + length, Data, Length); length += Length; } #define MAXPESLENGTH 0xFFF0 const uchar *cTsToPes::GetPes(int &Length) { if (offset < length && PesLongEnough(length)) { if (!PesHasLength(data)) // this is a video PES packet with undefined length offset = 6; // trigger setting PES length for initial slice if (offset) { uchar *p = data + offset - 6; if (p != data) { p -= 3; memmove(p, data, 4); } int l = min(length - offset, MAXPESLENGTH); offset += l; if (p != data) { l += 3; p[6] = 0x80; p[7] = 0x00; p[8] = 0x00; } p[4] = l / 256; p[5] = l & 0xFF; Length = l + 6; return p; } else { Length = PesLength(data); offset = Length; // to make sure we break out in case of garbage data return data; } } return NULL; } void cTsToPes::Reset(void) { length = offset = 0; } // --- Some helper functions for debugging ----------------------------------- void BlockDump(const char *Name, const u_char *Data, int Length) { printf("--- %s\n", Name); for (int i = 0; i < Length; i++) { if (i && (i % 16) == 0) printf("\n"); printf(" %02X", Data[i]); } printf("\n"); } void TsDump(const char *Name, const u_char *Data, int Length) { printf("%s: %04X", Name, Length); int n = min(Length, 20); for (int i = 0; i < n; i++) printf(" %02X", Data[i]); if (n < Length) { printf(" ..."); n = max(n, Length - 10); for (n = max(n, Length - 10); n < Length; n++) printf(" %02X", Data[n]); } printf("\n"); } void PesDump(const char *Name, const u_char *Data, int Length) { TsDump(Name, Data, Length); } } #endif xine-0.9.4/po/0000755000175000017500000000000011540204431011252 5ustar rnisslxine-0.9.4/po/it_IT.po0000644000000000000000000000633511540204431013154 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Alberto Carraro , 2001 # Antonio Ospite , 2003 # Sean Carlos , 2005 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2009-05-06 21:05+0100\n" "Last-Translator: Diego Pierotto \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Italian\n" "X-Poedit-Country: ITALY\n" "X-Poedit-SourceCharset: utf-8\n" msgid "X11 overlay" msgstr "Overlay (X11)" msgid "Blend clipped" msgstr "Trasparenza fissa" msgid "Blend scaled LQ" msgstr "Trasparenza scalata LQ" msgid "Blend scaled HQ" msgstr "Trasparenza scalata HQ" msgid "Blend scaled SHQ" msgstr "Trasparenza scalata SHQ" msgid "Blend scaled Auto" msgstr "Trasparenza scalata in automatico" msgid "No" msgstr "No" msgid "Yes (by hardware)" msgstr "Sì (tramite hardware)" msgid "Yes (by software)" msgstr "Sì (tramite software)" msgid "Ignore" msgstr "Ignora" msgid "Execute" msgstr "Esegui" msgid "Simulate" msgstr "Simula" #, fuzzy msgid "Live-TV SD video buffer [frames]" msgstr "Buffer TV dal vivo [frames]" #, fuzzy msgid "Live-TV HD video buffer [frames]" msgstr "Buffer TV dal vivo [frames]" #, fuzzy msgid "Live-TV audio buffer [frames]" msgstr "Buffer TV dal vivo [frames]" msgid "Buffer hysteresis [frames]" msgstr "Buffer isteresi [frames]" msgid "Buffer monitoring duration [s]" msgstr "Durata monitoraggio buffer [s]" msgid "Buffer monitoring mode" msgstr "Modalità monitoraggio buffer" msgid "Once" msgstr "Una volta" msgid "Continuous" msgstr "Continuo" msgid "OSD display mode" msgstr "Modalità visualizzazione OSD" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "Correzione gamma OSD [ 123 => 1.23 ]" msgid "OSD extent X" msgstr "Limite OSD X" msgid "OSD extent Y" msgstr "Limite OSD Y" msgid "4:3 image zoom X [%]" msgstr "Ingrandimento X [%] immagine 4:3" msgid "4:3 image zoom Y [%]" msgstr "Ingrandimento Y [%] immagine 4:3" msgid "16:9 image zoom X [%]" msgstr "Ingrandimento X [%] immagine 16:9" msgid "16:9 image zoom Y [%]" msgstr "Ingrandimento Y [%] immagine 16:9" msgid "Audio mode" msgstr "Modalità audio" msgid "Dolby off" msgstr "Dolby disattivo" msgid "Dolby on" msgstr "Dolby attivo" msgid "Control xine's volume" msgstr "Regolazione volume xine" msgid "Muting" msgstr "Muto" msgid "Get primary device when xine connects" msgstr "Mostra interfaccia primaria all'avvio di xine" msgid "Yes" msgstr "Sì" msgid "Support semi transparent colors" msgstr "Supporto colori semi trasparenti" msgid "Connection interacts with EIT scanner" msgstr "La connessione interagisce con lo scanner EIT" #, c-format msgid "Switching primary DVB to %s..." msgstr "Cambio scheda DVB primaria a %s..." #, c-format msgid "Switched primary DVB back from %s" msgstr "Ritorno a scheda DVD primaria da %s" msgid "Software based playback using xine" msgstr "Software per riproduzione tramite xine" xine-0.9.4/po/hr_HR.po0000644000000000000000000000427511540204431013147 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Drazen Dupor , 2004 # Dino Ravnic , 2004 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Drazen Dupor \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-2\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/zh_CN.po0000644000000000000000000000555511540204431013150 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2009-09-21 22:36+0800\n" "Last-Translator: NanFeng \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "X11的覆盖" msgid "Blend clipped" msgstr "æ··åˆå‰ªè¾‘" msgid "Blend scaled LQ" msgstr "æ··åˆç¼©æ”¾LQ" msgid "Blend scaled HQ" msgstr "æ··åˆç¼©æ”¾HQ" msgid "Blend scaled SHQ" msgstr "æ··åˆç¼©æ”¾SHQ" msgid "Blend scaled Auto" msgstr "自动混åˆç¼©æ”¾" msgid "No" msgstr "å¦" msgid "Yes (by hardware)" msgstr "确认(硬件模å¼)" msgid "Yes (by software)" msgstr "确认(软件模å¼)" msgid "Ignore" msgstr "忽略" msgid "Execute" msgstr "执行" msgid "Simulate" msgstr "仿真" msgid "Live-TV SD video buffer [frames]" msgstr "直播电视标清视频缓冲器[框架]" msgid "Live-TV HD video buffer [frames]" msgstr "直播电视高清视频缓冲器[框架]" msgid "Live-TV audio buffer [frames]" msgstr "直播电视音频缓冲区[框架]" msgid "Buffer hysteresis [frames]" msgstr "缓冲区滞åŽ[框架]" msgid "Buffer monitoring duration [s]" msgstr "缓冲区监测时间[s]" msgid "Buffer monitoring mode" msgstr "缓冲区监控模å¼" msgid "Once" msgstr "一次" msgid "Continuous" msgstr "连续" msgid "OSD display mode" msgstr "èœå•显示模å¼" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "èœå•伽玛校正 [ 123 => 1.23 ]" msgid "OSD extent X" msgstr "èœå•广度 X" msgid "OSD extent Y" msgstr "èœå•广度 Y" msgid "4:3 image zoom X [%]" msgstr "4:3 å½±åƒå˜ç„¦ X [%]" msgid "4:3 image zoom Y [%]" msgstr "4:3 å½±åƒå˜ç„¦ Y [%]" msgid "16:9 image zoom X [%]" msgstr "16:9 å½±åƒå˜ç„¦ X [%]" msgid "16:9 image zoom Y [%]" msgstr "16:9 å½±åƒå˜ç„¦ Y [%]" msgid "Audio mode" msgstr "音频模å¼" msgid "Dolby off" msgstr "æœæ¯”关闭" msgid "Dolby on" msgstr "æœæ¯”打开" msgid "Control xine's volume" msgstr "控制XINE的音é‡" msgid "Muting" msgstr "é™éŸ³" msgid "Get primary device when xine connects" msgstr "主è¦è®¾å¤‡ï¼Œåªè¦èŽ·å¾—xine的连接" msgid "Yes" msgstr "是" msgid "Support semi transparent colors" msgstr "支æŒåŠé€æ˜Žé¢œè‰²" msgid "Connection interacts with EIT scanner" msgstr "与转型期扫æä»ªè¿žæŽ¥äº¤äº’" #, c-format msgid "Switching primary DVB to %s..." msgstr "切æ¢ä¸»çš„DVB到 %s..." #, c-format msgid "Switched primary DVB back from %s" msgstr "切æ¢ä¸»DVB回到 %s" msgid "Software based playback using xine" msgstr "基于软件回放使用的XINE播放器" xine-0.9.4/po/pt_PT.po0000644000000000000000000000420411540204431013163 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Paulo Lopes , 2001 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Paulo Lopes \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/ro_RO.po0000644000000000000000000000432311540204431013157 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Paul Lacatus , 2002 # Lucian Muresan , 2004 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Lucian Muresan \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-2\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/es_ES.po0000644000000000000000000000425111540204431013135 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Ruben Nunez Francisco , 2002 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Ruben Nunez Francisco \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/ca_ES.po0000644000000000000000000000433511540204431013114 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Marc Rovira Vall , 2003 # Ramon Roca , 2003 # Jordi Vilà , 2003 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Jordi Vilà \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/lt_LT.po0000644000000000000000000000620211540204431013153 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Valdemaras Pipiras , 2009 # msgid "" msgstr "" "Project-Id-Version: VDR 1.7.10\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2009-11-26 00:10+0200\n" "Last-Translator: Valdemaras Pipiras \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "X11 perdengimas (overlay)" msgid "Blend clipped" msgstr "Nukirpti" msgid "Blend scaled LQ" msgstr "Pereiti į žemÄ… kokybÄ™ (LQ)" msgid "Blend scaled HQ" msgstr "Pereiti į aukÅ¡tÄ… kokybÄ™ (HQ)" msgid "Blend scaled SHQ" msgstr "Pereiti į labai aukÅ¡tÄ… kokybÄ™ (SHQ)" msgid "Blend scaled Auto" msgstr "Automatinis perÄ—jimas" msgid "No" msgstr "Ne" msgid "Yes (by hardware)" msgstr "Taip (aparatÅ«riÅ¡kai)" msgid "Yes (by software)" msgstr "Taip (programiÅ¡kai)" msgid "Ignore" msgstr "Ignoruoti" msgid "Execute" msgstr "Ä®vykdyti" msgid "Simulate" msgstr "Simuliuoti" msgid "Live-TV SD video buffer [frames]" msgstr "SD-Video buferis Live-TV [kadrais]" msgid "Live-TV HD video buffer [frames]" msgstr "HD-Video buferis Live-TV [kadrais]" msgid "Live-TV audio buffer [frames]" msgstr "Audio buferis Live-TV [kadrais]" msgid "Buffer hysteresis [frames]" msgstr "Burerio histerezÄ— [kadrais]" msgid "Buffer monitoring duration [s]" msgstr "Buferio stebÄ—jimo trukmÄ— [s]" msgid "Buffer monitoring mode" msgstr "Buferio stebÄ—jimo bÅ«sena" msgid "Once" msgstr "VienÄ… kartÄ…" msgid "Continuous" msgstr "BesitÄ™siantis" msgid "OSD display mode" msgstr "Ekrano meniu (OSD) bÅ«sena" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "OSD gama korekcija [ 123 => 1.23 ]" msgid "OSD extent X" msgstr "OSD pagal X ašį" msgid "OSD extent Y" msgstr "OSD pagal Y ašį" msgid "4:3 image zoom X [%]" msgstr "4:3 vaizdo X aÅ¡ies iÅ¡plÄ—timas [%]" msgid "4:3 image zoom Y [%]" msgstr "4:3 vaizdo Y aÅ¡ies iÅ¡plÄ—timas [%]" msgid "16:9 image zoom X [%]" msgstr "16:9 vaizdo X aÅ¡ies iÅ¡plÄ—timas [%]" msgid "16:9 image zoom Y [%]" msgstr "16:9 vaizdo Y aÅ¡ies iÅ¡plÄ—timas [%]" msgid "Audio mode" msgstr "Audio ręžimas" msgid "Dolby off" msgstr "Dolby iÅ¡jungta" msgid "Dolby on" msgstr "Dolby įjungta" msgid "Control xine's volume" msgstr "Valdyti xine garsÄ…" msgid "Muting" msgstr "IÅ¡jungiamas garsas" msgid "Get primary device when xine connects" msgstr "Xine prisijungus įjungti pirmÄ… įrenginį" msgid "Yes" msgstr "Taip" msgid "Support semi transparent colors" msgstr "Pusiau permatomų spalvų palaikymas" msgid "Connection interacts with EIT scanner" msgstr "Sujungimas sÄ…veikauja su EIT skaneriu" #, c-format msgid "Switching primary DVB to %s..." msgstr "PirminÄ— DVB korta perjungiama į %s..." #, c-format msgid "Switched primary DVB back from %s" msgstr "PirminÄ— DVB korta perjungta atgal iÅ¡ %s" msgid "Software based playback using xine" msgstr "Programinis iÅ¡vedimas naudojant xine programÄ…" xine-0.9.4/po/hu_HU.po0000644000000000000000000000436711540204431013157 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Istvan Koenigsberger , 2002 # Guido Josten , 2002 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Istvan Koenigsberger , Guido Josten \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-2\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/fi_FI.po0000644000000000000000000000566111540204431013121 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Hannu Savolainen , 2002 # Jaakko Hyvätti , 2002 # Niko Tarnanen , 2003 # Rolf Ahrenberg , 2003 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Rolf Ahrenberg \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "overlay (X11)" msgid "Blend clipped" msgstr "leikattu" msgid "Blend scaled LQ" msgstr "skaalattu (LQ)" msgid "Blend scaled HQ" msgstr "skaalattu (HQ)" msgid "Blend scaled SHQ" msgstr "skaalattu (SHQ)" msgid "Blend scaled Auto" msgstr "skaalattu (auto)" msgid "No" msgstr "ei" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "sivuuta" msgid "Execute" msgstr "suorita" msgid "Simulate" msgstr "simuloi" #, fuzzy msgid "Live-TV SD video buffer [frames]" msgstr "TV-lähetyksen puskurointi [ruutua]" #, fuzzy msgid "Live-TV HD video buffer [frames]" msgstr "TV-lähetyksen puskurointi [ruutua]" #, fuzzy msgid "Live-TV audio buffer [frames]" msgstr "TV-lähetyksen puskurointi [ruutua]" msgid "Buffer hysteresis [frames]" msgstr "Puskurin hystereesis [ruutua]" msgid "Buffer monitoring duration [s]" msgstr "Puskurin monitoroinnin kesto [s]" msgid "Buffer monitoring mode" msgstr "Puskurin monitorointitapa" msgid "Once" msgstr "kerran" msgid "Continuous" msgstr "jatkuva" msgid "OSD display mode" msgstr "Kuvaruutunäytön moodi" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "Kuvaruutunäytön gammakorjaus [123 => 1.23]" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "Monikanavaäänet (Dolby)" msgid "Dolby off" msgstr "pois" msgid "Dolby on" msgstr "päällä" msgid "Control xine's volume" msgstr "Käytä Xinen äänenvoimakkuussäätöä" msgid "Muting" msgstr "Mykistys" msgid "Get primary device when xine connects" msgstr "Käytä automaattisesti ensisijaisena sovittimena" msgid "Yes" msgstr "kyllä" msgid "Support semi transparent colors" msgstr "Tue puoliläpinäkyviä värejä" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "Vaihdetaan ensisijaiseksi DVB-sovittimeksi %s..." #, c-format msgid "Switched primary DVB back from %s" msgstr "Ensisijainen DVB-sovitin vaihdettu takaisin %s:stä" msgid "Software based playback using xine" msgstr "Xine-näyttölaite" xine-0.9.4/po/fr_FR.po0000644000000000000000000000445011540204431013136 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Jean-Claude Repetto , 2001 # Olivier Jacques , 2003 # Gregoire Favre , 2003 # Nicolas Huillard , 2005 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Nicolas Huillard \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/sv_SE.po0000644000000000000000000000456111540204431013162 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Tomas Prybil , 2002 # Jan Ekholm , 2003 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Tomas Prybil \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" #, fuzzy msgid "Live-TV SD video buffer [frames]" msgstr "Live-TV bufferstorlek [bildrutor]" #, fuzzy msgid "Live-TV HD video buffer [frames]" msgstr "Live-TV bufferstorlek [bildrutor]" #, fuzzy msgid "Live-TV audio buffer [frames]" msgstr "Live-TV bufferstorlek [bildrutor]" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "OSD läge" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "Ljud läge" msgid "Dolby off" msgstr "Dolby av" msgid "Dolby on" msgstr "Dolby på" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "Mjukvarubaserad uppspelning med xine" xine-0.9.4/po/nn_NO.po0000644000000000000000000000426711540204431013155 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Jørgen Tvedt , 2001 # Truls Slevigen , 2002 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Truls Slevigen \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-1\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/cs_CZ.po0000644000000000000000000000424211540204431013140 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Vladimír Bárta , 2006 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Vladimír Bárta \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-2\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/zh_TW.po0000644000000000000000000000554411540204431013200 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2009-09-21 22:36+0800\n" "Last-Translator: NanFeng \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "X11的覆蓋" msgid "Blend clipped" msgstr "æ··åˆå‰ªè¼¯" msgid "Blend scaled LQ" msgstr "æ··åˆç¸®æ”¾LQ" msgid "Blend scaled HQ" msgstr "æ··åˆç¸®æ”¾HQ" msgid "Blend scaled SHQ" msgstr "æ··åˆç¸®æ”¾SHQ" msgid "Blend scaled Auto" msgstr "自動混åˆç¸®æ”¾" msgid "No" msgstr "å¦" msgid "Yes (by hardware)" msgstr "確èª(硬件模å¼)" msgid "Yes (by software)" msgstr "確èª(軟件模å¼)" msgid "Ignore" msgstr "忽略" msgid "Execute" msgstr "執行" msgid "Simulate" msgstr "仿真" msgid "Live-TV SD video buffer [frames]" msgstr "直播電視標清視頻緩è¡å™¨[框架]" msgid "Live-TV HD video buffer [frames]" msgstr "直播電視高清視頻緩è¡å™¨[框架]" msgid "Live-TV audio buffer [frames]" msgstr "直播電視音頻緩è¡å€[框架]" msgid "Buffer hysteresis [frames]" msgstr "ç·©è¡å€æ»¯å¾Œ[框架]" msgid "Buffer monitoring duration [s]" msgstr "ç·©è¡å€ç›£æ¸¬æ™‚é–“[s]" msgid "Buffer monitoring mode" msgstr "ç·©è¡å€ç›£æŽ§æ¨¡å¼" msgid "Once" msgstr "一次" msgid "Continuous" msgstr "連續" msgid "OSD display mode" msgstr "èœå–®é¡¯ç¤ºæ¨¡å¼" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "èœå–®ä¼½ç‘ªæ ¡æ­£[ 123 => 1.23 ]" msgid "OSD extent X" msgstr "èœå–®å»£åº¦X" msgid "OSD extent Y" msgstr "èœå–®å»£åº¦Y" msgid "4:3 image zoom X [%]" msgstr "4:3å½±åƒè®Šç„¦X [%]" msgid "4:3 image zoom Y [%]" msgstr "4:3å½±åƒè®Šç„¦Y [%]" msgid "16:9 image zoom X [%]" msgstr "16:9å½±åƒè®Šç„¦X [%]" msgid "16:9 image zoom Y [%]" msgstr "16:9å½±åƒè®Šç„¦Y [%]" msgid "Audio mode" msgstr "音頻模å¼" msgid "Dolby off" msgstr "æœæ¯”關閉" msgid "Dolby on" msgstr "æœæ¯”打開" msgid "Control xine's volume" msgstr "控制XINE的音é‡" msgid "Muting" msgstr "éœéŸ³" msgid "Get primary device when xine connects" msgstr "主è¦è¨­å‚™ï¼Œåªè¦ç²å¾—xine的連接" msgid "Yes" msgstr "是" msgid "Support semi transparent colors" msgstr "支æŒåŠé€æ˜Žé¡è‰²" msgid "Connection interacts with EIT scanner" msgstr "與轉型期掃æå„€é€£æŽ¥äº¤äº’" #, c-format msgid "Switching primary DVB to %s..." msgstr "切æ›ä¸»çš„DVB到%s..." #, c-format msgid "Switched primary DVB back from %s" msgstr "切æ›ä¸»DVB回到%s" msgid "Software based playback using xine" msgstr "基於軟件回放使用的XINE播放器" xine-0.9.4/po/el_GR.po0000644000000000000000000000423211540204431013126 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Dimitrios Dimitrakos , 2002 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Dimitrios Dimitrakos \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-7\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/sl_SI.po0000644000000000000000000000432011540204431013145 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Miha Setina , 2000 # Matjaz Thaler , 2003 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Matjaz Thaler \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-2\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/pl_PL.po0000644000000000000000000000420611540204431013145 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Michael Rakowski , 2002 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Michael Rakowski \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-2\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/de_DE.po0000644000000000000000000000575411540204431013110 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Klaus Schmidinger , 2000 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Klaus Schmidinger \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "Überlagern (X11)" msgid "Blend clipped" msgstr "Abschneiden" msgid "Blend scaled LQ" msgstr "Einpassen LQ" msgid "Blend scaled HQ" msgstr "Einpassen HQ" msgid "Blend scaled SHQ" msgstr "Einpassen SHQ" msgid "Blend scaled Auto" msgstr "Einpassen Auto" msgid "No" msgstr "Nein" msgid "Yes (by hardware)" msgstr "Ja (in Hardware)" msgid "Yes (by software)" msgstr "Ja (in Software)" msgid "Ignore" msgstr "Ignorieren" msgid "Execute" msgstr "Ausführen" msgid "Simulate" msgstr "Simulieren" msgid "Live-TV SD video buffer [frames]" msgstr "SD-Video Puffer für Live-TV [Bilder]" msgid "Live-TV HD video buffer [frames]" msgstr "HD-Video Puffer für Live-TV [Bilder]" msgid "Live-TV audio buffer [frames]" msgstr "Audio Puffer für Live-TV [Bilder]" msgid "Buffer hysteresis [frames]" msgstr "Puffer-Hysterese [Bilder]" msgid "Buffer monitoring duration [s]" msgstr "Puffer-Überwachungsdauer [s]" msgid "Buffer monitoring mode" msgstr "Puffer-Überwachungsmodus" msgid "Once" msgstr "Einmal" msgid "Continuous" msgstr "Durchgehend" msgid "OSD display mode" msgstr "Anzeigeart für OSD" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "OSD Gamma Korrektur [ 123 => 1.23 ]" msgid "OSD extent X" msgstr "OSD Ausdehnung X" msgid "OSD extent Y" msgstr "OSD Ausdehnung Y" msgid "4:3 image zoom X [%]" msgstr "4:3 Bilder X-Skalierung [%]" msgid "4:3 image zoom Y [%]" msgstr "4:3 Bilder Y-Skalierung [%]" msgid "16:9 image zoom X [%]" msgstr "16:9 Bilder X-Skalierung [%]" msgid "16:9 image zoom Y [%]" msgstr "16:9 Bilder Y-Skalierung [%]" msgid "Audio mode" msgstr "Audio-Modus" msgid "Dolby off" msgstr "Dolby aus" msgid "Dolby on" msgstr "Dolby ein" msgid "Control xine's volume" msgstr "Lautstärke in xine steuern" msgid "Muting" msgstr "Stummschalten" msgid "Get primary device when xine connects" msgstr "Automatisch zum primären Interface werden" msgid "Yes" msgstr "Ja" msgid "Support semi transparent colors" msgstr "Unterstützung für halbtransparente Farben" msgid "Connection interacts with EIT scanner" msgstr "Verbindung interagiert mit EIT-Scanner" #, c-format msgid "Switching primary DVB to %s..." msgstr "Primäres Interface wird zu %s umgeschaltet..." #, c-format msgid "Switched primary DVB back from %s" msgstr "Primäres Interface wurde von %s zurückgeschaltet" msgid "Software based playback using xine" msgstr "Software-basierte Wiedergabe mittels xine" xine-0.9.4/po/nl_NL.po0000644000000000000000000000551311540204431013143 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Arnold Niessen , 2001 # Hans Dingemans , 2003 # Maarten Wisse , 2005 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Maarten Wisse \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "X11 overlay" msgid "Blend clipped" msgstr "Samenvoegen met afsnijden" msgid "Blend scaled LQ" msgstr "Samenvoegen met lage kwaliteit" msgid "Blend scaled HQ" msgstr "Samenvoegen met hoge kwaliteit" msgid "Blend scaled SHQ" msgstr "Samenvoegen met superhoge kwaliteit" msgid "Blend scaled Auto" msgstr "Automatische samenvoeg-modus" msgid "No" msgstr "Nee" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "Negeren" msgid "Execute" msgstr "Uitvoeren" msgid "Simulate" msgstr "Simuleren" #, fuzzy msgid "Live-TV SD video buffer [frames]" msgstr "Live-TV buffer [frames]" #, fuzzy msgid "Live-TV HD video buffer [frames]" msgstr "Live-TV buffer [frames]" #, fuzzy msgid "Live-TV audio buffer [frames]" msgstr "Live-TV buffer [frames]" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "Weergave-type van de OSD" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "OSD gamma correctie [ 123 => 1.23 ]" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "Audio-modus" msgid "Dolby off" msgstr "Dolby uitgeschakeld" msgid "Dolby on" msgstr "Dolby ingeschakeld" msgid "Control xine's volume" msgstr "Xine's volume via VDR besturen" msgid "Muting" msgstr "Geluid dempen" msgid "Get primary device when xine connects" msgstr "Xine als primair dvb-apparaat gebruiken" msgid "Yes" msgstr "Ja" msgid "Support semi transparent colors" msgstr "Ondersteun semi-transparante kleuren" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "Software-gebaseerde weergave met behulp van Xine" xine-0.9.4/po/et_EE.po0000644000000000000000000000554611540204431013130 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Arthur Konovalov , 2004 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Arthur Konovalov \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-13\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "overlay (X11)" msgid "Blend clipped" msgstr "lõigatud" msgid "Blend scaled LQ" msgstr "skaleeritud (LQ)" msgid "Blend scaled HQ" msgstr "skaleeritud (HQ)" msgid "Blend scaled SHQ" msgstr "skaleeritud (SHQ)" msgid "Blend scaled Auto" msgstr "skaleeritud (auto)" msgid "No" msgstr "ei" msgid "Yes (by hardware)" msgstr "jah (raualine)" msgid "Yes (by software)" msgstr "jah (tarkvaraline)" msgid "Ignore" msgstr "eira" msgid "Execute" msgstr "soorita" msgid "Simulate" msgstr "simuleeri" #, fuzzy msgid "Live-TV SD video buffer [frames]" msgstr "Live-TV puhver [freimi]" #, fuzzy msgid "Live-TV HD video buffer [frames]" msgstr "Live-TV puhver [freimi]" #, fuzzy msgid "Live-TV audio buffer [frames]" msgstr "Live-TV puhver [freimi]" msgid "Buffer hysteresis [frames]" msgstr "Puhvri hüsterees [freimi]" msgid "Buffer monitoring duration [s]" msgstr "Puhvri monitooringu kestus [s]" msgid "Buffer monitoring mode" msgstr "Puhvri monitooringu moodus" msgid "Once" msgstr "üks kord" msgid "Continuous" msgstr "pidevalt" msgid "OSD display mode" msgstr "Ekraanimenüü moodus" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "Gammakorrektsioon [123=>1.23]" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "4:3 kujutuse suum X [%]" msgid "4:3 image zoom Y [%]" msgstr "4:3 kujutuse suum Y [%]" msgid "16:9 image zoom X [%]" msgstr "16:9 kujutuse suum X [%]" msgid "16:9 image zoom Y [%]" msgstr "16:9 kujutuse suum Y [%]" msgid "Audio mode" msgstr "Dolbyheli" msgid "Dolby off" msgstr "väljas" msgid "Dolby on" msgstr "sees" msgid "Control xine's volume" msgstr "Helitugevuse kontroll" msgid "Muting" msgstr "Vaigistus" msgid "Get primary device when xine connects" msgstr "Esmase seadme automaatne ühendamine" msgid "Yes" msgstr "jah" msgid "Support semi transparent colors" msgstr "Poolläbipaistvate värvide tugi" msgid "Connection interacts with EIT scanner" msgstr "Ühenduse koosoime EIT skanneriga" #, c-format msgid "Switching primary DVB to %s..." msgstr "Esmase DVB seadme %s ümberlülitamine..." #, c-format msgid "Switched primary DVB back from %s" msgstr "Esmane DVB seade lülitatud tagasi %s-st" msgid "Software based playback using xine" msgstr "Xine baasil taasesitusseade" xine-0.9.4/po/da_DK.po0000644000000000000000000000421311540204431013077 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Mogens Elneff , 2004 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Mogens Elneff \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-15\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/sk_SK.po0000644000000000000000000000616011540204431013152 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Vladimír Bárta , 2006 # msgid "" msgstr "" "Project-Id-Version: xine_SK\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2009-11-02 10:43+0100\n" "Last-Translator: Milan Hrala \n" "Language-Team: Slovak \n" "Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=iso-8859-2\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Slovak\n" "X-Poedit-Country: SLOVAKIA\n" msgid "X11 overlay" msgstr "X11 prekrývanie" msgid "Blend clipped" msgstr "Zastrihnuté zmie¹a»" msgid "Blend scaled LQ" msgstr "Zastrihnuté zmie¹a» LQ" msgid "Blend scaled HQ" msgstr "Zastrihnuté zmie¹a» HQ" msgid "Blend scaled SHQ" msgstr "Zastrihnuté zmie¹a» SHQ" msgid "Blend scaled Auto" msgstr "Automatické splynutie merítka" msgid "No" msgstr "Nie" msgid "Yes (by hardware)" msgstr "Áno (podµa hardware)" msgid "Yes (by software)" msgstr "Áno (podµa software)" msgid "Ignore" msgstr "Ignorova»" msgid "Execute" msgstr "Vykona»" msgid "Simulate" msgstr "Simulova»" msgid "Live-TV SD video buffer [frames]" msgstr "Live-TV SD video vyrovnávacia pamä» [rámcov]" msgid "Live-TV HD video buffer [frames]" msgstr "Live-TV HD video vyrovnávacia pamä» [rámcov]" msgid "Live-TV audio buffer [frames]" msgstr "Live-TV audio vyrovnávacia pamä» [rámcov]" msgid "Buffer hysteresis [frames]" msgstr "Spozdenie vyrovnávacej pamäte [rámce]" msgid "Buffer monitoring duration [s]" msgstr "Doba sledovania vyrovnávacej pamäte [s]" msgid "Buffer monitoring mode" msgstr "Re¾im monitorovania vyrovnávacej pamäti" msgid "Once" msgstr "iba jeden raz" msgid "Continuous" msgstr "Na pokraèovanie" msgid "OSD display mode" msgstr "Re¾im zobrazenia OSD" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "OSD gama korekcia [123 => 1,23]" msgid "OSD extent X" msgstr "OSD rozsah X" msgid "OSD extent Y" msgstr "OSD rozsah Y" msgid "4:3 image zoom X [%]" msgstr "obraz 4:3 zväè¹ený v X [%]" msgid "4:3 image zoom Y [%]" msgstr "obraz 4:3 zväè¹ený v Y [%]" msgid "16:9 image zoom X [%]" msgstr "obraz 16:9 zväè¹ený v X [%]" msgid "16:9 image zoom Y [%]" msgstr "obraz 16:9 zväè¹ený v Y [%]" msgid "Audio mode" msgstr "Re¾im zvuku" msgid "Dolby off" msgstr "Dolby vypnuté" msgid "Dolby on" msgstr "Dolby zapnuté" msgid "Control xine's volume" msgstr "Ovládanie xine hlasitosti" msgid "Muting" msgstr "Ticho" msgid "Get primary device when xine connects" msgstr "Získanie spojenia primárneho ovládaèa xine" msgid "Yes" msgstr "Áno" msgid "Support semi transparent colors" msgstr "Podpora priesvitných farieb" msgid "Connection interacts with EIT scanner" msgstr "Spojenie s ETI snímaèom" #, c-format msgid "Switching primary DVB to %s..." msgstr "Prepnutie na primárne DVB %s..." #, c-format msgid "Switched primary DVB back from %s" msgstr "Prepína hlavné DVB spä» z %s" msgid "Software based playback using xine" msgstr "Softvér na prehrávanie pomocou xine" xine-0.9.4/po/tr_TR.po0000644000000000000000000000421611540204431013172 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Oktay Yolgeçen , 2007 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2007-08-29 21:10+0200\n" "Last-Translator: Oktay Yolgeçen \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-9\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "" msgid "Blend clipped" msgstr "" msgid "Blend scaled LQ" msgstr "" msgid "Blend scaled HQ" msgstr "" msgid "Blend scaled SHQ" msgstr "" msgid "Blend scaled Auto" msgstr "" msgid "No" msgstr "" msgid "Yes (by hardware)" msgstr "" msgid "Yes (by software)" msgstr "" msgid "Ignore" msgstr "" msgid "Execute" msgstr "" msgid "Simulate" msgstr "" msgid "Live-TV SD video buffer [frames]" msgstr "" msgid "Live-TV HD video buffer [frames]" msgstr "" msgid "Live-TV audio buffer [frames]" msgstr "" msgid "Buffer hysteresis [frames]" msgstr "" msgid "Buffer monitoring duration [s]" msgstr "" msgid "Buffer monitoring mode" msgstr "" msgid "Once" msgstr "" msgid "Continuous" msgstr "" msgid "OSD display mode" msgstr "" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "" msgid "4:3 image zoom Y [%]" msgstr "" msgid "16:9 image zoom X [%]" msgstr "" msgid "16:9 image zoom Y [%]" msgstr "" msgid "Audio mode" msgstr "" msgid "Dolby off" msgstr "" msgid "Dolby on" msgstr "" msgid "Control xine's volume" msgstr "" msgid "Muting" msgstr "" msgid "Get primary device when xine connects" msgstr "" msgid "Yes" msgstr "" msgid "Support semi transparent colors" msgstr "" msgid "Connection interacts with EIT scanner" msgstr "" #, c-format msgid "Switching primary DVB to %s..." msgstr "" #, c-format msgid "Switched primary DVB back from %s" msgstr "" msgid "Software based playback using xine" msgstr "" xine-0.9.4/po/ru_RU.po0000644000000000000000000000575611540204431013206 0ustar rootroot# VDR plugin language source file. # Copyright (C) 2007 Klaus Schmidinger # This file is distributed under the same license as the VDR package. # Vyacheslav Dikonov , 2004 # msgid "" msgstr "" "Project-Id-Version: VDR 1.5.7\n" "Report-Msgid-Bugs-To: Reinhard Nissl \n" "POT-Creation-Date: 2011-03-13 14:22+0100\n" "PO-Revision-Date: 2008-01-29 13:00+0100\n" "Last-Translator: Oleg Roitburd \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ISO-8859-5\n" "Content-Transfer-Encoding: 8bit\n" msgid "X11 overlay" msgstr "X11 ½ÐÛÞÖÕÝØÕ" msgid "Blend clipped" msgstr "¾ÑàÕ×Ðâì" msgid "Blend scaled LQ" msgstr "¿ÞÔÓÞÝÚÐ LQ" msgid "Blend scaled HQ" msgstr "¿ÞÔÓÞÝÚÐ HQ" msgid "Blend scaled SHQ" msgstr "¿ÞÔÓÞÝÚÐ SHQ" msgid "Blend scaled Auto" msgstr "°ÒâÞÜÐâØçÕáÚÐï ßÞÔÓÞÝÚÐ" msgid "No" msgstr "½Õâ" msgid "Yes (by hardware)" msgstr "´Ð (ÚÞÜßìîâÕàÞÜ)" msgid "Yes (by software)" msgstr "´Ð (ßàÞÓàÐÜÜÝÞ)" msgid "Ignore" msgstr "¸ÓÝÞàØàÞÒÐâì" msgid "Execute" msgstr "²ëßÞÛÝØâì" msgid "Simulate" msgstr "ÁØÜãÛØàÞÒÐâì" #, fuzzy msgid "Live-TV SD video buffer [frames]" msgstr "Live-TV ÑãääÕàë [äàíÙÜë]" #, fuzzy msgid "Live-TV HD video buffer [frames]" msgstr "Live-TV ÑãääÕàë [äàíÙÜë]" #, fuzzy msgid "Live-TV audio buffer [frames]" msgstr "Live-TV ÑãääÕàë [äàíÙÜë]" msgid "Buffer hysteresis [frames]" msgstr "³ØáâÕàÕרá ÑãäÕàÐ [äàÕÙÜë]" msgid "Buffer monitoring duration [s]" msgstr "´ÛØâÕÛìÝÞáâì ÜÞÝØâÞàØÝÓÐ ÑãäÕàÐ [s]" msgid "Buffer monitoring mode" msgstr "ÀÕÖØÜ ÜÞÝØâÞàØÝÓÐ ÑãäÕàÐ" msgid "Once" msgstr "¾ÔÝÐÖÔë" msgid "Continuous" msgstr "¿ÞáâÞïÝÝëÙ" msgid "OSD display mode" msgstr "ÀÕÖØÜ ÞâÞÑàÐÖÕÝØï OSD" msgid "OSD gamma correction [ 123 => 1.23 ]" msgstr "¸×ÜÕÝÕÝØÕ ÓÐÜÜë OSD [ 123 => 1.23 ]" msgid "OSD extent X" msgstr "" msgid "OSD extent Y" msgstr "" msgid "4:3 image zoom X [%]" msgstr "4:3 áÚÐÛØàÞÒÐÝØÕ Ø×ÞÑàÐÖÕÝØï X [%]" msgid "4:3 image zoom Y [%]" msgstr "4:3 áÚÐÛØàÞÒÐÝØÕ Ø×ÞÑàÐÖÕÝØï Y [%" msgid "16:9 image zoom X [%]" msgstr "16:9 áÚÐÛØàÞÒÐÝØÕ Ø×ÞÑàÐÖÕÝØï X [%]" msgid "16:9 image zoom Y [%]" msgstr "16:9 áÚÐÛØàÞÒÐÝØÕ Ø×ÞÑàÐÖÕÝØï Y [%]" msgid "Audio mode" msgstr "°ãÔØÞ àÕÖØÜ" msgid "Dolby off" msgstr "Dolby ÒëÚÛ." msgid "Dolby on" msgstr "Dolby ÒÚÛ." msgid "Control xine's volume" msgstr "ÃßàÐÒÛÕÝØÕ xine ×ÒãÚÞÜ" msgid "Muting" msgstr "±Õ× ×ÒãÚÐ" msgid "Get primary device when xine connects" msgstr "¿ÞÛãçØâì ßÕàÒØçÝÞÕ ãáâàÞÙáâÒÞ ÚÞÓÔÐ xine áÞÕÔØÝÕÝÞ" msgid "Yes" msgstr "´Ð" msgid "Support semi transparent colors" msgstr "¿ÞÔÔÕàÖÚÐ ßÞÛãßàÞ×àÐçÝëå ÚàÐáÞÚ" msgid "Connection interacts with EIT scanner" msgstr "¸ÝâÕàÐÚâØÒÝÞÕ ßÞÔÚÛîçÕÝØÕ á EIT áÚÐÝÕàÞÜ" #, c-format msgid "Switching primary DVB to %s..." msgstr "¿ÕàÕÚÛîçÕÝØÕ ßÕàÒØçÝÞÓÞ DVB ãáâàÞÙáâÒÐ ÝÐ %s..." #, c-format msgid "Switched primary DVB back from %s" msgstr "¿ÕàÒØçÝÞÕ ãáâàÞÙáâÒÞ DVB ßÕàÕÚÛîçÕÝÞ ÝÐ×ÐÔ á %s" msgid "Software based playback using xine" msgstr "¿àÞÓàÐÜÜÝÞÕ ÒÞáßàÞØ×ÒÕÔÕÝØÕ, ØáßÞÛì×ãï xine" xine-0.9.4/vdr172remux.h0000644000000000000000000002257311166172052013456 0ustar rootroot #if APIVERSNUM >= 10703 /* * remux.h: A streaming MPEG2 remultiplexer * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * $Id: remux.h 2.3 2008/12/13 13:55:07 kls Exp $ */ #ifndef __VDR172REMUX_H #define __VDR172REMUX_H #include #include #include namespace vdr172 { enum ePesHeader { phNeedMoreData = -1, phInvalid = 0, phMPEG1 = 1, phMPEG2 = 2 }; ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL); // Picture types: #define NO_PICTURE 0 #define I_FRAME 1 #define P_FRAME 2 #define B_FRAME 3 #define MAXTRACKS 64 class cTS2PES; class cAudioIndexer; class cRemux { private: bool exitOnFailure; bool noVideo; bool h264; int numUPTerrors; bool synced; bool syncEarly; int skipped; cTS2PES *ts2pes[MAXTRACKS]; int numTracks; cRingBufferLinear *resultBuffer; int resultSkipped; cAudioIndexer *audioIndexer; int GetPid(const uchar *Data); int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType); public: cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure = false, bool SyncEarly = false); ///< Creates a new remuxer for the given PIDs. VPid is the video PID, while ///< APids, DPids and SPids are pointers to zero terminated lists of audio, ///< dolby and subtitle PIDs (the pointers may be NULL if there is no such ///< PID). If ExitOnFailure is true, the remuxer will initiate an "emergency ///< exit" in case of problems with the data stream. SyncEarly causes cRemux ///< to sync as soon as a video or audio frame is seen. ~cRemux(); void SetTimeouts(int PutTimeout, int GetTimeout) { resultBuffer->SetTimeouts(PutTimeout, GetTimeout); } ///< By default cRemux assumes that Put() and Get() are called from different ///< threads, and uses a timeout in the Get() function in case there is no ///< data available. SetTimeouts() can be used to modify these timeouts. ///< Especially if Put() and Get() are called from the same thread, setting ///< both timeouts to 0 is recommended. int Put(const uchar *Data, int Count); ///< Puts at most Count bytes of Data into the remuxer. ///< \return Returns the number of bytes actually consumed from Data. uchar *Get(int &Count, uchar *PictureType = NULL); ///< Gets all currently available data from the remuxer. ///< \return Count contains the number of bytes the result points to, and ///< PictureType (if not NULL) will contain one of NO_PICTURE, I_FRAME, P_FRAME ///< or B_FRAME. void Del(int Count); ///< Deletes Count bytes from the remuxer. Count must be the number returned ///< from a previous call to Get(). Several calls to Del() with fractions of ///< a previously returned Count may be made, but the total sum of all Count ///< values must be exactly what the previous Get() has returned. void Clear(void); ///< Clears the remuxer of all data it might still contain, keeping the PID ///< settings as they are. static void SetBrokenLink(uchar *Data, int Length); static int GetPacketLength(const uchar *Data, int Count, int Offset); static int GetAudioFrameDuration(const uchar *Data, int Count, int *TrackIndex = NULL); static bool IsFrameH264(const uchar *Data, int Length); }; // Some TS handling tools. // The following functions all take a pointer to one complete TS packet. #define TS_SYNC_BYTE 0x47 #define TS_SIZE 188 #define TS_ADAPT_FIELD_EXISTS 0x20 #define TS_PAYLOAD_EXISTS 0x10 #define TS_CONT_CNT_MASK 0x0F #define TS_PAYLOAD_START 0x40 #define TS_ERROR 0x80 #define TS_PID_MASK_HI 0x1F inline int TsHasPayload(const uchar *p) { return p[3] & TS_PAYLOAD_EXISTS; } inline int TsPayloadStart(const uchar *p) { return p[1] & TS_PAYLOAD_START; } inline int TsError(const uchar *p) { return p[1] & TS_ERROR; } inline int TsPid(const uchar *p) { return (p[1] & TS_PID_MASK_HI) * 256 + p[2]; } inline int TsPayloadOffset(const uchar *p) { return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4; } inline int TsGetPayload(const uchar **p) { int o = TsPayloadOffset(*p); *p += o; return TS_SIZE - o; } inline int TsContinuityCounter(const uchar *p) { return p[3] & TS_CONT_CNT_MASK; } // Some PES handling tools: // The following functions that take a pointer to PES data all assume that // there is enough data so that PesLongEnough() returns true. inline bool PesLongEnough(int Length) { return Length >= 6; } inline bool PesHasLength(const uchar *p) { return p[4] | p[5]; } inline int PesLength(const uchar *p) { return 6 + p[4] * 256 + p[5]; } inline int PesPayloadOffset(const uchar *p) { return 9 + p[8]; } inline int64_t PesGetPts(const uchar *p) { if ((p[7] & 0x80) && p[8] >= 5) { return ((((int64_t)p[ 9]) & 0x0E) << 29) | (( (int64_t)p[10]) << 22) | ((((int64_t)p[11]) & 0xFE) << 14) | (( (int64_t)p[12]) << 7) | ((((int64_t)p[13]) & 0xFE) >> 1); } return 0; } #if 0 // PAT/PMT Generator: #define MAX_SECTION_SIZE 4096 // maximum size of an SI section #define MAX_PMT_TS (MAX_SECTION_SIZE / TS_SIZE + 1) class cPatPmtGenerator { private: uchar pat[TS_SIZE]; // the PAT always fits into a single TS packet uchar pmt[MAX_PMT_TS][TS_SIZE]; // the PMT may well extend over several TS packets int numPmtPackets; int patCounter; int pmtCounter; int patVersion; int pmtVersion; uchar *esInfoLength; void IncCounter(int &Counter, uchar *TsPacket); void IncVersion(int &Version); void IncEsInfoLength(int Length); protected: int MakeStream(uchar *Target, uchar Type, int Pid); int MakeAC3Descriptor(uchar *Target); int MakeSubtitlingDescriptor(uchar *Target, const char *Language); int MakeLanguageDescriptor(uchar *Target, const char *Language); int MakeCRC(uchar *Target, const uchar *Data, int Length); public: cPatPmtGenerator(void); void GeneratePat(void); ///< Generates a PAT section for later use with GetPat(). ///< This function is called by default from the constructor. void GeneratePmt(tChannelID ChannelID); ///< Generates a PMT section for the given ChannelId, for later use ///< with GetPmt(). uchar *GetPat(void); ///< Returns a pointer to the PAT section, which consist of exactly ///< one TS packet. uchar *GetPmt(int &Index); ///< Returns a pointer to the Index'th TS packet of the PMT section. ///< Index must be initialized to 0 and will be incremented by each ///< call to GetPmt(). Returns NULL is all packets of the PMT section ///< have been fetched.. }; // PAT/PMT Parser: class cPatPmtParser { private: uchar pmt[MAX_SECTION_SIZE]; int pmtSize; int pmtPid; int vpid; int vtype; protected: int SectionLength(const uchar *Data, int Length) { return (Length >= 3) ? ((int(Data[1]) & 0x0F) << 8)| Data[2] : 0; } public: cPatPmtParser(void); void ParsePat(const uchar *Data, int Length); ///< Parses the given PAT Data, which is the payload of a single TS packet ///< from the PAT stream. The PAT may consist only of a single TS packet. void ParsePmt(const uchar *Data, int Length); ///< Parses the given PMT Data, which is the payload of a single TS packet ///< from the PMT stream. The PMT may consist of several TS packets, which ///< are delivered to the parser through several subsequent calls to ///< ParsePmt(). The whole PMT data will be processed once the last packet ///< has been received. int PmtPid(void) { return pmtPid; } ///< Returns the PMT pid as defined by the current PAT. ///< If no PAT has been received yet, -1 will be returned. int Vpid(void) { return vpid; } ///< Returns the video pid as defined by the current PMT. int Vtype(void) { return vtype; } }; #endif // TS to PES converter: // Puts together the payload of several TS packets that form one PES // packet. class cTsToPes { private: uchar *data; int size; int length; int offset; bool synced; public: cTsToPes(void); ~cTsToPes(); void PutTs(const uchar *Data, int Length); ///< Puts the payload data of the single TS packet at Data into the converter. ///< Length is always 188. ///< If the given TS packet starts a new PES payload packet, the converter ///< will be automatically reset. Any packets before the first one that starts ///< a new PES payload packet will be ignored. const uchar *GetPes(int &Length); ///< Gets a pointer to the complete PES packet, or NULL if the packet ///< is not complete yet. If the packet is complete, Length will contain ///< the total packet length. The returned pointer is only valid until ///< the next call to PutTs() or Reset(), or until this object is destroyed. void Reset(void); ///< Resets the converter. This needs to be called after a PES packet has ///< been fetched by a call to GetPes(), and before the next call to ///< PutTs(). }; // Some helper functions for debugging: void BlockDump(const char *Name, const u_char *Data, int Length); void TsDump(const char *Name, const u_char *Data, int Length); void PesDump(const char *Name, const u_char *Data, int Length); } #endif // __VDR172REMUX_H #endif xine-0.9.4/MANUAL0000644000000000000000000004444611537763173012116 0ustar rootroot Please read the file "INSTALL" for getting the stuff working a first time :-) SETUP-MENU ========== Live-TV SD video buffer [frames]: 4 Live-TV HD video buffer [frames]: 4 Live-TV audio buffer [frames]: 4 Playing live-TV requires a buffer for having data ready when xine needs it for decoding. Without such a buffer or when it is not large enough, replaying live TV may not be fluently and may degrade into a slide show without sound. On the other hand, as buffering takes place before replaying, a too large buffer slows down zapping as it takes longer before replaying is started. So one may need to play with this value to find a suitable setting. The buffer size is specified in video frames, where 25 video frames make up a buffer which can hold one second of audio and video. Please note that this buffer is provided by VDR and xine so a too large setting may cause an overflow (check VDR's logfile for buffer usage). It is therefore recommended to increase xine's input buffer settings in ~/.xine/config. See engine.buffers.video_num_buffers and maybe engine.buffers.audio_num_buffers. A simple but stupid rule is to increase buffers by multipling the default numbers by 10 -- some HD channels require at least 1500 video buffers. As mentioned earlier, choosing a suitable Live-TV buffer is a compromise, which is easier to achieve when there can be separate values for different services like SD / HD video or radio (audio). Buffer hysteresis [frames]: 4 Buffer monitoring is a feature of vdr-xine which tries to dynamically increase the buffer for certain channels on demand, i. e. whenever the buffer size drops below the configured value of "Live-TV buffer" frames. As a result, a new buffer will be established which is of size "Live-TV buffer + hysteresis" frames, and in the case, where this buffer is still not large enough, vdr-xine will internally increase hysteresis each time by one frame so that finally a buffer is established which perfectly fits for the current channel. Buffer monitoring duration [s]: 10 Typically, the above mentioned buffer monitoring is only necessary for a certain amount of time after switching the channel, because once the buffer is established, it should stay constant as the amount of data put into the buffer by VDR and the amout of data taken from the buffer by xine should be equal. A value of 0 disables buffer monitoring. Buffer monitoring mode: Once Lets you choose how buffer monitoring is applied. * Once After the above mentioned time, buffer monitoring will be bypassed (which reduces CPU and memory load) until a channel switch or audio track selection occurs. * Continuous Buffer monitoring will never be bypassed. After the above mentioned time the internal hysteresis value will be reset to the configured one, to be ready for the next buffering cycle which starts when the buffer size falls below the above mentioned "Live-TV buffer" value. This mode is useful for channels which degrade into a slide show after a certain amout of time. OSD display mode: Blend scaled AUTO Lets you choose among several processing options for VDR's OSD. * X11 overlay Tells xine to use a method for displaying the OSD that is independent of the stream's video resolution. In this so called "unscaled" mode, xine uses a X11 window to overlay the OSD on the video window. The advantage of mapping a single OSD pixel to a single pixel on screen has the disadvantage of not being able to support semi-transparent areas which appear totally opaque in this mode. NOTE: You won't see any OSD in this mode if X11 is not available! * Blend clipped * Blend scaled LQ * Blend scaled HQ * Blend scaled SHQ * Blend scaled AUTO For these modes xine uses the CPU to blend the OSD into each video frame. As the result depends on the stream's video resolution you may choose among several modes which require a different amount of CPU time. The first mode simply cuts off all parts of the OSD that do not fit into the video frame. If for example an OSD of size 720x576 is to be blended into a frame of size 480x576 (e. g. VIVA broadcasts at this resolution) then almost one halve of the OSD will be dropped at the right and the OSD will be quite stretched in horizontal direction. All other modes scale the OSD to fit into the video frame. The difference among them is the scaling quality (Low, High and Super High) which also leads to increasing CPU load and slows down e. g. navigation in the channels list, etc. The last mode automatically chooses between HQ and SHQ depending on the stream's video resolution. SHQ will be chosen if either width or height are below 360x288. NOTE: Blend scaled is only implemented for VDR 1.3.x (1.3.7 and higher). OSD gamma correction [ 123 => 1.23 ]: 123 When OSD scaling is performed then multiple pixels (or parts of them for SHQ) have to be combined into a single one. During this process a so called gamma correction is applied in order to give the resulting pixels the correct visual representation of the original ones. You may adjust this correction within the range 1.00 to 2.50 (by entering 100 to 250 respectively) to get the best visual representation on your monitor. Changing this value is most noticeable in SHQ scaling mode so you need to watch a channel that doesn't broadcast at 720x576 in order to activate OSD scaling and to be able to see any change concerning gamma correction. OSD extent X: 720 ***** REQUIRES VDR >= 1.7.7 ***** OSD extent Y: 576 ***** REQUIRES VDR >= 1.7.7 ***** For a crisp OSD on HD displays increase these values to match your displays resolution. Valid extents are in the range from 320 x 240 up to 1920 x 1080. 4:3 image zoom X [%]: 100 4:3 image zoom Y [%]: 100 16:9 image zoom X [%]: 100 16:9 image zoom Y [%]: 100 These options may be useful to stretch the video image to fill the screen. A value of 133 for example will remove the black borders when showing a 4:3 image on a 16:9 screen. The drawback is that a part of the images is cut away on top and bottom for example. So it may be useful to have different values for X and Y (e. g. 133 and 115). Audio mode: Dolby on ***** OBSOLETE FOR VDR >= 1.3.18 ***** With this option you can control feeding dolby audio data to xine. You may want to use this option if you don't have the necessary replay equipment connected to your computer. In that way you can force xine to use a differently coded audio source among the supplied e. g. mp2 or pcm. Control xine's volume: Yes (by hardware) Allows you to control whether xine shall honor VDR's set volume requests. You might need this if you have a special setup (e. g. external audio decoder or amplifier) where changing the volume in xine might mute external audio: * No xine isn't instructed to change the volume. * Yes (by hardware) xine will change the volume by changing the hardware mixer (won't work when using a digital audio output). * Yes (by software) xine will change the volume by using it's internal software mixer (should work even when using a digital audio output). Muting: Simulate Lets you choose among several options how xine shall process VDR's muting requests: * Ignore Muting respectively unmuting requests will be ignored. * Execute Muting respectively unmuting requests will be executed. * Simulate Muting is simulated by setting volume to 0. Unmuting is simulated by restoring the previous volume. NOTE: This happens even if "Control xine's volume" is set to "No"! Get primary device when xine connects: Yes ***** REQUIRES VDR >= 1.3.32 ***** This option is especially useful for owners of full featured DVB cards which usually run the OSD on a full featured card (i. e. the full featured card is the primary device). With this option set to Yes, vdr-xine automatically makes itself the primary device while xine is connected to it. In that way the OSD and live TV are automatically available via vdr-xine without the need to manually switch the primary device via remote control nor SVDRP interface. Support semi transparent colors: Yes Depending on the currently broadcast image the displayed OSD might be hardly readable when the OSD is blended semi transparently into the TV image. If this option is set to No, vdr-xine simply ignores the semi-transparent component of the color and therefore makes such colors opaque which typically makes the OSD easier to read. Connection interacts with EIT scanner: No This option may be useful for users of single card systems. When xine connects to vdr-xine, a currently running EPG scan will be interrupted to get into live TV mode immediately. On the other hand, when VDR starts an EPG scan, vdr-xine will shutdown the connection to xine. COMMAND LINE ARGS ================= There are currently two optional arguments. -r Enable remote control. This argument enables Simon Truss' patch which allows controlling VDR by pressing buttons in xine. It will also allow control from any other suitably patched front end. -iN Instance number for FIFO directory If you want to run two instances of vdr-xine (in different VDR processes) on the same computer then you have to use a unique FIFO dir for each instance. For example "-i3" will append "3" to the FIFO_DIR given in Makefile. The MRL will then have to be like that: "vdr://tmp/vdr-xine3/stream#demux:mpeg_pes". -q Suppress debug messages on console This option is useful if VDR's console is to be used for other output e. g. for the OSD implementation of VDR's skin 'curses'. -s Switch to curses skin while xine is not connected Use this switch if it is useful to control VDR via it's controlling terminal while xine is not connected to vdr-xine. Requires VDR >= 1.3.20. -XN default 'SizeX' for GRAB command (720, 1..4096) -YN default 'SizeY' for GRAB command (576, 1..4096) With these switches you may change the default image size for the grabbing the current video frame (see VDR's SVDRP command GRAB for details). -p[N] use socket connections on port N (18701) -bIP ip address to bind for socket connections (see -p) These options control whether vdr-xine shall use sockets instead of fifos, which allows you to run xine on a different computer than vdr-xine. To enable socket connections, supply the option -p. This will use the default TCP ports 18701, 18702, 18703 and 18704. You may optionally provide a different base port number and vdr-xine will use the next three ports too. By default, vdr-xine will listen on all network interfaces. If you want to limit connections to a certain inferface, specify option -b with the IP address this interface. The MRL for xine looks then like that (:port is optional): netvdr://host:port XINE KEYBINDINGS ================ To make use of the remote control feature, you'll have to assign keys in xine to VDR's commands. Therefore, you'll find 36 keybindings in xine's key binding editor, named 'VDR ...', which control the specified action in VDR. Besides those, the following entries are also used for VDR: 'enter the number 0' .. 'enter the number 9' 'jump to media menu' 'menu navigate up' 'menu navigate down' 'menu navigate left' 'menu navigate right' 'menu select' 'jump to next chapter' 'jump to previous chapter' GXINE KEYBINDINGS ================= And similarly for gxine... You'll find several VDR-specific keybindings in gxine's key binding editor: Used for VDR VDR-specific "Play" Menu bindings "Red" "Pause" Number key bindings "Green" "Stop" "Volume +" "Yellow" "Up" "Volume -" "Blue" "Down" "Mute" "Record" "Left" "Power" "Right" "Select/OK" "Back" "Rewind / Back 1 min" "Audio" "Fast forward / Forward 1 min" You'll notice that the menu bindings have their VDR functions in [brackets]. If not all of these bindings are present, try "Add new default bindings" from the Reload menu; if they still aren't, you're running an older version of gxine. The volume control bindings assume that VDR is passing volume control events back to gxine - in the xine plugin's configuration, you need Control xine's volume Yes Muting Execute for these bindings to work. XINE-LIB CONFIG =============== This applies whether you're using xine, gxine or some other xine-lib front end. xine uses large buffers to gain smooth playback. The drawback is that VDR puts a lot of data into xine's buffers and therefore VDR's progress indicator is way ahead of the position at which xine is currently playing. For a recording of a radio channel, xine's default buffers will cause an offset of about 16 seconds. But this can easily be improved (with almost no noticeable effects) to less than 2 seconds by adjusting xine's number of audio buffers. Just edit your xine config "~/.xine/config" and add or change the following entry: engine.buffers.audio_num_buffers:4 The value '4' is the smallest possible value. You may increase it, in case that you experience noticeable distortions in audio playback. Another interesting option might be the following: audio.synchronization.av_sync_method:resample It should totally avoid distortions by adjusting audio data to fill any gaps. But it's only usable if your amplifier is not connected via SPDIF. Another useful option on less powerful machines is the following: video.output.disable_exact_alphablend:1 The result will be that a less exact algorithm is used for blending the OSD into each video frame and thus CPU time is saved. XINE COMMAND LINE OPTIONS ========================= This section is not intended as a replacement for xine's documentation, but it may be useful to have a starting point for further reading. So, some useful command line options are listed below. Options for xine (X11 required): -V vdpau (use hardware mpeg2/h264 video decoding acceleration) -V xxmc (use hardware mpeg2 video decoding acceleration) -V xv (use video overlay -- the usual case) -V xshm (should always provide a picture) -V vidix (for normal TV on my Matrox G550) -A alsa (use alsa sound system) --post vdr_video (highly recommended for correct and immediate OSD scaling especially when using "xineplayer". It further enables video scaling and positioning as used within yaepg as well as applying the preconfigured zoom values for 16:9 and 4:3 content) --post vdr_audio (highly recommended for selecting left / right stereo channel) -D (enable selected deinterlacer) -Dtvtime:method=Greedy2Frame,cheap_mode=0,pulldown=0,use_progressive_frame_flag=1 (use specified deinterlacer) -L (disable LIRC support) Options for gxine (X11 required): -V vdpau (use hardware mpeg2/h264 video decoding acceleration) -V xxmc (use hardware mpeg2 video decoding acceleration) -V xv (use video overlay -- the usual case) -V xshm (should always provide a picture) -V vidix (for normal TV on my Matrox G550) -A alsa (use alsa sound system) Other options must (as of 0.4.4) be configured from within gxine. Options for fbxine (no X11 required): -V vidixfb (for normal TV on my Matrox G550) -V fb (for watching HDTV) -V dxr3 (to use a DXR3/Hollywood+ hardware MPEG decoder) -A alsa (use alsa sound system) --post vdr_video (highly recommended for correct and immediate OSD scaling especially when using "xineplayer". It further enables video scaling and positioning as used within yaepg as well as applying the preconfigured zoom values for 16:9 and 4:3 content) --post vdr_audio (highly recommended for selecting left / right stereo channel) -D (enable selected deinterlacer) -Dtvtime:method=Greedy2Frame,cheap_mode=0,pulldown=0,use_progressive_frame_flag=1 (use specified deinterlacer) -L (disable LIRC support) The default deinterlacer doesn't take too much CPU time, but on the other hand it doesn't deinterlace in all situations (e. g. when there is only a small area with significant movement on the screen). The specified deinterlacer does a good job, but requires much CPU time. If you don't like VDR's OSD to be stretched when playing 16:9 material you might also use xine's "expand" post processing plugin. It simply puts the 16:9 images into an 4:3 image (adding a black bar on top and bottom) and therefore the OSD will keep it's aspect ratio. To make use of the plugin add the following options in this order on (fb)xine's command line: xine ... --post expand --post vdr_video ... The expand plugin now also supports a feature called "centre cut out" which crops away the black bars around the image when 4:3 material is broadcast in a 16:9 stream and displayed on a 4:3 monitor. The command line looks then like the following: xine ... --post expand:centre_cut_out_mode=1 --post vdr_video ... If you want to listen to mono audio streams in stereo, I'd suggest to use the xine's upmix_mono audio post plugin like that: xine ... --post vdr_audio --post upmix_mono ... XINEPLAYER ========== xineplayer is a companion of vdr-xine and is used to get the beloved mplayer plugin working with vdr-xine. I. e. you'll be able to replay DivX movies directly through xine without the need for CPU expensive recoding. And you'll still be able to continue using VDR's OSD while the external file is playing. To get it working just install the mplayer plugin. Then edit it's "mplayer.sh" and replace # where to find mplayer MPLAYER="mplayer" with # where to find mplayer MPLAYER="xineplayer" and now you'll only have to make sure that xineplayer is found by your shell. xineplayer was built in vdr-xine's source directory so you'll either have to copy it to a directory which is contained in your environment variable PATH or just enter the absolute path to xineplayer into mplayer.sh as mentioned above. That's it. NOTE: xineplayer is still under development and currently only supports mplayer plugin's TRADITIONAL mode. Furthermore it ignores any parameter given on the command line besides the last one and expects this to be a MRL recognizable by xine (e. g. a filename). If xine doesn't know how to play the given MRL you'll only see an error message on xine's console. As vdr-xine supports an instance number to create an unique FIFO directory it will also necessary to tell this number to xineplayer to have it control the right instance of vdr-xine. xineplayer's command line looks like that: xineplayer [ --vdr-xine-instance=N ] [ options ] mrl NOTE: "--vdr-xine-instance" must be given as the first argument as it might otherwise collide with further options originally intended for mplayer. xine-0.9.4/xine.c0000644000000000000000000002005311515314540012273 0ustar rootroot/* * xine.c: A plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * * $Id$ */ #include "xineCommon.h" #include #include "xineDevice.h" #include "xineSettings.h" #include "xineSetupPage.h" #include "xineI18n.h" static const char *VERSION = "0.9.4"; static const char *DESCRIPTION = tr("Software based playback using xine"); //static const char *MAINMENUENTRY = "xine - Toggle prebuffer setting"; class cPluginXine : public cPlugin { private: // Add any member variables or functions you may need here. PluginXine::cXineSettings m_settings; PluginXine::cXineRemote *m_remote; bool m_remoteOn; public: PluginXine::cXineLib *m_xineLib; int m_instanceNo; in_addr_t m_bindIp; int m_bindPort; cPluginXine(void); virtual ~cPluginXine(); virtual const char *Version(void) { return VERSION; } virtual const char *Description(void) { return tr(DESCRIPTION); } virtual const char *CommandLineHelp(void); virtual bool ProcessArgs(int argc, char *argv[]); virtual bool Initialize(void); virtual bool Start(void); virtual void Stop(void); virtual void Housekeeping(void); #if APIVERSNUM >= 10347 virtual void MainThreadHook(void); virtual cString Active(void); virtual time_t WakeupTime(void); #endif virtual const char *MainMenuEntry(void);// { return tr(MAINMENUENTRY); } virtual cOsdObject *MainMenuAction(void); virtual cMenuSetupPage *SetupMenu(void); virtual bool SetupParse(const char *Name, const char *Value); #if APIVERSNUM >= 10330 virtual bool Service(const char *Id, void *Data = NULL); virtual const char **SVDRPHelpPages(void); virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode); #endif }; cPluginXine::cPluginXine(void) : cPlugin() , m_remote(0) , m_remoteOn(false) , m_xineLib(0) , m_instanceNo(-1) , m_bindIp(0) , m_bindPort(0) { // Initialize any member variables here. // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! } cPluginXine::~cPluginXine() { // Clean up after yourself! } const char *cPluginXine::CommandLineHelp(void) { //Return a string that describes all known command line options. //" - -- x \n" return " -bIP ip address to bind for socket connections (see -p)\n" " -iN instance number to append to FIFO directory\n" " -p[N] use socket connections on port N (18701)\n" " -q turn off debug messages on console\n" " -r turn on remote (pressing keys in xine controls VDR)\n" #if APIVERSNUM >= 10320 " -s switch to curses skin, while xine is disconnected\n" #endif " -XN default 'SizeX' for GRAB command (720, 1..4096)\n" " -YN default 'SizeY' for GRAB command (576, 1..4096)\n" ; } bool cPluginXine::ProcessArgs(int argc, char *argv[]) { // Implement command line argument processing here if applicable. ::optind = 0; ::opterr = 0; #define INVALID_ARG(fmt, args...) do { esyslog(fmt, ##args); fprintf(stderr, fmt "\n", ##args); } while (false) for (int r = -1; (r = ::getopt(argc, argv, ":b:i:p::qrsX:Y:")) >= 0; ) { switch (r) { case ':': INVALID_ARG("vdr-xine: missing argument for option -%c", ::optopt); return false; case 'b': m_bindIp = ::inet_addr(::optarg); if (m_bindIp == INADDR_NONE) { INVALID_ARG("vdr-xine: invalid argument '%s' for option -%c", ::optarg, r); return false; } break; case 'i': m_instanceNo = ::atoi(::optarg); if (m_instanceNo < 0) { INVALID_ARG("vdr-xine: invalid argument '%s' for option -%c", ::optarg, r); return false; } break; case 'p': if (!::optarg) m_bindPort = 18701; else { m_bindPort = ::atoi(::optarg); if (m_bindPort <= 0) { INVALID_ARG("vdr-xine: invalid argument '%s' for option -%c", ::optarg, r); return false; } } break; case 'r': m_remoteOn = true; break; #if APIVERSNUM >= 10320 case 's': m_settings.SetSwitchSkin(true); break; #endif case 'q': m_settings.SetBeQuiet(true); break; case 'X': { const int X = ::atoi(::optarg); if (X < 1 || X > 4096) { INVALID_ARG("vdr-xine: invalid argument '%s' for option -%c", ::optarg, r); return false; } m_settings.SetDefaultGrabSizeX(X); } break; case 'Y': { const int Y = ::atoi(::optarg); if (Y < 1 || Y > 4096) { INVALID_ARG("vdr-xine: invalid argument '%s' for option -%c", ::optarg, r); return false; } m_settings.SetDefaultGrabSizeY(Y); } break; default: INVALID_ARG("vdr-xine: invalid option -%c", r); return false; } } if (argv[::optind]) { INVALID_ARG("vdr-xine: invalid argument '%s'", argv[::optind]); return false; } #undef INVALID_ARG return true; } bool cPluginXine::Initialize(void) { #if APIVERSNUM < 10507 RegisterI18n(PluginXine::Phrases); #endif // Initialize any background activities the plugin shall perform. m_remote = new PluginXine::cXineRemote(m_remoteOn); if (!m_remote) return false; if (!PluginXine::cXineDevice::Create(this, m_settings, m_remote)) return false; return true; } bool cPluginXine::Start(void) { // Start any background activities the plugin shall perform. if (!PluginXine::cXineDevice::Open()) return false; return true; } void cPluginXine::Stop(void) { PluginXine::cXineDevice::Stop(); } void cPluginXine::Housekeeping(void) { // Perform any cleanup or other regular tasks. } #if APIVERSNUM >= 10347 void cPluginXine::MainThreadHook(void) { // Perform actions in the context of the main program thread. // WARNING: Use with great care - see PLUGINS.html! PluginXine::cXineDevice::MainMenuTrampoline(); } cString cPluginXine::Active(void) { // Return a message string if shutdown should be postponed return NULL; } time_t cPluginXine::WakeupTime(void) { // Return custom wakeup time for shutdown script return 0; } #endif cOsdObject *cPluginXine::MainMenuAction(void) { // Perform the action when selected from the main VDR menu. #if APIVERSNUM < 10347 PluginXine::cXineDevice::MainMenuTrampoline(); #endif return NULL; } cMenuSetupPage *cPluginXine::SetupMenu(void) { // Return a setup menu in case the plugin supports one. return new PluginXine::cXineSetupPage(m_xineLib, m_settings); } bool cPluginXine::SetupParse(const char *Name, const char *Value) { // Parse your own setup parameters and store their values. return m_settings.SetupParse(Name, Value); } #if APIVERSNUM >= 10330 bool cPluginXine::Service(const char *Id, void *Data) { // Handle custom service requests from other plugins return cPlugin::Service(Id, Data); } const char **cPluginXine::SVDRPHelpPages(void) { // Return help text for SVDRP commands this plugin implements return cPlugin::SVDRPHelpPages(); } cString cPluginXine::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) { // Process SVDRP commands this plugin implements return cPlugin::SVDRPCommand(Command, Option, ReplyCode); } #endif const char *cPluginXine::MainMenuEntry(void) { // return m_settings.GetMainMenuEntry(); return 0; } namespace PluginXine { int GetBindIp(cPlugin *const plugin) { return ((cPluginXine *)plugin)->m_bindIp; } int GetBindPort(cPlugin *const plugin) { return ((cPluginXine *)plugin)->m_bindPort; } int GetInstanceNo(cPlugin *const plugin) { return ((cPluginXine *)plugin)->m_instanceNo; } cXineLib *&GetXineLib(cPlugin *const plugin) { return ((cPluginXine *)plugin)->m_xineLib; } } VDRPLUGINCREATOR(cPluginXine); // Don't touch this! xine-0.9.4/xineSettings.c0000644000000000000000000005550011210731642014017 0ustar rootroot #include "xineCommon.h" #include #include #include "xineSettings.h" #include "xineSetupPage.h" #include "xineDevice.h" namespace PluginXine { cXineSettings::cModeParams::cModeParams() : m_prebufferFramesVideoSD(4) , m_prebufferFramesVideoHD(4) , m_prebufferFramesAudio(4) , m_prebufferHysteresis(4) , m_monitoringDuration(10) , m_monitoringMode(monitoringOnce) { } bool cXineSettings::cModeParams::SetupParse(const char *optionName, int &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; char c; int v = 0; if (1 != ::sscanf(Value, "%d%c", &v, &c)) return false; if (0 <= v && v <= 100) { optionValue = v; return true; } return false; } bool cXineSettings::cModeParams::SetupParse(const char *optionName, eMonitoringMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("monitoringOnce", Value)) { optionValue = monitoringOnce; return true; } if (0 == ::strcasecmp("monitoringContinuous", Value)) { optionValue = monitoringContinuous; return true; } return false; } bool cXineSettings::cModeParams::SetupParse(const char *prefix, const char *Name, const char *Value) { const int prefixLen = ::strlen(prefix); if (0 != ::strncasecmp(prefix, Name, prefixLen)) return false; Name += prefixLen; if (SetupParse("prebufferFrames", m_prebufferFramesVideoSD, Name, Value) && SetupParse("prebufferFrames", m_prebufferFramesVideoHD, Name, Value) && SetupParse("prebufferFrames", m_prebufferFramesAudio, Name, Value)) { return true; } if (SetupParse("prebufferFramesVideoSD", m_prebufferFramesVideoSD, Name, Value)) return true; if (SetupParse("prebufferFramesVideoHD", m_prebufferFramesVideoHD, Name, Value)) return true; if (SetupParse("prebufferFramesAudio", m_prebufferFramesAudio, Name, Value)) return true; if (SetupParse("prebufferHysteresis", m_prebufferHysteresis, Name, Value)) return true; if (SetupParse("monitoringDuration", m_monitoringDuration, Name, Value)) return true; if (SetupParse("monitoringMode", m_monitoringMode, Name, Value)) return true; return false; } bool cXineSettings::cModeParams::MonitoringContinuous() const { return monitoringContinuous == m_monitoringMode; } cXineSettings::cZoomParams::cZoomParams() : m_zoomX(imageZoomDefault) , m_zoomY(imageZoomDefault) { } bool cXineSettings::cZoomParams::SetupParse(const char *optionName, eImageZoom &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; char c; int v = 0; if (1 != ::sscanf(Value, "%d%c", &v, &c)) return false; if (imageZoomMin <= v && v <= imageZoomMax) { optionValue = (eImageZoom)v; return true; } return false; } bool cXineSettings::cZoomParams::SetupParse(const char *prefix, const char *Name, const char *Value) { const int prefixLen = ::strlen(prefix); if (0 != ::strncasecmp(prefix, Name, prefixLen)) return false; Name += prefixLen; if (SetupParse("zoomX", m_zoomX, Name, Value)) return true; if (SetupParse("zoomY", m_zoomY, Name, Value)) return true; return false; } bool cXineSettings::cZoomParams::operator !=(const cXineSettings::cZoomParams &rhs) const { return GetZoomX() != rhs.GetZoomX() || GetZoomY() != rhs.GetZoomY(); } cXineSettings::cOsdExtentParams::cOsdExtentParams() : m_osdExtentWidth(osdExtentWidthDefault) , m_osdExtentHeight(osdExtentHeightDefault) { } bool cXineSettings::cOsdExtentParams::SetupParse(const char *optionName, eOsdExtentWidth &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; char c; int v = 0; if (1 != ::sscanf(Value, "%d%c", &v, &c)) return false; if (osdExtentWidthMin <= v && v <= osdExtentWidthMax) { optionValue = (eOsdExtentWidth)v; return true; } return false; } bool cXineSettings::cOsdExtentParams::SetupParse(const char *optionName, eOsdExtentHeight &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; char c; int v = 0; if (1 != ::sscanf(Value, "%d%c", &v, &c)) return false; if (osdExtentHeightMin <= v && v <= osdExtentHeightMax) { optionValue = (eOsdExtentHeight)v; return true; } return false; } bool cXineSettings::cOsdExtentParams::SetupParse(const char *prefix, const char *Name, const char *Value) { const int prefixLen = ::strlen(prefix); if (0 != ::strncasecmp(prefix, Name, prefixLen)) return false; Name += prefixLen; if (SetupParse("X", m_osdExtentWidth, Name, Value)) return true; if (SetupParse("Y", m_osdExtentHeight, Name, Value)) return true; return false; } bool cXineSettings::cOsdExtentParams::operator !=(const cXineSettings::cOsdExtentParams &rhs) const { return GetOsdExtentWidth() != rhs.GetOsdExtentWidth() || GetOsdExtentHeight() != rhs.GetOsdExtentHeight(); } cXineSettings::cXineSettings() : m_switchSkin(false) , m_beQuiet(false) , m_defaultGrabSizeX(720) , m_defaultGrabSizeY(576) , m_osdMode(osdBlendScaledAuto) , m_usageMode(modeLiveTV) // , m_usageModeDefault(modeLiveTV) , m_audioMode(audioDolbyOff) , m_volumeMode(volumeChangeHW) , m_muteMode(muteSimulate) , m_crtGamma(monitorGammaDefault) , m_autoPrimaryDeviceMode(autoPrimaryDeviceOn) , m_transparencyMode(transparencyOn) , m_interactWithEitScannerMode(interactWithEitScannerOff) { // m_modeParams[ modeLiveTV ].m_prebufferFrames = 4; //2 * 15 + 1; // m_modeParams[ modeLiveTV ].m_prebufferHysteresis = 4; //2 * 15 + 1; } bool cXineSettings::SetupParse(const char *optionName, eUsageMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("modeLiveTV", Value)) { optionValue = modeLiveTV; return true; } if (0 == ::strcasecmp("modeReplay", Value)) { optionValue = modeReplay; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eVolumeMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("volumeChangeHW", Value)) { optionValue = volumeChangeHW; return true; } if (0 == ::strcasecmp("volumeChangeSW", Value)) { optionValue = volumeChangeSW; return true; } if (0 == ::strcasecmp("volumeIgnore", Value)) { optionValue = volumeIgnore; return true; } // backward compatibilty if (0 == ::strcasecmp("volumeChange", Value)) { optionValue = volumeChangeHW; return true; } if (0 == ::strcasecmp("volumeDontTouch", Value)) { optionValue = volumeIgnore; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eMuteMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("muteExecute", Value)) { optionValue = muteExecute; return true; } if (0 == ::strcasecmp("muteSimulate", Value)) { optionValue = muteSimulate; return true; } if (0 == ::strcasecmp("muteIgnore", Value)) { optionValue = muteIgnore; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eOsdMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("osdOverlay", Value)) { optionValue = osdOverlay; return true; } if (0 == ::strcasecmp("osdBlendClipped", Value)) { optionValue = osdBlendClipped; return true; } if (0 == ::strcasecmp("osdBlendScaledLQ", Value)) { optionValue = osdBlendScaledLQ; return true; } if (0 == ::strcasecmp("osdBlendScaledHQ", Value)) { optionValue = osdBlendScaledHQ; return true; } if (0 == ::strcasecmp("osdBlendScaledSHQ", Value)) { optionValue = osdBlendScaledSHQ; return true; } if (0 == ::strcasecmp("osdBlendScaledAuto", Value)) { optionValue = osdBlendScaledAuto; return true; } // backward compatibilty if (0 == ::strcasecmp("osdScaled", Value)) { optionValue = osdBlendScaledAuto; return true; } if (0 == ::strcasecmp("osdUnscaled", Value)) { optionValue = osdOverlay; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eMonitorGamma &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; char c; int v = 0; if (1 != ::sscanf(Value, "%d%c", &v, &c)) return false; if (monitorGammaMin <= v && v <= monitorGammaMax) { optionValue = (eMonitorGamma)v; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eAudioMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("audioDolbyOff", Value)) { optionValue = audioDolbyOff; return true; } if (0 == ::strcasecmp("audioDolbyOn", Value)) { optionValue = audioDolbyOn; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eAutoPrimaryDeviceMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("autoPrimaryDeviceOff", Value)) { optionValue = autoPrimaryDeviceOff; return true; } if (0 == ::strcasecmp("autoPrimaryDeviceOn", Value)) { optionValue = autoPrimaryDeviceOn; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eInteractWithEitScannerMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("interactWithEitScannerOff", Value)) { optionValue = interactWithEitScannerOff; return true; } if (0 == ::strcasecmp("interactWithEitScannerOn", Value)) { optionValue = interactWithEitScannerOn; return true; } return false; } bool cXineSettings::SetupParse(const char *optionName, eTransparencyMode &optionValue, const char *Name, const char *Value) { if (0 != ::strcasecmp(optionName, Name)) return false; if (0 == ::strcasecmp("transparencyOff", Value)) { optionValue = transparencyOff; return true; } if (0 == ::strcasecmp("transparencyOn", Value)) { optionValue = transparencyOn; return true; } return false; } bool cXineSettings::SetupParse(const char *Name, const char *Value) { if (m_modeParams[ modeLiveTV ].SetupParse("modeLiveTV.", Name, Value)) return true; /* if (m_modeParams[ modeReplay ].SetupParse("modeReplay.", Name, Value)) return true; if (SetupParse("usageModeDefault", m_usageModeDefault, Name, Value)) { m_usageMode = m_usageModeDefault; return true; } */ if (m_zoomParams[ image4_3 ].SetupParse("image4:3.", Name, Value)) return true; if (m_zoomParams[ image16_9 ].SetupParse("image16:9.", Name, Value)) return true; if (SetupParse("osdMode", m_osdMode, Name, Value)) return true; if (SetupParse("osdGammaCorrection", m_crtGamma, Name, Value)) return true; #if APIVERSNUM >= 10707 if (m_osdExtentParams.SetupParse("osdExtent.", Name, Value)) return true; #endif #if APIVERSNUM < 10318 if (SetupParse("audioMode", m_audioMode, Name, Value)) return true; #endif if (SetupParse("volumeMode", m_volumeMode, Name, Value)) return true; if (SetupParse("muteMode", m_muteMode, Name, Value)) return true; #if APIVERSNUM >= 10332 if (SetupParse("autoPrimaryDeviceMode", m_autoPrimaryDeviceMode, Name, Value)) return true; #endif if (SetupParse("transparencyMode", m_transparencyMode, Name, Value)) return true; if (SetupParse("interactWithEitScannerMode", m_interactWithEitScannerMode, Name, Value)) return true; return false; } const cXineSettings::cModeParams *cXineSettings::GetModeParams() const { return &m_modeParams[ m_usageMode ]; } cXineSettings::eOsdMode cXineSettings::OsdMode() const { return m_osdMode; } cXineSettings::eVolumeMode cXineSettings::VolumeMode() const { return m_volumeMode; } cXineSettings::eMuteMode cXineSettings::MuteMode() const { return m_muteMode; } bool cXineSettings::AudioDolbyOn() const { #if APIVERSNUM >= 10318 return true; #else return audioDolbyOn == m_audioMode; #endif } void cXineSettings::Create(cXineSetupPage *const setupPage) { static const char *osdModes[ 6 ]; osdModes[ osdOverlay ] = tr("X11 overlay"); osdModes[ osdBlendClipped ] = tr("Blend clipped"); osdModes[ osdBlendScaledLQ ] = tr("Blend scaled LQ"); osdModes[ osdBlendScaledHQ ] = tr("Blend scaled HQ"); osdModes[ osdBlendScaledSHQ ] = tr("Blend scaled SHQ"); osdModes[ osdBlendScaledAuto ] = tr("Blend scaled Auto"); static const char *volumeModes[ 3 ]; volumeModes[ volumeIgnore ] = tr("No"); volumeModes[ volumeChangeHW ] = tr("Yes (by hardware)"); volumeModes[ volumeChangeSW ] = tr("Yes (by software)"); static const char *muteModes[ 3 ]; muteModes[ muteIgnore ] = tr("Ignore"); muteModes[ muteExecute ] = tr("Execute"); muteModes[ muteSimulate ] = tr("Simulate"); setupPage->Add(new cMenuEditIntItem(tr("Live-TV SD video buffer [frames]"), &m_modeParams[ modeLiveTV ].m_prebufferFramesVideoSD, 0, 50)); setupPage->Add(new cMenuEditIntItem(tr("Live-TV HD video buffer [frames]"), &m_modeParams[ modeLiveTV ].m_prebufferFramesVideoHD, 0, 50)); setupPage->Add(new cMenuEditIntItem(tr("Live-TV audio buffer [frames]"), &m_modeParams[ modeLiveTV ].m_prebufferFramesAudio, 0, 50)); // setupPage->Add(new cMenuEditIntItem(tr("Replay prebuffer [frames]"), &m_modeParams[ modeReplay ].m_prebufferFrames, 0, 50)); // setupPage->Add(new cMenuEditBoolItem(tr("Default settings optimized for"), (int *)&m_usageModeDefault, tr("Live-TV"), tr("Replay"))); setupPage->Add(new cMenuEditIntItem(tr("Buffer hysteresis [frames]"), &m_modeParams[ modeLiveTV ].m_prebufferHysteresis, 0, 50)); setupPage->Add(new cMenuEditIntItem(tr("Buffer monitoring duration [s]"), &m_modeParams[ modeLiveTV ].m_monitoringDuration, 0, 300)); setupPage->Add(new cMenuEditBoolItem(tr("Buffer monitoring mode"), &alias_cast(m_modeParams[ modeLiveTV ].m_monitoringMode), tr("Once"), tr("Continuous"))); setupPage->Add(new cMenuEditStraItem(tr("OSD display mode"), &alias_cast(m_osdMode), sizeof (osdModes) / sizeof (*osdModes), osdModes)); setupPage->Add(new cMenuEditIntItem(tr("OSD gamma correction [ 123 => 1.23 ]"), &alias_cast(m_crtGamma), monitorGammaMin, monitorGammaMax)); #if APIVERSNUM >= 10707 setupPage->Add(new cMenuEditIntItem(tr("OSD extent X"), &alias_cast(m_osdExtentParams.m_osdExtentWidth), osdExtentWidthMin, osdExtentWidthMax)); setupPage->Add(new cMenuEditIntItem(tr("OSD extent Y"), &alias_cast(m_osdExtentParams.m_osdExtentHeight), osdExtentHeightMin, osdExtentHeightMax)); #endif setupPage->Add(new cMenuEditIntItem(tr("4:3 image zoom X [%]"), &alias_cast(m_zoomParams[ image4_3 ].m_zoomX), imageZoomMin, imageZoomMax)); setupPage->Add(new cMenuEditIntItem(tr("4:3 image zoom Y [%]"), &alias_cast(m_zoomParams[ image4_3 ].m_zoomY), imageZoomMin, imageZoomMax)); setupPage->Add(new cMenuEditIntItem(tr("16:9 image zoom X [%]"), &alias_cast(m_zoomParams[ image16_9 ].m_zoomX), imageZoomMin, imageZoomMax)); setupPage->Add(new cMenuEditIntItem(tr("16:9 image zoom Y [%]"), &alias_cast(m_zoomParams[ image16_9 ].m_zoomY), imageZoomMin, imageZoomMax)); #if APIVERSNUM < 10318 setupPage->Add(new cMenuEditBoolItem(tr("Audio mode"), &alias_cast(m_audioMode), tr("Dolby off"), tr("Dolby on"))); #endif setupPage->Add(new cMenuEditStraItem(tr("Control xine's volume"), &alias_cast(m_volumeMode), sizeof (volumeModes) / sizeof (*volumeModes), volumeModes)); setupPage->Add(new cMenuEditStraItem(tr("Muting"), &alias_cast(m_muteMode), sizeof (muteModes) / sizeof (*muteModes), muteModes)); setupPage->Add(new cMenuEditBoolItem(tr("Get primary device when xine connects"), &alias_cast(m_autoPrimaryDeviceMode), tr("No"), tr("Yes"))); setupPage->Add(new cMenuEditBoolItem(tr("Support semi transparent colors"), &alias_cast(m_transparencyMode), tr("No"), tr("Yes"))); setupPage->Add(new cMenuEditBoolItem(tr("Connection interacts with EIT scanner"), &alias_cast(m_interactWithEitScannerMode), tr("No"), tr("Yes"))); } void cXineSettings::Store(cXineSetupPage *const setupPage) { setupPage->SetupStore("modeLiveTV.prebufferFramesVideoSD", m_modeParams[ modeLiveTV ].m_prebufferFramesVideoSD); setupPage->SetupStore("modeLiveTV.prebufferFramesVideoHD", m_modeParams[ modeLiveTV ].m_prebufferFramesVideoHD); setupPage->SetupStore("modeLiveTV.prebufferFramesAudio", m_modeParams[ modeLiveTV ].m_prebufferFramesAudio); // setupPage->SetupStore("modeReplay.prebufferFrames", m_modeParams[ modeReplay ].m_prebufferFrames); // setupPage->SetupStore("usageModeDefault", (m_usageModeDefault ? "modeReplay" : "modeLiveTV")); setupPage->SetupStore("modeLiveTV.prebufferHysteresis", m_modeParams[ modeLiveTV ].m_prebufferHysteresis); setupPage->SetupStore("modeLiveTV.monitoringDuration", m_modeParams[ modeLiveTV ].m_monitoringDuration); setupPage->SetupStore("modeLiveTV.monitoringMode", (m_modeParams[ modeLiveTV ].m_monitoringMode ? "monitoringContinuous" : "monitoringOnce")); setupPage->SetupStore("image4:3.zoomX", m_zoomParams[ image4_3 ].m_zoomX); setupPage->SetupStore("image4:3.zoomY", m_zoomParams[ image4_3 ].m_zoomY); setupPage->SetupStore("image16:9.zoomX", m_zoomParams[ image16_9 ].m_zoomX); setupPage->SetupStore("image16:9.zoomY", m_zoomParams[ image16_9 ].m_zoomY); { const char *mode = 0; switch (m_osdMode) { case osdOverlay: mode = "osdOverlay"; break; case osdBlendClipped: mode = "osdBlendClipped"; break; case osdBlendScaledLQ: mode = "osdBlendScaledLQ"; break; case osdBlendScaledHQ: mode = "osdBlendScaledHQ"; break; case osdBlendScaledSHQ: mode = "osdBlendScaledSHQ"; break; case osdBlendScaledAuto: mode = "osdBlendScaledAuto"; break; default: assert(false); } setupPage->SetupStore("osdMode", mode); } setupPage->SetupStore("osdGammaCorrection", (int)m_crtGamma); #if APIVERSNUM >= 10707 setupPage->SetupStore("osdExtent.X", m_osdExtentParams.m_osdExtentWidth); setupPage->SetupStore("osdExtent.Y", m_osdExtentParams.m_osdExtentHeight); #endif #if APIVERSNUM < 10318 setupPage->SetupStore("audioMode", (m_audioMode ? "audioDolbyOn" : "audioDolbyOff")); #endif { const char *mode = 0; switch (m_volumeMode) { case volumeIgnore: mode = "volumeIgnore"; break; case volumeChangeHW: mode = "volumeChangeHW"; break; case volumeChangeSW: mode = "volumeChangeSW"; break; } setupPage->SetupStore("volumeMode", mode); } { const char *mode = 0; switch (m_muteMode) { case muteIgnore: mode = "muteIgnore"; break; case muteExecute: mode = "muteExecute"; break; case muteSimulate: mode = "muteSimulate"; break; default: assert(false); } setupPage->SetupStore("muteMode", mode); } setupPage->SetupStore("autoPrimaryDeviceMode", (m_autoPrimaryDeviceMode ? "autoPrimaryDeviceOn" : "autoPrimaryDeviceOff")); setupPage->SetupStore("transparencyMode", (m_transparencyMode ? "transparencyOn" : "transparencyOff")); setupPage->SetupStore("interactWithEitScannerMode", (m_interactWithEitScannerMode ? "interactWithEitScannerOn" : "interactWithEitScannerOff")); } void cXineSettings::SelectReplayPrebufferMode(const bool select /* = true */) { m_usageMode = (!select ? modeLiveTV : modeReplay); /* if (Interface) Interface->Info(m_usageMode ? tr("Settings are now optimized for 'Replay'") : tr("Settings are now optimized for 'Live-TV'")); */ // cXineDevice::GetDevice()->AdjustPrebufferMode(); } void cXineSettings::TogglePrebufferMode() { m_usageMode = (m_usageMode ? modeLiveTV : modeReplay); /* if (Interface) Interface->Info(m_usageMode ? tr("Settings are now optimized for 'Replay'") : tr("Settings are now optimized for 'Live-TV'")); */ // cXineDevice::GetDevice()->AdjustPrebufferMode(); } const char *cXineSettings::GetMainMenuEntry() { return 0; // return (m_usageMode ? tr("xine - Choose optimized settings for 'Live-TV'") : tr("xine - Choose optimized settings for 'Replay'")); } bool cXineSettings::LiveTV() const { return (m_usageMode == modeLiveTV); } bool cXineSettings::AutoPrimaryDevice() const { return (m_autoPrimaryDeviceMode == autoPrimaryDeviceOn); } bool cXineSettings::InteractWithEitScanner() const { return (m_interactWithEitScannerMode == interactWithEitScannerOn); } bool cXineSettings::SupportTransparency() const { return (m_transparencyMode == transparencyOn); } void cXineSettings::SetSwitchSkin(const bool switchSkin) { m_switchSkin = switchSkin; } bool cXineSettings::ShallSwitchSkin() const { return m_switchSkin; } bool beQuiet = false; void cXineSettings::SetBeQuiet(const bool beQuiet) { m_beQuiet = beQuiet; PluginXine::beQuiet = m_beQuiet; } bool cXineSettings::ShallBeQuiet() const { return m_beQuiet; } void cXineSettings::SetDefaultGrabSizeX(const int defaultGrabSizeX) { m_defaultGrabSizeX = defaultGrabSizeX; } int cXineSettings::DefaultGrabSizeX() const { return m_defaultGrabSizeX; } void cXineSettings::SetDefaultGrabSizeY(const int defaultGrabSizeY) { m_defaultGrabSizeY = defaultGrabSizeY; } int cXineSettings::DefaultGrabSizeY() const { return m_defaultGrabSizeY; } }; xine-0.9.4/xineRemote.c0000644000000000000000000001573511537163774013501 0ustar rootroot #include "xineCommon.h" #include "xineLib.h" namespace PluginXine { static eKeys KeyMap[] = { kNone, kUp, kDown, kMenu, kOk, kBack, kLeft, kRight, kRed, kGreen, kYellow, kBlue, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, kPlay, kPause, kStop, kRecord, kFastFwd, kFastRew, kPower, kChanUp, kChanDn, kVolUp, kVolDn, kMute, kSchedule, kChannels, kTimers, kRecordings, kSetup, kCommands, kUser1, kUser2, kUser3, kUser4, kUser5, kUser6, kUser7, kUser8, kUser9, #if APIVERSNUM >= 10318 kAudio, #else kNone, #endif #if APIVERSNUM >= 10338 kInfo, #else kNone, #endif #if APIVERSNUM >= 10347 kChanPrev, kNext, kPrev, #else kNone, kNone, kNone, #endif #if APIVERSNUM >= 10510 kSubtitles, #else kNone, #endif #if APIVERSNUM >= 10715 kUser0, #else kNone, #endif }; cXineRemote::cXineRemote(const bool remoteOn) : cRemote("XineRemote") , cThread() , m_remoteOn(remoteOn) , m_xineLib(0) { #if APIVERSNUM >= 10300 SetDescription("XineRemote control"); #endif // ::fprintf( stderr, "XineRemote constructor\n"); m_active = false; Start (); // calls Action } cXineRemote::~cXineRemote() { { cMutexLock activeMutexLock(&m_activeMutex); m_active = false; m_activeCondVar.Broadcast(); } // Close(); Cancel(3); } void cXineRemote::setXineLib(cXineLib *const xineLib) { m_xineLib = xineLib; } bool cXineRemote::isConnected() { if (!m_xineLib) return false; return m_xineLib->isConnected(); } void cXineRemote::Action (void) { dsyslog ("Entering cXineRemote thread\n"); bool externalStreamFinished = false; bool osdReshowRequired = false; bool discontinuityDetected = false; int frameLeft = 0; int frameTop = 0; int frameWidth = 0; int frameHeight = 0; int frameZoomX = 0; int frameZoomY = 0; m_active = true; while (m_active) { if (isConnected()) { if (osdReshowRequired) { osdReshowRequired = false; if (m_xineLib) m_xineLib->ReshowCurrentOSD(frameLeft, frameTop, frameWidth, frameHeight, frameZoomX, frameZoomY); } if (externalStreamFinished) { externalStreamFinished = false; if (m_xineLib) m_xineLib->ExternalStreamFinished(); } if (discontinuityDetected) { discontinuityDetected = false; if (m_xineLib) m_xineLib->DiscontinuityDetected(); } cPoller Poller(m_xineLib->getRemoteFD()); errno = 0; if (Poller.Poll(100) && 0 == errno) { event_union_t event_union; //printf( "fd_fifo read: %d\n", fd_fifo0); if (isConnected()) { int n = m_xineLib->xread(m_xineLib->getRemoteFD(), (char *)&event_union.header, sizeof (event_union.header)); if (n == sizeof(event_union.header)) { // could check that we have header we expected //::fprintf(stderr, "got Key: %d\n", userInput.key); if (func_key == event_union.header.func) { event_key_t *event = &event_union.key; int n = m_xineLib->xread(m_xineLib->getRemoteFD(), (char *)event + sizeof (event->header), sizeof (*event) - sizeof (event->header)); if (n == sizeof (*event) - sizeof (event->header)) { if (m_remoteOn) { if (event->key < (sizeof (KeyMap) / sizeof (*KeyMap))) cRemote::Put( KeyMap[ event->key ]); // ::fprintf(stderr, "Putting Key: 0x%08x\n", KeyMap[ event->key ]); } } else { m_xineLib->disconnect(); } } else if (func_frame_size == event_union.header.func) { event_frame_size_t *event = &event_union.frame_size; int n = m_xineLib->xread(m_xineLib->getRemoteFD(), (char *)event + sizeof (event->header), sizeof (*event) - sizeof (event->header)); if (n == sizeof (*event) - sizeof (event->header)) { osdReshowRequired = true; frameLeft = event->left; frameTop = event->top; frameWidth = event->width; frameHeight = event->height; frameZoomX = event->zoom_x; frameZoomY = event->zoom_y; if (frameLeft < 0) frameLeft = 0; if (frameTop < 0) frameTop = 0; if (frameZoomX < 0) frameZoomX = 100; if (frameZoomY < 0) frameZoomY = 100; xfprintf(stderr, "frame: (%d, %d)-(%d, %d), zoom: (%.2lf, %.2lf)\n", frameLeft, frameTop, frameWidth, frameHeight, frameZoomX / 100.0, frameZoomY / 100.0); } else { m_xineLib->disconnect(); } } else if (func_discontinuity == event_union.header.func) { event_discontinuity_t *event = &event_union.discontinuity; int n = m_xineLib->xread(m_xineLib->getRemoteFD(), (char *)event + sizeof (event->header), sizeof (*event) - sizeof (event->header)); if (n == sizeof (*event) - sizeof (event->header)) { discontinuityDetected = true; } else { m_xineLib->disconnect(); } } else if (func_play_external == event_union.header.func) { event_play_external_t *event = &event_union.play_external; int n = m_xineLib->xread(m_xineLib->getRemoteFD(), (char *)event + sizeof (event->header), sizeof (*event) - sizeof (event->header)); if (n == sizeof (*event) - sizeof (event->header)) { externalStreamFinished = true; } else { m_xineLib->disconnect(); } } else { ::fprintf(stderr, "vdr-xine: unknown event %d!\n", event_union.header.func); m_xineLib->disconnect(); } } else { m_xineLib->disconnect(); } } } } else { if (m_active) { cMutexLock activeMutexLock(&m_activeMutex); if (m_active) m_activeCondVar.TimedWait(m_activeMutex, 100); } } } dsyslog ("Leaving cXineRemote thread\n"); } }; // namepsace PluginXine xine-0.9.4/xineDevice.h0000644000000000000000000001367711532457652013451 0ustar rootroot #ifndef __XINEDEVICE_H #define __XINEDEVICE_H #include "xineCommon.h" #include #include #include "xineLib.h" class cPlugin; namespace PluginXine { class cXineSettings; class cXineOsd; class cXineDevice; class cXineSpuDecoder : public cDvbSpuDecoder { cXineDevice *const m_xineDevice; void ptsAdjust(uint32_t &pts); public: cXineSpuDecoder(cXineDevice *const xineDevice) : cDvbSpuDecoder() , m_xineDevice(xineDevice) { } virtual int setTime(uint32_t pts); }; class cXineDevice : public cDevice, public cXineLibEvents { cXineSettings &m_settings; bool m_osdUpdateLocked; cXineOsd *m_currentOsd; cXineSpuDecoder *m_spuDecoder; virtual bool HasDecoder(void) const; virtual cSpuDecoder *GetSpuDecoder(void); virtual bool CanReplay(void) const; virtual bool SetPlayMode(ePlayMode PlayMode); virtual bool HasIBPTrickSpeed(void); virtual void TrickSpeed(int Speed, bool IBP); virtual void TrickSpeed(int Speed); virtual void Clear(void); virtual void Play(void); virtual void Freeze(void); virtual void Mute(void); virtual void StillPicture(const uchar *Data, int Length); virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); virtual bool Flush(int TimeoutMs = 0); virtual int PlayTs(const uchar *Data, int Length, bool VideoOnly = false); virtual int PlayTsImpl(const uchar *Data, int Length, bool VideoOnly = false); virtual int PlaySingleTs(const uchar *Data, int Length, bool VideoOnly = false); virtual void PlayTsTrampoline(const uchar *Data, int Length, bool VideoOnly = false); virtual int PlayTsVideo(const uchar *Data, int Length); virtual int PlayTsAudio(const uchar *Data, int Length); virtual int PlayVideo(const uchar *Data, int Length); int PlayVideo1(const uchar *Data, int Length, const bool stillImageData); int PlayVideo2(const uchar *Data, int Length, const bool stillImageData); int PlayVideo3(const uchar *Data, int Length, const bool stillImageData); int PlayAudio2(const uchar *Data, int Length); int PlayAudio3(const uchar *Data, int Length); int PlayCommon(const uchar *Data, int Length, const bool stillImageData); int PlayCommon1(const uchar *Data, int Length, int64_t ptsForce); int PlayCommon2(const uchar *Data, int Length, int64_t ptsForce); int PlayCommon3(const uchar *Data, int Length, int64_t ptsForce); #if APIVERSNUM >= 10338 virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); #else virtual bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); #endif bool m_is16_9; virtual void SetVideoFormat(bool VideoFormat16_9); virtual void SetVolumeDevice(int Volume); #if APIVERSNUM >= 10707 #if APIVERSNUM >= 10708 virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect); virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); #else virtual void GetVideoSize(int &Width, int &Height, eVideoAspect &Aspect); #endif #endif #if APIVERSNUM >= 10307 virtual void MakePrimaryDevice(bool On); #endif int m_audioChannel; #if APIVERSNUM < 10318 virtual void PlayAudio(const uchar *Data, int Length); #else virtual int GetAudioChannelDevice(void); virtual void SetAudioChannelDevice(int AudioChannel); virtual void SetDigitalAudioDevice(bool On); #if APIVERSNUM < 10342 virtual int PlayAudio(const uchar *Data, int Length); #else virtual int PlayAudio(const uchar *Data, int Length, uchar Id); #endif #endif int PlayAudioCommon(const uchar *Data, int Length); bool open(); void close(); int PushOut(); void initStream(); void reshowCurrentOsd(const bool dontOptimize = true, const int frameLeft = -1, const int frameTop = -1, const int frameWidth = -1, const int frameHeight = -1, const int frameZoomX = -1, const int frameZoomY = -1, const bool unlockOsdUpdate = false); virtual void OnClientConnect(); virtual void OnClientDisconnect(); virtual void ReshowCurrentOSD(const int frameLeft, const int frameTop, const int frameWidth, const int frameHeight, const int frameZoomX, const int frameZoomY, const bool unlockOsdUpdate); virtual void LockOsdUpdate(); virtual bool OsdUpdateLocked(); virtual void DiscontinuityDetected(); virtual bool DeviceReplayingOrTransferring(); cPlugin *const m_plugin; int m_switchPrimaryDeviceDeviceNo; cMutex m_switchPrimaryDeviceMutex; cCondVar m_switchPrimaryDeviceCond; void switchPrimaryDevice(const int deviceNo, const bool waitForExecution); void mainMenuTrampoline(); cMutex m_playTsMutex; public: const cXineSettings &Settings() const { return m_settings; } virtual int64_t GetSTC(void); #if APIVERSNUM < 10307 virtual cOsdBase *NewOsd(int w, int h, int x, int y); #elif APIVERSNUM < 10509 virtual cOsd *NewOsd(int w, int h, int x, int y); #else virtual cOsd *NewOsd(int w, int h, int x, int y, uint Level, bool TrueColor = false); #endif bool ChangeCurrentOsd(cXineOsd *osd, bool on); cXineDevice(cPlugin *const plugin, cXineSettings &settings, cXineRemote *remote); virtual ~cXineDevice(); #if APIVERSNUM < 10307 void OnFreeOsd(cOsdBase *const osd); #else void OnFreeOsd(cOsd *const osd); #endif cXineLib m_xineLib; cMutex m_osdMutex; cMutex m_pmMutex; cCondVar m_pmCondVar; static bool Create(cPlugin *const plugin, cXineSettings &settings, cXineRemote *remote); static bool Open(); static void Stop(); static cXineDevice *GetDevice(); static void MainMenuTrampoline() { GetDevice()->mainMenuTrampoline(); } bool hasNoSignalStream() const { return m_xineLib.hasNoSignalStream(); } bool SupportsTrueColorOSD() const { return m_xineLib.SupportsTrueColorOSD(); } }; }; #endif //__XINEDEVICE_H xine-0.9.4/vdr172h264parser.c0000644000000000000000000003670611170353014014206 0ustar rootroot #include #ifndef APIVERSNUM #define APIVERSNUM VDRVERSNUM #endif #if APIVERSNUM >= 10703 /* * h264parser.c: a minimalistic H.264 video stream parser * * See the main source file 'vdr.c' for copyright information and * how to reach the author. * * The code was originally written by Reinhard Nissl , * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de. */ #include #include "vdr172h264parser.h" namespace vdr172 { namespace H264 { // --- cContext ------------------------------------------------------------ int cContext::GetFramesPerSec(void) const { const cSequenceParameterSet *SPS = ActiveSPS(); const cSliceHeader *SH = CurrentSlice(); if (!SH || !SPS->timing_info_present_flag || !SPS->time_scale || !SPS->num_units_in_tick) return -1; uint32_t DeltaTfiDivisor; if (SPS->pic_struct_present_flag) { if (!SPS->pic_timing_sei.Defined()) return -1; switch (SPS->pic_timing_sei.pic_struct) { case 1: case 2: DeltaTfiDivisor = 1; break; case 0: case 3: case 4: DeltaTfiDivisor = 2; break; case 5: case 6: DeltaTfiDivisor = 3; break; case 7: DeltaTfiDivisor = 4; break; case 8: DeltaTfiDivisor = 6; break; default: return -1; } } else if (!SH->field_pic_flag) DeltaTfiDivisor = 2; else DeltaTfiDivisor = 1; double FPS = (double)SPS->time_scale / SPS->num_units_in_tick / DeltaTfiDivisor / (SH->field_pic_flag ? 2 : 1); int FramesPerSec = (int)FPS; if ((FPS - FramesPerSec) >= 0.5) FramesPerSec++; return FramesPerSec; } // --- cSimpleBuffer ------------------------------------------------------- cSimpleBuffer::cSimpleBuffer(int Size) { size = Size; data = new uchar[size]; avail = 0; gotten = 0; } cSimpleBuffer::~cSimpleBuffer() { delete [] data; } int cSimpleBuffer::Put(const uchar *Data, int Count) { if (Count < 0) { if (avail + Count < 0) Count = 0 - avail; if (avail + Count < gotten) Count = gotten - avail; avail += Count; return Count; } if (avail + Count > size) Count = size - avail; memcpy(data + avail, Data, Count); avail += Count; return Count; } uchar *cSimpleBuffer::Get(int &Count) { Count = gotten = avail; return data; } void cSimpleBuffer::Del(int Count) { if (Count < 0) return; if (Count > gotten) { esyslog("ERROR: invalid Count in H264::cSimpleBuffer::Del: %d (limited to %d)", Count, gotten); Count = gotten; } if (Count < avail) memmove(data, data + Count, avail - Count); avail -= Count; gotten = 0; } void cSimpleBuffer::Clear(void) { avail = gotten = 0; } // --- cParser ------------------------------------------------------------- cParser::cParser(bool OmitPicTiming) : nalUnitDataBuffer(1000) { // the above buffer size of 1000 bytes wont hold a complete NAL unit but // should be sufficient for the relevant part used for parsing. omitPicTiming = OmitPicTiming; // only necessary to determine frames per second Reset(); } void cParser::Reset(void) { context = cContext(); nalUnitDataBuffer.Clear(); syncing = true; } void cParser::ParseSequenceParameterSet(uint8_t *Data, int Count) { cSequenceParameterSet SPS; cBitReader br(Data + 1, Count - 1); uint32_t profile_idc = br.u(8); /* uint32_t constraint_set0_flag = */ br.u(1); /* uint32_t constraint_set1_flag = */ br.u(1); /* uint32_t constraint_set2_flag = */ br.u(1); /* uint32_t constraint_set3_flag = */ br.u(1); /* uint32_t reserved_zero_4bits = */ br.u(4); /* uint32_t level_idc = */ br.u(8); SPS.seq_parameter_set_id = br.ue(); if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144) { uint32_t chroma_format_idc = br.ue(); if (chroma_format_idc == 3) { /* uint32_t residual_colour_transform_flag = */ br.u(1); } /* uint32_t bit_depth_luma_minus8 = */ br.ue(); /* uint32_t bit_depth_chroma_minus8 = */ br.ue(); /* uint32_t qpprime_y_zero_transform_bypass_flag = */ br.u(1); uint32_t seq_scaling_matrix_present_flag = br.u(1); if (seq_scaling_matrix_present_flag) { for (int i = 0; i < 8; i++) { uint32_t seq_scaling_list_present_flag = br.u(1); if (seq_scaling_list_present_flag) { int sizeOfScalingList = (i < 6) ? 16 : 64; int lastScale = 8; int nextScale = 8; for (int j = 0; j < sizeOfScalingList; j++) { if (nextScale != 0) { int32_t delta_scale = br.se(); nextScale = (lastScale + delta_scale + 256) % 256; } lastScale = (nextScale == 0) ? lastScale : nextScale; } } } } } SPS.log2_max_frame_num_minus4(br.ue()); SPS.pic_order_cnt_type = br.ue(); if (SPS.pic_order_cnt_type == 0) SPS.log2_max_pic_order_cnt_lsb_minus4(br.ue()); else if (SPS.pic_order_cnt_type == 1) { SPS.delta_pic_order_always_zero_flag = br.u(1); /* int32_t offset_for_non_ref_pic = */ br.se(); /* int32_t offset_for_top_to_bottom_field = */ br.se(); uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ue(); for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { /* int32_t offset_for_ref_frame = */ br.se(); } } /* uint32_t num_ref_frames = */ br.ue(); /* uint32_t gaps_in_frame_num_value_allowed_flag = */ br.u(1); /* uint32_t pic_width_in_mbs_minus1 = */ br.ue(); /* uint32_t pic_height_in_map_units_minus1 = */ br.ue(); SPS.frame_mbs_only_flag = br.u(1); if (!omitPicTiming) { if (!SPS.frame_mbs_only_flag) { /* uint32_t mb_adaptive_frame_field_flag = */ br.u(1); } /* uint32_t direct_8x8_inference_flag = */ br.u(1); uint32_t frame_cropping_flag = br.u(1); if (frame_cropping_flag) { /* uint32_t frame_crop_left_offset = */ br.ue(); /* uint32_t frame_crop_right_offset = */ br.ue(); /* uint32_t frame_crop_top_offset = */ br.ue(); /* uint32_t frame_crop_bottom_offset = */ br.ue(); } uint32_t vui_parameters_present_flag = br.u(1); if (vui_parameters_present_flag) { uint32_t aspect_ratio_info_present_flag = br.u(1); if (aspect_ratio_info_present_flag) { uint32_t aspect_ratio_idc = br.u(8); const uint32_t Extended_SAR = 255; if (aspect_ratio_idc == Extended_SAR) { /* uint32_t sar_width = */ br.u(16); /* uint32_t sar_height = */ br.u(16); } } uint32_t overscan_info_present_flag = br.u(1); if (overscan_info_present_flag) { /* uint32_t overscan_appropriate_flag = */ br.u(1); } uint32_t video_signal_type_present_flag = br.u(1); if (video_signal_type_present_flag) { /* uint32_t video_format = */ br.u(3); /* uint32_t video_full_range_flag = */ br.u(1); uint32_t colour_description_present_flag = br.u(1); if (colour_description_present_flag) { /* uint32_t colour_primaries = */ br.u(8); /* uint32_t transfer_characteristics = */ br.u(8); /* uint32_t matrix_coefficients = */ br.u(8); } } uint32_t chroma_loc_info_present_flag = br.u(1); if (chroma_loc_info_present_flag) { /* uint32_t chroma_sample_loc_type_top_field = */ br.ue(); /* uint32_t chroma_sample_loc_type_bottom_field = */ br.ue(); } SPS.timing_info_present_flag = br.u(1); if (SPS.timing_info_present_flag) { SPS.num_units_in_tick = br.u(32); SPS.time_scale = br.u(32); SPS.fixed_frame_rate_flag = br.u(1); } SPS.nal_hrd_parameters_present_flag = br.u(1); if (SPS.nal_hrd_parameters_present_flag) hrd_parameters(SPS, br); SPS.vcl_hrd_parameters_present_flag = br.u(1); if (SPS.vcl_hrd_parameters_present_flag) hrd_parameters(SPS, br); if (SPS.nal_hrd_parameters_present_flag || SPS.vcl_hrd_parameters_present_flag) { /* uint32_t low_delay_hrd_flag = */ br.u(1); } SPS.pic_struct_present_flag = br.u(1); } } context.Define(SPS); } void cParser::hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br) { uint32_t cpb_cnt_minus1 = br.ue(); /* uint32_t bit_rate_scale = */ br.u(4); /* uint32_t cpb_size_scale = */ br.u(4); for (uint32_t i = 0; i <= cpb_cnt_minus1; i++) { /* uint32_t bit_rate_value_minus1 = */ br.ue(); /* uint32_t cpb_size_value_minus1 = */ br.ue(); /* uint32_t cbr_flag = */ br.u(1); } /* uint32_t initial_cpb_removal_delay_length_minus1 = */ br.u(5); SPS.cpb_removal_delay_length_minus1(br.u(5)); SPS.dpb_output_delay_length_minus1(br.u(5)); /* uint32_t time_offset_length = */ br.u(5); } void cParser::ParsePictureParameterSet(uint8_t *Data, int Count) { cPictureParameterSet PPS; cBitReader br(Data + 1, Count - 1); PPS.pic_parameter_set_id = br.ue(); PPS.seq_parameter_set_id = br.ue(); /* uint32_t entropy_coding_mode_flag = */ br.u(1); PPS.pic_order_present_flag = br.u(1); context.Define(PPS); } void cParser::ParseSlice(uint8_t *Data, int Count) { cSliceHeader SH; cBitReader br(Data + 1, Count - 1); SH.nal_ref_idc(Data[0] >> 5); SH.nal_unit_type(Data[0] & 0x1F); /* uint32_t first_mb_in_slice = */ br.ue(); SH.slice_type = br.ue(); SH.pic_parameter_set_id = br.ue(); context.ActivatePPS(SH.pic_parameter_set_id); const cSequenceParameterSet *SPS = context.ActiveSPS(); SH.frame_num = br.u(SPS->log2_max_frame_num()); if (!SPS->frame_mbs_only_flag) { SH.field_pic_flag = br.u(1); if (SH.field_pic_flag) SH.bottom_field_flag = br.u(1); } if (SH.nal_unit_type() == 5) SH.idr_pic_id = br.ue(); if (SPS->pic_order_cnt_type == 0) { SH.pic_order_cnt_lsb = br.u(SPS->log2_max_pic_order_cnt_lsb()); const cPictureParameterSet *PPS = context.ActivePPS(); if (PPS->pic_order_present_flag && !SH.field_pic_flag) SH.delta_pic_order_cnt_bottom = br.se(); } if (SPS->pic_order_cnt_type == 1 && !SPS->delta_pic_order_always_zero_flag) { SH.delta_pic_order_cnt[0] = br.se(); const cPictureParameterSet *PPS = context.ActivePPS(); if (PPS->pic_order_present_flag && !SH.field_pic_flag) SH.delta_pic_order_cnt[1] = br.se(); } context.Define(SH); } void cParser::ParseSEI(uint8_t *Data, int Count) { // currently only used to determine frames per second if (omitPicTiming) return; cBitReader br(Data + 1, Count - 1); do sei_message(br); while (br.GetBytesAvail()); } void cParser::sei_message(cBitReader &br) { uint32_t payloadType = 0; while (1) { uint32_t last_payload_type_byte = br.u(8); payloadType += last_payload_type_byte; if (last_payload_type_byte != 0xFF) break; } uint32_t payloadSize = 0; while (1) { uint32_t last_payload_size_byte = br.u(8); payloadSize += last_payload_size_byte; if (last_payload_size_byte != 0xFF) break; } sei_payload(payloadType, payloadSize, br); } void cParser::sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br) { const cBitReader::cBookMark BookMark = br.BookMark(); switch (payloadType) { case 0: buffering_period(payloadSize, br); break; case 1: pic_timing(payloadSize, br); break; } // instead of dealing with trailing bits in each message // go back to start of message and skip it completely br.BookMark(BookMark); reserved_sei_message(payloadSize, br); } void cParser::buffering_period(uint32_t payloadSize, cBitReader &br) { uint32_t seq_parameter_set_id = br.ue(); context.ActivateSPS(seq_parameter_set_id); } void cParser::pic_timing(uint32_t payloadSize, cBitReader &br) { cPictureTiming PT; const cSequenceParameterSet *SPS = context.ActiveSPS(); if (!SPS) return; uint32_t CpbDpbDelaysPresentFlag = SPS->nal_hrd_parameters_present_flag || SPS->vcl_hrd_parameters_present_flag; if (CpbDpbDelaysPresentFlag) { /* uint32_t cpb_removal_delay = */ br.u(SPS->cpb_removal_delay_length()); /* uint32_t dpb_output_delay = */ br.u(SPS->dpb_output_delay_length()); } if (SPS->pic_struct_present_flag) { PT.pic_struct = br.u(4); } context.Define(PT); } void cParser::reserved_sei_message(uint32_t payloadSize, cBitReader &br) { for (uint32_t i = 0; i < payloadSize; i++) { /* uint32_t reserved_sei_message_payload_byte = */ br.u(8); } } void cParser::PutNalUnitData(const uchar *Data, int Count) { int n = nalUnitDataBuffer.Put(Data, Count); // typically less than a complete NAL unit are needed for parsing the // relevant data, so simply ignore the overflow condition. if (false && n != Count) esyslog("ERROR: H264::cParser::PutNalUnitData(): NAL unit data buffer overflow"); } void cParser::Process() { // nalUnitDataBuffer contains the head of the current NAL unit -- let's parse it int Count = 0; uchar *Data = nalUnitDataBuffer.Get(Count); if (Data && Count >= 4) { if (Data[0] == 0x00 && Data[1] == 0x00 && Data[2] == 0x01) { int nal_unit_type = Data[3] & 0x1F; try { switch (nal_unit_type) { case 1: // coded slice of a non-IDR picture case 2: // coded slice data partition A case 5: // coded slice of an IDR picture ParseSlice(Data + 3, Count - 3); break; case 6: // supplemental enhancement information (SEI) ParseSEI(Data + 3, Count - 3); break; case 7: // sequence parameter set syncing = false; // from now on, we should get reliable results ParseSequenceParameterSet(Data + 3, Count - 3); break; case 8: // picture parameter set ParsePictureParameterSet(Data + 3, Count - 3); break; } } catch (cException *e) { if (!syncing) // suppress typical error messages while syncing esyslog(e->Message()); delete e; } } else if (!syncing) esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is invalid"); } else if (!syncing) esyslog("ERROR: H264::cParser::Process(): NAL unit data buffer content is too short"); // reset the buffer for the next NAL unit nalUnitDataBuffer.Clear(); } } } #endif xine-0.9.4/xineExternal.h0000644000000000000000000000216511132432564014011 0ustar rootroot #ifndef __XINEEXTERNAL_H #define __XINEEXTERNAL_H #include "xineCommon.h" #include #define FIFO_NAME_EXT_CONTROL "/external.control" #define FIFO_NAME_EXT_RESULT "/external.result" #define EXTERNAL_COMMAND_MAX_LEN (1000) namespace PluginXine { class cXineLib; class cXineExternal : public cThread { int m_fdControl; int m_fdResult; bool m_shutdown; cMutex m_shutdownMutex; cCondVar m_shutdownCondVar; bool m_connected; cXineLib *m_xineLib; cMutex m_enabledMutex; bool m_enabled; char m_command[ EXTERNAL_COMMAND_MAX_LEN ]; virtual void Action(void); bool checkConnect(); bool isConnected(); bool readCommand(); void cmdPlay(const char *const mrl); bool writeResult(const char *result); public: cXineExternal(); virtual ~cXineExternal(); void setXineLib(cXineLib *const xineLib) { m_xineLib = xineLib; } void enable(const bool enable); void externalStreamFinished(); void disconnect(); void StartExternal(); void StopExternal(); }; }; #endif //__XINEEXTERNAL_H xine-0.9.4/README0000644000000000000000000000115210664367414012057 0ustar rootrootThis is a "plugin" for the Video Disk Recorder (VDR). Written by: Reinhard Nißl Project's homepage: http://home.vr-web.de/~rnissl/ Latest version available at: http://home.vr-web.de/~rnissl/ 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. See the file COPYING for more information. Description: Plugin for "software only" playback using xine. For more information see INSTALL. xine-0.9.4/data/0000755000000000000000000000000011232610463012074 5ustar rootrootxine-0.9.4/data/noSignal4x3-old.mpg0000644000000000000000000006116711006330326015475 0ustar rootroot³-@#O£€µ‚µ# B¸@ÿøµÿ÷Ý€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Foê ê õ A»Á" €€?ÏðÏŒ¢~ä|l´ÇÏÝû´ÇB wíóÇBèp›­ô`@ì@ö Œ€9¼è!óè þ€&‚` €Ø&ÿ8¨&€C}†ù˜ v |`°@¿  ~¨°@ýp  €}3:öFD™ÀÁú{ÛïN€`Íç=ó° ­'l»6DoÎ`~Â@)|ïüGãNÞ½þŸOÿÁˆ'­€#±^üv+—Pê77,¥7í†üä }ÁPîòm¼‚ô‰<«ãß’eÀAÝó ßb¦ºù‘¹sç€c 7óí‚å+ì=À Œ?TŽºwäO}€5ÄP“ûüw;Íú^‘z†&@ŠÜFôÀ‡æ€‚ ›õ`‚›þ(0a†þê’Ò„€pç4ÉiBÀ8€ó›è-a¿ÇÀù@ û¸cÕF'®ø×` A€$þÿ_ÇNòvW¤Ná©&7½0!†` „h&‚¨ @&€z [̰Ãq½ y@ ;ÁØÁ%…>ú€ï|y‚ëÞÅwñ×~îpŸæ¿;üh¨'¬{ªÙRæÆ°=\F‚‚K‚ …½ÿú8Ztˆ‰œ`þÓ‹s€+# á8ì¼5Ï™åõp¿Ðà @€/rGí÷^'ë÷õÿ‹÷"ûû÷·½ òþmÛ£ Da†ü v¸ {è@û0@ü/xÈÛàŒ)Â~ ‚ß@Âß¾X$ßÝq›C±ü̵ û%HÞ˜=Ì>`>ßîôX¿žÁò€€;÷™.ót³¼o©mÚhà ù`@ò°@÷`@ûP@Ð@A"À -ﮈà@€7öÎCþ8Øì˜6®íI úß6¢°üù` }øC¹×¼@hž;Þ£½€?üŸ¼»lN€U̅ÛîMa¿Å @@€6b°¹_¼{ý>à>Ÿñ^äOŽÝzØg.zåÁ”&Õ7é@@ó¿ÿüìÄ?XÌ?_·ó«»î rFGî*€: ®2Àà—Ç<‰Ö¡¹à쎺ߚì²Ë/¥a†ãFa¸Ñ£Fn5À@@‚€2Z´±·\x¯Ü×ôàŸ¯··9yÀBn_ĺƒ`m•–ûð {W÷êvV¦¼W/Ðìè@b€€?(‚ú?ÒDÎ ëùí’.€Xù½?ånõŒ0ÞP@ꀠޅ€?XÃôøöŸÜˆóê+z9#€>Ÿá^è¾pÀBgó×Þ §TÅo£‚©ÂÚ‚ç‹ϰ@ý }€>ÿ{q^ýÓ{㎕vJ’A‹kzŒ0ß,>T>´?ãéÏb¸Ì/fE´=òÞt;ì=à?H,æè ~¸¾å À @:ùóÁÿÎuÄvfmR± ÂÇHÞÙ£ 7ùd<ˆÜP p {/@  hœœ^ýÀéé@Ÿr?7€ß;{×#ÝÇBsž®¬²Ëšâ[ö  ~¨Ÿè ` À0ð@¾”áPà€È¿N¹@-½u¶\vG¿CÌ‹[ò0 |ïÙÀ€@ðÀ€ @ù$aNRüˆÝ€;#8ÿÓ·ØóE}qÖ-·n]êþ+ 7Äo¸‚‚É€3-jŽ1,"6aôËÙß À`ˆÝûgbÞnß0A’(O‘td„FïR7Ûôø!ö¨ úø&þx & €ð&€s 7X vH {HFÀœÞp<@>,x   }0 |˜ x/È@ßl|è€ ˆž>¯ì[ÌÈ`ßÁx ?û}݉ìãA ?Ù4€ A7ü7ýXa¿Ê­4a†å–Élž1¾ž¦iôd]û€å| P °ì·ëñ¢ˆà{ó¯þû8_‰¼ ë|欛,­Ó:ú€?Ú3p à ÏÇ=íœpFnן¯¶€€>Þ߇L"4?uèîý¤~!ómëåÍÆûÕ¶Ûmõ¬0ߌoÅÇÇ^‹ëíñЀ >žÜ¾™Çÿß™¼êFKN:nÐ {Ð |à }ÈŸž{íàù@@û {õáíÙhà ùÀ@ôp@÷ð@û€òùÀúù?þÿ}Ýäâ€,)Û»CœB#x€@êp@ûЀ€  Ÿÿ° } ~ŸÓØDëØçøØO‡¿ü+è÷XG>wxU„ Ͳ,Qõ¦Œ0ßã@û @ à€€õ@@úà€€ ÄSÈà €=áâ¼> ÌÑ ùëÜSçn¦áÛ ~žbþÿA?í€7œ¿jï" >Àúd_ðÞÙ“ŽœjÏØ[ú#ut¿Õû^é²ûZ27æ,²Ë<+ 6_ ü |õç8ÙâlÊKoÒ Ú€íÀÀÿ‘…@€8»:{íõÄ´»ø=à[°°Ï9óX!ÿ¨ð&ÿÑôÂK~Ó?Øàßç‚où‚oþ0Ã~;$=¤#àÎo›˜€ýAAÁà@‚à‰þ…+ùì$)€  >gN†‚|:XGøæ½- öï!nC\ߢ›’TÞ‚96‚Üä8ÝÚL‚;$UýäIVÿÙÞ û‰…H@íç»»R0ÃŽZ5àà‚‚ç /`GÀ xÀoøßó¸2€<‰õ,U²¬¾^O‹ ja¿®‚£Ò€9÷„¼Hoöxè”_ÜÇð è ¿}{ôÌ€{Æ€9ö¦dPŸÑ\KKo|’Ò„€pç7 ú(~|@Ð@Ø@˜±¾°Ã ü\<\>8ˆ  |ø |ˆ € ~¹ ¦N¾tÀ¨È!¶ö-æd?ïöt'ˆ6›óÆ$o…·êPC@Aö0MÀOþ€AMÿ6ü´Ã Æ0ÃERÓx wØ  €@Hàðnþ€-1CõìO ©àŸÛ¬½½ðÈ_òNÇR~k}@:Ô=Ç䀴Ùï¯`Ø@ù ï  º}£ñõ0\«#¤_öxîÀ¤zû|ÔÞžEp¤ôVoçÖ î? Œ °Žß,Y!ä =°’ç<ásȰàAþ0@ý@ LÞ…-¨ØûúF«Êm/÷A}“ñôÓ|Ô>øÐ@À@À@øPîø´{½óÀ>šä4ÿÓÿç;ö6Òr¸¾ÞýÜ/Xa¿Í­4a†ã}Ü;à>;Ü=\>dÄà g»€ÈøÐ?$0¿¤÷í9zte#v]èOÔ9¾߬ÆÚA7ëÁùA4ÀÀšL0ß:Ô=Ç䀴ÛÉ‚Œ‚Π׉™1ÔäH%àlhãäÑþÓ ê"»é }XS};zbx>Ò  8 6 ¿À&°Ã™Z4hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0À ü>Ñ£Fn4hц4a†ãFa¸Ñ£Foï ‡Ï ƒú𠀂`›üà š ö6øà!óè þ€&‚` €Ø&ÿ8¨&€C}†æAä»tø:GÏ €ÂýÀ#ÜÀú€ 9Ïûø÷#X¼RÈ®òøUoLцüIî+Ÿ¨qº®ç®„ñßOvÃ,'=Üæ»r•C¬±Ò‚šF‚oÕ‚oø Á»àñwä¨>¨ˆ Ø»´»È ÿ  @½h/ß± „¾îçÌÇTÐ >ÍøR¶7üÐB4~¬@ Å 0ßëf0Ã’A 3#A4A 4ÐbÞk—obÏLO`è@ðÀó`9¯ Œ  € ~€2IÓ§â <MzèŽP þ´ž%òK#zva¹,–Éo…o:f–@šØˆÏ@ýoåßo©Ÿ÷ñØ¡9œç°ž·ŽVY®~\Ø•¾~VÃüt“Ø }0 ~ˆñ]d¯ìþþ@}þÀÂ~8tÍÐà@*›òD“Þ&ð[ôŸÄo‚É$’Iáa¿Ú`€`` õAÀ€2%0gö÷ãŸà_ þx› ý>K.x¼ˆôY¿·è¿ð °è±kçhá Èî|êí<í<æ–ý>ß+Áç°@0@óÀ€œËh"€/°p@ýpu“ ?òx°ùâRí ždTà þwhÑ£ 74hà Ø×Û@€$lÜŠàûÀ>¢p}þ:ï˜ nVýžCÿx ~€ ~P8X·Ûû7î¤H°drÏ/ú7ú‰ù&ÇêFü‚Aä4^{GÒŒq?‚yCáßøoÙ 0Üo~Šî­;¸¶s÷#‰DOà Äb0nU¾ÂȽýwŽˆßr9ßáýïEK¢qýwœrSäo²·ç€@õ@@`€"À Á€$ÿw†vDÿû€€ô,SÈ ïç’(Ÿg öï9¹pGb瘌0Ý €(`€ @@ù`ÍD3ù~ÿÜŽìø!ôP§ÿ¾@p'¿«…`¿mt°^ ® ä}|É-oÐà €ü#$b¨¿Ó™úïàø°Ÿ_÷÷Û™ù€…þ¾H‚Å ãO?ûÍvÊߊ[í €$@øAÖÁéèAó ?À€^ÿ౿þÀ € @óõÜùwÔ5ÀÓ¾¢Í±h}õyjà ý;…Ȱ@ßp€"íªMëï¾ÄPÿ¦ûÿ}xû}Ra°µø#mµ6·¢hBÀsýrÙ¼X Ð }€0@ @ p@û °¿¨'Ï×éì'°:nôn@ñÚŸ÷¾ï#ûõl¨ B­a†þRß§ÁµA×Á7óÀ 4Á4›ó~Ÿ>Õ_ßÏ4Ð#Ða†ýëxÐCàAö`M`PMÿ A@MÿVöMãA ?Ù4€ A7ü7ýXa¿Ê­Óµ|?2ÂŒ”öÍýoõÀ:#¸J‚7û| Ð žô¨€b§=>·1¾´yÏ`Ò` Á =²Ð#VJ·ïõÔüÚhç€.áù±&ú¿ˆihà ýè„ŸÈ ~  ~PxXß«Àz$ØðÿÞèÈ¿$~ûå2Ù»RDm 0ÃyÁõÀ‚Ü%.+FüCÃ…ÿ¾À HßZaÊþ9Ìæâ¿]ñ¼»dßÕàATݯ؊ëëÏÐÐ8B}€À •ÏO¾ÿô÷CÌ£¡Töù0  ¯à ¨ ¸ Ÿ¹ŒÂùú‚é€:Õ#9[ñòý':XÈW»¹#{ r÷¶ñ¹qÿ; ó#xVoö@ 8 ~@ú@à  ò}L׊ç½ú›õ"~9ú|õ¼wbXê›,Rån£~Lvuô=…^Xñ}q°Ð` ˆÀ @ =½ù}âHäP’7OwׇdïiŸ+æÓ27é@ p@ø€@0@à@ûK €ûcÈ€ À~¢8ä ožµsfÂÊX¹ea†ý¶Kdôù,>»±`€€ô €@äqw{@‚qþdäŸ@)?óç@ þ?Ýêy;åýp.ßZm³Ô?Zh ~€ wàÐoÛØWU_? Lœùaë¦" Öøø X €@€@€€€€ €€5‚‚ôH¯ {q7ã­x—v,P§ÑYº(¡Y_SXa¿>ê| ¿ô@ßý0’ß´Áöø7ùÀ ›þ@ ›ÿø°ÿÔøè€ ¿úa%¿i‚ìðoó€A7ü€A7ÿa»Ú4hà þ}Š=WÔ:Nof‚/þ@à A yú€0õ'h!×@|Þü€>kòì„òVý€=÷Ð@ð@p@ú°@üÏkÍ¥„{¾ý5#˜†ÚÈC¾w¯íÔ@B}›®¿ˆßÀý…€2NðÏûç‹ûàOÓíÿø"‘…»€#‘üwÓò”ÍËa¿Ð‡Ñ@ƒóàš€‚ÀšÀƒüÀ›ýõ†ù8!ôP üø&  °&€0 ÿ0&ÿc}a†ñ†"€ƒì`š€Ÿý‚ ›þmùi¿R‚б‚hô ‚où·å¦n3ÿþ;~ÓíýúŠÀ÷ðGúù÷ë  †ÏìS¹ç!à8·,do^{{H‰ûõÕ§I¤P@ü`¿`Cm2ýW1Ç.Ò`!€W3Dº°Ã§Áó~AíîÁòAõñôkà°P Àë´ôÈ¿=ãŠ;˹sFç‚w € ö ÷À€ýq§.wA À @€4ÑV0‰mõ"Š(h!€/}kò0Ã}æ‚ä‰Ð@@@û @ýAY@x € ÿß´À€B¾hžF‚9Þv6°ÞøFH„QÔ^à À=|'#þóïô÷ÌåÕ¯a†ÿ‘DrDÿüý»€<ä@ž…ôü&L÷svÉ–XÊÞ¨>øÐ@ @û`@Pü‹ Ò8 Øo¯š“Ô÷xU×—ÐýÑ\<ÓÛʰÃq¿X´‚oׂò‚h€ 4›ò{~°?i߯åÐh0Ã~±¾zbx>Ò  8 6 ¿À&·Ð›ç †'€ƒí š€‚`›ü`‹ 7ù•£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà  ü>Ñ£Fn4hц4a†ãFa¸Ñ£Foï ‡Ï ƒú𠀂`›üà š ö6øà!óè þ€&‚` €Ø&ÿ8¨&€C}† È<A¶#"YÌ èïøÞO ¿Ð!ÿ×±Ûéó‚ÞžR€P$QºsléîìG×m*{ûð„ý­ùãe5Ð@ë, °>²‚ÙA+üL7ëVø |Tù<—÷øÿ‹ó?yá½0"@ÜX“nÚg÷ê(Uÿ ¦í<ã„.€Rà ýí£FŒ0ßäC ÀAÐMPA€Mô·¬/{Á¦@6€2vP€Aààú„÷™ÄO·ØY@ÿ›õ¼e9¼˜!†` „h&‚¨ @&€z [ßýv=¸˜‰€¨HXQ4:?ÁìFt,â€Ât5†û»FŒ8^»ç„ ~àËla†ÿ#‚qÀ Ø à ø,ê«@Í${ó)4ÿÖ:f(ï¾ià‡ÿY>v7œmDL€€ÿà@Â3¤ÏÖYð¸VÐ8A×ÑÑ!P¢l¬0ÜoÔ€b:Þ0nçÛ©H(ŒÇè40ŸÈ‚/°¯—©js ÂmUVü¾ß¡ÈŽ’è>kæ) _šyà†;0ÿËk 7õVò ; €À€íßÜuR’‚'òŸüðIH?ØD|ú€€ÿšÞ©¸ä}€€¿@@ù‘n°4Ð@È™€˜¯fëày¬0ßã¶0Ãq£FŒ0Û€>öp?ïñH?o¯ßÛàN‚@à±lñl-û >? ¸ p h ½~Ï>@h‰¹ýjëáÀ|§·ÆM»sžõÝq‘Úß–†¨  {¸ ø ~pUòf?€.÷àØO}A{ ¿¶õ”\'É×)9ǹý=Xa¾~Ÿ>Õ_ßÏ4Ð#ÐoÌ ú|ûT}|<Ð@Œ@x@9†÷­ãA ?Ù4€ A7ü7ý[Ù70þfÐF5ßòßõa†ÿ*·è€Ð@é@ òÀ ²€‹ `€€Üù:~íýÒÁÀý|7çvý9÷x öoÔ=§6]PoÀOgÈ` €ô}ÉP·Žuiu†úr 8àÌ?>¬_h4œ(ðh?Ûß`†wx,Ê( ”¶ê·‘û8CÄø {/\¦Bö  {€ûœÐ’0Ow-©'˜ŸiÌ0ßå$’Ié4ovN”¼;Ę”¼Ô7}ë¿~ú#€7ûv­þ~î;ºp/íÏ;.Ò¼èÑ\c 7á7ê @ð@ö`@û°Fÿ/àOã@@ð@ö`@û°Fÿ/àOã 7ùU£~ªf»oòþ4þ7ËÞÁëÀ AKãÀŠÅGî ˆ#¯]çø@ ÏþvíaïÜïCíÙa¿”ú€oý7ÿL$·í0Cý€AþMþp&ÿè&ÿã~,?õ>ßú oþ˜IoÚ`‡ûƒü›üàMÿ ÐMÿÆnö ðÞ„Š<Åó¹]€.þ}@ À ,6`!ƒ€ÿ€À+@ (!=yÿ¨?î0Ã{›ó˜ |0ø }ð h ~f}^$ëȉóÚÅ Ü#ó¿b­ÊA">éPêߣÍÉ ìÐü>|?UîäÐã8lTþÀ#ìÄרàBÿ=ÏqÅ#»8É1†ñ€‡Ñ@ƒóàš€‚ÀšÀƒüÀ›ýõ†ùG¸®A¤ˆ ôÂD ã`søï @‘`€]õË·<¶ŸìðºíË °ÊæË+}]†ñ†"€ƒì`š€Ÿý‚ ›þmùi¿R‚б‚hô ‚où·å¦n7ÚïH Ð:>Ð ‚¿È?ÜRf‘~8ÿ6|ÄýòžñVýHzh wp ~ {ž î‚@‚ê`gÓ $€góÛ~¡ …þ;#ÉJa†ýÃ|øÔœh¨çýåô$@À@À _%oü낾Âê_ôøEÇCÛè-ðð@bp ¾ó…ÐHÿÀAûnûEöƒÊ€ :ϯ÷FBÀùû`Úà ûð@bp xP Ð {qŽýðý@ü>q~ýˆâò?úð)â]Oobß6'Dòôºp à@ ½ÀCÿšEqò/§`¹7Øa¿„ßZõ÷&:”Z„Ûþ—ù¶~ € €õñõêPƒy"{ §+|i¿O‚ßQ`€$€€@î¤Nø” „@à@ýr0€Ä<‚kïÀŸ§™1 Ý=º|õ¿00Ãq¿X´‚oׂò‚h€ 4›ò{~°?i߯åÐh0Ã~±¾zbx>Ò  8 6 ¿À&·Ð›ç †'€ƒí š€‚`›ü`‹ 7ù•£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà  ü>Ñ£Fn4hц4a†ãFa¸Ñ£Foï ‡Ï ƒú𠀂`›üà š ö6øà!óè þ€&‚` €Ø&ÿ8¨&€C}†ò`†Àƒí š ƒü ›ÿ `Mÿfæ:€t‚0‚Ë€5ÿ ¹úÊ“ú?Åþë£<¦‘=ßÝkÇ”‘Ò|‰œÏž¤'7LØ}¤AD”ôL ¿ìßwwÀ xp |p W_Asæ€!ZEÀÏm஑ºÒ?ßÊ´ó‹›‚è!60êPìADøË®ù§˜ Ð ~H ~X€Ðì@ü€?åÝÿm« Ü39,î·^Ye–_ц4a†ÿ$‚fF‚h*‚h Å¾ƒððAç0÷ˆ «Ð¾µ@V?»ßÆâpƒ­ß>0ÌÐU(Ð@‹{p t—ôˆžPÀ@€úœ=À" @bÄa¬S˜a¿ÐÌ*è[ê…pçífÖ-a¿Å‚ð@Ãä[”4À]€4~)äÀ#ÈÖx+fnè ~·\‚N‚æ9-㘽L÷-ëÜ×?„b,÷ í¸L0ÜoÐä\€Ò}‚ÌüI4$ˆ@ ~Ðã;v ‰þXßžôà‚g‚¶{uÌ ®F é#ÿ\±Äo¯n§‚ý`¿1†úqþ6‚BýAæz̶‚&P@üÍÀ%à›ï€æ7a¼°MÀ@ `Ð@÷³¶€ò(àßTÀðëÛûÌ5í²0Ó4hà Æ0Ãqˆ®/ߎ»åÙ ×ñÇ=ãž÷d6ß,Ý“w$ZTR›@ ~hp@b7Ô¬ O¿³Ýïör'àPžýºï®r»`½~ùÊ&înJ[n`»`5BÛå†ú@ 0¢8 {0 ~¸8 àav?3îL÷ú&û‰ùYîü|ùï샾uú(g­&Ûdo´8Ú‚Ù‚1ÿ€ù€ ‘¹ë}ýõóqÞÿïù¿=i <`EoÉÀð €  ÷¼ <ÌtÜǹû™»)”?&Ù-…šØ×ÐØ§ÐèߠޱâþH¾ÄNDçŸ~Ýq¶ác a†ÿl4oÌ€€$€ ÀäÁë~>@N0ÂpO¨Ÿ—Í €oÇÇ3Fñç©Ñ¿B4ëô"øŽhG86fѨ¿±@ò&ì#]Ÿoîv@/©³Éò,±üi¹Xa¾»|Ä>‡ßÜ,Ð>lÛænžo@ ¹Çû Î:@è?Ô,H¯o©½_Í«à_üî‰éÍÆKÛÁ"@ æ€õÅPã6‚$|é*‚(‚v÷®Ê<ˆ)úS 7øñ¿O‚j‚¯‚oç€h‚h7æý>}ª>¾ ¿žh  F <  à ûÖñ †ÀƒìÀšÀ ›þ@‚€›þ­ì›Æ‚³h#‚où oú°Ã•[ôéh v‡À {=Û ö„€:ƒÉ€‚Úë eoÌMú,»` tР@øÐ°>€-1Õ ?Ï÷ØøŽaÀˆÔøö°ÃQo Etôú±òûFœ  ÅBkî¸VføÞ½¸ÀúÝ<Ü>xÄ_7E"8!÷!4À@d×¡à†¤ Ôa†ÿ7ÓA¨A Á4Aò4@@›ýMó&ö€ÀdP@ò@@÷@ @àO nĈ# =ÿH€ @èö½ÐÀ?ßaO¼Épšgîéa†ÿV4o–‚´È!`9þ€9lÞÜÔ¤Ž–bSõqȱBöë¿þçøø!N½Àˆ¸D"þ#亀ñG0Ã}¶ø°!Œ ÿH&ýx &€ð ›þmö–ø°!Œ ÿH&ýx &€ð ›þl0ßÃý@7þˆ›ÿ¦[ö˜!þÀ ÿ&ÿ8„Èôñ¿ú€oý7ÿL$·í0Cý€AþMþp&ÿè&ÿã 7{|\£ã€@ °@øP@ p@û\Ñð@û.Áñ¾à =®‡`!V€5þ # ”’oÀfQ}å¾ÐßA \A7öAï7úÁA4˜a¿Ðÿo‘_pD_¡Ûœ¥ç"=ýÈÂý½Ås—Ï(¼wÓÝ˶Sgç.Yc;|ÐãÀÙ € Àôàùà …L&Nœ…?ú+„\ ƒü¼{ˆÐÍ9òß«»ã·‚ýåo¥°Ã|€@ú€`ƒö ö` ¾€ ùæþ÷ÜŽ~fPMŸ+U@ ´øéè+b‚ ü~pTÐêÝva¿b(>Æ  x ÿÐ 2 ¿æß–›õ(!ˆ  û&à'ÿ@ €È&ÿ›~Za†ô€ý4?<̈¯úì¿­Ò 0À üæRà!=þ#|ö€Ždþ+~ÌGõä”?\ït¼‘À€1~ÿçh#ÿÖýH¾âù ‘ÿ¾o­ù˜>ø‚Àvñ׆À ÁõýÀ n¨"€ ±âþ¦!ªlê [Ъߨa¸ß—Á ÉÀ€$€€ €>üð‡ÀBÿ€¿¨1{@U@ûûû{Xˆ€TðUVý@ß*±ÕF@D1,yÈ@¿ÿcùú.]íÛó èNìçøíò=Ï5ô¶o“ªÀ Á€ñPlx’0 HO×e@àß~¿Âîôñ"þ8ߤæ ÙÀ € óÀó@€ € È€ÀþýyòD)1ûñÛœNÛïs‘b…>.T÷pé[éÌ0Þ`R-45!)ûe ¤p €5öŸúˆÿ<Oþ8øÈ9 PÀ þÝàüäŒÐ¯øÿ³ð˜` ¤B(|Xp+Ÿö³~Z¾½‚‡àG[Ì«T¿Ì¼¸@„‹*žèûÜØ}€O>HÓQXa¿߬ÆÚA7ëÁùA4ÀÀšMù=¿X´‚oׂò‚h€ 4˜a¿Xß=1<iÐßà [èMóÐCÀAöMÀA°Mþ0ņüÊÑ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Foï ‡Ï ƒú𠀂`›üà š ö6øà!óè þ€&‚` €Ø&ÿ8¨&€C}†ò`†Àƒí š ƒü ›ÿ `MÿfüŸ ©i ¿Xs´‚h(‚ò‚oþ€ 7ý›ò{ 7úœêˆ u˜ëùÕÏÖP``ƒü@ :èË’y7åàäO$t°‹”žñ€·µoÌ €úFì›gzèYƒxáú‚<?ûÜJ™`·ÙîãóLØ}ïS㸎­úy†4a†ÿ$‚fF‚h*‚h Å´o@âÀ÷ €ŸÁBì:æ{‚à8t‡ãÎ_+<Á¹þó»•6ft/L=³‚fF‚h*‚h Å¼ßÐGp¤o§È·OÛúïóÐ|ºÐ Ïns÷!dÉm±†ý´Ñ¸ € ?Wµ:‚Ó‚‰6ïØ?_í÷÷qï–` €¯{õžÿi@'€„Ýyã»ÏÌnó 7ð @÷ßÐ@@1##qÎl~ ýH„pàÙ@(KìðY÷÷' s-ùÕêO ñ†ý²÷îGx»ïÿ}üùï΄ˆÿ_‚'~ÿÞýóEpè/·q»Ã¹~ÜÝ’K[Á‚i¸'*"héà À2ëŸ_ ~³Ž¸ãŒþûìx±=å™$ó~ÛmQ¬0ßç@@úÀ/¨ }( 8   ~x)§å75ßà ?>½º€s—çí» w…œ¸ ü·+~½ÊwAïîóô€Õgܬ¼˜~09ý"ûög HgÙŽ…6@÷@úðp@ùÐ@øoé?Ÿ×Ë8ø#gù¾.‘ˆ€?ßöuºû©o=öýÛd"7m†ó@·ô°ZS¶ÀFÿ±P@Ðc³AýsúÜIÓöA)"æ[éÍù¨«¿`@`@Ð@pb® ÐAþ°AüR82*h!€ÇY×:äâЋ(ŸœÑ†ùÃFa¸Ñ£Foé`Oà:¨X '÷°vß`@ú`ß”À@_²0í¸x÷ÿõêS›ô‹~?HÞÚÁé … ˆXq?È 0܈üôŒwö´°Dÿyqd-¿_»cøÜÜ£·6åÍË …Ä.Vn çD|v/§PÍ玞÷eýÜvÜuñÀ ~Û@À@úà@À@ý/·.¥® €*ùÿØWÚuÁUSe;ê;·SÛöC|äêèü4ä?Iɧã@ û+ð}¦Æë7ÏÁÅôR„ôçžlÌ  Œj ¿ä ( ¿êÞɼh!‡ð û0&‚0¨&ÿ € &ÿ« 7ùU¾ÀÈ @è `€€ù~Û`ç‚€(jËï‡ì Ò(q]ˆÌùv·½9¾>ݰ@P=È>HlÙó€O"Aô@€<ß÷FÐè!çHœ‘‰o˜!ÿ¶8W‰&Úà þÁhÈðŠ(Øïèï¿ÃàÎDÈ¿or7ȧÏÌ/ùèXŽxíÛÀs››+; 7ù<=ü×Ô0=øh½çŽqHÀ@0Яw?4ÿÄŠ+ã¬3Î^,®‰w'ñ­äÛö8 |À ~ Ð@Ð@0@`B}“¾üLàOÿèFê}ÞWÁvÑSv9ÙhÝÚS|Ð:?‡°îýÀ ?ÿü#¿éñï÷“'Ÿ·Æ‹¹<ô¤LZŠ9†øò¿Œ×ä£7rä»™K…T.Fý@†DÌ;>ø=ÿúH´ˆògB/Ðßðpa`‚øÞ…õ¿^ þxœ»m‹‚_‹ h x |¨Ð@ø°@øß°ïp@Ÿˆ$_/"6H,I‘À H¯þûÎ8àn»¡?v¥©onÃ Æø°!Œ ÿH&ýx &€ð ›þmö–ø°!Œ ÿH&ýx &€ð ›þl0ßÃý@7þˆ›ÿ¦[ö˜!þÀ ÿ&ÿ8„Èôñ¿ú€oý7ÿL$·í0Cý€AþMþp&ÿè&ÿã 7{~u1pyßÙ¼ßë!Ð"oÑMùÔÅÀAäd~ð¬@„@‰†òÀ€œtrÈ\+d€-1ÿt¼´¨  ý·ùÓøûâGÏÁšþüêdoÖB¹p¿nÅwÆÏÖ»žz×=Ûçç5Öl°³â߉ôØ Æ‘¹AÁæ@€<a28@ê’?ÏüÒDOùå7–I~~s@xÀìa†þ‚Ñ£FoÄŠ±‚hô ‚où·å¦ýJb(>Æ  x ÿÐ 2 ¿æß–˜a¿»ßNÁ¡¾  €bŽãÀÿu?0?ÿ’7ÓÕÄo‘pöû3y0@ûâx y }¸ {Ⱦ;Z|߀@ûÿðP_O ­xŒÿ܉A þ»‚»y§·¬a†ãFÎ\N˜ ~®à  Àzìÿq4Oľâ,§’»ŸÃà,&æàØÙAAìÁó@ ‘a¾“Fæ‚0‚Ù‚7æ€1"6°@` ‚û‘€€,oöùëŸà±üwо/‡Çl vºéx~_ÞnnX\a†ïoŠß^Á_]ý>š?€Aûñb þËä~ý?ýý¿¹úöÁö¶ñÀ÷×@ @`@÷b/<]ý€1"€}LÊ<À¸GNÄPCÿ^A¬0ßåFý`~4>Ò ¿^?Ê  &Ð oÉíúÀüh}¤~¼”@ L  à úÆùù@°@O  {o>â:~Xþ; ©øÜ?É@!o 0ï—ð+wŽ´¬ÍÔXa¿Å@ ‚0‘D= ˆÀàŒ$QoÚ`~äpFÿ~ùÖ€~äpFÿ~ùÖoñ`ˆÀàŒ$Qdsþú‘¾úïr0ž·]جÛ&Ù²©Ô1¿k=û‘Áýûç[åq"Á[D‚ÒNùï¦ëäÒ€€‡F!Ð a†ÿ>´hц4a†ãFa¸Ñ£Fn4hц ü>Ñ£Fn4hц4a†ãFa¸Ñ£Foï ‡Ï ƒú𠀂`›üà š ö6ø( ¸ }€ p v( }ܤŸrç¿ýÈ€@à€ …}}°pCÿ·7ìŽþ6œL¢3c}D`=¸L¿\@,0Ús´‚h(‚ò‚oþ€ 7ý›ò{~­ŒC»8…1/d_×B÷3çúÐÐ1aVÎKéúlâ€W=÷-oÎ 0ÜÉ7d¶ÉjZ߬7Ø<¼ô>d'À«(#Ð04H‡••À†^À|§Ñ)àürá|f‡loÉíô @ 0à »ÁâÀ€;Üâæ€Uì§ðTGøùMu oæy÷q€`Ä"»ÈEÊkØa¿ÓMËl·)‚7ëˆ%  À {p ˜#~¸Xa¿Î@†€‚ š  ‚šè1o2Ýð@¿À@ `@ @ @ @ ’îg¿Ø¸ (€ïïÀ¹/1Dõôt6iãۀà ý¸I;Ä> È?X€MË.^=ôŠ /ï/“Á€vH¾¾`!e„ñ:]êyúS§»±Oãž7(~=ܽÛÍÊl¦åË,²¬. oÄô @@@Ï@ôð@øÒ( ~° ˆ yQ€¦GGZ°Sð‹ð+yæ–iŽ@×ó¨>ìa†ÿI4o.–xl¨%À H  z/ú|¼`ߎÝLïùÛøœ ë°ÿ.p²ÆVô >äOèab¾ÿO‘\æv+ß®ìOüvÀ‹/Œ²Ë a†Ý£aNÅ+)È(¨8xo¨­ëä÷Ÿþ7íK Õ•bVVù¸ @ }ï`€ù„pàö@€Äñﯞ*#Måÿp9ñ°ðžxáÅÝó"¸¡©F0Ã~„°ÏÀ À {p ~'?ñBãÊp˜  ü€!ùw+ñp€_¬ÝàïmäU…@Bwìy•¿-tDÿ{||ã sº W·…uóË!‡q˜ìÉ Ù$’6°@@@ €€@€  €€ €€ ¾0ð ¾>yÃ…i=ôX¾wE›wžÄ‹}í÷ÍënÒÛ 74hà Æ0Ãq¿MGÈ€ÝÀ€ àõ€ %u€v@EËïíüa®/ít/þY*¸Où´ƒÖã[ó+tAÁ ÀÕÌ‚‚è€=àª`!Ÿÿï(׸·€À ȱËЯžQÆí§Ç:nÚà þËhß1@©€ñ`€ß_¸ ð ~h‡ð,Œ+ ß7_×ÀR'OGúo\=nf¿Eæd:Ôo~ž†XNb,P€` >⳿ÿ›Ýf£ÝVZ²[ál²à ïhÊF+ãëØ¯a=»ew=qÌÐí~l”rØ[Ï‚W‚Ç"‚ÖûŠ.ä @€€6ÿÿzÞ€Pø‹Õ–ç9´¬Å͔豆ô"ð Ž,P|æ®?Ž»ã·»®yvÇl¯v>ÌÝÊ–,Rã~_”÷øýƒQœQƒ7€@ý`AýN@€/ÿvCpgöøØ¢â»“y%П‘º@€"` €`€?Ð@üð@ü2'P^õì§ôöPºs‚ïw}¨>ÿ.sºà&ëê7̘a¸ß§ÁµA×Á7óÀ 4Á4›ó~‚—< ·}-+N®»Íß§ü€ ~¹ˆïnÄm˜MÀžÀ  ãööSØa¿­70þfÐF5ßòßõm €íÀ€ `úâ o,N‚ž|^R¸]w¹„ø @Äw‘D¶<™¤W€>ã($î|îZs 7ùÕ£FŒ0ßÞ-¾à³À%ž.Ïs}½€À¿@ý@•ÕAüݰ×ã™~zÕ²§»âòö&üLûéðüŸÛùýúˆã&¿¿‚'ÉW·Ï¸Ž;~Ñ]sÛËøÉo–[l…·ÚASû"‚µàà€ ¢ˆÂÉ¢€‚;ê'E÷×_÷ á7VÞ>0N; Rñ# 7÷Ƈ >ÉG(”°¡8$‰ï¢Éý€}HãݦÙú~YcvwÇ=s×€ > ÞýÀõôï‚0áØ_{Äy[ C 7ô@ð@ü°@€ ò€ @€ Äæå3ÿ"{éøD÷pŸáàsw7ŽSniT™±c~b®(hDWö#5¦ßÆ€/1ìŒä¾uº¤ò0®€Ç±Ÿx{ÔÝÛ^–ÈÜ @0@@€ €€ € @™’÷¿Q\ Ȱ^ÀâlìK²OÎtìãŒRêd­Èa†ØÿÔøè€ ¿úa%¿i‚ìðoó€A7ü€A7ÿñ^lùxŒ—ǻĞ>w -ú°D=ÌD¼Ž¬X®@ÄL ûÿóÆáÀ8Ò+ÄîXqÕö0Ã~½¿:‚¸<‚oì‚Þoõ‚‚h7ØA6ÛÁõÄßQÉ@X´İ?0«ÿ˜÷¹ßß Ð@üâ¹îK€†žà{T!Úâ !ÿÓ€{ â“Xa¿ž7²u>Škþ}…˜LÜãZ\?›žÍò«óô¿N ’/”™´ëŽw° jõ‘¼kFoöä.%„&ó@ð@û @p@ô°@ôp@€à úÏ Û@€ À¼Ûýí†^!<æ}vüŠò—‰õøþD@ Ò‚l·)‚7ëˆ%†Âb(>Æ  x ÿÐ 2 ¿æßšÁòDP@ú@@@0@ýA2_Õ˜¤0üzœOE|h®Fý9þV·ÝA(äÁ8Aà ÀÀ#ë®÷0‹ÿ`cö/÷"ØðEþÞÀuÆŒŸïËNo˜"À.ÿÎr,</s˜÷n€>üèxr1Ge¼ÀC°ý½Åm¤@B¹ºÃ þ£"‰×ÿòG÷æ†ò,OÏbû}<}ç—½ö€®ÛY›Ël·)‚7ëˆ%  À {p ˜#~¸Xa¿“‚\À'‚êóˆ>òœœ¤ð§¨u ~2û=pÿá¾âøÒk|‘½@ p¿° h à Ð@œÌ‘‚8ýoíÜ—¸¿Q^ÿò?ÎõÉo›oáÚ{yvn7Û IÀÐçqP¿ìA €æ*è ‚ù‘*H_3ößon”° wà |Ø   €¢s7¿à @5nëßÜËÑZà™òFùÊ}€Ï‘|H•†ýJÈüÈ ÐÿATü‘>Þÿ>Üi0ÿéâ„sÏNÍÌ‘¼È À {p ˜#~¸Zl·)‚7ëˆ%†ü²œFG¼5ÕúL¿¼Á€@9s¦ž7ø‹ñÝpñÛc~£?i߯åÐh7À€ဠó v$Ä.w˜÷æ?öj°³úð@¾Òº‚{€?¼‘~'K2gËÄY뀀 ñ€€`è ùð  À À úè^nü@߀;f/íî*ÙÁ]? œP ç¶l¬0ßÓZ7–ØnSo× K@@ €@öà@0Fýq°Ã~¥£y`@ €@öà@0Fýq´ØnSo× K 7ê[äà†.@ƒô`›ú ‚ &€[zð@ €@öà@0Fýq·ÍÁ êA KAÁí@‚å€/ãŽ_Žÿûý€ H OnøY€‡ÿhŸðG=øÍ‘†ø£Fa¸Ñ£Fn4hц4a†ãFa€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`! ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`" ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`# ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`$ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`·xine-0.9.4/data/noSignal4x3.png0000666000000000000000000010506710666565132014744 0ustar rootroot‰PNG  IHDRÐ@\Tö pHYsÃÃÇo¨d tEXtComment‰* IDATxœìÝù[Ù?p]@QP@ÜPÅ]qÇ 7\ZÅ]Q@Q@}G;“Ìô3Ý“'“e:“tz2Ý=I:é$O¦'I§—$¶÷$Ï<ß¿æûn>Ó§UuëÖ­{¡î½¼_?ø\‹SU§ªN}ꜪS§bbˆˆˆˆˆˆh¢áááÁÁA¯s4^çÅkñññ^gˆ"Äùóç½ÎBØàÕô+ÙÙÙ^g!œtvvz…°ÁZÖW222¼ÎB8aÑøÊÈÈÈÔ¬(2vzooï¬eÆŒS°–`EÆ›III+W®ô:S˦¢9‹†¯mù¾Øµk—»}åÄõ¿òäÉ“üü|}†•ÍŸ?ßW&V¬X!E)ÐÛ_ñññzú™3g^¹rÅW ñcxxØðW•¸¸8‡yðiÇŽ¾v‡ú““c“E0ozzºü¾~ýºš¾~ýzóbeÊêÕ«-Wªï)×ùùÊÐЪJÄÆÆšWc¹¦žžž wGŒUÆWÑhjj¬¬¬ââbýO*Ò™‹ŒûœeffrƒæY[[›ZeZZš“|´Rs‰kmm5rLQ›jY4 ;(Xj£££æ‰k×®5gêÑ£G®×xúôi,Á&' v„šØÐР^Ëô×yû¿…ª² SvïÞ­þë°H;!¡û}ñâņ?ak ‹Å.ÓOÃ_9b™ fwyûÂöíÛÍkÒÿk>'ñW\Œ\¬«¿¿ß¼|}±zñìîîF=]¥¼zõª„qÿþ}_Ëq/55ռĂ‚LDyV¹4çÛźô"†%ìß¿_~¯Y³FM”%oݺռ"ý·*ȘxîÜ9ù­ïJ—Ì%VTWW[æ#Æí°‹/êÿE1‘·¯&$$$¨ß¨Ho8mQ'Â^Ø»w¯‹,¹¤ï\\Ý-äÔ©S¡ÉM8@HCåÕõì!«D‡¼HÇÝ1wÇÜ óõ׺ºº©ÌLX@±¼73}/:RïR÷Çûúú¼ÍQÄzúô©¯éŸ~ú©ù¯ÉÉÉŸ}ö™>=77WŸëã?Æ¿‡6/ß°4ñXW[R×®]{çw m¶áóÏ?7$V»;¼ü§ãÌ‹jll4Ü‹ÝQ]]ƨ¡k’¾ao½õ–aÃR»Ã² ÉDý_}º¾sÃewÄ˜Ž¹Mé0O´Üꔚh8õ>§O £ÝcÚì?ü0f¼˜¨TB2m;bÆŸÑšwG^^žú- ………òû+}\ðEDDDDDDDDDDSlãÆƒƒƒ!x… Ò•––|Éë¼x»à+–ï¶MSS6„E˜¾=‡ÍV¬Xáu‰áÅÖic¨}…!c‚%K–x…°Á×Wô—¿iŠ¢†á•ü05e§Idœ†·é§µÈ8b!d&÷ìÙcò&úYÖ5Ñ61Y³f…d /¸aÆäädóô¹sç† +2Č͘2ò§¼¼<ó¼’råÊ•ÝþêèèHHH°vÊfÅÃ×’Cs.ëKiooOIIQÿU£\›×TVV¦S$ƒz¹X©¡0úÿâT]¾|ù­[·ô?©^·È@iii@yð“3_¿oÞ¼i9— Àã*øR§hnnÖ§×××›w‡~ŸÍ¦à`^w™1RKÔÇT²)Ì1ã·¿‚iÎb^Ëh6ENœ8¡ãû]ögŸ}Öeΰ8Ë]¾hÑ"ù{õêU_Yt!''ÇrvÈ0ç£:ʤrî†åAV«éëë3¯³¸}¨¼¼<Æj3=z„‰†ƒ¯§ìîî6\Èð'óýê`/v•••ˆO†ÕHI–“€– £ëå&›Í ‹¦¢\ÔôpœÍ;X®»Rj?nø«åe? ú¶-]ºTM” ˆj¹é%WÍ¢†îׯîñññÁ¾Ø¡_PuEEEê÷íÛ·ƒZÇ— × Ù’€GÈ,©©©úÄ®®®`†> XRR’* ®Ç5ŒªÛˆ?>xð ë%ðóhûy…°qòäÉÐ4®¢ÃéÓ§:äu.®ŒL»{hö¤îu.ÂIkk+öHGG‡üUlv!""""rå£>R¿õá|:;;ñß?üÐ<ö¦ëã&ésedd࿟|ò‰aÈ#ËUÿå/Ñÿû /¸Ú‚B^Õ¨Dú6<ÿüóò#55Õð˜çƒ>Ðÿ«ÏeR,fü»–Ͱ^}O…Ëîˆ1 êõꫯêip´õÿ¢ýªë¤æòõ™´´4óäùS˜îŽ˜ñá¯Ô†n©¿ñÆæÕ@tzé8yò¤üÐ÷—t¡1œ2˜KÆ ûþ÷¿/SÂkw In:­¸¸8==Ý<†ÚøÃ¼¼¼¯}ík*¥úÊÑÇ U"}¢ê¦&bwÈ aA}·‰ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆÈ ---qÌÔéndÜúõëõ‰øïØØ˜WY"ÅÆÆ20¾PI-Ƚ{÷¼Î…%mAúúú\çHÑl`` >>Þë\P˜)**â 8daxx˜­²€€ÑÐÐàu.(ü d,X°Àë\„Æ™3gø|'4 £©’qòäI}¬erïîÝ»Üd—%ƒ, Žó:4™äëj$Z‡X_‹* ;wî¼ÿ~__ŸáONê_ .’ž|WaÀô™ÄãÇ«¾^%%%nyyyr™óµ9˜ØÖÖf“[ÇɆåe´··WÖ˜‘‘á7“íííóÆÉ,qqq~g™ ’›9s榣ýiP.\¸ OAùpòÕù¢™|ÓéÕk²_fÏžífÍ4ÅÄòS@B¾oæ÷3«×¯_оþ•˜˜xøðág§Vzzºát £‘:¥ÌÂQ´ü ªe‰q¸I*Ù¥K—Îkp 9,++Ãmf“ô§N²_lvv6’ÉwŽô¯¡ÀùÝ!H`øÐ¦tuuÙÏ5ud466¦ãðïÞ½Û0QÎ$ËOt9©gȺýž|ƒÊ/\Ýl"_1Ãܾ}»ó¬¾f&Õ²={öbòl¿™W®\ÁŒû÷ï—ÿîÚµ+ì:Â!ZžñæÝ‡‹ŽeJ9r~Û&²A¾q6õŠÕKªåbýž*<¤¦/©©&瓞KËz®¦Hf.°ÿ$ª(((ðê:*½Õoß¾µ›¿iI²*Ÿî󥿿?ÆGeÅoÉhnnv¾7RÇ9IzÈâÚµkå÷ãÇ-ÓȵӰ1²Ó¢1ÇÞfhzX¬IõÌ3Ïè_@vr0$”u›Ä²—ªªªÀ¼›-­¨¨P¡Úï7í7mÚü×ÝÛ»w/"'Žý¶mÛ|¥AL6¦®®N¦è§…ßà¯O—ßBX‘¯OŽªMؼy³L)..¶|?*--MEJ욚˥™¿y‹2!s­[·NMÄ*Ì·+:;;U~¤=(opY~:=R]¼xÑætœâ›¤N.á铟úÒð8}ÊÝ»wW®\9•y@èò¤ªK  6!M#ÒHV_='úÊ¥K—P8rrr¼Î…Ÿ 6X¶<‰¾ mBó㢯žÚTWW4ã… ظÔ3°ÎÎNû”çÎ3?‚¦i¡««K=QFFFNœ8áuÖˆˆˆˆˆˆˆˆˆ(Š”––Zv Åt›¹Îž=ûÒK/9ykRyñÅ_~ùå””ËuÙ¼²–——÷íoû;ßùޝ,:=f¼¯¨ù]/eõêÕ6F>úè£?üðÓO?5Lúô©9qkk+¦ë/úÍœ9S>ÿüs›U|öÙg?ÿùÏm` ¹¹¹†‰8~˜þÁè322:džn‚>K{{»å__xáäÙ&ÃÓ ‡<ÅÎ:}ú´šnÞ³'Nœðµ»±+mÊÇ… 0ãoû[_ , &:|‚”iiiYYYø‘™™©O·L“AÆãFËžî,ÿGøå/©v¨yÏ>ÿüóØ­– ùtœßuavËf.(†”8ZrºËkj†ÙQ8T2l‘¯Mbduݸ5kÖX¦aáø?záØ_¿þõ¯-÷òÉ'†‰¯¿þºMýÅ¡÷Þ{Ï2þ›#Ç;ï¼c^,Š—}áPSpí3Ïþì³Ï~üñÇú”²²2s2Žÿc.päÈ›ã­Nb@YñûºßÎ;%fXÖ b|ñãÿø©ç½ùý"sá€o~ó›†M˜={¶åF½ù曆¢€Âñt¢Û·oÛo#Ñäimmâç•è éééêãáŠåÇâiz01ëEÓËêÕ«Qêëëcccõé Ó]KK‹ßøÑt„*ç²e˼Î…\5,S§éU¯³@aéÌ™3üÞ8YX±bÅ‚ ¼Î…4MÏŸ?ïu.(, { Kh¸z KCCC¬j…’’’‘‘¯sAai``Àë,PXÂeãÆ^ç‚ÂÏ7FGG½Î…ŸØØX^PÈÚ®gΜñ:~êëëycƒ¬á‚bþ>#Ñ·ÉYÛ »wïfÉ k(û÷ï÷:~Çy‹Á¶°òIIIØ•~?K)Ô«4^g$*à<‹¦û¡R2øÈ0Ž;e'Y{{û‰'¼ÎET@Éhnnö:~úúú¢,lPÈ dìÙ³Çë\PøA•-šš¯däú-£E‹!ldgg‡6?^pŒ:äb®¡¡¡ÉÈ…OË®_¿ÎQ4¦…ÞÞ^éåË—;L_UU…ôá|׫¸¸8++Ë0qÓ¦M“·Æ‘qÎÓ#3Èääå'`óçÏ¿|ù2jJJŠ>½§§ûôéÓö³·¶¶bû{Æ9‰4;vì@²¤¤$}¢º3=5Ùêêj5¶˜ùP]»vÍ&ÛEEEN²„% @¥MÖèä"‹Äõõõˆ¸­=~°«« 9زeKss³¯ç(.˜¾}ûv_ AhÙµk—úïÐ8¿«–Õ-^¼Xþ+»CÙºukÀãŠBAAZ)lذÁÆæê©Š”ýZä¬èèèPSŽ?ÞÞÞ.󦥥ÙÏ~ïÞ=50 *yÞåþýû¾J·<´œ+//Ïð'„k'acÙ²e*<ÄÆÆê·PQ'o§àhY把 _Ùž9s¦ÃÇl†d²3ëêê0åÆ~^ߌŒ õß––Ì¥Ÿr^’ ëëë³ü“åÑ’Í6LÄÑOûÕádBÐ2ãc¸®…ÐÉ“'ÍçÀÅ‹m.rnœ;wÎ~É%%%HvãÆ ùïãÇõÙíç3ìáðêá G ‡Ê0=??ÓÍw;îÞ½kn`v'gØÍ›7¥ÆzôèÑàr0ÙL”5G×V_é¥ríd£ôBpëÖ-ù1{ölLD¸r8£ðæwS$33Ó×.@¸ëîî6L<{ö,›ŸL:¼Lʺp!s›_÷ôÍDµÆ~$dIï÷ @rr2’•––â·^s÷6PÏE‚ÊÊJ5eÑ¢Ea3àÔ©S¾ ‡ybvvö€Um_*t~ïmÈþrqf 9à·¹¨*:H‰æ’}â 8k7I# -¯¨‰2Žª^“°ÌÚÒHñ›«©&w2̵QË}myh·mÛPؘԛ ¾,]º«F]Ïá©)YÅ\öÉ6oÞŒdmmm†Ö¾ß"ü‹æð9ƒß&OèÉIo3úðáÃsæÌ±L©ŸâöíÛ˜ŽsÅ~EÒ*öª6k_»v­“ÄrÂø-Fª-ƒª®>½¦¦Æo 4‚š¯ïSAr¹nÝ:5[µ{÷nC2Ô-÷¹ÄUûµ ¨¹» „„<"v¾vÉ*jW6iÐ/((° º~Ï)=ÎÏÕü™jæcfÙ Î2ÌJYpД—cƒúM¹ Ôüùó׬YãøL-..~ôè‘“ÄÛ·o·L†KŒÃ°¡WEm]»vÍãÍHuÚù1ÓŸ§§¦¦êòu¢((‚N.®¡â+3R«›<êÃÕU(˾*ìË.2÷ Ú¸q£åŠÌQ±ÙòµÍè8”E\³oèdÒ>9qåÊTÄÌÓíKXuuuÌøÙhöܱ/…ÙßÃpµ|”BÙ|_nÞ¼)…©®®Îƒ[ŸSÀ¦p ;•ï¡#RG¶7njòC_RË6ýw=—ÞxS¹FòOî¡éÝs’’’¦¾w1Âß„G(R¥zòä Ú„ž|+ÍË©_/…»óçÏóšBÖä†×¹ °гxš^¦öÁ E_÷ʼnX8È7Ö9È'yBá°ç0M/~Óô%ÕwÏ6}u¤(aÓ—Ç"žT/ ‡é/^|áÂ…IÍ…‹¹s窎63gδOΣRÑdQÝ]eÄ"Ã_wïÞëÛ5ÓׯUD×ÛÛ›žžîuˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ"]JJŠù˰k×.ù¦•¯¹žþù_|±°°ÐáŠ>üúë¯755™ÿ´téR›uAssóK/½422b9f·üJ¶Kÿƈù¯6k´ÏÏt=ûôéÓo}ë[†é|ðÁG}d9Ë'Ÿ|‚YðWùóæÍ³_Ë矎dÿûßñïgŸ}fø+޽¯u=ýÒ§Ÿ~úá‡âþ5¤¹wï¦;vÌ0+úÞ÷¾ç+K˜åµ×^³ù«ÝöLR8`Ñ¢Eút_…)?þøc} ’a¢ù롆¹ÔoóW|Ž¿þõ¯˜Ñð‰µŸýìg†dR8̇Ӧp¼òÊ+–³Xfxú’ÂÑÑÑaØ–…Ær¯á´öuêÇŒBm¿¯- Çüùó1—߯ÆŒÌþ›ßüư›Â”EEEø××g³Y8¾ …?>§¦[$° Åqqqö{S®¾F¶,Ÿs² R8̳ø*ÅÅÅ’Û_üâ(Ö–Ëdáø‚*1ãµ³, RbÏZ.²#¤ùÑ~dþ“eá@úwß}×É&¨Â!Ù@‘ß¾ ¶ñ§?ý©Jo¹LŽ/è…#f|§<÷Üs1¾ Ǿ}û,—ã·Úo¼ñ’½ÿþû†é–…‡ðÏþ³a–½pH²²²²ß… Ö¬Y³nÖbÎO ‡0Ž9sæà¿ÙÙÙ–…ÍË8\]]ípo^¹rŜҲp¼úê«æ”Ë–-ó[8Ž?.i, Çï~÷;ij)–9gáø‚¡pÄŒ¿½Ž)úÓŸÌL®ÖË—/7L·ŒŠáæ„ÃÂ!)ùË_êS ýøÃþ€d(ÊæÂéK–,1L©©©1'³Þ˜iÅ\8b´;æôï¼óþô«_ýJþ[__onÜ$$$`Q999øýÞ{ï9/¸:ÈùØó¬Y³Þ~ûm'…Cm‚¡pTTT˜g±¼€¦Od³Q˲pÄŒWN}µN·% Ôùý®åoû›Jo¾cfs,66…CÍ‹Rhþ—eáˆ?À†ÂóÝï~×2¥¡ÂôÔÄï6ù”””´téÒÂÂÂeË–Í;×ëìQøINN®­­ðaûöí^g“ˆ¼W__?44ä+Rè>ìu~‰È;‰‰‰]]]P¿@-£³³óÂ… [¶l)***))9}ú4Êè訪}”——{k"ò¢êˆ—.]r>WBBÂäe‰ˆÂW__BFee¥×!¢°‹æÆððpZZš×y!¢HÐÝݨ1cÆ ¯3BD‘ ¡¡µ † "r¤´´ttt” "r$##!cýúõ^g„ˆ"DoooGG‡×¹ ¢qâĉ¡¡¡E‹y"ŠÉÉÉ###ÃÃÃ^g„ˆ"DSSÓÀÀ@}}½×!¢H””„ZÆàà`qq±×y!¢HpúôiT4ÐB‰õ:/D äý÷¶¶6¯3BD‘ ??_Þpß±c‡×y!¢Hp÷î]„ ¾¨FDNŽŽÊM ¯3BD‘`É’%Ò|xðàA¯óBô¥ãÇËMû÷ï{š °°PÿHÕðððÌ™3½ÎQLL{{»Ê3gÎxš çí._¾ìu¦ˆ¾ìH›6mò:/dtôèQ2FFFÒÓÓ½ÎM{qqq(‹|€BDNeggK]mæÄÄD¯³CDaoëÖ­ªöëu^ˆ(\¸pA¢j^ç…ˆ"AKK‹DÞÞ^¯óBD[±bÅààà½{÷rss§fêþüƒ¦fDzò”ÿ.X°`òV+}É¡¶¶vòVDDSaxxXõœ¤­¦¦¦ªµTUUMÆ*(Ò%&&^¿~}ß¾}^g„ÆÍž={Þ¼y6 :::T ¢¬¬,ø5"ú”——wwwËXäz§ÃŠŠŠà—?44„%#ùMyæÌTs†ÇŒŒlÛ¶-øµOœHÇŽ“€.»qllÌï\ ØÒÇ»^/*¡;wît=»8xð`OOÃé¥K—ôrrôèÑ ×®¤§§ß½{÷ÙgŸÅ>Q5küÞ¸qc¨V©°kŠŠŠÎž=ÛÒÒ"'‰~¢¶··û]‚~ÌΟ?ï.«V­êïïGPp-Z¤¿ØŠé²ð-[¶¸[¸¯Ü9rÄœàøñãÒ©Ì gáÂ…Áça2 ÚîÚµKеù8:¿ŒÓÕÝsîÖÖVµo‘·Ûñh•g›l,_¾Ü|¤PZ‚Yµ8pàÀãÇ—,Y¢OTÏò$|8¹ðD§øqú”‹/êÇÀÉ;8ÃUÅy•’’P&0b–¯RÁqBt hÉ–úúúô Ôÿtåʹ2#K¨ñÖÖÖâr§:³Cgggð˜<æ.pzAwÔ«®®.ç«ÆÕB­AÞ)Ç•Lív_‚qhšššðcéÒ¥ú• È iBBÂÐ8Ë¿ââªV„ðÌŠ¢~™rxÁ©¬¬T³ ¤:\Ñ¡C‡pxpÈík•=’¨’§6úÖ©(Šèš5k|Í…rŒïܹ|¦˜z]XXV¯”½{÷Êè)=©—™àor©[àØí†ëPNNVaa¡e⌌ ×+ÅQ–EÙT™Uí)'é^^DBUS/g¸;™KíÍÆÆF'éeÀqxøð¡}Ji¡ 4çåå9Y² f½N[]]3^îï?ª7†|P‚3SjaØ?«W¯tú^ É7®T³Ôpr–––š+Ar]‘Ä®mB¥[½îds}Rõš{÷î¹[QÔÒ v““š››%½“{¢uuu’GÈïƒ[© ëׯw˜_fÏž­ª¾XàÚµk÷ïßëÖ­ æôX‰ÍǵZÿ+êärÖ!Íž={\,_oêÔ >Ϫ¡¤×v¯]»véÒ%sbUö‚ùf8š¥j!¾*Ë–-“ò’»'Ñç§^Ýp²äui'ƒ²ÈM;Y²ß;¨_¨Äååålƒ•ââbÃs™Iíoš––&Ï_ü;³»»'jë8TðSü Ù3Ì"0=˜÷}ÕXÍpãÆ ™ˆxŠ–ŒõÓú“Íô[ Hmm­! Š&­ebu×õ'TŽô¦‡ešŠŠ iPççç»[Kô{ðà~vùüܹsHfsSS¨×Þ¥x•””ا¿sçŽÊCð•ýæüÀT} —âÄP&†êþÛÑÑÍÇ6ø ÌÈÈÐï‡$«Ú(²ÊÎäÊÊJ_‰UÌrýÕ 4ÐôÀ‡ÍAB0¬úÉ“'òhÏíÖL'U;±—‘ÞðDÆìäÉ“ú2íï'¡Ú¢ç!ø»ÖÔ£†Ü„Ÿ&ªªªôp|ÅMA%H¯Á_{G£ÕPy±É­~r=Œ˜ø°êääd—Y÷íÞ½{/^ ùbÃb¼~ŽÙtèž;w.Žêÿ~—90‘}bÔ¥)TqþMyÔVôµû­éD“Ó§O«s{2„#•éQ#ÈÄ ÉãÇõG?ö†ÐÎR!^w+ÅõISëÖ­s·_Ö¬Y3ÆyPE‰Á¾nP£¢aÿHO ¤êÇÆ¾*‹Äcccˆ\j|àȧ^¸“äØ94 IDAT’’‚YZ¤¨®®nmmű۵k—Úüö=Y¶l™‹ÍÏJ‚kþæÍ›W¯^­"ѱcÇlÒ£H¨j‚ó§þfz­öÑ£G®—c6gΜgŸ}vz ¬†ãØdee™´´´ØWEÊ央qçÎy Š °š ÄÕFü}ÕÓ!ö£~ÑÑÑ¡?Õ;¹ßõMµ'‡ y¶™Q¿¨¸«@•••¡€[I¯“ ˆZØxÕÉÌÌÄ9Œèî®{µáu#óýv„ ¤‘^›ŠþÄÔ¾¾jO¿$†ùK%îHŸh¿‡Fß.Î4Ä4|ô)z}P8|™©©©Ñ'꽓ôOןå;¹%¯òŒºÒÍ›7õ‰†î<(ç.n~íØ±5e›GÅÑoß¾}س’eŸS³5¼k×®]SÅ5ÇÉò:©JF0Ï_õ’}ŸqËÍÍÅ¢¢á$±¾+œ0 §fôõÞ¡‚CiSM@¹BzÄnó-öúúz=o~suûöm•ÞÉ€1ã¯ç í`ÙqNufWö[Cµâîݻب«W¯:Impö"üˈrü«â‹-2,V¿°ÈÍ03õe@ü뺺ÑÔÔ¤ÖåðýšHƳ¼ ï¼5nh¯ÙŸŸ¨Ã#΢ŠaåŸ;w®!vÈaE{öÔ©S8PQʼn`ß{×™·»»Ûɶ^„ñ• ™G@ìï×>C©<ªÃÏÓÑt—››«¿Fõ=Vˆ(4ôºœ|蛈è‹aoô×4ëêê¼ÎEýóÝ ÆûœTçÎCegÚ ´KMT‡õtÖï˜&®Ý¿¿««+ø{‰È{†9Kø¨ªª ÉÂãââGGGW¬X’Q™={¶!|Ȩ%W¯^ ô™Kbbâµk×d\ŸÅ‹ON~‰(œ¬]»ÖA¤"C{õöö644  œ?þܹsÕÕÕ---&†‡‡ñWËTÑ42oÞ¼#GŽ477ËÛ´2B=¢CGGGSSSeeå¢E‹¢ul4"""""""""""""""""""""""""""""""""""""""" {K—.ýøãŸ>}úöÛo;I”~ø!f h-¹¹¹¿ùÍo>ùä“§aQŸþyww·«¼egg›Wñé§Ÿ¾ôÒK~çmnn–íBVZ郰 ÌkÞ4LÿýïèÐ!¿ ¹wïÞ‡ã¾ño8Y)vï{ß («píÚ5äí£>Z¶lY ó¾ð r¼‘¢Š‘*ëçÏŸ·OÿÁHÉs¸üï~÷»ê\Â\÷ïߟ={vBBÂÌ™3׬Yóâ‹/âì’¿¢Dæåå¹Þ×^{M6¡··W}»`ûöíï¿ÿþgŸ}æwvÉ¡ÃÀ1þ|,VrŽhõÊ+¯8p --->>>999''å½÷ÞC$B–p@YY™$ãüæ(ª*R.[ZZœäDš¸îܹ3 M°'ç¹ßçÇÎÇÊ•+¥’oÙˆÐá /)ƒ Bš~ðë_ÿ:ÆqàHMM•½ú“ŸüdÇŽ;M0ñ­·Þ²ƒ }Åoà¨o?Õ8|ûâ‹/Jz”6”Q_ÉÞxã ué[¸paÀÛ0~·ïïÿûÌÂ)áä–D@OUpåW­¡×_ÝW²åË—‡*pÀŒ3Ô£ká7pH&ýn»Z¬}2úŠÃÀYYYª„9ïÇ?ýéOÕ#ü@ËùÛßþ¦Ÿ(އv½R³ÊÅ_Ç©ª8~û½œÚ#f¼ ßLAÔÃFá_„*Ã>88h¿('Cœ={VíLûÀñî»ï>uöÌ599Y2lc¾ OÇo<ûr÷î]¿«£h€‹çýû÷qÚ”””8I_SSƒÄ˜Åź¶lÙòÏÿüÏüãQÂpÊýüç?Çr’’’\,Ê—óçÏ¿úê«8± Ô’jkkÎXZZ*Û5kÖ,ë½~ýúË/¿Œk;Ö‹µ£i€šó[¼jí÷ŽôUUU¾TTTÈ322œ,°¨¨¨yÜÖ­[}¥9uêT³?6õJ""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""¢È0oÞ¼+V,^¼Øë¼QKMM­ªªÆ¿ƒãðcddäüùó^玈ÂLII‰Äˆð'¯óHDa£¬¬  _ñB¹pá‚×9%¢0P\\Œ&‰ßˆ,±±±^ç—ˆ<•œœÜÞÞî$dš0………^g™ˆ<µ{÷nÕ6APÀ†C‡­]»¶¨¨¨¼¼üêÕ«ÝÝÝê~ÇØØ˜×Y&"Oµ¶¶J¼€ãÇÏš5ËWÊøøøšš´e._¾<•9$¢0‚! „Œüü|‡s!|Lf¦ˆ(Œ-[¶ldÜ®]»¼Î E‚mÛ¶É ‹ÔÔT¯óBD‘àÀh›>u3gÎx"ŠCCC7oÞô:D9:;;[[[½ÎEŽÛ·o x "Š'NœNHHð:#D!Ö­[722’——çuFˆ(Bdff¢®qäȯ3BD"66uÎÎN¯3BD‘£§§#..ÎëŒQ„¨©©ؼy³×!¢±cÇŽÁÁA6RˆÈ©Ù³gKIIñ:/D!P×@#¥²²ÒëŒQ„¸~ý:¢F¿×!¢QXXˆê)6ßC!"úJllìèè(ª|kžˆœ’¯½ y"Š»ví’¨‘••åu^ˆ(¤¤¤È×/]ºäu^ˆ(Btww³‘BD8räˆ|)zÑ¢E^ç…ˆ"Azzúðð0Ç;w¼Î Eˆþþ~D‘‘‘ØØX¯óBD‘àðáÃãvìØáu^ˆ($''K#¥··×ë¼Q„èééAÔ@ìHMMõ:/D öíÛ'”ªª*¯óBD‘ !!Aº{¡ºáu^ˆ(BÜ¿_:n¬\¹Òë¼Q$(((qz:::¼Î EÕH™9s¦×y!¢Hpþüy¹'zîÜ9¯óBD‘ 55U:n Òáu^ˆ(BtuuIucõêÕ^煌Μ9ÓÑÑÑØØ˜””äu^ˆ¾TRR"QáÃë¼Ðñññ2b£@ìð:GDãÔ`¢hªÌž=ÛëìÐ}}}šææf¯sD4îæÍ›R(kkk½Î M°uëÖ‰***¼ÎQLLvvöÐÐßO†ê?ñMáBFÜ€²²2¯óBFÒ­F9zô¨×9"Š‰Ù¾}»”HŽ'žÔmÑÁÁÁk×®y¢qR.Q(óóó½Î YسgŽNKKËÒ¥K½Î Ѹ+W®ÈÕìÑ£G^ç…ˆ"AZZšôE#%==ÝëìQ$ï9­[·¼Î E‚+VÈ»ó|¼GDNI#*++½Î E‚ŠŠ ‰£££^ç…ˆ"&ªkÀ¦M›¼ÎE‚ºº:Õ Ñë¼Q$ÈÈÈ×R—,Yâuvˆ(@ž¼NÖÝÝÍ_D £···««ëäÉ“S³Æ5kÖÈ#Øááá9sæLÍJ‰(Äô¾›SЙB=‚½sçÎd¯‹ˆ&‘|Ÿu ì;uꔬá#!!aR×ED“«ªªJ½Õ>þüIZ ª3jX¾šMñÖ®]«Ž^½zu’ÖrïÞ=5~ÔŒ3&i-ѶlÙÒÚÚêu.È™eË–É KèïïíÂÓÒÒöìÙsçεŠãÇ¿ØÂÂÂàAæÌ™³mÛ¶’’¿) ¦ K!÷èÑ#)$7oÞ ùÂccc±[vìØØ4wîÜ/? a—Ù÷Î\°`t¬ݧá±ÌÚÚÚÑÑQµäv0ß°a{æÌ¿)—.]ÚÞÞŽ:Îð8Ôz‚_ûTZ±bE}}=ò¯z¾ èÛÏ’€-íêêrýÐ ¦´´4È›P¨T¶µµuwwoÞ¼ÙoâÌÌL½œ„ö[\¸n=~üX¢þź:;;C¸Š(1sæÌ¢¢¢ÊÊʺº:Ô p®bßÙ¤ÇÙ¥ªAžØqqq§N’‹êëëÃêèèPeÂ>'©{%ÕÕÕ¾Òäää d¨íR0%ø L*TöíÛ‡;6ÓœÿÇÛÏŽC ®\<ðFȸqã†Ä©¬¬¬ 6bÂpûgeeeêY›@Ä fÕÊÎ;/Ö¯_Ÿ””„8xîÜ9}-ØÒ¬%Ra§Ì›7—\šzzzpºê¥ %`ݺuöKXµjU𦦦¡׺ýû÷ëFA`R™q·p6Ç>·¨ˆ"æSN´´´Ÿ‡I‚œß¾}çíè8˨·hÑ"û… ®§fÄ™ã|í8ÁTDþš¯olS7ljj2ocðñCðEe;SŸ¨>Ü#¦o¥cÉ’%¨OžGKÄœ&99††C| þq………((Õæ?éá̦ºýÒÒÒ Sô]ãðùêlj–@ïi/_¾%%`åÊ•– p•’’Û®zùÖo‘¢Z>2ÝÚÚZTÐRÓƒÒçææŸ‡Io˜‚8h¨Ã?óÌ3ö ÁÞ–MÆ¿Îïë/+„âUµ44TÍ V¬X þ™øÑ–àk:‡¶´O¬î¶„¤ÃÕƒÔvÉÓ”õØ2Zá"¬¶Úülb×®]r8p™uRU4»víš^`Ž;d†QBÔÕbß¾}j"BüÌ™3 ‰õ[•ØwkÔŸ\¼xÑW2u‡>B[¬“«µµU/N^ ‘¾tNM¡ý©÷²2III*1.lƒ•… êíp´ŠQVœÜ¾‰túVë÷qp,¸å@¸Þ½héwÄBÒìWOgTåÇQåÁœX6A¸^£ª/ÛT—š››%ÁâÅ‹]¯(šÝÉCr'³ÔÕÕùM©÷çñ{T}5$½&BNŠ‹‹ƒ_¬/¨¸L„5Ê8i¨Ä¡¢Äã ÖÙÙÙÓÓ#÷ö°OFLp*655“=R«Jø¶mÛdáA>P/+ ´sƒYZÌx{DE"y±`íÚµ¾Ú ‹-’…]gÿŒÙ†úÂà·9Azz:ê§XEMM»UL zà0ßÐ6C•dÀYgJ½.ƒóÊ>ñîÝ»Uâàz¡oèYˆFPË´‘››+oj„Dww·ëœè]Ýd98 òöM[[[o áŒÒ¤ožô¡P4|ljjÕ®Áêû˜Ë0j:cccõõõîÚqÓâ}@ïÊ~G|ñûŠñ™3gô«½}b”H›§q.èÑP‚\ ‰¡äsMýÇêÕ«wìØ!™¢¢¢à7P¿„¤¥^Ý(--ÅIkóYOuiÁ,æ{ݽ{w`"yA ëEãK>pà@4mèÁ¼ÀƒC‹@ã·Ÿ(ª”*9鎡7›ƒï©©W^œ„­h¢:8H烡„k†Çö†÷}ÜÑ£~„&ªßAŒ»¡ßŶ œ °nÞ¼—.·1ÁÑ£G§æ*å1½Wœ={Ö&ñâÅ‹±»íÓÄŒ?®Ó/øçÏŸ·O¯fÉoÿh' wïBU&ŸáA)®¥!Üv}”ƒT7 wå±L›g«ªEƒ t=8¶¾FÃ+°!!O‚‚¯/G€ÚÚZýÈÙWhPÓó»L¹-ü¶}6n܈4ú,åßLïñ§OŸr$;;[]ÃC;ª>öÊ@(în\¿~]}(Gâ»M8Ðû㸮@¥¦¦ª¶Åü.LðÆÆÆ"tµ€éoÙ<œÛNnÚéÝÉpxì߈ONNƤ°°P ý‘tIIIzu#œ_‡9´ÕSÆP7£—ð;ž˜=ÔçïÝ»§–‰Ãdß‘m ]ßBcR Ún!n*2©øJväÈD'ÇÌPU¶O,55®G0ÅB¨>…aÿ:|h]ºt M‰G…êÜÖé#6_—ÁÙ…‚¡tm?”†~10ôFw ‡C¹ƒFÐ4zÿͰ7Ñl±L&œ:B‡ßQ§®]»&c½© dC{Ê-•ɺ6 !8¢†,·«ñCíüv˜¨¡¡Áõ¢¶mÛ60ÞuÞ¤RÏA‚é`~ìØ1½d†äåI;dòFùGË—/×;hY¾Ãƒ“ÿñãÇNžÿë‡&{a¶iÓ&éå…<¨Ã™——çz[b&V§CÛÂ[—/_V=âããõGÚÁJ"==Ç4''Ç0¬‘ë»×6l'\+W®TϾ1‹2©Öîú}¶¬¬¬ÉÓ׿`^´‹H'OžT{ÓrÀ%éùëð&¶^O¶¿ÏŠ‹!+£©{õAN=¡C‡‚YZøÃ>T Ša¨ÆŽŽ?Ùž}öYükxöázÉ2<¥üVõ\û«KÌÄ—ßܽšhxÑÑÉJC ÑïCÃ(ÔÙÙ©.PæçvK—.u^9D˜p^¼Pâå]C½û™ëæ«Ð’‹úašöìÙ£ÆÂRôwÁô=ùò±b~~¾áfó€Û§˜;wîTbÔÎ!D+L…Bw½{äù´*ç"T»¨©©¹råJHaÔLó ‚ú‘vÂÔmn!Þ«÷èTYG ¦Ç~§ E-øäÂÙýû÷-ïFéý,ÆõògΜ‰È.C`èãQ QT}V¯ú}UZmf tœ¡˜/‡D<|ø°þÜwÀ_g%‡§éIìe?šÄ­[·úD…þ剾†ÛCÖßàRá&È~†Ì‚YT8Ã)-õ5,¨»¼h u@\NÔpX†SÎÅH_8âj€8Ã{"ö— ´dUmÕÉ@75å¡/?¾e¸ªùýÄŒ_h@M£g(¨e ˜ºë-Y²E3Ð>³†âå+ éý”QU%Ãá(§–JKKõµ‡ä¥Œ0´råJ4| A®¿ô1àöq#Bƒº9>>~hâ'#üÖtræëýµôrâ·'! ¡J\YYІ $!XÈ\Ø(ÃëŽÁ¼2gÎì¢iý>‹|ÜPÝøÄ•DÆæ ô-RýÊ ,ïVTWWFýQÏn1Ýrä' ÝÛCxß+¬ É€òj3˜ˆoQ¸øÎ.N6¬bëÖ­jŠù cçœ0/Ú;z›Q¿ã÷ˆëPº?}Ù²eXµêÆ¢ß3®:Ü·o–ìëƒÓÂúõëqžBVVJVgîBª'ø=+|ѯKA>— O÷ïß·Ü{f†/ãØ|ÁàèÑ£¾ºH TN9›hMtttè·H½gÚ€ƒöŽatÂ-[¶øÝŠÙ³g£ Y>l2ô—Ç‚“nŠÈLðolG¶Ý»wË%ZïðdM½…¸sçÎPÁÉìëƒæa&]ЋW0ý Ã“ }âðk¬úŠ÷dAÕÀ&  N9ü×|_!C>Ž#nyçÂp{Òo®6nܨ¯Ôï@­5&_=P CU©Rg?À—<à÷õ)ÙiaýúõUUU("†‡RØ›!é`oè]£CóÇW¯SUu=Œ||X•ƒ(Aî[Žˆi Ýp’ÛßæØ¶mN6ýV¨%ýR;pÈP9wî\]]N-„ TpP»ôµ”9R(rN®R†QÔmžžJÅÌþ•óµMÊ ê>ú#E´qP³¸|ù2þ„ņ¤»GCE×Ðùo`üÁx¨ÞóÑ? ªû§åêÞ„ëŒ#ÿj»Nœ8án!áI>wPMÃð\r†£"`øº8.þ8°óQ;p؃ûäÉ“†KŽ:Ĉ 8u,DŽ”ÃniæheNƒj,®XȪ'~¸dÉs¥C c£_ÂoiYÿÁ‡(Èq£û IDAT*» »¯½½Ýï-®€ Eªß¡Ä*>|hóYî³ø½žØ>Ñ2G”u0ÇFI)tFËÓ[FÓ‡…%÷öö:Þ?.Ũ)9r 4 QE' ±|=е;¬>óÌ3†­èîîFåÁ¢  àÔ©Sø/6* ¾?¨wë¥Ô E¹u2Äÿô‚v`YY™ë!^íáÚ…’Q[[‹²e¸ÄYÒ{ô™{!ïwÉ1F„ räî°"_lw÷µA\{ÍwõK+N \{'#ÛN8)"))É×IŽ­èèèðõQK{¸\•——£Öƒf2Ê…Šÿ¢Í…Ýü·oiÒéÃWWH_P'G©òÕí:ÒÉ #×/\₌³§„|fAêámmm¸8GÖW/q’ïÛ·¯¹¹YŠqóæM4‹¢é"AÓ›*Ÿˆ… ¢ÿ‰ãð„Se ¯öâ≠Ñ9ø!9ˆÂ‹jôàsEë+œäR?Ÿ¾ÏÿˆüRÏbŽpüøqœW!G3ÜÈ7"¢ìF/Qˆ©ÎEN:€I§u›ž‘NõI‰¾nlD!öàÁ©tØ £TRR‚‹p¨†l [ê­à¿rDåbccÕ{. ê9híÏ™3çìÙ³Ò!ÇðÊfT’;>.†É!š¦víÚÕ××'ƒƒŒ=‡éöíÛÓäÍ"õ¾_H¾ÆJ4ÄÅÅ¡ŸššŠ£ì­¿ÔèðQ?2;…ŒÜ *G!"¢ÐSï_M“/HQ=zTÚ) DäT¨>dGDÓˆ>ÆŒ×y!¢¡^öëêêò:/D!ÔÀQ9D;M 8Xã "GôÏÐEñ'o‰(ÄTƒOUˆÈ)" ˜jª O·—tˆÈ%Õ‰cppcó‘#===*v„äÃzDýª««õï†x"Š›6mRQchhhîܹ^爈Â^bb¢þeߺº:¯sDD‘@ÿn#‚?VFDþ•––ê·9¦lH÷Y³f=yòdjÖED¡§c@¿§ëšH###/^œìÑd9yò¤^éÀ)’’2y«KKKC¨:~üøä­‚ˆ¦‚êBªž°LÒ7VŠŠŠ?~\RR2 '¢)µhÑ"µXo³„|-7nÜž3gNÈ—LDÞ8sæÌÀD%hV„dá«V­B¥¦©©)$K#¢0r÷î]Cì@aÿþýÁ,u™þþþÑÑÑÕ«W‡*ŸD^®\¹¢÷ìP\|Cwݺu2ø Qôà ¯? U·<ÐÖ8þ|NNŽýì¹¹¹×¯_W[[75Ù&"͘1£±±Qï®GLGdiii©©©¹téÒÙ³g/^¼ØÐÐ ¿—/_æDÓQZZÚ­[·¤g‡9‚˜ï¤"ÙƒV®\éuƉ( $''£~ÑÞÞÞ××'ÕŠñ±Ñ[[[«««KJJø•ˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆ,mß¾½´´tóæÍNgee•ŽÃë*((èêêú·û·—^zé…^¸páÂÌ™3],ÇÆìÙ³ûûûÿó?ÿó'?ùÉ¿üË¿lÙ²ÅáŒ{öìÁvíØ±ÃÅJ“’’***ž}öÙïÿû?üá±ÞšššÜÜ\çKµçåå9I¼nÝ:$Þ½{w\\\ Y]»v­óébcc±FÌ»aÆ@ç¥(ôöÛoøá‡Ÿ|ò‰“sæàÁƒ;pà@@kimmýüóϱ¢§a½o¾ùæ’%KÜf‚¯ýëX ¾|Ù4œ–~çýÛßþ†Äï¾ûn@kÌÉÉyã7>ýôÓ§&X6A$##ÃáÚ?ûì³””¿‰y%±‹°+G=Ðd^DÆ@ç¥(ô‡?üA : bzzº}bŽÇ9X¦/äLÆ™†è£&~üñÇ?rCþñÿQ–†%ã<üë_ÿŠÅÊ”ý×õ;;Ò#å;ï¼ã|½½½zœR›¦Ö+p¾9\;¼ÿþû~#p %Vhà@•cygÍšм(!˜—ƒ¾ ‡}ûÄ´hpÚ¨ÊZ(¨è¢¸£¢àÞ¹sçƒ>Pkùå—]oEZZš\ö‘· ¨‰ÿðÿ€é“8¾÷½ï鯻»{Þ¼yÉÉÉØ4¬·¬¬ì;ßùŽ\¢ñW‡kÿñÿaŸØuàÐõóÏ?м 4^˜àõ×_·IhàPQµœW–iš››Õ%zhhÈÍ6ÄÄ|h“²ººúïÿ»Ãµ«ðÝØØh“Ø]à˜={¶Š­RÁt>o Hà@aúío+?ÚÛÛ}%(p¼öÚkr& ZaŸòÌ™3ÒrAÉÎÉÉ xbb¾ùÍoº8tΪQÒBAž9âz浿õÖ[rncù«W¯ö•Ø]àxî¹çdÉ­­­r\°ÛÏÎÀAHàSN*¸Š–””X&v8æÌ™#ç–éä†ZRš÷»ß¹ØŠú§’¨wâÄ ³Ç8~õ«_IVÇÆÆÜ­Ë×ÚÿûßHTÂ~ùj™Ø]àƒûÇ?þQýþóŸÿì|vš@K–,Qg»e¡t8^xá9»œßõ”ÒŒ ¤¦¦º‡’8κÂÂÂ@gq8âââäüqrË3е#pà÷Í›7e[þò—¿X&v8*++åÀII–€“ºäM ¸xñ¢”Z˥·:“““æä;ßùŽÄšªªª€6A¨g¢X©‹%8 ¨ÑȦ!2ºÈ¤ýÚ%pÀK/½$Û‚æÄ.ÇŸþô'=ØÍŸ?_ê5?úÑ.ƒ&0øö·¿-¥öµ×^3$v8pY–êJ§óœìرCÖ‹# MóæÍ“’-Qï'?ùÉŒ3œÏî0p<ÿüó²ü@{²8Y» 1_žêXQ]]!q {FÂt†åcÅÆÆ:YM`ðî»ïÊØÒÒ¢Ow8æÎ+%õ­·Þrž4”ä©ý“8‘ô>#8µœwst8äò™ŸŸï.“6k×GRR’œ¨Ø“+W®Ô8äJðñÇëýtŽ=*ûêöíÛNÂÀAXÕ’GiÛ´i“šî0pdffJàøŸÿùç9Y¸p¡<—ýÅ/~èVè~ðƒ¨ðl\½zÕÉ\Ço¼!ÃWòšššÿžHök7¤\½zµºQš˜˜¨¦8P¡ÚŸ9ˆËtl‹ß…Ä0peà€eË–™o”: ñññRÈJ±~ýz9Ûýö€ò«¼¼\õŒ@0rÒåÜaà§?Ø;wî´LÐØØøt"'‰-ܹsG¢÷( (p\¸pA–ÐÐаn¢ÿ÷—mY»v­ßå0pоGÌxç%ÃRç7G¥î€ ¦ó±ž}öY)âöÝŸBU_uKÅ9æ7½ÃÀqñâEYæÈȈe‚¦¦¦O¿|à€ÿøÇ²œç2% Àˆ#³#ˆ<‘ª—¡ýåw9 4Mà¹(Á+¯¼Hà@z™ñîÝ»s"g ôüùóÚrÓF;È>¥ÃÀ‘’’"ùtÞ{ï½àÈžÄV\¿~=&À‘——§öÀGVÔ«+¾úŒ( 4}àˆÑ.YˆǪU«¤ÒSÂI6T ßWçwªªªä´¹|ù²}JçÀ$`™¨‘9I|à@€P\–/_î;p8Ö®]+ËÄAÎmR†*pÀ¦M›$ci8u9Õí;ãÊýW¿ñƒ&ð8bÆ«R¼Ô-'C½U?ýéO}%Û¶m›JæúAì /¼ðæ›oš§ŽŽJ†ËÊÊì—ÐKn?øÁÔݓݻwûJÂÀˆÚO5~ÇÍ›7eÛQݰIÖÐÐàd1pÐNÔ×׫{iλ?¡ú F¬À*šššMÔ_ ö³Ÿ©9œ Eá‹ôpǺŽ9"=šðï­[·dáXµßÎ`¾V¯Þ*Æ*P¥BøÓWQPP Þ¾ Uà€ÿú¯ÿr8¾«ž×Úo» /¿ürºŽñAÌaà€ýèGŽ˜ñ …þÎ8Šûßþö7ü‹‰zg­ÿþïÿf+¾õ­o©Eá4Àæè+íîîö»ù êé›ðù8Ù4}˜"çù8éñ¡Öh8ŠŠŠ$ýýý~—)m„ÝÌÌL_iTà@>õáÿý¿ÿçw]%œœv¸Æ¥ø•W^±_…ç•Ã>Zöp56 ½%'Ã7¾ñ '³»1ã/×!ÿz˜Päûõ¯íëUcóÚŽÔÔTu£Ô&p m(‡ÕÉ»B ,]g3â‘ 6Þ§hpûöíææfC×r_ïß¿ô¨Šº¢¸¸¸;wîà*-W'4L¾ùÍoÚÜ paÞ¼y###o¿ý6Š8ÎÛçŸÞùˆÁ­­­Ø.w]HV¯^õâ´—MCôyî¹ç*++/AÖÞÐÐà$ñºuëøÁƒ6ÏPÝœæqmmm¾àð¡4ÛÂJ®ŽˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆˆÈ™ØØØŒŒŒ¥K—îÙ³çÊ•+÷îÝkkkëììì×ÝÝÝÕÕ…ÿ¶¶¶Þºu«¤¤$11Ñë,QØKJJZ±bÅ©S§P‡pfpp‰7lØàõFQøÉÌÌܳgOss3ª ¨a8¬^XV8zzzòòò¼Þ """ ÉÉÉ;wîliiA cxxØu%CAe¥»»{áÂ…^oy-77÷üù󃃃ÁÜÉ0C•åæÍ›III^oygñâÅ555¾:dÈ“”ÑÑQÔðÕ5SäOøWM7ÏÞÐÐïõV‘²²²._¾,Õýn„ÜÞèèè¸xñâæÍ›.\˜’’b¿¨äädÔZÊÊÊîܹ#7H¤þûûû ¦dƒˆˆˆ(lÄÇÇïß¿¿¯¯OÕ3P?èîîFõ¢¨¨(555øU$&&®^½úæÍ›ÙÙÙÁ/ˆˆˆ"Fnnn}}½ÜÀwãÆ T2ø¼ƒˆˆˆB`ãÆÃÃÃò¼£®®nñâÅ^gŠˆˆˆ¢Bllìž={úûû¥ƒç£GÖ¯_ïu¦ˆˆˆ(Š”••©[·nÝš?¾×9"""¢(²{÷nGUS§Nñ &DDDJëÖ­ëëëwèС¸¸8¯sDDDDQdéÒ¥2ƾ}ûbcc½ÎE‘¬¬¬¦¦&©jð®…Xrròµk×äûjÇçpDDDJ3fÌ8|ø° C~éÒ%~ˆˆˆBlÆ }}}£££wïÞÍÌÌô:;DDD],XÐÖÖ†ªÆ£Gòóó½ÎE—¤¤¤«W¯Ê‡àËË˽ÎEÝ»w£¶qûöí´´4¯³CDDDÑeÉ’%¨mô÷÷¯[·ÎëìQtIII¹~ýúȸ[·ná¿^爈ˆˆ¢ËÎ;Ç á·×Ù!""¢è’••uÿþ}ù ~Ì;×ëQ‰‹‹;yòäÈÈÈÀÀj—/_æÈ¡DDDJÝÝÝCCCRÛØ·oŸ×9"""¢(wöìY¹±188ˆ ½ÎE‘ìì쎎uc£©©)==ÝëLQ9pà*ãFGGO:ëu¦ˆˆˆ(ZÌš5ëÎ;RÛÇ(Ô‹ˆˆˆB©¤¤D*044ôàÁ¾ûJDDD!“põêUé øqþüù¸¸8¯óEDDDÑbáÂ…roCF-))ñ:SDDDEÊÊÊÔ¡¡¡ŽŽŽùóç{)"""ŠÉÉÉ úc”ëׯsQ""" ™¢¢¢þþ~ÕEttt´¢¢ÂëLQ©¬¬T76¤Î±zõj¯3EDDDÑ"--M ³!6ÚÚÚ²²²¼ÎE‹åË—÷ôô¨Ç(###555III^狈ˆˆ¢BllìÁƒÕcù<Ê¡C‡¼ÎE‹¤¤¤›7oê6€6ˆˆˆ(dòòòºººä£¯Òi£½½6ˆˆˆ(d6oÞ<<<¬wÚ¨¯¯OIIñ:_DDD-ôw_e¤ªª*~…ˆˆˆB#%%åæÍ›êÝWé"ºwï^¯óEDDDÑ";;ûáǪӆ(..ö:_DDD-Ö®]«Xލ‚x/"""І‘6nß¾=sæL¯óEfÏžªù¼yóÒÒҼΠQ¸Š¿víš^ÛÀï .°‹(‘äääÇ÷õõŽŽÊû\CCCø166V^^îuÂÌœ9sZZZôN¨mpQ"{úKã:L¿ÿþ¬Y³¼Î#QØX¾|9ÚgzÐDmcëÖ­^ç‹(|%''ß¾}[Ë'Q]]]ll¬×9%" ¥¥¥zÐDµ£¿¿¿°°Ðë|…¯¤¤$Ô6ôçR}_´h‘×9%" gÏžUQrhhˆ/¤ùµgÏýÄ1×6z{{—-[æu6‰ˆÂ@bb¢y\¯¦¦¦ôôt¯³FÖbccÏ;çëö¦ß¸q#55Õël…Ë.¢ÕÕÕ ^g(¬ZµJ^EQ·4FGG»»»<˜œœìuÂÃâÅ‹õÚbå3Ï<ÃÞmDIKK›?~ff&ïg­_¿~pœþ$…£QÈ”••zº¡¶±e˯óEDDDÑâÌ™3zmCîs¬\¹Òë|QTHJJª­­Õ;Õ£ªÑÓÓÃAˆˆˆ(4fÍšuïÞ=Ãë¯mmm™™™^gˆˆˆ¢Â‚ ººº ]DïÞ½ËOYQh¬ZµJ ±¬Û¨­­MLLô:kDDDvìØ¡´!ƒm\ºtiÆŒ^gˆˆˆ"_lllee¥aÜeü÷™gžñ:kDDDÓÀÌ™3=Z^^^\\œŸŸ•ýâââ®\¹blµƒz5""¢édß¾}cccƒƒƒÃÃÃòùƒÒÒÒèÕ;99¹±±Q!Ež¤`½ÎÑôSVVf”é]»vy¯ dff¶··ë]DÆ?7¿yóf¯³FDD4-%$$455™ûTFn/‡¥K—öôôè[$5U«Vy5""¢il÷î݆ŽRçÀt¯³°7*OÐßß¿lÙ2¯³FD‘±ÔÔÔèxÄL.ŠŠŠ cTÈ;::ÒÓÓ½Î# iii†R°ÝÝÝ .ô:ƒDòòòjjjÐn×ÛÛ›ŸŸïu¦ˆ¢Å¢E‹ Cp*›6mò:wFIII¹¹¹[·n½råJ[[[OOôx5ߨÀ”öööyóæyå/Þ—¹páöð¡C‡Ðl á’±u6l¨¬¬¼zõêÍ›7¯_¿~êÔ©7Κ5+„k!_Ð^ºtiyyyCC*è}}}r¡Â±nmmÍÎÎrùóçÏG Çb¯]»¶xñâä9¢¡i‘™™¹`ÁŒŒŒ.911DZ¿¿ßIÐŒÁ9Âu…ÚZ+V¬8zôèíÛ·QQ`$úI9DáyôèÑåË—Y~È%\tqÖ-_¾|ÇŽ¸’¡<¹þ´úÂ… ;;;ͧ®â'NœmÎ]ÈÊÊ*))A›Cê8ù‘ÕAü×\Û@ÄŸ={¶×ÙIOO×?à‚üߺukîܹ®˜““ƒã‚½!»Â\M”µ`ŸìÛ·ƒ›… .u¸ü¯[·Uºû÷ï#Žc'KL÷up ~¥UUUrßN:t?|øpÊ>kŒÊ1Ö…º,ZùȪïÞ~uo`ü‹râã΂ »jÕ*PÃÍQá\³fM(¶ dÐÒ8|ø0ªˆü8.hl JzãÆ =ëy ¦£:…ìuÞ)œà ³º  x´\q¡EyBA‘‹(Š>êꢋø‚¬}c](渉={6TÛå\\\BÀ¥K—°i8d{‘=\¶QO?vìØÎ;qþçççã0g\mm­~ša· 1ZŸSŸyT ïܹc+ׯ_ï|9‹-:yòdww·M%à {õQ>„v'99yÓ¦Muuu¬Úórú8p È< Ùk¡¹O÷¤¾o…*þ3Ï<ƒ˜#笿рFyž¼õÚÃiŽÊ–9L¡9rÄÅQéG]ʦʨö6N¢0©¸#è!Ö=~ü¡a0>>Þœ­¬––˲Š-Eë4%%eêsN^ZºtiYYÙéÓ§¥NŠÂÓÅhlœºÄÚœø+"Bqqqð™)))ÁJ-Ï4\ä‚_¾C+V¬ÀùíÂ~Àª›šš¯—,Yâ÷zyþüy½u‚ßõõõ¸ZLI®í TWW[¶œ°ûöí³ŸÁ}×®]mmm^êÆ«\]]]ì*(kœ•r>ÊÇ„QÍ=~ü8®jG•æ¾ý¹)…5fTƒÌÏþýû͵U\PlîÙ³QÅÜDÆÔ›=¼PaO¢ïëTÚ±cG@KÛ¶m.Ø8¾~O+¬ñĉžWÙÑ•1 áÎïSWó{RxPᤦ—™3gVUUáz€«QyyùÚµksrrÌuU42|ÝåC¹AÔ I_HœKgΜ1¯HþMvo)\SwïÞÝÞÞ.çROOêaõ?0Œ\Žß¨Ã…¶Ÿ„;رˆ–GqÝòÕfBæ+**¾|,"ÏbG&²”H€6YðW»iòººº½{÷.X°ÀW {÷îÙyœü 6\ åKF(777È%›egg£ík¹QÒÄ÷ö¢{ìØ1Ë+h@‚ùóç×ÔÔ žÈ†°ƒ»jÕ*ü¶¬>J þyMvîÜ)O‘K‡[Ѝh¹»œ´sh:BÁBà³|'¡€nÈÛÀ%u^ó)‡"ŽZÑ$ET±8 N$ùp«‹v‡Òw‘4+Ãáæ'örâ«¶á«í»|ùò¦¦&ixኅ¨QTT„Ý‚ª¤Ç¦!JâeeemÞ¼μ L9|øðoïtsêÔ)ûGþAö¶ÆQ¾xñ¢á)áÀ¤uâ...0½§¦¶%øž(AÚ²e‹eMÈyÞJJJº»»ÇÆÆ._¾¬wŸ:xð åqÄž¿uë–‡wt’““Ñà”çæÈ! ƒóÛ¨_¿~Ýò6®)–OahºËË˳ìÈ)§Ùµk×BUÀu˲Çåd¼ßzôh¨2 ynYSÔ¿ä‚M@ƒU^q I·_šJ¸V¡[Ö)qÚž>}:ЊoQQ*©ú•ÕGÔM'õ•ÔŒ›šš|=´íîî¶éÅ25Ð1ïgéZ¶¯¹¶lÙ"Ý2ÊÊÊlº1™{ÈI7ü){åX‡ZÂåË— 1 @û¢a™¦È3ÜpèÖFá(//Ϧ?Z/®g†ˆ#7rC5Ä *Ôúø_6Ý‚y­@Cgué ’ ©°°pÀêA8ŽfOO¼V€ˆpìØ±±±±sçÎ…Ã[»äÂÂ… -_T‘ÁywyÎÏϯ¯¯Wï£Iïà«W¯NÁ}oT&|õ‡‰øÓdôK *ågÏž5´»7œJ–åqf ,ÓgCo‚|¿pá´ZDDĤ%¯[·N_roo/F-ïTŇ?z°5&??T5Û‘¸?ÌMü èt½«Vw ølK¶;.Y²DÝÐ%*Ûüþì°°0}æ Ñì&Sa:¨éœ(÷Êö”oÙ²Å(ìîøñã ˜QIf3 F‰?€y$æÐ³_£o:‰ëD3hjjRt†œÓ¹ìãƒ~rss‚ÃÄïúC-,,Ô¬@àGÜvÍ¢`lll{{ûóÏ?___ï⎒õë×ë'B¶¤íq¨¨<8ø)))©»»[6>88èω؉‘žž~âÄ £ Ô’è MPªU9˜Ñ…ôiû𛃚|}PRR¢ƒ:vì˜ígËÁÁh^¾:|jèAÐs¼6!…G³råÊ¥K—>ñĘ£'&&Ξ=Ã7dñ´ Pæ ‘‘‘˜›ÂébÔWvœú8c ºƒ>®\ÖoÜŠÏÉÉQ™£"pååå|QKÜ@¿Ü§n‘ècæßïÆÇÇÃÿI—–è0óÙ½-Z„殟ö¡3˜Ü7wÕÜܬOtüøqTĤÙ& 2Ê~¨ø!°fÍ{íô ‹{öì™4ã­RMÍé6ô£à¼p‡—C»*((ðý=ÐÑ'—v®É§áµ{÷nõhkkC7±q#ÕÚµkÚªÿ¼š„lu¨6‰ yzòäIÜpÈS×÷ ã/õyq0ÒnÞ¼Ù[51ªT=#c»+çÔ„„„@É*8ìÇ}ÀgüÆzÉ#„¶¼Ü5víÚeòsçÎ…«–ænIôrDDÄnuéíí5©  „Ð95“; S÷ ðÇFkôáäéÑÐå$‘îîîÿ–À÷ ,Y²ÄÏ]RR¢ŸàA?ýôÓbt>‹Â@Û®®®6³áÙ* ‡œ ,õõõþ./++ ][óªO³ö¨¨(YJ”¼vn­I„††ê»*Ê)..ö^]²téR£áíZdÖ‘‘‘¨/æ–ÖÛ¶mÃÄfppp||üüùó˜"B¢ 0Œ˜bΜ9F¡ãÒC<Û߯““£¨\ȼϖHI½©’ðßÌB%†?‡ ×e òø\\«p²¥pôá|…»Þ§aaaÐÐú7†h è˜Ðî²´svâTÕuëÖùÃë Á(µ×èÃiŒí l`€>JÖ6Ö®]Û3ñâìØ±ÃÝ RLL žš~ÕÄ«§ß9¤¨¨Èáh)iÉZ²hÕ…ñcªßÞÞŽZ¯\¹299Ùv…–/_1d{ƒ!€>àpP}˜•ÖLd“²LV#M¶Èp£ù=†]“ÑÑ©©©NŽu°=o·“ÃeÇÁ9õ€ç3jðrØMgg'æþv„“]o²¥Üö¶š••e”UY4þ…ñ`-6--mhhH]²]É6ôÁªÊÀþ¨ì%Y¼xñðð0já<Á ydعs§QÚeII‰gÅb¦%o=äM­ù‰—f‡‹‚ä³óàH…iÓ¦mß¾]zæÉ“'õã£ï#¼4$&&‰!¹'9996šG¼Dxx¸>£tLÌA†‹ÚzSMMsµaï›”èèh‡wU=¤h’óºNvvöèÿ΋#i|Ÿ—¢´´ÔáÀŽŠ{oÃ÷̘1£©©i||üÈ‘#¶;ëq2o–Ý•n¡ª-/²R %%Ť‘FGËJç9|ø°™i(•¾¾>I݃٤æ˜Ü™®®.¯æ^œ”'žxBóŽY]ýcÇŽ±7NUÎP%G–?„hè.ß³g“ L[[›Ú=88x×®]cccèÔ===ú> [ZZ<^ÓŤHŸÚËÂ$F®³fÍýÞ½Q/ŸŽi!«V­ò“Suˆ•À™ÍE<‹¦†o~æ™gð]øHK^XJ£SBž~úi3³%Ù•UQXX§YbÉeo>"8> ÉCÌc§pýýýð‹U&ZìöíÛí6Óè‰ÏH$ÆÈÆ|eeeçÏŸÇhñâÅû÷ï×w+ÜXüÇåÃ5ê#ÍmYÛXºt©ÃA¿ÄôÒ³9¤ÏX¸p!ô´ì=ö‡˜bb%F^£ç º˜——'›þËËË-i+ ,0J‚«ÔÕÕy\2&ˆ=Ç,µ··Ïœ93,,L¿ÒƒKäææš¯…ÇlݺÕèŒ+3o»ˆáXTT$C-t$dnFF†þXQñaæ·”[ŽVìd·6:—]›¼pceóº3ôœ~W¹ÄX`öåñ% îk“³|IJJÊÐÐјéχ(aÊwäÈ‘ñññÆÆFß§D#^göìÙFïSàçÜ:p# ¦˜@TVVZ5ç6Ú2ú0ÊÝãµÙÍ›7ËŒè‰3f´¶¶ê“óØ(*;øÔ†Nx#»l#‚ŽöVSS£$E€_Ôô5úðEÈ]¡As´¡¾ÃnذÁ÷VåççËëHIKã0~4y2¥>-›tRßëBç34Œrþ¹a>""ÏåÂ… Ь®ä‰'$ðèF§3`níâżyóž}öYHªª* [³æÈyÇ¥¦¦Š0ª¯¯W„Q]]f¼ôÏv-èaX4J$Š¡dppÐöäcÄ$hZòÞ®Z…¥¤®Ñ÷M[lV“””ÔÑÑ166&öÊË˨ ô&«ÎFpÌ%äPeâ¢W0NÎÌI¿±Vžš×fΜ¹oß>£1S¬Z¹r¥/Mr…ððð¦¦&4¤ÆÆFÿÙÑM¬ýÿÀOgp1tÎûرc²eÔÂHÌíœD¹c¼ÀHç²ÁWЦѲ»»»Õaú¸<×ðy'iËaŒ÷‡S'ˆÇddd:t’wÏž=N¶VaÒìð5<Ú€½;’Ð/Ða?š¢l“1Êô ø¹çž{ΗQ“p«ÃÃÃ###jÿ:þ|ý&Xô23Ñ0©vìØ¡L(Ö’³ \c¯Ñ›qõS@õm?“AMDD¤¥ÆcÁ²eËFuÇš£Cºrf2ýZ»v­µÏhyíííFa’2¿÷@ ¬Y³Fò0jöƒ-Z´H¿W_²d‰uurŒ˜o|™\ø%6‚F  ½ÛÓÓ3iëÂ\¼­­Mß ¬:gÀ3dÝâĉÙÙÙòK( çyâá·lÙâó0£(//—ì‚êÐ+ýQì‚ùãÕÕÕú<5·Ôº &'eeer24Æ#å'»oü$Åœ9sZ[[ÇÇÇ›››ùå±@F4u'” –Î}yxxø¶mÛÐÇ 6”ÇBBCC;;;ìu?á„\‚Æ­ DBe•ã]Ôã#êhiµ\ÅHlÉºŽ½»s‰`ˆ‡Æ•-â[·nu1Ö¨:tÈ÷û’Ðð0 EÇGEÔ £`‰“éÁèC•ìm§+ bN6n”ÈÄ̆°qãF½ˆÁo B|°éR Ê–%<'ïS`Xee¥·­š”ÌÌÌÞÞ^<©††Û˜ß¡n’VÏÉ–Œ’蜘ÖH/¥B/mjjr²0ƒÝ’’ÞRcppP+Ž ɯ™‚¯Ø‘‡îg”¶FîÛ·Û_-æÎ{àÀ±±±¡¡!ws ìÚµËaG}àÞ±×1èû0Èáq€N}TXºt©—lƒ˜+..†¸„¾¬ƒ‘w=“m˜Ì|%ÙëWF>ìííUUU’d¶¶¶V^.ãV`ˆp¢üð˜ÌÍmÆæŠŠ < ‘Gþ¸J¼ Ü(ïÙfDff&æQhE0Ïö³¨ˆ  sØ¢ÙÒÒ"k3NNܨ¬¬tÒgOM`íé!˜o ú(¶;vÌ(«‡dÒw(ù™d缚Ž?uNNNŽœÚŠ‰¢Ã ©²“ñͲ©©ÉgÛî š%òòhÿþýöæN$¶ÛÙىᬼ¼\팡7oÞÜÝÝ&"oX\W—4Ð œ¿ –ó·­ÝrèÐ!'Ù¾á¿õKÖ¸:îŒïƒ¥cbbdµI_k<&ØI&E‚à¤í¡™ ê\¿~½ÁI‚UfëIKK“ÌK œçÔ5Îû,|¤ £O¹!࢛6m2Y¸ÇÒ¥Ka°,串$“——7ªÛr¨mmm–žCA"÷ôôȺ2tFQQÃÛ_àÕêêê$bsÂPÚ"<[b2331¬8é-2;QË Ù³g+K2 ïâ)qõõõúXQLL}+ŠYšÑ‘•°Ç­4¯Ä÷$$$tuu¡!áa¡ïXøò~ãÆF+hêCCCæ´€nnnÆ|ý Mέ%wˆ çÊF'·oßîz±pT¹¹¹²UMv{îÛ·ÏÅe‰ÐÐP‡ò]vx™_¼LJJÒgšW:¬ùô¸K( Úºu«ë6'&&:áPnÅáÇÍo­ÇP™••…ùªd!Ãï“O>ÉhÐÇ<òèèh ãPÇòrĨýÉi®ëÖ­³·¡¤¥¥¹Ò[$ÈYJ‰ŠÚµk—ë¯~Ð3õy¸NÞU{ £¨‘]Y@ˆ+ ÉË;i±’jÓBÖ¬Yãdë‡_lr£)ôzUU•¬Õïܹӳ7§’}ßù"ÇèÃ`´vè$õŽéÓ§CsC·¡µWVVÊ`_‹1].//w×* búƒp'-‰]ضm›Qea¶Ç룑‘‘eee²užÛ•…‡@ØMºcHx·²áÆÇǯX±-_î*nDjMM»çm‘GžÀÀÀ†††‹/J;˜ôeþ Ý=ÄOòÀ8 Ë7ª‚,z0Õƒ ×tK\œÓwb’ñì³ÏêO{‚ðòýIÖÄEäpsñ:hEuuuÞèANŽÃP³~ýz·Öäà`V­ZÕÙÙ)Kßp9Nòœº¼£Z~MÚm•9ÃY耸±Ë–-3¹§³,uïv=iò¤`Œmoo7èÈn=Ž””<ÑX¸3õõõ&=74óÃóô6ƒãÇã[P<+W®\2üBqq1îÛž={ð\¤)â®J44þ uþüù ÎxÜÉÉÉ1z1!S|hii±6nÜ*0zJD˜ó":ÃÌòµþØ ±®*“ƒù¥ŒÑx^Šæ×ÌvåS'“"G JH5†]¯êBç iÔMW^èìÝ»·²²rݺukÖ¬Y1Aaa!¼¼8|†8Œ±±±“'ObšŽæç ›.\ˆË¡ ËJ¤¬´kÁ144ÔÚÚZQQa¹ß’lÉ3¬]¶ÔŸï¨0‹ËÊÊ2úúŒ30pmß¾ÕÇX'g,”””Xjš——7222é«.WñV^7—––: Ã')¹ÕÕÕ²,)sŽC‡¡­Ø• Ù-¢££¡©eTRqêÈ‘#›6m2_‹   }h½ÉÐ6·ˆˆˆ€oǸ,ãñññēڽ{7ðOÐ2áK¤ÙÀ_ú •$œQ8wÜOcc£]çÂûܺ§Ÿ~#†åS5WÞ)Ñ' ²6…ñX!2¼½žš€™”2Šê÷ ËoÄ<åÏðãÑ£Gkjj Yyf!¦ÖïjCOóAÌDTT”ä"ƒ0™wˆøˆQq32@»›3Ôcdçȉ'à¥&}[¡È ø 4°öööââbKÞ˜5³gÏnhh%Å‘ë]¸2ßÞ¿QQ‘—²3»Ÿ ,^¼xÙ²ePø€!CSSSñ¿|?BˆõLŸ>}ïÞ½ú“¹€œ# Bä%hEE…]GÞw •7_£-Øâ¡’ÓÓÓKKK›››;;;{{{ñ/¤3suu5´p||<›–™3gÎüùóqósrr࿳²²à¿Ñßm9âw<õÔSš Qx/åÜÅB¶§KRWîM„HLL„_W6Cõõõ1¼†Bˆ` ¢ÉL,'>Xx 9SêäÉ“²”RVVæ'»ˆ‹`Â:ú0þíïïgÄ!„·Ñ¤#”¤¿–$nÂ$¸¹¹YÞï:Y'~‹ú$y ÿøD\B±’ÀÀ@MY3÷xYQQÕrþüyø'|ööÕÄK,_¾\âƒFyj·Q„BerssGFFÔ;VÎ;×ÐÐà¢ìˆŠŠZ¹råÁƒå¤"|½¦¦ÆKˆoÈÈÈPg²AÛxöÙgÍç&„BþŸéèèPïp“]mýýý•••6lÀ·¢¢¢®®®­­mhhH¶ÔË×½{÷æçç3(} ©>‰Cö7Ùm!„©EHHHVVViiiCC„EWWÜω'1Í=xðàž={6oÞ¼|ùò””žQ4Å ÂóÕ¼eƒµå,CB!„LM #ÕjC‚F/^l·]„B™*„‡‡=zT+*Ùô™ižB!–‘ŸŸ¯?ÏoÛ¶mvÛE!„©ÂôéÓžçÇón!„bqqqCCCšÓÑ ?RRRì6B!S…üü|ÍÉÝ’y6>>ÞnÓ!„2Uغu«æ0?ŽÁÁAÕF!„˨©©Ñ Ž B!„XC``àîÝ»5[Tä•JBB‚ÝÖB!dªÐÐРYá]*©©©v›F!„©B]]CÁQXXh·i„B™*TUU9 v›F!„©Âš5kNŸ>­øMoooll¬ÝÖB!dJ™™9<<¬IüÎ;dzé !„b ¡¡¡½½½zÁ!›c###í6B!S‚ŠŠ }‡DrÔÖÖÚm !„B}RSS‡††ô‘òbeõêÕvH!„)Aee¥ÃE!77×n !„òèÚÖÖ¦9¤^Ï‘——g·Ö•““f·!„BÈc¼o__ŸÃ+òn¥²²rÊÄs@`577Ÿ={õª¨¨°ÛB!äq"99Ù(˜ct"9Ç‘#G¢££í6Ó»ví‚Ô8sæLmmmHHˆÝB!±±±ú]²ê¥ŽªªªiÓ¦Ùm©ÛÄÅŵµµ?µ(--}«@!„L$žCs„¬&¤ãôéÓëÖ­³ÛR—†©ÃÃÃãããG]¸p¡ÝB!ä!EEENö­ÈY*˜>}ºÝÆ: ,,lãÆCCCccc'Ož,)) ²Û(B!„è˜9sfKK‹“¥°ÿþääd»í  LOOß½{÷ÈÈÈùóçO:U[[;{öl»í"„BÈd$%%uvvN*;àÝÏœ9ÓÖÖ–••å˵\+--­®®î¹çž;;>lÚ´iÖ¬Y>³B!Ö²eËåMʤkÃÃÃûöí+((ˆŽŽ¶6 ðïñãÇ»ººêêêV¯^½páÂyóæÅÄÄ„‡‡C"iDIpp0þ ––VXXX[[{øða”€‹Jiøwddš&77—"ƒB™²„……uvvž™àÔ®HEˆœ>}Z„$àR#y2ðR,>à7ø²£¢¢"))‰›Z !„ÇŽÀÀÀØØØ¼¼¼­[·¶¶¶ŠŒ-¢èµ"9¥âôòÇòEüØÝÝÝÔÔ´aÆÌÌL¦ç"„Bˆ3‚ƒƒãââ.\˜“““ŸŸ¿zõêÂÂÂU«V@ ,^¼8==}îܹ‘‘‘S&o:!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!dj2k|°Ûå111óæÍ›;wnTT”ÝæxÀÀÀ°°°ÈÈȈˆˆéÓ§Ûm!„s¤¥¥ýñüË_þòàÁƒ?ÿùÏøÐØØhUá›6múöÛoQ¬Rø† ¬*܈ ´´´¼óÎ;ß|ó ®ø÷¿ÿýÿøÇßUÈýë_ñW¯^mkkËÌÌô¶U…Q__ÿÑGÁT±üoûÛÿ™P ü‹»ŠšîܹZÊ’‹=zôŸÿüçƒ p÷þô§?AßXR² ¢Ðê^ýõ;wîà¢R;õSÃgX…QwTù믿~íµ×š››sss-4ãÈ‘#JÝq¡÷ß?!!ÁªÂßzë-TD GúӟΜ9ÓªÂHII¹yó&Ú.Š»766æéüãÿX©,>üüç?÷öE !jÁ¡Œ†Ÿ|òIjjªùÂ})8ªªª>ûì38 ¸Šn‚_¼qãÜž?L¦ccc1FÃ$¹u“‚?ûþûïß|óMó—ö¥àÀ#ûôÓO={dʃEò«_ýjñâÅ&íQ NúðáÖTÖ÷‚#,,ì¿ø®¥®NGG‡W/@ÁA1B/8Œ¼& ÷à€o~å•W0’jª€ËÉ’ ¸~ýúÕ«W/]ºôòË/¿ûî»_|ñÅ7ß|ƒÿÂh<:~DQo¿ývrr²µvºNMM,]¨+1töìÙ–––†††œÿüsyÁä=Á!à÷Ï?ÿüŒ3<.ÜÇ‚£½½]¹œ¦ƒÜ¹s'''Ç{—¦à „8Æ¡àPÿˆ¡óQÏGï Ž7Þ¿_m'>ã7{÷î r·4¸í={öÜ»wOÞv+C3Êܱc‡%»¼šâ1R_¸pÁÅ/Â>ù䓿 ðžàزeËŸ'Pî0´ʯ­­ÅtÜLɉ‰‰0£Œ½àP[‹»òÔSOyV¸/GYY™ú-úàÝ»wÕ·ý³Ï>³0¨ÉÜê1°Êƒ\;z(8!ŽñXp˜É}ñÅê¡%¼ð ˜ùx'æ»êE\¸•Ä9111ðˆêa@‡ó0™ Ó@ó½ú꫊æÃ†)--5S¦7òpôôô¨" ~÷ÝwMº[4oÞ¼¹¸¸Ø™F1»Uk{÷îÝ7š/“i¥^6À yýõ×}“îsÆŒééén}Esö Ìßzë-ÌñêWàÊð /^__o­® ©eïøøãŸþy¸äÎÎÎîîn|þì³ÏðgšÃrïÝ»·bÅ “6xUpP¥3‹à‘‰Øúè£Ð„Nœ8°wïÞýû÷êsçÎ]¹r嫯¾ÂSÃ=ÑoÒñ+Á! ¦úÌfGPPЋ/¾¨ô/ÉF“””dÆÎ€‰4jñŠû‰{îYQúfŒV„ú¾å&PQÕÕÕ&ëEñ#,BNNÎ74©½!8LÑ^zé%ñÐ<¦â»o¾ùæ¢E‹¼a¡+666B$¹U Ônòµ×^›5k–y| 8„äädL£'}d9¨Gd >˜?.ØrÁ0¿288¨QWfGWW—z›1þ}òÉ'M)455ik¬¬¬ô àðçéË! ,ÀÌæ‡~øŸ þóŸÿ´¶¶ZR2ÜçáÇ¿ûî;)ùûï¿ÇUÊËË-)ÜˆØØØúúú«W¯Þ¹s稣Y•aZ¼F´{÷î]»v Óh ÷¹˜'11±½½]RÂHý2 Ü¡Lô¯_¿ÞÖÖfaÎõÞÞÞÿþ÷¿òÈð¼p-ó³gçDGGoß¾ý7Þ€¸A¤¾w¬j¨5þàÛo¿…œ½téfÀ¦šíééQ×mà RRR~ó›ßüë_ÿ’Âÿýï{¶O§¶¶VéªÒ§,\Š›6mÚÈÈlSînøêÕ«Ý-‚ãòåËIþÇx€VÕŽB¼KHHH|||ZZZvvv~~~AAANNNzzzBB‚/F1ILLÌüùó333óòò–-[–‘‘€ªÙm—·˜3g<4žYnnîÂ… qâââ¬Ê†I!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„B!„¿äÿe,‹iñÙªIEND®B`‚xine-0.9.4/data/mkNoSignal.sh0000755000000000000000000000107111006327345014477 0ustar rootroot#!/bin/bash for inFile in noSignal4x3*.png ; do outFile="$(basename "$inFile" .png).mpg" cat "$inFile" \ | pngtopnm \ | ppmtoy4m -F 25:1 -A 4:3 -I p -r -S 420mpeg2 -v 2 -n 1 \ | mpeg2enc -f 8 -a 2 -q 1 -n p -I 0 -T 120 -R 2 -g 10 -G 12 -o /dev/stdout \ | cat >"$outFile" done for inFile in noSignal16x9*.png ; do outFile="$(basename "$inFile" .png).mpg" cat "$inFile" \ | pngtopnm \ | ppmtoy4m -F 25:1 -A 16:9 -I p -r -S 420mpeg2 -v 2 -n 1 \ | mpeg2enc -f 8 -a 3 -q 1 -n p -I 0 -T 120 -R 2 -g 10 -G 12 -o /dev/stdout \ | cat >"$outFile" done xine-0.9.4/data/noSignal16x9.png0000666000000000000000000006550510776142165015036 0ustar rootroot‰PNG  IHDRÐ@\Tö pHYsÃÃÇo¨d tEXtComment‰* IDATxœìÝ÷{Ç}øqôB$@€ Ñ€I`o"Á ‚$Ø{  QX6Ô‹“Ø–"GO,çq;‰íÈŠâÈŽòXqù>¶$ÛR,ÛâõýM–Ûn÷ öîð~ý€ç°7;÷¹½ÝÙ™ÝÙ™´4€P(t‰!ô¥ Il…ÿ355t@¢»wï^Ð! 1q6vWBÂ`×xÅØØX sKî{þüùæ–››ÃÜÜ?&bÃv/X¼»FuuµiIEEźuë¢Ì¶´´4lš‡š–äååEù¹‘0^ÎÚ¾}»éÝÞÞ^SbÓ*N*++ucbÓŠ—.]R ³²²ôBõ«¶WZãÖÁ™b’;;;ýfë´Äv{¹¬¾@¬aÙ†ÒÞÞÞÝÝA¶NKäßÖÖVyqúôiSš¹¹9ïc4¿?—iÛ;w®¸¸Ø¸D}gS¶£££Á”š èøñãÆ³³³£Ùããã¶Ÿbý×¥d«W¯öþ¡1P[[›f(D¥§§ë×¾¶ÅªU«fffŒKvîÜiÌ¡©©ÉKyÑÑÑáýCcC"hii1$Ÿ={fJæ1þ¾>SúžžžÝ»ww/Çc0 hk@ÓÓÓ¦åûöíó˜ÛóçÏMÙæççËßmÛ¶©%“““ivGŠÚÝËò¸»|ù²iÉìì¬ze@òµ½TOLivìØ‘ •à~¨ý&±"_ïÈ‘#ÞÓ?}ú4n±H]Ož< :„Db{f‘úèÂG’dsìß¿?è(‰l‘XUä¼êåË—Ö%âÎ;zIzzºNöÎ;ï˜Ršò1fäM¶Èô÷÷ÿݶm›zñþûïë…Æo¨7‡i;êM£—¯_¿Þº­ÝÈȈ1h§/`Ý;Þ|óMÛÆÆûõÉA6Gša¯þðÃÕ‹ììlSʺºº4×½#Írøˆššš8Ejs¼ñÆz‰iŸ×¯Õf½9ž?¾sçÎÓ¤9l&Q0/>_€çÏŸÓì©ç bûx>Jêÿ$ýpˆΩpÀ®ì¯Ø»woÐ!$ ÓóøQJú-¶_@=㟬V¬Xt‰$é÷툩.‘ø_a/òeÕªUSš–,]º4âÏ\qqñ‘#G\†áyñâ…1½ÇqÃäÝË—/ë4Öüõ¿W¯^5-ÏÈÈvsIIIDß):úêži»ÇþI³lw999[·nM³ÛÄÆ—-[¦b0¥1 ´p\hF†RU—m±qãF—l7lØàý³bÉe µcÇŽéd²ûzêB2Ñ#o™²5ý«ŸÊV Ï;gL¶Ðµa§(M¿ß2Õ”>33Ó¸DÊNõ¯q‹Gð)±g»9ÚÚÚ¢RÎ}u]4—ËÞ'Ū¯O‰ ]†ß¿__vì-,÷Í¡ÿÕÃ:uJ%K–øýÐhIUUUÆoß¾­F,,,4. gõBý¯:èÊÞÁƒÕ‹C‡¥ÍïÁ,aI!j}¤Ç–i‡ ûÝêëë%ÍÀÀ€q­»wïF&’—Ÿ×HÊø“|’à°_HlŽW°9^Áæx…¯öî¢`ÝA®_¿H$ ¡¡¡Ao‘ÜÜÜä{²H\zà#ÓH?Nƒ`‰ .êå}ô‘)s/N%Ù¦lG42ô¬7‡‰õöâgŸ}¦_'ÍæH›ŸKD‡›““£^¿À_|¡ok«ÍñÞ{ïYs³ Îé­D¤6‡qŸ÷ÝwÕ §ñ®ÔæÐ3Ë9}Ïßþö·¶Cð%4kÙ!/–,YòÇ?þј¬µµÕ¶ìHsÞjÖå €Ô3332:$Ù:::ô¿Ïž=ËÈÈ0$JØ(**R£~¯H¾I¿±0Œ#ˆÿgjj*è8›À†i`Fàåää°gÀe±gÀ^Âîê>NÐQ,b »õ_{í5‰Q#[Ÿ›g°!{†AGÄ377ÇeÐÅEÍ6YgggÂV53ªÖ¦ÿ­¨¨pùÕ{{{Ãöà:yò¤ñÝ;wîDßã+==ÝösÇÇÇò–Ôol?kÍ.[¡¦¦Æv¹B^?~üX/”¶S>1¿á匦§ÊT-,S]D§½:‰£Z²råJãÓ^h¬Š*Ò´Iý=C4a‹MÅx´9­òôéÓØ³¹¹¹Æ¡Ù!õööªM?vš!fS0ê‰qÉèè¨,‘ó¦^Ñø®”£:Ÿééiù«§…#Û= æ3nÊo&ÍKÛ·äÀ5Ϊ‹";Ç™3gŒÿÆéSâ‘-’žìúܼ‚bŽØ9àHvކ†† £@BŠ÷x$1ÝiÃKâ7Æ;$µÔÖÖÚ¾ÛØØØÞÞ¾À!!±„,ŒWY`áÔÖÖš†¨nll´MÙÕÕöÑûœœœï|ç;¦Q=ª««‹ŠŠ¬‰7mÚôÆolٲŴÜ€mHeee¶18Åß>üð×/_uù×”F–ÈO®^/_¾Üš@ùôÓOe?°.çw.\¸`ÊÐ˜Éøø¸Ë»¶!Ù&sIŒHÈÎ122"G°Þ¦¦ûÅ_ܸqô–ípüøqÛå¦ãÚµkúÓŸ\BR™ÈßÇ;}œ,ÉÏÏÿøãOŸ>í%6DBíióS è_Ř@þ-,,4­åòüá0½kÚ9¾õ­o}÷»ßU¯ËËË]ʉ_üâòó[?NN^²¤qží~ãüÑ;‡b[V[¼¿ù›¿±fuïÞ=õâ‡?üá­[·ôrÛÓŠÇwïÞí~–‘š‡—s"aÚ9Ò\OðN§yeãÆ¶i¬;‡xóÍ7uâŸÿüçî—|ûÛß¶ÖQŒË{ Í®]»Ô,*---AÇ‚„‘ŸŸoœb§««+èˆdW0Í¿tDH ÏŸ?—½aÉ’%êßÂÂBvüáááM›6φ Ö­[tHH===A‡€„DÅö®\¹tHTUUUA‡€„Ä övíÚtHT/^¼:$¤™™™ C@¢š›› :$$ê¡°—žžN±{°—ÍÎ{ì°G±G²g§_þÅìÉžQ__tHH°7;;{ùòå £°'{íÅ‹ƒŽbKØbC=säÈ‘ Y¬†††ö,ÏM,‘·¾ÄVRRt‹Õ¶mÛyç@dÏ(++ : $ž¬¬,Š Ø“=£»»;è((6?ùÓ§OÙ9ùÉkjj¼$ËÎÎ^€x@äWŸššrOsïÞ=—bCÏs™™™ñÈÖK¸jÕªx|tB“†èÈȈñJ‘ûUE=\ÓÕ«W]ÒØ®Mœííí*“Ç»|–ÒÐÐàôHUyy¹mx.­®¤-¢‹­ò=Ÿ‹®ØH³Û”akò.ghUñ²­CÕÙ®¢ëž!ç>îjœS—Ï•w—.]jüWJ §”ê…Ô¬Ÿ>}ö¥Õ¨;wîœ^râÄ ãàÀ°ûŠ÷w£$eFØüÆ'GÂáÇKlSªF\gg§ËÀ©²õ‰Ùö-ÛŸßxÍÔ选ä›$i†¹yyyêÝ5kÖè4W®\‰¸GÿU;Ç… LŸkŠáÆê_)üõ91déžb Øt1Tœ8qB/YµjÕÄÄDTß'Y¨/ïÞ'ô%½D,—Cÿ±ŠÐ´D5Dõr)LÏØ© zÊ¥K—Üs3f%G‚mEµÏãQÑNP]]]a˾¾¾¶¶6ã’Ð|ÿkÊ·>…ÞI[@J,ÛÒª««½gX\\ì’•í*ú]Û !0“-e*êÓæoØÆãƒœÞjll\×’ŽõÆý† œz[E¬²²2õ/>¦$ãαk×.kq“ˆSbÄ×–-[ÜOÕÑ[,ÍEø%õ vØS]ÕƒŽ ‰ç+á(®µ$7v8b瀣ééivØëîîfç€=ug5Å»ü#b¾ª•••q ‰Eí^n¯$ì˜ÉˆÝ Í=™ñáw,"aïíQi]Ôôþa èöíÛ¶½Ñ°¸¨q>L‚ `±ill »$m~Rà¯ýë»vírÏmjjj||ÜK†ió# [§Í^müwåÊ•eeeÖd¶Ù:}"ñòåË;wš–˜Òüþ÷¿ià’•òÇ?þÑ=Ãc†_ýêW]Òík_3ÍGì”­ZÛu5ëïmú÷âÅ‹Æ%.û‡^þÓŸþÔ%CµÄ8¾uç0®b»s|ðÁ’Æ:$&;G,ÉÖüä“O~ñ‹_—˜èùlø].ÿ¾þúëî!?ÔvçûöíûôÓO­ËÙ9bFýr¦²ÁšÀ´¤µµÕ6«>úÈé#\2´¦ÏÍÍÕÉœv§ØØ9bFmß#GŽè ö·üÞ÷¾÷öÛo[³’_ÅZáð’¡mzùûÆo¤ÙíÓÓÓõWe›;G,÷ 5§pØßò?ÿó?MµSú¿þë¿vÉÁZƒyï½÷œBJ³Û9dyCCƒ4LäôôÃþÐô;GÌXO(^N++V¬pÊ0//Ï=Ó¿N;Çì쬼¶Ý9œZOì±dܸüñ‡~hÝÜßúÖ·ô¿YYYÏ NÿþÃ?üƒqIww·ÓΡ^ÿíßþ­qç3à¯ýk—ÏbçˆëƵ-*Þzë-y±iÓ&ym{ÕAg566æ¥ìùüóÏÕëÏ>ûÌeçPåñå_ãüßùÎwŒ³-É»………ólg„¦_Nš!Öß²¸¸X—á¦ú„)+Å4%½mIc­Òxi_äçç;í£*iøøùâÅ‹ÖìÒ¥Kòïìì¬T4fffŒ{¿ßüc¢´´T¿.,,ô>E‡í0®³²Ý°.¤ŒÖëšf‘’¨¯¯Ï˜¹Kg«¸¸XÖ*++3.ÔõC îôi\?—dú°4VÒŽPÉlo—·®\¹â7HuBSTU¥µµÕv?xð`‚ì¬ú°Ë9\u~óR¨&XØŸÉÖ‹/Ôº6lÐ ¥ì~úô©z­ /—ö”KTÖ¶­´PT†Ç÷-’ÀÞ½{õÎ-ͧd¹¹¹*Ôi]r“}ZҌپ«õ’Èor*ÖAJH;÷ïß÷›É““°©°3Rm9¤½d¥ ÜÈNàRX¨/_¾¬–¨KEÆ4*Áôô´÷lËËËÕZÖ>8zo9sæŒßh‘Œ{äÒ¥K’Éê~blllT™tttØ&ؼy³JÁu¡pVV–ßqíÚ5¶T+ŒËÕ6÷^öIMo+Vø C­¨;©S˜šR^ø-’Nœ8¡V±v푽H½•§/!§4jÄ]­µV9HóÁ6Áš5kT‚–––h"|òä‰ßÕméª{hþ’ÍàààÍ›7¥B$‡zWW×ÀÀÀÇŸ?z•ßjŽ^QB™™™êßúúzï™èñÐÄk¯½æï{¦¥Iy¡\ù\ù¥L ԈБ•‡6½%?,àŽ’‰ñšÙÈȈS2÷wõ%’={öØ&Ø´i“JA#Eï÷âØ±c~WwR[[[ï_aa¡÷¨ªªRaKí®®N^Hñä7N©[é¯ï÷š¥8tèZWÝɲÆàÀ• ¸¸Øcž[¶lÑ!õ÷÷K9«NSSSRëô!’’n>ˆõë×Û¦‘·öíÛ甃^}×®]¶ ÔäòŠm÷Ꚉ{x K×ÂüV1Œ Õß—6ä¨6áÖúþúîÝ»=æ©/^D‰4’[”™ jBV—ý ä:^÷ܹs¶ îܹ£/×K+ÀWlÍÍÍ:ÿ¤›hZU1¢<Àtϱÿ‡Có3K¨u·ÞMiB>Û_:ž(ï•D_î Hz?°Ž #ÕZ÷9Môº¦+šä)ç4}ñÕW`º“˜Ø¿¿¯uƒÕÑÑqéÒ%ÝN‰àÖ©ÒÖÖ¦·@OO÷¥À•òZWÖ¶mÛf›,‚ríüùóÑ—†7nd8åäfìe`zKöT÷Úµºíê²ÍÍÍegg744DVeЙ߼yÓ׊zòä‰îtÛÛÛ«âwºÇäBÖ2^Úðu”ÎÌÌÈÏZSSã^/PG„÷gRt<.¼ÜMOOgffF¶.…î¢cì°¸råJ§„öèÑ#½™º~§}y=O½Viüí¥{š%Ë4ngee¥q‰Þ>¾^Þ¼y³lÛ´/ïM(ª[gX­­­ª›†.¬¦ÎÔD=vÃËÈÈÍ«C:räˆ×¯ô%9s0pŠ0ͤŠ!ÿ†=!¬[·Nï@ÖáСCºÍ,§;•L,Þ£’ÊNôõá#Í1ùʦ…EEEú+H›ËcVR\ªþþ¦Žêa;­È/¢õdËÖS¥€÷Í+ÅÖÕ«WÓ ÷ÎB>ëjkxO„vîÜ9]‹V—ßëê꼬hܧM÷_eISS“)¥¯ê†4Èuæ~oÁ,°£G:}59!ëoá¥Ô®]»Œ}mõÓh¡p—HT§;½¡Œ7¡œVÑSÂ^Ý”ê±ÆgüÝ=>ðªžu¹8’hô8#A,26l#ÐïMœD J F²à•êM›Dõ#ÁSÛh§ðAßR :ÉC÷Â:Ƀ‚€oºà`tL^éu¸ À+=X­>è‚#‚YQ,RûöíÓeÇÅ‹c•­µˆK'@ÊÑeÇøøxôŽÙÆ ¥gEUUU‘å399yýúõØÆ q­\¹òÕ!¯B'Ožô²¢´JÔt‰?X€¸(//Ùyöì™T%ZZZ6oÞ¼cÇŽ+W®LOO«·\†5°èHa¼ü¡Hy!¥†i~& Éýüç?ùò¥üuIórž—ÜþéŸþé¥ÅîÝ»#lçΦ|z{{¿ûî»’ÀË$L---ÖÅôô´ËZ*Maa¡S‚¯ýë’ÀãL.¿ûÝï$qii©—Äê£ÓÓÓ½$ȇ~¨§4^ Ž_üâ*Ù§Ÿ~zðàÁ’’’êêê7ß|Sg¾qãFïQMNNÊ*øÃŠ‹‹åߺººÿþïÿ–Ò¿óÎ;’þÂ… .yJù¥ƒyûí·%Ï¥K—ÖÔÔLMM©…_ùÊWœÖÕ+:tÈ6Á×¾ö5y÷ñãÇa¿Zkk«Êê‹/¾›8‚‰Iï½÷žÚAm': [p¨¿úÕ¯lßmjjR :;;=F奨2 [pLLL¨<ËÊÊœÒdffºÇóÇ?þQþJåšÀ{Á¡²zÿý÷åo__ŸÇôH,ªà)((Pû¨´LiÜãï}ï{.¥†²råJ•I^^ž—¨~ö³Ÿù*;Ü ©Y¨Ü222ùäSÇßýÝßI²¡¡¡4Ï…#‘.8Ô¿j7™™1¦qßÅջ˗/wÿ u1EÚ ûÍo~£rþío6±{Áño|CÞ•4?ÚʸdãX7ˆ—‚C6‘qÅŽŽy-M>n\t˜FSS“*ž={¶qãFk‚––y·§§gácˆÖ¬Y£Jêêj—d999 €ÄURR"EÆóçσ@’„”A yÌÌÌLNN€äqçÎî’ðAÝ|¥S¤Ôhjj : ÉcttôöíÛAG ylÞ¼yvv6è($ôôti¤äåå€äñèÑ£#GŽ€äÑÜÜL¯ þH#%;;;è($ÁÁÁóçÏ€äQQQA'QþÌÍÍ544€äqñâÅ©©© £<²³³¥‘²bÅŠ <&&&¤€ RÝXµjUÐHRjðX 8 ÇÚµkƒ@ò`þœ={–¡zø#¥ÆÜÜ\ÐQH·oß–‚cÓ¦MA I¨©R¸ºÀ‡žž)5¶nÝt ’DVVÕ þ J©±wïÞ $rrr¨nðghhHJööö $ÔãóT7øÐßß/¥Æõë׃@’ÈÌÌTÕôôô c$Tß{÷î€$¡fu999AÇ I\»vMJ'Ož€ä¡ªåååA I´µµ1> TucóæÍA Ilß¾N_üQ¥Æ±cÇ‚$™TWW÷ööRGÃ"UWWGuï7†¾t,@¦¦¦dïïéé :d2:`Á•””¨½?///èX’Æž={(8°¨=~üXvýgÏžH2yþü¹.5ÆÆÆ‚XXzÀžÕ«WK21V7V¬Xt8ÀºsçÓ¦D@—< ˆÅHíý:$ÓÝÝ-Û­££#è@€wøða®íðG•wïÞ :IbÍš5Ü…àÏä䤔ò7è@$‰‚‚UÝX»vmбHjs.‹ðA•§OŸ:IâÔ©SL€ÀUj<|ø0è@$‰ 6¨‚cåÊ•AÇ Ö233gffb~bzzšË¢@*S#^¼x« —.]ªJ#GŽÄ*O‰%//Oç{÷îI†ÃÃÃT7€Ô§Žó˜Ìu’‘‘¡r»ÿ~ô¹H\1œîüùó*«ÒÒÒèsC IÕR~ £@ ‘º†:Ú£™–177W;*ŸÆìIÒî¥óœÞÍÊÊZÈ`\,_¾\ý4Ñä#?“ì$eee± I ¸¸Øvù‹/Ô^ÕÜÜA¶MMM:åàÁƒ~3éèèpoÝܾ}[e~éÒ¥‚Œ-9~Ξ=«¿¯SQQQá·ò%¿‘4÷|³zõê¡¡!—ƒY?4ÍÖ[¶l™q T—oT™™¹víÚsçÎIµb``À6¾{ºoß>_™¯_¿>d'‚8ÕŠgΜ±¾¥?£ÖÖÖ>"&êêêÔÀËF………¶‰‹ŠŠT‚üü|/™ËA+Ïýæ·Äf uÆ ¾2W®]»&±­X±bëÖ­:+Ùd…Ä%§‚;vÈ Ü¸ÇÈéÂ)½´,TšÃ‡{ü9+>{öLV‘f³>lT½#‚Ë¢z®&ëÞßÓÓc-˜©tHm_o()mW©¯¯W víÚåž¹¾¨A±«W´6C¤À2…A“ðáÇmmmú_]NE*ÔÈȈ>´.\¸`Üc\*–:Çy7mÚ$‰%ãB½ë—””ø {ttT­+;¥^(G£¾øbâ7ÿèI#.4_#ËÍ͕ք1cÌ&]]]*S•DÓ¹É/è+0—©Þä7m·þþ~_™‹ññq©®š–¨ÜbØ÷“Z½q¬-/;·1YØý[\¹rERÖÖÖš–«±E#Ê\ /ŽH©¡#WSÀiÛ¶m‹à#¢´téRã¿úà± FË‚-[¶èܤuã+0]æ>xðÀúé’›¾Ïr®9‘È'&&L õ•Ž«W¯úÊ ÉAÎz9{ö¬S2_•ä{÷îI2©qXßRµƒ†2×SØë¤:²\p‘Ãc @wrÒβM#•>ëPC¥é0LwÄB_Výôuk¿Ã¾ªkvv¶S´Ñ܃CâjjjÒ{•ËLH+W®ô¸×ªKw¶'X}.‚8u{D_‘×Ë–-³¦”]öúõë|D̯Ù^5ƒJ½[YYéžUUU•Ϊ¾¾ÞWeeejESEO~J]%Ô™[‹j°Ø¡¡!ë[ÞO3HJÆê´Ëi¨4î·*:::$´lßU3€ØîgîJKKuª9rn1IÝdÉ’%~?"Œ5뻺®çåö¶ºÆÙ¡¨~qèÐ!cl[·nU¯uÉâëˆn'Z‡˜®©©Qo]»vÍo´H!—dº‘ìr½]] NÇ­z·¢¢Âo>Tëª"Iþuêl’Pôëõ ÝiÂK©¡ï× Ó5H/¬¿ï™3gŒ·ÆnݺÁOóäÉ“Ãlµ²êFŠóXp„=#IW¥éííµMpàÀÈv&}fëÖ­;yòd²L.«Ã6=¸bÅ µÜcw‰ÁÁA•ßþTº„ÒD®]»ÖTð²˜ìÙ³Çé$¡oÓx¼û†ä#¿ºÞi\n›é]Át×À(ìàêݾ¾>¿A^¿~]­;33#‡SÁ”€ô¶5] j¡÷á‹t>\Õ_UËN"yúô©1¾ÎÒÒÒâ=[µŠm›ôÑ£G!FuKmºë‘èêêrJ¦ž.‘ª©S‚Ó§O«Lœ*ÒRSP "¸Æ®#Tí|¿«ÅØS/ܱc‡Zⱓ¨8tèΧ±±ÑoºÌÕÿšDЬÐ#Å®_¿Þô–ú‚L‘“âô%ÏkPuOÞ©Ë–±ß¡Súžˆßu•X‰ÉsSrjÕÊõàÁƒ»wï^š'­}©Ñ OLLû>ª”¾šH:r]àªãM2÷­±ª¯Ó íuÿ[Žgëe•àæÍ›Þ³uŠGuíõÛ9 ÉGÚºz'¨©©±M£Ú)ãããN™¨yØBÎcüèzM­ c¯ÐXÍ,ŠT{{»÷OѽàU‰¬º‡zï­¯Ha­?=‚¡u+/mþB‰TL ô3xÞïÂêb÷ÆzazzºÚ—ü>Ç„¤¤«©"77×6ÍÅ‹CÎUkããLNŸ¢Ï™a{+˜ï&¸äï—T[êý«®®öõ)RCQagffª»BUUU~CÕ³U ¿Ã¦.iËQm[ÓQ ¤ è7ÏÐüŽÎÎÎ{÷îIíLþ½sçN’zߌݴҘÎ-Öw§´ôèùRCÑëêNÉB…-…¦4ô¤‰ìérýõ}ÛŠ¾144d»ñ7oÞ¬xïQ¶jÕ*’T©¤# Èú†Z;¿#i„=Ÿ«‹ N«ëk¢îåŽ2::qx1¯p!o!ÛötðbçÎ:¿Ï¹Ÿ$9 Ì£ëDÞ³ÕUÔ[·nùŠÇ¤¢¢Âïµ$½c9]¹V°õêN#•K3GÝœ ùŸSÚx sãÆ¾Ö \mm­ŠÜý¹AwúÀöul+“““º…h{íI_7õÕ£LÇÁÃͦ|"hy!QèýÀ¶k°T(\º‡Ÿ§´M B3^Ÿ‹,¶Ö œîê*-ÿÈrX¶l™þúRøúZ÷СCz$4}#ÖD?Ëï½ ¥{Gù‹HÊïEb$ã®i­ «Ça]V×ë:ÕZ¶mÛ¦Ò8=½âD?ìò0¼M¢ ͇är¶÷Âx}Ç×yyyRÝÐMmŸQNûòçóU!zúô©Z+‚ .šÔtüî H,Æý¬Ï)=xðÀ¥Û‚VC±}2Eö9×éÙXüŽâÍ3]R]ZŒ×"žÝ2â Whþy"÷uõ¸>Ö´^B’*ƒ¯L™øï‰E·N»×öíÛ]y¸l)'=iëáÅœîõÚÒ½ãht nÅŠzk¸”ÈaÉ*ÕÕÕ:_ƒI Eªj ²se-‚É7õ=á÷δ&íÖ¦¦¦ÈÖE¢Ð¼š.pÈAîÔ0VŒcÚØrBSϤèës¾S=G’«ºQ__¯{ˆF32¨Tå:::Ô˜&Š÷>k×®íîîN W.è”\:õ™èšc§MšŸòÕ"X‰EÕ999Æå¡pcÕÛ¶ûÚeõ£q~lÓ™Gp7rÄŸé2Žâ+uH§½ÚNihhð²®üˆª¾£‹u§:£~ˆÞû4’s4=ßÓ¾6‚‘pÔN`ê‡óøñc÷q÷M#b[ôôôHK'Íp?Õ×£YÆZºTþ½¯”õë×›NÝ»wïŽì ÍßæÔÓq+o[êâ^ßÄ5Œ)}ÅvõêUã´È ŽÏ±Å tonc#¼¿¿?ìÕ2}{5d÷ðHEE…~2R_„÷˜qD2_+bõêÕÖ'AåTÁW¸}ûöÉ“'åEkk«ß£T UÈê;¦Nw.ô¦‡ë¬Y³F²2ög‹àw‘ê˜äãw-$"ÕÜ8Kè£GÜ/ˆ*ÆÈZ‹6–D*¯ ªñv@€Ó)y¤Fܳ.×÷b½Ï ÐÔÔ¤ïq‹N/³ÏȧèþøzE§»°ú.¯—Í«šNRåÑýÙ"(8älÁãyHPÒ$1ž*å”åå‚‚ñ~‡uÑ»ˆ~¬ÓKa¤9s&Yªêž«Ó%_gu••uu±ÿ~÷uå#ô6— KØ­§¸ŒÉdL¬*2ûö틬àvkC7!q…¾ì2(•mymÖqŽÓÔÒÒb¼qxôèQ•Æéi}§¨”ÄÓ!ä<4žþ·jèÕÜÆ-ì>ˆäo¼O¡×r"Ø8GTبä\¢oïÅz/8#˜¯‰KŽêêêji¨§¶½÷hÖ™C¯v:Tó¶ObÌo¢Ç° ÅhÀžø‘ó¼ËØ"ú[xiª¦Xû½Ô ‰ñÍS„´vïÞ­FjÒ"RÁXãЬãY뎆—/_ö˜³ñù¿Q-¤‡Ë½Æãô|ÚÐÐÓóì¦NbÏž=’j'J­ÁvâX5€£û=,¡íXêÖ‰|–õ-c_XE>ÎÔ Rµ Ï©ÿ’é¢f(Ò±!×8\vAuÉCìܹÓcÎzLðX ,ê‘Ó°ÉŒ,C–ËÃê·pßþÆ!µ©©)—+Íêr•K7÷¾ííí!ׇPt¯#)§¦§§õ÷åaù¤{H]Ô½o¨ ãØ¹!ç …“Îû £•¡ˆž [ª4ôÒMÛøD¼-/cí­_¿^MÝ(G¦ÔÿÃ>e«ævOcŒ¡§§GªYYYj胰wŽ%±Ë7Šr@0$.9áHS%úgå„)ÕT©h¸Üøxíµ×Ôþ䥳@Úü`ëÒt—¾žX`³ó¼¤\¾|¹Óæ2yMôÂÎÞ`ª iÇ%”]È8vh¾Á’Ȥdt!/Íݵk׺?Œ›Ô`îÖù‡œ¨K˜¦3¼÷I˜â$;;Û8lúãÇyÈ dË–-j× ÛîP—Q?¢ÂR_Ç×*éééR¨¯¯rxN`±Ð7}Ã^L‘4»wï^˜¨"¦†J3>× öôÝ;÷ûùSSSIqQMÍ>Ùè5|P×á\p’öÀÀÀB†ÝÁ$è@€E@ßX±}÷Å‹ÉòÐä•+Wä[$E¤5ÕóçÏ ÛÚÚdá… ‚ŠÊ/UüE<Ëߤ=bº+)K’è.ƒ~VÕc‡±‘““³fÍšææf_s$ÝU?è@${÷î…’pÊkA’"#äy /øªrûöí $ôëgΜ :IbÙ²eªà°·öô\ŠtâàÕÆUÁá4ç;˜éN¶Ãp€ ý¸ ƒ\ðjçΪà8uêTбHj¸@úqðA ür ^QYY© ŽŸ’@Yºt© èX$]p0¯tÁaSìMOO«‚C^ €$qéÒ%]éÈÎÎ:ÉÀ8Éö•+Wb›ùØØXl3(Œ#-Ç0Û;wî$Ë|“#\1ɳ©©)ñçÙ¹œœc¥#ú¹Ôĺ1‰ @âUƒEõFeZ õegg Žˆ=ß³g¥°ˆœ:uÊXvDpCdxx˜{ºÀ¢óðáCcÙ155UXXèeÅÍ›7KúGÅ;B‰H)¡Wõ÷÷»<ƲnݺÉÉII¶oß¾…Œ@bééé YÌÍÍŒŒ´µµíÚµKêGŽÑÕy‘™™tÔ‚¶víZkÙaõøñãòòò ƒH Ο?¯&—5’ªGKKKÐÑ`ill¬¯¯wIPZZ*iV­Zå1ÃíÛ·OMM}ýë_¿{÷nVVV4±;vì›ßüæ[o½ÕÒÒâž²qžÇl322nÞ¼ùñ¡PH^äææº§¯¬¬ ›¹¯$åòå˽¤,,,”Ä«W¯ö˜3°@^Î[¹r¥S‚±±1I Goج¶mÛöÒâûßÿ~dýêW¿2æóÙgŸ9CÙÙÙ*—lßyçkŸ|òIuuµÓ*ÿ÷/iÞ}÷]—l½ $)åÛyI,E§$þÿø/‰…£öøßÿþ÷N FFF$Á7¾ñ ÷|.]ºd= •?üá~£’ ‹5Ÿºº:ÛÄÞ Ž/¾øÂ)H—Õ¿ýío»æ§àxóÍ7½'nkk“”ï¿ÿ¾—ÄÀÂч͵k×lx)8t>R7©©©)))9pàÀoû[µð£>Š ªÞÞ^y-ÇG_p|úé§*Ùï~÷»ööö+V”••I#èƒ>ðXpüñtØËWÓ…×k¯½61TØS®—‚Cç`­ð¿ýöÛê­›7oz IÊ—óÍã­[·:]DðRpòZ*Y¶ážÏ… $ÍO~òy]QQáe $(ãî{ïÞ=ëÞì^pŒË»R¡pÿÝZñÕÖ­[uÙqñâE÷Ä ް½œ [¿õ#Üóùå/)i>l\¥¸¸Øe $(Ó¯.XkõîÇ[o½%ïþË¿ü‹ß kppP—.)Ý c!2¦‚Cjd*ÃôôtÆËG˜Ò¨ÊËää¤Ë*HP¦½YZïjIee¥Zâ¥àøîw¿ëñƒŒ[XúªÆŽS²ˆ 9tÿà //Ï6SÁ!>ÿüó—¯^% [pìܹSüæ7¿ÑK6oÞüÒµoH–u7]%u/8^¼x!ïþ×ý—û§øênÔÙÙ©ËŽˆ»œ«w333MËÿßÿû/ òóómW·«V­R«¬Y³Æø._Duu—úEã—t¯9—'z(8 l÷xµPuÙr/8NŸ>í¥Dؽ{wÄíÝOdzzÚ6Ç‚ÃÚ9íã?ެàHû²¡ñÅ_?Âå[¼tÖÕÕå´”íßß߯—»º+¤½.Ÿòƒüà¥ç'»¬¤)ôÒ¹gGØ‚Cu6Ÿ››sJAÁ‘öêUR÷tÇ [¿ûÝïœV¤à@‚rÚãÕ#ßûÞ÷îß¿ïRpèþìÏþ,ì§twwGäòåË]ŽÌ°Çðð°{‚È ©‘élÝóW½6Þ~ûíV ÷)8 œv\ÝÿÚ×¾æ^püùŸÿ¹JéÔV׫ØvÖ¶:räˆi‰{ѶàнÔffflDVp¤}y•TÕ§\P¥pyy¹õ-5zÀñãÇmW¤à@‚rÙãô£éê´KÁ¡o[|öÙgÖwu×/9ü<†tðàAS·î-[¶DSp©:©4¶ÝÞ#.8LmÛÕ—,YâòîÕ«Wå­?üÐö] $(—}Z—î‡èêêÒ)·nݪæåå½ñÆî•-õÛüã´ùèªwÉøCÛô^ Žôôt‰Ô¡ŠŠŠÔòÒÒR]!Š àHûò*©Kª¡äÔá=33Óe]Upüä'?)°pŠX¾Jv }J·å2¾–ÕáÇm3©¨¨°Mïñ^o]]K„i†;&¶ïþ÷ÿ·¼µiÓ&§ÕÕ“oºÀ5R‡­>øÀýûqöSgû°Gšáb‡‰÷ñJ5©º›2q¹øê½“ˆ~JÍjppÐi­°GGG‡S^ú­ªaŠ~ðƒXßr)8""ˆ±y. ¤² NŸ>í%7i\¼þúëêúÓŸþÔô¼œ/'NœPÅǯ~õ«ÖÖV—”RÛû-Œ.^¼øþûï1ïûßÿ~د&é%óææf—4NÔ××ËrÓƒ-&ÒîpZ½±±qÌÁ­[·ÜÀdP^^¾oß¾K—.õööÏêëë»|ùòÚµkƒŽ$§‚‚‚ƒJÅ"äÁ³gÏŠ‹‹ƒ$‰ºººîîn/• *ÀŸššš¾¾>¿õ mëÖ­A¨rssÏœ93;;qUCìÚµ+èïREEÅýû÷£©g(R_ ú«€Ä³víÚ'OžX«G­ªªÊÌÌ4¦OOOÏÊÊ*--Ý¿¿©Ž233³bÅŠ ¾HDÏŸ?×Õ…ÉÉÉ“'OJMÂo>ååå·oßž››Û´iS<âIiÕªUãã㪞!…ööö¼¼¼ ƒ©"??¿§§GU5ž>}Ê ] Æ<¨ªµµµA‡RKiié£G¤ª1;;»cÇŽ Ã)çðáÃêÂÆÙ³gÓÓÓƒ¤–üüü¡¡!©j<~ü¸¬¬,èp@ÊY¿~½3ôСCAÇRÑŋՅ’’’ c)géÒ¥jŒS§N HE›7ož›››œœ\½zuб€T¤n£ôõõegg H9K—.£(ˆ—ÆÆÆÙÙÙ™™™úúú c©¨µµ5  H9™™™]]]RÛèééÉÊÊ :rŠ‹‹'&&¤¶ÑÑÑt, 544ÌÌÌHmãâÅ‹AÇRÑÞ½{ÕLl'Ož :Š.\¸ jçÎ/’h IDAT :rrrrúúúTmãêÕ«A‡RÎ’%KÔ¸^âöíÛéééAGR‹žŒMtuueddH-¥¥¥OŸ>Uµ»wïRÛ1¶jÕªÉÉIUÛèíí¥¶b¬¶¶V ¶¡&€ÍÌÌ :"ZÔ|lª¶ÑßßOmÄØúõëumcppyR@ŒmܸqnnNÕ6†††²³³ƒŽ¤–-[¶èÚÆððpnnnЀԲ}ûöЗ>|¸dÉ’ #©eÇŽº¶ñäÉ“eË–H-Û¶mÓµ/^”––H-[¶lѵ©©©ÊÊÊ #©eÓ¦Mº¶133S__tD µlذA?“"/ššš‚Ž¤–õë×ëچضm[ЀÔRWW§Çû÷ï:"ZV­Z555¥kíííAGRËòåËŸ?®kW¯^ :"Z ÇÇÇum£»»;###è @ ÉÍÍÕµááa&f±”••Õß߯kA…T–žžt€…•‘‘ÑÙÙ©kÏŸ?/++ :(¤¦;v<{öLïl³³³k×® :(À‚¸zõªñÀp¢ˆ‡¢¢¢±±±ÅíÛ·ƒ gΜ1–þ ð…xÚ†±?²Ñ•+W‚ŽgmmmÆ¢ÿðáÃAG„”žž~÷î]ÛÚÆÜÜWÔ ÅíÛ·ÏXô_¼x1舚֯_o[ÛÇ::@W0ŗ 0’ºø‚E *ùùùREؽ{÷µkפ9þøñcãc&BJÉ’’ïVUU™ºJˆÄ)þ+V\¹rE¢?>>.eî¹sçäC·mÛ¶yóæèH¤ØZ»vmœ‚Ѥ®622bÜR{[Ë‘µ¤±.­7Û‚Õô£,ª:Ǻuë¤ÒæT3‘}X*¾ò/--5e“ÞÄrÖÜ»w¯1gÙ9åŒ}ÎN^{í5ÓÖèïï÷¸«¸T5B UMO›?¢Õ}iꘞq“i LŽè•+W.@`¼’Ò¤±±QÚÍ7oÞ”ÓÕóçÏÃÜr$KEÄïIImÍ*æ)•ôà]Ò$=zô¨Si¸ÿþ¸FbKÚ‚Ö°iÓ&§ô[¶l1³îBš}qm%'© ·µµéÍ"õ0©ÃÝ¿__Šp"¿¸ßÏ’ã”I”©j?~Ü”§PRý&[wEEEÖÚªl1©Èº¯˜››+Å‚Ë&•È`àÿºº:õsËác{KHê‘Ö‚Kb‹ò®%€XZºt©Ó333ƒƒƒgÏž•ÂÔxÍÙô «Y71kÙ-Ÿ«ÎYYY­­­êŒ”MR|»ß«–’ÈxáýÊ•+1 ÃiZEšÖûÍr&¸víšKAoÕ××—8ÇUss³ì´Rç°^¨¬¬4]‡Ó†‡‡ý^û‘ö±©×Q”ÏUÉùÛt}Kiii‰&Û°Î;gýP÷ñ÷d_ºxñ¢$ëîî®®®¶öÛÍŸÑå·ˆkäòƒªzdgg§ËÏ———÷äÉSx Ó @lȹÐZÊHÕ$²Y7nܰž#ýÞH¶’Ö›¾ÞûìÙ³†††°«Hü:Œ…Dâ´–‰Ò,3]€‘ØÎŸ?¯;vìØÚµkËÊÊŠ‹‹KKK¥üÝ»w¯µ)ÿðáä9>ÞäÄ3::jÝi…ßkr²µ/Iå8Ê;n²«ë';ŒÚÛÛ£É6¬Õ«W[+aî5'©»‡æ«Âê‚™Dn- $ϸŽU#•3}\Ë>¼¼ÜT5;vìˆ_xbÉú¼¨ÙTiŒ>~ü8¶%‚œª¥£só~OÄØà“S¾¯ž(‘‘"ûîÝ»¦¯o^L]l—z†T,Ü3¬­­Õ=䃢*999Æ]B{úô©Tø¼ç³iÓ&ãµ(©žúímje{oâêÕ«Qf–µ»tGG‡SâÍ›7KMBU9…ë…ÍÍÍÖ]7®×6ŒÃþz)p¬=Tä'‹¾1`Ø–R‘ŠH¹555™²’r0â¡  ÀØXöÞ¾—ZŽ1ŒÆÆÆÈbðåòåËÖiìR JXùRÞÏ‹ÙÙÙªÓkÂÎM³ðœ*§Nò˜ƒT,Œ·^¼x“=äÂ… Ö¨äƒâýôæÎ;MêtkIê¸Ož<™ššZ¿~½é-ëý”0öHöÿ±±1ýAØU²²²L7ª¦§§ÃVÙ$Šššۇͺºº"Ëðúõ릡¢¢"²¬ÚÚÚŒY;vÌûºUUUÆ® óàÜÑ£G]NµµµRÊ÷õõù}€V² ­Òä7¶×äææ^½zU¯599éÒ™×ÙE­;€œÝ}]t‰@aa¡©Ç±í%RÿP½µlï\ìÚµËùÉ“'ã°ÔcŒôÚk¯yYË´yåhª®®ŽS„bïìÙ³Ö"2é£RÜ›ˆÝ¾}{dù˜ºAøº)³téRc_}i·-ÀEWÓåÆióÍñîîî‡rC$VÖ¬YcÝÚa{ï;HU#†÷ ¬WûCó·$¼t6Š’éºš|¨užTm;Y/[¶Ìôx‹TM¢:ÌVff¦~¸LñØ‘vÛ¶mƵ=z´ÔRGVV–mwÑÙÙÙÈ*1MûÞÚÚA&¦ ~‡Í0= Í xdNN*Öþz†TtÖ­['o>|8Þ1,*Ö§Že?qÙi¥)l¼€/§«ØÖm¯.À¥µ7š>ÔtDUßûúúœnGJÅ¢««Ë˜ÃððpØ'i# >Ó•—-MÓ3I+•!q´|ùrÛ±#Tª¢¢Â˜ÛÑ£GýæPPP %1©.TUUyÏAÚO¦>› Éà×Ê•+­RK ìúõëòƒÄ–üÊÖŠ²´Èm777Gn3kd“\Èqd;ªÍLÖ³dÉÓ­¥Ó§OëweCÉ(5!÷Þ?êYMêK—.y¨²•LQÔõ?wR±0v‹‘í¼0±Ä˜é*¥&Gx¹;Ÿúêo¡¬[·Ît;Fše¾F6ÌÈÈèìì4æ •xßL)**’º…íf Í?&8]è¥ÞS[[»úUÒô—pII‰Ôölk™rÒÊÍÍ•›|SIVVV&ùH-pµ3y7®ƒiÊ·0]N°Þ¹Í~èÐ!}ÙINTûöí‹Ço‘““cª%+ ð ¶éfŠ|¨îªº‘J…ý¿êæÍ›9H?âNW.d·1= çeð ùMuMNJï³+H8§N²=MnÙ²ÅoVÆÑ<?îwuÓmñàÁ_+¥%dº7<==½bÅ ¿‘ø"'0Û’”°ƒ ,©Ì9M;?çÏŸÓ×±v<2öq–º‘q,59ãÆ£½®™zI+RwGÑ4K]arrRÎëióƒð>zôHNÕaŸàê¦qÇz›õÑ•èɦ0ÝIÑ¡:‘Ú’®jȯó‹R”œ¡mç9{ñâ…ßÞXõõõº)é÷†œ³­ãœŽŽŽšæP›‰uˆ˜M[Üe"åiBõŸßµk—ÇYHbedd$NÓ[äçç›Î^²ï­^½:m¾³ÂÐÐZ(iâ=8fšeÔ|effÆ×}ÀÈÈ9ØtGêrÔ¨[Š/^ ›ƒ榫ñ¸ÿ˜““c¢­½½ÝØ #;;[*I¿qR$yï€" AÛñ¾:;;}åSZZ*uµ®ßK#R>ZG€–%¾¦—MŸf´ÁÁÁx_Ð6ud3Æï«¶_LÍzqæÌi—ëùæÍ›q½¤¡ÕÕÕÙvõøg4¬× ¤†¡.üLOO{™êÖZ¸téR}ê·9" ,ëàÃÃþj………Öñ¿¥É[[[ë+¿¤Im7´˜&: Šuà|Ej½^ŠÙñlûî,@GQ5 ‹íF¸~ýº—Ý/##ÃôXJœ†&³Þ*œœô2VJü¨.¨ 0`Ñiii±Xrõ~%\Ÿì{zzüžeËÊÊôuíÁƒ¾& ‘LlŸ0vÔkûR‰fLUxa; ww÷ Zoä4[ŠìÀñ¾¸ez¢D“Áû}Óãë²3Ç£“ï† ¬Mš…¬Z©Új\'ì`ö³›÷‰ ¤ˆW×'"x Eê4ÖÚ†œH|6jjj¦§§e­Ë—/ÇÀvgôÓa¸ÈÍ͵T[Úˆ\Ûˆ7Ó•âñãǾ.‰ÅÄ¥K—¬;€íp[1”““#µiX' —š®÷ë¦gÓ䨉GØÖÁOCó#ú5~FAAÁÈÈÈää$C¡ -;;ûáÇÖBÓcGKiKMMMy¼[l²bÅ ëe ¿s"¨ÆÓØØ˜T;LåZ\›PR\šž¼U†††þ´·¨lݺÕZI ù™<%V¬•%ì8§ÑP“HÅÂú„Nww·÷³xEE…évLœ†&³mÏÄã/Ôà{ýýýñÍ @rv·>'éqPN)úCóSZGpõض߆T|Mf¡zkJá+¯“b„âSÃvj®ÑÑQ¦Œ9C¨SìíÛ·¥vk½Ÿ×™Ó­¤ªmÛwÇvî’XQw@^{í5ë$ÞÇè“Ö8Üjh¾‡x<.9Xg ù¬Åz”,²¹ÄÀ¾}û¬%BØ&š”êb²¬Á‡ÊYÙö²ŠœQ¼g¢º¡©1L3ÓJY¬Œ“Ç[ƒô蓱ÅÃÒ¥KÕž²ÙÕ‰Ê:NÝóçÏ`¸ -++kppк¨ ãñ‰R‰W#àåææšòÞd—m(•6ãê’m<ºnX§eQ¾÷†D"‡çÄÄDüê‚Â3ÿ­Ên÷.+V¬ö¥Ô"{¨,##ÃvØû÷ï{ìú %¦ÌT ¨%Í5SõÅËØkhh°ö€›ššb¨€˜«ªªzðàÁ‹/L#K‡óRz{{ãÝKWvÎË—/«ÎN6ãÔmèСC’ù‰'Ô¿Æ•ʇ¯ëj'Ož4…í>Þydêëëm‡˜“¢cçZSâ™Ã˜íÔîë1I£:SjÖbHNáuuu^V—êÅðððÌÌŒNo&5®×´¥!h½§"{1“Ó•œ™¤Uj}àBÎèÖçŸ}]‹€ìlR§”öºÔÅ+**lo¦Ä㬦/lèé…MONNúªéªÛ FñèqÒÞÞn»}Bó“ ÄüãœÈÑ:66&{ËB^ý`ÏÚëÍ4½¤Qmm­œËïÝ»Íó~ÖÇñ“¶¨>ªÒ¤Ó%ˆµôàa¤ m½ Šhv:8Y·nT‚å$áÔ‹¨¡¡ÁúìÞ½;~!;w.4?²…º¡c{}.4ßvá@RòYªÏî¾m³_lذÁ{žR513;11Ûû€rŽ•öC__Ÿíè>ñ~R];räˆ|œ8 óqÜdffJ¡`, œ:dß¿_ÎôQ>5×ÜÜl[y,©Õ¥ÑÎÎNãÅsÓÝ蘠FRVZƒ¿yó&dÇ„ì]RÕ_Ðerù4‡Ösœ&UŒ“¬:Í>ÛS©ê Ýß߯ï•XGè­­­Þó”½Ô:dHl¢PÂFFF–,Yb[5-ȼBuuuÒ4zøð!=6€D!ź.:å…mÑSYY©F`6©8M¦ö Avv¶š!ÅÔ^Y¿~½©ô—JI”q:Ù±c‡5riÌñlôªªª?~,?å¶mÛÜSfeeYG¯Ÿ™™‰GÕD¾wïžñ'¶žõ¿·6œlذA²’ó¥±rŸ——gýÖ·nÝò•óL9ܹs'ú€9µ«Ç^Ôõ˜ÒÒRÛøDØŸ8%%%jC%Ît‰þ‡~>^QëU-[¶¨"£¯¯/&=Øm/„æ¯I¸ßa­¯¯Ÿšš’“Š©+-6ÓšGÅixG©œYoÛËYŽ¢Q’Ÿ~``@6æ¹sç¼ôú\½zµql7åÙ³g±DT~V©ÉmݺոÜú0ªvùòå(?Tvoõ¬¯iœeË–Ykêrv÷ÕQ´¼¼Ü:zL. IP•$r0ê³RzØn%!÷è?ÔJÊ(Õ&¹qã†÷ǃ,„uëÖ©¢Ó4•€Ô<Ξ=«Þ’27Vƒñ­\¹ÒzžPäLã´–œ¤Í_¤µV‰Ö¯_oÊ*S\¦9L–!›È×ís˜dffªîÃRãô>ˆ¾íX[’C ;O¨šqWW—õASÓ@àFÑŒdUWW§v0Ù ¦qBmÇ“š®û”î&R5·N¡|÷îÝèoª9r¥ždºÙêÔU+‡~µR!S½j¤á´ÀÛOJg)ņ‡‡õ¥…¼¼<©yè¥ø‹íœgN%µœ¶æ„”*‘j“9õ'5M>ùðáÃx º%…²í,—xðÀ¶è1§‘6ßÄÔC,ܹsÇå^¬© ;xLÅ$gDk× 9_r“82zX[9ËFv=ß©'PăWʹ_͉sþüù°H¬Ó—hRuÖCe¸hjjÒƒ„ž~üض«Wü¨! ÜËïWÖ®]kZ]–Ä~uâÄ ÷“úØØ˜TÅd‡×Ï~Ëž«3½D®ÇûñâÉ“'R““öƒÇy$)^—,Y’8coK<………ÒfЦ±«žþ×?~Ã):U³LÚ…ñ˜ösñ0œßÕÕÛ=ÐWcpppëÖ­‹ùæòª¦f?Ù¾}{œî±J¶555‡¾pá‚TC¥zõêÕÓ§OËOÙØØ(±$¢;wËÊĤٺ|ùrõŒÌÉ“'£Ïm1“ŸcxxXý:ñ›8~ãÆêN¿¾r655%;Cggg[[[UUU¼'¬O.R”S¾Ô§ÏŸ?åÊ©…Èk9Ù36?82õárðu=¤é‹/Œé"rb»wïžúiÂŽ[@âÊÏÏ7>å;;;M×¼;v¨a×O:à -ý,ñÜÜO’ÛÑ£G9")m~..õ|oooü&µ_TŒC_ºt)èpˆNvv¶ñy¿©©)_·®Y³FõÉ\¾|yüâ\TÖ¯_¯»Sxé€Dgiff&ìà ¥¥¥—/_V7P:;;é^CòsïsE?]; dïÞ½Æñ—¦§§ïܹ³cÇŽÚÚÚÊÊÊÆÆÆýû÷G-|ôèÑ®]»x~!æN:e|Æ’Þ€´lÙ²¶¶¶ÁÁÁ§OŸêÙ.fgg_¼x1::ÚÑѱ{÷n®gÄiòÞþþþ #)Ç4küŽ;‚ޤ–eË–§œœŒëäÃ`1Ú¾}»ñòÆÀÀ]d@ŒÝ¸qÃXá¸råJЀԒ““£& ÑŽ;tP µ¬ZµjrrÒXáhii :(ZÖ¯_zÕž={‚ ¤–-[¶˜*»ví :(ZvïÞmªpìÛ·/è @j±V8Z[[ƒ ¤Ó !¦m1·iÓ&S…£¿¿Ÿ¿@,ÕÕÕÍÍÍ+ mbLêƉT”;wH!YYYccc¦ ǽ{÷‚Ž ¤–sçΙ*sss AÇRHsssÈ‚9c@,eggŽŽZë‡ :4B¶mÛf­pÌÍÍ­Y³&èÐÂÈÏÏ:àMzzzWW—µÎ155UQQttöÊÊÊž={&A677 𦼼\ª¶uŽÊÊÊ £3;tèÄ611‘°õ!`¯¹¹Ù4˜¾·²cÇŽ £û_UUURϨ<t, "---Ö ‡ÒÑÑ‘™™`leee÷ïß—H®^½š••`$ Z;wîtªsLNN666.|HMMMêªÆµk×rrr>{õõõÓÓÓNÕŽ±±±òòò£¨¨èÂ… êC¯\¹BU€T³dÉ’ÁÁA§:‡xöìÙæÍ›ãñÑÇŽ›™™Q×TvïÞOàÿ·wÿOQUÇW¤Ä/ b ) Ìh³ J4N¦ •F#¡¦(ê jD“CŒi˜‘eY”ZSÖD€5¦Y¦µ‰ßZ–?êóžÝéÎùܽÜ=÷ž«}{>~R¼ç}Ï™=¯½{î¹ø»(**jkks‰¢¥¥E’‡ù¶¤ÅÅÅõõõVÙ†††ûî»/Wþ$v´¶¶ºÇëÛ–µk×–••eggO˜0Á¥fff¦$ŒêêêÆÆFµ‚ä îtà¿kòäÉ555:ÉÓýû÷¯^½:77÷¯~‰àoC’G8^¿~ý¾}û÷íÐI;wî\²d !èÊÉÉ™7o^uuõæÍ›wïÞ-AäÕW_ݳgOsss}}ýêÕ«+** ä0ž@ àg``àÚŸÖ¯_ï¯È®]»¬"o½õVP}›6mZ]]]__ß­[·ÆR õ÷÷¯[·r[ü ¾IDATnúôéAQS8ŽD"±XÌÖ¥‘‘‘S§NUVVú¨y×]w}þùçÖ0®Y³&¨ÞfggË0ž9sææÍ›©Ã(dx/\¸ÐÜÜ\RRâû,]]]Éž_½zuùòåþŠ:tÈ––ßIuâĉdÙ#GŽX¶ªªÊêpooï„ ,ÿ*?þø£:÷8pÀG‘—^zɪðÁ˜÷ª¦¦FÞÁgÇñ¦Ì 6˜Ÿ7­‚‚‚Ÿþ9mâñøÜ¹s=U–ÀñÍ7ßXÖ®]kØU™ü¶nÝúûï¿ëcR4}æ™g¼žîÓO?U‹466úèó;ï¼cUxå•W|TpT[[«öí‰'žª²”²Ê~÷Ýw—-pˆÓ§OOš4ÉS‘ÇŠ+FFF'BùèÜŸðÛo¿90::êc¦ÔWVVöǨgìëë;xðàk¯½öá‡Z?—Ê‹‹=6plܸÑqˆÄÍ›7ÏŸ?/Çe‚”!u<æÍ7ßôzF[àGõ:ߎÀqï½÷þòË/jÇ.^¼8mÚ´@Š8@Wjà—/_ÎËËÓ/Hà÷äÉ“¶ž|ÿý÷=öØxMÊËË¿úê+[“H$’••å¯.¤æÀÀ€u–¶¶6ÇÃ$0ÉèÉUàÈÉÉùöÛomcÒÝݽxñb—VÙÙÙ .\0pI‡S§NÕ/r;Ç¡C‡R;ÖÚÚHqèr bxxxÑ¢EšĖL“ò±Û6Wi†ù+«måEÍœ9ÓG7\H¸±ê÷ööº‡¥Wžê8$åܸqCŠ®®®)S¦x*²jÕª;vx=µcàƒƒƒúÙ+ðÀQQQÇS{522"ÿGæõ  K ---êû²æÂÃÀ‘••¥ÎµbëÖ­^‹ìÚµK­ ïþž>[§µråJµ¸äƒ‹‡‚¹¹¹W®\QçÔ¥K—ÛIjàØ´iÓ'Ÿ|bý5‹i.# 6pLš4IÒÚÚÚ}ûöYíîîÎÈÈ0<t©Crƒü¤©©I¹u–‘Ž={öøH9©žþyµÎÁƒýÕqTZZ:::jD"™™™Ö7GUÓFyyy€ÝKK Ï=÷œüDÆ_ýïØ¹sgÚ"ÁŽíÛ·[Õzzz$ äää$¿ðJ’_ÃS8@WjàÕÕÕê=Ÿ_~ù¥û2R“À1{ölõ[™rü¿˜PèÈ‘#V©áááxÀ¤š¼4u•I}ÕªUA7 ‹/Vó¿›DL¤!ÙQ1‰Dî ÖŒ¿Ö¬YcâêÕ«÷ß¿ÉY Ë1p„«ÔSås¡Ë[³IàØ²e‹ÕVü)--U7íhjj2©f“••%Ùkìÿ]¿~=Øa8ÚÚÚ¬¶ÑhtÆŒæ]òÄ1pˆÊÊJõ¶£³gϺÜ!`à8vì˜UÊv•N]›lp  k¼À!î¹çu1¦Lãmie8Þ~ûm«í¹sç&OžìÿÅ„BòZÞ÷­‚2ë˜Ts$³©z-ÁŠJÛ¶m3)k8zzz¬¶2÷ßù™o¼À!òòòÔÅ%ƒƒƒã]y *p¬X±Âª#§ÎÍÍUÿUÎ>44dPUUåûDÐå8’Þ}÷]ufݸqcê1&Cý¸ùÙgŸ™¯ãûøãÕ©×¼ £ÖÖÖ1'¾c‡Ià˜8q¢d5«mgg§¿>˜p ¡”‹Cñx\2Aj‘@Ç”)SÔ;žGR]b,û޹Е6p„Rnéèè°`8Ô¥Žýýý†[hÈ;þ™3g¬‚Ç7©––md’¢Ñè¼yó¼–2¼Â¡îG¢óªóòòÆÛõ+9wzƒÝGÒ믿®žEFÏv@ ãå—_¶ŠH„u¤{÷î5©¦ióæÍ¶/Yb±XEE…§"†CËÏ;—vïY³fÙößTIò»ClÚ´I=‘í;/óÀQZZª.¹víš¼ÌhŠ_ýU]ª<<<<þ|§#p€.ÍÀ!|ðAu.¿råÊœ9s’ÿd8zè!u¿pùxêÿŤÜÓ«¿w™9Ûͽ^/Ž•+Wª‰ð©§žr?~úôéííí:|ø°zeèöŽPÊ2Òìììä?ŽŒŒŒñöKë‹/¾ð K?p„œ–‘>úè£!ã}8Ô b±˜¿§­†›ªwóÞùµ“ÕÕÕê¬ÿøãë·5 Òü믿¶š_¾|Ùë^«ê®·5p„÷B«ÏÀL>¨Ö0pÈ ©btt4îÊv]ª®®Îë  ËSà%I¼÷Þ{¶·iõº‚À‘ŸŸ¯>ùìÖ­[ .ôZ$«7ÄÊŸ},¤p!ówÚõ%r€ºx³¾¾^¿¾ùÆ_2êµ¢K—.ÉÀê7¿“#”«îîn«‰LÿK—.}ã7|Ž3f¨÷ÂhÞ«,‰ÁʈÑhtÖ¬YžNJà]^G’m±¤z…Üß³TÊÊÊl‰mhhÐoÞØØ¨¶•O®ž®.訪ªrß&dêÔ©?ýô“ÕÚÚZýú*Lœ8ñý÷ß·Šœûì³êLG?üðÆ  · ÓW\\,‰Êñ»¤±Ä•˜ŽŽÛVʯnʾnݺ@:,##¥ÔÊŽâñ¸ŒdsssQQ‘¿©+@ý=ïWºªn_ÖÞÞ®ÓJ:¬Þšäé Uee¥: ,Ðiõä“Oº¬£‹/úë$àö’ðQXXøðÃ/[¶lùòåååå2Íøø  ŒŒŒ¹sç†ÃaéÒ#Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`! ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`" ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`# ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`$ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`·xine-0.9.4/data/noSignal4x3-completelyBlack.png0000644000000000000000000000252711006327777020045 0ustar rootroot‰PNG  IHDRÐ@+Sƈ pHYs  šœtIMEØ 8/òþ²´tEXtCommentCreated with The GIMPïd%nÍIDATxÚíÁ1 õOm  ¾ ÿN±Ò”~IEND®B`‚xine-0.9.4/data/noSignal4x3-smallTextAtBottom.mpg0000644000000000000000000006135410537064047020360 0ustar rootroot³-@ƒÐ ¢²MPEG ¸@ ¸ØR—óý)IJå)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ +üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ +üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ +üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ +üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ +üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ +üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€!+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€"+üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R´+AøP .‚p ]PX €€b` À€z@z”ÀìX` ÔÀøX2Ð €:È`@v‚``À014å“ Ø`&!$²W¥€@€à@€u @R0@€† `€Š¬)LÀ  Y€Ð @8|V)`@À€À i€+È   KÀÀ €è`:&9r’@–à° @u~V– @€[P@2 `@ƒ Ð@€p àÀ ÁÌÀ ’Bb¸ €@È@Ö~‚1Èðð€#VX¸†šH`“‚€€)t  ´¤èJ„Ý`@ÿ@¢@Žf €Ì  ô@¦¨À A´à A¬¨@@‚€0`èà€d@Õ É ‚X‚ ˆD0D ‹0€3I4a ¢qçékÐ@€PÐ@Í€ , |àèÐÉ€€@C90`À€€#90†C|@À€ˆ@'^NVü ì†BpÐ2Àt €3jü° €:~ü«J €Trh@@€D@ A´  ˜€ A(€™¡Ð€è€˜N¬ÃR¥€˜@'%1dÔt€dZK(™~&—P<,Á¨x@ƒ $À@Ó è š †à„H@€ZI ˜Øð à À3PC!¤ ð €%+0!fC!:9-\¸‹”¥êà AT(Áà¬Aè€/´2ÈÈ À˜@E&€* @ @É &rˆ @ÆFìÐ` @„˜bM *Rˆ„´ÀL`;!踊¢Š^Ô„ AÈàP*@,@±!pB Hb€? À˜\„üX@‹zÄPÀÔlYJ€€ô}€¨€:/tk– ÊF@?pN@|‰šȘ2tà„ ²ÀtLH8 €€2vôàà€èE$ ð€à ÀbB(†MÀà“`ÒEùê] @€`P@8 @EÈ-@ i#Px k8ò€h   À èPÀ @@*BFŒ° `TtÃPý WC@¢t„†–k  @€°@€G!% @>d `À t[ÀÀ€Ä0` Í€1®XÀeT.8À;()È4¬Ô"À`P J=H¢/…K® l €„Ø ‚Ø` b@@Æz¼ÀB` >,¼ €À† À„á px!0˜B,  HENhCR^ !ˆFJjÁ  ð ð8 _@@Î0\€43P€h@&À¹4È #&€ÄDÀ q` /Ð004š‰0|dÀ °‚`i6"ù”·‚ @;‚è P‚` Á´.È@À5H€.@ @@ |ˆ0 @€í$ÀÀà €€5T5 PD €=8`Àt0ÀäÐÌM&”Q6ëˆ @‚P B‚h àp U€ - Ѐ?Ð`ð@ÐHÀ€p À.~·h @8p I€;Ä„è†È@àÔìù4&Iaœ‡“¢/ÍR—«Ì A`à@ÏÔ È`€h)j€ k€Ä ˆ`1&Ê@$è €– 4 †Ò% p†$4¢j@4@H˜¾Àƈ¿)JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"#CüÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥"/` @;‚ F; YY°›î„ ÐÒg!HDÐÒ ™Ã ¤"´†ÈDÐ΂à ¤ÀÔ|Xa4˜^Aa¥|’‹B¾ÞøX @;‚ F; YY°›í€ú A-€ Q À @  øpbqD  À‚h €‚p@Å–’Š…'ÌÀ„³€)Ž9h-.Rb+¯Ÿì50 hXäÒ÷z`@@h4ÌÐnXÄjLI0(l1†Ì°Ïz@€<€‚9(ðKï]w@€ìPàPÅ 7€€Ä·À`P‘À  0! šò’Á!ŸPP®JßO~LB. @@A”ø%÷€.ˆªïp@«~ ¤‘†¤²iƒxöfÀà®ì²cô` É  Ò"á¡»„€h ,Å$ ï`P@ÿ) À@PhÀ Áü€¬V° %XD€4À € &À€4€P¥9!œ œP(M…€g€4tCà 8jIœ„ô†BHg½0 @@rjA8$¸ FàÐA§ L’^oŠÀž§c°F!b±\Ò²;ýß߸ ``1XÁ€ @0ÁŒ0B+±L¢V ÈÊfh‹ûh #‚h BØ@ðÐÀ€<Z€®°@ªÓ·ˆ^Ø5Ä•øa  €À˜¨ƒðiA€'(° À¡½|D„C ¤Âa „‚i†Q4„M+îPh`j1a¥|®‚Êù;å}÷Ê÷Ú[Là-€¦F àˆŸ§#m€-IhH ÿØðò uòú þøè $òoP@>àWþCà€j<‡ÿÿòoH‡ ÿõþKýÿ[ž¸ŠÆð@®nˆ @H P`Y@À.° ; ™þ Ð(à%œ®…䀃‚¾æÚúà -€>`”p€j‰`€‹Ð ÉdÐð* ‰ ä#h1ÄLCÈþM,4(&² ¡„ÄHdÂaa©¾,ˆ r€ €1! €˜`f ×;HE@‹o¹(šB ,1-÷dõßU `@ @@0Ä 1$|p@Ð$᥆€hCpPèOÿ €ÐA›œ`T˜€-ÀNè„rB1 4  >*"¼ ¤1ˆ¾@< € À b ¤"€>& @;ü´ÀL€*C:CC røÂ Hii é÷™)€ ÀL €'0pÀ5  €LZ¾4À€À ’°ÐÀÒɤ ÀÀÒÒ‚Òå–‘¼kßJh¼H ðÐ!Ô°`P4a 3ÃòºÈ@€€7‚@L„°ÄáY-õó€ h P@cv¤ „CØ€!!ÿAP9ÐÚ Óƒɤ,ž”€X5=/â+ÿT @‡–Bq0äbe&ÀBœ ø ¡0`T™»`Ðà! ¾,¼r@,в!:ÊàT °m`€Äyd G~F&[Aß—Á ´–ã@À¦Î&¸˜ L*"ø €` €À€€5z ¸å$šp€LL!'”â`Àb’I/V%ƒ0c¹Iä1¥ô[É€€œà0^PÀ ¸! c’%P€Eð(‚h BBÄÒ¿p,_@`a7†’ÿè÷Œ^ÔP€€ª ÀW˜à&PE€L@lB!⊤Ѐ0,; &“4 Ë@+°Ñ¹“·×¹°@Ä^0J\ ¢ãÁo“x €aÀª @à ÀrÒ‚h àe'ät•‹|„ôoó±×ÐÀ €ì°Xàà:t¤@+’M&nÀ€À½ÃN®`!}D–„¹(±»:5áà ;¼À 1 `78¡ NhŠ T` €NM!–J B @ÐÒÒ‘…!C²¶Ú÷ T@5PÄht”°à‰€' ÙÆèîÔ¬·=‰€;јœÝ@;%öÅx‹ñ€.^À 10n&€À%€„ :&ã‹!ÀL`ÐÄŠt'+¯uuØÀÊ‚?Á,H¥ïÀN”†¨P`€TjA\ð À¨}€@ˆ`gH¹ „ ‰¡¨ër€©+6&mï†Päøv b@ "/   B@&)'`*Ì(à Ë‚_‰:ö  &€„pøbJ!€˜4Ð(Ž ’``S‹( ¾ÜÏ“÷¹€|ü°`’×Np`Ció€€Aò8 Ô²nùð _\Ð@¡TBh˜Ph UÀaŒ´€,è BRÇ~NBFs Š;ÄV_0@txLˆPC0 :€! ‚h€vLA „€Z…{î@‚8 ¢Kä ¾V` ƒ¸ 44 €„¨0Vä A/qN{‚Àh‚û­Ë72}õ@ ÁGÑ%òÄUŸ ¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD\¥)$cüÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥"+€¨e àŽ@´Ä@c}PAؘ@ðKo|Èrèè¢ÃRL(ø!À²j6é @@t¤†£ut•ƒzvêÿmÕz`@/À€€\^ð¼ %Ä2hà²P `&Ñ004ì €eÆ–Rzq)1Iy²Òº¿ˆ¯ý` SØv Á€€æÁÀèøÌ˜(é, ¡Ùù š‚³;U‚X!è€ÄÇÀ1¿ ×ÏTˆ @Ô`X@ Ë,„_DÀ€èäÄ!$ÐRgà)) ä²bI¤"òUÊ&¥™9]0@0° ¼¨hP(Ôà– B!B0¬‚ >XÒ“øÝÀa¶Ç2xè‹÷ X šÀ4€1¤¯€@´»€LÁ ,›²3 p(84´ ô© <;û†¨%À €tà Bp`À d Ð(€„€Á0†²Ë/P”!àÀ(¼’Æ–PÌœQWŒ-&dà €NR@ &'œÖ`€èšÉ–RP”L ͉¡…íµëÉ €îBäßÀ 40 :ƒ` !òr@@tWKðägXÃ"*ßfP˜´$A¨>H @‚ð jÄ0 1Ðbp` Ôò øfÉ€:h`ô?P `Ä OF÷ÃÀ &À €` @`Ñ00 ƒ™„ÂaX00¤l¬„Œ±ÛmvA´$üˆ@1ˆDj´”è° €‚¸Þü°t”MHÀ€ ƒ €0lñ$0‰¥@(€Ð€í!„.˜ €Ó.,EõØÀ¬@j ‚¨p @0 Vð@€„àTð €=€€'À4˜|˜` 3ÈÂy 1ÀWM’F{è € à €n\ V€–B@ @PÀ1Hp  @ p@ Ò°Ä `Pà€œ!¡¨ ÀPš Pb@OÀ`ªI¤ÀÀ*(€(|£í÷ßo¾ûïŸ}Õ÷Üýñø:ö@v@€xÀ @5V@ Áù @©4à1a€@ð€P ÓÃCAPNxÀ @D–”—€@#Ø €§N И`_ú@°)LN´@€(ÜšP€2¨ ¤ €Ã€„¾M冃eú`SìM)òjPŒJ) ÷  €¨À€ÜÿTr’ˆ`hòŠNû  ìØ°’³`P0 ¡•Ê&£žöP€€ÚÀ †ìˆP*¼0˜œZ@PøDß»äĽÀ5!††(0šBrC£Þ\`(”0  97€hBB ˜4 ø(˜M&£v)° ±Y ÙK CË!8˜ò12 &@@2+&-A_€˜h ™!¬¤b€btLüÇ1@& )Û–HÍÀÂ}åA°2°èb€œ% @T@ÀtB +à+‘ºF”ž—@Åcn°@€ @À5Òbbv~€Òa5¨& $˜C ~0´sºJNHae”˜‹÷€€`@ €è0hÀ€ÄLLÀ:Ðh   NBØ ‰‰¢a ˜°* DÐlQ0¥”_IEî÷¦ ð€+ vÅ86„À€t€07”BHj@-!âaE šMá¼j ŠÙÑ„¿y‚° COÈœ @(á€'lø&bIâð"åÈp †fÉ  @3!|’@5(00@(Z; šLÈ¢^I5hHÈ`;Å$aaˆ(5¸‹ê‚ P +‚qE¡%R YE£¡(Jï% Gn„ï›ïŸsëÁ¨@?¸3€0f&€ÀR``ì3€”„C&3 /9œb{'-¾oµãÁ@ @  h€4 l@BCÈ À È@â0¢jzHDÀ*LÅtöo€¯ÿžèÖ€@5¦w€tü`€x9`“ €68À1È?rXïç ̈¿¬€*ØpÐà €bü° €¯@a7æ¬ìã†Qg%'¤®¯  €Ê\  Kf|ìØ òqe“:I00I47 3éÚâ‚h "€: ð€€2@¨à€À É :&@@§, “ &àß›BÒCå% ø†œ—Ù Z`@KÐ¬Ø “€„øÈÐÀ À¨@ˆ@ìVÈ‚PÒQ))È3¥.ÿt¡š"þ¤€à` @5<ÀTø Tä>L! –M g !†÷ûÒŸùü³^À  @MÄø€ @4Ð…ÊÄÐÉ›rhj … ù=‰dÒÊ+p&¾h€–p €9!€7!=ˆä0Ð @4A@T ‰ˆ& šM„ÒÃQÊÿòÁŽÃXûŒ„p1\xà–’a0ð(nM+d¤˜œ”“ ¨BT”“ ¥^Ý_¡¢/Ü‚Q€1R ´¨†L!7ÒY$$à‡ìjKA4!lLç™{ Ã@,R‰ †€Ø˜ É£†’€3 K;vvgnýó0 @T0°-€ @©`:@ ,A 0…PÀ*B,™Ê-%$nÈJPMC”1«` Á´à @bð`•Àb²ú0 €uò9 š’oFãW‘ÒۚѨb8"•`Iäá{Ê‚¨ k‚ˆä€320 TÀ  † ‚b@È À€`I»  bOu^Œ]/à’Mý¼Ð €¸—hBMÌ® À-!†XÒ ˆå +üȶnWòéJD\¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘¸€ $øòÇóý)IJå)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ ‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ ‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ ‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ ‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ ‹üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥"+IJR"å)H‹”¥".R”ˆ¹JR"£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€£üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)HŠÞR”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"»üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€»üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€»üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€»üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€»üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€ »üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€!»üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ€"»üÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R´Ø$ÀÀ/r´–dp He²M,˜(XÃPLAe†Ô”ñX²ÊGOÙ%£?×¥HÀ€hŒx „À @.&€98`€Pà@`à0B€vC…Ë€0“JÄ2( ”‹F (—Ê(ihˆ¿+KPH0&v„` H` l Ð*LÑÂÀbYD²ŠBA š œ·/(²bÂn° ðP a€-&€(dð(NL|@¤4Ø€uÈ@: &Ñe! r¨h ù`! èÜ™ŠèAn^ûdÄ_Ÿ¥¬¬”@ 8 bŒÀ 4 N GNId0Ó¿(&™$Ѐ켄üÐR /”L ,´^¬† ÀÈß(@@ H`&É` ÃC@ˆiC­(†Ù5Є€í¨i+¾ÑâiuÐ/DÐÐÀ' À'‚€8Ì3èè ŠOd–BÅ“C ¤/Ù€˜†°)‹ø1!¨ ä«—r”½P j‚ ?Ø` `ˆˆ  Iá pe~€ÒÔ†ä0ÐÄá…ðè†BÉtZKBQ¢/ÊÒõ ÀòÀ À1!>r àÁ 1 A0„ R €:Å—ò @ÀNB- 0_ȹ4oË!Ra[¦ó  àP@  “N@@à˜Q †LÑÂÀbZ !“¢11T†¢gI,šÉd0Ì3~ÿ¶ñ’—¢0@˜& €å"lè €BX €vMz A€0ÅÃËGH €€„šKI/°ƒ;q©Bžâ/ZØÀ€3Z™ðàK 1¿ä!û ¢ŒÒi45?L! ,šM/  Â’Zr-´0RÀ0Ix@€6¹@W€ë§$z±``—Ÿp04bÉN¨‹ò”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥" #ÓüÿJRR¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥".R”ˆ¹JR"å)H‹”¥"+ý€`†BŒÆ@‚Xp¾é¿Í÷ûæûîß|½÷?}×¾;ß +2`Ž2ÜÀ­ö€„0$À@0ì€Àøh²˜4 €€à À3&¿í‘€©`:Ä! $%îÍ> ø„€€fÀ/ÐÀ@Ðh¸ Öä63œ˜ÀæÃ7&¤3,¯z@k=‚8ø —äÚî€0J@dbØ´ð)yÀ@HGr@tMÄÐ^„°I_Àt…q„>žü1¸@€Öz 1qð A/È´EÞÈ` ÁT `ßÀ0LÀè1À°ÿ(¢À/!ôt^}ù¡€0&ÞÀ¸À3v˜ ;Ø€2¼P à0Àƒ“HA…J P \PZ BQÉ `„çèCJ>½0 ià d·pAàRî8FÌ)¯ˆ1X˜LÁŒJÁ…b˜âVF™š"þÚ°Ø €^RCñd €4¼°^$Âa7@à -”œYH +uì^(š€”#ßBrP„£ä¡;% Ç}óï…|»í@ˆ0t F3X Ý€Ÿû–KâÜÛã  †‚À!¤A¸h À‰ŽOˆ»ÁL ;À€ÄPt  :\üšB-4˜€Œ!æ[ìï® €N”D @×߀$†ܰ†@ÂÀvØ À€ì˜ÅB]=$Ü’™(+$´”ûâÀ;„ @*`•‰ :‰š©1$€Ò ®M),¶,¤ôq^ú¨ k‚°x É  `MVÐjR 6¸ä Ï €1Ñá¡¥eÜÅJ Ø–”º@lˆ‹ ¼À3Øþ°@7 € Àì„M&PÔàÔ@+ùh!€Á2h P*R2 &”åñ„|ŒëdEû0 €@ˆ h€“t²À3Nì4†ÊG€bCHÀ3Û,a0DÎM&±…3?káå€ÂQ[ ¢‚ŒvB3#a[f» €\€ Á¥€i‰ '~JTè É\ ðÇÉä (QHA@:(Éå¹eü€ I¤ÒQC.,Eõ0èh +à €vp?€&@@¡ "4 Ç` dÀR‰¤.€Ñ¤0‰¤ÁˆîY,”±­}ä ÀÈX0`@Ä€è7€ìà'¼Ø›Ë &„¢hhoNĤ“ ¸˜Ž ½(A|¬Ro”^È( 4€,zrh@=  ¸-!¡ 0<( -=Ò|L´ÄÐ(WÿðVXaN0˜€Â¶+D_© Àà @4ˆÉ¥è €bz@ h 8KäÞXaX0†Pa >Á¤Ò‘ÿ&¥Ä¢ŸpÀ2¸ €\ ÀøG) †€?(¤ïº À€ëé+0† Ú\¢j9ïeX   2ˆ`À€5  à ‰Å €tMû¾@ A ÜRh`òƒ ¤!§'ô:=åÀ€&€ @j““x€P$ À)ƒ@ a_B‰„Ò`j0`gb›Û­qÑô°@b<²#‰€?#*dQ4 ²`Ôø €€À™’Ê@  A¤ÏÌp`Ò¹`ŒÜ 'ÞT @ @+^€ P˜ ÀBZt@ta D ¾¹¤iIét V6á+TèÄñ ÀP  &!·gè &Pà` BjI„0Çã G;¤¤ä†YIˆ¿xTÈЀ€€\¨ @ÄÌ €À Àä €˜˜‘ :&É€; P”MÖÅ Qa‰@aEô”^ïz`@Ï1€8°Ð` |P€`(LHyD$†¤Ò&Pb ¤ÞÆ ¡¸­P K÷ˆ ;Á$4ð € À¨ŽpÁ€!!ì;R` ž/é.P0 ,€"Ø``!,š¤2É!$Rƒ4…£º ¤À Š%ä“QÖ„€ì†¼RF‚ƒQ‹ˆ¾¨ `¡%¸ ZQe ¡”Z:„ @òP”vèNù¾ù÷>¼€4Ðð 8†bh 5&6€Ã8 HD2`0Âó™Æ'²rÑÛæû^<4¤ ˆ@ÀÀT$0 Š Ì„®!ƒ &§¤„L¤ÌWAfø ÿùîhP@9€ `x@Àvˆ‡€ƒ–Y00˜`Œ€c÷%€ñ®pÌÇø‹úÀx €-~¸/ËÈ ô~` À`^Á 0Þ1ˆ`Q%örRzJá(úðÀ€¨ì À€€JÔ¶`ÀÀ€Ÿ!'Y0€¤““Cr0þ®( f‚( @Ï83!€„ €>8 ¡`0,š¢`a4ô rÀÉ0¢n ù¸-$>RP¿ˆiÉ}Õ &°€ À‚Y0x@€€= ¼ €„ø„nÅ` ! %’ ƒ:Rï÷J¢/ê@>v¤PÀ `@€b@nCä dÐÖpÒo±½)ÿŸÇû0à€\JÐÐ @€(Ä@ \¬MÙ ™°`g&† ˜PߓؖM,¢·iØÛ怀` `x’rЀ^C Ð4D@¡˜‚b ¤Ð(M,5¯ÿ,ñXì1%¸À@@À€.‰i&/€6äÒ¶JI‰É@i0š„%II0šQEíÕú"ýÈ 95 JˆdÂp!Õ’BN0~Ƥ´@2ÄÎy—²¼4À Øh €€œš1Øi(0Ô³·gfvì;ß3"@+0Ð)Ô –¤PÂÀt(P` ¤"Éœ¢ÒRFì„¥Ô0 Cê¶œ@Ä 1F‰\ ;!€ï£˜_#É©&ôn5y-¹­z#‚)Vž@N¼¨ ‚¸ è€.H0 #j@@|zr`È&$ €Üø°À0¦$÷UèÁÐÐ"ð^ !`0ßÛÍx €p€P$Ð Êà!!€œÒaA…! ˜ŽP¿̀fç÷ñË¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD\¥)r”¤EÊR‘)JD@·xine-0.9.4/data/noSignal4x3-old.png0000644000000000000000000003162510370751426015505 0ustar rootroot‰PNG  IHDRÐ@+SƈgAMA± üa2tEXtSoftwareXV version 3.10a-jumboFix+Enh of 20050410_ÂX0 IDATxÚíÝéS[Ù™øq-HHH±  ±ƒ0`ì6í½“8½8ÝéžÉ¤jªRórþ§©©šLÍt’™Œ;Û;n»ÛnƒY f5û"v± ! ‰y¡ßøç¸mŒÎ•t¯ð÷ó*•öG÷^Ýûœsžó• §æà ´Z­ÙlÖét)))jõÿ¿m¼^¯Ïçóûý^¯—³¤R©ôz½Ñh´Z­*•Êh4êõú—ÿkøDƒÁÍÍÍ­­­`0È{Ó9üñͦR©B¡ÐÖÖV( ƒ[[[œ+€€#¾¹Z­Ñh …B{{{‡ o0t:]FF†Ýn/((HMM5Z­Ö`0¼üðù|@ x<ž………ùùù‰‰‰ÝÝÝÝÝÝ84R!7’^¯OMMu:V«599Ùl6«TªäädN÷ò?>Ÿ/ moo{½^—˵°°022â÷ûc|hµZácãs›©T*F“””d2™l6›Íf+((ÐétÉÉÉ?¾Ù­ÚÙÙ …Bás¸ººº¶¶>Á`0ĨÍÂg2Z—UöâG]]ÀCCC26àéÓ§±;3)))%%%EEEUUU©©©‘î÷ûgffFGGGGGC¡Ða½…4Ýnw:555ÙÙÙbá£J¥ÚÞÞ~þüùèèèóçÏ}>_[xáÂ…ðXK¤@[[ÛÒÒRLO`rr²Ãáp8EEER>Êï÷ÏÎÎNMM ÍÍÍE÷®ÓétMMMÙÙÙÇ~óÍ7Q¹¦b—Òãñî"v)WWWÛÛÛcpäåå}ôÑGÂq¶J¥ …BýýýÛÛÛ±hžÝn¿råŠØ±Š`·Û‹‹‹ûûûÙôÔ©S Ñ _ÒÒÒŽ;V]]=<<üøñãÉÉÉÃq®Ìfsssó‰' Ct»ÑEEE“““÷îÝ›™™9”sRz½¾ººúÌ™3999ÒãŒ×†2•••¥¥¥ÝÝÝ÷ïß___—÷ûÖÕÕõ÷÷onn¾KY\\,%Ú?% £2` ŽÃI«Õ666G«ÿ$û×)**ºtéR~~¾”)ÿ}B™ªªªpˆvÿþýÍÍÍÄ}j4›ÍvõêÕœœ‰Ú}.GIIINNNKKK{{{tgXdg³Ù.\¸PVV–”Û'ORRR}}}QQÑ;w†‡‡eœ×³Ùlååå‡ï1XVV&ýsÊËËŸ?þŽä{AÃ)PPPP\\|8¾Ë™3g>ÿüóÂÂÂXDajµ:%%¥±±ñ—¿üeaaaâž«òòò¿û»¿³Ùl1Š6^0›Í.\¸téÒáyÐh4•••¿úÕ¯ªªªbm¼¸ë²³³ñ‹_;v,Ö×kÿfœ8q"99ù=ÓÒÒRRR¤NNNNT>‡Vrrrsss¢ ³ÙüÑG]ºt)¼¤"Ç?üÃ?444$âéjjjúì³ÏÒÓÓãó牊|ð”¤<…ÐëõÍÍÍŸþyÜÎÞ )))}ôщ'dŒ9l6[uuõ!{æååY,–¨QùpfEEEyyy‰ÛþÔÔÔ>ø ¡¡!óèû¿~ò“Ÿ466Æn@%ÔòòòK—.E7iã ·©©©©©)Î×(êcÍÍÍçÎNC–î\¼x±²²R®3 Óéêëëú"þXAAATFª Cnn./ìG«Õž>}:A{Ÿ)))Ÿþy]],Ý>ƒÁpåÊ•¦¦&;±ÛíŸ|òIœ£/ËsçÎÕÖÖ&èÏ$99ùç?ÿùùóçåý¥˜Ífy§óŠŠŠ„—h*S¸ªª*^(x‹²²²ÌÌÌDŒ6®\¹"oJRRÒÙ³gëêê”ßí3 /^¨GÅwö¹sçL&Sâ=\4𦦦'N(¡1:îÊ•+ñŸÓy¡¡¡A®1ž¨KKK‹â™ÌÎÎNÄÛqe2™Nž<™@S*•J«Õ655?~\ qÏÏþs»Ý®ð3ÖÔÔ${‚pFFÆ™3gëNS©TG=sæL|RD¢  àôéÓrÆòòò„Θ~™ô±¯üðC£Ñ¨´H®© ½^Ÿ¸“°óÎÐh¢‚ %?@À¡f³Y¬Z¨,RSS/_¾¬¨z¹¹¹gÏžUfß=œ¦£Ó^Wœ(wZJJʇ~(KÖË[;ÓgÏž•ë¯J¬à®s(V¾vŸð4*%=@Àqø?~\Ɖრ§ï)p®´ªªJ™I‘YYYÇŽSN{ÊÊÊ¢û ]øäÉ“ŠÍ‘,**’k‘ªÁ`PÎø¢”^VVVVÔC±C0öŽxôƒ”¿à¢¢¢¢¼¼\ Óëõ²¯bx-§Ó©¨øÌd29åÿ"òòò›¬V«kkkå¸r:999 ýÄËÏÏú¯•ű8¨cÇŽ)|¹ŠÕj½xñ¢rÒ÷^‘––vùòeEM¬ ¥(Óh4 1wñ⟕’#ãX‘V«}ï½÷ûK<ˆÒÒÒõˆx•pà@ïK…W<~ü¸Ø6åqsäÈEmv»]JĽ½=¿ßïõz······wvvvww¥·*''Gáéu%%%Ê/8‘””tôèQ¹Æ`ÊËË£µEbüY,– Eäåå¾ðxõ§Ç)ˆŠºººžžžµµ5¶-99ùôéÓ Ÿô±X,'Ož¼qã†BÚS\\,œó¸¾¾ÞÑÑ1::º´´ä÷û5ÕjµÛ퇣ªªJâÎÕÕÕ±Û5^"ƒÁ(ËwKKK322Ünwüÿ´ÙlVÔ­‘ÜÜÜÍ3æåå™ÍæC¶[!ሠ«ÕzäÈ6L­V×ÕÕ%ÄöHåååÊ™Þ.))Øûýïÿþ}—Ëå÷ûU*U(ZYYéééùæ›oþð‡?¸Ýn){cÆb=Zìv{¢ljh6›e\Ю¨[]!ã&“)ѳ[@À¯ó¨Ñœ>>.o/'//Oਙ™™¦¼…B¡‡ž>}Zl7¯¢¢¢§OŸ*êÎ×jµÇ—Úîîî¿üå/û\ý@ 0>>îr¹N:uöìY‰Û¡•””LNNÊ5$ÙÐÐ0<<¼ººš7µZë2©f³9//€ã#‡#Ê¿ÉÆÆFåL±;NéN¯×{ëÖ­¯¾újzzzww÷E´¡R©ööö‚ÁàÊÊÊ÷ßÿÇ?þqjjJú³Ûn·Ë¸5ë‹ë(pÔ['S^‰áFGGÅš§À¢/EEERV‡ï¥¶¶¶Û·o$Öôûý?nkk“x¿åååɸƒkvv¶Â—Ó¿L¯×‹â¼4šC³¿8â!??¿¦¦F!9zô¨Äaçõõõÿú¯ÿêìì|kß}rròw¿ûÝàà Ä6§§§Ë[ÀÛh4ƧV}___0k¡¢*h4é ˜?üðÃwß}^×sÀá;wîôööJ‰9l6›Œ5Ê4McccZZZB<Ù,‹X&uDbTU ‡“r*F‰Ë̼^ï_ÿú×ñññþûííí7n¸\.‰ý΄(àýÚkDÿ~~~~ssS¬¯©¨Rž&“Iâ«hnnîÁƒ@ Ò>|(\ü&œ‡$oÕå× |ùöŽt¸ÔãñDzMÓÒÒXKÀÈ9”§———KŒ{ZZZ"ж¶¶¾þúk‰ÓÒRŠnÉ(///¢r®«««b‹”pH|Ix<žÛ·o|lã• ­§§'Ò8czzºµµõæÍ›ÿþïÿ.Kí¯—û'MMM Qé\à™666öJæïA(¿R-„‘4ÓÓÓ2®yÓh4°ÍÍ͵´´Œù/..¶··ðÁÂáNJJJvv¶bìO§Ó577ß¾}û€½º`0866&09âóù^Τ‘]qq±”q‚¡¡¡ÉÉIáÃûúúêëë÷É ƒggggzzzrrÒåríìììììˆÍgE]FFFuuµÄ¹¡XKNNÄêêêÊÎÎŽ4%«  €Å±ˆ@II‰Íf“kÅ]ø-eË¿ß/6Ä­R©ööözzzjjj„Ëø †ôôô„ 8T*Õ±cǺººø2{øðaGG‡À›‹‰)S`ÛÛÛmmmRâ§ÕÕÕ™™™OLƒÁ……—Ë5;;»°°°²²¢Ø¢õõõ###ƒq“••%ìr¹–——#­”m4u‡ƒ€CéÝÓ§OOOOËÕ‹2RÖ2ÌÏÏŒŒ¾µµÕÙÙ)phµÚâââH‡Ê£%?ú ÃOúÓÒÒÒû÷ï/..¾õê{<Ǔзzjjª”­ÈFFF$NjêêêP(äñxÖÖÖÂqÆØØXxÏ<…Œd죸¸Øétö÷÷+¶…v»=Òbk³³³>Ÿo||<Òšª6›-==€ƒ€(,,ÌÏÏ—«›ž––&eun__ŸØœú ½½½W®\^s(ãÊÏÝÝ])]a½^_]]m·ÛÇÆÆúúúž?~¸ïs«Õ*%áfhhHl íe“““Ož©®®VTñ.aRî4éi‡Iuuµ2KP”——GzÈÖÖÖââ¢J¥òz½{#ǧÈ8áC$ÒáDé,‹”‚ÍÑzÝJ)‰O¹Ï×Z[[‹Ý|iiiuuuŸ~úéo~ó›K—.ååå%Ê–î¯%<–æ÷û—––xD¼ì½÷ÞSÚÍ Ó鲿×××_üöž':Ž€ƒ€"o—¦¦¦8ÿQ“É$å±­€#¼ÍJ"Žp„B¡X'ß$''çççŸ;wî7¿ùÍÕ«W+**dÜÔCXRR’pÇÎÎN¢ì[7555J{ÑšL&q—¹¹¹eEÄž'%%%܈Xyyy¬7=z…Ñh8ü~¿p­è×>w¤| +Ä Å'gÍ`0;vìóÏ?ÿ§ú§ÊÊÊÄšº–2Ÿâóù666x>¼L¯×Ÿiiii:333/þ÷ÚÚÚÖÖV¤Ÿ›››ˆ!88df±X"]‰.‘Ùlþ­.//G±â¡”7ŠÅb‘1ËavvVà))eÀ#//ï‹/¾øâ‹/êêêåÞ–Ra]J½“CÌápĹ²¿²²²HSY}>ßËC›{{{ã…†Ü8±ðžõÂ#Ï’’’„Ç¢»µÄòò²”o!cÒþ‚”zÛbt:]qqñÕ«Wÿþïÿ¾°°P [¾µû+|ìüü<‡3™LJy=h4EEEÏWBI±kít:•ÿ‡â„×¼%D¿ó­ÛÐGDJ-“É$ã˜êÞÞ^WW—,‹65MMMÍ/ùËúúz…?p¥\ ª,¼IMMBfÖ¬Vk¤;¡„»¯ä\ÏÏÏ ÌÍÍMÄM±_7’S ýýýQL؇À3"*c?6{jh4y˸\®‘‘¹vOOOÿøã ¾ýöÛxNîDDJGDù=1š_ …B ŠšÜÑëõgΜùë_ÿ*{L–››+Ð{YZZz¥ÏÊÊŠ×ë4<µÙlf³9vëÅ@ÀqheddTVV¶¶¶*¼QïÓ g„¨ÕjyŸÏ×ÖÖVQQ!c_]]]ZZÚÍ›7£&œË—/K©e·?ýéOñŸ>Û_UUU{{{tç78ôóŸÌÕÕUÇé\x%W¸ž¦TâD­V×ÕÕÅ'“C9ã@@¸ƒ"±šHTŒ÷ôôȸo¸Z­.))ùì³Ï¤L“ňV«•²ú:¢¹6ƒÁF£Q…_ÓÓÓãœiþZN§SਯƒÝÝÝ[°&Ps ‡Êä䤨0@vvveeeZ(\O=ê+ƒÁ ð&pZ­V  ßÿýËküdQPPpõêU¹Ê俉N§“@ÇgzQv».=zTJN®tf³Y háÜÜÜk· «Æ‘““£ÀPq}‚ˆUHLJJŠO0ᙈP(¤ü½¼ãlmmíÛo¿•}"¹¢¢âÒ¥KŠê‹Kœó’qÜ(ž:;;Åb ¹ò‡Â‡Àõu¹\¯ýÿž-)))2V‡ü¶··;::Ä^̹¹¹ò>D `rròúõëÑ]¿# ¦¦æÔ©S\Ž„ëŸˆU­Õjµ§OŸ–«Ò¹F£)++è±¼)àØÜܨ;œ’’"PX‡Êààà›~Woí644FÎaÙÛÛëïï¿ÿ¾ðôPT$%%={–_bñûý---¯ex+«Õ*W&GJJŠÀ|ŠÏç{S¹z±Jöjµº¸¸˜»ˆ€ãæñxÚÚÚÄŽ-++#*=|øðøÃúúºŒm0.\HèÍÞÞAÓÓÓ™ÍÍͲdrX,5ÏÛÛÛoZYãóùÄÖ›”””(0«q544$œõV__Ï6‰hllì¿ÿû¿£[ý=RÕÕÕùùù\‹âóùº»»ÅŒ Yú'6›M ðÉÆÆÆ>DOMM ´D¯×+ªÖ;8d?~,–É‘ŸŸ/0? Ù…w‘ý·û·ŽŽYŠªþo/J>'–……‘´FS__Ÿ’’ç‹­§›žžÞ§X™p1{vŽ%à€j``@lûu½^¯üªÕQ»ÃÝ×ÜÜܼ}ûö—_~¹°° 67/QQQ‘Àü:äÕÖÖ&v·dggÇyÃl6‹ÕÝ?¤ØØØHãßðÔ8'àx×­¯¯÷ööŠ[VV£Ê‰Š’œœ,¼ŒÞï÷+v®ßïûío{çÎø—ƒ´X,JèóI\Dý®½B†††Ä–ÓëtºÓ§Odz©bÕ/¼^ï[¿ p5Žxn~ …êíí‹ÙU*Uì¶sn’Á`ˆn†šZ­Nøòx<ò® 9H [[[ÿõ_ÿõþýû‹‹‹q Ôju<÷Ü'êÚÜÜ>\iuÌâp·<}úT,“Ãf³åææÆ­©b;׸Ýî·–«q¹\gÀb±dddðº!àx×­®®Š›”«l„7hU«Õ$DdooÏãñܽ{÷w¿ûÝ7ß|3??Ÿ ·l6[üçõüÝ¥$ÏÊ»QŽ\ƒb“°RwÅÅÅWgmmí­Ožååeä'µZ]TTÄÓ†€ã] …=z¤ðŽxD¢û\ÓétïHÑ‘•••öööù—ùíoÛÞÞ‡%,®fJ%ÒWrGG‡Â©ÓéJKKœžž~ëý°²²"V´·¬¬,v=4p$ŒÍÍÍþþ~E5IÊnæÑ­+•””$ü^ÙÚÚJ¸Hnwwwbbâúõë¿ÿýï¯]»ÖÕÕµ½½£1%¬믇½›ƒäÝÝÝÂñQTT$6øtíÙÖÖÖ¼^¯À‡›L¦ÌÌL^7‰Ž˜1 ZZZJJJRSSÒ)°Y,–(¶Dʰ ˆÏôD,¸Ýn·ÛÝÛÛ›œœ\ZZZQQáp8ÒÒÒ¢8““c4ÅßÑ"em°ìSB²ðx<=ºpá‚bç.Å*{p?ØÝÝÝ©©)XÙd2ååå‰--#‡ÊâââÀÀ€rÚ#e}GtG8¤|š’W©œÏçëïï¿~ýúüÇܹsgii)ZQ”Åb‘½|œ”:V«õLãP©TÏž=NëŽ5ƒÁ VVîàÉbKu´Z-›ª0•J¥ÚÛÛëêê:zô¨BÖn…×wˆ%ODw [Ê(¨òW©DÔÿ[ZZZZZêììljjª­­•~žÍf³ìÇ>e%ßJ¯×[,)£q juuµ··÷üùó l›Ùl[®¯ÑhΞ={)<\YYyýúu^7P-// Õ××+¤W-<6`4M&“”Éˤԧ®¯d^¯÷Áƒ.\¨ªª’2®n4eßTeee% ‰}‹äääÔÔÔ›››HNNNˆ™šÞÞÞºº:Y6IÙ_nn®X¯)''çòåË1m›ÅbÉÌÌŒÙp(± ÛÚÚª€cssÓçó WܲÙl£££Ò›î .–Í®|¡Phqqñþç®^½zôèQ‰1‡¼ß% ®­­‰Ö «Õ:33s|óæÍƒ,R8räÈ¥K—”¸Ýî8—ó:±±qãt: 88 Rý_&Guuµ)¹„v»=*Gff¦”7büÄ4Xÿ8 îììDT”bww÷Ö­[©©©‡C¸ÁJ¨µ¼¼,p$%%ÙíöÖê=à&½Ñ™‹CÐùøñㆆEmü«Óé •|Þìv{gggâæ’ƒ€#jöööº»»KJJŠôE½%«««Â9Vyyyz½^zþDVV–”ñíøwe222>ùäggg¿ûî;ŸÏÑQç‡~°Ûí ]`@Êezªmç¬æwIDATû¿ÉÚÚÚààà±cǔӤÜÜÜè.R‹º¼¼<³Ùüæýpà5FFF¦§§•°¬X*xXaaajjêòò²Ä68á2b^¯7þ >µZ­Øš@­V«Óé" 8T*ÕôôôÈÈHUUUâÞó³³³Á`PìB;ÙWöʨ­­­¤¤D9»„äåå)<&33Ójµp$.–ÅFÓîî®BFü„w‚V©T&“IÊ8˜Z­–xIi¿0á2V F|>ߨØX¢÷Ô¥T²RB½Tcµééiå´§°°Pá;$%%ð¢!àÀÿ3>>>99){3VVV¤Ìg9rDâìrii©”$|YÄ@@lq«Õ*ÜO˜˜Hèþ [víãèÑ£²OAÊ% >~üXJñ´è’Þ͈%Œƒ€C)vvv:::¤ljGʬJ^^žØ~ a:NÊv¦Á`PJã%jb gÌHÉ8`*eLmooK¹Xv»ý].Y=???<<¬„–äææ*§Vò>222Þµ}† 8ðF{{{½½½©òë¸GÊkÀ`0477 ^\\\YY)å&=ƒDŒp H§Ó)]I9QJ¸ç].—ð±)))ï½÷^<·BUZÿäÁƒJh‰ð g&“é]Î5&àÀk´´´È;Rº··7>>.åìvû©S§ætÍfóûï¿/¥æÆÆ†\«í…G8rssŪœ Ÿ¨±  ¨›˜˜Ò’òòòŠŠ éÍÐjµ _dñZóóóÏŸ?—· Ê_ûòï%Qš Ž8‘kRàå×€”ÃÕjuss³À´îùóç%>fggå*j¾°° –ók±XÄV¸dee‰5Õëõ*dú}}]JуÁpùòe‰9CZ­öÔ©S§NJ¸gExc%NQ”’’"¥(pœ‰íö%`YlLx½Þ'Ož|òÉ'2f}oooK)MMMýôÓO¿þúë‘‘‘ö“Ο?ߨØ(¥Xa(’±Ã·ººº³³#°8P£Ñ444tuuE:E"œ©·²²¢¥[[[R–dee}öÙg7oÞÜÚÚˆŒÓÓÓò“Ÿ”——'hE“‘‘—ËURR"W233(àp8ƒAÊÚ¨·2™L±[ãóùÂ{p jÆÆÆ–––rsså}IœšMMMýøã¿úê«©©©ý!ÉÉÉï¿ÿ~SS“ÄÒÈkkkr%p¨TªÍÍM¿ß/V //¯ººú€¥3_tî…“sÃMUÎ+SJÒJ¥ªªªòz½·oߎ¨¯¯ÑhJKK/\¸Ð«%@gg§Ó锫¬xQQQíÜ«V«‹ŠŠbÚ-)))‰Ý8ÊÔÔÔõë×c0p¼sÖ××;::>üðCÛ0<<ÜÔÔ$q(«ÕúÿøƒƒƒíííÓÓÓ?îÁ›ÍæÒÒÒ÷ß?*¤ññq û¬¬¬,// §Á_¸pannîà (N§Sl"F¥R¹\.åt’ÂWMÊ2‡ðQVVÖ­[·æææÞºÎK£Ñdee?¾ªªê䜎MNNʲ0U£ÑÈ8¸"¦¬¬lxx8v‹õz}ì¶bNIII ðŽ€#aôôôœ?^ÆJ‚n·{vvVú¦”IIIGŽq8KKKsssóóóá°ÃjµæääØl¶ŒŒŒhí 100 eáFTúÂký³²².]ºtãÆƒ¬ÉÈÈ8wîœð,Àì쬢îö¾¾¾Ó§OK|’ÿêW¿êìììëë á¼òRÑjµz½>==½¡¡ÁétŠmã¢@^¯÷éÓ§²©©© —l›››k4ëþŽ®ÉqöìY¹âÙ`0øìÙ³ŠŠ é]@µZm6›ÍfsL—Ï­­­0_$v£8Âg,¼{ߟÿüçý‡LÍfóOúÓ¼¼<±¿²µµ%e1j, ÕÖÖJ¯––vñâÅÓ§OÏÎκ\®pVÍ‹x.--Íf³ W=Q¬ðrú .Ä¿ÈD~~~Â999&“‰€ƒ€£··÷øñã2Vª™œœœ™™·§P(ôäÉÙ›áv»—––„×úk4ššš“ÉôèÑ£éééçuêõz›Ívùò墢"áF*íòÍÌÌÌÌÌDe«J¥2¥¥¥¥¥¥»»»/F¼wMÒÝÝÝ'Ož\¾|9Ι扸} Ñh,**’}% 8”eqqqxxøäÉ“r5Àãñ<}ú4!Ž %¼GÃRŠ ©Õj‡ÃQPP0999::êv»ÃY)F£133Óét:N)#@@;°ìîîvtt”––F7£")))¡wÓH___CCCœç‰¥ä×+Š‹‹;::xÅpào´µµUTTHO¤ÖÓÓsöìYåÏvwvv ×ÝŠ¢P(ÔÝÝÝÐÐ ñst:]YYYIII0 gwj4F#ý}¼²²¢ÌX¦¦¦ÆÇÇÙíBØÚÚZ{{û|·A‹Å’ …;Ã{Û2«BÀ¿áv»ûúúΜ9#W‚Á`ggç¥K—”¼äúúúÀÀ€ì{Є…SÒÓÓ¥T8ȈnóÆÇÇ•ùœõz½===RôõõÕ×× —ƒ‹Taa¡p†ÙÝ»w×ÖÖ$6àg?û™XZ}JJJVVÖÔÔ÷ þ¦ÇüðáÃÆÆÆØ­³z«'OžTUU)9Õ®­­M®ræ¯ Ñž*C’ËËË— %%%''‡— ^ …ÚÛÛåÝübuuõþýûÊÙ€C¥RÍÎÎ>xð@!»ž¾Âív+*[bii©¥¥%!îv·Ûýí·ß*ó²†C¢‡Þ¾}[±kæç燇‡cýW„÷ñQ©TѪ;·¶¶&e§ÜhÕ}Ça377‡‡ÈþºººÚÚÚ2&¿µµuãÆ Åf$ìíí=yòdffF ñù|÷îÝSxöÆËFFFZ[[…GËcgggçæÍ›>Tx¢I{{»Çã‰Ýç[,á­%www¢u9…/((8Üåà8 Èï÷Ë^©& >zôH e£|>ß;w”¶!ȬwïÞUB?¸££Cö¢ï‘†k<èêêRT«ÖÖÖþò—¿ttt(jœïµÜnwL¯xVV–ð6O QLñ–2û–-ãfU àP´ññqÙ×qmooÿñœšš’±è…×ëýî»ïº»»â’=zôHÆžz0ìîî¾sçŽbg(ö‰)¿ýöÛ¶¶6…´gttô?ÿó?â®S©T@àñãDZŒÌÏÏ7bÇJYZÝO³Z­QÙ¤‡P8ïOö7ÇÎÎΟÿüç‘‘YbŽP(tëÖ­'Ož(¤Ì×[/Y{{ûÐÐ\}xxøöíÛŠ]ÒüÖ;íîÝ»1ÝIü vww»»»ÿô§?-..&Ä]677799£®Ï …¢5Ÿ¶µµ%eŸ”=‰@ÀqÈõööÊ»\%liiéÚµkqþ»ëëë×®]{úô©g÷÷yk~õÕWƒƒƒñÏ}éëëûúë¯c:—‡³wíÚµÖÖV¹âìåååk×®]»vMiK´âîÝ»±¸úÉÉÉÂe½^ot—JmooKù@ÇpàÂ…Æ•¶¹½½}ëÖ­ï¿ÿ>>U†ööö\.×µk×¥HÃËÀ×_ÏuF@ »»[±ë6#½ÓîÝ»wçÎ8çiú|¾þþþ/¿ü²¿¿?AOÝÜÜ\,öÍÉËËÓétbÇúýþè†n;;;RÖŸ§¦¦*£(„Ê`rrrrr²¤¤Dö–„‹sÌÌÌœ={6¦#“á×ç÷߯´"K½5oß¾=11ñ³ŸýÌjµÆôo­¬¬<|øðéÓ§ ¯ñÑ»¿µµuaaáܹsñ¹ógff~øá‡ááá„>‡á*ûåååÑÝAJÎÕÕÕè®– …B333'Nœ;Ül6çææJYêŽÃÌëõ¶··+!àv¹\µµµuuuYYYÑÝ^ÜçóMLL<~üxff&áÒüôX\\¬­­=~üx,ÂÇ3::zÿþý•••ÍÛØÇÄÄÄÒÒRUUUsssjjj,FÂý~ÿÒÒÒÓ§Oûûû···(ccŸAŽÑÑÑêêêh} Á`Ò»ˆE%x)«Õ´Z­Óé|öìo¼ÞóçÏÝnwff¦rºï---ýýýÇŽ+((vƒÁÑÑÑžžžÁÁÁÊØx+·Û}ïÞ½gÏžÕÕÕUWWGk87ôöööôôLNN¾Pã刪££ctt´¬¬ìÈ‘#N§3ZŸÆÇÇŸ>}:11q˜v-÷ù|Q 8L&“”±Èc]ZZ ³<Êé¼aêwö›ççç‹í”844•ꥥ¥býŒh5àõ7„Z­Óé¬VkuuuIIIjjªÙl>øƒ  nll¬­­MOO?{ölmm-ÑG5ö¡Ñh C~~~UU•Íf³X,f³9¢@Íçómmm­¬¬ŒŽŽ mllDwü¿±±Ñb±ˆ½¼ûúúVWWcøèQ«“’’rrr***G¤wZøfóx<gqqqbbblll{{;÷[RRÒ‘#GÄ"Ë{÷îEå\9sF`VÅëõvww¿’¤e±X„w¥ïèèˆEúíÉ“'Íf³Ø±{{{>|åÒ ?ácmee¥¯¯ï0õÁ8z½>333---===+++<Û’““óʃoqq1<޽ººêv»WVVÜn·ò +EWJJJzzºÕjÍÌÌ4›Íùùù*•*==ý•ªD~¿qq1 .ÿŸõõu·Û}hr5bz§ù|>·Û …fgg·¶¶Ün÷ÆÆÆúúz".?Dä<â+eøPe7tIMEÖ½ND1ltEXtCommentCREATOR: XV Version 3.10a Rev: 12/29/94 (PNG patch 1.2) CREATOR: The GIMP's PNM Filter Version 1.0 Yew@IEND®B`‚xine-0.9.4/data/noSignal4x3-completelyBlack.mpg0000644000000000000000000002115311006330326020020 0ustar rootroot³-@#O£€µ‚µ# B¸@ÿøµÿ÷Ý€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`! ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`" ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`# ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`$ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`·xine-0.9.4/data/noSignal4x3.mpg0000644000000000000000000012244611006330327014720 0ustar rootroot³-@#O£€µ‚µ# B¸@ÿøµÿ÷Ý€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãF"Er'ãŽÅŠãq5Üóƺ]Çn*PÆí‚´‚7‚Ú'*ù"9ø6Jø[úä€&ºãÜüü»fâ,«a¾FèÔ‚ ‚éÛÀAàÀÿNôWÔ§,DÑb…ŠíÝðù.K›–X°ln{ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 0 ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hÞ°=ÿ°@ö@ àšÌ|ï#ÓèHRoÓÀ|‘yÌ"ÿ; úTŸÁ:íx®vse–0Ã~l?XP0@P @ À½úÜïàÿü±íÇ?"¾;Ï0,>æÌܨ6Vúø u`@ð` »X¾µÜw½` à}‘Ü@ pGûäí¼¼¢¹ÎMQ¦ o@¾X wøPp@—R>s€ôUÕç°„@ ?¼6ÿ‰³n‡WÿÀ+¼Ã0¦ò €.`€Àùà€ …r”V[žÙýÐ |ð•ùü¤^ó’vàf˜¦ ÆoìÀïÀõ ùÀöó^âÁó€)÷ÒÛÝrýÜÐ×Û¡NS7iPÝ]¨t9».É!R7æp@@@ Pß߯°®¿€ ¿yïó}½ß!âÀ,â·°lrB,óØè ¸:fp#ج‰wœÈ?ag‘Åc´êüÁqJa†ý +ÁLÁóAâÀþaÎý\èÈGÔx¾†¿h €·¹ÛÛà‹z“ò/h[|Ñ¿3·¯Ð ø H €~>7‹½v# @9"€-GÔ÷v€ZêE"|üùtDóâß*Fë:êse}Vò 0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0À ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£zð@ö>?€ê`€@ € € k¨÷ö(¹|§¿@@½ï§€åÁ`H;ÔDÅÁ2<üåØûœã 7¨=à z8 vê´‚?À@@ûà8@ ¿À<O¦ Ýîg[HÛHÂ_–_Õ¹[¨Ý>A%À1®ŒÇ ä·ûÿ#ÞP"ÀèÀX*?î_ôe^}ïÓŒ#|&Dž^s#r,¯Ô¼g@ûà@ûð@ýc`!ÿǸ°@ú¿¸ØØü€3Œ?å_ÐKúÌଠ“%oà ÈTIM4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFÑ­ ú‚Ô4çëý"Yl€€4ý"‰à€/¸_ØÔïóÂôlç‚­Ü©c 7Òu0è= 8Ð_ܽ´×Q ôß?à`?Wsœ@Š'™Ç9³ç2îÍË™Ìun¸ h`€`ÀAçé@ä†ùö?\ô?K;ì¬Sœ"Јaçe÷s¯]L7òöì ‘+AÛñAÙÁëVy¼X7\›ð–¸‰Æfm€€¿‡Ì½‹ÙñÙsM ‘ÀÈ‘¾Zà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà  ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ßþD÷ÿ€4ú~äAOÄÞû‘¾H‚½þD ~^Ý@,ï±N.vÃeòÛ–J[}Ì„j²òIüø"ù‚à ‚çðìHò(ðDÿ…>"“¹à¤‹ÆÏ·Äóø2€ròlì ^¸¿ú…ý·ðÿö€7öb'Ø—±uE!< ˆGx·¿p¿Êi½»]xœo-àA#@` à ¨ xp¢þ?àÀ?€üùÛÅ/íîx_±IíQÞÐ:s¦óXa¿2ûéìø—Ûùü" v&p#؉ñôñð S²tèW] wpì†Iå“$¦Fa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцëAÚÏ7—Kf(X¤&óþ'ùZ$$äœM/óþ÷xóÿ×þ›+ú¤îcy€Ð_P°@à@ @ÿ…oèYа„Oï÷Ûª€œ‰öãžâY,1r¬¨ß¤}Áõ(°@î@ö>§óê @7B7Èy.?~ÂëÀ€?¥Ððø$œÂ0Ã~TCþà@0p ð@øÿÑkùçû¤éŽ LE@΂ú€ýÄó€n¼€) ÿÞîò¢c›âMp€b ` `ÀÀ ¿gèQРŸOçðG9@8öøãޱm[äfEµ©a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hÞ”=øÕQR×äX FÿÏàb`àϯ•6‚õ/»€,øeù;çfñÆUØÃ ý8=ÿ ×@þà€ À€<¿@q¿õÀ‡‘Áô½õSWä'¶œAý²ºóÅ‚3[ÂL;àÌ@XÑ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fl÷ÃûÇ}ðíøþxçŒ{Üí†Ëå»’ìËFVý8<†§‚˜‚€ÿy»ú#0xD~P^Gè®r“ÀEáç9/ŠoqÔGò‰[æ€í{õÈM<G0êòL‚¹-è?,Dÿ€>vùo+ǸÞm äW: …ÿÝ€@ ôVú}æ@Ï#ÈÿÎ Àÿ¯Ü?C¡>ß @ žÙ@+ìùΔ@üuŽéïÎÏôÇÆùƒ~É+cAåÁôÁëÀ€+½¨y‚ã@ wç§åüü/w÷§òGHNÛœì˜a¿•‚’É€-겓6Ì)ëì(GØ ‚  €‘¶ÿ½ûA‹×íöúðNã èG}Wå¥7›ì`€€ @žÐ@ú`@üðÂxÔ_PßèŸðàNˆ¢Ÿˆ~€ß}äŽG¹Ìø´‡;€ Ÿ„þq¾8à Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0à ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ûò|‚á%ê!"A«Ø`à ?ßÿªÀ1 c"²Á}º<ú8[}Ø=¾}A«€9eE|Î rFú8ÐàúåüÐBÿ0Â79þ|LJ“¾¦Çó(N¬0ß¶ÿçPÀáàíôŠ(]¥½ÛÈ þÈ¿Ÿ¸\'D$âÿ "D w@ „Iœ‘í1õ¿-2#$@³ùÄÇÀ À >ßïà§hã½¾:ë¨m[L3"Ú­ô`@À€@0@@@ @ü@=ygx[ ø½|õþ턳ÏùõØ_õh!¢›™@*s|Ɇ4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцÓkR<îNª/°@à€½®¿ /ç‘oÐdq{´‘`ÿ|¾·1£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0à ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãz`@ôp@ø }˜ @ ~€ ` ~Ä|ùúwõÿüŠúˆùÿÀs= ¾=»áæh©ÁÑ£Fn4hц4a†ãFa¸Íÿú|ÿ@|~ÿAñ¹È¯ŽÄcÅô+·^]±ÚË[p @ ``@û @ú`@À@ý¼v'¾¾sôUÿwÞAÎqÆ]ã®{áÏÝ¹Îæd¬0ßP=>¼?PÞÀ¢ @Š;tìŒ#žxŸ?\Þ¤:°õo õÁõgÏØ¯ÀÍåø“}…ïÚ <¶7”iC:KÈDج·¶ "ûç¿.ptúýHüÙ»VÖýb‘ÀàÀàíÏDï·fu×Úýñ€rËtW|7>þ ûãÞ¯ªEàTíÖà üð:œÄ†‚â€-&LnG_ÙþìØ€ÿÀ¢HÀ„éõ5ÝÙ²Çew<»seŽÜ»å"€ ‚ h }Ñ'À±"}þ`?ðMýú: è¡;'ó¾ÞþÀ¦osË¥’émö`@2  @P  ÷_^sîò” @](ü_ÇýÞÿÙÍ¥u\ K§ç[á 0Üo}ïÐ öˆ€@ ÙÊ‚êvÿ®N ?  ìP€Ì­à[êÀú\h t˜@@ ‰D$ˆ ~°•``/§|‚OýV/Ù†úÿôMÑ|ßÈ©C WäãA$!ÐŽì<` _¨ß’›ô¸ `±@€'`ùð y}üÿ} €1Aý8ú‘øþ+åòÈ—÷Ø….VüÐà ÆÀý¾@€2ú¿a»s‘^är'É¡?>Þݺ÷ÆÁ]¿Œ×»‡åÙLÛ,•†F‚+€‚Ê‚?üh ~w·´,G÷ì›ÿ;ÊDû€'}ØP_VùاàÅ.7ÑAÂó?€òàóàú € €ÞúŽ?4?Lè ¢È}wX؈E0[Ü‚Å}€“»‚¦€8ïÈ¿êÐ ý믰ŒØß˜Õ?ˆè  xÀ\á?B@€,{€¿^ª_Ùí¼üàÂu„#§y‡ío*à Æ0Ãq£FôñÀ ¬Ÿóx@‘¾à=û&ûÜ&‚óФ\wÑÎ?ùŸ‘:½ e†úè yx ~8X x`€@øÀ€€€ ˆü‚(iøÞ{€9øÖÈ¢ô8¨-×êF#{bÈgÒl¼Û}Ò7Éâtà p {˜ Ð ~W¸l6×ÞˆßðBû|ç>5ô‰wü‘ÿÜô,_´ÝáÛ¤í'[r·êÀ° }X ~pO°1f©»@ ~ÀúÀ zq¦P(¼{ï¼§0ܤþëö;a±¿0‚ë‚#lã[4xÿ‘ x#€0.n^(‰þTªcöæ ðo.ïyù÷Ï~·‘Ýõï×eÉ%a†üP@€:f´§Ûß­p¿ïÜñíÀ²?±_·láû…™Iq¾²P`Ø>’.WÔ®ìœtôÿ®  ^ßyðþü?—‡"ôùcÖˆÙ-Açô€‚¢ý@ ÁâÁ ½÷ƒEû€#€&/` ‘„7ìÿ­5\#çw¼wXu¹2ÆP@p@p¿Ñ?NˆŸ@@ü@ÿàÿ}ùùæñì›u9}:G*°Ãa$o°-%vôOðœOuöhËÛ_¯~uü#qå{çȲœ´ºßj©ú‚Ä€.xŒÏ‘g|çü€'ЋåÓˆ¢EA` ½»üôEb '{ÄÞ%ªêß_Ⱥà>ŒT>€>€3áÛ^NÄX`@ý_¿°r#·yøÝö+¾øÛ±FÿúÍ–æ=ÔÜ­ù”d€ € à€€À³ À ÈÀ8ïûÉbm¿ûŸæE#öMÈ_+¶;# 7ËhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà ú@@ôÐ@@@ù`@¼©HÉ ?t\Ï·eàXæèDqèù\ÿ>7)VYeo¥‚Sæ‚*Ô}…@@0_.ú»à‰AóÀûh¿/gß··÷¥ç´;Ï2Iw4â›ä € à€ €€ €çãŠÐžÐƒòG÷NFÌobþïÿö@ ññÓü¸#&fE ;Ž!ö·p—Ø@ x(p@ ÈGÓ°½}¶":úû ÛIÎÉïßÇÀ×ñha¼‘ma†ÿB‚žÁÞ‚ÞØ‹Ðâ€ûÐà >öåô;ýìi<_3¶FøàX ~H@°p@ñ  À‘D¿ŸŸ¨ð ~ˆ q@ 2÷Íø@v7îî±Ë!zÞÊù«~‰¡ù*úÆüŒ“‚-Á€ ´DÏÑ„p@÷*ÿ€=¯R €À€€‘Üóä ô]tÈ‘†ó7­S ì=,pÄŠ$ÿ·0:ÀúâÀˆpg¿y "·ýûLžÀ9§¾®é×­-a¿«‚¨Ü€‚­:¨ü‚rGßdðàÀ>Éö¨ï¸¶háQ 2=Íx½×CpŒ9Oµ¾}À€ Àõ€ ùT³õ2îý´¼¤P—ûyØçØþ?žýkÉÏNžbÞ4:·è0@ °@ýA  @ôà@ü°@ü@üN¿¾h`à€ÿ=€7v¼‡çtIä'û{ö,X·Š#º‘EtA›o—Oý7æÀŸo`@ ¿€¿ßANÎñô"}„|½…; ÙºèS¸ë‡d2~2d’à £Fa»€ @=--`¤8/§n±â>¿ÐЂ>~¿á>rþ%Ô–K*r¥o³Oh@øaDP€€ ŸÑB…¿®(0|÷Íë=Ü»¾@o×§MœßŽ6¨Öê´‚]è‚/my0Î@ &G)û¸› …ü¿ç`®2NT¦“ÈóTTnˆ œ"•‚ÒÜ‚ã€%îa7{p6‚ ÐÑYŸ•Cì*“þŸn$‚MO¨ ]*{ 7ùóîN(0Ø ÏÔ?WÛŽ p(ĤޛÜoYÔ³‰@ðã²#Èùå ³«²ÂÛ FŒ0ÜhÑ£ 7ì€åÀø A»AAñÁ@qôû ÷ú‘xúû€0û<vc}ÉÎdüwßoÑÐÛÎÐïs+(ä @ ++Pœ¿Æô_ø¸°ÿ¯PÀ%är ¡}Ä?ñw)Mû ?$8 @û@@ù @û`@ý~HâeXþ>€À Aõ‹#r€Y`!dqb¿ ¾ ˜1†üäÐV =İ@ý]q™À®€€%vSÿqýgí×ÄwÏBE Ÿ<:|™ã¯(ç£x6ô €?l¸Ww„*ç¸Pà&•A ú0Õ×ßã„ Pâ3³NÚ–¶°@ö¯¸@@ù à€ € ù=mü'Ð>Ï?üV°Šç€ûì×Ìn‚ÉÝÜ›6,I¹¬0Þ@ö@?¸ {° ¸pp@ýqfñ¦ïÐÀü"¸@X¨^!P¼ïÏÏb?D&ì•+~”V¾ùnfKõ)þN^”Рdúw  €:6<Š,ÎßR_¹s’w‡eo”‚7€}AÚÁAñïí8ë?=€''±ÁÏöPò3ˆœ``Yÿø"=î8ñ÷D[‚…Ì"\t)¾ÊÄ–;‚ÂÝ€*ã\P~@7÷¿PNKÚÀ_ð <þ¢¦~ï|Rv°Ã%3/x ñì¦,GÔÒóE&¼ÿ¼—ÓúF3ËpÌŽàˆoÉïRü„ ï®Ìüâ7Åh qL¸¼£íƒÈ (úÛ¯/ú}A|9^$O4מ×_·ÖÄà Æ0ÃqœïØŽô‡Ûûÿþ{wÆçb¾HÿO’'B~}½»â÷ÆÁ]¿Œ×¹Û.ÊeJ•¯Í.vš„\NRËã_?pÀ{|ýzàÙíâ=À9çŽsŽ]kНv̱¾Ðt“~µé ÿ€@y·ÈP`@@A^à @‘EÍsfû;Ìö~Eúq>>„÷áˆ(_7—0Ã~=°?$` @ø@  Eÿï]` }ˆï¯ó¿€>ÀÀú€NŠ+œMqHÖcê2Ë+zð@@ø@÷à@ó°@ð@p@ûÂ( ~¸ ~§\ÿ` Îó*Ÿ sø+Û ÓÎEv$W"«·“C÷Žrpi±[ã € *Dˆ‘ŒëéüløÇ·ÇÛÛÙß…¢œî$Àý»»TýÛm­ìô>œ‚tKô'%ä$?, €ïÓ½&PBÿ¨ø‹ýåî`Ø¡B]a†þð~:¨äʯÿ")Oÿ#÷ÏȰà:;ÀÿÐÜnàNøp§¿;?÷æŠË®KcÌ‘k~OzqËŸ% žOÍ%Y[õ¨ èŸ`@ @ù`@øðà _”ù  ¾?à @ôíÂ~h €¤è³þEûÄ2’6gBß oÉ0¨’˜a¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£FoP?,´¨r /€ð€ ¼ ؉ï|.oÙà~yÿ€1ú€=ú‹ =çäý÷"ûóO?úx¿ž{ïA#ÿnîÖü¤Oy*”ûç'@û €p§æP¿?éàW½oÖ?2ÂŒ0ßæ€@ À 0 h׺¯°€` Àùé4À£`ËŸke)¾tÝ @ @ð@ð@pËÅg € ï”ðCÿx˲0¿•¥ÐBÿŸ.yZÜVn7èÐ@û,ì:¸?,>T¿ýóOë¯`@€€ ˆÓ¶Pîà›Èþóœ«:tûGå•[àÀõ@ 9—mBÉS3TG°hÏ·}}Þ` `¯ñïò"ù&JÑ®³bÊßg…V(ô>>Ü>>óæðþ7¤Ø |:yh!~ñ‹È¾ô§ûȹç7f|ºG Öo瀀Ýü{‚A‚,À¢¤p?[Ø?Yà À‘;¡àˆ^'Ûì€Å{ù@ Q¼;FŒ0Û€;û|€;]öÿúç,^ä¡ì#ççØG.½»`Žûçòî6/–Ùd¥·èð@òp/'tN|}×ß9Ïn ußR'@ú?~¹¨¯<\*ý¾—>Ý‹µ ^÷0Ình yŸ Á¹ñ‚-‚ËÈ€}À è”Nr½l€:¢¿@×-÷^CŒ÷ޤ魄¿>À€'ÿþÁåAõ4œM?ú àG" ߎ €#å×}ݤ‚ûŠèKÝ×<ÊeüäÙk 7óPBo‚+‘íõ8íÛäN-À§Èp Ï¿w@¿îñà$|Ñ@ ~€ > ù/Ãó]Ö]ùÙâÀ€b?à€ €àAñóAó§Ððp@û_" 0¾2:âp³6PàNïx.“–Èû.£uÁ0?ÜpH5K÷ŠA;Æp€Aþ°ä€>q>;o9ÇÀŸuÝPBÿšåÇ7ƒa†ãFa·yö#{‘Äü‰ð+Œºþĉøún‘¾;åôGNÍ×òç=öþmòÏÔdhц1¼Ás‹ÕËe˜ùÏ3<®[6Jù¨b[)ã˜a°Ñ¾à`žGÄBFÿà×ÈHß?>Ÿ Ä‘ïǰ.øD¿3ÛŽæÎ§ùçÈåݨü;©‰Ï.äĔȋ½‰,€$v0Ãhßnlò1Ž3‘\‚%+ʼnÙh _¬à ®¾H¿_ÏÿA]a†þѾô€Š×ßçnàx ‘8련̒Ŀh?P»îà ü £{°`@õ `€€( |GÖ}@ØïÉا°B΀‘øï™Ûûïé(ÞûtøävgdúZÖðŒ0ÜhÑ£ 7ß‚èÁ´ A%˜éAþÀ @ À€(uÓ¨t]Ï×ê.Âh!€>a›oßqÁ'‚@óý;Xî…tSõ&Ðýïûù?G~>ôk 7ôÀ@Íà€>ˆ>T³{°l„`@ϸ¿£Á €€ýö©ÔF¾œßïûqoBe¼^1\ã ü@ìBŠ(¦ýb„Ü Ë€@ù@@ûÀ‚!¦ Â1?7ýðHTÜÏÓA>ÌÿÀð°¢·¾¹¸P§aJCU¼±=§¯¸ wÇb€1|“ôVO@ú%ù @¸NÐü#hætë´x¸¥0Ã|Ð@î D@@öp@¯ø Ø @ ~3ºy¾ß߀ úü‹þOàNžd¼Pß[µÆ}ÜF|vó£7ñº`€ àú`AAí@ ÁìÚÀý~M³¿p ·ûÉü‰â€)öÃ_{6vðçäÂŒtá+|üà`` ø oÐ@§æyfR(p°üoN#û€/ï‡8ˆDé!“tP‡tj-o0à ¸¯áÐÏ·øçóã®3:F#}>øööëˆ+Œ‚ºwŽs²LÈ…24hà Æõ ðÜ‚Ÿ‚E‚-€н!7Ÿ`à ~ð Gdäº_Ö,íÞ_nŒ ÚN#ìQÅ>7–oÓÀïÔŽà(>а³N¼‘A èÀDqF°NüN>9œ X_Ätö¡Ï¬0ß·") ~` È÷KìÀb€€=ëùt×ЀýäÞ‡¨tž¶–ß‘Á@@@ 0@à@P@b&ñ>}À >ßà þæÕÚ?Ñóäé–€Y þ¸–ð#Äo°‚DaL?\Œ>;ÌCH8øè7 € ¹  …üÿw÷ÐïùW‰¸'Ïq{ a¾à Æ0Ãq£Fo¯À¿¨Ï©àW9cú'ØOÇB}Å å×·lß=9ü¹Û+åòÛ,”¶oéàòPH u  à ¸ èçM r0 ~‘_"žjÀBÝßãûú‘â&‚ý¸¢5пê7¶Ф`@P@ü@ü0@ûo0ò|Uòkß@ ¾¼!š_½É?Àü€ ›;÷ „ù[Ô‚Ðÿh z  { |HÄ^ucÝ¢?À ûú€îFÀÂý„ #ï,¼»@þµý÷ˆß0jÇþ°žXþ{“(Ð }p níïO4“Ü¿×ã÷ßïÔ€Ž ·[Üxç+ 7äæAõþAíÁìAõÁ ® !ÿ¾‚à€5ô°?4à:‘à„bzo`$n/·À¹=_C®7ÐËà¤Ô Žûä??ð¤~~ý‘@€!Sýä°Z?þׯoíÝfté¶îÙ VΦ\èH¬¬¸ç›È!—óí”ßçß›ýùæûÿkÔ_¿Ô^€[d{ÆÌ‰#tâP@õÐ@ @@@ýžz ý€€'œL<¿Ÿ ¼Â7]üBýöoBÍa¿²ÅqòVš~”Û™ª—Šð° «ˆà {rA øúßï„õC¨¶ažýà1¼P v‘NÁÁï€õ\†çn1Ž+’'øY¿€B:v¼ÿØmóú”N”û+}d´¾‚`«ÇPð¾÷OïîÛ‚7È( ~yò¼§‘½ùä·™«0ç« „Ú_õù\G\øèP®;8ã®:w9ÙžY<ŽÌÌÉD‰0Ãa£FŒ0Üfúü û€<#}H¿n&»±>Þän„ûŠéùyÊg|ôçòçl6_-²ÉKeo¯‚µ?êlXX¼]Åy2 ìÏÀX¨Iûiˆ£ýžpP·Ë;R ¹z#f(¶oÞ‚¨€à y˜ z˜ ~0 ` e"šP@ ¾?€€À"ø/÷~€@»žÄt%â ˜EóÃfõ¼þu¾ ã,A$A wÔŸ¯øðÄ|~Wõ×Ó(!V`¾$oi'Á3ã$ãÐúß Áó‚u$‚Ò‚èÖ6Ð_qÿÀäá"û EÏYÏø ƒý^ýö@Â5Md«Î÷´ÇFø w8 —_x(¤??$¼?ÇÓê,«ýýó€v$Ë_”ë5là øN]¼ôîyvSóuÚìÝÌØl¦mŲRáU¿GÀ€=(¦XŒ^Gvþ€>èpÀ__ €; › ƒüÞE>}ˆ‚Ý¿ÇOÀ—ZøÞ z˜ @ü°@ó²0`AÇ{›Áêî‚"oßËú´?DŒ÷÷ñ¤b'F‡¿¯ŒïwžÜZ™¶ò`€`€ !Á%pRG¤çdì¬}×Ò(>xv¾8œCÀ>8O7ÚÛ# 7¸ÌÕÒž4Ÿï´@þ‚è< …€ŠsôРAQN—¢1áâj‡íÌšdoÑ£ 74f"û ~7s”­'Žñü?¾³ÉïáìØl«3rŒ¬0Ú´hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Foé€~`ÔÀ ÀfPrF1Ö"4À4#uýåV{íœß~2åÀ€ `÷ ö €À;Ü>lì·¾–„``€ <#ÿÞR‡®øw4YÉvó€Zïk_¼ÐKú­òp€ßÀ€@@€6øp >>À €4#}?žÂ³ð·ÛŽ…f:.Û»`ý¶ÚÚA )2‚:à ŽhØÇNãÜ_zv'H$SW±$~ìîÈ÷ŸfD‘¹ì0Üo¤‚ªïÜ;¤¼ÜÌQ§ABÁô£ììX.Ôp!Èþîõïïïh¤‹ñM7ºAâ7‚ÈQî‚–Z¦Œÿ'Iþÿ‘M€c{Šu‚ûu >yôþp:·ëÀ@ú`¯—‘ÀÇG.þÀˆà HïëýÀ!€, ÿo_OŽ?t§&únÖožüþV¹Õ‚páj cîÐwZ쬈O:‚ <Ò(›@*ÏÆ\ï—Àêß“:"((h¸—øVA÷øÑéöö€bŠíÇ;ÆL‘½ø Æ{”,2€=âañô/° ~ ÿ?¿·jP!©W €­=@,"Ýß„Èß;a†ã}Œ=ðßÔ;À?[àlߣm˜@Jà”¨Ð@ûB ®r»a×ñüþüŠÐ _°Ãž)ûÖ‚cx°@ø0¿êÀ€ ?À@@àpÅ5À ì@âïøƒü˜'ë¹ÀïŸçÓxò}¸úk¹®W=¾ÄÀ ð@ò @0@0@€ îïçt(P@À"ÁñÁíÀ‘ùîûÞAù\~Nþt R ”Ñ}éÎù¢Ÿ¬0ߣe¢1ÁãAä@ =û  €„op` ü€r`'Ð €¥É?ý0Ámù/2fd’I"Ië € 8(  Ð }¯Þé¬?_êÈÆmü €· ‚·ç ´¿Ç-yß"a†ãFa¸ÌGÙì#çäG]ßbˆàX¾¸~yå rþ3^çl6S6Ë%-•¾¢÷É:ü  P«§N´OÀ ~.;úDìßÀ!ìUÂ?þßÆÂ ru þ# 7õà@÷¾û1t¤NF¯S¯@€ À ã?»íϘþMY@-÷Óo¡Íóð@ Lû¤v?*C³‚hÿ±³~€€PÂùàŸUx!lŠFÓa=Dù7Ó ‚ïõ`€b €Š¿PÀŸÏô/Úì3( €Ï [¤q}‚ åÁbÓÉ[òP @€ 0ÿ ÏЂ À€ ˆŸÏç·Yø0 èGÇu‘K¾S2à ÒÑ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£z@÷`o  |à ׸/€@üÿþb€€ 5Ëû/€‚ÐäÐD\?€¿½NóÊ–Voˆœ§üH0 ` À}ñ®ïãï÷|Eþt$‹ðFÅís®™I ú¤>x‡ô<€>@>$¬Ùœã»Ì"€$)øÀw¿üêXFþþ{ûꣴ¿×g>÷4ªß })kìeÈ ~8 { ß|î @ŸhÛÿ€ëÁúÀ ~|¹çÙÂç@û"qÎq¹l³c•ôØ’p ~ h°WÌÎ×@ƒü€@ÿó®éÆdw_ÿŸ‘náÙžRîvL…0ÃÕBx¤ø>¤? §L‹ØÐÀ À €óò°ÐÏã ýýÞFù"ˆúŠ'çzãË_}[ Üñ¾38ë8áÙÌÌÇfffC$3&$’ Ö Î €g ™AÀì FÿL&Ñ;O?úÑZûOnC 74hà ÆÐº‚êAÁ ´¤¿wÇ>à € õ Aô¿:ÿ“·úFîŠÞpOÀîÑïuÇþ] ~la†ú ï àáà€ € âÀq(èOý!  æS'wß}ÿ&þ%>Ûµþð€·êÀÅ"×Z¨ÁêM ?tÇûþú z–_íŸQbºÛCÔžcáe·‹¿ Þàó@ñÞßÁ>gk@‚‚4äQž[-ú¿ã’zæ‚úqM(À°‚-` @ð@ü@øÐŸ¯2 Ð$;|Ì×?öcÈôŒû&@Cÿ{DˆÚŒ0ßÀ@@@à@À@p@€—3õŠ$Qþ/ë 9P_¯O ý}ôd=ìÞvdI”Ñ£ 74hà Æû  z܊ئèÿ/$ÉìZ@ü@ðà zîPø Àh§v'íô”»Ã¶þE8|Ûłۀ3«n+\Y`ý(€ >Ø ¸mlëÛ燀 àø«AþD€5÷v‘ÈÔ‰·ƒÕ¾ê”Eô`Í FêK?$b ¿À ¨€ @ïý¯_ ƒý™‚ïÜ™O*&€ ù7cŠŒ0Û‚D€!FÔxRØç9ûÀ ˆß@€:Ix}Óçû‘ø€›ª"U~¹s[òÀ ¨ €`@0@`@À@Ð@‚ŠÇ4}þÃ>E‹x¿»À-"šH²?vwd»!Ù$‘½ ™Ød8Kíõœ?×þ!Àà¸÷‡ ØOâŠx"bl'tj‹ä¿éíã˜a¸ß¦AÞôŽÂ:JLÐ]Õ¹½@`€lFúŠ0Ǩÿè“pÎ(!1„ÿ¤È#4=õ¾.õ‚AÇ!AÿÿÎ%€;1*áò‚/û?¿ÿaà Ó@-p!6·ÝAà @p@ø @ùÜ¿·÷@6 ÜOòí¥öé{ 4ú™H//ó°Îý0Âπ@€@@:üTÀ'@ @ê@àÅ SeP‚…ñ~ßhïÌ †Î}è!ÍoÑ£ 74hà êÒ`$?,øˆŒ_ÄD €:R˜ð¿®~¨!<#‘ºíúüüRfâì)¿`‚Û) ôàó ø`ù@ y}¤Òô¬ÄÿÌö@0þwoO‡@1AÖ[||=8§È v(°€ €€WrLÑr?ȱ=‚èýl¸ÿÀoŽDŠÌ€â´Þó8vtù›¸ßP9ã @ @û@øpábäýÞ½D€‘H¯Ì´éÄ@Q;’` ›ñ¶à þq)0¨ZbH÷ê¸ñ§‹÷ú‘À‚oøè·üòû¥7ã€ùïóí’N!`§Ê€>û¾À8ãèDøëŒÎ{;€F@¢™0Ãq£FŒ0ÜhÑ´‚ç€;Ï@0))`Fx ÿpÀ{|ýÅtl5ÛÇ›‰¯áóŽ]n^í’ÆnÐ@û° ÷ € €€ú@ú  …7þ `Ô;¼hBñDi\ ÷ë@,|óàªøS{@ð@ý_à p } ~ {  }¸aozü‚‘ýÿ¿`À€ÀÛÄ„U%þ¼X!ÿ`¸Äw7Iî©»Žf<1x^ЦftÈŽâk¿à€ €À € ú‚ /ñ„Ÿ½‚êÈ@ ÍyŽy?‰o-óé$’I<# 74hà Æ0Ãq£FŒ0ÜhÑ£ 0 ü>Ñ£Fn4hц4a†ãFa¸Ñ£z @ö€€`ì €àÇ`/F踠@üè‡>€B…s ?§`à h²tþ2žüõ ¯‘ðÖoí½‘ÐA×AÑA  óKÀÿ»)}ííÛ€ÄRqˆÛŠÿ؉ÞW%ûé‘bÆøX   °@à@ P@àÂÿßîú®ì‰÷Cöú2üÇê+¼¢] ?ûÅå"×:€:]ë&8Wë ê€ŸÀ@ð@ °€@@@úoø¡nߟp@üÀ@ûÀ  w°ߨn@ûž+¿[ß=½ßá[¾y?Û»çÞ—#q!Á。#‚–}ÁòÞ@=œê8ÿ‚ÿ'?PßÐŽob?nåÅñ}„€;Ø/:Á®3V0Ã|@ùp@ü®ýÁÍÀ‚2;éù;øä>¼úFF#{ý°Ã;œ4¸‚ʼnÿîr#Ë!îÛÞ0´° X è H X ha_½w €³ÈObù¤_žïd_5ìIKÀ-x{ÿi là€ ,Hˆ‡Ó¬ôÿ6þ:íôööwáoNsŠÛ»µOݶÚÔ0Ãq£FŒ0ßÛÁã‘A¦ @Ðޝ8^€ À yOûUþ:½À ³8GÒþ»®‚Fú  x6Pm•à<Þ €1w÷Šà'ß°€ÿþ 0 ^»S›8 ~‘@õ@ü€@øP$~wz ötø!ߤ8èØŽÿ߈  œ´ºÞDPÇ‚°?‡þ‘À,Ø\ @÷þÞr6yòEöêM”û>›Ï8{ 7ùÓè#¯ ï‚'À§C³Ž„øè@®“ÊQÇ9ÎÌ…@ì’HÝð@÷pg¸‚E1zçjnoÀP°?À` ø~ŠßØžgX(\žî•ön\ÎrnVÖzõk‘hà  øï7rð¨±n€½À”Eþ é©Àç{Æï\éÅlnè Ð ~C(AðôAßAâ„“´ÞpX ~´ûõA Á È„H` ¹ó7«õã½ ]ré ûšúæo˜دÜdÊ‚ÍÑýz/mìX }¸0@ý?b(p@ω”ð‡ € Þß^ÜU#ÓEc·‰³.·è @üpîžx™1öùäó@ˆ  ú`ä@Ù¤ÀC’6<_𦠆촶üâyÏ`àà€ €¤_aWY¿P@ü€¾À_C@pž`-ïà^U*>_œVûx OGAïÝû‹@ϰ@ gÀ8™„ÐCÿ°¦þbåÎ{ˆâa†ÿ7‚!  à À   ãy¢ € €>´?ëßé×âuM´ü\¼¿imÎhц4a†íNëä   ~Ü_×dpÀ@?¿n/äÀ'¿Ž»èà »›õÐ |¨ºà\“÷ãš¿øFö-ú‰öaüL* {¾Æ‚üÐ0.€R'@Ío‚ÃoÈ u@ h ¸ ..Œâ€?h¢f·­SA Dwåÿh¾LžÝd ë}H Ÿ`@ €@÷ @ú_^v7þ2M@€ë´°Cïó¾9 ?Ù§ˆ†_õŒ0߯!ÀKÁôôb9€p??ïû 48€lˆ!Ó@ ±;ºgÖî÷r“œß7c×èGqÆd+îÆ9Î̆Iå™’d’$* `à Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74~kúÎyt§æîk¦îfÃe3n-’— ­õ€@øP@üŸø }H z0 ~0 ` ÀÁfÜp@÷`r&ð×BüÀ^³èD¾| ïŽ{×}ú›çžÜÜ~°Ã|0@öÐ>îÁíAô @‘¸Ïp@ü@¿R&Š!5"@ݽÜx ÿWÇ@MzY³˜†?‚WØ h gÇÉ¿ïÉzERžG÷WE€€'Góh‘B³ça{ŸAPWFèÌ›¹0 8 —øœàȸ¸‰ Ì5þÿoá?ÜßÔ^€ ÅØEþ›=ûÜÊî2¤áÒdnS 7éòÉàç €à€  ó€±`ö”¸çÁ¤Ê_îrÌEÖPî|™oÖ?dIÿcöË` €§ÏÿÚ $€o~ü`VüÛó͹ö”áÒ† ¿Ñ\ 8€À}”³xÅ9¿H}ø)p[÷í û¼@ D(‹Îø2°Ã‹=ßAÜ@IAB8éÀyq™<2±ÃÅ{ô/®`Çãݸp7.XqÕX0Ãq£FõÀìºÐ>܈  8 ~$Õòß`öÀ ]?€ >„~SüåÝ5ó€„ßN9¤'*Q›,a†êlþ>mlû$L©É~\ý AïÀ4ŒXEùÍ¿mÏ¿Ÿ”ë•ï—MÚäm›èÁˆà ÜX¼ ‚OÅÈÈ@ú`@û0à žüçS κϨ!?ÐÀŸdîB>gb8N$v7Ÿ˜€ Á!Á ø€ù ùf$Ð À Áì@€æ€)ût*`O£À/0C`ÆÉÝÉÄLÏ'DÞçî=Và@©ðj”`‘àú™õ•@*2¸Ä_ø;)1†ý$÷¿y×óÍ£uﯺ÷í¿º»¶—K¥«~pN½©7ë|¸'H¤p@üÿWZ ŠpH€/¥‡Xo{„^å‡7èóÑmù8¤°@ àp@Î@r+p«Ç]$€>HÛêFÜ »`ÑÌëêO”<¿ë”¯såÍŒ0ß”hѽ( zˆ ˜0@ø@@¤'ú¦iŸP€€ àœ@ü=¾z|58Ü[·aEl¬0ßžjûêÔ'øØßñ¹ß±ëö#ý…{‘ý¾ñEñ°Woã5øí†Â«} >À? Žä<¼¸ ` À hÞ3Ÿ`Aû`@¹ßèBcÿsà {¶`Çá‚ypû=òf8ºàåm€ ó®ha÷B0„EÙ:þ&‚&8Eh¤É^º¹Þü;tUpŽzü ” žÞ<?, @@ùÀ@ €@Àðo·N™×±§ð¬´S€aŒ3 ïÁ=ä@ÛÒÞm9ŽÕ–šÃ ý0>8´@ x¸ p  P àÁ¨eÈ’7ÏñÀ@r'½Ï4·¶|åxðýöw54›ñ¥÷‘½0 ,#šÖ‚×ä€3çÈûÌ0 €ÚGãê(ÀõB W2ÿ¾®r™÷K­o~,?SE‚Ó‚çsÈ:€å@ h ~ Bÿs¤€r=÷“S âNd|Ž˜˜ß“AÀ@@ P@@@ @O·<@ >¿ ¾@ÿÖ­ ƒý qÿS ð õÀŒ†à´hÝà@úï0@öP@p@P@üþ2€‚ä€:dà”x!h¿zDþD.‚ûn ã!ì0ßè@øûòÖ> óø.ê¦äHìã v*a–‘yãšÕõåPBÿ_c8Ã|˜ˆP€@€@@@ _w3ygð„$x¼x#€˜ª*ˆq\L _9ùQú<‘ú`à #õI»Ù°ÿÃvß:_õÏÐùRÚ;Ça­øÄÃùÀ `@€@GO° à„G@aˆ WPÉц¡£F°hèðà€ À€:" ãs¿o¯×üGût,íð/‹Û¶uιveŒ0ÝmïÁÝ@^Š®Úøï¢k¹À@ý0@üñbÈ €€ >x ó›þn¸\kÕu6adü„ÆÐ@€, züȸ§ëíÜ㾄‚Ô}úô'û¼G4Ä]wŸ þÿê÷N3 °²µ¼x € Ѐ@€@À`@/¤Ô4__nõjô‹ÈŒM„_¸t{Äùg—6ÌÈà þš@(ì<äl>¨ôÃî'MÛÉ® &`(˜ßï6‘KJì·üPÐýóĹÒîðç:Ù-nQ¨pGd6€€ ê œxL(°G5ïPöéñïźgžê¥© ö{Ü cÀh¤DBpÅÆ°>Çü,Dt>´¿ÆX^r$À Ëþà €ˆã@)ÀB¯q` }¾Àq fP EóßNuŠÃ þLhÓrãòË,•e‡Z0Ãq£}r/ã¡búãq5ÜóÇ=cÞçnWåòÝÉlËF7¿Tˆ>¿‚‚6Ñ|›øÐŽÉtV8,o× ûœ‹ÕÊ+ª#ç˜÷Z2VoÉÏ‚€1¦o"#Ìh(³]þùg_ÿ©¹þ#ð~D©]ÏÏ ‘ø€NT­ëÁAò@‚§‚ÞÒ oöM<pÛäø ¿÷¾2è‹y<µÎ_` @qŸN5UX-P ˜Pà€   @ÁëÁàAð…B¯@üމà  ¿‚¸t%æuó|†GÓ£õý”Þ †™&d’@É$I4hà Æ0Ãq£FŒ0ÜhÑ£ 74hà  ü>Ñ£Fn4hц4a†ãFa¿¸‚Á‚©‚ë‚…9‚ä€<ød(‡ðP±b€94"€è°g ÿ‹Ñ.˜(Çs-#wnóÄYMã´ÅÆú( 8 |H @p@ö` @#J$Nßÿ Aõ·ŸøþßðZ_¿°åi¹xíÁÉFFüžLp1 @€8û{qÏÏÜè¡î+®¸táÙ‹ÇI™ß;rD›»d.µBºàW· éÙÇ9ÓŽÌ2C da†þ„ ˆ!@í   ùB„I½€ýÁô>\Зð€TõâÌ'?Qy„î¨R7Ï4a†ãFa¸Ñ£Foõ° P  z ˆ |wðY[¿Oùè?ÈP  ûrëìü ½À€;#üІRZ9äÎÜ¢ŒhØt>Ô¬à ?ü+Ä¿®>|€5ö·€g;èYè-Îy<Ç€U¹9«…•¿ &-Ñðb”pâ']‘à Ew\î¸tÉ ÐÚÊ\¨°€O§ð`„Ogqñô|F"cƒ³ØW’0ÃE(÷¯a#‚æßæ'‚ü{óAò>#Èý,hOžßßþp¿/ïÜä+(n¢·Í4a†þ¾cËÁ ˆ€€@ ( œ‚ä§ûÞÈYÀà€; Âm¿PÖÞRçÂØFôýx }|Ђ”éøÀÆD¥Ò=ö0w=‹)g–€¬@ Û¼„Ð Áù~ÿnúÕ(àÂ'1’?µx /Æ G¸ }ç@úâû4ðCP` ÒM°Wpk 7ò0ÃqœßÛäØï°ðÿaåÇõîGúþ‚=þ}„rëÛ¶ワþ\í†Ëå¶Y+8hààà€  €'hDíÖt¿?ÀÇì2Ìóè]ÔÇ‹ãðæoé€ À€.'˜¹£Uõ1ûsç0Xp@Ð ~ Àò3¹Ï´{ŸÇ°aûж=ðç—_{|óœ|oˆFgÀ h  ßø˜èØŒ ¿Û»AÿuÏìÉæ€a¾~ú_ó[È,x ¨€€ŸÂ'XLötøÀçdz¡8W¶gN]±Q$[Z–n4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£{À@öù‡Jú&ã,?ô( ~ ˜@îp¹@€‹ÌŸ…€•\èüçæÈçk 7éÝÁõ‚Ê‚âK4‹:F|)þü;÷þéu×¾Üã<€núý>‚h ÿMqáðêÏÍo‚=‚Š„‹<.(ØìÎ@ßó à Hº¿(#ûæº ¤ °VHp¹-ohžÀ€& €€ ö`Þõü¿ç°^AñxðD÷×Ô ‚ÅóÈ€}í¢_IÿsÜ`+|½†4a†ý° ¿ÜP w ~8  ¯à—å—7޽ÁòÀñf¿¨üØŠ/åÃA ¹à܇·êd]‘`R·âA¸G H¾3_Ï]r-ÜwÏÈý·‡kó7s!¶™Ùê €+à€ À õ@ùÀ À‚€3úDì‹€‘yï‘@ |ü‘=¼ßæûi;®$ÇeZT¦o¸t¨p Ø x € p }gÈ·íßÔ€_` €¿ß}ûw ðGdB¶qíБ>Oïs§I0ݲÖò€Ö@€$_éŒ,WÜ?LÀObütFíó ç·ûw“f¸e©[ºVdaì/@€=J«ìEsîà×]d¦îòbDÀšÞtè¸t _Ô0ßgØz¼ðâ„]s¿à^Ý||t÷¡SvÙXa¾è }` {H@à@õ!]‚8ó†trbœo ÿAõ€u~‘ƒÒ½ÿ<{ã¸0õºR·‘~:ú5<0û ݧîìÇþ$ï>à À€#؇¼Â)Wnð)ØçM«iöÖâB¢HBhà Æè‚( §š76'ïKåÕlœO°®Ä z{e¼‘ÉÏ#Ø]ÉÁ´OxçfÌ‹[œÑ†ý4V ¸>”›€ ðîD÷EÀèÂ.n‘7êàHÛ½½Åcâä÷s·k¬ƒYÁ ÁÈÁ A‚!,¿Iäà`ióÿÐr(á_mc¹ï#³‘/~Ÿ¬‘¿14a†þ¬°XV@"Þ‚(>À1À]ðÚvö—žçÄ'gœ6W¿dÞXPwüØ×ýGì¬Ea¨ ~° ~´}ä]gDqìFö¡÷^/¸»GFòMa¸Þø€@ô0@ú°@À Ä}]ìü>øŠ»çü%ò“¬9@{€?Ûçâž|MØSy¶Œ0ßÝ'¾@¥|€3ר«“·}~@h€@·¿­‰@ @û‚þ€é¦ïv€WÄo…´Ì˜é3 é Öкцô,¬8¼©^@PAý@>`êu@'—’/úÂ(åzù7Í›H €p€@ý@ü ÈØ¹4þ`ô¨oè‘FíÇgSuLÞ_¼9ÓvI»dŒ0ßìÐb`p@¿{žà €?z#pš÷Ÿo·Ï¿™ÇòÀ x° 8 }ø ð ~8€7€}ø }@%iîDìCÈ¿oÿèŸnÅûqròâ? ‘R·£eoR¬´Š$P0 ~oßéô÷þ ƾÚáïßXþL,¬7FFΠA @ …‹^¤FÜϰO€ý?‡ZiSû~ÚT2[B# 7ˆ;øÐ ~hÈ€ ° |à·¸¬  õ¨¿/Ž?À úw÷¡B_Í*Onrêå¥"VÚHPZ˜=ظ( {S³ç—€X(˜Tw·BÈà=€|ï` þ¸,Çe×;œß¦!G8¶ð@€à€€@€€ `€,ûtèŒØGÚBùçE‹ïnÌ+Íû[ 7h€ßÀ@€0pDÆg^ßO°ï§øW±ãàS §dÜ™2Z¦a¿·‚W€6ÅZb>^Çç­ËÀ¿·ÓÙÝÿ¾€úäo¨ŽfwƼŸ·ñÜ{_‹³ç­<ŽëÏ+¶Æûmʶ[%(±ø»`€ € @€Àú`ø¿?Pðà€ >ÝqÖwòG绽÷î/ï—î¿t»»µ†ý¶;À € 0€ϸ ~P-ùäû€ü."¸ö"=rþºçäHltŽàVn¹Ù&32IÉ‚p¹5€Ðvð,ø AþpA<gǹwr # û{ö(Nyrkƒµ[åà€€@€ € ‹x Ü7ó®ýý÷yÇAƒCm­Ã}ö#{¿ŸOޏÌèW°n:™Ã…pèì†b Œ0ßÔ,AÝA쀼Â( {¸/—Šv||&éïïyëä‰?à…þ9Ö?þò“$«[ç €  € @€ `€ @ €"}¿½ö¿L,YŽEùçîG¯ï2@3§d‘¹a¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцô€€üh zp {¸ è ºoû÷nŒ¿¸ §oç ú§_Ï?o~5;Àâ5¼‹ãCã}Ì>'êØ<8>˜>Ô0qË-à·:F»çÛM÷ÿè‘_Nô‰Ï;Óó‡óß} ’b·æ†ê7&‚‚ۂДV˜ˆï'oÜY;¬ÁB¿R?bºã3y’ ª]a†È |p ¸ ~À@ ÀA @€8ùÇÇ úqÛ¹Ã&ǹù—6YBcwà Aí/ðÐ ~8?àH €Íà‰óõö@ÛïÒ‡_Ë·%ÙF£8   8 Ng¿½yxˆSuù Œ½qçAA Áxp P`ïgið»nf¿äóËÞû˜ hÚà û°@à@ùî>€æà @ Aã½ýùòÌÿ(‚!}¾‚H  €ÿÈï€î2û¹ÖðoBø3h6eݪß60`ø P € :c„}?€¿€|}>>:Âpç"—K>Ú­KFn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fnn]vnän\–ÉK…T.FýD›÷4h¢ðG|Ò×0wÐèH XaY'” #ÿßw ÷¶W€ãþ_íÊDð`@ûŽy-S‚OíÕ£&ýÁð€6]þÍ¿€CÿWèÞÎëN&w‚Þêßi8Ð 8 |Ñ|È7IÜ‚?ë0ÿÛ@ý^ó©¤Ä²õî&)Ì0ßç„A@€}¾;b'ÛØð)Óñø#Ž:vŒÌÉ4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Foêàox tpà@ðÎÊ€‚À AôÅòÿØ!€.tèóÒ?ý¼Ž/¬§7{ýà·ÇAêºyñ€÷ïËèX‡§FÁtyà@š (ð¨ ÿþ'€ÅÖÝýuˆÀË žTÿçÎPPnðø?Ú÷QŒ0ßÂÚ4hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0À ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Foë  Ÿ€Ð~à€ @ e +À@ÿð ~¹r¿ñýq²Œ_ú#XÞÌ?3®AU€ á tìq?X›ì)ðàCÿlÿÿ»Á§€X+Ýo’‚"{Š÷ºôs÷œ}AóÀ@Ÿzù !€PNùùU.ü”{~–¿~ ªª†˜ëmǸ Hp0ùî4Àà‰× ûáà†KËã 7øÍ£iÔaòbŠPN£ë‡Ã€8X§üÿìÿÞPàÏž¹v=ԞꙚʱ³0Ãq£FŒ0ÜhÑ£ 74hà Æˆâû}]ϰž¹Ë]ÏOušà퀹ün=Û)²›–Yal0Ú´hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fmˆ‚:"ÿþß"xÃ÷ž„ ùèP¾Ý“ÎÒï÷ä¡Ùm²7ëp@Pý¬CÔ 'æ–iÀ‹~`EvÃ6 šE~ü|<ÿæ颻CloÈÍë)Àh yè { Øéþç˜P:°ä£¬ºs²ûÿ½Äÿ¯ñg}e0Ìc±Xa¿§‚?à€$À€žà€ ÀùØpŽÔ@ ¤w€8ü¾ýYÿi¿/¯ú‘àδ‚õ»òéw¹àMû@?X ~ €X ~˜¢>'¹üóÝïIüh"·€“¿a<^Ÿ‰¿ªß0Ós.~€€@õÿªð(¤'óÚ{€1ë›ÿ{€ ã&;¢7dXî(¹$žRB£ 7ùù£o¯9)@«dÄļ‘‹?î~Üsš@x~=ý„ñå("çË+`a†ãF,¶KdñŒ0Üie²[=cz°@òà@°°@ù`@Db·ƒPPàÀüÃvÕÂ;äˆG뾕3tÜ݇œÞ”P {xà yfh( §×—»§€,SÏ›tœ?7Ýü~Ú(‰Á«ü#hn‡ÄÒ˜a¼`ï ú܂̂π‚3C?ãtÞ·€@ü ?w€ê€"ûºˆ+~™_\.€3”×W2š³»eἨ €‡¸ ø ( ø  oï×›»çõÀáý þs ü8·î,‹t‹Ù+ué$tÝÅ­”HÌ` `   ‚& tÛèDÄØžô‹ò/{ù+ÏŸ‘o¼ÓÐé7=†4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцî‚G%€,ŒxPØ?(Ží3sàŒÏ¸#Àýv}'|îë˸ G÷ÛFÖû@  }p0@úÀâ8@à@ý]™ËÀ?öx$Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцúà sø À Ð x°   }p H ~x8 ~( €Ð² ðA¸ð€À \â$íÛ`Я§×îD̶Ï=þý¹Ø¦+|Ü>làˆ P {è  }È ð‘EÔàŠPLGþÿ€>Çõ ¢„n´ÎÈß_g€ŠÐ }Œ]ùû2ÊÏ-€”0\@xˆ}¶}ëpÀ@ºBÈÏ®àŸà GÔ?\ß A ûë@*ÁTH?Á ¢8ìp ÿ`¾4@¤o½uæÃ þhÍ÷#{ý€‘¾¿B(®]»À¿or7±¯oqºŠåÔP¾;søç‡åvÏ-–\±Xa¿³*‚0‚ ‚Ò‚/‚²‚E€;þ‚àP€xl¼ à?L·t€ȬPŸ€ ÷uíç™A¾îwíã-ûäüz$œ~LA‚  Dߘa¸Ñ£oµ*z^Êv„žË‚àû€‚€2ZÌW—ãœ87ûuÛùÖ€UÞ“{ã|¶0Ãd> ø„>¼°´?4>øÀWתOú-bXô³Ÿõ艻™Ø§;±>ÂÜ+¾5,¦å÷ @Ûð@Ð@à@ùÀ@ `Aíñ_öÂ(ð@®@¸H‡¸ü–€9xDä_·žÄøXÊS}ø=ü=¸à {ð sp {H HÏüA x;ú8"ÿÅ€,ñ€ `½X¯ç Bÿ¨Gk°ž|{¢ßŒâN+~x–ùHIò4DÄøÿ€À3€§=oôŒSüõÿ{ÿw±Sqzpè.ñr*i‡0Ã}°@ÙÀ@0@ @ Eç ÊB»ØWÈ_ÿ°§ìç®þ×Üܸõ&CsLª1c|8< ü? Œ<à @Àð“™ÁýØ®Eý8xà ¾®v+øzew¿Çx÷nâ‡ÊÞäxœà> >äP 00? ÀÀ@À E× ïøãã‘;œîùGGcùצéQõ0 ° {˜( z¨ è |PwÞ5ý߀€÷$@­"ü‚€3øþu  úùÀý¼ýá‘€?"ñÏY›4CøUÆoùêѾ€2ˆ@  xX   ~8 _­~ˆD¬øp°ù ;:ó  }ó®ž!u·“tQ,¸!õâµ¾~à Æ0Ãq¾¦  âÀ€Á À‚ã7¤n]ó €€@øj#ß@{€?"ñ€6¢zíÂÜñBg__OžO–WsÆæÆùè ~FŠ¡MKÌžø0ÑŽT@‡âšMè |æ~{õ+~é¤@´`;H=L¸ |€°ÞÞá_ú}ó>Àð ÁõäR>ö Œà$Oèðb3?×—üÎ8Èšî=²õ¨Ã ü¬6L¬ÀX7#ú‚è>eþùþ€8÷iæh¯ùGÌg‡ÍtÕ(õoƒ’‚6€%½ 0×XóöïÍà€€0ϸ @-#p,èà’?·±ÜŠo·b»uvóÇ.„Ü­éf<ÁñAñ€ @@_pÿWô¸ˆ‹ûÞœ+ài:pŸ¿ËŦLœÎ_ΨּdÈ ~¸ ¨ x°Âô…tg—Çð@À@ù@ü°àÁ D-öøÐ € @…Ä{üõ×$ ëù®ÝĬ0ßÉÚ4hà ý9|xü<Ä\>dt?X¼? €&wñý~×ëôlåúøÿŒY× €ñÈ®—“,›…·Ç~r}>¿9”³ ë¼”ó§»bøVýâ|> ¿Ä ^  vt ¿öß„A AÁ4€ 4@,Ða†êhÑœÏÛäȯðпaåëÜöúÿ½ˆ¤_º‹vÏ"ý¸ì_\>yå'?sfRØa»õÀ €‚€&/)DèŠ!ü|ÿ€5Xô§±ÿóüG¿"Ÿ>¿Îq³Ö›€€  áÁÀ€4<¢Ïìþ€,Uÿ#üükµà€ Èà öïMĩȮsqGVûØ x@ }'À wx v |p ° §ÔI?sØÈÿí€@‚é ë(¾þ@àrwÓ„¿¯€ €ìÙöçž@)oŽ‚,€'¹ @¬=Ô? ˜¿r`p(¡<{‘@@ûò߯¶ÿ:ܺî~zã»Å1Sfm-†èÈ‚D€>y?†Aò€(öïÜÉWþ=ú㲓¶7%•ç€Æ€€àúà€ð¿P@Áý:ùþýà}€‘ûÇ]ÿ€*#õÿøÍÜš·R Í’ÆÂZþØÄ˜ À_`°@ý0ûŸ望œç~vGöûÎÒcÌ‘jŽõp@$`Àø@ê]€<Z+ëÏÝ}=Áñ ¾ÿ×9üÈ€"þŸoè’Ì]{¸÷â½ÖUFoò¨¾¹óб]»bãøç®;Çð÷?+¶y?.nl2Ä«°m` ¼&0m÷ênZççp`À ¾Ÿ.ǺÍÊ%À ¾>Ýw“rË<ଥ¶Fo¸ Ð {X à  }00 À»G|Ð?Ø?¹þ}ÏÁ?:áîï 2ezæH{{€@é @P€€àá€ú €€€€€ ¹üà@ À}ý‰çŠÐ^çûß„ãý…á3v«¹ã·\ÆÀ}*!æ ¾î l ¿ðj  ßÐ0 ö|‚*‚+9ž>\Ï+~À~ÿà@Ð/gðnh ÿAwõÂÀ=w·Pïh®>¿Ï¿ ]a†ÿ–íèကà‚#`ဠÀúŸÀß©· ô~€  @üpOÎ@hÿŸb€4hï;=ÌÔϘ§·ÏØa¿dZ``2â®b¢RbeòFÿ‚ÿ||€€f}À,çPôH ~Âxp¯yÀ -oß¼å@ ~œËAÇ€ È¡è,ðOa €ú„DïqÔF‚ñ{“Ÿò‡yç- …þ_Mšó­•Ì0ßã@@õÀ@~.AÍÁAéA ¾ŸÐábHÀ@ € €èŽÀ;Á\ÿñÛýÿ@Hß< 2ÇÏ7÷Äç§ÂvÆóMû=wH €Gàø € xÝÐü(Œ„¯ìœUÇ’u_Ùà :öREåÖX@"Etñ@)Vü~à Æ0ÃDä@+ÿ pô?0?\?T@OêDz DîAò¾¿O±èzN×è0‚…÷Î,áfOœ”öó€îàU‚§6Ï€(nx,OФà @@@üò1éðüûŸ’v»>ÿ` º†)ÿR#õË1[ô !ì€ p&„H ÿ&þX@Y¾Tc<"  ü 0 ¿Â?¢ ¿Èà ü½£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà  ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцýÂ{‚>€  J ´  8?Æ ¿¾ß/;À@Ø`ë € ó€ ¯Î¤0èà }`  ˜  ”? ^ýh 𘌩úð^¾€8k¹ø4ô±ôV7ÔpAÐ4 A4øÁ7÷Ûñ!ƒ ‚Ð&‚0 &0 €À&€[ 7úl;@‚ìñ € ôn¼Ûà°ºÙÀ@€ }èœåA ®UsîÐl²®lß”ö ‰à qÀ Àˆ zÈ /ÛÞ<Ïa@ &#ÿÀÿAð€ $öë‚é x÷ûYÿI÷<¯ÿåÆ ×7ʳˆÀAbÁËAñ8\aAú0Aö9Ñ×d"o‚ ýt`¬ù€ øÉEevû‰â`™Œ0ßèàCò@AèMþpAù0Mb&~oß ‡ä€ƒÑ ›üàƒò`šÄM&ü à Æý&ìH n( À € H¯n_l™À!¸ }¨ ~j-?6ÐÄ`AooëÅð“mó‚À0öüîߨÁ ¿¼Êû‚ñ¼ÁÏû?ÿ€ À{ôë ?ù€ú À|üsI—ðeþ÷pÖo–"¸ˆn\ì!õçŒÈ¤pð@ý@@ü` 9Ý¢¸€‚¢ˆ œŠ7HÍêç)âPBœùÁ¾ÔÛ@€@ýP@°ŸKÂ~º#‚‚€&wÓå'Ú úœ€9j+³J „œèÑ ñVn7´˜Iðîœ6‚ï¾÷Hÿð@û‡€*^Ló·‚q÷>H  „@r …ÿ;@-#ràò3 ºÞ-ºú` ‚Ô"ë€-øá '‹t˜`PÏûà„Z¯üûÜYEYä_c 7ûP>\Ø@" @@ô€@û9Ý ˆ@ >àö`€"…?«ä¨W œsuàA€ÄÈ߆Û÷èýëê‚?ø›'^0Oÿöq6å¼^?õGþû>›ð 74hà é+u€ù€ø € € _§Ù˜øÄ¼€ÏîíL€„xôÙˆ&í*PAïU¿¢ÝhX ø z—?@ˆï]æ$èŒ)"è€@ü` Çï× füûóÄ~èPšEû½¸³0‹íëùÌÞœü°=L<Èøà>°¯W¹ñìÔŒ/(°€ À÷|Wýÿ¯×d ñí×>þüŒ˜ì‘†ôàÖ€?P@Ð@ñ €ñóו€ Aî~|rÕÿž0ÑY¤RZ$Ò?°_<Y…•[æ-ý<‰r:B”ÀÄØ¼€>@nvFÕà ð/ :¿Ñ‰ûô€È¿ND|õ›GÇô¶7æ @íP¢ÁiË턊Çfq× Aí‚!}û‚êYÆwÇX`¼ˆ-þˆÀHÿ è›w±†øsFa¿Þ`‡Á`ƒà€›ü@‚àš`@›ÿmøDÀt@\@HÐ@$ÀM6ýÜ|> ¿Ä ^  vt ¿öß„A AÁ4€ 4@,Ða†êo®‚Ë;ù;fâÀDG¾ÙŽàHð@ü@@ü_è اw·ÈÀ&2Wß {ý  €°§‘>ýáu¾Dßx*û¸7l °Ÿ‡¾R®ˆ€Àöà ~®ý¨ç €>ä`I–,/ù€ @ŠªS 7ï#@}”€9‰kò)Ø^óÈð@úÀ‚‘äR!ýSØ—íÈ@FþÏëö"ü âûvêF÷3ùYØ¡b¹ËÆÂÀî\¥2e‚oòØoí€4Xa¿´hцõÀí¿nAÅ-A$Áëή“ÄÁí€(€ŠaO”¯ØX°@Öe‚ùsõÀ­1¿O‚H}ø ª†~#ÙïyïìŽ|Fú:úë×Ì¿?î$?[7îF»{€„z/~@¿i9¹€ë×îtܤ$eó8×°  ¡@øàúV`!Ê€ þÀ.ú"®,À/î.ýÈñà„6 @ç0@ @@P@0@À@!`Aìþ€ ô,HÔ@‚ë;"h·{ox¿ø±z,@‹¸"Ùñôòøøê¦VoÙ‚€%"ˆœp‚á‚ý6Tœt ƒ 8 }p _ϱ•À†à¿À È´Nà?(ø®Áûyø/«ð  ~0B(  }ˆ  ~`gõÔêï@` €€8ƒ^ðÙŽ ܦå'¹Ãr¬oÛ Û€€ €€ÒÀß €`TˆF\ïçìààÁò @€þÝûñÚо@ûOþs~1þà}2ÀBÿ§ÖøÈ zÝú‚ ‚ÕD‚1-ìž}ï€-]vàBû7ËTµòGÿ0ÅâOïÐ^}ÈO„a†ÿ>4hцýJ{  \ ¡?Ä ¿–$Ðo•σh? oð‚è‚oò7ÕAd 4"Aø7òÀ€šÍò CàA`MàA€MþAýMþFoåí4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцýÂ{‚>€  J ´  8?Æ ¿¾ßŒ 4 4 4Ûõ(   {P à q0 {È { ˜ ~( ~yÛç®Å€*n˜° PA|·sAü€ /Ë»’ÐCW€)+˜LoÊà>þÀ€ À €?ús Nþ>à€?à@¢!Û¸¼ÀBÿq@=€t/‡Àð óã6SØa¼€€ Aõ@&RÔ‰Ó¦óïõ|ÐØ?§øÿ܈)Óß¿Or?B„‹éПiÛ»rß».Aì? á€.€ì õ@€ ?À¤ðCÿAþ @ ¸ð`€ 8¹@û‹=6æyÿH•Àmöëw—€6 ¯ÆüÝÐ@°@€í€ê€€ À€ @÷ÿød»Ø€Pò3þ½‚ €` ÉÆý?ŸÞd_¢û„î2¿k 7ûü>P @õ @ð`@ú>€sÜ(KÀP@à@üÐ@ûÿ¿Ø‚s³ëÐ _DR'ÔùïžÕð  wEä­üãoÞà€-`çà€ €@€@ê œ¤J ŒE›È @ü€@à@À@û2;€€&½ºx €è@’ R<ÉíÈϵߧ?Éåß Î+~a†ã{Ð@ t×-Iþ8U…à‘  ù|8Äî¯á z¸ß§÷ŽaÔ€ñú$‹ÃäØÞ £ 7ûä<ÿЄ\><È>¼Àa ‰ÿ € Áõ¿  òA Àÿ;u®Ð ÜÓ¿aDJ#¼j÷?Hï`œ¿ ¬­øD?° À } ` ˆ(¸‡¿ãþ¸ˆ@€ D{ÿˆ¿ï~/TÇË!›5ÛnYc}ˆ\øè vˆ yA>ðèíí „xð@ú²8Yò¦€@·!ñÌã9Âõf,W‹w ˜ça“1¿J‚Z‚^væÌŒdT1÷¿p € ÿ€ïæòôÃàWøÒ/@ôíÐü1ù”ù,-†þ~¿+øÍ܃·räÝÉK…T.Fý^^Z”8䜤d”Àœx‚ÀÐ`DЏóïÕtG¤@@—»X‘b´ŸF‰òv8Qòóۛꢦ~€ ÁÕD€1#_|cð«ä ØÀÏûÞ½ûUÈî~üûîdÓ(“‰É¡mò€@ ÿ¿È ø `ø€ë_ ˆù`èÀ@î»"ý½þûP@ýq ;g÷8@®ˆ¯ÞêHW¬0ßP˜À@!À@@ð@÷ @ýç<\ów¿Àp@û’0@½Ý«ˆ\ÜWÀ¿q` ñ@6_¾ÌëèFíÄáq¿5³}>}|}>„a\:g„{|øèG°]:uஸéÎ㎑Ù<²I2Dea†ãFa¾ˆ ~7?`@ú@@°@ü®çf> ßãÁñÁ € së‚ü¯FïÀP¾ðY<Ï%' i¨Ý€@ô@pà€`å € À÷À€ "»ënZ› ÂzÀàÀ ÈÏ€ Å÷6þ7¾ö#zùº†Î;rVâ‚ú‚ר |€>þÅ}Äç‚êvDï‡^³  @}ž)îË›Lx‘vXS~èBú#Ø|‚ã€#UF’EØ“Añþß>ÕÀˆäP@ }€ü #ÿ1îy:¼ŒFï’w+ ó-a¿ÄÂh zçÈ Ï@÷pÁ¿è²‚‚Ö<Ћ>†y “À ÀùÍäúpn‹2 ¯'#~ÍÀà‚CÔ<ž€>¾ò‡ö°¨˜\ Ô?\üªQÀ`‚€žjÝ3±PZe € ú€ù@íà€ü.j€„ר}ÿœ€.üçE‰éÏŽèƒ@Ä›¸’½[òÀ Æ}A\>A À‚ç€-þi™½ÿ„‚Ø-Èà {¾:÷0°ã{_êìïÀbM¸'8Þf°ÃhÑ£ 7ûÌø,|ˆ@¼@ìèí¿‚‚ ‚h h€X  ß°Á9A×Á˜ÁâÌAóAóAóß~»ì€°€¢@ˆ?ì÷æü 1û|gȧ€æCg6+~;éø¸@Ž9u Çÿ8#9ùO&ç¶u,¡3aºÛ¼ô‘àëàì € À€ ELý€ýïÁó@8ÏõÀê_äüŠ_ôûù¸ …þ$y×·VÜŒ0ßô(=Ä>Ì0 @ ˆ zH @ü‰÷Šçù€ýà€àö¿~IÔ´d¿säXa"øx"9Þ\Fë5âloÁ`ñÀ€ `úà€ ï€ øà€ `ú` „á•à>àðñB¯·¿±§‘°Êñ_R0— ëaŸ¸Þð<[Ü; <€_p@`¬x©ïö˸À‰ð³ÜОbïËØ¶á®€´ÏDAknÝÎ6î·ê @@í€@ú €à` Áž‚¯ÿØ Á€x#ñ»Ÿò0ààˆà[×7‡Çšµ-JS 7ø ¿r w ¹<‚;í•73@t­âÏÎY¸a@¾[( vl±+vèAŸþ½‚áŠ(¿ é€À ?4ë¥|÷öÿŸžósËÌûªyMÏ×&à°=à˜°@ûp@Ò0ùûý€‚ã)ûyïÿîJËúE €=úõàfûþT5l¬0ßâÀC pApMÐA0M0AMæâ6Ð@ú‚%"`ûן0Fýð´À&tTæõç>@4}ô‚ï×),P¾””@qb…%/ƒ/ ‚A6à!èì7öOüÿlíßé†üÞßr7Ø9<üô>ÀtìˆáÀôÔÜ÷ ~_5Eó ‚À 9ö óðï›@]¥õõäL¯ïß hà ÿHÁÈÁò8ZRŠRœçúý/ú;{ôä?¬ÀÝÂ- $PøG°¾ÎØø‰ öÏêé¾ÜA$ç7å€C# A °Mþ@AûMý°&€Ë{@ð@ý0@@æp@üÀ@ü°@øÀ@Àò?9üTnþýøÅß‘PÛÈ ~¼íp‹‚6/™·,Q$Pî{ó€@`@úà@ @ð@P@ p@@úð@=uß} VÐ(1`÷Àøy3`!€q¿û\õlÀàN»u=†øËhÕXEÑ;&!Gþâ•g?P À /sðà‚‰þ€>øùùâ` ¼ðøÙÛë>Àñ€ÀÍ ú`€€€ €øàúâ8+žÀ (P`÷€@+i¯/øt # 7{ÿ09å€> Ð!ÿÌa†ÿQ‚%})A£A¼€‚å‹þ‰³¡Bº NŒˆ ~P ~ŸÙä~÷n0¼@°@@ 9'¾»0¦ho'Ò|oÌà€<à€@€@ûàù``ۨОR¸X´ždU÷ã®{ë€4ÃÌÁWñ@=ž}ð@H+>ßÚä~Ð_«û"(!Loƒ°Ãdéù@~䜂æÿ÷LÛ¶IšdŦÚuI±­úH?<?Ô8;\à?XÉ©Ewî˜_@ € ¤Q ¨ _è?LîµLž` zÐDMoÁ-úl>¬ô8x>@>à>|°?(_B…Šíà?À A€4&Îëë‹Í•à ûmE€.ÙÍœ¸€#øA쀄,0ßå¶ëÁõê ÁÑ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`" ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`# ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`$ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`·xine-0.9.4/data/noSignal16x9.mpg0000644000000000000000000010727311006330327015012 0ustar rootroot³-@3O£€µ‚µ# B¸@ÿøµÿ÷Ý€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ¯%D¼ Qê”Po¿Ç||ÿû‘–à!?|_Ü x }ˆ ø ~xP ~yè\Œ¤8žÈ€À|ìÇ‚üõÌ#÷ ™bÆo Fa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц5€ @}@€:fÔ÷·8›×Ï×ý÷}ìEúü{s„ Ýö)ÏåÎ¥“-•†_¯ÀŸ¨Ï‚/ÔŒ+—\B}ý…üp,H¡]ºöívþ3^ç?.ÊfÙd¥·ÙA¯Ep¾ÈH 7÷Û£=ýAôýx³ÝÄO³ Ïr=œ¹°äP¬®äZSoªš|<öBzÃü"¿œ B8P@Oè~¿àþÄ}Àó®ô‹3nQ ôDÞôžÂöDsr[A Áô€…‚ß'^@Gùp€‰÷âÊþ úÀøFÜr_a†þÞ‚¦Œ?,>è˜ð@ýPä~÷ÏÜïÏüX»v?œuÖyã‡fdn x`€Àù€ïß@_7C³ØŠ$줟×â¿ó4@ÿ€ùûöé€f€Yñ¢÷€ío’‚³‚)‚ß3} €€€ýû||@`ðß¹—Ú€8ãïóÞcõÉg>W%¡¨Ô‚]äýB! À‚ãû}½ú¾Dr0¿¸ }ø˜˜a§óÀ‡ ëù÷"9är(Ÿg áÞsœ°Œ+gæFoå­4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцõ``ÿð@ú@ñÀ@û @@`^úX!X È ‡×Að€»ž …›ÿaÇO³¨î'[ο<·üñç×ZÞx>¬§@ôΨ ( N®þ Þàø ï3Û <ôìR#àüóÐÂqÎó_ÌåÙ[€ÊD(¸äìÌ¢ÓŸ¯°O³žEsóªG«ÿ~O€¢ÅÈœ;Í"qÖy:0Ãy@ ‚)‚2öÃÀ*ødð×\{i=ôX¾v‹(¿q|Ñ|é¯=ô¶ÄÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà  ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn7D>ÀÀX }0 ( À ~`à|Î?À À…Ò(¸@ …ÿmÚïb8™ Ó@-êk¶îT­Îo¤r€/Cv"óO7( f4‘à À ~ž^bÔ?3ìã\Fyñ˜mÛMÎú³(‘na†ù € @z¯øà>„>ú‡þkÄà€)¸¿p¤ÐÎwàLÙóy¢Ÿ“‰Ë¶»‹[Æ´hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0À ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£ÜþÅ;žxÍø÷ðþ1ïvl6S7r[%‹ £a¿fbŽ>‚(àëO›×@úbÁõt`P¡.Øê½€;ç—OŸ±Ý[zGçd|€ZØAs€@ @€@ð$H‡ß€àbûæ“Þ_Îà.r$Œhuü}0œ"mK[è}¿ÁÈÐAòDüìCkÜÿU0aã2-¯xö"þêùù€€7çûB7Řa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸ßEƒßp@íð@p@p@ÀκL³ è ~ €¯·Ç¼y€[ä\غ·ªï/¶=Þ÷o›«|ïÜW>äaBEŠvÀüç¾¹Ç}ðíÏ<¤çë³w2Ãe2Ùd­÷` ܤ4l˜Eù~UÛÏ@€ßð@ýùÿo¶d³@ =¸uîàÀ_À!Õ `ôŸœ8¿êFo怀ø Hø”Q‰$ý€%LÏ€ÇÛt°¤I÷âÏä˜pñÓ®ÿËëy6a¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãF,¶KdñŒ0ßÑAâ/È v¸ ¸ øŽTÎøùà :öìÒ>Eñ”¿'á„ï¦?“i®l@€Sष¿è£h ÿ @ýAýð}voœ¾_n>z|y~\@²¸X˜_ôßIïoüº¤ÐB^ ĸßc<@|M·7Q@yæ{Ôü9¿?fLvI d’$㣠74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 0 ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ã{`@øàÐ@ðP@À@ÛühÌã^ª B»®Íò¿Íä~€ ÈÄÿÆOäH!€5oß¡€;÷QAJQEËɱývÀ0/øGt`ß:xÑÏÌNˆý°à ø @ÙðŠz®Œÿƒ%п ø¢Eûtõ3 €ægózú|ÄWÐ ~y¢o€FùƒZ ð€€À 8#8QíþxÛéüú q0À'‚7B…:8ffF©†4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn7ÕÁÀÐ@ñ@àð@@YþÂ÷õ#€3B+¯b(ð+=´[çó¯|·$~qÝ»(¡+ã6ñ·å úüŽ[ð4n$ð)‡®wרh ~PÐ@üÿOùëH[ôg ý½¸×‰ Þq’à þp!À@@ø@0@ýZD¾Þà HÞÀ À|ýzÀâO¤bñÙ͙DŽã·RÖñíèŸ+A ~c¬×P`€Ÿo¸ô|üÀAþÇhþ~Hâ#ç–Ï?|éÔ9¼ë 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0À ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ý |à®AÖÁôf8¼Áõ…?€ ;0­/è—úgÐT€d?7h®¯å¾”À/€@¨@ú¢ßûÑ"E‚é äÎ_µ(ûòýùg»ÊËMëì¸_ð­¸>¯ûA¯ïAí >¯ªš Œ V»‘_”<¿|ð´ööx%8òåß}ÏÌoŽ(ÿ ˜ @ ` ¿Úº wp¬@¡_?0xœŽ+ ¢F@Bÿ^o=ì…Fo’Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ý@@ @Ð@0À ôòÀ {8; `ÿ_ëùùü^£¸Î{äX"ûå÷¾_Gµ¹ma¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†õ‚æ€4€øà€`À  gÔŒ/í÷úrà¸ÿ°¿an¯ãHß>Zo·F༕ÝË]ևƸ>¤è¼(>€@p@`ðÏÜã°ŽÿÉÏ\û?ÛߟanìPv€Rí‹©VVÂøÐÐoçÔK ×õðGúˆø÷íÐ>‚p,_rü…€ã.Ûô ~ß@ @@õ @ó9äðB@ƒü  A À÷Öb‚ý{€?à‹ìðW|íÉ ð[¸ï¬§k 7ù©£FŒ0ÜhÑ£ 7h ~8NAîA ô€ñ(ÀBðð‰ç@&@"ûq®Ü¢ž©|lMö@ùPð@éà@€ €!ªžYò\‡° s`PA?lûwŸoœ€þBîoÐea†ý8  W€¼‚·Ö‘ì¥ëùà(pr.ôˆ_ÇíòÜ^<øÈ‚¸?øìÒ"„oÏ îABAÅ‘E3Aú@èÐpyɦ©;Øßˆû¨âð€9ù‚»ìÿî·†a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц6à@ûp@ `ø € € €‚ç€<8ÿ‘Ànr+ÏÛ… ëEŠsï|lRÌØ²°ÃxZ3DvDÿûþñ°9ý +ß¡b»áùå°Ë¹Ç¹îØe†l²ÊS+ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 0 ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£FmÀýˆÿðÞÄïÓã¾7;ò'Ûž¥å¹u~T¡éÊÁ!AïAð²7ú€&åùòÐ8b žx?ß8çË·kƒR¦Ë·,¨è° } |h €ù ßõ"‹D"g°ϧØèž‘ïÞ_   wxã ¿Œç·Õµ#{P@Ð@À@ @ò@@ @ €  €}:Ýw³ùÏùðÖ‘Hä^ÝÎïÝà »s©ïÏ6¨摺ŽCU†Þ6zxB_ðÜ”?[’&€9öm÷Ïì÷çÛíïÇ=cò{ðîÞî]¦>GÉe²£|ßú‡üXneý¿lȾy÷Ѐ€ @}ðŽEuñÎ×÷„ïãŽþÞß<ð{®³‹r 2ê7èà@nGAñÜû"‹‘ ×| Þ`#ÿÖÿ~Îôê“üÅçÈŽ)Íù†úÌý  H€êß߈ Bÿô‚zx|êì!ÆüœGꑟ"ò¯¡>Ý ç ëøÍز·êÀ|KÏ‚°`º?ð_ÿÎ1"€K(Õß±½”QŠ»eFoò‹Fa¸Þ„=úþDÀÿè¾ òþ€0"V+ÿ€‚ À‘®„ñÌTvô'Š®×àÙ¹[¾o~Ð}Á€ \èWÏ_`@ûB #}€t(¿Ü^›Ø¾»ØOßé+[›,ÆCØ)Ä  V˜Ž¥4?¿¸ð°@û€!9OþÚ8ë¬äàytWBé<^dâ`§Xèà ú°@ð@°@ý@÷¾¨ p ð¯ùiùõ€ü‚öœŽßùò(ŒDëM\ºZ]ʰ8¶úx 8P@Ð@ùp@öð@Àp±ÿpBÿp4¯È¡z)ï?½ùw³üÿpôïÁóÿ€„6}Öõçp¯æ%ó?øä‚çq1¾Š¼þY€ ꀾþ÷±dp@ý@>à€WîÜ „~ûñÈ~óKP~°€DÙùa†þ:+®|t(WNÈV;Ž8éÎáÎÈdžNÇdÌÉ#a¸ÏÿøìøO·÷ê!ßßÁˆ#çß§Pø@ ÏbÏ.ÃÀq·,ŒïAè3Ü;œHàçÀ£N™€ À‚å¼³øOT0 ^<Î;K€„Âþïš°Ã~¼Ÿ{hÏ ` ~Ä_®ù€ÝLàŒE÷á¤{^t;Có$oânœ” GA -uþø" WÿzöÛäÆ äXŒçÜV»,íÝù+îj#vÁ"@€68ä>¤?P?_áüCA ýˆÜ‚ê‚"ò P­˜p þØ¢h¡?îKÜÀÉË»ù{שa¿Ô–T¾£FŒ0ÜhÑ£ 74hà Æ0Ãqº`óà€  õÀ€ ‚å&þríŸNsR/ÓçáV¼SÝ(!ÓŽØ2¬±¹ÓeA,ÓÄ#×'Oþrï? ùn˜¸@ _Ob!‡o:OïÏ<€[Ûë¸vmw°Ã] ^b-søšÛ\hóÀ@ñßr ßpžß?$Nwª~çPÔ§·­i‰Ä~À}Ð` Áõþý=¸€´èÁXÀá$þtàI}`‰ tŸ/s5lo›$tDžÐ@ßìw·N†fwîQþÑíÓöùH`¯ç¸¢ øvKK xáÖ‡HÞôÛ¥‚À€-ï™îõ € ã°ÿØBG 5LBvôGå.0ÛšßqBÀ‘ íy;¬(د·}ŠÞiŽ‘Ü+^êò v¾X­S}=¿øGè[N4m³T€@ýo¸ ~Að¯÷sH.@ŸþÅ?¢Ë<~¾—Íü¿óÕa†ûàè€øÄp@ð@û p@ @ýß}Òûú€%è‹ðÐØ΀‰/ð#òNû}H‚óo{ÑNÝãE 3xÀüÆøÛ}4 ôd-Ä×ø~pI?[Ü?Oá v“ø ‚žÎ4__r;¶|ºç>ùŽs¸|ß2a†ãz€@õ0@­eEõNåüäÝÏ `¾€¸@cûõ~gdNt×¹7·Ï—7®v Í•¼×üˆ,øO·÷ïñÆþ»FùúùùƘ@ßbÏnuŸ¹d=¿Yâ!à>\=P>èÏ‘kûú zFœ`P¤Btø·èâ‡W/¼¹Ïo¦ÖoóÈ |P ~h@@ @÷P@@@üÀ@ýp'¾}ððˆß°ß° éï4\pËÿøöü<^;vOžˆÏë 䢎Ú..‚‚€4LýNHÀ€}ÓòòEöÌÉà9?÷ooÓۜà Æˆâ9"ÿ=¾~E`çö _·BÅñ”>‡¿Œ×ä<™l²0Ãb»"‚‘5͸ÇóáÏìóìûì“ùòŒýÞóíñ××ìø‹~lê÷©@ —}»Ú#|”?T@ @ @üP@Ð?`ÿB!`Ï¡]€N7@¿äo|sÐÛ8å-oÔÀ€ß  z( {8¡W¹Us AñÁôD¿é£( €×?8/·ý!0ÀWÌQ9 oËàú`€p@ý0@ @p  ÀXw¸@î Äç€vê ‰×³…{r,Sç·0€#«ð9†p@úP@ïè } h ˆ ~ˆ ~ŸÚ&á¸àÀ™3ïò*ß°¹>~Þ`„Âþ]5Ñùe‰íܨߍÙÁôè {( {8 ~p¿ËΘo@ ?íE$qð™¢ì#^wA þ¾¿6|¿0ÔÉsŒ™‘¾>Lü }` }Ð h |@ |Àª\/ñBþá0Ãã¬èî€3~(üÿ€‚ ào û\ÎL˜æú€ Î|yÅ̈z,Á» `"ú3Îö|vqõޤý€9žNö?úŒ0ßçÀ ù¦(vt—‘*‚&{Ši>T#6ìGbˆÖR¨žÑüpÀi ‰! £ 74ov¥°<ŒäÀ¤³û3ÞÀ÷ß@@ý9ð ÓÅ'{ 9¯‘{ îíìR§¾º^¶°Ã|À@ø0  íxÓNQÔúwÎo·D}Ú+Üè €aàƒûÀ?ÇÄÞ@ï°Ÿ#òÔK+}Dˆ=>DÑ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn7Ø€ _Ø tÀ pS¥pÀ@€à À÷£` @=º€”ßþmo޷߀Ç‚Dû‚Û0”@‚¿à¹€#¾àÀô¡@ý²0Ãt€^_}Áô mü·ÿ*qƒÀ€~þú H 8 ~x?^êxgOøþh/úu´Žýùû9ñ¾p˜Â/1‚,L…aˆPñï`?øúôHä~@s ýÄÝíöà ó[Ö´: çÀ€€À Ýò(°þ@‰bdMÜu®‚ú}ûòóþ} ôÎÍÊñVñÊ7—bûäx€  ~ÝoèYýýxFúý}ºÐ3E‰øã±|a4mÜÙôð ~` ~­´qÿ6àLÒ·Ÿ·ð¸Ð€ ĶÿsÙü‹'­úÙDß‚ëø’æ0Ã~¸Ý2ÊFÜ_ï¿ÿÏü ëð‹ç½@à8¿¨½ëu~Oi µÛvûº7Å[X ¨¯`@°@p@ @°Dh‰(l 'ûüD6û t öúk齇¿¦Öp@ûàÂÁí óÀŠ(°C ?ßq"Þ` CþºA_"ð€‚ý5Ïæ0Ãc¢÷x uHXh  owÓ´PŽA ½¹^Ì^áe€;ûþ€2ã—@ .óÙA£|ˆâ6‚.&|G 3A À." üýÈž\ýÄ‚Gú÷æ-íú°ÁaHÏ-#¡5ô€Œü?(¯]'” „?ߣÀB:poÖsöèT9¿50Ãq£FŒ0ÜoÓòú$;€> >D Á7NÞ0qåÀˆð† „NùèSiðô‰&2‡Vø }(®AÚÁAó®9?n#‚é€ iÙ„Ø?Ôçý@?™ ?øærçu1¿U‚€À@@ù`@ü _^n“y¬phÜò'èì/þOÑdqs@+BwÍœ»-5†øˆ G4,( ¨ x¯§}¨Žú=&l’- GÉÌ@šBz ˈ®ÿçt aާ·N"[×/o”<û|<aöÿ¾Â8tÇ ö#}>‚½¾=„téÓ² 뎱Ü;Œ“$3$’FÞ¤‘À€ À€ À€à€ -…’?ÀÀ °þûiöýÀo$R/½2WÙ¯¦d™šÃ ư¯çð³ùû}>žÝft+ÙïúÿýÈ¿R.í=Αþuîvèžßm¾Xç9Ù!’5ma¸Ñ£t ù é€ÁÀ‘»ã\/¢/Ð?TŸ ”Gb7§íåßz.™œ^¼w¹Ë®æ°Ã|æ÷®A±¥»P—Ç-´8(1±‚èd€:yÇ÷çøkó×"è#ÿî„…Foâ-îúÀÙ ¿¿Ò¾>~À€âß®ö.œÚ»ÆÇ¿i€¸wú‹Fà ü£}>‚“Ð@ í¾œ„xP@0?Ø [ÌtmzhO5„pC¯0 ëçqgSM1¾`à Æ0Ã[-4:/蚊H­9rGýÝ€´ÀÎ*?þÑ×5À€D@oà  ~op'ÂbÄWÀ€†ÀÀ >=Å¿  ‡ÿrÿóª1»àú$ð@èð`ö`œmS ùt€ÿ?ܪà _üaÍÅŽû€.EmeQÒ¼K€û>£C'tÏܻ؜ð0^ #‚ñoÿ=¾~ ÜDš$WYÏ$ÖoÆ‚8=(L€Ï [iüäÀ= Š ƒý½d3'™ÀçBÈ~&Œ×cÃñ½ ø ~µªÎÓ‹gÎCÿàtïðÀ³ã¬Ô”‹Ä_atìÿ9òÉ7œ’cq6ÿ€Á%@‚Ü‚ׂáM¸iîô>Ò€=ïûî»ùž_ÁoÂ8Rqë8œ|taÓ2È­àa¸Ñ£FoèàòÛò˜–ÌDøUÑŽé ø~à 3ŽU ;xŒì¿pû úfŠùÂlo¡‚0§æÒ‚äýù0û¯}ÿ8ÿ?Û\bU[(œöøWð@-ð‹ #uçï¸ wˆ }H |?ÁY§n '1À`ÛÁ†XApúõõäÐBÿ7{ÀѾ Ë‚;AÁ¾Ÿ"¦  ƒý ü€/œ›ç(ÿ°‡F®ÐWíœÐ3Xa¿Ñ£Fn7²¯‚Áà þEtoê Áôˆ$|jù!pŒëß­¥@BþöÝNoß{¬½‚Q‚ëâ'€2ù£4@€AЀ?Ûà‡ýƒý$â ¡rù=ÙŸya¼ € @A°^Àó€È‚·.sÑŠDÍþLØ@®\k“¯ï|8Š¿‡ Ê”œ)ÓØÌ/øVð` ;ö/XÖº/w|lwÈ€ 0௨°@Üö0Ž‹3ËÑZäö"Š>\³H{+åoF]а@ @ú€€€ ¾€Pï p@À @o`AûP@üÞ½ÍÿràAýp@’'/û‘ÜàJ€7ï3ûÏŸ.èS·Ç·Ê~=ºøÿ{|||u˜fuЯa_t(WNtáÒuúÌs˜džY$’à ý\>©ÂÁ§€ ÁÐÐ#¨p ~¨¿ÿß6•ÀA8.MA ü·9ðXÜ @ö.Nk-þôb ƒý"ñ€-ïŽO0œg@ „Å }mì‹!Íôÿÿ¿`÷ãÀç ?‚ù¶—×`õ€(" u„ì@o¿ ·¿ù@( ‡þüáÝø¿7ÿ¿À ¾ŸÏ§ÇúàAàˆ#ÛØWø€„Ýt)ÜpìÓñ“$Œ0Ú4hцè‚Ú(Ð@úP@ @À@ü òîæuü/IöùÒ?(b€[ôxö#ö„é@ÅMáûb7Û@ €‚p‚¨‚·‚Í‚å}i¿Þ€vß8#H} ÿ`ç{:”4¡/ã‘ÛE>¶@@ù ùœ"€9Zq.€Ibbø:ß’#…〠2AÈû{m«žä$ŽÂüààoÓèØxîDrtZèÙˆ|빜åúü}ùäP¡RñrÀl°E~þu†øã~…nǰ@ö0@ñÀ@õÀ@ @t[¸Ø @‚.öúkµöN¿¡|€'p«(¯wý’{%³¸Þ>p>Œ ?,=Cü\Þ\·ã²À‚Ú wŸ ü¾ÄX`/ o2 ï‘<Ë*ã&Û0ƒlo0:v(@`˜ G×\åÀ ÷ï4xétGçÁŒê!Úú¶Ó_¶Úà ò@AL--Eð à0O÷ø‰» ïã±a¢Å¼¿éôŸØ{ùÐúØÚ4a†ãF›•w"m’Õ–R¬8|°kFn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn7é`&‚`5o"J _ܸøÀ¸‹Ÿoý¿ýú·æ–ý\ì‹À z }Ø {'¾¯xëÜ>ôެ“ì/@& ðd ,+›€'‚„KXa¿„€W‹€KëÜ ?öh¿üí28· £(!H} C›Æ€€6#€‚€(J¿ùÅÙÐ_OÿôC‰ †<ûüußx”¹änU•´‚õAAÁ x°3` h€@øÿùÈ‚!®ùüµ½N‰ä>”=˜? „”åûøÏ` €´‹äø }ÝÝËñ®?6"tÿð±k  Ä\Îf`Ž9ÇUa†ö‚€=û^‚Ù‚ç´çæoÈà úàùü‘ €?f` W¿=A>âÞrŠÈìw„ÿšß:f"{ ">ø"¾8v&;¡Â>: WNÉÆI×ëÃÉ É$¦ È‚À€2ûþÿ_×"HİŸ‘\oæö,Göì¡óñ·6FoëðG°-0TYÅÓ§èÀ@ý\þ€'óM€„¿Û¹Ðó­Ð•uøì'òÚÁ³à‹øÀ>˜ºaäýbõà€ € €Wˆ~€;çL€…ÿlÎ=Œ€íoªÝ€+p | {h |¸1B{_ßÐXÐp`€u¸ЉÕïìÐÔ'Ð <ˆïooÊÀ€?責€gþ¹cÄ€2ÿZ4ÿëä‰ÇþúCÿröý=†ç7ÛÁæÿ¾à×€0M&ò¨X h¿¨ ~§Ë´;T(`@æ€2öçŒ ›>Ÿþ]ï-Ôé¾TGíìÔ~[Õ×0j÷·€6,^-Ï =Àƒü?á@ôPç‡dµëä#@ÌoÐÿP@ò€@¿  gôÜÓ¹Àà`€¢p?>üûßùØ?§ÇÜŸœ€2|YDðòù¬0߃L‚[@‚á|Ä^$?DĸïâßÀ!€súdîE‹Âm/õpíøÓ£|…» €ùÀ à À @ UêgèÏp@ÏðnÿàJ€À¿³ïý•Aý²‰îC›„à Æð‚À Á #oíEÿ€€0ÿ=úô ò/ÐP±qîÒ˜a¿¶‚Ëà@éŸø@`”Ð 3€@^•'å@~'¿€ Ü „>ïÑ?3@(ÌzÂ0 ÐÀÀ@úPrbO¿P ôp‡Z4€‚d^þ93"›nHr#ÊœgŸ-õž@è°@õ@@øªº(!UÁ#Ìþ`#€AŸ"ó@tß“Á À 5?""Uñ t 0àŸI@s „ÚFøëŒ;Iû9úXa¼­4a†ãFa¸Ñ£Fn4hц4a†ãFa¸ÄH®D‘… +Ä×sß\ôî{ã‡å~YÃõÙ»™°ÙL©d­`°p`€3yñÞXñD¨ï¸ïb?×ãÛœ å>ï¾³yÇRÉ–Æö„@ùÀ yÿP`€ @-òhÀ@iÓßðn´üJøÌ#“·f‚‘½À,{Ë•¹â.0ÃqÂû}ÁËÁåÜÀ€3 %øùÿë‹"€(aò!ðùøf€8"„þ•ÏÊEßbÍ…Æüô<n(@<ÃàpýE½ýÀ @Ðühp`ùP ¸­ÎÜ"|eèžþl(Þv+~þ€€ @ø€ë¾ßÜdòëÛèøÈ }¨±>8téØ&ö"@¢s¡YžP€%ÓÇ7äÖn4hцݤ í@€ €  ù»Þàú_ïð ݘM/ëvýïÇó$ ‡ÿÎqÝ_Ìn@ŸP@  @€< $õtô±ïýö¿”¿‹öãžòKnR«}Ø=»ü(;`>¸À@/ðqžðˆìŒøDO° ~»þ¾ÞbòÒ0¿š,àïž"Û8ú‰wױР« 7îÁ»¡ŽŽ*þ‘ù÷ò Äøà {Ý¥@ €ÿß{ Œ?­íç7äŒÉŽÌ̃¦dÉ2HT*!R7ÞðP ¯ü?T?M€=çñ@:Mü€$?ùÒ žþ~xÚX!€6R'Br[àì0ÜhѬ^ëê´¸Ð0ÿáÀþ=þ¿ïèÏb/×àCáE‹î{s¨x6呆ìËÿº{t=/çûš0@X¿ôN€>.kÌÓ [ñ>pó\ÊGèÿãszøx‘¿P‚äÿAÁØÁæÀ;ëɦexPß  ~ˆ²?÷ÛÜv@½ëýþäa\~# Ž÷9vp˜ó@ûO¼R1AGüîl7V¯Ð  !ßÛ@x@@ùžÿðBþ ?ÝÛñð „¾äH¢9Í߀, Ó7E'ß` yDÀˆˆ&"÷ðýîmH_íñ˜â1ƒ¬0ßËA@ˆ€€ `€ € €@ W€>`ØV‰x!· …þÿß5ïó²s2ròɼ;¾àâÀ€ à€àÈ‚ ýebJ•À€ ³³IÒ‰À.Á¡Ð€4KóåÏÅoß¡Aèż:Ø? >¤—»é3z茸ÀF{‰0š_ÅÀç/¦ù`!W]sÎý0Ã| @Ùìø>»úy¯ä?L‹È0¬"T,ÀÐ|LJýŠâ‘…\¥[âíÙPöp  0 ¸  ¬t…¼‚€0iŸÞǨ¾ý½ÀÀò|?ôÛØ³!Q¸Ì0Üký€5Pðß÷Û"ÈŸ÷Áç¬&‚r'ÛŽût<mÜÙ¶ú( |Ïp@íà@0@Ð@6Ǫ„ÿAñ=€{|s4ƒˆÃûÞÜ{φ¯Ë×Ò(¾l7a†þÛÈÛüNjÖò EøÅÎ ‘µL0ã÷_´& äèoË € `€à€À € µà €&qñ{}9玦;;ï,˜â¥¶úÖýYœ¦‚¾€7åò »€‚€݃ þAú¾tÓ–”0_^ã~na†ä’d’OÑ£Fn5À  >  €à €€+|+BH €4"¾ÝPô¿‰öãžbl›.,«*V¥½·x ~( zØ }ð ÿÑö“ ßP@;v$?ŒSòˆîPð@j$‰ü"X?ʸò;‰Äÿ‡Þ£ 7H }Oå;‘%þ}^ý+Üq?\?øpŽÎâ‚çÀê *ì‹WÞd õïìçn·cÓЧqǬs¸s±ÎÌÈd†fd’I0a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£FnÐ?N€?¯¯øzëéý÷t<À~HÂ…ó #€~Vûð x¿  € |° |è |è}~o` €€ jä@÷Lø?´ï¿ß±=¦€RàCÿW÷@-€„[æ@õ`€úÀå  Á…€2Œþ€?s ø¢¾ßP(\ï¾ €ó ùùßŽçø‹Õøÿ#ýþ®\ÇÆ÷@ì@ @ €éûB\€:"õå¤àùß`žÂ‚úùq€ÄpÀå€óüû ³¹ç¯åÜ–æoçàñ E‚Í€9×dYÿŠÈ¹ï‚à¸σý`€ 5Àƒý»Aª{»ÿþÐ-æpšhºgÜ@ðª¾ZD™ýéýüýúþ¾ÿPñ:% üüï?_¯× fA|­ü:L‘º € ‚HE8ÿ3ä=ïÒ(H~@óõ+Wÿ/ç.€?æi»Ç°£Dc÷W_¾|î¿kyÖn7Ðêû‚i‚#‚<2þ »úuõÊû€ vrL0UÞà߀‚‘7ˆGóÖûà?»Á¢>Að ‹ ŒŸ€à à #Α@€?õ¥@1þâa†ý0 &t"~é¢ûA „`€@ÜüÌ<›H ¸Ï¤ðØ,ò÷|?±sÏØã÷;a²ùnä»2Å…Q÷Pß°0{]`#¸ }‘§ó¯9PÀ@oêG.劲 ©No&@äè {à {à ~` X ÓîÅwÀ@ýBwܾèÇ¿Ùø :ëx~}D‘L¢N/›ÈÔÇöæoÀ@Ð@õ°@ú@úñ<ÕóèX 7¸à@âŸh@ý_¨ ý…>&d#!|ó.k}}ûh zÈ y%Ò-”€B1Æ“p þqüíoè€@û @ @è\éÇ€ A A ¸hãCßIàQÇ»ï¼ó »Î¹ýí7[ì €¸à@P ô "y£ëà8@c…a þxÿ‘YtÀ'(‹°öoóÓFa¸ßL?½>b1þph€Eâ}~sÔ B—€Q?ÑqàdÅøíЙ[æmú€>£¾A½ÁéÁßÈÄA;Iw‚:ÜÉåaœûrgÎÚš_àíxž4a†üÐ @€,€ AôAì¿´À%ÿ ^À nP0ÛØ—"üià†Ý÷ðFô€€ 8(ÐÈ §ž $p h°°{·óA ú²=¬íhp"\Üâcy¶n4hц4a†ãFa¸Ñ£Fn4hцŒà ÿ„pà ?À@ÿs–?¯r?Ø‹öòEö˯nÙì/±Nnãa³ñ¶Y+#}0§ÿõ¿/©[$îýü;ìœKäÔŽ,>ì·>T‰\%&èWtPöÊ-ðÜ'o¸Aã§°Ãx?Td”¤ðŽí3E{€&eà7Ýà€÷Ïéð˜ŒHÇið‹ý“üGhqœ÷0Æò€½P@ú0@€@ÐóÛf–¨‹ëÓ²O) À¤`Ïè äóÁy…Ø}¾*ÿ<}ôìLàAèDû#=…q„@á] wpì†OÆL’B›Ó{í'#IréBÁ먇@Eûõ´i2 }Foôé=@‚²q·À/ ¤{âfFd''ÿ@ ÝùU¿Sýú € ¿`@ö-ï m€€½ûû`$ÿìf,C´ân쵿,0Ãq£Föõ@bÁá€%fVבøœ?$PŸnÀ‚‘5~ñ3 ˜,X²7 ‚›c²j¿¸g–°Ã{ȢłÝ¦Ú)0¼Ú9HÐ@Ÿé>èÄPµøåÔx ìˆ+ª  rç"fL¼=35¾à—Õ'Ç}ù#N&&g]ðÀ?.|(SfB3° ’õ’d¥îÛÛó  P€@@p@0@àŽ6þûôqþãy~Eî€÷@ @ïfü‘ÈïNfNÞ_áÒdúü_ÍÎù#EºÐð}€3¹¤ÐCÿ +È÷•<$Xa¿Î €W€@@à@€<ñD€<Õ^¯nž` ?Øî@{XL›œûåož7§vÛ@ Àà€]€6ï‹ ¼T£Ð1·–P~?„lõs×ÌÿÚWv´¶ó  À@€:jÌѾ79Eúÿ¾¿á^ä_ŽÝ{u³·ñ+ó)g0Ãu÷Ç=ˆã¾øvÀüEsõ¼÷έ¿l(H±}íŠÃ ýx>ïÏÁÖÁâ@|Àï( Œ¨ˆàŠ\_î!Ù8Éç÷öçäG?|Ð1(ß• !>7ç@PPTðÈÂA‘ä`@€°_ì’'\Wîð è!È™y{ém÷@à@°@ü€@ú°@üOÿ&ç >à€$m#räM€ƒü°Já_UÀ„"yžä ±¿;0Ãz@@<?<ܘ¤ÏŸ þ'LèÉp!€8¯ÛØO6“(t{­°¦ý.“ï.AÝAÔ€#ùª—‚ öœ?,Âj¯~Üÿz½Lˆ_ä!ûñ]ò@û÷‹Ë5…p#€€Ð ú@ @g™À† C¤]À‚Šyƒ!íìÁ ºp ïÈh ~¯6¯@ûðÀžÙNDîGTh!ÿÏðAE°Ã hѶ‚ß7¿R$×Õañ§WòÈè®'IÏ‚…ñº(‚ýÅss†¸ý…0ÃwÓ€{‚Ñ5‚éñ@ž½ˆ€þØáȽh—pÌäí>>Hù/óœ§=×rÆý`«€ã}òAïnét@\?Ä>ìH1 €b±eÀ€¿ç Tîwgõú‚ÈŸ"ï¼}3k|ø´Ð h {¨ ~x |Ð } oFÿ0@ÀÀ €h°@¿õàAþ-ÛК¸ŠÄD:",æL']®‘¾‚B‚àZ  €”£Éÿ Àdð–ð9†g@@ׂä|Ôý¸è¬ˆ@L_ÿwÄž+à­ú$?'íØ ð€ |Èq¬°¹ªœ„£¯§« zëÜ‹. ŸÐÛ†·çfn4hÞô=ho° }` ~h È „Ð@ý:@^dDGê|‘Üÿb>›Ç}zâq(;w=%a†ðÒ!ùŒ^ZÌ¥³ï€`ùÀ Äv¬ð®¿U€ÎPO ÈÝœdß>ùž^ZÜ@p€  € €À€€·@KÏ?à@8çç½7@ÿ¤ýçÜŠEä¿çd.ëÞô•p ¸o@ @0â'°Ÿu×½ÿð0"@‰~B„ß/¾(5å½²üuweÝ®¯Û»[„à Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0à ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸ßF˜Šö쀬à€ŒÞ}ÿ¼ý@ h `€éÛ1ï{ï{ÝøFªçi;§Ï!1'8ß9k€ 81ÕpÀ(ßñSŸïõþuòE™óïôöë;~ܘçòìýHO~ffÚ‘†ýLî!AÁÍAó€è°ôYÐàù®ì‹ò?ÐD|&'¿Òºd/®—:Âs Ìu›ó˜  ×À @ ˜`!uÎ;OõŸ1à À€=z‹ë€÷¹Óð Ð]K¶yí»[„ëøDx»éüyüøÆ;¯b1ˆ+ã W°®3§ds“õ## 74hà ÆûØ p€@è¿`@ü@ýeç)ðŽØçî\ºg[<ógôCÿŸÿ{å'?(§ü‘s r·Á@ Ppàp@üÐ@ € @€6ú{;ŸŸèàîEÝåäAfÐýÌ͵$hÖoõ h } °@@ú@ý` @¿àß¿ß\F}ôÛÿâ½€‘€ð¦½à4Ý'€Þ ¾5a[›#~h–/À@ p@ €@À ¾þPÓ€'wŸp @€5"Ã@,ÊE½; „oŒ¿n˜nÜÞvîÖâ‚&)9|àÀ‡ûã®ßàŸOä˰û§BÍÐÚÙ˜a¿r¥ô;˜P äâP‚å=ìð@°ûÍ€„N[Ó Ç½{€'²?þè }ïü¼òÐG9þyþP Á üj·Ÿ|'³ÿ €õ´1 ~À ~{ƒ@&Ѓý¢’¬Áx[tň €-páÈ ö[À ~UíO8Nðž^‚²°Ã-hш‘\‰# $W‰®ç¾¹î{ã‡å~YÃõÙ¯ÌØl¦T²Vo` ~/¸ }È € x ~ ˆî"áº$ПO‘wx·®ðÑ]É:'­³˜Aé\þp:·è°@ €À÷ìþÀùŸ@þÏ=9öÏ`! ú ú/€Bÿ…óèב@k‹x!·n" ÆùpŸw&(ªoÅ–œÐ@q@€ ` ùþâû9(½ údpªNpÓ2pý¾jº×7ξŸ8èOÏâf ,LÁ}÷Ëï/Û˜î€Ã¤üd?va†Õ£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑœÏØHp@€ÿHÜ_×>ÿO¯ûØŠGÆþ`!Gbý¸ì_CàÝÍ‘†÷@ð€ h x° P 8 ОFt€È>ÜH _ï›LêÏçÿ>{öß|šFtóøšº,‘U¼ø ˆÈ’bð_ôÿü€,ë‚(Ð/»æ ƒý$pü¤_, Φõ’p#-â7Ñ]õ–¸è•üÃÀ-öp~\ð€€ÄGfyI ­Ý"w þP@Müšÿ纭ñÖn7 &ùà=P?Dè—ÓºÎ=ÁìÄ‚ö#Ôž‘\ýÔ?(©l)¼ûGd9›°~æ]™»!t¨¥ØÃ þÏÍjð@À@P@ý0@ýPÜ`ý À v䉮À3@)qŒ/¹Ïh3 t-¿;‚_AA @|€‚€-`ÿØŠÐïÏøïÅèíÜñÞÝò(^by®u²ŽnÀ @ ~x } ~¸H@ @ÊØÜп¸¯·×þ'ÝÿB0¾óüîùçw@…^xâ].¦DHÙÁXÀ A@ ˆ¿Ð@ü@üÐ}`¿~€{>Ñ]]ópgò'·Ù™@qƒhDa†ùÀè€ú`Îs~V¿_£åy¤>ç¡*(¸ŸÁ`Ñ|uÜxxŸ¶ˆö»®ßÝvÅäA@ »giIþxÔŽõÝ줨°þÿðððâwæöo ‚ôPŒX»]–®Öç‚€2)7`˜°Gûیξ€>ÎwÇ_a‹pºý¯~Õµ[; 7ïà€ Àø Ì¿€ø ù € €¢wvïØ°@ ÿP ü?ËÜCÿ¼{‘Þ,ÏÓ^ïnžAåû»:4ÌÖøFoô°@ò°@@ °@L€ðO¬þ=ph¯÷×ÿî-ÿ ½Ýò(˜G"ùÎ7vôîÞû$ÌÃm’7Í,ÁéAðÈ<¸ {`quÐWÐð ûù×?.pb:9#õÏ.s®¹Ûúìr„7Íš0Ãw0|€¨ À >‚¸™Ç·ÓøëìøW·ÓÛã¬`Çж–}µk}ËïìbJòçíî/6\Y~€@?ÿw\ª`ȯŸoöÈ| Â?ÈVù£¸óÇBÅñ˜ºþxã_&ë³ µ c 7ê@4ÁÁDLAù€ €¸8Èà‡ýÞÀWhµ‘PøˆìÖÐúÞQ£Fo켑` sô,ã Qä`@-s¢ÌCAþAøñ*˜€&‰BkwàA €‚¡‚*€%"Ï0þwþÀÀˆÍ%qЗȭ [ˆ¤`€¯÷¯ãx <ó³l­ñ@@P@° |ˆ {è ` H ~oȾÌxñßé¹ïõ"¿¢cˆ˜þH¿.áÎÏ<éÚ÷×9ï~ä-è €/€ÀøDn÷î,?PˆáÝÑ\}'4¿ÛïÁ¤s‹?Pdà øð@ò°@0 ö€€Â>áïŠ'üœȘÅÄ[Ð`àùöx÷`½yÇë¶9û,(Ù[¤ò€ Œ2BdûrX¾À€ šøçP@à À ÿPþ_~ª½`y/“Ðé´€S›€>¶HÐú">-7ßúE[ý¯ã~à ÿ¼ÇñíÖeÐýã¡NÚÞ}†õ@p¿`@ëÐ@û@@0@üðàâøÙsŸb7üP ¸—ÿ{÷áóŽp@dO×þàÿ¡—Îýw×·Z)àã|½¤.¥¤6oößè  P€éf{}@€:hGúÿ=»2eÛîþ…n¹“_µÌ’Úß$ÍK+à·ð˜PÀ Hà¿à@ßPÿt{ÐÇû€Q;{’ÞìÊd´ú‘¾X€–ª–ì¸Ñ`Â;FtóØOß±/äX½Ç`¾ùÞy[n½ûwj¶ @à@ @ @Ð@þE‚€&$uíœöuõ÷˜PŸô‹Û 7ô€é ΄å6Áòò?°@€N"ËO@,ùøø›-ï§Š#>€[™*Ë#|ðŒä` Ð x0@  !%æ?ø)Þbÿ¤R?wûòð Èødº'fì ̉–ц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fo¨‘ìð@ùP@ú @ @o~0~ö?¤„‡ÐW¶Bè!¯¹ËŸ ©  …þÝùuˆßh›ûr"†¶fY6\çà¿}‚å9û€Uö# ë~zاàGð¡×|Šx€o;Т«¹ÆøwÁ'A Aèã;ùÊsüxžÅ?€ÿøëޏÀQw{ÏL+8ã#xÖo }(  ~ˆ ~¸ ¸`` € €ý;¾DæçÎã¹ÁE?®»fXÞì?8?K‚ˆýÁ A Àˆç ¿°@üÀ@.E p( ~ˆñO€ÏÂvùóôg½ï@è`íÇ“:/2ûÿ}€Šÿ€?÷y¨¸/޵ü`‚1¢oÛ2ØTã~Ÿ>€½ˆÄB0®1Ýì(C¸Ю8vN˜™3"HŒ0Ýà€‚€4öÏ׷Ѐ^Äo·N6ù¿zòpyåûÏ/ºõº÷íÛ[3FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 7è@õO¦ŠôÃéÒŒÀ/ô?+yÀ†ÏèÏ»€90#oSæß=o¿ç‹x t¸@û@!xg6X“û¦`#¤Ð~Î@ û |a†þpwÇ€!À€ €N@ß@ÐAýâ+è ÷ëÿ÷ÏÀ†óz#Fñ ¬ C A @ ½¸€*è˜P €"û}E¥ÐDW[# 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0à ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn7é`ª`®Yôçhó:þû'e˜0c„o;ÿ4Ç|Ú{~ioÔuhø-ùî@ øo ’`‚Ým€±†ü¾Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn7êaAÀ­ s¨O øïÿ¯eÈ88›@7å–ý`/€@ê€Àñ`ä@FÿÏoà80¼ÙO/÷wøç?anÓÁ ¨·Ÿ Œ0ßç°â´¬Œ°H‡Àh  À?ëpà ·ùõøë&Ÿ;{€@û…‚†‚Û‚Ctþf3þ@V Mÿvʪ_`—ù²ŸIÉi67‡a†ãFa¸Ñ£Fn4hÒË6Y`É`\a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn7ÓEˆµÇaÑþva× ~À€`Èþþj€† ìûê*!¸_ò'Œ";­-ó°pï€ à €9úñ?}‘¿ßÐdo·×߬üÐBž½þxè_8xÙ»¶0äÁÜÁEAÁì€%ö&ç=€h ~PŠøAžn@àý¸|}z|¾ñ摯/îbtßš)Ue!·ë0@ð½ªI‚Þ"ùï‡y€5IýûòE(ÿ»œó(×ëî>¼XG€ÜåÖ?+|Ð;ä°@`A0@p@úää›> ú¾à¾œ{ü Pâ¹>ÿ9ѺýãEr'#¢Gcß'› 7ñvŒDŠäAP±BøÜ\õÇnðü®Ù\ü|¹¹±,J±+n4c۟اsϰ?þÆ=î͆ÊfîKd±aTc{Ð@óŽ@Ù€#-I~»oãxn•¢Hà€€þžÿç¯/»ÕÏä#’öÓ·ÑZà óäˆn4àü[®k€ÙópŒ€ƒ®@¾ÂÀ€ Þóÿ?›‘ÝÍw4×s09ñ¹€€ înîÒ†]@ý{ç_ßPäp„IòïÿÙ¶9@üûÿýÈÿÞxâÞ&ßšþßäm‹[` '°P@@ñ @‚è €9߸ ~˜ ~Ðz,—çß(˜ˆìû žÀ ½¦ ݆I0ØìŽsy¶n4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa€ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hцê‚4:œ‰,)Ïþâf“¸ôùìŸìûïï³ø)HÿTÈç òÏ>u-\s¥µ¾øn2 >`ÿïîb‚+‘]ç¿Çó°ßWnû8h‹ÏÏ?a=:ªg4Êó$(=ósFoõˆ |Ø {h0@@  €‚æü _ØP™<;øëã‡gSK&ª¬.©mãËAÇ ña° xŒ'ysæO/á®|¯(ó ðÆüþцúð P z° }¸À@÷G‹„("÷ôÎ``@ Î9bþ}Å4b„ÿ\+N{àÍAæÖù   à`@p@Ðàà@`„h÷¹ÿn°Ñß©Nÿ~y½ûædQ{!ŽÁó2ÜÆŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0À ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Foè@Ü€€ Àက þÞàøÁôD8wþz,˜À@°@ünú}í©Hó‡Úœ&o¿ ˆ·rŠp R·®,~²&~~dÝÉvL"€ê€4bÑõËßZ}ÁÆ€Uî+@Eo˜¨k„ñ¾y[ôÐ!ì` €¸&„°¨&ý¸€šÍ´<ÀŸ €>À€ ÀùÀ€ ö@ æ§b€ñ€;ùjvû˜„RTG'{€9sÜ&®ÑÒ»æhùa¿É ô€@ì°@`€@ó@ ð@ü@ @ü `ùžÀ €ý¿½€ ÷~x‹ñòëØ‰½ðErì/ïÿ–Ÿ÷¹³Éc|Ý¿\õ‚¯hN € õ4˜a¿Èí4a†ôé1Ó=0'%@óç­p¿   €,"@ÿ?žü_ž4èOb³yÇSâ•+}ü7(@Ü>dìÄ€ ð }˜ ~_?G|€?(à?·É¾¹ùd#þ>$¯{¶/\¦nPœ¥Vú zh }ÜaBrÃbs÷nG<tÐ`  ‚êÇñ¤P à ~8¯·{æ`òQBÀ(šN"üÍ"õÆ7ÍA%þ) yš¬þ€5oïöpÈž›þùð € ¾ß"À ÿö$ŒççŸ\ßЮ\¦^|¥a†ù@Æ€€tÔ=¼¼ˆ ÈÝúwHš¦F#þ9ððØÀÿû{‘Þç\ûñ7Žz{òU×>eo‹·ë@@.µAë%¾ýÄבÀýÁ€'&#9NEàŠôXf|‘ÀEü;’9‹þóϳºss‰ÏÌ{xà@ð`à AyÜAð@ö.Úþþ ÁòÀ‚'};hŸûÏ7ÜKè †‘¹ß âv‰åú'é„á>×úyÖoóûFa¸ß\vutÊRuîvÏÜŒAù"$,ÆÇ ùÿ€(ökǰß_",÷w"„üýçD:íqýrêç»[å ø€ 5ï@EÀpŸÏ»ô~ Úçû,ä @ @î0@ùÀ@ù`@ø0@üÎÿ¤qbˆ‚½À âÀ/@ûoÏ ùÇ+úýõÿ~„B0ˆFÝ¢…9ú^lÌ Foäà€`À€ì`ù€ € à$_@à èh ¸  À5çìtGàøï‚/"½¾~¸ O·(/©®×¹ù–Vøk}Ðü«ø0 >Ô>¤ð¿ß²?×ç@ ‚)‚à€€3FUžüü8 -€€ €ý½9¤n¾ò¹ë{5ál0ÜØne¤9+};4X t}¤|”> õøìøtŽ`°ýÞà’€€8÷´Wøø½„  dLìGoØ’°Ã}0@ñ@@Ð@ýp@ô€@  `õ_Éןzà£ëð÷‡€0ZNèŸü„_‰¼õcðQè']³\\Ùí@³À€  €?^ôDˆ„@¡9íßÔÓç37ëüëïqx}5.¸Ñ”loª‚ZÊ€‚Y‚,‚¿Ô‚Ù.þð»óß@ €€ùD`úOí~=  oZðA.çþøQNŸÁ=‘ pð‘sç-ù¸DÜ,p`‚À€7Ó'zDú À÷ç°}‘ˆ× ö×vîä¹Ë«yàCôÐAàMù AðMü@A`Mþöþ„‚µã96çAä@ {š^p(°ÌPPÀðNÅ_È €°=ñØ>¸„Ný# öàŽ.ea†ÿšÍ܆,þ¶‚O/‚Ó€€9nïo‚? ¾€ óÁ Ÿÿèî/âòî3®þ¤I–€O¢=òaíÎÅ(z~`\šTÄFþý€€ .'l¼‘¥36;úE k¶=ÛfÃ]²ÊÞôúCÔ~@”@4„@Q†á¢‚2Ì$X®5X‡¯¡ïüè €¡` @dÿà®è!ÿÇ Àlïx»°Žò o(ßp2 äÐÄßÒ'‚h7ÃØa¸ßBÙdþŽ|ÿÀÞ7'öþ~Ÿ_pˆà€€øüÒ Çåà®þ€À€:ç¡7ÛÏ:|óÏ¡: ‡ÿxß=oÞ õà ~”AÛAË@/"ÅNÈÀ‚÷€€  €ÿ:×ÿý€hbÈ!Ò‚ó0ß} a†ÿ}~ý€‘¾¤O~4íà_·±¡";ãg•€sÇnÊ,²²·ÒÁáA]AÞÁ4ÀüüÝýî€1V$tn¦w´ƒh«ô‚ÿ|ói¶‚ dÿ µ¾hà Æú8 vp °@`8 yØ ·Çù¾À €î@‚æÿÿÂ}¹ÐÎÿÿG,}~ø°{p°Öå¶eoœ7ëpCAàpMý€AþÐMý`APMÿ¦oòCFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F` ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£FoõP!ì` €¸&„°¨&ý¸€šÍÓÅK¤ AÍ|Êàp`€À >ò¾„}*P0@ý@?àHT#žSêöàç¢Ëþ'·0ö0@\BXÔ~ÜÀMæüðvPè>ÀPÌ?XˆŒýû€>@\Nš¸™¥\Éüàô°@»y}s²:üm~Ù# 7Õ9M €!Àä€`÷_>6 41$à€ù  ‰Úc­'ÉüèßîF/ø¦øÞ=þÿ'loЇ½`ƒëÀš€‚àAý@Mfí‚äÿA(hÀAðïÈÇ|€7R/Ç"€>$ˆ@2` €è ~oÓþ&€:í çéÏ<×:€ úsqyv‚÷‹bçŽr·5À€ï]¹¹°Ë ×h ~­–X”ª æoò3kÕ€ø€`€@úþ #.L·½PFLjëøE}½VÈÝ€ß8ôFÖ«èýæ|‚Ö€3<}íºL?úÀ@ý?¨AZpgÝñ†ýZ‘®škˆ×vRëÄ‘À‚Í>¶Snðè ~(ñp ôh°¯ïÓïõtüÖüt߬õ¿ÚØîA _Žpÿ‚½w€ zåàŽZl*‘ŠÃà0?b;§7äönfM~K`l–¥°ó£~Ñ(ê6n(‚?Xî>ÀÜ€!Ñ\+f`6øø ~@ ?‚±:?òÀáo“~.oÙÞ( |@ }À q'°¯yú h PÀ@üOu¡÷ò ‚TAõv"ß̨àžÂx¸Ã þ7hÑ£ 6ÿáô¿ÛùóÈ$‚9GÀ½€ÎÅ»‡ÐKµ¿z‚Ï‚×2g¯*,BïîLï€Ap @ýõd^CœŠÐ–‚¼YHá_~s~[…|é—Ÿâ‘÷œoÀíôP@`@zچؘp¡Ì@ù€ù ‡‚:÷w:ïî_òûß“iWÃ︯l䩿'˜—Xa¿ Š^âä€xSôÒhçÔ¨P‘OøÏ·yýú Îÿ†ý¿¤nçÏWω)V7%¿x‚‚Éüo‚¹¡‚*×€2û÷Aþ»@EȰ` ñ{iب«‘Pˆ—þþò(çÀÍœûŽ#ç8dFæ “†üe›,°&JS 6Aþ‰À›ÿ† Q¿[‚Ço³‚ h ð‚oö7äCÿ¢p&ÿá€oÖÀ†FàƒñÀ›ìà‚€šƒü ›ýŒ0ßä†ý_÷pHxDhœ$‚ä‚äýþÿº@#°`þ!`‡þB÷ÿöûøoÎmú„ÎË{‚€'˪û@ÿ€ @w˜Šõ€úÀ µQ<€ ƒXa¿‚6€8øNyÍEôÆ‘ÁÀ€è€-#ó …ÿO/êçü®(˜‘OÐ1!èïq_227ϛҀÿ¹ÌМVû¾·àÏúš÷LÀ ¶qÏ)¹H.ûfæÂã{`@÷@ø?¸ s ð Ð ~° ·ðŽUþxôø9ø ~ /¾¼ˆ+«ÔÀŽÿv³ß³ëÒÐ?Ò¹&Úà ü‘£~׊ Å­ÆeF¿ã € Ü2ú€4hÆ®ŒÀoÉÿwÁœÛ€ D kõÑÀ`Ðúߊa¿@~š! ¿$ ž ¿ˆ L ¿Þß«A1@@@ò@@Ÿ‘þ€€%s‚¤ÌÄx{¥#óøLm÷ø+úž_îŸÏÏÆ¾¿27å CôÐAàMù AðMü@A`Mþöû¨!’è ƒ˜&üÐ þ&€x €ø&€K 7Ò8S €=À€ÀA3ê€|è\ßàh( ÿ €ßo|É € ÿ‚hh®x_ !ÿбn,Où9¾@} !ê ¿ J  ?  (ßi²)-y°+_X'Ë{ýA :W€ú@ .C|Áö€ À¹  Š ßÎàî:wó@*#ëxP@ôÐjØZàZ«ØSÉ»À/¨ }ô·üN£cÍ4p÷Ûø•ÀÈ ÝÒÈ+‚~ÜØ#Xaº ƒA94Añ7ô‰àšðöû€!h ‡ &€ˆ þ &þ‘<@1¾à ôÁì€h |¸ P x }àÜvþ´HŽdPcóʇàPðßÏ _):ÃzÆ=ð¦ýP2NÂYLX€E'÷÷ÿÐ@ôp@û@€Þw@Á€dp ú¸¼Ÿ®v×@Bÿ®Å‹atöùÈ |5ù öS»9û »ús€à }PHjAT@A®À ´ˆzàCÿ»H$H#üEt.·Ò›A( DÁ5$ÁàAìÿ€û{Ð@@‚ý»nWýûh¾o8¿ ÿ¢Þ+ÙÅ "vuìâc 7βfð@"@à@û0@ °@ýr5Úœý€‚Áÿˆà?AF'¿¨SÀ ?  y|£´Ð„gh{~Qoހ㠀 Ÿ@@?€@ë@@÷@@P@}}ˆä·°_ð  € ~p ~‹€ þ>]íØ·Ò€?À€ ]òYòÏ¡"Éê˜ß‚Øa¸ß­Á AÁ7öûA7õA7þ›òC~·0YߨíßÖUßúa†ÿ$4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a† ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£FoõP!ì` €¸&„°¨&ý¸€šÍù°ÂA”A8@à@¬@l@1¾èF ‚üÐë @@ü@ñwÃÅ€*{.#¼xàDÿ‰.P@ü@ýO§¸"%²‚½ú*7ÁÀþ@)€4wöì»'doø À@ÿ·×ã¹/2R8€¯€B…ôòôÿíÄlÙOa†þ¬Þ"OK{Í+ù'L3D` }ß`€ b€2RGó¤Ð@>¾]€/ ñ°õàý'AqEûë~ºP)€øí˜€?Pïڛذà A'z0  tži´sýø}À}ˆúo}ÚFÿÛÈöþn7ìÐ@ @ú @`@ïÀ@ @ôp@üð@`@ýP¢…wÖ¼ €@ ~ˆp@ýw‚õÑŠÀoö€9äˆÁ8M˜o߃Dû{"°ÃŒšfMÜ–ÀÙ-K`CFŒ0ßî@@ý`ä@€€2€Ù@ƒý}øæÌц^¼ø¯é:{à ‚7÷ú¨E"ý€ uó~Otã "N·âP@ü°  € ÷ úÀ€€à=øÝèŠà˜ @ HŸÐßà·Y „]ô/+’l×lC·6Y[è €#à€„P@í`@ïÀ@/·`‚P1Þ‚ê‚ဳ°@áNw`ƒýB]A=Åš/êȃ4ˆ+_³2 NÏ>[õ˜ ` u¿ó@Ÿø X` |ÅwÐðÀ €¼€4Ùàð@×>Ü øßšõºEv€[’¬Ù‘a¿Å è@€_Ð@ó@ `@@û @üÀîò‡‰J G°P?ÿ®gÛeJØ^õH›‚\nDy=ôÉð.Gä­ú`€?AÀ€<`îê€ê ùD^]My<ïö  ;Ÿ~‹ø0+‚æëÈÿç:“Ä@A[ß¿×ë$8±½X P x°@à@îà@üÐ@ù @ø¢ÿýð÷ìó<\þ‘ û"(þöàÒ~ö#ì/ˆ-Êe'éêß‹#€KW‚*‚(‚!)˜ì}€&,H)@ÿE˜ìIŸÐº/Ïëõþ¿3¼éݼ´ë¬Ç:0Ã|0Ãw€1Þ€@ 3p™©¾Z+@€:uýÎñéqw¹Ø\|Ô°¼ç[º¾ý¯@]±;À¾?Ð_@ ~Øôõ×ñpˆ§;€R!à!ÏW¼Ê½ âñÎVà€0w   r)ò×wÀpÏãø|Ã-1äR8¡W6DT¯Ô­û´ˆ;»@û€@Ð@ü!`€ p@ûlw ¿à€ Àú ýùàˆ§ü«’0Ò'ýÿÃcÀºûˆÁ<Šöç‹Ìµä羫 7á> Af`ŸˆF˜@€ú@€<€6ãÿ¿Rxê‚Ð@ý'謀Àšî¼ÿÝ[ò¨”A8@ô¶r¥“Àà€‚@¾kæ `‚@@ ÍSAþÀ*5[öP P zÏФ<˜>>|=ˆˆoäìP ~@ÐÀ ?à @‚ê€#yýóŠ @‡øà_dNÿÝ‘)@«ÞyJFÎçoÆüO$™$“Â0Ãdÿèœ ¿ø`Åõ°!‘¸ üp&û8 € &€À ÿ&ÿc~I–w]%¯ý¸1Fý*`Ž H¼>¼ܧۡ|¸Pò8à@ø@°œ.ÿõ „ú\Û ¢ùuKA _ÿåÖà þio¨‚P=ú=‚­`,EýÎz#‚æöXE«pïí움R(ž,öä€XP¿ý÷À åÍa¿Ó€ß`@ Á~À‚O‚â:øï6w×ß°‘BE‚ €#Îe „ ÿQd_p@ýŸžÂz¤~ûä_l+Û[ó~­ÇÐøÉj”2KØ‚?]ˆì €@!` Àý@€)û}~~;.4@p=ëÄ‹àÜíôÑbêÑf »Žo¨Ù‚C‘ú$è «€0!ÿ Ò=#'þÌ÷[óýãËÉÆ;1†ðÍÐ@å9,£drpœÜýʸÆ?Û¥ÀÍ›,¥Vü‚ß±ÁbAÓE‚ˆ)Љ”:÷H¢Ü¤‡¢0 Ø ~° ~ °°@üߎHýý»äP@r<ù=÷øíÙËÝÇ 1¿0Ã~€ý4B8~HA<@˜½¾êdº æ ¿4?€   >  ßDÏìÚ¬êP±2\Ýöÿ7¥^‚è×ÔNelþ‘óþàßû’ºÐ xþ½Û§7×Á#ÁçA{Á$€Õ€%îyp ¤`àõ€ Aòÿàˆ½à Ÿ£€ Üu,ÿíÂ2Êà ü\L³üJ$€„žÝÉ6šF0×ÔdÀÎÝN¸€Ü‘Hü`^âôó¬óLàO;å‘¿}7°À@û @P €`úÀ È«|úœ€®‚߀*ÿ||û9À„ëÈ ~x¯``@ ¹Õ¿ù»þúºï½áóHÑ¿ž÷@ðà ’ÁÐAðÁ€î;]°þ°lƒz"ýnk¹x à‘`?ÏœO÷ æ•Ö€‰Œ0Ø2 äÐÄßÒ'‚h7ÃÛë`€ €öà€`€5 €À€@€öÀ€@ úǽÝ3hcöà??û°ŠéA„€‘ßDv꘠›·É„“ˆÿ€[Η®-ëŽIý¸ hìÓ_Ôë˜a¿×]‹J>ž2ø_|ê¸û3€P }®þHà ¼·6«¨ wDOgÂÛå€=`€À€@ûù@?vö€€€ð`€ €)à ÁÈÁØÁá@ Å ÛºB(H |Çýßpgò @¸Œ?LÌ  0˹¿Ïë²; oÌÌ0ßëÀ@ @@@Ï €à€Àõ`€   €'n,_ô×=ÿ€ ~€¯ã„;|€<fâ0¡§è ÿg]€|ÃäoÎàð¹òÔ\}ßÌßߘÐ@ïø àæ©>PAþ="(j/e@CÿA:Dï·PêÜÀ@Â3ÓÜÁÁ=õÈ_žè }x ~ Xϰ¬@EþÏ ÂÀ| ?îzøuo­%¼#‚ñ‚>')‚Îêˆè_<8Yìqü/+)#û=>þ‘¾û•`¿7ýðE»ùVoàúÜÁdx`´TATé¿$7í@@ @ñ@@`@ðü pø²/÷ã¢0 H ~†Ó}@öaÏd^À ¤ŸpÀÄ~Ü=¦|}€+¹Ÿ–oñ[FýÔt¢þÄ—þý¾ ˜<ÆÑ€*mÖìÿ½û‰õÄ?žE €fš tÎ Ð ÿb í]ä¿Å0Œ0ßÃÚ4hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà Æ0Ãq£FŒ0ÜhÑ£ 74hà ! ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`" ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`# ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`$ ü>Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£Fn4hц4a†ãFa¸Ñ£F`·xine-0.9.4/xineI18n.h0000644000000000000000000007341311216233724012752 0ustar rootroot #ifndef __XINEI18N_H #define __XINEI18N_H #include "xineCommon.h" namespace PluginXine { static const tI18nPhrase Phrases[] = { { "Software based playback using xine", "Software-basierte Wiedergabe mittels xine", "",// TODO "Software per riproduzione tramite xine", "Software-gebaseerde weergave met behulp van Xine", "",// TODO "",// TODO "",// TODO "Xine-näyttölaite", "",// TODO "",// TODO "",// TODO "Mjukvarubaserad uppspelning med xine", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Live-TV SD video buffer [frames]", "SD-Video Puffer für Live-TV [Bilder]", "",// TODO "Buffer TV dal vivo [frames]", "Live-TV buffer [frames]", "",// TODO "",// TODO "",// TODO "TV-lähetyksen puskurointi [ruutua]", "",// TODO "",// TODO "",// TODO "Live-TV bufferstorlek [bildrutor]", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Live-TV HD video buffer [frames]", "HD-Video Puffer für Live-TV [Bilder]", "",// TODO "Buffer TV dal vivo [frames]", "Live-TV buffer [frames]", "",// TODO "",// TODO "",// TODO "TV-lähetyksen puskurointi [ruutua]", "",// TODO "",// TODO "",// TODO "Live-TV bufferstorlek [bildrutor]", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Live-TV audio buffer [frames]", "Audio Puffer für Live-TV [Bilder]", "",// TODO "Buffer TV dal vivo [frames]", "Live-TV buffer [frames]", "",// TODO "",// TODO "",// TODO "TV-lähetyksen puskurointi [ruutua]", "",// TODO "",// TODO "",// TODO "Live-TV bufferstorlek [bildrutor]", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Buffer hysteresis [frames]", "Puffer-Hysterese [Bilder]", "",// TODO "Buffer isteresi [frames]", "",// TODO "",// TODO "",// TODO "",// TODO "Puskurin hystereesis [ruutua]", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Buffer monitoring duration [s]", "Puffer-Überwachungsdauer [s]", "",// TODO "Durata monitoraggio buffer [s]", "",// TODO "",// TODO "",// TODO "",// TODO "Puskurin monitoroinnin kesto [s]", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Buffer monitoring mode", "Puffer-Überwachungsmodus", "",// TODO "Modalità monitoraggio buffer", "",// TODO "",// TODO "",// TODO "",// TODO "Puskurin monitorointitapa", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Once", "Einmal", "",// TODO "Una volta", "",// TODO "",// TODO "",// TODO "",// TODO "kerran", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Continuous", "Durchgehend", "",// TODO "Continuo", "",// TODO "",// TODO "",// TODO "",// TODO "jatkuva", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "OSD display mode", "Anzeigeart für OSD", "",// TODO "Modalità visualizzazione OSD", "Weergave-type van de OSD", "",// TODO "",// TODO "",// TODO "Kuvaruutunäytön moodi", "",// TODO "",// TODO "",// TODO "OSD läge", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "X11 overlay", "Überlagern (X11)", "",// TODO "Overlay (X11)", "X11 overlay", "",// TODO "",// TODO "",// TODO "overlay (X11)", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Blend clipped", "Abschneiden", "",// TODO "Trasparenza fissa", "Samenvoegen met afsnijden", "",// TODO "",// TODO "",// TODO "leikattu", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Blend scaled LQ", "Einpassen LQ", "",// TODO "Trasparenza scalata LQ", "Samenvoegen met lage kwaliteit", "",// TODO "",// TODO "",// TODO "skaalattu (LQ)", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Blend scaled HQ", "Einpassen HQ", "",// TODO "Trasparenza scalata HQ", "Samenvoegen met hoge kwaliteit", "",// TODO "",// TODO "",// TODO "skaalattu (HQ)", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Blend scaled SHQ", "Einpassen SHQ", "",// TODO "Trasparenza scalata SHQ", "Samenvoegen met superhoge kwaliteit", "",// TODO "",// TODO "",// TODO "skaalattu (SHQ)", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Blend scaled Auto", "Einpassen Auto", "",// TODO "Trasparenza scalata in automatico", "Automatische samenvoeg-modus", "",// TODO "",// TODO "",// TODO "skaalattu (auto)", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "OSD gamma correction [ 123 => 1.23 ]", "OSD Gamma Korrektur [ 123 => 1.23 ]", "",// TODO "Correzione gamma OSD [ 123 => 1.23 ]", "OSD gamma correctie [ 123 => 1.23 ]", "",// TODO "",// TODO "",// TODO "Kuvaruutunäytön gammakorjaus [123 => 1.23]", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Audio mode", "Audio-Modus", "",// TODO "Modalità audio", "Audio-modus", "",// TODO "",// TODO "",// TODO "Monikanavaäänet (Dolby)", "",// TODO "",// TODO "",// TODO "Ljud läge", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Dolby off", "Dolby aus", "",// TODO "Dolby disattivo", "Dolby uitgeschakeld", "",// TODO "",// TODO "",// TODO "pois", "",// TODO "",// TODO "",// TODO "Dolby av", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Dolby on", "Dolby ein", "",// TODO "Dolby attivo", "Dolby ingeschakeld", "",// TODO "",// TODO "",// TODO "päällä", "",// TODO "",// TODO "",// TODO "Dolby på", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Control xine's volume", "Lautstärke in xine steuern", "",// TODO "Regolazione volume xine", "Xine's volume via VDR besturen", "",// TODO "",// TODO "",// TODO "Käytä Xinen äänenvoimakkuussäätöä", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "No", "Nein", "",// TODO "No", "Nee", "",// TODO "",// TODO "",// TODO "ei", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Yes", "Ja", "",// TODO "Sì", "Ja", "",// TODO "",// TODO "",// TODO "kyllä", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Muting", "Stummschalten", "",// TODO "Muto", "Geluid dempen", "",// TODO "",// TODO "",// TODO "Mykistys", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Ignore", "Ignorieren", "",// TODO "Ignora", "Negeren", "",// TODO "",// TODO "",// TODO "sivuuta", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Simulate", "Simulieren", "",// TODO "Simula", "Simuleren", "",// TODO "",// TODO "",// TODO "simuloi", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Execute", "Ausführen", "",// TODO "Esegui", "Uitvoeren", "",// TODO "",// TODO "",// TODO "suorita", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Get primary device when xine connects", "Automatisch zum primären Interface werden", "",// TODO "Mostra interfaccia primaria all'avvio di xine", "Xine als primair dvb-apparaat gebruiken", "",// TODO "",// TODO "",// TODO "Käytä automaattisesti ensisijaisena sovittimena", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Support semi transparent colors", "Unterstützung für halbtransparente Farben", "",// TODO "Supporto colori semi trasparenti", "Ondersteun semi-transparante kleuren", "",// TODO "",// TODO "",// TODO "Tue puoliläpinäkyviä värejä", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Switching primary DVB to %s...", // %s will be substituted by the plugin name, i. e. 'xine'. "Primäres Interface wird zu %s umgeschaltet...", "", // TODO "Preklapljanje primarne DVB naprave...", "Cambio scheda DVB primaria a %s...", "", // TODO "Eerste DVB-kaart wordt omgeschakeld...", "", // TODO "A mudar interface DVB primário...", "", // TODO "Changement de carte DVB primaire...", "", // TODO "Bytter første DVB-enhet...", "Vaihdetaan ensisijaiseksi DVB-sovittimeksi %s...", "", // TODO "Przê³±czam na pierwszy interfejs DVB...", "", // TODO "Cambiando el interfaz DVB primario...", "", // TODO "Ç êýñéá DVB êÜñôá áëëÜæåé...", "", // TODO "Byter primär DVB enhet...", "", // TODO "Comut dispozitiv DVB primar...", "", // TODO "Primér Interface átkapcsolva...", "", // TODO "Canviant a la interfície DVB primària...", #if APIVERSNUM >= 10302 "", // TODO "ÁÜÕÝÐ ÞáÝÞÒÝÞÓÞ DVB-ãáâàÞÙáâÒÐ...", #if APIVERSNUM >= 10307 "", // TODO "Preklapanje primarnog DVB ureðaja...", #if APIVERSNUM >= 10313 "", // TODO "Esmase DVB seadme ümberlülitus...", #if APIVERSNUM >= 10316 "", // TODO "Skifter primær DVB enhed...", #if APIVERSNUM >= 10342 "", // TODO "Pøepnout primární DVB...", #endif #endif #endif #endif #endif }, { "Switched primary DVB back from %s", // %s will be substituted by the plugin name, i. e. 'xine'. "Primäres Interface wurde von %s zurückgeschaltet", "", // TODO "Preklapljanje primarne DVB naprave", "Ritorno a scheda DVD primaria da %s", "", // TODO "Eerste DVB-kaart wordt omgeschakeld", "", // TODO "A mudar interface DVB primário", "", // TODO "Changement de carte DVB primaire", "", // TODO "Bytter første DVB-enhet", "Ensisijainen DVB-sovitin vaihdettu takaisin %s:stä", "", // TODO "Przê³±czam na pierwszy interfejs DVB", "", // TODO "Cambiando el interfaz DVB primario", "", // TODO "Ç êýñéá DVB êÜñôá áëëÜæåé", "", // TODO "Byter primär DVB enhet", "", // TODO "Comut dispozitiv DVB primar", "", // TODO "Primér Interface átkapcsolva", "", // TODO "Canviant a la interfície DVB primària", #if APIVERSNUM >= 10302 "", // TODO "ÁÜÕÝÐ ÞáÝÞÒÝÞÓÞ DVB-ãáâàÞÙáâÒÐ", #if APIVERSNUM >= 10307 "", // TODO "Preklapanje primarnog DVB ureðaja", #if APIVERSNUM >= 10313 "", // TODO "Esmase DVB seadme ümberlülitus", #if APIVERSNUM >= 10316 "", // TODO "Skifter primær DVB enhed", #if APIVERSNUM >= 10342 "", // TODO "Pøepnout primární DVB", #endif #endif #endif #endif #endif }, { "Yes (by hardware)", "Ja (in Hardware)", "",// TODO "Sì (tramite hardware)", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Yes (by software)", "Ja (in Software)", "",// TODO "Sì (tramite software)", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "4:3 image zoom X [%]", "4:3 Bilder X-Skalierung [%]", "",// TODO "Ingrandimento X [%] immagine 4:3", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "4:3 image zoom Y [%]", "4:3 Bilder Y-Skalierung [%]", "",// TODO "Ingrandimento Y [%] immagine 4:3", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "16:9 image zoom X [%]", "16:9 Bilder X-Skalierung [%]", "",// TODO "Ingrandimento X [%] immagine 16:9", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "16:9 image zoom Y [%]", "16:9 Bilder Y-Skalierung [%]", "",// TODO "Ingrandimento Y [%] immagine 16:9", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Connection interacts with EIT scanner", "Verbindung interagiert mit EIT-Scanner", "",// TODO "La connessione interagisce con lo scanner EIT", "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, /* { "Replay prebuffer [frames]", "Vorpuffer für Wiedergabe [Bilder]", "",// TODO "Riproduci prebuffer [frames]", "Pre-buffer voor afspelen van opnamen", "",// TODO "",// TODO "",// TODO "Toiston puskurointi [ruutua]", "",// TODO "",// TODO "",// TODO "Uppspelningsbuffer [bildrutor]", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Default settings optimized for", "Standard-Einstellungen optimiert für", "",// TODO "Impostazioni predefinite ottimizzate per", "Standaard instellingen geoptimaliseerd voor", "",// TODO "",// TODO "",// TODO "Oletusasetukset optimoitu", "",// TODO "",// TODO "",// TODO "Standardinställning optimerad för", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Live-TV", "Live-TV", "",// TODO "TV dal vivo", "Live-TV", "",// TODO "",// TODO "",// TODO "TV-lähetykselle", "",// TODO "",// TODO "",// TODO "Live-TV", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Replay", "Wiedergabe", "",// TODO "Riproduci", "Opnamen afspelen", "",// TODO "",// TODO "",// TODO "toistolle", "",// TODO "",// TODO "",// TODO "Uppspelning", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Scaled", "Skaliert", "",// TODO "Scalato", "Geschaald", "",// TODO "",// TODO "",// TODO "skaalattu", "",// TODO "",// TODO "",// TODO "Skalad", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Unscaled", "Unskaliert", "",// TODO "Non scalato", "Ongeschaald", "",// TODO "",// TODO "",// TODO "skaalamaton", "",// TODO "",// TODO "",// TODO "Normal", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Settings are now optimized for 'Replay'", "Einstellungen nun optimiert für 'Wiedergabe'", "",// TODO "Ora le impostazioni sono ottimizzate per la 'Riproduzione'", "Instellingen zijn nu geoptimaliseerd voor 'Opnamen afspelen'", "",// TODO "",// TODO "",// TODO "Asetukset optimoitu toistolle", "",// TODO "",// TODO "",// TODO "Inställningarna är nu optimerade för 'Uppspelning'", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "Settings are now optimized for 'Live-TV'", "Einstellungen nun optimiert für 'Live-TV'", "",// TODO "Ora le impostazioni sono ottimizzate per la 'TV dal vivo'", "Instellingen zijn nu geoptimaliseerd voor 'Live-TV'", "",// TODO "",// TODO "",// TODO "Asetukset optimoitu TV-lähetykselle", "",// TODO "",// TODO "",// TODO "Inställningarna är nu optimerade för 'Live-TV'", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "xine - Choose optimized settings for 'Live-TV'", "xine - Wähle optimierte Einstellungen für 'Live-TV'", "",// TODO "xine - Scegli impostazioni ottimizzate per la 'TV dal vivo'", "xine - Kies optimale instellingen voor 'Live-TV'", "",// TODO "",// TODO "",// TODO "xine - Valitse optimoidut asetukset TV-lähetykselle", "",// TODO "",// TODO "",// TODO "xine - Välj optimerade inställningar för 'Live-TV'", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, { "xine - Choose optimized settings for 'Replay'", "xine - Wähle optimierte Einstellungen für 'Wiedergabe'", "",// TODO "xine - Scegli impostazioni ottimizzate per la 'Riproduzione'", "xine - Kies optimale instellingen voor 'Opnamen afspelen'", "",// TODO "",// TODO "",// TODO "xine - Valitse optimoidut asetukset toistolle", "",// TODO "",// TODO "",// TODO "xine - Välj optimerade inställningar för 'Live-TV'", "",// TODO "",// TODO "",// TODO #if APIVERSNUM >= 10302 "",// TODO #if APIVERSNUM >= 10307 "",// TODO #if APIVERSNUM >= 10313 "",// TODO #if APIVERSNUM >= 10316 "",// TODO #if APIVERSNUM >= 10342 "",// TODO #endif #endif #endif #endif #endif }, */ { NULL } }; }; #endif //__XINEI18N_H xine-0.9.4/xineDevice.c0000644000000000000000000032774611532512173013437 0ustar rootroot #include "xineCommon.h" #include #include #if APIVERSNUM >= 10703 #include "vdr172remux.h" #define VDR172 ::vdr172 #else #define VDR172 #endif #include "xineDevice.h" #include "xineOsd.h" #include "xineSettings.h" //#define LOG_ME(x) x #define LOG_ME(x) namespace PluginXine { void cXineSpuDecoder::ptsAdjust(uint32_t &pts) { if (0 == pts || -1 == (int32_t)pts) { return; } const int64_t ptsXine = m_xineDevice->GetSTC(); if (-1 == ptsXine) return; // ::fprintf(stderr, "ptsAdjust: %ld, %lld, %lld\n", pts, ptsXine, pts - ptsXine); pts = (uint32_t)ptsXine; } int cXineSpuDecoder::setTime(uint32_t pts) { ptsAdjust(pts); return cDvbSpuDecoder::setTime(pts); } static cXineDevice *theXineDevice = 0; bool cXineDevice::HasDecoder(void) const { return true; } cSpuDecoder *cXineDevice::GetSpuDecoder(void) { if (!m_spuDecoder && IsPrimaryDevice()) { m_spuDecoder = new cXineSpuDecoder(this); } return m_spuDecoder; } bool cXineDevice::CanReplay(void) const { return true; } static bool findVideo = false; static bool foundVideo = false; static int ts = 0; static bool f = false; static bool np = false; static bool muted = false; static int jumboPESsize = 0; static int jumboPEStail = 0; static ePlayMode pm = pmNone; static bool audioSeen = false; static int64_t ptsV = -1, ptsA = -1, ptsP = -1, ptsD = -1; static cMutex softStartMutex; static enum { sstNone = 0, sstNormal, sstNoMetronom } softStartTrigger = sstNone; static enum { sIdle , sInitiateSequence , sStartSequence , sContinueSequence } softStartState = sIdle; double tNow() { timeval tv; ::gettimeofday(&tv, 0); return tv.tv_sec + tv.tv_usec / 1.0e+6; } static double ttt0 = tNow(), ttt1 = tNow(), ttt2 = 0, ttt3 = 0, ttt4 = 0, ttt5 = 0, ttt6 = 0; // static int streams[ 256 ]; static bool doClear = false; bool cXineDevice::SetPlayMode(ePlayMode PlayMode) { if (pmNone == PlayMode) { doClear = true; ttt0 = tNow(); } else ttt2 = tNow(); /* timeval tv0; ::gettimeofday(&tv0, 0); */ if (0) { time_t t1 = time(0); static time_t t0 = t1; if (0 == PlayMode && (t1 - t0) > (30 * 60)) *(char *)0 = 0; t0 = t1; } bool playModeSupported = false; switch (PlayMode) { case pmNone: case pmAudioVideo: case pmAudioOnlyBlack: case pmExtern_THIS_SHOULD_BE_AVOIDED: playModeSupported = true; break; case pmAudioOnly: #if APIVERSNUM >= 10308 case pmVideoOnly: #endif break; } ptsV = ptsA = ptsP = ptsD = -1; ts = 0; np = 0; f = 0; xfprintf(stderr, "SetPlayMode: %d\n", PlayMode); if (pmExtern_THIS_SHOULD_BE_AVOIDED == pm && pmNone == PlayMode) { m_xineLib.enableExternal(false); } m_xineLib.pause(false); m_xineLib.execFuncTrickSpeedMode(false); m_xineLib.execFuncSetSpeed(100.0); if (muted) { muted = false; m_xineLib.execFuncMute(false); } if (pmNone == PlayMode) { pm = PlayMode; jumboPESsize = 0; jumboPEStail = 0; /* for (unsigned int i = 0; i < sizeof (streams) / sizeof (*streams); i++) { if (streams[ i ]) fprintf(stderr, "stream: 0x%02x\n", i); } */ m_xineLib.ignore(); // m_xineLib.execFuncMute(); // m_xineLib.execFuncSetPrebuffer(0); m_xineLib.execFuncClear(-2); // m_xineLib.execFuncStart(); // m_xineLib.execFuncMetronom(0); m_xineLib.execFuncStillFrame(); m_xineLib.execFuncWait(); // for (int i = 0; i < 2; i++) m_xineLib.showNoSignal(); PushOut(); m_xineLib.execFuncFlush(); { cMutexLock lock(&softStartMutex); softStartTrigger = sstNone; softStartState = sIdle; } foundVideo = false; findVideo = false; } else { audioSeen = false; { cMutexLock lock(&softStartMutex); softStartTrigger = sstNone; softStartState = sIdle; } // ::memset(&streams, 0, sizeof (streams)); m_xineLib.freeze(); m_xineLib.ignore(false); m_xineLib.freeze(false); /* PushOut(); m_xineLib.execFuncFlush(); m_xineLib.execFuncWait(); */ // m_xineLib.execFuncSetPrebuffer(m_settings.GetModeParams()->m_prebufferFrames); // m_xineLib.execFuncSetPrebuffer(0); m_xineLib.execFuncClear(-4); // m_xineLib.execFuncStart(); m_xineLib.execFuncWait(); #if APIVERSNUM < 10342 m_settings.SelectReplayPrebufferMode(0 == cTransferControl::ReceiverDevice()); #else m_settings.SelectReplayPrebufferMode(!Transferring()); #endif if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); // ::fprintf(stderr, "LiveTV\n"); softStartTrigger = sstNormal; } else np = true; foundVideo = false; findVideo = true; cMutexLock pmMutexLock(&m_pmMutex); pm = PlayMode; m_pmCondVar.Broadcast(); } if (pmExtern_THIS_SHOULD_BE_AVOIDED == PlayMode) m_xineLib.enableExternal(); /* timeval tv1; ::gettimeofday(&tv1, 0); fprintf(stderr, "spm: %.3lf ms\n", 1000 * ((tv1.tv_sec + tv1.tv_usec / 1.0e+6) - (tv0.tv_sec + tv0.tv_usec / 1.0e+6))); */ if (pmNone == PlayMode) { ttt1 = tNow(); ttt4 = 0; ttt5 = 0; } else ttt3 = tNow(); return playModeSupported; } void cXineDevice::DiscontinuityDetected() { if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); if (softStartState == sIdle && softStartTrigger == sstNone && pm != pmNone) { softStartTrigger = sstNormal; xfprintf(stderr, "DiscontinuityDetected: triggering soft start\n"); } } } static bool lastCmdWasClear = false; bool cXineDevice::HasIBPTrickSpeed(void) { /* #if APIVERSNUM >= 10706 return true; #else */ return false; //#endif } void cXineDevice::TrickSpeed(int Speed) { TrickSpeed(Speed, false); } void cXineDevice::TrickSpeed(int Speed, bool IBP) { f = false; ts = Speed; xfprintf(stderr, "TrickSpeed: %d\n", Speed); m_xineLib.execFuncTrickSpeedMode(lastCmdWasClear); m_xineLib.execFuncSetSpeed(100.0 / Speed * (IBP ? 12 : 1)); m_xineLib.execFuncWait(); m_xineLib.freeze(false); m_xineLib.pause(false); } void cXineDevice::Clear(void) { lastCmdWasClear = true; doClear = true; ptsV = ptsA = ptsP = ptsD = -1; static int cntClear = 0; xfprintf(stderr, "Clear(%d)", cntClear); m_xineLib.pause(); jumboPESsize = 0; jumboPEStail = 0; if (f) m_xineLib.execFuncSetSpeed(100.0); m_xineLib.execFuncClear(cntClear++); // m_xineLib.execFuncStart(); np = true; if (f) m_xineLib.execFuncSetSpeed(0.0); m_xineLib.execFuncWait(); m_xineLib.pause(false); xfprintf(stderr, "!\n"); if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); softStartTrigger = sstNoMetronom; } cDevice::Clear(); } void cXineDevice::Play(void) { lastCmdWasClear = false; f = false; ts = 0; xfprintf(stderr, "Play\n"); m_xineLib.execFuncTrickSpeedMode(false); m_xineLib.execFuncSetSpeed(100.0); if (muted) { muted = false; m_xineLib.execFuncMute(false); } m_xineLib.execFuncWait(); m_xineLib.freeze(false); m_xineLib.pause(false); LOG_ME(::fprintf(stderr, "----\n");) } void cXineDevice::Freeze(void) { lastCmdWasClear = false; f = true; xfprintf(stderr, "Freeze\n"); m_xineLib.freeze(); m_xineLib.pause(); m_xineLib.execFuncSetSpeed(0.0); m_xineLib.execFuncWait(); LOG_ME(::fprintf(stderr, "------\n");) } void cXineDevice::Mute(void) { xfprintf(stderr, "Mute\n"); m_xineLib.execFuncMute(true); muted = true; } static void store_frame(const unsigned char *buf, int len, int line) { if (0) { static int cnt = 0; char name[ 100 ]; ::sprintf(name, "/tmp/frame_%05d_%05d", line, cnt++); FILE *f = fopen(name, "wb"); size_t r = fwrite(buf, 1, len, f); (void)r; fclose(f); } } #define VERBOSE_NOP() do{ xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); } while (0) #define VERBOSE_NOP1() do{ store_frame(Data, Length, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); } while (0) #define VERBOSE_RETURN0(x) do{ xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #define VERBOSE_RETURN1(x) do{ store_frame(buf0, len0, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #define VERBOSE_RETURN2(x) do{ store_frame(buf, len, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #define VERBOSE_RETURN3(x) do{ store_frame(Data, Length, __LINE__); xfprintf(stderr, "FIXME: %s:%d\n", __FILE__, __LINE__); return x; } while (0) #if APIVERSNUM < 10331 enum ePesHeader { phNeedMoreData = -1, phInvalid = 0, phMPEG1 = 1, phMPEG2 = 2 }; static ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL) { if (Count < 7) return phNeedMoreData; // too short if ((Data[6] & 0xC0) == 0x80) { // MPEG 2 if (Count < 9) return phNeedMoreData; // too short PesPayloadOffset = 6 + 3 + Data[8]; if (Count < PesPayloadOffset) return phNeedMoreData; // too short if (ContinuationHeader) *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]); return phMPEG2; // MPEG 2 } // check for MPEG 1 ... PesPayloadOffset = 6; // skip up to 16 stuffing bytes for (int i = 0; i < 16; i++) { if (Data[PesPayloadOffset] != 0xFF) break; if (Count <= ++PesPayloadOffset) return phNeedMoreData; // too short } // skip STD_buffer_scale/size if ((Data[PesPayloadOffset] & 0xC0) == 0x40) { PesPayloadOffset += 2; if (Count <= PesPayloadOffset) return phNeedMoreData; // too short } if (ContinuationHeader) *ContinuationHeader = false; if ((Data[PesPayloadOffset] & 0xF0) == 0x20) { // skip PTS only PesPayloadOffset += 5; } else if ((Data[PesPayloadOffset] & 0xF0) == 0x30) { // skip PTS and DTS PesPayloadOffset += 10; } else if (Data[PesPayloadOffset] == 0x0F) { // continuation header PesPayloadOffset++; if (ContinuationHeader) *ContinuationHeader = true; } else return phInvalid; // unknown if (Count < PesPayloadOffset) return phNeedMoreData; // too short return phMPEG1; // MPEG 1 } #endif //#if APIVERSNUM < 10345 namespace cRemux { // Start codes: #define SC_SEQUENCE 0xB3 // "sequence header code" #define SC_GROUP 0xB8 // "group start code" #define SC_PICTURE 0x00 // "picture start code" int GetPacketLength(const uchar *Data, int Count, int Offset) { // Returns the length of the packet starting at Offset, or -1 if Count is // too small to contain the entire packet. int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1; if (Length > 0 && Offset + Length <= Count) return Length; return -1; } bool IsFrameH264(const uchar *Data, int Length) { int PesPayloadOffset; const uchar *limit = Data + Length; if (AnalyzePesHeader(Data, Length, PesPayloadOffset) <= phInvalid) return false; // neither MPEG1 nor MPEG2 Data += PesPayloadOffset + 3; // move to video payload and skip 00 00 01 if (Data < limit) { // cVideoRepacker ensures that in case of H264 we will see an access unit delimiter here if (0x01 == Data[-1] && 9 == Data[0] && 0x00 == Data[-2] && 0x00 == Data[-3]) return true; } return false; } int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType) { // Scans the video packet starting at Offset and returns its length. // If the return value is -1 the packet was not completely in the buffer. int Length = GetPacketLength(Data, Count, Offset); if (Length > 0) { const bool h264 = IsFrameH264(Data + Offset, Length); int PesPayloadOffset = 0; if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) { const uchar *p = Data + Offset + PesPayloadOffset + 2; const uchar *pLimit = Data + Offset + Length - 3; #if APIVERSNUM >= 10326 // cVideoRepacker ensures that a new PES packet is started for a new sequence, // group or picture which allows us to easily skip scanning through a huge // amount of video data. if (p < pLimit) { if (p[-2] || p[-1] || p[0] != 0x01) pLimit = 0; // skip scanning: packet doesn't start with 0x000001 else { if (h264) { int nal_unit_type = p[1] & 0x1F; switch (nal_unit_type) { case 9: // access unit delimiter // when the MSB in p[1] is set (which violates H.264) then this is a hint // from cVideoRepacker::HandleNalUnit() that this bottom field shall not // be reported as picture. if (p[1] & 0x80) ((uchar *)p)[1] &= ~0x80; // revert the hint and fall through else break; default: // skip scanning: packet doesn't start a new picture pLimit = 0; } } else { switch (p[1]) { case SC_SEQUENCE: case SC_GROUP: case SC_PICTURE: break; default: // skip scanning: packet doesn't start a new sequence, group or picture pLimit = 0; } } } } #endif while (p < pLimit && (p = (const uchar *)memchr(p, 0x01, pLimit - p))) { if (!p[-2] && !p[-1]) { // found 0x000001 if (h264) { int nal_unit_type = p[1] & 0x1F; switch (nal_unit_type) { case 9: { // access unit delimiter int primary_pic_type = p[2] >> 5; switch (primary_pic_type) { case 0: // I case 3: // SI case 5: // I, SI PictureType = I_FRAME; break; case 1: // I, P case 4: // SI, SP case 6: // I, SI, P, SP PictureType = P_FRAME; break; case 2: // I, P, B case 7: // I, SI, P, SP, B PictureType = B_FRAME; break; } return Length; } } } else { switch (p[1]) { case SC_PICTURE: PictureType = (p[3] >> 3) & 0x07; return Length; } } p += 4; // continue scanning after 0x01ssxxyy } else p += 3; // continue scanning after 0x01xxyy } } PictureType = NO_PICTURE; return Length; } return -1; } } //#endif /* static bool IsNotVideoIorPframe(const uchar *buf, int len) { if (0xe0 != (0xf0 & buf[ 3 ])) // not video return true; uchar pt = NO_PICTURE; cRemux::ScanVideoPacket(buf, len, 0, pt); return (I_FRAME == pt || P_FRAME == pt); } */ /* static char *getFrameType(const uchar *buf, int len) { if (0xe0 != (0xf0 & buf[ 3 ])) // not video return ""; static char *frameTypes[ 8 ] = { "", "i", "p", "b", "4", "5", "6", "7" }; uchar pt = NO_PICTURE; cRemux::ScanVideoPacket(buf, len, 0, pt); return frameTypes[ pt ]; } */ static bool getPTS(const unsigned char *buf0, int len0, int64_t &pts) { while (len0 > 0) { while (len0 > 3 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { buf0++; len0--; } if (3 == len0 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { break; } if (len0 < 6) VERBOSE_RETURN1(false); if (0x00 != buf0[ 0 ] || 0x00 != buf0[ 1 ] || 0x01 != buf0[ 2 ]) { VERBOSE_RETURN1(false); } if (0xe0 != (0xf0 & buf0[ 3 ]) // video && 0xc0 != (0xe0 & buf0[ 3 ]) // audio && 0xbd != (0xff & buf0[ 3 ]) // dolby && 0xbe != (0xff & buf0[ 3 ])) // padding (DVD) { VERBOSE_RETURN1(false); } int len = (6 + buf0[ 4 ] * 0x100 + buf0[ 5 ]); if (len > len0) VERBOSE_RETURN1(false); const unsigned char *buf = buf0; buf0 += len; len0 -= len; // if (len0 != 0) // VERBOSE_NOP(); if (0xbe == (0xff & buf[ 3 ])) // padding (DVD) continue; if (len < (6 + 3)) VERBOSE_RETURN2(false); if (0x80 != (0xc0 & buf[ 6 ])) // MPEG1 { do // ... while (false); { int o = 0; for (int i = 0; i < 16; i++) { if (buf[ 6 + o ] != 0xff) break; if (len < (6 + ++o)) VERBOSE_RETURN2(false); } if (0x40 == (0xc0 & buf[ 6 + o ])) o += 2; if (len < (6 + o)) VERBOSE_RETURN2(false); if (0x31 == (0xf1 & buf[ 6 + o + 0 ])) { if (len < (6 + o + 5 + 5)) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); if (0x11 != (0xf1 & buf[ 6 + o + 5 + 0 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 4 ])) VERBOSE_RETURN2(false); int64_t _pts = ((int64_t)(0x0e & buf[ 6 + o + 0 ])) << 29 | (0xff & buf[ 6 + o + 1 ]) << 22 | (0xfe & buf[ 6 + o + 2 ]) << 14 | (0xff & buf[ 6 + o + 3 ]) << 7 | (0xfe & buf[ 6 + o + 4 ]) >> 1; // ::fprintf(stderr, "pts: %lld\n", _pts); if (0 == _pts) break; // if (!IsNotVideoIorPframe(buf, len)) // only PTS of I and P frames are progressive in time // break; pts = _pts; return true; } else if (0x21 == (0xf1 & buf[ 6 + o + 0 ])) { if (len < (6 + o + 5)) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); int64_t _pts = ((int64_t)(0x0e & buf[ 6 + o + 0 ])) << 29 | (0xff & buf[ 6 + o + 1 ]) << 22 | (0xfe & buf[ 6 + o + 2 ]) << 14 | (0xff & buf[ 6 + o + 3 ]) << 7 | (0xfe & buf[ 6 + o + 4 ]) >> 1; // ::fprintf(stderr, "pts: %lld\n", _pts); if (0 == _pts) break; // if (!IsNotVideoIorPframe(buf, len)) // only PTS of I and P frames are progressive in time // break; pts = _pts; return true; } else if (0x0f == (0xff & buf[ 6 + o + 0 ])) { break; } for (int i = 0; i < 30; i++) xfprintf(stderr, "%02x ", buf[ i ]); xfprintf(stderr, "\n"); VERBOSE_RETURN2(false); } while (false); continue; } if (0x40 == (0xc0 & buf[ 7 ])) VERBOSE_RETURN2(false); if (0x00 == (0xc0 & buf[ 7 ])) continue; // ignore // if (0x00 != (0x3f & buf[ 7 ])) // VERBOSE_RETURN2(false); bool hasPTS = (0 != (0x80 & buf[ 7 ])); bool hasDTS = (0 != (0x40 & buf[ 7 ])); unsigned char hdl = buf[ 8 ]; if (hdl < ((hasPTS + hasDTS) * 5)) VERBOSE_RETURN2(false); if (len < (6 + 3 + hdl)) VERBOSE_RETURN2(false); if ((0x20 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { if ((0x20 * hasPTS + 0x00 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '00X0' instead of '00X1'. } else if ((0x00 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '000X' instead of '001X'. } else { fprintf(stderr, "buf:"); for (int i = 0; i < 6 + 3 + hdl; i++) fprintf(stderr, " %02x", buf[i]); fprintf(stderr, "\n"); VERBOSE_RETURN2(false); } } if (0x01 != (0x01 & buf[ 11 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 13 ])) VERBOSE_RETURN2(false); if (hasDTS) { if (0x11 != (0xf1 & buf[ 14 ])) { if (0x21 == (0xf1 & buf[ 14 ])) { // accept streams, that start with '0010' instead of '0001'. } else if (0xa1 == (0xf1 & buf[ 14 ])) { // accept streams, that start with '1010' instead of '0001'. } else { fprintf(stderr, "buf:"); for (int i = 0; i < 6 + 3 + hdl; i++) fprintf(stderr, " %02x", buf[i]); fprintf(stderr, "\n"); VERBOSE_RETURN2(false); } } // accept streams that have no marker bits set // if (0x01 != (0x01 & buf[ 16 ])) // VERBOSE_RETURN2(false); // if (0x01 != (0x01 & buf[ 18 ])) // VERBOSE_RETURN2(false); } /* fprintf(stderr, " %02x %02x %02x %02x %02x\n" , buf[ 9 ] , buf[ 10 ] , buf[ 11 ] , buf[ 12 ] , buf[ 13 ]); */ int64_t _pts = ((int64_t)(0x0e & buf[ 9 ])) << 29 | (0xff & buf[ 10 ]) << 22 | (0xfe & buf[ 11 ]) << 14 | (0xff & buf[ 12 ]) << 7 | (0xfe & buf[ 13 ]) >> 1; if (0 == _pts) return false; // if (!IsNotVideoIorPframe(buf, len)) // only PTS of I and P frames are progressive in time // return false; pts = _pts; return true; } // VERBOSE_RETURN2(false); return false; } /* static bool stripPTSandDTS(unsigned char *buf0, int len0) { while (len0 > 0) { while (len0 > 3 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { buf0++; len0--; } if (3 == len0 && 0x00 == buf0[ 0 ] && 0x00 == buf0[ 1 ] && 0x00 == buf0[ 2 ]) { break; } if (len0 < 6) VERBOSE_RETURN1(false); if (0x00 != buf0[ 0 ] || 0x00 != buf0[ 1 ] || 0x01 != buf0[ 2 ]) { VERBOSE_RETURN1(false); } if (0xe0 != (0xf0 & buf0[ 3 ]) // video && 0xc0 != (0xe0 & buf0[ 3 ]) // audio && 0xbd != (0xff & buf0[ 3 ]) // dolby && 0xbe != (0xff & buf0[ 3 ]) // padding (DVD) && 0xba != (0xff & buf0[ 3 ]) // system layer: pack header && 0xbb != (0xff & buf0[ 3 ]) // system layer: system header && 0xb9 != (0xff & buf0[ 3 ])) // system layer: stream end { //fprintf(stderr, "buf0[ 3 ]: %02x\n", buf0[ 3 ]); VERBOSE_RETURN1(false); } int len = (6 + buf0[ 4 ] * 0x100 + buf0[ 5 ]); if (0xba == buf0[ 3 ]) // pack header has fixed length { if (0x00 == (0xc0 & buf0[ 4 ])) // MPEG 1 len = 12; else // MPEG 2 len = 14 + (buf0[ 13 ] & 0x07); } else if (0xb9 == buf0[ 3 ]) // stream end has fixed length { len = 4; } if (len > len0) VERBOSE_RETURN1(false); unsigned char *buf = buf0; buf0 += len; len0 -= len; // if (len0 != 0) // VERBOSE_NOP(); if (0xbe == (0xff & buf[ 3 ]) // padding (DVD) || 0xba == (0xff & buf[ 3 ]) // system layer: pack header || 0xbb == (0xff & buf[ 3 ]) // system layer: system header || 0xb9 == (0xff & buf[ 3 ])) // system layer: stream end { continue; } if (len < (6 + 3)) VERBOSE_RETURN2(false); if (0x80 != (0xc0 & buf[ 6 ])) // MPEG1 { bool ok = false; do // ... while (false); { int o = 0; for (int i = 0; i < 16; i++) { if (buf[ 6 + o ] != 0xff) break; if (len < (6 + ++o)) VERBOSE_RETURN2(false); } if (0x40 == (0xc0 & buf[ 6 + o ])) o += 2; if (len < (6 + o)) VERBOSE_RETURN2(false); if (0x31 == (0xf1 & buf[ 6 + o + 0 ])) { if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); if (0x11 != (0xf1 & buf[ 6 + o + 5 + 0 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 5 + 4 ])) VERBOSE_RETURN2(false); buf[ 6 + o + 0 ] = 0xff; buf[ 6 + o + 1 ] = 0xff; buf[ 6 + o + 2 ] = 0xff; buf[ 6 + o + 3 ] = 0xff; buf[ 6 + o + 4 ] = 0xff; buf[ 6 + o + 5 + 0 ] = 0xff; buf[ 6 + o + 5 + 1 ] = 0xff; buf[ 6 + o + 5 + 2 ] = 0xff; buf[ 6 + o + 5 + 3 ] = 0xff; buf[ 6 + o + 5 + 4 ] = 0x0f; ok = true; } else if (0x21 == (0xf1 & buf[ 6 + o + 0 ])) { if (0x01 != (0x01 & buf[ 6 + o + 2 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 6 + o + 4 ])) VERBOSE_RETURN2(false); buf[ 6 + o + 0 ] = 0xff; buf[ 6 + o + 1 ] = 0xff; buf[ 6 + o + 2 ] = 0xff; buf[ 6 + o + 3 ] = 0xff; buf[ 6 + o + 4 ] = 0x0f; ok = true; } else if (0x0f == (0xff & buf[ 6 + o + 0 ])) { ok = true; } if (ok) break; for (int i = 0; i < 30; i++) xfprintf(stderr, "%02x ", buf[ i ]); xfprintf(stderr, "\n"); VERBOSE_RETURN2(false); } while (false); if (ok) continue; } if (0x40 == (0xc0 & buf[ 7 ])) VERBOSE_RETURN2(false); if (0x00 == (0xc0 & buf[ 7 ])) continue; // ignore // if (0x00 != (0x3f & buf[ 7 ])) // VERBOSE_RETURN2(false); bool hasPTS = (0 != (0x80 & buf[ 7 ])); bool hasDTS = (0 != (0x40 & buf[ 7 ])); unsigned char hdl = buf[ 8 ]; if (hdl < ((hasPTS + hasDTS) * 5)) VERBOSE_RETURN2(false); if (len < (6 + 3 + hdl)) VERBOSE_RETURN2(false); if ((0x20 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { if ((0x20 * hasPTS + 0x00 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '00X0' instead of '00X1'. } else if ((0x00 * hasPTS + 0x10 * hasDTS + 0x01) != (0xf1 & buf[ 9 ])) { // accept streams, that start with '000X' instead of '001X'. } else { VERBOSE_RETURN2(false); } } if (0x01 != (0x01 & buf[ 11 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 13 ])) VERBOSE_RETURN2(false); if (hasDTS) { if (0x11 != (0xf1 & buf[ 14 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 16 ])) VERBOSE_RETURN2(false); if (0x01 != (0x01 & buf[ 18 ])) VERBOSE_RETURN2(false); } buf[ 7 ] &= 0x3f; for (int i = 9; i < (9 + ((hasPTS + hasDTS) * 5)); i++) buf[ i ] = 0xff; } return true; } */ static uchar padding[ 6 + 0xffff ] = { 0x00 , 0x00 , 0x01 , 0xbe , 0xff , 0xff }; int cXineDevice::PushOut() { uchar *Data = padding; int Length = sizeof (padding); return PlayCommon3(Data, Length, -1); } //static bool blahblah = false; void cXineDevice::StillPicture(const uchar *Data, int Length) { #if APIVERSNUM >= 10701 if (Length > 0 && Data[0] == 0x47) { const uchar *tsData = Data; const int tsLength = Length; uchar *pesData = (uchar *)malloc(Length); int pesLength = 0; uchar tsHead[4] = { 0xff, 0xff, 0xff, 0xff }; VDR172::cRemux *vRemux = 0; cPatPmtParser patPmtParser; while (Length >= TS_SIZE) { if (TsHasPayload(Data)) { int plo = TsPayloadOffset(Data); if (plo < Length) { int pid = TsPid(Data); if (pid == 0) patPmtParser.ParsePat(Data, TS_SIZE); else if (pid == patPmtParser.PmtPid()) patPmtParser.ParsePmt(Data, TS_SIZE); else if (pid == patPmtParser.Vpid()) { if (!vRemux) { vRemux = new VDR172::cRemux(patPmtParser.Vpid(), 0, 0, 0); vRemux->SetTimeouts(0, 0); } memcpy(tsHead, Data, sizeof (tsHead)); vRemux->Put(Data, TS_SIZE); int n = 0; uchar *p = vRemux->Get(n); if (p) { memcpy(pesData + pesLength, p, n); pesLength += n; vRemux->Del(n); } } } } Data += TS_SIZE; Length -= TS_SIZE; } if (!vRemux) { free(pesData); return; } uchar tsTail[TS_SIZE]; memset(tsTail + sizeof (tsHead), 0xff, sizeof (tsTail) - sizeof (tsHead)); memcpy(tsTail, tsHead, sizeof (tsHead)); tsTail[1] &= ~TS_ERROR; tsTail[1] |= TS_PAYLOAD_START; tsTail[3] &= ~TS_SCRAMBLING_CONTROL; tsTail[3] |= TS_ADAPT_FIELD_EXISTS; tsTail[3] |= TS_PAYLOAD_EXISTS; tsTail[3] = (tsTail[3] & 0xf0) | (((tsTail[3] & TS_CONT_CNT_MASK) + 1) & TS_CONT_CNT_MASK); tsTail[4] = TS_SIZE - 5 - 13; tsTail[5] = 0x00; tsTail[TS_SIZE - 13] = 0x00; tsTail[TS_SIZE - 12] = 0x00; tsTail[TS_SIZE - 11] = 0x01; tsTail[TS_SIZE - 10] = pesData[3]; tsTail[TS_SIZE - 9] = 0x00; tsTail[TS_SIZE - 8] = 0x07; tsTail[TS_SIZE - 7] = 0x80; tsTail[TS_SIZE - 6] = 0x00; tsTail[TS_SIZE - 5] = 0x00; tsTail[TS_SIZE - 4] = 0x00; tsTail[TS_SIZE - 3] = 0x00; tsTail[TS_SIZE - 2] = 0x01; tsTail[TS_SIZE - 1] = VDR172::cRemux::IsFrameH264(pesData, pesLength) ? 10 : 0xb7; if (0) { FILE *ff = fopen("/tmp/still.ts", "wb"); fwrite(tsData, 1, tsLength, ff); fwrite(tsTail, 1, TS_SIZE, ff); fclose(ff); } vRemux->Put(tsTail, TS_SIZE); int n = 0; uchar *p = vRemux->Get(n); if (p) { memcpy(pesData + pesLength, p, n); pesLength += n; vRemux->Del(n); } if (0) { FILE *ff = fopen("/tmp/still.pes", "wb"); fwrite(pesData, 1, pesLength, ff); fclose(ff); } delete vRemux; StillPicture(pesData, pesLength); free(pesData); return; } #endif xfprintf(stderr, "StillPicture: %p, %d\n", Data, Length); if (0) { for (int i = 0; i < Length - 3; i++) { if (i != 0 && Data[ i + 0 ] == 0x00 && Data[ i + 1 ] == 0x00 && Data[ i + 2 ] == 0x01) { xfprintf(stderr, "\n"); } xfprintf(stderr, "%02x ", Data[ i ]); } for (int i = Length - 3; i < Length; i++) { xfprintf(stderr, "%02x ", Data[ i ]); } xfprintf(stderr, "\n"); } const int maxPackets = 3; uchar pes[ maxPackets * (6 + 0xffff) ]; static const uchar header[ 6 + 3 ] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x00 }; do // ... while (false); { if (Length < 6) { VERBOSE_NOP(); break; } if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_NOP(); break; } if (0xe0 != (0xf0 & Data[ 3 ]) // video && 0xc0 != (0xe0 & Data[ 3 ]) // audio && 0xbd != (0xff & Data[ 3 ]) // dolby && 0xbe != (0xff & Data[ 3 ])) // padding (DVD) { if (Length > maxPackets * (0xffff - 3)) { VERBOSE_NOP(); break; } int todo = Length; const uchar *src = Data; uchar *dst = pes; while (todo > 0) { ::memcpy(dst, header, sizeof (header)); int bite = todo; if (bite > 0xffff - 3) bite = 0xffff - 3; todo -= bite; dst[ 4 ] = 0xff & ((bite + 3) >> 8); dst[ 5 ] = 0xff & (bite + 3); ::memcpy(dst + sizeof (header), src, bite); Length += sizeof (header); dst += sizeof (header) + bite; src += bite; } Data = pes; } } while (false); //NNN stripPTSandDTS((uchar *)Data, Length); ts = 0; m_xineLib.execFuncTrickSpeedMode(false); m_xineLib.execFuncSetSpeed(100.0); m_xineLib.execFuncStillFrame(); m_xineLib.execFuncWait(); f = 0; m_xineLib.pause(false); // PushOut(); //blahblah = true; #if APIVERSNUM >= 10704 uchar pesTail[] = { 0x00, 0x00, 0x01, Data[3], 0x00, 3 + 4, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, VDR172::cRemux::IsFrameH264(Data, Length) ? 10 : 0xb7 }; #endif for (int i = 0; i < 1 /* 4 */; i++) { //fprintf(stderr, " (%d) ", i); int r = PlayVideo1(Data, Length, true); if (r < 0) return; #if APIVERSNUM >= 10704 r = PlayVideo1(pesTail, sizeof (pesTail), true); if (r < 0) return; #endif } /* FILE *ff = fopen("/tmp/still.pes", "wb"); fwrite(Data, 1, Length, ff); fclose(ff); */ PushOut(); // m_xineLib.execFuncFlush(0); m_xineLib.execFuncFlush(); // ::fprintf(stderr, "------------\n"); LOG_ME(::fprintf(stderr, "------------\n");) } static bool softStartPoll(cXineLib &xineLib, cPoller &poller, const int timeout, const bool result); bool cXineDevice::Poll(cPoller &Poller, int TimeoutMs /* = 0 */) { cMutexLock lock(&softStartMutex); if (softStartState != sIdle) return true; if (m_xineLib.Poll(Poller, TimeoutMs)) return softStartPoll(m_xineLib, Poller, TimeoutMs, true); return softStartPoll(m_xineLib, Poller, TimeoutMs, false); } static bool jw = false; bool cXineDevice::Flush(int TimeoutMs /* = 0 */) { const bool jw0 = jw; m_xineLib.pause(false); if (!jw0) { int r = PushOut(); if (r < 0) return true; } bool retVal = m_xineLib.execFuncFlush(TimeoutMs, jw0); if (!retVal) xfprintf(stderr, jw0 ? "f" : "F"); jw = true; return retVal; } static bool dumpAudio(const char *proc, const uchar *Data, int Length) { return false; nextPacket: if (Length == 0) return true; /* fprintf(stderr , "%s: " , proc); */ if (Length < 6) VERBOSE_RETURN0(false); if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_RETURN3(false); } int l = Data[ 4 ] * 0x0100 + Data[ 5 ]; if (Length < (6 + l)) VERBOSE_RETURN0(false); if (0xe0 == (Data[ 3 ] & 0xf0) //video || 0xc0 == (Data[ 3 ] & 0xe0) //audio || 0xbe == Data[ 3 ]) //padding { Data += (6 + l); Length -= (6 + l); goto nextPacket; } // if (0xbd != Data[ 3 ]) //private (dolby, pcm) // VERBOSE_RETURN0(false); // fprintf(stderr, "private "); if (l < (3 + 0 + 2)) VERBOSE_RETURN0(false); int h = Data[ 8 ]; if (l < (3 + h + 2)) VERBOSE_RETURN0(false); xfprintf(stderr , "%s: " , proc); xfprintf(stderr , "0x%02x 0x%02x\n" , Data[ 6 + 3 + h + 0 ] , Data[ 6 + 3 + h + 1 ]); Data += (6 + l); Length -= (6 + l); goto nextPacket; } static bool IsVideo(const uchar *Data, int Length) { return (Length >= 4 && 0x00 == Data[ 0 ] && 0x00 == Data[ 1 ] && 0x01 == Data[ 2 ] && 0xe0 == (0xf0 & Data[ 3 ])); } #if APIVERSNUM >= 10701 static int m_tsVideoPid = 0; #endif int cXineDevice::PlayTsVideo(const uchar *Data, int Length) { #if APIVERSNUM >= 10701 if (Length >= TS_SIZE) { #if APIVERSNUM >= 10712 m_tsVideoPid = PatPmtParser()->Vpid(); #else m_tsVideoPid = TsPid(Data); #endif } #endif return Length; } int cXineDevice::PlayTsAudio(const uchar *Data, int Length) { return Length; } #if APIVERSNUM >= 10701 static bool m_trampoline = false; struct sTrampoline { uchar Header[6]; const uchar *Data; int Length; bool VideoOnly; }; static VDR172::cRemux *m_tsVideoRemux = 0; static VDR172::cRemux *m_tsAudioRemux = 0; #endif int cXineDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly /* = false */) { #if APIVERSNUM >= 10701 cMutexLock lock(&m_playTsMutex); int ret = PlayTsImpl(Data, Length, VideoOnly); // if (Length > 0 && ret <= 0) { char cmd[500]; sprintf(cmd, "ddd /soft/vdr-" APIVERSION "/bin/vdr %d", getpid()); system(cmd); sleep(10); } //should never happen! return ret; #endif return -1; } int cXineDevice::PlayTsImpl(const uchar *Data, int Length, bool VideoOnly /* = false */) { if (!Data) return PlaySingleTs(Data, Length, VideoOnly); int ret = 0; while (Length >= TS_SIZE) { int r = PlaySingleTs(Data, TS_SIZE, VideoOnly); if (r < 0) { if (!ret) return r; return ret; } ret += r; Length -= r; Data += r; } if (ret) return ret; if (Length > 0) return PlaySingleTs(Data, Length, VideoOnly); return ret; } int cXineDevice::PlaySingleTs(const uchar *Data, int Length, bool VideoOnly /* = false */) { #if APIVERSNUM >= 10701 if (!Data) { m_tsVideoPid = 0; delete m_tsVideoRemux; delete m_tsAudioRemux; m_tsVideoRemux = 0; m_tsAudioRemux = 0; } int ret = cDevice::PlayTs(Data, Length, VideoOnly); if (ret <= 0) return ret; if (!TsHasPayload(Data)) return ret; // silently ignore TS packets w/o payload if (TsIsScrambled(Data)) fprintf(stderr, "TS is scrambled **************\n"); int payloadOffset = TsPayloadOffset(Data); if (payloadOffset >= Length) return ret; int pid = TsPid(Data); if (pid == 0) { m_tsVideoPid = 0; return ret; } m_trampoline = true; sTrampoline trampoline = { { 0x00, 0x00, 0x01, 0xe0, 0x00, sizeof (trampoline) - 6 }, Data, Length, VideoOnly }; PlayPesPacket(trampoline.Header, sizeof (trampoline)); return ret; #endif return -1; } //static int aaz = 0; void cXineDevice::PlayTsTrampoline(const uchar *Data, int Length, bool VideoOnly /* = false */) { #if APIVERSNUM >= 10701 m_trampoline = false; const tTrackId *track = 0; int pid = TsPid(Data); if (pid == m_tsVideoPid) goto remux; else if ((track = GetTrack(GetCurrentAudioTrack())) && pid == track->id) { if (!VideoOnly || HasIBPTrickSpeed()) goto remux; } return; remux: static int vpid = 0; static int apid[2] = { 0, 0 }; static int dpid[2] = { 0, 0 }; int v = 0; int a = 0; int d = 0; v = m_tsVideoPid; if (!m_tsVideoRemux || vpid != v) { //fprintf(stderr, "vpid: %d, v: %d\n", vpid, v); delete m_tsVideoRemux; vpid = v; m_tsVideoRemux = new VDR172::cRemux(vpid, 0, 0, 0); m_tsVideoRemux->SetTimeouts(0, 0); } if (ttAudioFirst <= GetCurrentAudioTrack() && GetCurrentAudioTrack() <= ttAudioLast) a = (track = GetTrack(GetCurrentAudioTrack())) ? track->id : 0; else d = (track = GetTrack(GetCurrentAudioTrack())) ? track->id : 0; /* if (a || d) { if (aaz) if (aaz != (d ? 1 : 2)) *(char *)0 =0 ; } */ if (!m_tsAudioRemux || *apid != a || *dpid != d) { //fprintf(stderr, "a: %d, d: %d\n", a, d); delete m_tsAudioRemux; *apid = a; *dpid = d; m_tsAudioRemux = new VDR172::cRemux(0, apid, dpid, 0); m_tsAudioRemux->SetTimeouts(0, 0); } if (pid == vpid) { m_tsVideoRemux->Put(Data, Length); int n = 0; uchar pt; uchar *p = m_tsVideoRemux->Get(n, &pt); if (p) { if (m_xineLib.isTrickSpeedMode()) { if (pt == I_FRAME && VDR172::cRemux::IsFrameH264(p, n)) { fprintf(stderr, "TrickSpeedMode: push H.264 SequenceEndCode\n"); uchar sequenceEndCode[] = { 0x00, 0x00, 0x01, p[3], 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x01, 10 }; PlayVideo(sequenceEndCode, sizeof (sequenceEndCode)); } } int w = PlayVideo(p, n); m_tsVideoRemux->Del(w); } } else { m_tsAudioRemux->Put(Data, Length); int n = 0; uchar *p = m_tsAudioRemux->Get(n); if (p) { int w = PlayAudio(p, n, 0); m_tsAudioRemux->Del(w); } } #endif } int cXineDevice::PlayVideo(const uchar *Data, int Length) { #if APIVERSNUM >= 10701 if (m_trampoline) { sTrampoline *t = (sTrampoline *)Data; PlayTsTrampoline(t->Data, t->Length, t->VideoOnly); return Length; } #endif if (ttt4 == 0) ttt4 = tNow(); // static FILE *f = fopen("/tmp/egon1", "wb"); // fwrite(Data, Length, 1, f); return PlayVideo1(Data, Length, false); } int cXineDevice::PlayVideo1(const uchar *Data, int Length, const bool stillImageData) { LOG_ME(::fprintf(stderr, "V");) if (f) { LOG_ME(::fprintf(stderr, "<");) return 0; } if (pmNone == pm) { cMutexLock pmMutexLock(&m_pmMutex); if (pmNone == pm) m_pmCondVar.Wait(m_pmMutex); } int retVal = PlayVideo2(Data, Length, stillImageData); LOG_ME(::fprintf(stderr, "v");) return retVal; } int cXineDevice::PlayCommon2(const uchar *Data, int Length, int64_t ptsForce) { /* if (blahblah) { fprintf(stderr, "blahblah C2"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ do // ... while (false); { if (Length < 6) { VERBOSE_NOP(); break; } if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_NOP(); break; } int l = 6 + Data[ 4 ] * 0x0100 + Data[ 5 ]; if (Length < l) { VERBOSE_NOP(); break; } if (0xe0 != (Data[ 3 ] & 0xf0) //video && 0xc0 != (Data[ 3 ] & 0xe0) //audio && 0xbd != Data[ 3 ]) //private (dolby, pcm) { VERBOSE_NOP(); break; } int payloadOffset = 0; if (AnalyzePesHeader(Data, Length, payloadOffset) <= phInvalid) { VERBOSE_NOP(); break; } if (l <= payloadOffset) { // drop short frames // ::fprintf(stderr, "i"); return Length; } // if (0xc0 == (Data[ 3 ] & 0xe0)) //audio // return Length; } while (false); return PlayCommon3(Data, Length, ptsForce); } typedef long long int lld_t; static int xzabc = 0; int cXineDevice::PlayCommon3(const uchar *Data, int Length, int64_t ptsForce) { // if (!m_settings.LiveTV()) // { // VERBOSE_NOP1(); // return Length; // } // if (xzabc) if (0) { if (0xe0 == (Data[ 3 ] & 0xf0)) //video { uchar pt = NO_PICTURE; cRemux::ScanVideoPacket(Data, Length, 0, pt); if (pt != NO_PICTURE) { static int64_t last = -1; int64_t pts = -1; getPTS(Data, Length, pts); fprintf(stderr, "** %c ** %lld ** %lld ** %lld **\n", "0IPB4567"[pt], (lld_t)ptsForce, (lld_t)(ptsForce - last), (lld_t)pts); last = ptsForce; } } if (0xc0 == (Data[ 3 ] & 0xe0)) //audio { { static int64_t last = -1; int64_t pts = -1; getPTS(Data, Length, pts); fprintf(stderr, "\t\t\t\t\t\t\t** %c ** %lld ** %lld ** %lld **\n", 'A', (lld_t)ptsForce, (lld_t)(ptsForce - last), (lld_t)pts); last = ptsForce; } } } if (0) { int64_t pts = ptsForce; if (ptsForce > -1 || getPTS(Data, Length, pts)) { xzabc = 0; int64_t *pPTS = 0; if (0xe0 == (Data[ 3 ] & 0xf0)) //video { pPTS = &ptsV; } else if (0xc0 == (Data[ 3 ] & 0xe0)) //audio { pPTS = &ptsA; } else if (0xbd == Data[ 3 ]) //private (dolby, pcm) { int h = Data[ 6 + 2 ]; if (0xa0 == (0xf0 & Data[ 6 + 3 + h + 0 ])) // pcm? pPTS = &ptsP; else pPTS = &ptsD; } else { xfprintf(stderr, "0x%02x\t", Data[ 3 ]); VERBOSE_NOP(); } if (pPTS && *pPTS != pts) { *pPTS = pts; int64_t ptsX = -1; m_xineLib.execFuncGetPTS(ptsX); int64_t dV = (ptsV != -1 && ptsX != -1) ? ptsV - ptsX : 0; int64_t dA = (ptsA != -1 && ptsX != -1) ? ptsA - ptsX : 0; int64_t dP = (ptsP != -1 && ptsX != -1) ? ptsP - ptsX : 0; int64_t dD = (ptsD != -1 && ptsX != -1) ? ptsD - ptsX : 0; int64_t dVA = (ptsV != -1 && ptsA != -1) ? ptsA - ptsV : 0; int64_t dVD = (ptsV != -1 && ptsD != -1) ? ptsD - ptsV : 0; fprintf(stderr, "ptsVideo: %lld, ptsAudio: %lld, ptsPCM: %lld, ptsDolby: %lld, ptsXine: %lld, dVA: %lld, dVD: %lld, dV: %lld, dA: %lld, dP: %lld, dD: %lld\n", (lld_t)ptsV, (lld_t)ptsA, (lld_t)ptsP, (lld_t)ptsD, (lld_t)ptsX, (lld_t)dVA, (lld_t)dVD, (lld_t)dV, (lld_t)dA, (lld_t)dP, (lld_t)dD); } } } /* if (blahblah) { blahblah = false; fprintf(stderr, "blahblah C3"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ /* if (aaz) { if (Data[3] == 0xbd || (Data[3] & 0xe0) == 0xc0) { fprintf(stderr, "===== %c =====\n", Data[3] == 0xbd ? 'D' : 'A'); if (aaz != (Data[3] == 0xbd ? 1 : 2)) *(char *)0 = 0; aaz = 0; } } */ int done = 0; while (done < Length) { int r = m_xineLib.execFuncStream1(Data + done, Length - done); if (r < 0) return r; done += r; } return done; } int cXineDevice::PlayVideo2(const uchar *Data, int Length, const bool stillImageData) { // fprintf(stderr, "D"); int done = 0; while (done < Length) { char ch = 'X'; int todo = Length - done; if (todo >= 6) { if (0x00 == Data[ done + 0 ] && 0x00 == Data[ done + 1 ] && 0x01 == Data[ done + 2 ]) { int id = Data[ done + 3 ]; int len = 6 + Data[ done + 4 ] * 0x0100 + Data[ done + 5 ]; if (0xba == id) // pack header has fixed length { if (0x00 == (0xc0 & Data[ done + 4 ])) // MPEG 1 len = 12; else // MPEG 2 len = 14 + (Data[ done + 13 ] & 0x07); } else if (0xb9 == id) // stream end has fixed length { len = 4; } if (todo >= len) { todo = len; if (0xbe == id // padding || 0xba == id // system layer: pack header || 0xbb == id // system layer: system header || 0xb9 == id) // system layer: stream end { done += todo; // fprintf(stderr, "x"); continue; } ch = '.'; } else { // ::fprintf(stderr, "todo: %d, len: %d\t", todo, len); VERBOSE_NOP(); ch = '3'; // break; } } else { VERBOSE_NOP(); ch = '2'; } } else { VERBOSE_NOP(); ch = '1'; } // fprintf(stderr, "%c", ch); int r = PlayVideo3(Data + done, todo, stillImageData); if (r < 0) return r; done += r; } return done; } static void resetScramblingControl(const uchar *Data, int Length) { if (Length < 6) { VERBOSE_NOP(); return; } if (0x00 != Data[ 0 ] || 0x00 != Data[ 1 ] || 0x01 != Data[ 2 ]) { VERBOSE_NOP1(); return; } if (0xe0 != (0xf0 & Data[ 3 ]) // video && 0xc0 != (0xe0 & Data[ 3 ]) // audio && 0xbd != (0xff & Data[ 3 ]) // dolby && 0xbe != (0xff & Data[ 3 ]) // padding (DVD) && 0xba != (0xff & Data[ 3 ]) // system layer: pack header && 0xbb != (0xff & Data[ 3 ]) // system layer: system header && 0xb9 != (0xff & Data[ 3 ])) // system layer: stream end { VERBOSE_NOP(); return; } if (0xbe == (0xff & Data[ 3 ]) // padding (DVD) || 0xba == (0xff & Data[ 3 ]) // system layer: pack header || 0xbb == (0xff & Data[ 3 ]) // system layer: system header || 0xb9 == (0xff & Data[ 3 ])) // system layer: stream end { return; } int len = (6 + Data[ 4 ] * 0x100 + Data[ 5 ]); if (len < 6 + 1 || Length < 6 + 1) { VERBOSE_NOP(); return; } if (0x00 != (0x30 & Data[ 6 ])) { if (0x80 == (0xc0 & Data[ 6 ])) // only touch MPEG2 { xfprintf(stderr, "reseting PES_scrambling_control: 0x%02x\n", Data[ 6 ]); ((uchar *)Data)[ 6 ] &= ~0x30; } } } int cXineDevice::PlayVideo3(const uchar *Data, int Length, const bool stillImageData) { /* if (blahblah) { fprintf(stderr, "blahblah V3"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ resetScramblingControl(Data, Length); dumpAudio("Video", Data, Length); return PlayCommon(Data, Length, stillImageData); } static double videoFrameDuration(const uchar *Data, int Length) { int PesPayloadOffset = 0; ePesHeader ph = AnalyzePesHeader(Data, Length, PesPayloadOffset); if (ph < phMPEG1) return -1; if ((Data[ 3 ] & 0xf0) != 0xe0) return -1; if (Length < PesPayloadOffset + 8) return -1; const uchar *p = Data + PesPayloadOffset; if (*p++ != 0x00) return -1; if (*p++ != 0x00) return -1; if (*p++ != 0x01) return -1; if (*p++ != 0xb3) return -1; p += 3; static const double frameRates[ 16 ] = { -1, 24000/1001.0, 24, 25, 30000/1001.0, 30, 50, 60000/1001.0, 60, -1, -1, -1, -1, -1, -1, -1, }; int frameRateIndex = *p & 0x0f; if (frameRates[ frameRateIndex ] < 0) return -1; long n = 0, d = 0; if (ph == phMPEG2) { const uchar *const limit = Data + Length - 10 + 3; while (p < limit) { if (p[ 0 ] != 0x01 || p[ -1 ] != 0x00 || p[ -2 ] != 0x00) { p++; continue; } if (p[ 1 ] != 0xb5) // extension start code break; if ((p[ 2 ] & 0xf0) != 0x10) // sequence extension break; p += 7; n = (*p & 0x60) >> 5; d = (*p & 0x1f); break; } } return 90000 / (frameRates[ frameRateIndex ] * (n + 1) / (d + 1)); } static int frameSizes[ 256 ]; static double audioFrameDuration(const uchar *Data, int Length) { int PesPayloadOffset = 0; ePesHeader ph = AnalyzePesHeader(Data, Length, PesPayloadOffset); if (ph < phMPEG1) return -1; if ((Data[ 3 ] & 0xff) == 0xbd) { static int samplingFrequencies[ 4 ] = // all values are specified in Hz { 48000, 44100, 32000, -1 }; if (PesPayloadOffset + 5 <= Length && Data[ PesPayloadOffset + 0 ] == 0x0b && Data[ PesPayloadOffset + 1 ] == 0x77 && frameSizes[ Data[ PesPayloadOffset + 4 ] ] > 0) { if (samplingFrequencies[ Data[ PesPayloadOffset + 4 ] >> 6 ] < 0) return -1; return 90000.0 * 1536 / samplingFrequencies[ Data[ PesPayloadOffset + 4 ] >> 6 ]; } else if (PesPayloadOffset + 4 + 5 <= Length && Data[ PesPayloadOffset + 4 + 0 ] == 0x0b && Data[ PesPayloadOffset + 4 + 1 ] == 0x77 && frameSizes[ Data[ PesPayloadOffset + 4 + 4 ] ] > 0) { if (samplingFrequencies[ Data[ PesPayloadOffset + 4 + 4 ] >> 6 ] < 0) return -1; return 90000.0 * 1536 / samplingFrequencies[ Data[ PesPayloadOffset + 4 + 4 ] >> 6 ]; } else return -1; } if ((Data[ 3 ] & 0xe0) != 0xc0) return -1; if (Length < PesPayloadOffset + 4) return -1; ulong Header = 0; Header |= Data[ PesPayloadOffset + 0 ]; Header <<= 8; Header |= Data[ PesPayloadOffset + 1 ]; Header <<= 8; Header |= Data[ PesPayloadOffset + 2 ]; Header <<= 8; Header |= Data[ PesPayloadOffset + 3 ]; bool Mpeg2 = (ph == phMPEG2); int syncword = (Header & 0xFFF00000) >> 20; int id = (Header & 0x00080000) >> 19; int layer = (Header & 0x00060000) >> 17; // int protection_bit = (Header & 0x00010000) >> 16; int bitrate_index = (Header & 0x0000F000) >> 12; int sampling_frequency = (Header & 0x00000C00) >> 10; // int padding_bit = (Header & 0x00000200) >> 9; // int private_bit = (Header & 0x00000100) >> 8; // int mode = (Header & 0x000000C0) >> 6; // int mode_extension = (Header & 0x00000030) >> 4; // int copyright = (Header & 0x00000008) >> 3; // int orignal_copy = (Header & 0x00000004) >> 2; int emphasis = (Header & 0x00000003); if (syncword != 0xFFF) return -1; if (id == 0 && !Mpeg2) // reserved in MPEG 1 return -1; if (layer == 0) // reserved return -1; if (bitrate_index == 0xF) // forbidden return -1; if (sampling_frequency == 3) // reserved return -1; if (emphasis == 2) // reserved return -1; static int samplingFrequencies[ 2 ][ 4 ] = // all values are specified in Hz { { 44100, 48000, 32000, -1 }, // MPEG 1 { 22050, 24000, 16000, -1 } // MPEG 2 }; static int slots_per_frame[ 2 ][ 3 ] = { { 12, 144, 144 }, // MPEG 1, Layer I, II, III { 12, 144, 72 } // MPEG 2, Layer I, II, III }; int mpegIndex = 1 - id; int layerIndex = 3 - layer; // Layer I (i. e., layerIndex == 0) has a larger slot size int slotSize = (layerIndex == 0) ? 4 : 1; // bytes int sf = samplingFrequencies[ mpegIndex ][ sampling_frequency ]; //fprintf(stderr, "afd: %.3f ms, PES-Length: %d\n", 1000.0 * 8 * slotSize * slots_per_frame[ mpegIndex ][ layerIndex ] / sf, Length); return 90000.0 * 8 * slotSize * slots_per_frame[ mpegIndex ][ layerIndex ] / sf; } static double softStartTime = 0; static const double softStartSpeedStart = 0.75; static const double softStartSpeedMin = 0.15; static double softStartDurationVideoSD = 31 / 25.0; static double softStartDurationVideoHD = 31 / 25.0; static double softStartDurationAudio = 31 / 25.0; static const int softStartMaxSpeedChanges = 20; static double softStartLastSpeed = -1; static double softStartSpeedChangeTime = -1; static int64_t softStartPtsVdr = -1; static const int64_t softStartLogPtsDelta = 4 * 90000; static int softStartHitPoll = 0; static bool softStartNoMetronom = false; static const double pi = 4 * ::atan(1); static bool softStartHasVideo = true; static bool softStartHasAudio = true; static bool softStartIsVideoHD = false; inline double getSoftStartDuration(const bool forVideo) { if (forVideo) return softStartHasVideo ? (softStartIsVideoHD ? softStartDurationVideoHD : softStartDurationVideoSD) : 0; return softStartHasAudio ? softStartDurationAudio : 0; } inline double getSoftStartDuration() { double dV = getSoftStartDuration(true); double dA = getSoftStartDuration(false); return (dV > dA) ? dV : dA; } static double softStartCalcSpeed0(const double /* t */) { return 0; } static double softStartCalcSpeed1(const double t) { const double p = (1 + ::cos(2 * pi * t)) / 2; return softStartSpeedMin + p * ((softStartSpeedStart * (1 - t) + 1.0 * t) - softStartSpeedMin); } static double softStartCalcSpeed2(const double t) { double p = 2 * t - 1; if (p < 0) p = -p; // p = p * p * p; p = p * p; return softStartSpeedMin + p * ((softStartSpeedStart * (1 - t) + 1.0 * t) - softStartSpeedMin); } static double softStartCalcSpeed3(const double t) { if (t < 0.25) return softStartSpeedStart; if (t < 0.50) return softStartSpeedMin + (1 - softStartSpeedMin) * 0 / 3; if (t < 0.75) return softStartSpeedMin + (1 - softStartSpeedMin) * 1 / 3; return softStartSpeedMin + (1 - softStartSpeedMin) * 2 / 3; } static double softStartCalcSpeed4(const double t) { const double p = (1 + ::cos(pi * (1 + t))) / 2; return softStartSpeedMin + (1 - softStartSpeedMin) * p; } static double softStartCalcSpeed(const double t) { if (t >= 1) return 1; return softStartCalcSpeed0(t); // choose a method (void)softStartCalcSpeed0; (void)softStartCalcSpeed1; (void)softStartCalcSpeed2; (void)softStartCalcSpeed3; (void)softStartCalcSpeed4; } bool softStartPoll(cXineLib &xineLib, cPoller &poller, const int timeout, bool result) { if (softStartState > sIdle) { if (result) { softStartHitPoll = 0; } else if (++softStartHitPoll >= 2) { do { softStartState = sIdle; xineLib.execFuncFirstFrame(); xineLib.execFuncSetSpeed(100.0); xineLib.execFuncWait(); } while (!xineLib.Poll(poller, timeout, true)); softStartHitPoll = 0; result = true; } // ::fprintf(stderr, "softStartHitPoll: %d, %d\n", result, softStartHitPoll); } return result; } static int64_t vpts, apts, extra0, extra; static bool useApts; static bool seenAudio = false; static bool seenVideo = false; static bool seenApts = false; static bool seenVpts = false; static const int64_t extra_max = 50 * 90000 / 25; static bool oldMode = false; static inline int64_t calcPtsDelta(int64_t lhs, int64_t rhs) { int64_t delta = lhs - rhs; if (delta < -(1ll << 32)) delta += 1ll << 33; else if (delta > +(1ll << 32)) delta -= 1ll << 33; return delta; } static inline double softStartCalcQ0(int64_t vdr, int64_t xine, bool queued, bool forVideo, int64_t hyst) { if (vdr < 0 || xine < 0) return 0; double delta = calcPtsDelta(vdr, xine) / 90000.0; if (delta <= 0) return 0; if (delta >= softStartLogPtsDelta / 90000.0) return queued ? 0 : 1; if (delta >= (getSoftStartDuration(forVideo) + hyst / 90000.0)) return 1; return delta / (getSoftStartDuration(forVideo) + hyst / 90000.0); } static bool gotQ1 = false; static int64_t hystQ = 0; static int64_t hystQ1 = 0; static bool vdrTime100reload = false; static inline double softStartCalcQ(int64_t vdr, int64_t xine, bool queued, bool forVideo) { return softStartCalcQ0(vdr, xine, queued, forVideo, hystQ); } static inline double softStartCalcQ(int64_t vdrVideo, int64_t vdrAudio, int64_t xine, bool queued) { double qV = softStartHasVideo ? softStartCalcQ(vdrVideo, xine, queued, true) : 1; double qA = softStartHasAudio ? softStartCalcQ(vdrAudio, xine, queued, false) : 1; double q = (qV < qA) ? qV : qA; bool gQ1 = (q >= 1); if (gQ1 != gotQ1) { gotQ1 = gQ1; if (gotQ1) { hystQ = 0; } else { vdrTime100reload = true; hystQ = (hystQ1 += 90000 / 25); } } //fprintf(stderr, "q: %.3lf, gq1: %d, hystQ: %lld, hystQ1: %lld, vdr: %lld, xine: %lld, d: %lld\n", q, gotQ1, hystQ, hystQ1, vdr, xine, vdr - xine); return q; } static int64_t vdrPTSLast = -2; static int64_t vdrAptsLast = -1; static int64_t vdrVptsLast = -1; static double vdrVptsCalced = -1; static double vdrAptsCalced = -1; static double vdrVduration = -1; static double vdrAduration = -1; static double vdrVptsBuffered[ 2 ] = { -1, -1 }; static bool getPTS(const uchar *Data, int Length, int64_t &pts, bool isVideo, bool isAudio, double &vptsCalced, double &aptsCalced, double &vDuration, double &aDuration, double vptsBuffered[2]) { uchar pt = NO_PICTURE; if (isVideo) { cRemux::ScanVideoPacket(Data, Length, 0, pt); if (pt != NO_PICTURE && vDuration > 0 && vptsBuffered[ 1 ] > 0) vptsBuffered[ 1 ] += vDuration; } bool retVal = false; if (getPTS(Data, Length, pts)) { if (isAudio) { aptsCalced = pts; double duration = audioFrameDuration(Data, Length); if (duration >= 0) aDuration = duration; } if (isVideo) { if (pt == B_FRAME) vptsCalced = pts; else if (pt != NO_PICTURE) vptsBuffered[ 1 ] = pts; double duration = videoFrameDuration(Data, Length); if (duration >= 0) vDuration = duration; if (pt != B_FRAME) goto other; } return true; } else { if (isAudio) { double duration = audioFrameDuration(Data, Length); bool frameStart = (duration > -1); if (aptsCalced > -1 && aDuration > -1 && frameStart) { aptsCalced += aDuration; while (aptsCalced >= 0x200000000ull) aptsCalced -= 0x200000000ull; pts = (int64_t)aptsCalced; retVal = true; } if (duration >= 0) aDuration = duration; } if (isVideo) { other: double dur = vDuration; if (pt != B_FRAME && pt != NO_PICTURE) { vptsCalced = vptsBuffered[ 0 ]; vptsBuffered[ 0 ] = vptsBuffered[ 1 ]; dur = 0; } bool frameStart = (pt != NO_PICTURE); if (vptsCalced > -1 && dur > -1 && frameStart) { vptsCalced += dur; while (vptsCalced >= 0x200000000ull) vptsCalced -= 0x200000000ull; pts = (int64_t)vptsCalced; retVal = true; } double duration = videoFrameDuration(Data, Length); if (duration >= 0) vDuration = duration; } return retVal; } } static double vdrTime100 = -1; int cXineDevice::PlayCommon(const uchar *Data, int Length, const bool stillImageData) { const bool isAudio = !IsVideo(Data, Length); const bool isVideo = !isAudio && !stillImageData; //if (isAudio && ts) fprintf(stderr, "A"); if ((stillImageData || m_xineLib.isTrickSpeedMode()) && isAudio) { // ::fprintf(stderr, "x"); return Length; } /* if (stillImageData) fprintf(stderr, "writing: %d\n", Length - 6); */ if (findVideo && (isVideo || isAudio)) { findVideo = false; foundVideo = isVideo; //xfprintf(stderr, "foundVideo: %d\n", foundVideo); } if (0 && isAudio) { static int64_t opts = -1; static int64_t xpts = -1; getPTS(Data, Length, xpts, isVideo, isAudio, vdrVptsCalced, vdrAptsCalced, vdrVduration, vdrAduration, vdrVptsBuffered); //fprintf(stderr, "audio: %.3lf, %d, %lld, %lld\n", audioFrameDuration(Data, Length), Length, xpts, xpts - opts); opts = xpts; } if (0 && isVideo) { static int64_t opts = -1; static int64_t xpts = -1; getPTS(Data, Length, xpts, isVideo, isAudio, vdrVptsCalced, vdrAptsCalced, vdrVduration, vdrAduration, vdrVptsBuffered); //fprintf(stderr, "video: %.3lf, %d, %lld, %lld\n", videoFrameDuration(Data, Length), Length, xpts, xpts - opts); opts = xpts; } int64_t ptsForce = -1; { cMutexLock lock(&softStartMutex); if (softStartTrigger && !stillImageData) { softStartNoMetronom = (sstNoMetronom == softStartTrigger); softStartTrigger = sstNone; softStartState = sInitiateSequence; //fprintf(stderr, "/////////////////////////////\n"); // ::fprintf(stderr, "#(%d,%d)", ::getpid(), pthread_self()); } if (stillImageData) { softStartLastSpeed = -1; } else if (softStartState > sIdle) { timeval tv; ::gettimeofday(&tv, 0); const double now = (tv.tv_sec + tv.tv_usec / 1.0e+6); if (softStartState == sInitiateSequence) { xfprintf(stderr, "["); softStartDurationVideoSD = m_settings.GetModeParams()->m_prebufferFramesVideoSD / 25.0; softStartDurationVideoHD = m_settings.GetModeParams()->m_prebufferFramesVideoHD / 25.0; softStartDurationAudio = m_settings.GetModeParams()->m_prebufferFramesAudio / 25.0; softStartHasVideo = false; softStartHasAudio = false; softStartIsVideoHD = false; softStartTime = now; softStartSpeedChangeTime = -1; softStartLastSpeed = -1; softStartPtsVdr = -1; softStartHitPoll = 0; softStartState = sStartSequence; vpts = -1; apts = -1; extra0 = 0; extra = 0; useApts = 1; seenAudio = false; seenVideo = false; seenApts = false; seenVpts = false; vdrPTSLast = -2; vdrAptsLast = -1; vdrVptsLast = -1; vdrAptsCalced = -1; vdrVptsCalced = -1; vdrAduration = -1; vdrVduration = -1; vdrVptsBuffered[ 0 ] = -1; vdrVptsBuffered[ 1 ] = -1; vdrTime100 = -1; vdrTime100reload = false; gotQ1 = false; hystQ = hystQ1 = 90000 * m_settings.GetModeParams()->m_prebufferHysteresis / 25; } { char packetType = 0; char packetTypeOnce = 0; if (isVideo) { if (!seenVideo) { softStartHasVideo = true; softStartIsVideoHD = cRemux::IsFrameH264(Data, Length); //fprintf(stderr, "\nH264: %d\n", softStartIsVideoHD); seenVideo = true; // ::fprintf(stderr, "seen video\n"); // xfprintf(stderr, "v"); packetTypeOnce = 'v'; } packetType = 'v'; } else if (isAudio) { if (!seenAudio) { softStartHasAudio = true; audioSeen = true; seenAudio = true; // ::fprintf(stderr, "seen audio\n"); // xfprintf(stderr, "a"); packetTypeOnce = 'a'; } packetType = 'a'; } int64_t pts = 0; if (getPTS(Data, Length, pts, isVideo, isAudio, vdrVptsCalced, vdrAptsCalced, vdrVduration, vdrAduration, vdrVptsBuffered)) { ptsForce = pts; if (isVideo) { if (!seenVpts) { seenVpts = true; // ::fprintf(stderr, "seen video pts\n"); // xfprintf(stderr, "V"); packetTypeOnce = 'V'; } packetType = 'V'; vpts = pts; if (apts > -1) { int64_t delta = calcPtsDelta(vpts, apts); if (delta < 0) delta = - delta; if (extra0 < delta) { extra0 = delta; // ::fprintf(stderr, "max. A/V delta: %lld pts => total extra buffering: %d frames", extra0, (int)(extra0 * 25 / 90000)); extra = extra0; if (extra > extra_max) { extra = extra_max; // ::fprintf(stderr, ", limited to %d frames", (int)(extra * 25 / 90000)); } // ::fprintf(stderr, "\n"); if (oldMode) xfprintf(stderr, "+%d", (int)(extra * 25 / 90000)); } } // ::fprintf(stderr, "video: v: %lld, a: %lld, d: %lld, e: %lld\n", vpts, apts, vpts - apts, extra); } else if (isAudio) { if (!seenApts) { seenApts = true; // ::fprintf(stderr, "seen audio pts\n"); // xfprintf(stderr, "A"); packetTypeOnce = 'A'; } packetType = 'A'; apts = pts; if (vpts > -1) { int64_t delta = calcPtsDelta(vpts, apts); if (delta < 0) delta = - delta; if (extra0 < delta) { extra0 = delta; // ::fprintf(stderr, "max. A/V delta: %lld pts => total extra buffering: %d frames", extra0, (int)(extra0 * 25 / 90000)); extra = extra0; if (extra > extra_max) { extra = extra_max; // ::fprintf(stderr, ", limited to %d frames", (int)(extra * 25 / 90000)); } // ::fprintf(stderr, "\n"); if (oldMode) xfprintf(stderr, "+%d", (int)(extra * 25 / 90000)); } } // ::fprintf(stderr, "audio: v: %lld, a: %lld, d: %lld, e: %lld\n", vpts, apts, vpts - apts, extra); } } //xfprintf(stderr, "%s%c", getFrameType(Data, Length), packetType); packetTypeOnce = 0; //ZZZ // if (packetTypeOnce) xfprintf(stderr, "%c", packetTypeOnce); } if ((seenVideo && !seenVpts) || (seenAudio && !seenApts)) { softStartTime = now; } if (softStartState >= sStartSequence) { if (isVideo) useApts = false; if (useApts || isVideo) { softStartPtsVdr = ptsForce; // getPTS(Data, Length, softStartPtsVdr); if (softStartPtsVdr != -1) { bool queued = false; int64_t ptsXine = -1; m_xineLib.execFuncGetPTS(ptsXine, 0, &queued); const int64_t delta = (ptsXine >= 0) ? calcPtsDelta(softStartPtsVdr, ptsXine) : 0; if (softStartState == sStartSequence || delta < -softStartLogPtsDelta || (delta > +softStartLogPtsDelta && queued)) { // if (softStartState != sStartSequence) // ::fprintf(stderr, "SoftStart: ptsVdr: %12"PRId64", ptsXine: %12"PRId64", delta: %12"PRId64", queued: %d\n", softStartPtsVdr, ptsXine, delta, queued); //AAA m_xineLib.execFuncStart(); //AAA m_xineLib.execFuncWait(); if (!softStartNoMetronom) { xfprintf(stderr, "M"); // xfprintf(stderr, " %12" PRId64 ", %12" PRId64 ", %12" PRId64 "\n", delta, softStartPtsVdr, ptsXine); //ZZZ m_xineLib.execFuncMetronom(softStartPtsVdr); //ZZZ m_xineLib.execFuncWait(); } softStartTime = now; softStartState = sContinueSequence; } } } } // if (softStartState <= sStartSequence) // stripPTSandDTS((uchar *)Data, Length); m_xineLib.execFuncFirstFrame(); int64_t vdrPTS = -1; if ((seenVideo && vpts <= -1) || (seenAudio && apts <= -1)) { } else if (vpts > -1) { if (apts > -1 && vpts > apts) vdrPTS = apts; else vdrPTS = vpts; } else if (apts > -1) vdrPTS = apts; bool queued = false; int64_t xinePTS = -1; m_xineLib.execFuncGetPTS(xinePTS, 0, &queued); const double totalDuration = (getSoftStartDuration() + extra / 90000.0); const double q = oldMode ? ((now - softStartTime) / totalDuration) : softStartCalcQ(vpts, apts, xinePTS, queued); double p = softStartCalcSpeed(q); if (!oldMode) { if (vdrPTSLast == vdrPTS || vdrVptsLast > vpts || vdrAptsLast > apts) p = softStartLastSpeed / 100; else vdrPTSLast = vdrPTS; if (apts != vdrAptsLast || vpts != vdrVptsLast) { // xfprintf(stderr, "p: %.3lf, DA: %lld, DV: %lld, DC: %lld\n", p, (apts > -1) ? (apts - xinePTS) : -1, (vpts > -1) ? (vpts - xinePTS) : -1, (vdrPTS > - 1) ? (vdrPTS - xinePTS) : -1); // xfprintf(stderr, "(%.1lf|%.1lf)", 25 * ((apts > -1) ? ((apts - xinePTS) / 90000.0) : 0.0), 25 * ((vpts > -1) ? ((vpts - xinePTS) / 90000.0) : 0.0)); } vdrVptsLast = vpts; vdrAptsLast = apts; } double speed = (p > 0) ? (100.0 * p) : 12.5; if (speed >= 100.0 || xinePTS == -1) { bool newlineRequired = false; if (vdrTime100 < 0) { ttt6 = tNow(); /* fprintf(stderr, "+++++ %.3lf ms, %.3lf ms, %.3lf ms, (%.3lf ms, %.3lf ms, %.3lf ms) +++++\n" , (ttt1 - ttt0) * 1000.0 , (ttt2 - ttt1) * 1000.0 , (ttt3 - ttt2) * 1000.0 , (ttt4 - ttt3) * 1000.0 , (ttt5 - ttt3) * 1000.0 , (ttt6 - ttt3) * 1000.0 ); */ vdrTime100 = now; xfprintf(stderr, "]"); newlineRequired = true; } speed = 100.0; if (vdrTime100reload) { vdrTime100reload = false; vdrTime100 = now; } if (xinePTS < 0) softStartState = sIdle; else if ((now - vdrTime100) >= m_settings.GetModeParams()->m_monitoringDuration) { if (m_settings.GetModeParams()->MonitoringContinuous()) hystQ1 = 90000 * (m_settings.GetModeParams()->m_prebufferHysteresis - 1) / 25; else softStartState = sIdle; } if ((softStartState == sIdle || speed != softStartLastSpeed) && vdrPTS > -1 && xinePTS > -1) { xfprintf(stderr, "buffered %.1lf frames (v:%.1lf, a:%.1lf)", calcPtsDelta(vdrPTS, xinePTS) / 90000.0 * 25, (vpts > -1) ? (calcPtsDelta(vpts, xinePTS) / 90000.0 * 25) : 0.0, (apts > -1) ? (calcPtsDelta(apts, xinePTS) / 90000.0 * 25) : 0.0); if (softStartState == sIdle) xfprintf(stderr, " <<<<<"); newlineRequired = true; } if (newlineRequired) xfprintf(stderr, "\n"); } if (queued) speed = 100; if (100.0 == speed || (speed != softStartLastSpeed && (!oldMode || (now - softStartSpeedChangeTime) > (totalDuration / softStartMaxSpeedChanges)))) { softStartSpeedChangeTime = now; // fprintf(stderr, "slowstart: %lg, %lg\n", speed, p); m_xineLib.execFuncSetSpeed(speed); m_xineLib.execFuncWait(); // if (100.0 == speed) // m_xineLib.execFuncSetPrebuffer(m_settings.GetModeParams()->m_prebufferFrames); softStartLastSpeed = speed; } } } //if (jw) *(char *)0 = 0; jw = false; // fprintf(stderr, "v"); if (ts) { //NNN stripPTSandDTS((uchar *)Data, Length); } else if (0) //ZZZ { int64_t pts = 0; if (np && getPTS(Data, Length, pts)) { np = false; // fprintf(stderr, "M %lld %llx\n", pts); m_xineLib.execFuncMetronom(pts); m_xineLib.execFuncWait(); } } int r = PlayCommon1(Data, Length, ptsForce); // fprintf(stderr, "V"); return r; } int cXineDevice::PlayCommon1(const uchar *Data, int Length, int64_t ptsForce) { /* if (blahblah) { fprintf(stderr, "blahblah C1"); for (int i = 0; i < 50 && i < Length; i++) fprintf(stderr, " %02x", Data[ i ]); fprintf(stderr, "\n"); } */ if (oldMode) return PlayCommon2(Data, Length, ptsForce); struct sBuffer { sBuffer *next; int length; int64_t ptsForce; uchar data[1]; }; static sBuffer *pHead = 0; static sBuffer *pTail = 0; static int bufCount = 0; while (!doClear && pHead) { if (sIdle != softStartState) { cPoller p; if (!m_xineLib.Poll(p, -1)) break; } int r = PlayCommon2(pHead->data, pHead->length, pHead->ptsForce); if (r > 0) { sBuffer *p = pHead; pHead = pHead->next; ::free(p); //fprintf(stderr, "bufCount: %d\n", --bufCount); } else if (r < 0) { while (pHead) { sBuffer *p = pHead; pHead = pHead->next; ::free(p); } bufCount = 0; pTail = 0; //fprintf(stderr, "--- bufCount: %d\n", bufCount); return r; } else break; } if (doClear) { //fprintf(stderr, "----- clearing -----\n"); while (pHead) { sBuffer *p = pHead; pHead = pHead->next; ::free(p); } bufCount = 0; pTail = 0; doClear = false; //fprintf(stderr, "=== bufCount: %d\n", bufCount); // return Length; } if (!pHead) { pTail = 0; do // ... while (false); { if (sIdle != softStartState) { cPoller p; if (!m_xineLib.Poll(p, -1)) break; //fprintf(stderr, "### bufCount: %d\n", bufCount); } return PlayCommon2(Data, Length, ptsForce); } while (false); } sBuffer *p = (sBuffer *)::malloc(sizeof (sBuffer) - sizeof (p->data) + Length); if (!p) return 0; p->next = 0; p->length = Length; p->ptsForce = ptsForce; memcpy(&p->data, Data, Length); if (!pTail) pHead = pTail = p; else { pTail->next = p; pTail = p; } //fprintf(stderr, "*** bufCount: %d\n", ++bufCount); return Length; } static uchar jumboPESdata[ 6 + 0xffff ]; static uchar *jumboPEStailData = 0; static bool mkJumboPES(const uchar *Data, int Length) { int origJumboPESsize = jumboPESsize; jumboPESsize = 0; if (Length < 9) VERBOSE_RETURN0(false); if (0x00 != Data[ 0 ]) VERBOSE_RETURN0(false); if (0x00 != Data[ 1 ]) VERBOSE_RETURN0(false); if (0x01 != Data[ 2 ]) VERBOSE_RETURN0(false); if (0xbd != Data[ 3 ]) VERBOSE_RETURN0(false); int l = Data[ 4 ] * 256 + Data[ 5 ]; if ((6 + l) != Length) { const uchar *data = Data + (6 + l); int length = Length - (6 + l); if (length < 6) VERBOSE_RETURN3(false); if (0x00 != data[ 0 ]) VERBOSE_RETURN3(false); if (0x00 != data[ 1 ]) VERBOSE_RETURN3(false); if (0x01 != data[ 2 ]) VERBOSE_RETURN3(false); if (0xbe != data[ 3 ]) VERBOSE_RETURN3(false); int L = data[ 4 ] * 256 + data[ 5 ]; if ((6 + L) != length) VERBOSE_RETURN3(false); // ignore padding Length -= length; } /* for (int i = 0; i < 20; i++) fprintf(stderr, "%02x ", Data[ i ]); fprintf(stderr, "\n"); */ bool cont = (0x80 == Data[ 6 ] && 0x00 == Data[ 7 ] && 0x00 == Data[ 8 ]); if (cont && Length >= 6 + 3 + 5 && Data[ 9 ] == 0x0b && Data[ 10 ] == 0x77 && frameSizes[ Data[ 13 ] ] > 0) { cont = false; } if (!cont || 0 == origJumboPESsize) { if (0 != origJumboPESsize) VERBOSE_RETURN0(false); if ((origJumboPESsize + Length - 0) > (6 + 0xffff)) VERBOSE_RETURN0(false); if (jumboPEStail > 0) { int headerSize = 6 + 3 + Data[ 8 ]; ::memcpy(&jumboPESdata[ origJumboPESsize ], &Data[ 0 ], headerSize); ::memmove(&jumboPESdata[ origJumboPESsize + headerSize ], jumboPEStailData, jumboPEStail); ::memcpy(&jumboPESdata[ origJumboPESsize + headerSize + jumboPEStail ], &Data[ headerSize ], Length - headerSize); origJumboPESsize += headerSize + jumboPEStail + Length - headerSize; jumboPEStail = 0; jumboPEStailData = 0; //FIXME: PTS should be adjusted to take care of jumboPEStail's duration. // Otherwise there is a certain jitter on audio duration <=> PTS. } else { ::memcpy(&jumboPESdata[ origJumboPESsize ], &Data[ 0 ], Length - 0); origJumboPESsize += Length - 0; } } else { if (0 == origJumboPESsize) VERBOSE_RETURN0(false); if ((origJumboPESsize + Length - 9) > (6 + 0xffff)) VERBOSE_RETURN0(false); ::memcpy(&jumboPESdata[ origJumboPESsize ], &Data[ 9 ], Length - 9); origJumboPESsize += Length - 9; } if (0 == origJumboPESsize) VERBOSE_RETURN0(false); jumboPESsize = origJumboPESsize; if (2048 == Length) { // fprintf(stderr, " b %d", jumboPESsize); return false; } jumboPESdata[ 4 ] = (jumboPESsize - 6) >> 8; jumboPESdata[ 5 ] = (jumboPESsize - 6) & 0xff; // fprintf(stderr, " B %d", jumboPESsize); return true; } static int initFrameSizes() { ::memset(frameSizes, 0, sizeof (frameSizes)); // fs = 48 kHz frameSizes[ 0x00 ] = 64; frameSizes[ 0x01 ] = 64; frameSizes[ 0x02 ] = 80; frameSizes[ 0x03 ] = 80; frameSizes[ 0x04 ] = 96; frameSizes[ 0x05 ] = 96; frameSizes[ 0x06 ] = 112; frameSizes[ 0x07 ] = 112; frameSizes[ 0x08 ] = 128; frameSizes[ 0x09 ] = 128; frameSizes[ 0x0a ] = 160; frameSizes[ 0x0b ] = 160; frameSizes[ 0x0c ] = 192; frameSizes[ 0x0d ] = 192; frameSizes[ 0x0e ] = 224; frameSizes[ 0x0f ] = 224; frameSizes[ 0x10 ] = 256; frameSizes[ 0x11 ] = 256; frameSizes[ 0x12 ] = 320; frameSizes[ 0x13 ] = 320; frameSizes[ 0x14 ] = 384; frameSizes[ 0x15 ] = 384; frameSizes[ 0x16 ] = 448; frameSizes[ 0x17 ] = 448; frameSizes[ 0x18 ] = 512; frameSizes[ 0x19 ] = 512; frameSizes[ 0x1a ] = 640; frameSizes[ 0x1b ] = 640; frameSizes[ 0x1c ] = 768; frameSizes[ 0x1d ] = 768; frameSizes[ 0x1e ] = 896; frameSizes[ 0x1f ] = 896; frameSizes[ 0x20 ] = 1024; frameSizes[ 0x21 ] = 1024; frameSizes[ 0x22 ] = 1152; frameSizes[ 0x23 ] = 1152; frameSizes[ 0x24 ] = 1280; frameSizes[ 0x25 ] = 1280; // fs = 44.1 kHz frameSizes[ 0x40 ] = 69; frameSizes[ 0x41 ] = 70; frameSizes[ 0x42 ] = 87; frameSizes[ 0x43 ] = 88; frameSizes[ 0x44 ] = 104; frameSizes[ 0x45 ] = 105; frameSizes[ 0x46 ] = 121; frameSizes[ 0x47 ] = 122; frameSizes[ 0x48 ] = 139; frameSizes[ 0x49 ] = 140; frameSizes[ 0x4a ] = 174; frameSizes[ 0x4b ] = 175; frameSizes[ 0x4c ] = 208; frameSizes[ 0x4d ] = 209; frameSizes[ 0x4e ] = 243; frameSizes[ 0x4f ] = 244; frameSizes[ 0x50 ] = 278; frameSizes[ 0x51 ] = 279; frameSizes[ 0x52 ] = 348; frameSizes[ 0x53 ] = 349; frameSizes[ 0x54 ] = 417; frameSizes[ 0x55 ] = 418; frameSizes[ 0x56 ] = 487; frameSizes[ 0x57 ] = 488; frameSizes[ 0x58 ] = 557; frameSizes[ 0x59 ] = 558; frameSizes[ 0x5a ] = 696; frameSizes[ 0x5b ] = 697; frameSizes[ 0x5c ] = 835; frameSizes[ 0x5d ] = 836; frameSizes[ 0x5e ] = 975; frameSizes[ 0x5f ] = 976; frameSizes[ 0x60 ] = 1114; frameSizes[ 0x61 ] = 1115; frameSizes[ 0x62 ] = 1253; frameSizes[ 0x63 ] = 1254; frameSizes[ 0x64 ] = 1393; frameSizes[ 0x65 ] = 1394; // fs = 32 kHz frameSizes[ 0x80 ] = 96; frameSizes[ 0x81 ] = 96; frameSizes[ 0x82 ] = 120; frameSizes[ 0x83 ] = 120; frameSizes[ 0x84 ] = 144; frameSizes[ 0x85 ] = 144; frameSizes[ 0x86 ] = 168; frameSizes[ 0x87 ] = 168; frameSizes[ 0x88 ] = 192; frameSizes[ 0x89 ] = 192; frameSizes[ 0x8a ] = 240; frameSizes[ 0x8b ] = 240; frameSizes[ 0x8c ] = 288; frameSizes[ 0x8d ] = 288; frameSizes[ 0x8e ] = 336; frameSizes[ 0x8f ] = 336; frameSizes[ 0x90 ] = 384; frameSizes[ 0x91 ] = 384; frameSizes[ 0x92 ] = 480; frameSizes[ 0x93 ] = 480; frameSizes[ 0x94 ] = 576; frameSizes[ 0x95 ] = 576; frameSizes[ 0x96 ] = 672; frameSizes[ 0x97 ] = 672; frameSizes[ 0x98 ] = 768; frameSizes[ 0x99 ] = 768; frameSizes[ 0x9a ] = 960; frameSizes[ 0x9b ] = 960; frameSizes[ 0x9c ] = 1152; frameSizes[ 0x9d ] = 1152; frameSizes[ 0x9e ] = 1344; frameSizes[ 0x9f ] = 1344; frameSizes[ 0xa0 ] = 1536; frameSizes[ 0xa1 ] = 1536; frameSizes[ 0xa2 ] = 1728; frameSizes[ 0xa3 ] = 1728; frameSizes[ 0xa4 ] = 1920; frameSizes[ 0xa5 ] = 1920; return 0; }; #if APIVERSNUM < 10318 void cXineDevice::PlayAudio(const uchar *Data, int Length) { cDevice::PlayAudio(Data, Length); PlayAudioCommon(Data, Length); } #else int cXineDevice::GetAudioChannelDevice(void) { return m_audioChannel; } static int m_acd = -1; void cXineDevice::SetAudioChannelDevice(int AudioChannel) { #if APIVERSNUM >= 10701 if (m_acd == AudioChannel) return; #endif m_acd = AudioChannel; xfprintf(stderr, "SetAudioChannelDevice: %d\n", AudioChannel); m_audioChannel = AudioChannel; m_xineLib.execFuncSelectAudio(m_audioChannel); } static int m_dad = -1; void cXineDevice::SetDigitalAudioDevice(bool On) { #if APIVERSNUM >= 10701 if (m_dad == On) return; #endif m_dad = On; xfprintf(stderr, "SetDigitalAudioDevice: %d\n", On); //aaz = On ? 1 : 2; m_xineLib.execFuncSelectAudio(On ? -1 : m_audioChannel); if (pmNone == pm) return; doClear = true; if (m_settings.LiveTV() && !audioSeen) { cMutexLock lock(&softStartMutex); if (softStartState == sIdle) softStartTrigger = sstNoMetronom; return; } m_xineLib.pause(); //xzabc = 1; jumboPESsize = 0; jumboPEStail = 0; if (f) m_xineLib.execFuncSetSpeed(100.0); //double t0 = tNow(); if (m_settings.LiveTV() || !foundVideo) // radio recording: audio channels are not related { ptsV = ptsA = ptsP = ptsD = -1; m_xineLib.execFuncClear(-3); // if (!foundVideo) // m_xineLib.execFuncStart(); // np = true; } m_xineLib.execFuncResetAudio(); if (f) m_xineLib.execFuncSetSpeed(0.0); m_xineLib.execFuncWait(); //xzabc = 2; m_xineLib.pause(false); //double t1 = tNow(); fprintf(stderr, "!!!!!!! %.3lf ms\n", (t1 - t0) * 1000); if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); softStartTrigger = sstNoMetronom; } //fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); } #if APIVERSNUM < 10342 int cXineDevice::PlayAudio(const uchar *Data, int Length) { // fprintf(stderr, " 0x%02x ", Data[ 3 ]); return PlayAudioCommon(Data, Length); } #else int cXineDevice::PlayAudio(const uchar *Data, int Length, uchar /* Id */) { // fprintf(stderr, " 0x%02x ", Data[ 3 ]); return PlayAudioCommon(Data, Length); } #endif #endif int cXineDevice::PlayAudioCommon(const uchar *Data, int Length) { if (ttt5 == 0) ttt5 = tNow(); // fprintf(stderr, " 0x%02x: %d ", Data[ 3 ], Data[4] * 256 + Data[5]); /* if (Data[ 3 ] == 0xd0) { FILE *f = fopen("/tmp/d0", "ab"); fwrite(Data, Length, 1, f); fclose(f); } */ { static int i = initFrameSizes(); (void)i; } store_frame(Data, Length, __LINE__); LOG_ME(::fprintf(stderr, "A");) if (f) { LOG_ME(::fprintf(stderr, "<");) return Length; } if (pmNone == pm) { cMutexLock pmMutexLock(&m_pmMutex); if (pmNone == pm) m_pmCondVar.Wait(m_pmMutex); } int retVal = PlayAudio2(Data, Length); LOG_ME(::fprintf(stderr, "a")); return retVal; } int cXineDevice::PlayAudio2(const uchar *Data, int Length) { // fprintf(stderr, "D"); int done = 0; while (done < Length) { char ch = 'X'; int id = 0x00; int todo = Length - done; if (todo >= 6) { if (0x00 == Data[ done + 0 ] && 0x00 == Data[ done + 1 ] && 0x01 == Data[ done + 2 ]) { id = Data[ done + 3 ]; int len = Data[ done + 4 ] * 0x0100 + Data[ done + 5 ]; if (todo >= (6 + len)) { todo = (6 + len); if (0xbe == id) { done += todo; // ::fprintf(stderr, "x"); continue; } ch = '.'; } else { VERBOSE_NOP(); ch = '3'; } } else { VERBOSE_NOP(); ch = '2'; } } else { VERBOSE_NOP(); ch = '1'; } // ::fprintf(stderr, "%c", ch); int r; if (0xbd == id) r = PlayAudio3(Data + done, todo); else r = PlayVideo3(Data + done, todo, false); if (r < 0) return r; if (r != todo) VERBOSE_NOP(); done += r; } return done; } int cXineDevice::PlayAudio3(const uchar *Data, int Length) { resetScramblingControl(Data, Length); store_frame(Data, Length, __LINE__); /* ::fprintf(stderr, "l: %d\t", Length); for (int i = 0; i < 20; i++) ::fprintf(stderr, "%02x ", Data[ i ]); ::fprintf(stderr, "\n"); */ // fprintf(stderr, "A"); if (mkJumboPES(Data, Length)) { int todo = jumboPESsize; jumboPESsize = 0; dumpAudio("Audio", jumboPESdata, todo); bool dolby = false; bool pcm = false; do { if (todo < (6 + 3 + 0 + 2)) break; if (0x00 != jumboPESdata[ 0 ] || 0x00 != jumboPESdata[ 1 ] || 0x01 != jumboPESdata[ 2 ] || 0xbd != jumboPESdata[ 3 ]) { break; } int l = jumboPESdata[ 4 ] * 0x0100 + jumboPESdata[ 5 ]; if (l < (3 + 0 + 2)) break; if (todo < (6 + l)) break; int h = jumboPESdata[ 8 ]; if (l < (3 + h + 2)) break; if (0x0b == jumboPESdata[ 6 + 3 + h + 0 ] && 0x77 == jumboPESdata[ 6 + 3 + h + 1 ]) { if (l < (3 + h + 2 + 2 + 1)) { VERBOSE_NOP(); break; } int frameStart = 6 + 3 + h; bool failed = false; while (true) { int frameSize = 2 * frameSizes[ jumboPESdata[ frameStart + 4 ] ]; if (frameSize <= 0) { failed = true; xfprintf(stderr, "frame_size_code: 0x%02x\n", jumboPESdata[ frameStart + 4 ]); VERBOSE_NOP(); break; } if (frameStart + frameSize > todo) break; frameStart += frameSize; if (frameStart + 2 + 2 + 1 > todo) break; if (0x0b != jumboPESdata[ frameStart + 0 ] || 0x77 != jumboPESdata[ frameStart + 1 ]) { failed = true; VERBOSE_NOP(); break; } } if (failed) break; jumboPEStail = todo - frameStart; jumboPEStailData = jumboPESdata + frameStart; todo = frameStart; jumboPESdata[ 4 + 0 ] = (todo - 6) >> 8; jumboPESdata[ 4 + 1 ] = (todo - 6) & 0xff; dolby = true; store_frame(jumboPESdata, todo, __LINE__); break; } if (0x80 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { dolby = true; break; } if (0xa0 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { pcm = true; break; } for (int i = 6 + 3 + h; i < todo - 2 - 2 - 1; i++) { if (0x0b == jumboPESdata[ i + 0 ] && 0x77 == jumboPESdata[ i + 1 ] && 0 != frameSizes[ jumboPESdata[ i + 4 ] ]) { jumboPEStail = todo - i; jumboPEStailData = jumboPESdata + i; } } } while (false); if (pcm || (dolby && m_settings.AudioDolbyOn())) { int done = 0; while (done < todo) { int r = PlayCommon(jumboPESdata + done, todo - done, false); if (r < 0) return r; // fprintf(stderr, "."); done += r; } // Don't return done here as the remaining bytes were buffered elsewhere! // return done; } } else if (jumboPESsize == Length) { int todo = jumboPESsize; bool dolby = false; bool pcm = false; do { if (todo < (6 + 3 + 0 + 2)) break; if (0x00 != jumboPESdata[ 0 ] || 0x00 != jumboPESdata[ 1 ] || 0x01 != jumboPESdata[ 2 ] || 0xbd != jumboPESdata[ 3 ]) { break; } int l = jumboPESdata[ 4 ] * 0x0100 + jumboPESdata[ 5 ]; if (l < (3 + 0 + 2)) break; if (todo < (6 + l)) break; int h = jumboPESdata[ 8 ]; if (l < (3 + h + 2)) break; if (0x80 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { dolby = true; break; } if (0xa0 == (0xf0 & jumboPESdata[ 6 + 3 + h + 0 ])) { pcm = true; break; } } while (false); if (dolby || pcm) { dumpAudio("Audio", jumboPESdata, todo); jumboPESsize = 0; } if (pcm || (dolby && m_settings.AudioDolbyOn())) { int done = 0; while (done < todo) { int r = PlayCommon(jumboPESdata + done, todo - done, false); if (r < 0) return r; // fprintf(stderr, "."); done += r; } // Don't return done here as the remaining bytes were buffered elsewhere! // return done; } } // fprintf(stderr, "\n"); return Length; } #if APIVERSNUM >= 10338 uchar *cXineDevice::GrabImage(int &Size, bool Jpeg /* = true */, int Quality /* = -1 */, int SizeX /* = -1 */, int SizeY /* = -1 */) { const char *const FileName = 0; #else bool cXineDevice::GrabImage(const char *FileName, bool Jpeg /* = true */, int Quality /* = -1 */, int SizeX /* = -1 */, int SizeY /* = -1 */) { int Size = 0; #endif xfprintf(stderr, "GrabImage ...\n\n"); if (-1 == Quality) Quality = 100; if (-1 == SizeX) SizeX = m_settings.DefaultGrabSizeX(); if (-1 == SizeY) SizeY = m_settings.DefaultGrabSizeY(); uchar *result = m_xineLib.execFuncGrabImage(FileName, Size, Jpeg, Quality, SizeX, SizeY); xfprintf(stderr, result ? "\nGrabImage succeeded.\n" : "\nGrabImage failed.\n"); return result; } int64_t cXineDevice::GetSTC(void) { // ::fprintf(stderr, "GetSTC: "); int64_t pts = -1; if (!m_xineLib.execFuncGetPTS(pts, 100) || pts < 0) pts = cDevice::GetSTC(); // ::fprintf(stderr, "%lld\n", pts); return pts; } void cXineDevice::SetVideoFormat(bool VideoFormat16_9) { xfprintf(stderr, "SetVideoFormat: %d\n", VideoFormat16_9); cDevice::SetVideoFormat(VideoFormat16_9); m_is16_9 = VideoFormat16_9; m_xineLib.selectNoSignalStream(VideoFormat16_9); } #if APIVERSNUM >= 10707 #if APIVERSNUM >= 10708 void cXineDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) { int x, y, zx, zy; m_xineLib.execFuncVideoSize(x, y, Width, Height, zx, zy, &VideoAspect); } void cXineDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) #else void cXineDevice::GetVideoSize(int &Width, int &Height, eVideoAspect &Aspect) // function name misleading -- max osd extent shall be returned #endif { Width = m_settings.OsdExtentParams().GetOsdExtentWidth(); Height = m_settings.OsdExtentParams().GetOsdExtentHeight(); #if APIVERSNUM >= 10708 PixelAspect = (m_is16_9 ? 16.0 : 4.0) * Height / ((m_is16_9 ? 9.0 : 3.0) * Width); //fprintf(stderr, "GetOsdSize(%d, %d, %lg)\n", Width, Height, PixelAspect); #else Aspect = m_is16_9 ? va16_9 : va4_3; #endif } #endif static bool firstCallToSetVolume = true; static void switchSkin(const bool restore); void cXineDevice::SetVolumeDevice(int Volume) { if (firstCallToSetVolume) { firstCallToSetVolume = false; if (m_settings.ShallSwitchSkin()) switchSkin(false); } xfprintf(stderr, "SetVolumeDevice: %d\n", Volume); m_xineLib.execFuncSetVolume(Volume); } #if APIVERSNUM < 10307 cOsdBase *cXineDevice::NewOsd(int w, int h, int x, int y) #elif APIVERSNUM < 10509 cOsd *cXineDevice::NewOsd(int w, int h, int x, int y) #else cOsd *cXineDevice::NewOsd(int w, int h, int x, int y, uint Level, bool TrueColor /* = false */) #endif { // ::fprintf(stderr, "NewOsd ---: %s\n", ::ctime(&(const time_t &)::time(0))); cXineOsdMutexLock osdLock(&m_osdMutex); // ::fprintf(stderr, "NesOsd +++: %s\n", ::ctime(&(const time_t &)::time(0))); #if APIVERSNUM < 10509 if (m_currentOsd) { esyslog("vdr-xine: new OSD(%d, %d) requested while another OSD is active", x, y); xfprintf(stderr, "vdr-xine: new OSD(%d, %d) requested while another OSD is active", x, y); return 0; } #endif if (x < 0 || y < 0) { esyslog("vdr-xine: new OSD(%d, %d) requested with coordinates out of range", x, y); xfprintf(stderr, "vdr-xine: new OSD(%d, %d) requested with coordinates out of range", x, y); } if (w <= 0 || h <= 0) { w = m_settings.OsdExtentParams().GetOsdExtentWidth(); h = m_settings.OsdExtentParams().GetOsdExtentHeight(); } #if APIVERSNUM < 10509 m_currentOsd = new cXineOsd(*this, w, h, x, y); #else return new cXineOsd(*this, w, h, x, y, Level); #endif return m_currentOsd; } #if APIVERSNUM < 10307 void cXineDevice::OnFreeOsd(cOsdBase *const osd) #else void cXineDevice::OnFreeOsd(cOsd *const osd) #endif { #if APIVERSNUM < 10509 cXineOsdMutexLock osdLock(&m_osdMutex); assert(osd == m_currentOsd); m_currentOsd = 0; #endif } bool cXineDevice::ChangeCurrentOsd(cXineOsd *const osd, bool on) { cXineOsdMutexLock osdLock(&m_osdMutex); if (m_currentOsd) { if (on) { esyslog("vdr-xine: OSD activation requested while another OSD is active -- ignoring request"); xfprintf(stderr, "vdr-xine: OSD activation requested while another OSD is active -- ignoring request"); return false; } else if (m_currentOsd != osd) { esyslog("vdr-xine: OSD deactivation requested for an OSD which is not active -- ignoring request"); xfprintf(stderr, "vdr-xine: OSD deactivation requested for an OSD which is not active -- ignoring request"); return false; } else { m_currentOsd = 0; } } else { if (!on) { esyslog("vdr-xine: OSD deactivation requested while no OSD is active -- ignoring request"); xfprintf(stderr, "vdr-xine: OSD deactivation requested while no OSD is active -- ignoring request"); return false; } else { m_currentOsd = osd; } } return true; } static cDevice *originalPrimaryDevice = 0; #if APIVERSNUM >= 10307 void cXineDevice::MakePrimaryDevice(bool On) { cDevice::MakePrimaryDevice(On); xfprintf(stderr, "-------------------------\n"); xfprintf(stderr, "MakePrimaryDevice: %d\n", On); xfprintf(stderr, "=========================\n"); if (On) new cXineOsdProvider(*this); else cOsdProvider::Shutdown(); originalPrimaryDevice = 0; } #endif #if APIVERSNUM >= 10320 static cSkin *origSkin = 0; #endif static void switchSkin(const bool restore) { #if APIVERSNUM >= 10320 if (restore) { Skins.SetCurrent(origSkin->Name()); cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme()); } else { origSkin = Skins.Current(); Skins.SetCurrent("curses"); cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme()); } #else #warning vdr-xine: switching skins is only available for VDR versions >= 1.3.20 isyslog("vdr-xine: switching skins is only available for VDR versions >= 1.3.20"); #endif } void cXineDevice::reshowCurrentOsd(const bool dontOptimize /* = true */, const int frameLeft /* = -1 */, const int frameTop /* = -1 */, const int frameWidth /* = -1 */, const int frameHeight /* = -1 */, const int frameZoomX /* = -1 */, const int frameZoomY /* = -1 */, const bool unlockOsdUpdate /* = false */) { cXineOsdMutexLock osdLock(&m_osdMutex); if (unlockOsdUpdate) m_osdUpdateLocked = false; if (m_currentOsd) m_currentOsd->ReshowCurrentOsd(dontOptimize, frameLeft, frameTop, frameWidth, frameHeight, frameZoomX, frameZoomY); } void cXineDevice::LockOsdUpdate() { cXineOsdMutexLock osdLock(&m_osdMutex); m_osdUpdateLocked = true; } bool cXineDevice::OsdUpdateLocked() { cXineOsdMutexLock osdLock(&m_osdMutex); return m_osdUpdateLocked; } void cXineDevice::mainMenuTrampoline() { #if APIVERSNUM >= 10332 cMutexLock switchPrimaryDeviceLock(&m_switchPrimaryDeviceMutex); if (m_switchPrimaryDeviceDeviceNo < 0) return; cControl::Shutdown(); if (m_switchPrimaryDeviceDeviceNo == (1 + DeviceNumber())) { char *msg = 0; ::asprintf(&msg, tr("Switching primary DVB to %s..."), m_plugin->Name()); Skins.Message(mtInfo, msg); ::free(msg); } SetPrimaryDevice(m_switchPrimaryDeviceDeviceNo); if (m_switchPrimaryDeviceDeviceNo != (1 + DeviceNumber())) { char *msg = 0; ::asprintf(&msg, tr("Switched primary DVB back from %s"), m_plugin->Name()); Skins.Message(mtInfo, msg); ::free(msg); } m_switchPrimaryDeviceDeviceNo = -1; m_switchPrimaryDeviceCond.Broadcast(); #endif } void cXineDevice::switchPrimaryDevice(const int deviceNo, const bool waitForExecution) { #if APIVERSNUM >= 10332 #if APIVERSNUM < 10347 while (cRemote::HasKeys()) cCondWait::SleepMs(10); #endif cMutexLock switchPrimaryDeviceLock(&m_switchPrimaryDeviceMutex); m_switchPrimaryDeviceDeviceNo = deviceNo; #if APIVERSNUM < 10347 cRemote::CallPlugin(m_plugin->Name()); #endif if (waitForExecution) m_switchPrimaryDeviceCond.Wait(m_switchPrimaryDeviceMutex); #else #warning vdr-xine: switching primary device is no longer supported for VDR versions < 1.3.32 isyslog("vdr-xine: switching primary device is no longer supported for VDR versions < 1.3.32"); #endif } void cXineDevice::OnClientConnect() { reshowCurrentOsd(); if (m_settings.LiveTV()) { cMutexLock lock(&softStartMutex); softStartTrigger = sstNormal; } if (m_settings.AutoPrimaryDevice()) { cDevice *primaryDevice = cDevice::PrimaryDevice(); if (this != primaryDevice) switchPrimaryDevice(1 + DeviceNumber(), true); originalPrimaryDevice = primaryDevice; } if (m_settings.ShallSwitchSkin()) switchSkin(true); } void cXineDevice::OnClientDisconnect() { if (m_settings.ShallSwitchSkin()) switchSkin(false); if (m_settings.AutoPrimaryDevice() && originalPrimaryDevice) { if (this != originalPrimaryDevice) switchPrimaryDevice(1 + originalPrimaryDevice->DeviceNumber(), false); } } void cXineDevice::ReshowCurrentOSD(const int frameLeft, const int frameTop, const int frameWidth, const int frameHeight, const int frameZoomX, const int frameZoomY, const bool unlockOsdUpdate) { // ::fprintf(stderr, ">>> cXineDevice::ReshowCurrentOSD()\n"); reshowCurrentOsd(false, frameLeft, frameTop, frameWidth, frameHeight, frameZoomX, frameZoomY, unlockOsdUpdate); // ::fprintf(stderr, "<<< cXineDevice::ReshowCurrentOSD()\n"); } bool cXineDevice::DeviceReplayingOrTransferring() { #if APIVERSNUM >= 10341 return Replaying() || Transferring(); #else return Replaying() /* || Transferring() */; #endif } bool cXineDevice::open() { if (!m_xineLib.Open()) return false; if (m_settings.ShallSwitchSkin()) { #if APIVERSNUM >= 10320 Skins.SetCurrent(Setup.OSDSkin); cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme()); #endif switchSkin(false); } return true; } void cXineDevice::close() { m_xineLib.Close(); if (m_settings.ShallSwitchSkin()) switchSkin(true); } void cXineDevice::Stop() { if (!theXineDevice) return; theXineDevice->close(); } cXineDevice::cXineDevice(cPlugin *const plugin, cXineSettings &settings, cXineRemote *remote) : cDevice() , m_settings(settings) , m_osdUpdateLocked(false) , m_currentOsd(0) , m_spuDecoder(0) , m_is16_9(true) , m_audioChannel(0) , m_plugin(plugin) , m_switchPrimaryDeviceDeviceNo(-1) , m_xineLib(plugin, settings, m_osdMutex, remote) { m_xineLib.SetEventSink(this); } cXineDevice::~cXineDevice() { #if APIVERSNUM < 10320 close(); #endif if (m_spuDecoder) delete m_spuDecoder; } bool cXineDevice::Create(cPlugin *const plugin, cXineSettings &settings, cXineRemote *remote) { if (theXineDevice) return false; theXineDevice = new cXineDevice(plugin, settings, remote); return 0 != theXineDevice && theXineDevice->hasNoSignalStream(); } bool cXineDevice::Open() { if (!theXineDevice) return false; return theXineDevice->open(); } cXineDevice *cXineDevice::GetDevice() { return theXineDevice; } }; xine-0.9.4/xineLib.c0000644000000000000000000035131211540176110012724 0ustar rootroot #include "xineCommon.h" #include #include #include #include "xineLib.h" #include "xineOsd.h" #include "xineSettings.h" #include #define ASSERT_PALETTE(x) //#define ASSERT_PALETTE(x) x #define PROFILE_SCALING(x) //#define PROFILE_SCALING(x) x #define DLOG(x) //#define DLOG(x) cDlog dlog(x) namespace PluginXine { class cDlog { char m_s[ 100 ]; public: cDlog(const char *const s) { ::strncpy(m_s, s, sizeof(m_s)); m_s[sizeof(m_s) - 1] = 0; ::fprintf(stderr, ">>> %s\n", m_s); } ~cDlog() { ::fprintf(stderr, "<<< %s\n", m_s); } }; int cXineBitmapAdapter::X0(void) const { #if APIVERSNUM < 10307 return 0; #else return m_pBitmap->X0(); #endif } int cXineBitmapAdapter::Y0(void) const { #if APIVERSNUM < 10307 return 0; #else return m_pBitmap->Y0(); #endif } int cXineBitmapAdapter::Width(void) const { return m_pBitmap->Width(); } int cXineBitmapAdapter::Height(void) const { return m_pBitmap->Height(); } bool cXineBitmapAdapter::Dirty(int &x1, int &y1, int &x2, int &y2) { return m_pBitmap->Dirty(x1, y1, x2, y2); } void cXineBitmapAdapter::Clean(void) { m_pBitmap->Clean(); } const tColor *cXineBitmapAdapter::Colors(int &NumColors) const { #if APIVERSNUM < 10307 return m_pBitmap->AllColors(NumColors); #else return m_pBitmap->Colors(NumColors); #endif } const uint8_t *cXineBitmapAdapter::Data(int x, int y) const { return (uint8_t *)m_pBitmap->Data(x, y); } int cXineBitmapAdapter::SizeOfPixel() const { return sizeof(tIndex); } int cXineBitmapAdapter::SizeOfStride() const { return m_pBitmap->Width() * SizeOfPixel(); } #if APIVERSNUM >= 10717 cXinePixmapMemoryAdapter::cXinePixmapMemoryAdapter(const cPixmapMemory *const pPixmapMemory) : m_pPixmapMemory((cPixmapMemory *)pPixmapMemory) { } int cXinePixmapMemoryAdapter::X0(void) const { return m_pPixmapMemory->ViewPort().X() + m_pPixmapMemory->DrawPort().X(); } int cXinePixmapMemoryAdapter::Y0(void) const { return m_pPixmapMemory->ViewPort().Y() + m_pPixmapMemory->DrawPort().Y(); } int cXinePixmapMemoryAdapter::Width(void) const { return m_pPixmapMemory->DrawPort().Width(); } int cXinePixmapMemoryAdapter::Height(void) const { return m_pPixmapMemory->DrawPort().Height(); } bool cXinePixmapMemoryAdapter::Dirty(int &x1, int &y1, int &x2, int &y2) { const cRect &dirty = m_pPixmapMemory->DirtyViewPort(); if (dirty.IsEmpty()) return false; x1 = dirty.Left() - X0(); x2 = dirty.Right() - X0(); y1 = dirty.Top() - Y0(); y2 = dirty.Bottom() - Y0(); return true; } void cXinePixmapMemoryAdapter::Clean(void) { //m_pPixmapMemory->SetClean(); } const tColor *cXinePixmapMemoryAdapter::Colors(int &NumColors) const { NumColors = 0; return 0; } const uint8_t *cXinePixmapMemoryAdapter::Data(int x, int y) const { return m_pPixmapMemory->Data() + y * SizeOfStride() + x * SizeOfPixel(); } int cXinePixmapMemoryAdapter::SizeOfPixel() const { return sizeof(tColor); } int cXinePixmapMemoryAdapter::SizeOfStride() const { return m_pPixmapMemory->DrawPort().Width() * SizeOfPixel(); } #endif #if APIVERSNUM < 10307 bool cXineLib::OpenWindow(cXineOsd *const xineOsd, cWindow *Window, const int maxOsdWidth, const int maxOsdHeight) { execFuncOsdNew(maxOsdWidth, maxOsdHeight, Window->Handle(), xineOsd->X0() + Window->X0(), xineOsd->Y0() + Window->Y0(), Window->Width(), Window->Height()); CommitWindow(xineOsd, Window, false); return true; } void cXineLib::CommitWindow(cXineOsd *const xineOsd, cWindow *Window, const bool optimize /* = true */) { int firstColorIndex = -1; int lastColorIndex = -1; const eDvbColor *modifiedColors = (optimize ? Window->NewColors(firstColorIndex, lastColorIndex) : Window->AllColors(lastColorIndex)); if (!optimize) { firstColorIndex = 0; lastColorIndex--; } if (modifiedColors) { do { for (int colorIndex = firstColorIndex ; colorIndex <= lastColorIndex ; colorIndex++, modifiedColors++) { execFuncSetColor(Window->Handle(), colorIndex, filterAlpha(*modifiedColors)); } } while (0 != (modifiedColors = Window->NewColors(firstColorIndex, lastColorIndex))); } // xine_osd_draw_bitmap(m_osdWindow[ Window->Handle() ], (uint8_t *)Window->Data(0, 0), 0, 0, Window->Width(), Window->Height(), &m_mapToXinePalette[ Window->Handle() ][ 0 ]); int x1 = 0, y1 = 0, x2 = Window->Width() - 1, y2 = Window->Height() - 1; if (!optimize || modifiedColors || Window->Dirty(x1, y1, x2, y2)) { // fprintf(stderr, "dirty area: (%d, %d) - (%d, %d), window size: (%d, %d)\n", x1, y1, x2, y2, Window->Width(), Window->Height()); execFuncOsdDrawBitmap(Window->Handle(), (const uchar *)Window->Data(x1, y1), 1, x1, y1, x2 - x1 + 1, y2 - y1 + 1, Window->Width(), 0); if (m_osdWindowVisible[ Window->Handle() ]) execFuncOsdShow(Window->Handle()); } } void cXineLib::ShowWindow(cXineOsd *const xineOsd, cWindow *Window) { execFuncOsdShow(Window->Handle()); m_osdWindowVisible[ Window->Handle() ] = true; } void cXineLib::HideWindow(cXineOsd *const xineOsd, cWindow *Window, bool Hide) { if (Hide) { execFuncOsdHide(Window->Handle()); } else { execFuncOsdShow(Window->Handle()); } m_osdWindowVisible[ Window->Handle() ] = !Hide; } void cXineLib::MoveWindow(cXineOsd *const xineOsd, cWindow *Window, int x, int y) { execFuncOsdSetPosition(Window->Handle(), xineOsd->X0() + x, xineOsd->Y0() + y); if (m_osdWindowVisible[ Window->Handle() ]) execFuncOsdShow(Window->Handle()); } void cXineLib::CloseWindow(cXineOsd *const xineOsd, cWindow *Window) { CloseWindow(xineOsd, Window->Handle()); } void cXineLib::CloseWindow(cXineOsd *const xineOsd, int Window) { if (m_osdWindowVisible[ Window ]) execFuncOsdHide(Window); execFuncOsdFree(Window); m_osdWindowVisible[ Window ] = false; } #else bool cXineLib::execFuncOsdNew(const int maxOsdWidth, const int maxOsdHeight, const eNeedsScaling needsScaling, const int videoLeft, const int videoTop, const int videoWidth, const int videoHeight, int window, int x, int y, int width, int height) { m_osdFlushRequired = true; if (!needsScaling) return execFuncOsdNew(window, videoLeft + x, videoTop + y, width, height, maxOsdWidth, maxOsdHeight); int x0 = x * videoWidth / maxOsdWidth; int w0 = ((x + width) * videoWidth - 1 + maxOsdWidth) / maxOsdWidth - x0; int y0 = y * videoHeight / maxOsdHeight; int h0 = ((y + height) * videoHeight - 1 + maxOsdHeight) / maxOsdHeight - y0; return execFuncOsdNew(window, videoLeft + x0, videoTop + y0, w0, h0, maxOsdWidth, maxOsdHeight); } static inline int intersection(int a0, int a1, int b0, int b1) { if (a0 < b0) a0 = b0; if (a1 > b1) a1 = b1; int d = a1 - a0; if (d <= 0) return 0; return d; } //#define CRT_GAMMA (2.2) #define FIX_POINT_BITS (6) // * 3 + 8 + ceil(ld(ceil(1/scale_x) * ceil(1/scale_y))) < 32 !!! #define FIX_POINT_FACTOR (1 << FIX_POINT_BITS) static uint16_t data_delinearize[ 255 * FIX_POINT_FACTOR + 1 ]; static int init_delinearize(int crtGamma) { for (int i = 0; i <= 255 * FIX_POINT_FACTOR; i++) { data_delinearize[ i ] = (int)(::exp(::log(i / (255.0 * FIX_POINT_FACTOR)) / (crtGamma / (double)cXineSettings::monitorGammaBase)) * (255.0 * FIX_POINT_FACTOR) + .5); // ::fprintf(stderr, "de_lin: %d => %d\n", i, data_delinearize[ i ]); ASSERT_PALETTE(assert(data_delinearize[ i ] <= 255 * FIX_POINT_FACTOR);) } return 0; } static inline uint16_t delinearize(uint16_t i) { // static int init = init_delinearize(); // (void)init; ASSERT_PALETTE(assert(i <= 255 * FIX_POINT_FACTOR);) return data_delinearize[ i ]; } static uint16_t data_linearize[ 255 * FIX_POINT_FACTOR + 1 ]; static int init_linearize(int crtGamma) { for (int i = 0; i <= 255 * FIX_POINT_FACTOR; i++) { data_linearize[ i ] = (int)(::exp(::log(i / (255.0 * FIX_POINT_FACTOR)) * (crtGamma / (double)cXineSettings::monitorGammaBase)) * (255.0 * FIX_POINT_FACTOR) + .5); // ::fprintf(stderr, "lin: %d => %d\n", i, data_linearize[ i ]); ASSERT_PALETTE(assert(data_linearize[ i ] <= 255 * FIX_POINT_FACTOR);) } return 0; } static inline uint16_t linearize(uint16_t i) { // static int init = init_linearize(); // (void)init; ASSERT_PALETTE(assert(i <= 255 * FIX_POINT_FACTOR);) return data_linearize[ i ]; } typedef uint64_t tColor16; static inline void mkLinearColor(const tColor &color, tColor16 &color16) { const uint8_t *src = (const uint8_t *)&color; uint16_t *dst = (uint16_t *)&color16; #if __BYTE_ORDER == LITTLE_ENDIAN *dst++ = linearize(FIX_POINT_FACTOR * *src++); *dst++ = linearize(FIX_POINT_FACTOR * *src++); *dst++ = linearize(FIX_POINT_FACTOR * *src++); *dst++ = (FIX_POINT_FACTOR * *src++); #else *dst++ = (FIX_POINT_FACTOR * *src++); *dst++ = linearize(FIX_POINT_FACTOR * *src++); *dst++ = linearize(FIX_POINT_FACTOR * *src++); *dst++ = linearize(FIX_POINT_FACTOR * *src++); #endif // ::fprintf(stderr, "c: %08x, c16: %016llx\n", color, color16); } static inline void mkDelinearColor(const tColor16 &color16, tColor &color) { const uint16_t *src = (const uint16_t *)&color16; uint8_t *dst = (uint8_t *)&color; #if __BYTE_ORDER == LITTLE_ENDIAN *dst++ = delinearize(*src++) / FIX_POINT_FACTOR; *dst++ = delinearize(*src++) / FIX_POINT_FACTOR; *dst++ = delinearize(*src++) / FIX_POINT_FACTOR; *dst++ = (*src++) / FIX_POINT_FACTOR; #else *dst++ = (*src++) / FIX_POINT_FACTOR; *dst++ = delinearize(*src++) / FIX_POINT_FACTOR; *dst++ = delinearize(*src++) / FIX_POINT_FACTOR; *dst++ = delinearize(*src++) / FIX_POINT_FACTOR; #endif // ::fprintf(stderr, "c16: %016llx, c: %08x\n", color16, color); } class cXinePalette { #define maxSize 65521 public: class cEntry { friend class cXinePalette; int m_index; public: tColor m_color; uint32_t m_count; private: cEntry *m_prev, *m_next; static int comparePointersByCountDesc(const void *lhs, const void *rhs) { const cEntry *cLhs = *(const cEntry **)lhs; const cEntry *cRhs = *(const cEntry **)rhs; if (cLhs->m_count < cRhs->m_count) return 1; if (cLhs->m_count > cRhs->m_count) return -1; return 0; } static long colorDistance(const tColor c1, const tColor c2) { int c1a = (c1 & 0xff000000) >> 0x18; int c1r = (c1 & 0x00ff0000) >> 0x10; int c1g = (c1 & 0x0000ff00) >> 0x08; int c1b = (c1 & 0x000000ff) >> 0x00; int c2a = (c2 & 0xff000000) >> 0x18; int c2r = (c2 & 0x00ff0000) >> 0x10; int c2g = (c2 & 0x0000ff00) >> 0x08; int c2b = (c2 & 0x000000ff) >> 0x00; long r,g,b,a; long rmean; rmean = (c1r + c2r) / 2; r = c1r - c2r; g = c1g - c2g; b = c1b - c2b; a = c1a - c2a; return (((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8) + a * a; } public: int getIndex() const { return m_index; } }; private: cEntry m_palette[ maxSize ]; int m_numEntries; cEntry *m_lut[ 257 ][ 257 ]; bool m_warned; cEntry *m_lookup[ maxSize ]; const cEntry *const *const m_lookupLimit; const int m_numPalColors; cEntry m_head, m_tail; cEntry *(cXinePalette::*m_add)(const tColor &color); cEntry *addHash(const tColor &color) { cEntry **lookup = m_lookup + (color % maxSize); cEntry *entry = *lookup; while (entry) { if (color == entry->m_color) { entry->m_count++; return entry; } if (++lookup >= m_lookupLimit) lookup = m_lookup; entry = *lookup; } // ::fprintf(stderr, "i: %d, c: %08x\n", m_numEntries, color); entry = m_palette + m_numEntries++; *lookup = entry; entry->m_color = color; entry->m_count = 1; return entry; } void addHead(cEntry *const node) { node->m_next = m_head.m_next; node->m_prev = &m_head; node->m_next->m_prev = node; node->m_prev->m_next = node; } void remNode(cEntry *const node) { node->m_next->m_prev = node->m_prev; node->m_prev->m_next = node->m_next; } cEntry *addLru(const tColor &color) { cEntry *entry = m_head.m_next; while (entry != &m_tail) { if (color == entry->m_color) { if (entry->m_prev != &m_head) { remNode(entry); addHead(entry); } entry->m_count++; return entry; } entry = entry->m_next; } // ::fprintf(stderr, "i: %d, c: %08x\n", m_numEntries, color); entry = m_palette + m_numEntries++; addHead(entry); entry->m_color = color; entry->m_count = 1; return entry; } public: uint8_t m_vdrMapping[ 256 ]; cXinePalette(cXineLib *xineLib, const int numVdrColors, const tColor *const colors = 0, const int numPalColors = 0, const tColor *const palColors = 0, const int transparentIndex = -1) : m_numEntries(0) , m_warned(false) , m_lookupLimit(m_lookup + sizeof (m_lookup) / sizeof (*m_lookup)) , m_numPalColors(numPalColors) { // fprintf(stderr, "numVdrColors: %d, numPalColors: %d\n", numVdrColors, numPalColors); if (numVdrColors >= 16) { PROFILE_SCALING(::fprintf(stderr, "hash: ");) ::memset(m_lookup, 0, sizeof (m_lookup)); m_add = &cXinePalette::addHash; } else { PROFILE_SCALING(::fprintf(stderr, "lru: ");) m_head.m_prev = 0; m_head.m_next = &m_tail; m_tail.m_prev = &m_head; m_tail.m_next = 0; m_add = &cXinePalette::addLru; } ::memset(m_lut, 0, sizeof (m_lut)); if (colors) { for (int i = 0; i < numVdrColors; i++) { cEntry *e = addUnused(xineLib->filterAlpha(colors[ i ])); m_vdrMapping[ i ] = e - m_palette; } } if (transparentIndex != -1) { ASSERT_PALETTE(assert(transparentIndex == m_numEntries);) addUnused(clrTransparent); } if (palColors) { for (int i = 0; i < numPalColors; i++) addUnused(palColors[ i ]); } } cEntry *add(const tColor &color) { if (m_numEntries < maxSize) return (this->*m_add)(color); ASSERT_PALETTE( { if (!m_warned) { m_warned = true; ::fprintf(stderr, "warning: cXinePalette is full!\n"); } }) return 0; } void addPersistent(const tColor &color) { cEntry *const entry = add(color); assert(entry); entry->m_count = 0x7fffffff; } cEntry *addUnused(const tColor &color) { cEntry *const entry = add(color); assert(entry); entry->m_count = 0; return entry; } cEntry *add(const uint16_t lutIndex) { ASSERT_PALETTE(assert(lutIndex <= 256);) ASSERT_PALETTE(assert(lutIndex < m_numEntries);) cEntry *const entry = &m_palette[ lutIndex ]; entry->m_count++; return entry; } cEntry *add(const uint16_t lutIndexA, const uint16_t lutIndexB) { ASSERT_PALETTE(assert(lutIndexA <= 256);) ASSERT_PALETTE(assert(lutIndexB <= 256);) cEntry *&entry = m_lut[ lutIndexA ][ lutIndexB ]; if (!entry) { if (lutIndexA == lutIndexB) { entry = add(lutIndexA); return entry; } tColor16 colorA, colorB; ASSERT_PALETTE(assert(lutIndexA < m_numEntries);) ASSERT_PALETTE(assert(lutIndexB < m_numEntries);) mkLinearColor(m_palette[ lutIndexA ].m_color, colorA); mkLinearColor(m_palette[ lutIndexB ].m_color, colorB); tColor16 colorC; tColor color; //colorC = (((colorA ^ colorB) & 0xfffefffefffefffeULL) >> 1) + (colorA & colorB); // doesn't work with ULL and -O2: buggy compiler??? { uint16_t *pA = &alias_cast(colorA), *pB = &alias_cast(colorB), *pC = &alias_cast(colorC); for (int i = 0; i < 4; i++) { *pC = (((*pA ^ *pB) & 0xfffe) >> 1) + (*pA & *pB); pA++; pB++; pC++; } } // fprintf(stderr, "a: 0x%016llx, b: 0x%016llx, c: 0x%016llx\n", colorA, colorB, colorC); ASSERT_PALETTE( { uint16_t *c = (uint16_t *)&colorC; if (c[ 0 ] > (255 * FIX_POINT_FACTOR) || c[ 1 ] > (255 * FIX_POINT_FACTOR) || c[ 2 ] > (255 * FIX_POINT_FACTOR) || c[ 3 ] > (255 * FIX_POINT_FACTOR)) { ::fprintf(stderr, "%d, 0x%08x, %d, 0x%08x\n, A: 0x%016llx\n, B: 0x%016llx\n, C: 0x%016llx\n, %d\n", lutIndexA, m_palette[ lutIndexA ].m_color, lutIndexB, m_palette[ lutIndexB ].m_color, colorA, colorB, colorC, -1); } }) mkDelinearColor(colorC, color); /* fprintf(stderr, "a: 0x%08lx, b: 0x%08lx, c: 0x%08lx\n" , m_palette[ lutIndexA ].m_color , m_palette[ lutIndexB ].m_color , color); */ m_lut[ lutIndexB ][ lutIndexA ] = entry = add(color); return entry; } entry->m_count++; return entry; } int getNum() const { return m_numEntries; } tColor *getVdrColors(int &numColors) { int usedColors = numColors = m_numEntries; int usedIndex = numColors - 1; if (numColors > 256) { usedColors = 0; usedIndex = 0; { cEntry *src = m_palette; for (int i = 0; i < m_numEntries; i++) { if ((src++)->m_count > 0) { usedColors++; usedIndex = i; } } } if (usedColors > 256) { numColors = 256; } else { numColors = usedColors; if (usedIndex < 256) { if (numColors <= usedIndex) numColors = usedIndex + 1; } } } if (0 == numColors) return 0; tColor *const colors = new tColor[ numColors ]; // ASSERT_PALETTE(::fprintf(stderr, "used: %d, nc: %d, index: %d, ", usedColors, numColors, usedIndex);) if (usedColors > numColors) { cEntry *sorted[ maxSize ]; { cEntry *src = m_palette; cEntry **dst = sorted; for (int i = 0; i < m_numEntries; i++) { if (src->m_count > 0) *dst++ = src; src++; } ASSERT_PALETTE(assert((dst - sorted) == usedColors);) } ::qsort(sorted, usedColors, sizeof (*sorted), cEntry::comparePointersByCountDesc); cEntry **src = sorted; { tColor *dst = colors; for (int i = 0; i < numColors; i++) { *dst++ = (*src)->m_color; (*src++)->m_index = i; } } for (int i = numColors; i < usedColors; i++) { cEntry *const entry = *src++; { cEntry **src = sorted; tColor bestMatchDelta = cEntry::colorDistance((*src)->m_color, entry->m_color); entry->m_index = 0; for (int k = 1; k < numColors; k++) { ++src; tColor delta = cEntry::colorDistance((*src)->m_color, entry->m_color); if (bestMatchDelta > delta) { bestMatchDelta = delta; entry->m_index = k; } } } } } else { cEntry *src = m_palette; tColor *dst = colors; int index = 0; if (usedIndex > m_numPalColors) { int num = m_numEntries; if (num > 256) num = usedIndex + 1; for (int i = 0; i < num; i++) { if (src->m_count > 0 || m_numEntries < 256) { *dst++ = src->m_color; src->m_index = index++; // fprintf(stderr, "%3d: 0x%08x\n", index - 1, src->m_color); ASSERT_PALETTE(assert(index <= numColors);) } src++; } } else { ASSERT_PALETTE(assert(usedIndex < numColors);) for (int i = 0; i < numColors; i++) { *dst++ = src->m_color; (src++)->m_index = i; } } } return colors; } }; static inline double tt(const timeval &tb, const timeval &ta) { return (tb.tv_sec + tb.tv_usec * 1e-6) - (ta.tv_sec + ta.tv_usec * 1e-6); } template class cBresenham { const int m_yInc; const int m_dx; const int m_dy; int m_eps; T m_y; public: cBresenham(const int dy, const int dx, const int eps, T const y0 = 0) : m_yInc(1) , m_dx(dx) , m_dy(dy) , m_eps(eps - m_dx) , m_y(y0) { } cBresenham(const int yInc, const int dy, const int dx, const int eps, T const y0 = 0) : m_yInc(yInc) , m_dx(dx) , m_dy(dy) , m_eps(eps - m_dx) , m_y(y0) { } int eps() const { return m_eps; } T step() { m_eps += m_dy; while (m_eps >= 0) { m_eps -= m_dx; m_y += m_yInc; } return m_y; } T step(int n) { if (n <= 0) return m_y; while (--n > 0) step(); return step(); } T stepRelative(int n = 1) { T const y = m_y; return step(n) - y; } }; static tIndex *ScaleBitmapSHQ(const int maxOsdWidth, const int maxOsdHeight, const tIndex *src, int x0, int y0, int w, int h, int stride, int ws, int hs, int x1, int y1, int w1, int h1, const uint8_t /* transparentIndex */, const tColor *const colors, const int numColors, tColor *&palette2, int &paletteSize, cXineLib *xineLib) { timeval t0, t1, t2, t3, t4; (void)t0, (void)t1, (void)t2, (void)t3, (void)t4; PROFILE_SCALING(::gettimeofday(&t0, 0);) tColor16 *const screen2 = new tColor16[ maxOsdHeight * maxOsdWidth ]; { tColor16 *const colors16 = new tColor16[ numColors ]; for (int i = 0; i < numColors; i++) mkLinearColor(xineLib->filterAlpha(colors[ i ]), colors16[ i ]); tColor16 clrTransparent16; mkLinearColor(clrTransparent, clrTransparent16); int x1 = x0 + w; int y1 = y0 + h; int x2 = maxOsdWidth; int y2 = maxOsdHeight; if (x1 > x2) x1 = x2; if (y1 > y2) y1 = y2; tColor16 *dst = screen2; for (int y = 0; y < y0; y++) { for (int x = 0; x < x2; x++) *dst++ = clrTransparent16; } for (int y = y0; y < y1; y++) { for (int x = 0; x < x0; x++) *dst++ = clrTransparent16; for (int x = x0; x < x1; x++) *dst++ = colors16[ *src++ ]; src += stride - w; for (int x = x1; x < x2; x++) *dst++ = clrTransparent16; } for (int y = y1; y < y2; y++) { for (int x = 0; x < x2; x++) *dst++ = clrTransparent16; } delete [] colors16; } tIndex *const scaled = new tIndex[ hs * ws ]; PROFILE_SCALING(::gettimeofday(&t1, 0);) // cXinePalette xinePalette(numColors); // fails in getVdrColors(): stack??? cXinePalette *const pXinePalette = new cXinePalette(xineLib, numColors); cXinePalette &xinePalette = *pXinePalette; xinePalette.addPersistent(clrTransparent); xinePalette.addPersistent(clrGray50); xinePalette.addPersistent(clrBlack); xinePalette.addPersistent(clrRed); xinePalette.addPersistent(clrGreen); xinePalette.addPersistent(clrYellow); xinePalette.addPersistent(clrMagenta); xinePalette.addPersistent(clrBlue); xinePalette.addPersistent(clrCyan); xinePalette.addPersistent(clrWhite); cXinePalette::cEntry **const scaled2 = new cXinePalette::cEntry*[ hs * ws ]; { memset(scaled2, 0, sizeof (*scaled2) * hs * ws); int x2 = x1 + w1; int y2 = y1 + h1; if (x2 > ws) { x2 = ws; w1 = x2 - x1; if (w1 < 0) w1 = 0; } if (y2 > hs) { y2 = hs; h1 = y2 - y1; if (h1 < 0) h1 = 0; } cXinePalette::cEntry **scaled2dst0 = scaled2 + y1 * ws + x1; cBresenham<> yBh(FIX_POINT_FACTOR * maxOsdHeight, hs, 0); int yf0 = yBh.step(y1); //FIX_POINT_FACTOR * y1 * maxOsdHeight / hs; cBresenham<> xBh0(FIX_POINT_FACTOR * maxOsdWidth, ws, 0); xBh0.step(x1); //FIX_POINT_FACTOR * x1 * maxOsdWidth / ws; cBresenham yyBh(maxOsdWidth, maxOsdHeight, hs, 0, screen2); tColor16 *screen20 = yyBh.step(y1); //y1 * maxOsdHeight / hs; cBresenham<> xxBh0(maxOsdWidth, ws, 0); xxBh0.step(x1); //x1 * maxOsdWidth / ws; for (int y = y1; y < y2; y++) { const int yf1 = yBh.step(); //FIX_POINT_FACTOR * (y + 1) * maxOsdHeight / hs; const int yi00 = yf0 & ~(FIX_POINT_FACTOR - 1); const int yi10 = yi00 + FIX_POINT_FACTOR; cXinePalette::cEntry **scaled2dst = scaled2dst0; cBresenham<> xBh(xBh0); int xf0 = xBh.step(0); //FIX_POINT_FACTOR * x1 * maxOsdWidth / ws; cBresenham<> xxBh(xxBh0); int xxx = xxBh.step(0); //x1 * maxOsdWidth / ws; for (int x = x1; x < x2; x++) { const int xf1 = xBh.step(); //FIX_POINT_FACTOR * (x + 1) * maxOsdWidth / ws; const int xi00 = xf0 & ~(FIX_POINT_FACTOR - 1); const int xi10 = xi00 + FIX_POINT_FACTOR; // ::fprintf(stderr, "(%d, %d) => (%d, %d)\n", x, y, xf0, yf0); tColor16 *screen2src0 = screen20 + xxx; //&screen2[ (yi00 / FIX_POINT_FACTOR) * maxOsdWidth + (xi00 / FIX_POINT_FACTOR) ]; int sai = 0; int c0 = 0; int c1 = 0; int c2 = 0; int c3 = 0; for (int yi0 = yi00, yi1 = yi10; yi0 < yf1; yi0 = yi1, yi1 += FIX_POINT_FACTOR) { int aiy = intersection(yi0, yi1, yf0, yf1); int saix = 0; int c0x = 0; int c1x = 0; int c2x = 0; int c3x = 0; tColor16 *screen2src = screen2src0; for (int xi0 = xi00, xi1 = xi10; xi0 < xf1; xi0 = xi1, xi1 += FIX_POINT_FACTOR) { // ::fprintf(stderr, "-(%d, %d)\n", xi / 256, yi / 256); int aix = intersection(xi0, xi1, xf0, xf1); saix += aix; uint16_t *c = (uint16_t *)screen2src++; c0x += aix * *c++; c1x += aix * *c++; c2x += aix * *c++; c3x += aix * *c++; } screen2src0 += maxOsdWidth; sai += aiy * saix; c0 += aiy * c0x; c1 += aiy * c1x; c2 += aiy * c2x; c3 += aiy * c3x; } tColor color; uint8_t *c = (uint8_t *)&color; #if __BYTE_ORDER == LITTLE_ENDIAN *c++ = delinearize(c0 / sai) / FIX_POINT_FACTOR; *c++ = delinearize(c1 / sai) / FIX_POINT_FACTOR; *c++ = delinearize(c2 / sai) / FIX_POINT_FACTOR; *c++ = (c3 / sai) / FIX_POINT_FACTOR; #else *c++ = (c0 / sai) / FIX_POINT_FACTOR; *c++ = delinearize(c1 / sai) / FIX_POINT_FACTOR; *c++ = delinearize(c2 / sai) / FIX_POINT_FACTOR; *c++ = delinearize(c3 / sai) / FIX_POINT_FACTOR; #endif *scaled2dst++ = xinePalette.add(color); xxx = xxBh.step(); //x1 * maxOsdWidth / ws; xf0 = xf1; } scaled2dst0 += ws; screen20 = yyBh.step(); //y1 * maxOsdHeight / hs; yf0 = yf1; } PROFILE_SCALING(::gettimeofday(&t2, 0);) palette2 = xinePalette.getVdrColors(paletteSize); PROFILE_SCALING(::gettimeofday(&t3, 0);) { cXinePalette::cEntry **src = scaled2; tIndex *dst = scaled; for (int y = 0; y < hs; y++) { for (int x = 0; x < ws; x++) { *dst++ = (*src) ? (*src)->getIndex() : 0; src++; } } } PROFILE_SCALING( { ::gettimeofday(&t4, 0); ::fprintf(stderr, "num: %d, new: %d, ", numColors, xinePalette.getNum()); ::fprintf(stderr, "t1: %.5lf, t2: %.5lf, t3: %.5lf, t4: %.5lf, to: %.5lf, sc: %.5lf %%\n", tt(t1, t0), tt(t2, t1), tt(t3, t2), tt(t4, t3), tt(t4, t0), tt(t2, t1) / tt(t4, t0) * 100); }) } /* char fName[ 50 ]; static int osdCnt = 0; ::sprintf(fName, "/tmp/osd_%05da.ppm", osdCnt); FILE *f = ::fopen(fName, "wb"); ::fprintf(f, "P6\n%d %d\n255\n", maxOsdWidth, maxOsdHeight); for (int y = 0; y < maxOsdHeight; y++) { for (int x = 0; x < maxOsdWidth; x++) { ::fwrite(&((uint8_t *)&screen2[ y * maxOsdWidth + x ])[ 2 ], 1, 1, f); ::fwrite(&((uint8_t *)&screen2[ y * maxOsdWidth + x ])[ 1 ], 1, 1, f); ::fwrite(&((uint8_t *)&screen2[ y * maxOsdWidth + x ])[ 0 ], 1, 1, f); } } ::fclose(f); ::sprintf(fName, "/tmp/osd_%05db.ppm", osdCnt++); f = ::fopen(fName, "wb"); ::fprintf(f, "P6\n%d %d\n255\n", ws, hs); for (int y = 0; y < hs; y++) { for (int x = 0; x < ws; x++) { ::fwrite(&((uint8_t *)&scaled2[ y * ws + x ])[ 2 ], 1, 1, f); ::fwrite(&((uint8_t *)&scaled2[ y * ws + x ])[ 1 ], 1, 1, f); ::fwrite(&((uint8_t *)&scaled2[ y * ws + x ])[ 0 ], 1, 1, f); } } ::fclose(f); */ delete pXinePalette; delete [] screen2; delete [] scaled2; return scaled; } static int deduceEps(const int dst, const int src) { int eps = (int)(dst * (2 - ((dst > src) ? 1 : -1) * ::log(dst / src) / ::log(1.5)) / 2); if (eps < (dst / 2)) eps = dst / 2; return -eps; } static tIndex *ScaleBitmapHQ(const int maxOsdWidth, const int maxOsdHeight, const tIndex *src, int x0, int y0, int w, int h, int stride, int ws, int hs, int x1, int y1, int w1, int h1, const uint16_t transparentIndex, const tColor *const colors, const int numColors, tColor *&palette2, int &paletteSize, int currentPaletteSize, tColor *currentPalette, cXineLib *xineLib) { timeval t0, t1, t2, t3, t4; (void)t0, (void)t1, (void)t2, (void)t3, (void)t4; PROFILE_SCALING(::gettimeofday(&t0, 0);) // cXinePalette xinePalette(numColors, colors, currentPaletteSize, currentPalette); // fails in getVdrColors(): stack??? cXinePalette *const pXinePalette = new cXinePalette(xineLib, numColors, colors, currentPaletteSize, currentPalette, (transparentIndex < numColors) ? -1 : transparentIndex); cXinePalette &xinePalette = *pXinePalette; uint16_t *const screen = new uint16_t[ maxOsdHeight * maxOsdWidth ]; { int x1 = x0 + w; int y1 = y0 + h; int x2 = maxOsdWidth; int y2 = maxOsdHeight; if (x1 > x2) x1 = x2; if (y1 > y2) y1 = y2; uint16_t *dst = screen; for (int y = 0; y < y0; y++) { for (int x = 0; x < x2; x++) *dst++ = transparentIndex; } for (int y = y0; y < y1; y++) { for (int x = 0; x < x0; x++) *dst++ = transparentIndex; for (int x = x0; x < x1; x++) *dst++ = xinePalette.m_vdrMapping[ *src++ ]; src += stride - w; for (int x = x1; x < x2; x++) *dst++ = transparentIndex; } for (int y = y1; y < y2; y++) { for (int x = 0; x < x2; x++) *dst++ = transparentIndex; } } tIndex *const scaled = new tIndex[ hs * ws ]; PROFILE_SCALING(::gettimeofday(&t1, 0);) cXinePalette::cEntry **const scaled2 = new cXinePalette::cEntry*[ hs * ws ]; { memset(scaled2, 0, sizeof (*scaled2) * hs * ws); int x2 = x1 + w1; int y2 = y1 + h1; if (x2 > ws) { x2 = ws; w1 = x2 - x1; if (w1 < 0) w1 = 0; } if (y2 > hs) { y2 = hs; h1 = y2 - y1; if (h1 < 0) h1 = 0; } cXinePalette::cEntry **scaled2dst0 = scaled2 + y1 * ws + x1; cBresenham yyBh(maxOsdWidth, maxOsdHeight, hs, 0, screen); uint16_t *screen0 = yyBh.step(y1); //y1 * maxOsdHeight / hs; const int eps0 = deduceEps(ws, maxOsdWidth); const int eps1 = 0; cBresenham<> xxBh0(maxOsdWidth, ws, 0); xxBh0.step(x1); //x1 * maxOsdWidth / ws; const int xLimit = x2 - 1; for (int y = y1; y < y2; y++) { cXinePalette::cEntry **scaled2dst = scaled2dst0; cBresenham<> xxBh(xxBh0); uint16_t *screen2src0 = screen0 + xxBh.step(0); //&screen2[ (yi00 / FIX_POINT_FACTOR) * maxOsdWidth + (xi00 / FIX_POINT_FACTOR) ]; for (int x = x1; x < x2; x++) { // ::fprintf(stderr, "eps: %d\n", xxBh.eps()); if (eps0 <= xxBh.eps() && xxBh.eps() <= eps1 && x != xLimit) *scaled2dst++ = xinePalette.add(screen2src0[ 0 ], screen2src0[ 1 ]); else *scaled2dst++ = xinePalette.add(*screen2src0); screen2src0 += xxBh.stepRelative(); //x1 * maxOsdWidth / ws; } scaled2dst0 += ws; screen0 = yyBh.step(); //y1 * maxOsdHeight / hs; } PROFILE_SCALING(::gettimeofday(&t2, 0);) palette2 = xinePalette.getVdrColors(paletteSize); PROFILE_SCALING(::gettimeofday(&t3, 0);) { cXinePalette::cEntry **src = scaled2; tIndex *dst = scaled; for (int y = 0; y < hs; y++) { for (int x = 0; x < ws; x++) { uint8_t idx = (*src) ? (*src)->getIndex() : 0; // fprintf(stderr, "%02x ", idx); *dst++ = idx; src++; } // fprintf(stderr, "\n"); } } PROFILE_SCALING( { ::gettimeofday(&t4, 0); ::fprintf(stderr, "num: %d, new: %d, ", numColors, xinePalette.getNum()); ::fprintf(stderr, "t1: %.5lf, t2: %.5lf, t3: %.5lf, t4: %.5lf, to: %.5lf, sc: %.5lf %%\n", tt(t1, t0), tt(t2, t1), tt(t3, t2), tt(t4, t3), tt(t4, t0), tt(t2, t1) / tt(t4, t0) * 100); }) } /* char fName[ 50 ]; static int osdCnt = 0; */ /* { ::sprintf(fName, "/tmp/osd_%05da.ppm", osdCnt); FILE *f = ::fopen(fName, "wb"); ::fprintf(f, "P6\n%d %d\n255\n", maxOsdWidth, maxOsdHeight); for (int y = 0; y < maxOsdHeight; y++) { for (int x = 0; x < maxOsdWidth; x++) { ::fwrite(&((uint8_t *)&screen2[ y * maxOsdWidth + x ])[ 2 ], 1, 1, f); ::fwrite(&((uint8_t *)&screen2[ y * maxOsdWidth + x ])[ 1 ], 1, 1, f); ::fwrite(&((uint8_t *)&screen2[ y * maxOsdWidth + x ])[ 0 ], 1, 1, f); } } ::fclose(f); } { ::sprintf(fName, "/tmp/osd_%05db.ppm", osdCnt++); FILE *f = ::fopen(fName, "wb"); ::fprintf(f, "P6\n%d %d\n255\n", ws, hs); for (int y = 0; y < hs; y++) { for (int x = 0; x < ws; x++) { ::fwrite(&((uint8_t *)&scaled2[ y * ws + x ])[ 2 ], 1, 1, f); ::fwrite(&((uint8_t *)&scaled2[ y * ws + x ])[ 1 ], 1, 1, f); ::fwrite(&((uint8_t *)&scaled2[ y * ws + x ])[ 0 ], 1, 1, f); } } ::fclose(f); } */ /* { ::sprintf(fName, "/tmp/osd_%05db.ppm", osdCnt++); FILE *f = ::fopen(fName, "wb"); ::fprintf(f, "P6\n%d %d\n255\n", ws, hs); for (int y = 0; y < hs; y++) { for (int x = 0; x < ws; x++) { uint8_t *c = (uint8_t *)&palette2[ scaled[ y * ws + x ] ]; ::fwrite(&c[ 2 ], 1, 1, f); ::fwrite(&c[ 1 ], 1, 1, f); ::fwrite(&c[ 0 ], 1, 1, f); } } ::fclose(f); } */ delete pXinePalette; delete [] screen; delete [] scaled2; return scaled; } static tIndex *ScaleBitmapLQ(const int maxOsdWidth, const int maxOsdHeight, const tIndex *src, int x0, int y0, int w, int h, int stride, int ws, int hs, int x1, int y1, int w1, int h1, const uint8_t transparentIndex) { uint8_t *const screen = new uint8_t[ maxOsdHeight * maxOsdWidth ]; { int x1 = x0 + w; int y1 = y0 + h; int x2 = maxOsdWidth; int y2 = maxOsdHeight; if (x1 > x2) x1 = x2; if (y1 > y2) y1 = y2; uint8_t *dst = screen; for (int y = 0; y < y0; y++) { for (int x = 0; x < x2; x++) *dst++ = transparentIndex; } for (int y = y0; y < y1; y++) { for (int x = 0; x < x0; x++) *dst++ = transparentIndex; for (int x = x0; x < x1; x++) *dst++ = *src++; src += stride - w; for (int x = x1; x < x2; x++) *dst++ = transparentIndex; } for (int y = y1; y < y2; y++) { for (int x = 0; x < x2; x++) *dst++ = transparentIndex; } } tIndex *const scaled = new tIndex[ hs * ws ]; { int x2 = x1 + w1; int y2 = y1 + h1; if (x2 > ws) { x2 = ws; w1 = x2 - x1; if (w1 < 0) w1 = 0; } if (y2 > hs) { y2 = hs; h1 = y2 - y1; if (h1 < 0) h1 = 0; } cBresenham yyBh(maxOsdWidth, 2 * maxOsdHeight, 2 * hs, hs, screen); uint8_t *screen0 = yyBh.step(y1); //(((2 * y1 + 1) * maxOsdHeight / hs) / 2); cBresenham<> xxBh0(2 * maxOsdWidth, 2 * ws, ws); xxBh0.step(x1); //(((2 * x1 + 1) * maxOsdWidth / ws) / 2); tIndex *scaled0 = scaled + y1 * ws; for (int y = y1; y < y2; y++) { cBresenham<> xxBh(xxBh0); int xxx = xxBh.step(0); //(((2 * x1 + 1) * maxOsdWidth / ws) / 2); uint8_t *screen00 = screen0 + xxx; tIndex *scaled00 = scaled0 + x1; for (int x = x1; x < x2; x++) { *scaled00++ = *screen00; screen00 += xxBh.stepRelative(); } scaled0 += ws; screen0 = yyBh.step(); } } delete [] screen; return scaled; } bool cXineLib::execFuncOsdDrawBitmap(const int maxOsdWidth, const int maxOsdHeight, const eNeedsScaling needsScaling, const int videoWidth, const int videoHeight, cXineOsd *const xineOsd, int window, cXineAdapter *const bitmap, int x, int y, int width, int height, int stride) { if (!needsScaling) { // offset destination location by clipped area int xClip = 0; int yClip = 0; int xo = xineOsd->Left() + bitmap->X0(); int yo = xineOsd->Top() + bitmap->Y0(); if (xo < 0) xClip += xo; if (yo < 0) yClip += yo; int numColors = 0; return execFuncOsdDrawBitmap(window, bitmap->Data(x, y), bitmap->SizeOfPixel(), x + xClip, y + yClip, width, height, stride, bitmap->Colors(numColors)); } int numColors = 0; const tColor *colors = bitmap->Colors(numColors); if (!numColors) return true; static int crtGamma = 0; if (crtGamma != m_settings.GetCrtGamma()) { crtGamma = m_settings.GetCrtGamma(); init_linearize(crtGamma); init_delinearize(crtGamma); } int transparentIndex = numColors; for (int i = 0; i < numColors; i++) { if (clrTransparent == colors[ i ]) { transparentIndex = i; break; } } // ::fprintf(stderr, "ti: %d\t", transparentIndex); if (m_settings.OsdMode() == cXineSettings::osdBlendScaledLQ) { if (transparentIndex == numColors && numColors < 256) { execFuncSetColor(window, transparentIndex, clrTransparent); } } // calculate position and size of source bitmap on screen int xClip = 0; int yClip = 0; int xo = xineOsd->Left() + bitmap->X0(); int yo = xineOsd->Top() + bitmap->Y0(); int wo = bitmap->Width(); int ho = bitmap->Height(); if (xo < 0) { xClip += xo; wo += xo; xo = 0; } if (wo < 0) wo = 0; if (yo < 0) { yClip += yo; ho += yo; yo = 0; } if (ho < 0) ho = 0; // calculate position and size of destination bitmap on screen int xs = xo * videoWidth / maxOsdWidth; int ws = ((xo + wo) * videoWidth - 1 + maxOsdWidth) / maxOsdWidth - xs; int ys = yo * videoHeight / maxOsdHeight; int hs = ((yo + ho) * videoHeight - 1 + maxOsdHeight) / maxOsdHeight - ys; if (m_settings.OsdMode() > cXineSettings::osdBlendScaledLQ || numColors > 256) { if (numColors > 22 || shq == needsScaling) { // consider complete bitmap area, not just the dirty one x = 0; y = 0; width = x + bitmap->Width() - 1; height = y + bitmap->Height() - 1; if (clipBitmap(xineOsd, bitmap, x, y, width, height, xo, yo, wo, ho)) { width = 0; height = 0; } else { width = width - x + 1; height = height - y + 1; } } } // calculate position and size of dirty destination area on screen int x0 = (xo + xClip + x) * videoWidth / maxOsdWidth; int w0 = (((xo + xClip + x) + width) * videoWidth - 1 + maxOsdWidth) / maxOsdWidth - x0; int y0 = (yo + yClip + y) * videoHeight / maxOsdHeight; int h0 = (((yo + yClip + y) + height) * videoHeight - 1 + maxOsdHeight) / maxOsdHeight - y0; // calculate position and size of dirty destination area in destination bitmap x0 -= xs; y0 -= ys; (void)ws; (void)hs; // scale dirty source area into destination screen ... tIndex *scaledScreen = 0; if (m_settings.OsdMode() > cXineSettings::osdBlendScaledLQ || numColors > 256) { tColor *palette = 0; int paletteSize = 0; scaledScreen = (shq == needsScaling) ? ScaleBitmapSHQ(maxOsdWidth, maxOsdHeight, bitmap->Data(-xClip, -yClip), xo, yo, wo, ho, bitmap->Width(), videoWidth, videoHeight, x0 + xs, y0 + ys, w0, h0, transparentIndex, colors, numColors, palette, paletteSize, this) : ScaleBitmapHQ(maxOsdWidth, maxOsdHeight, bitmap->Data(-xClip, -yClip), xo, yo, wo, ho, bitmap->Width(), videoWidth, videoHeight, x0 + xs, y0 + ys, w0, h0, transparentIndex, colors, numColors, palette, paletteSize, m_scaledWindowColorsNum[ window ], &m_scaledWindowColors[ window ][ 0 ], this); if (shq == needsScaling) { execFuncSetColor(window, 0, palette, paletteSize); } else { // ::fprintf(stderr, "cached: "); if (m_scaledWindowColorsNum[ window ] < paletteSize || 0 != ::memcmp(&m_scaledWindowColors[ window ][ 0 ], palette, paletteSize * sizeof (*palette))) { m_scaledWindowColorsNum[ window ] = paletteSize; ::memcpy(&m_scaledWindowColors[ window ][ 0 ], palette, paletteSize * sizeof (*palette)); execFuncSetColor(window, 0, palette, paletteSize); // ::fprintf(stderr, "no\n"); } else { // ::fprintf(stderr, "yes\n"); } } delete [] palette; } else { scaledScreen = ScaleBitmapLQ(maxOsdWidth, maxOsdHeight, bitmap->Data(-xClip, -yClip), xo, yo, wo, ho, bitmap->Width(), videoWidth, videoHeight, x0 + xs, y0 + ys, w0, h0, transparentIndex); } // ::fprintf(stderr, "N: (%d,%d):(%d,%d)-(%d,%d)\n", xo, yo, x , y , width, height); // ::fprintf(stderr, "S: (%d,%d):(%d,%d)-(%d,%d)\n", xs, ys, x0, y0, w0 , h0 ); // clip dirty destination area's size to stay on screen ... if (w0 > videoWidth - (x0 + xs)) { w0 = videoWidth - (x0 + xs); if (w0 < 0) w0 = 0; } if (h0 > videoHeight - (y0 + ys)) { h0 = videoHeight - (y0 + ys); if (h0 < 0) h0 = 0; } // send dirty destination area to xine bool retVal = execFuncOsdDrawBitmap(window, scaledScreen + (y0 + ys) * videoWidth + (x0 + xs), bitmap->SizeOfPixel(), x0, y0, w0, h0, videoWidth, 0); delete [] scaledScreen; return retVal; } bool cXineLib::SupportsTrueColorOSD() const { return m_settings.OsdMode() != cXineSettings::osdOverlay && m_capabilities.osd_supports_argb_layer && m_capabilities.osd_supports_custom_extent; } cXineLib::eNeedsScaling cXineLib::NeedsScaling(const int maxOsdWidth, const int maxOsdHeight, const int videoWidth, const int videoHeight) { if (m_settings.OsdMode() < cXineSettings::osdBlendScaledLQ) return cXineLib::no; bool ignoreScaling = m_capabilities.osd_supports_argb_layer && m_capabilities.osd_supports_custom_extent; if (!ignoreScaling && videoWidth > 0 && videoHeight > 0 && (videoWidth != maxOsdWidth || videoHeight != maxOsdHeight)) { if (m_settings.OsdMode() == cXineSettings::osdBlendScaledAuto) { if ((2 * videoWidth) < maxOsdWidth || (2 * videoHeight) < maxOsdHeight) { return cXineLib::shq; } } if (m_settings.OsdMode() == cXineSettings::osdBlendScaledSHQ) return cXineLib::shq; return cXineLib::yes; } return cXineLib::no; } void cXineLib::SendWindow(const int maxOsdWidth, const int maxOsdHeight, cXineOsd *const xineOsd, const int windowNum, cXineAdapter *bitmap /* = 0 */, const int videoLeft /* = -1 */, const int videoTop /* = -1 */, const int videoWidth /* = -1 */, const int videoHeight /* = -1 */, const int videoZoomX /* = -1 */, const int videoZoomY /* = -1 */, const bool dontOptimize /* = false */) { int vl = videoLeft; int vt = videoTop; int vw = videoWidth; int vh = videoHeight; int zx = videoZoomX; int zy = videoZoomY; bool ignoreZoom = (m_settings.OsdMode() != cXineSettings::osdOverlay) && m_capabilities.osd_supports_argb_layer && m_capabilities.osd_supports_custom_extent; if (zx <= 100 || ignoreZoom) zx = 100; else { int nvl = (2 * vl + vw - vw * 100 / zx) / 2; int nvw = (2 * vl + vw + vw * 100 / zx) / 2 - nvl; vl = nvl; vw = nvw; } if (zy <= 100 || ignoreZoom) zy = 100; else { int nvt = (2 * vt + vh - vh * 100 / zy) / 2; int nvh = (2 * vt + vh + vh * 100 / zy) / 2 - nvt; vt = nvt; vh = nvh; } sendWindow(maxOsdWidth, maxOsdHeight, xineOsd, windowNum, bitmap, vl, vt, vw, vh, zx, zy, dontOptimize); } void cXineLib::sendWindow(const int maxOsdWidth, const int maxOsdHeight, cXineOsd *const xineOsd, const int windowNum, cXineAdapter *bitmap /* = 0 */, const int videoLeft /* = -1 */, const int videoTop /* = -1 */, const int videoWidth /* = -1 */, const int videoHeight /* = -1 */, const int videoZoomX /* = -1 */, const int videoZoomY /* = -1 */, const bool dontOptimize /* = false */) { const cXineSettings::eOsdMode mode = m_settings.OsdMode(); const bool supportTransparency = m_settings.SupportTransparency(); const eNeedsScaling needsScaling = NeedsScaling(maxOsdWidth, maxOsdHeight, videoWidth, videoHeight); const bool videoMatters = !(m_capabilities.osd_supports_argb_layer && m_capabilities.osd_supports_custom_extent); const int crtGamma = m_settings.GetCrtGamma(); int osdX = xineOsd->Left() + (bitmap ? bitmap->X0() : 0); int osdY = xineOsd->Top() + (bitmap ? bitmap->Y0() : 0); int osdW = (bitmap ? bitmap->Width() : 0); int osdH = (bitmap ? bitmap->Height() : 0); if (osdX < 0) { osdW += osdX; osdX = 0; } if (osdW < 0) osdW = 0; if (osdY < 0) { osdH += osdY; osdY = 0; } if (osdH < 0) osdH = 0; assert(0 <= windowNum && windowNum < MAXNUMWINDOWS); if (!bitmap || dontOptimize || (videoMatters && m_osdWindowVideoLeft[ windowNum ] != videoLeft) || (videoMatters && m_osdWindowVideoTop[ windowNum ] != videoTop) || (videoMatters && m_osdWindowVideoWidth[ windowNum ] != videoWidth) || (videoMatters && m_osdWindowVideoHeight[ windowNum ] != videoHeight) || (videoMatters && m_osdWindowVideoZoomX[ windowNum ] != videoZoomX) || (videoMatters && m_osdWindowVideoZoomY[ windowNum ] != videoZoomY) || m_osdWindowLeft[ windowNum ] != osdX || m_osdWindowTop[ windowNum ] != osdY || m_osdWindowWidth[ windowNum ] != osdW || m_osdWindowHeight[ windowNum ] != osdH || m_osdWindowMode[ windowNum ] != mode || m_osdWindowSupportTransparency[ windowNum ] != supportTransparency || m_osdWindowGamma[ windowNum ] != crtGamma) { if (m_osdWindowVisible[ windowNum ] || dontOptimize) { m_osdWindowVisible[ windowNum ] = false; m_osdWindowVideoLeft[ windowNum ] = videoLeft; m_osdWindowVideoTop[ windowNum ] = videoTop; m_osdWindowVideoWidth[ windowNum ] = videoWidth; m_osdWindowVideoHeight[ windowNum ] = videoHeight; m_osdWindowVideoZoomX[ windowNum ] = videoZoomX; m_osdWindowVideoZoomY[ windowNum ] = videoZoomY; m_osdWindowLeft[ windowNum ] = osdX; m_osdWindowTop[ windowNum ] = osdY; m_osdWindowWidth[ windowNum ] = osdW; m_osdWindowHeight[ windowNum ] = osdH; m_osdWindowMode[ windowNum ] = mode; m_osdWindowSupportTransparency[ windowNum ] = supportTransparency; m_osdWindowGamma[ windowNum ] = crtGamma; execFuncOsdHide(windowNum); execFuncOsdFree(windowNum); } if (!bitmap) return; } bool colorsModified = dontOptimize; if (!m_osdWindowVisible[ windowNum ] || dontOptimize) { m_osdWindowVideoLeft[ windowNum ] = videoLeft; m_osdWindowVideoTop[ windowNum ] = videoTop; m_osdWindowVideoWidth[ windowNum ] = videoWidth; m_osdWindowVideoHeight[ windowNum ] = videoHeight; m_osdWindowVideoZoomX[ windowNum ] = videoZoomX; m_osdWindowVideoZoomY[ windowNum ] = videoZoomY; m_osdWindowLeft[ windowNum ] = osdX; m_osdWindowTop[ windowNum ] = osdY; m_osdWindowWidth[ windowNum ] = osdW; m_osdWindowHeight[ windowNum ] = osdH; m_osdWindowMode[ windowNum ] = mode; m_osdWindowSupportTransparency[ windowNum ] = supportTransparency; m_osdWindowGamma[ windowNum ] = crtGamma; m_scaledWindowColorsNum[ windowNum ] = 0; /* fprintf(stderr, "Bitmap[ %d ]: (%d,%d)-(%d,%d)\n" , windowNum , bitmap->X0() , bitmap->Y0() , bitmap->Width() , bitmap->Height()); */ assert(0x0000 <= bitmap->Width() && bitmap->Width() <= 0x0fff); assert(0x0000 <= bitmap->Height() && bitmap->Height() <= 0x0fff); int vx = m_osdWindowVideoLeft[ windowNum ]; int vy = m_osdWindowVideoTop[ windowNum ]; if (mode < cXineSettings::osdBlendScaledLQ) { vx = 0; vy = 0; } else { if (vx < 0) vx = 0; if (vy < 0) vy = 0; } // fprintf(stderr, "n: %d: f: %d: v: (%d, %d) - (%d, %d): (%d, %d)\n", windowNum, dontOptimize, videoLeft, videoTop, videoWidth, videoHeight, vx, vy); execFuncOsdNew(maxOsdWidth, maxOsdHeight, needsScaling, vx, vy, videoWidth, videoHeight, windowNum, osdX, osdY, osdW, osdH); colorsModified = true; } int numColors = 0; const tColor *colors = bitmap->Colors(numColors); int &numWindowColors = m_osdWindowColorsNum[ windowNum ]; tColor *windowColors = &m_osdWindowColors[ windowNum ][ 0 ]; const bool setColor = !((m_settings.OsdMode() != cXineSettings::osdOverlay) && m_capabilities.osd_supports_argb_layer) && (!needsScaling || (m_settings.OsdMode() <= cXineSettings::osdBlendScaledLQ)); for (int i = 0; i < numColors; i++) { if (dontOptimize || !m_osdWindowVisible[ windowNum ] || numWindowColors <= i || *windowColors != *colors) { if (setColor) execFuncSetColor(windowNum, i, filterAlpha(*colors)); *windowColors = *colors; colorsModified = true; } windowColors++; colors++; } numWindowColors = numColors; int x1 = 0, y1 = 0, x2 = bitmap->Width() - 1, y2 = bitmap->Height() - 1; if (clipBitmap(xineOsd, bitmap, x1, y1, x2, y2, osdX, osdY, osdW, osdH)) { if (!m_osdWindowVisible[ windowNum ]) execFuncOsdShow(windowNum); bitmap->Clean(); } else if (colorsModified || bitmapDiffers(windowNum, xineOsd, bitmap, x1, y1, x2, y2, osdX, osdY, osdW, osdH)) { // fprintf(stderr, "windowNum: %d, dirty area: (%d, %d) - (%d, %d), bitmap size: (%d, %d) %d %d\n", windowNum, x1, y1, x2, y2, bitmap->Width(), bitmap->Height(), colorsModified, !m_osdWindowVisible[ windowNum ]); cloneBitmap(windowNum, bitmap, x1, y1, x2, y2); execFuncOsdDrawBitmap(maxOsdWidth, maxOsdHeight, needsScaling, videoWidth, videoHeight, xineOsd, windowNum, bitmap, x1, y1, x2 - x1 + 1, y2 - y1 + 1, bitmap->Width()); execFuncOsdShow(windowNum); bitmap->Clean(); } else { // fprintf(stderr, "not dirty\n"); } m_osdWindowVisible[ windowNum ] = true; } void cXineLib::cloneBitmap(const int windowNum, cXineAdapter *bitmap, int x1, int y1, int x2, int y2) { #if VERIFY_BITMAP_DIRTY < 1 return; #endif int w = bitmap->Width(); int h = bitmap->Height(); int stride = w * bitmap->SizeOfPixel(); int s = h * stride; if (m_osdWindowBufferSize[ windowNum ] < s) { if (m_osdWindowBuffer[ windowNum ]) delete [] m_osdWindowBuffer[ windowNum ]; m_osdWindowBufferSize[ windowNum ] = 0; m_osdWindowBuffer[ windowNum ] = new uint8_t[ s ]; if (!m_osdWindowBuffer[ windowNum ]) return; m_osdWindowBufferSize[ windowNum ] = s; x1 = 0; y1 = 0; x2 = w - 1; y2 = h - 1; } const uint8_t *src = bitmap->Data(0, 0) + bitmap->SizeOfStride() * y1 + bitmap->SizeOfPixel() * x1; uint8_t *dst = m_osdWindowBuffer[ windowNum ] + stride * y1 + bitmap->SizeOfPixel() * x1; int n = (x2 - x1 + 1) * bitmap->SizeOfPixel(); for (int y = y1; y <= y2; y++) { memcpy(dst, src, n); src += bitmap->SizeOfStride(); dst += stride; } } bool cXineLib::clipBitmap(cXineOsd *xineOsd, cXineAdapter *bitmap, int &x1, int &y1, int &x2, int &y2, const int osdX, const int osdY, const int osdW, const int osdH) { int osdX1 = xineOsd->Left() + bitmap->X0() + x1; int osdY1 = xineOsd->Top() + bitmap->Y0() + y1; int osdX2 = xineOsd->Left() + bitmap->X0() + x2; int osdY2 = xineOsd->Top() + bitmap->Y0() + y2; if (osdX1 < osdX) x1 -= osdX1 - osdX; if (osdY1 < osdY) y1 -= osdY1 - osdY; if (osdX2 > (osdX + osdW - 1)) x2 -= osdX2 - (osdX + osdW - 1); if (osdY2 > (osdY + osdH - 1)) y2 -= osdY2 - (osdY + osdH - 1); return (x1 > x2) || (y1 > y2); } bool cXineLib::bitmapDiffers(const int windowNum, cXineOsd *xineOsd, cXineAdapter *bitmap, int &x1, int &y1, int &x2, int &y2, const int osdX, const int osdY, const int osdW, const int osdH) { if (!bitmap->Dirty(x1, y1, x2, y2)) return false; if (clipBitmap(xineOsd, bitmap, x1, y1, x2, y2, osdX, osdY, osdW, osdH)) { // dirty area clipped is clipped away bitmap->Clean(); return false; } #if VERIFY_BITMAP_DIRTY < 1 return true; #endif int w = bitmap->Width(); int h = bitmap->Height(); int stride = w * bitmap->SizeOfPixel(); int s = h * stride; if (m_osdWindowBufferSize[ windowNum ] < s) return true; if (!m_osdWindowBuffer[ windowNum ]) return true; const uint8_t *src = bitmap->Data(0, 0) + bitmap->SizeOfStride() * y1 + bitmap->SizeOfPixel() * x1; const uint8_t *dst = m_osdWindowBuffer[ windowNum ] + stride * y1 + bitmap->SizeOfPixel() * x1; int n = (x2 - x1 + 1) * bitmap->SizeOfPixel(); for (int y = y1; y <= y2; y++) { if (0 != memcmp(dst, src, n)) return true; src += bitmap->SizeOfStride(); dst += stride; } struct timeval tv; ::gettimeofday(&tv, 0); double tt = tv.tv_sec + tv.tv_usec * 1e-6; fprintf(stderr, "%.3lf OPTIMIZE OSD DRAWING: bitmap dirty, but no difference!\n", tt); #if VERIFY_BITMAP_DIRTY > 1 *(char *)0 = 0; // fail with a core dump #endif bitmap->Clean(); return false; } void cXineLib::SetVideoWindow(const int maxOsdWidth, const int maxOsdHeight, tArea vidWin, const bool dontOptimize /* = false */) { if (0 == vidWin.bpp) { vidWin.x1 = 0; vidWin.y1 = 0; vidWin.x2 = -1 + maxOsdWidth; vidWin.y2 = -1 + maxOsdHeight; } else { m_vidWin.bpp = vidWin.bpp; } if (dontOptimize || (0 != ::memcmp(&m_vidWin, &vidWin, sizeof (m_vidWin)))) { m_vidWin = vidWin; execFuncSetVideoWindow(m_vidWin.x1, m_vidWin.y1, m_vidWin.Width(), m_vidWin.Height(), maxOsdWidth, maxOsdHeight); } } #endif extern int GetBindIp(cPlugin *const plugin); extern int GetBindPort(cPlugin *const plugin); extern int GetInstanceNo(cPlugin *const plugin); extern cXineLib *&GetXineLib(cPlugin *const plugin); cXineLib::cXineLib(cPlugin *const plugin, const cXineSettings &settings, cMutex &osdMutex, cXineRemote *const remote) : cThread() , m_plugin(plugin) , m_settings(settings) , m_osdFlushRequired(false) , fd_fifo0_serv(-1) , fd_result_serv(-1) , fd_control_serv(-1) , fd_remote_serv(-1) , fd_fifo0(-1) , fd_result(-1) , fd_control(-1) , fd_remote(-1) , m_osdMutex(osdMutex) , m_paused(false) , m_frozen(false) , m_ignore(false) , m_shutdown(false) , m_muted(false) , m_volume(0) , m_audioChannel(0) , m_trickSpeedMode(false) , m_noSignalStream16x9(false) , m_eventSink(0) { memset(&m_capabilities, 0, sizeof (m_capabilities)); m_noSignalStreamSize[0] = 0; m_noSignalStreamSize[1] = 0; m_fifoDir = FIFO_DIR; if (GetInstanceNo(plugin) >= 0) { char s[ 20 ]; ::sprintf(s, "%d", GetInstanceNo(plugin)); m_fifoDir += s; } m_bindIp = GetBindIp(plugin); m_bindPort = GetBindPort(plugin); m_fifoNameControl = m_fifoDir + "/stream.control"; m_fifoNameResult = m_fifoDir + "/stream.result"; m_fifoNameRemote = m_fifoDir + "/stream.event"; m_fifoNameStream = m_fifoDir + "/stream"; m_fifoNameExtControl = m_fifoDir + FIFO_NAME_EXT_CONTROL; m_fifoNameExtResult = m_fifoDir + FIFO_NAME_EXT_RESULT; m_external.setXineLib(this); ::memset(m_osdWindowVisible, 0, sizeof (m_osdWindowVisible)); #if APIVERSNUM >= 10307 ::memset(m_osdWindowBufferSize, 0, sizeof (m_osdWindowBufferSize)); ::memset(m_osdWindowBuffer, 0, sizeof (m_osdWindowBuffer)); ::memset(m_osdWindowColorsNum, 0, sizeof (m_osdWindowColorsNum)); ::memset(m_osdWindowColors, 0, sizeof (m_osdWindowColors)); ::memset(m_scaledWindowColorsNum, 0, sizeof (m_scaledWindowColorsNum)); ::memset(m_scaledWindowColors, 0, sizeof (m_scaledWindowColors)); ::memset(&m_vidWin, 0, sizeof (m_vidWin)); ::memset(m_osdWindowVideoLeft, 0, sizeof (m_osdWindowVideoLeft)); ::memset(m_osdWindowVideoTop, 0, sizeof (m_osdWindowVideoTop)); ::memset(m_osdWindowVideoWidth, 0, sizeof (m_osdWindowVideoWidth)); ::memset(m_osdWindowVideoHeight, 0, sizeof (m_osdWindowVideoHeight)); ::memset(m_osdWindowVideoZoomX, 0, sizeof (m_osdWindowVideoZoomX)); ::memset(m_osdWindowVideoZoomY, 0, sizeof (m_osdWindowVideoZoomY)); ::memset(m_osdWindowLeft, 0, sizeof (m_osdWindowLeft)); ::memset(m_osdWindowTop, 0, sizeof (m_osdWindowTop)); ::memset(m_osdWindowWidth, 0, sizeof (m_osdWindowWidth)); ::memset(m_osdWindowHeight, 0, sizeof (m_osdWindowHeight)); ::memset(m_osdWindowMode, 0, sizeof (m_osdWindowMode)); ::memset(m_osdWindowGamma, 0, sizeof (m_osdWindowGamma)); ::memset(m_osdWindowSupportTransparency, 0, sizeof (m_osdWindowSupportTransparency)); #endif readNoSignalStream(0, "4x3") || readNoSignalStream(0, ""); readNoSignalStream(1, "16x9") || readNoSignalStream(1, ""); assert(remote); remote->setXineLib(this); GetXineLib(m_plugin) = this; } bool cXineLib::readNoSignalStream(const int index, const string &suffix) { string noSignalFileName = m_plugin->ConfigDirectory(PLUGIN_NAME_I18N); // noSignalFileName += "/noSignal.pes"; noSignalFileName += "/noSignal" + suffix + ".mpg"; FILE *const f = ::fopen(noSignalFileName.c_str(), "rb"); if (f) { m_noSignalStreamSize[index] = ::fread(&m_noSignalStreamData[index][0] + 9, 1, sizeof (m_noSignalStreamData[index]) - 9 - 9 - 4, f); if (m_noSignalStreamSize[index] == sizeof (m_noSignalStreamData[index]) - 9 - 9 - 4) { ::fprintf(stderr, "vdr-xine: error: '%s' exeeds limit of %ld bytes!\n", noSignalFileName.c_str(), (long)(sizeof (m_noSignalStreamData[index]) - 9 - 9 - 4 - 1)); esyslog("vdr-xine: error: '%s' exeeds limit of %ld bytes!\n", noSignalFileName.c_str(), (long)(sizeof (m_noSignalStreamData[index]) - 9 - 9 - 4 - 1)); } else if (m_noSignalStreamSize[index] > 0) { m_noSignalStreamData[index][ 0 ] = 0x00; m_noSignalStreamData[index][ 1 ] = 0x00; m_noSignalStreamData[index][ 2 ] = 0x01; m_noSignalStreamData[index][ 3 ] = 0xe0; m_noSignalStreamData[index][ 4 ] = (m_noSignalStreamSize[index] + 3) >> 8; m_noSignalStreamData[index][ 5 ] = (m_noSignalStreamSize[index] + 3) & 0xff; m_noSignalStreamData[index][ 6 ] = 0x80; m_noSignalStreamData[index][ 7 ] = 0x00; m_noSignalStreamData[index][ 8 ] = 0x00; m_noSignalStreamSize[index] += 9; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x00; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x00; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x01; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0xe0; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x00; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x07; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x80; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x00; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x00; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x00; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x00; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0x01; m_noSignalStreamData[index][ m_noSignalStreamSize[index]++ ] = 0xb7; } ::fclose(f); return true; } else { ::fprintf(stderr, "vdr-xine: error: couldn't open '%s'!\n", noSignalFileName.c_str()); esyslog("vdr-xine: error: couldn't open '%s'!\n", noSignalFileName.c_str()); } return false; } cXineLib::~cXineLib() { Close(); GetXineLib(m_plugin) = 0; #if APIVERSNUM >= 10307 for (int i = 0; i < MAXNUMWINDOWS; i++) { if (m_osdWindowBuffer[ i ]) delete [] m_osdWindowBuffer[ i ]; } #endif } void cXineLib::SetEventSink(cXineLibEvents *const eventSink) { m_eventSink = eventSink; } int cXineLib::CreateServerSocket(unsigned short port) { int fd; int onoff = 1; struct sockaddr_in sain; if ((fd = ::socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket failed."); return -1; } memset(&sain, 0, sizeof (sain)); sain.sin_addr.s_addr = m_bindIp; sain.sin_port = htons(port); ::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &onoff, sizeof (int)); onoff = (port != m_bindPort); ::setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &onoff, sizeof(int)); if (::bind(fd, (struct sockaddr *)&sain, sizeof (sain)) != 0) { perror("bind failed."); return -1; } if (::listen(fd, 1) != 0) { printf("listen failed."); return -1; } return fd; } bool cXineLib::Open() { ::unlink(m_fifoNameExtControl.c_str()); ::unlink(m_fifoNameExtResult.c_str()); ::unlink(m_fifoNameControl.c_str()); ::unlink(m_fifoNameResult.c_str()); ::unlink(m_fifoNameRemote.c_str()); ::unlink(m_fifoNameStream.c_str()); ::rmdir(m_fifoDir.c_str()); const mode_t origUmask = ::umask(0); #define MkFifo(String, Mask) \ do { if (::mknod((String).c_str(), (Mask) | S_IFIFO, 0) < 0) \ { \ string msg = "vdr-xine: error: couldn't create fifo '" + (String) + "'"; \ perror(msg.c_str()); \ esyslog("%s", msg.c_str()); \ ::umask(origUmask); \ return false; \ } } while (0) struct stat stat_buf; if (::stat(m_fifoDir.c_str(), &stat_buf) < 0) { if (::mkdir(m_fifoDir.c_str(), 0755) < 0) { string msg = "vdr-xine: error: couldn't create directory '" + m_fifoDir + "'"; perror(msg.c_str()); esyslog("%s", msg.c_str()); ::umask(origUmask); return false; } } MkFifo(m_fifoNameExtControl, 0666); MkFifo(m_fifoNameExtResult, 0644); if (m_bindPort <= 0) { MkFifo(m_fifoNameControl, 0644); MkFifo(m_fifoNameResult, 0666); MkFifo(m_fifoNameRemote, 0666); MkFifo(m_fifoNameStream, 0644); } else { if ((fd_fifo0_serv = CreateServerSocket(m_bindPort + 0)) == -1) return false; if ((fd_control_serv = CreateServerSocket(m_bindPort + 1)) == -1) return false; if ((fd_result_serv = CreateServerSocket(m_bindPort + 2)) == -1) return false; if ((fd_remote_serv = CreateServerSocket(m_bindPort + 3)) == -1) return false; } #undef MkFifo ::umask(origUmask); if (!Start()) return false; m_external.StartExternal(); return true; } void cXineLib::Close() { m_external.StopExternal(); { cMutexLock shutdownMutexLock(&m_shutdownMutex); m_shutdown = true; m_shutdownCondVar.Broadcast(); } { DLOG("cXineLib::Close"); cMutexLock ioLock(&m_ioMutex); disconnect(); } ::unlink(m_fifoNameExtControl.c_str()); ::unlink(m_fifoNameExtResult.c_str()); if (m_bindPort <= 0) { ::unlink(m_fifoNameControl.c_str()); ::unlink(m_fifoNameResult.c_str()); ::unlink(m_fifoNameRemote.c_str()); ::unlink(m_fifoNameStream.c_str()); } else { ::close(fd_remote_serv); ::close(fd_result_serv); ::close(fd_control_serv); ::close(fd_fifo0_serv); } ::rmdir(m_fifoDir.c_str()); } void cXineLib::internalPaused(const bool paused) { cMutexLock pausedMutexLock(&m_pausedMutex); m_paused = paused; if (!paused) m_pausedCondVar.Broadcast(); } bool cXineLib::Poll(cPoller &Poller, int TimeoutMs /* = 0 */, const bool special /* = false */) { if (m_paused) { if (TimeoutMs > 0) { cMutexLock pausedMutexLock(&m_pausedMutex); if (m_paused) m_pausedCondVar.TimedWait(m_pausedMutex, TimeoutMs); } // fprintf(stderr, "p"); return false; } if (-1 == fd_fifo0) return true; Poller.Add(fd_fifo0, true); if (Poller.Poll(TimeoutMs < 0 ? 0 : TimeoutMs)) { // fprintf(stderr, "_"); return true; } if (TimeoutMs >= 0) xfprintf(stderr, (special ? "p" : "P")); return false; } void cXineLib::flush() { ::fsync(fd_fifo0); } int cXineLib::xread(int f, void *b, int n) { int t = 0; while (t < n) { void (* const sigPipeHandler)(int) = ::signal(SIGPIPE, SIG_IGN); errno = 0; int r = ::read(f, ((char *)b) + t, n - t); int myErrno = errno; ::signal(SIGPIPE, sigPipeHandler); if (r <= 0) { if (EAGAIN == myErrno || EINTR == myErrno) continue; xfprintf(stderr, "read(%d) returned %d, error %d: ", n, r, myErrno); errno = myErrno; if (!m_settings.ShallBeQuiet()) perror(""); disconnect(); return r; } t += r; } return t; } int cXineLib::xwrite(int f, const void *b, int n) { const char *yyy[] = { "i", "I", "d", "D" }; // char *yyy[] = { "", "", "d", "D" }; const char **xxx = yyy; if (f == fd_fifo0) xxx += 2; int t = 0; while (t < n) { // fprintf(stderr, "%s", xxx[ 0 ]); // fprintf(stderr, " xwrite "); void (* const sigPipeHandler)(int) = ::signal(SIGPIPE, SIG_IGN); errno = 0; int r = ::write(f, ((char *)b) + t, n - t); int myErrno = errno; ::signal(SIGPIPE, sigPipeHandler); if (r <= 0) { if (EAGAIN == myErrno || EINTR == myErrno) break; xfprintf(stderr, "::write(%d) returned %d, error %d: ", n, r, myErrno); errno = myErrno; if (!m_settings.ShallBeQuiet()) perror(""); disconnect(); // fprintf(stderr, "%s", xxx[ 1 ]); return r; } /* if (t != 0 || r != n) fprintf(stderr, "::write(%d): %d\n", n - t, r); */ t += r; } // fprintf(stderr, "%s", xxx[ 1 ]); return t; } bool cXineLib::showNoSignal() { // fprintf(stderr, "showNoSignal: enter\n"); int index = m_noSignalStream16x9; bool x = execFuncStream0((uchar *)&m_noSignalStreamData[index][0], m_noSignalStreamSize[index]); // fprintf(stderr, "showNoSignal: leave\n"); return x; } static tThreadId limit_thread = -1; static tThreadId GetThreadID() { #if APIVERSNUM >= 10337 return cThread::ThreadId(); #else return syscall(__NR_gettid); #endif } bool cXineLib::isConnected() { // return (-1 != fd_fifo0); if (-1 == fd_fifo0) return false; if (-1 == limit_thread) return true; return limit_thread == GetThreadID(); } static bool m_connectionEstablished = false; void cXineLib::disconnect() { cMutexLock disconnectLock(&m_disconnectMutex); const bool wasConnected = m_connectionEstablished; m_connectionEstablished = false; if (-1 != fd_control) { // ::fprintf(stderr, "close: fd_control\n"); ::close(fd_control); fd_control = -1; } if (-1 != fd_result) { // ::fprintf(stderr, "close: fd_result\n"); ::close(fd_result); fd_result = -1; } if (-1 != fd_remote) { // ::fprintf(stderr, "close: fd_remote\n"); ::close(fd_remote); fd_remote = -1; } if (-1 != fd_fifo0) { // ::fprintf(stderr, "close: fd_fifo0\n"); ::close(fd_fifo0); fd_fifo0 = -1; } m_external.disconnect(); if (wasConnected) { if (m_eventSink) m_eventSink->OnClientDisconnect(); xfprintf(stderr, "vdr-xine: Client disconnected!\n"); } memset(&m_capabilities, 0, sizeof (m_capabilities)); } bool cXineLib::execFuncNop() { if (!isConnected()) return false; DLOG("cXineLib::execFuncNop"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_nop_t data; data.header.func = func_nop; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; return false; } void cXineLib::Action(void) { int delayConnect = 0; while (!m_shutdown) { if (!isConnected()) { // fprintf(stderr, "a"); DLOG("cXineLib::Action"); if (delayConnect > 0) delayConnect--; else { cXineOsdMutexLock osdLock(&m_osdMutex); cMutexLock ioLock(&m_ioMutex); cMutexLock dataLock(&m_dataMutex); checkConnect(); } } else { // fprintf(stderr, "A"); if (m_settings.InteractWithEitScanner() && EITScanner.Active() && m_eventSink && !m_eventSink->DeviceReplayingOrTransferring()) { cMutexLock ioLock(&m_ioMutex); disconnect(); delayConnect = 50; } else execFuncNop(); // fprintf(stderr, "n"); } if (!m_shutdown) { cMutexLock shutdownMutexLock(&m_shutdownMutex); if (!m_shutdown) m_shutdownCondVar.TimedWait(m_shutdownMutex, 100); } } // fprintf(stderr, "Action done\n"); } int cXineLib::SocketAcceptHelper(int fd) { // use cPoller for checking server socket for incoming requests cPoller poller(fd,0); /* POLLIN */ struct sockaddr sain; socklen_t len = sizeof (sain); int client; // ::fprintf(stderr, "vdr-xine: polling for connection on %d ...\n", fd); if (!poller.Poll(100)) return -1; // ::fprintf(stderr, "vdr-xine: incoming requests on %d\n", fd); if ((client = ::accept(fd, (struct sockaddr *)&sain, &len)) == -1) { ::fprintf(stderr, "vdr-xine: socket failed to accept ...\n"); return -1; } // ::fprintf(stderr, "vdr-xine: successful request on %d (client: %d)\n", fd, client); return client; } bool cXineLib::checkXineVersion() { int32_t version = 0; execFuncGetVersion(version); if (MIN_XINE_VDR_VERSION <= version /* && version <= MAX_XINE_VDR_VERSION */) return true; xfprintf(stderr, "vdr-xine: Client reports unsupported version %d => disconnecting!\n", version); disconnect(); return false; } bool cXineLib::checkConnect() { limit_thread = GetThreadID(); if (m_bindPort <= 0) { fd_fifo0 = ::open(m_fifoNameStream.c_str(), O_WRONLY | O_NONBLOCK); if (-1 == fd_fifo0) return false; xfprintf(stderr, "vdr-xine: Client connecting ...\n"); char zero = 0; xwrite(fd_fifo0, &zero, sizeof (zero)); fd_remote = ::open(m_fifoNameRemote.c_str(), O_RDONLY | O_NONBLOCK); fd_control = ::open(m_fifoNameControl.c_str(), O_WRONLY); fd_result = ::open(m_fifoNameResult.c_str() , O_RDONLY); ::fcntl(fd_fifo0 , F_SETFL, ~O_NONBLOCK & ::fcntl(fd_fifo0 , F_GETFL, 0)); ::fcntl(fd_remote, F_SETFL, ~O_NONBLOCK & ::fcntl(fd_remote, F_GETFL, 0)); } else { if (fd_fifo0_serv == -1) return false; if ((fd_fifo0 = SocketAcceptHelper(fd_fifo0_serv)) == -1) return false; xfprintf(stderr, "vdr-xine: Client connecting ...\n"); if ((fd_control = SocketAcceptHelper(fd_control_serv)) == -1) return false; if ((fd_result = SocketAcceptHelper(fd_result_serv)) == -1) return false; if ((fd_remote = SocketAcceptHelper(fd_remote_serv)) == -1) return false; } internalPaused(false); m_frozen = false; m_ignore = false; // reinit = true; // doBufferAndStart(); // } // if (reinit) // { if (checkXineVersion()) { execFuncQueryCapabilities(m_capabilities); execFuncSetup(); // execFuncMute(m_muted); // execFuncSetVolume(m_volume); execFuncSelectAudio(m_audioChannel); execFuncTrickSpeedMode(m_trickSpeedMode); execFuncSetPrebuffer(0); execFuncClear(-1); // execFuncStart(); execFuncStillFrame(); execFuncWait(true); for (int i = 0; i < 25; i++) showNoSignal(); pushOut(); execFuncFlush(); // execFuncResetAudio(); // execFuncWait(); // execFuncSetPrebuffer(0); //m_settings.GetModeParams()->m_prebufferFrames); execFuncClear(-5); execFuncWait(); // m_setPrebufferRequired = true; /* if (m_setPrebufferRequired) { fprintf(stderr, "Prebuffer required\n"); m_setPrebufferRequired = false; if (!isConnected()) return false; } */ if (m_eventSink && isConnected()) { m_eventSink->OnClientConnect(); if (m_settings.InteractWithEitScanner() && EITScanner.Active() && !m_eventSink->DeviceReplayingOrTransferring()) EITScanner.Activity(); } execFuncSetVolume(m_volume); limit_thread = -1; m_connectionEstablished = true; } // execFuncWait(); // execFuncSetSpeed(-1); // execFuncWait(); { // if (!execFuncSetSpeed(-1)) // return false; #if 0 if (execFuncOsdNew(0xf, 320, 16, 16, 16, 0, 0, false)) { if (execFuncSetColor(0xf, 0, clrBlack)) { if (execFuncSetColor(0xf, 1, clrCyan)) { if (execFuncSetColor(0xf, 2, clrRed)) { uint8_t data[ 16 ][ 16 ] = { { 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2 }, }; if (execFuncOsdDrawBitmap(0xf, &data[ 0 ][ 0 ], 0, 0, 16, 16, 16)) { if (execFuncOsdShow(0xf)) { return true; } } } } } } #endif } // execFuncSetPrebuffer(31); // execFuncStart(); // execFuncWait(); if (isConnected()) { xfprintf(stderr, "vdr-xine: Client connected!\n"); return true; } // } // else // { // dataNop_t dataNop; // dataNop.header.func = funcNop; // dataNop.header.len = sizeof (dataNop); // // if (sizeof (dataNop) == xwrite(fd_control, &dataNop, sizeof (dataNop))) // return true; // } // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; xfprintf(stderr, "vdr-xine: Client connect failed!\n"); return false; } /* void cXineLib::doBufferAndStart() { m_doBufferAndStart = 64000; } */ bool cXineLib::execFuncSetPrebuffer(int frames) { if (!isConnected()) return false; DLOG("cXineLib::execFuncSetPrebuffer"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_set_prebuffer_t data; data.header.func = func_set_prebuffer; data.header.len = sizeof (data); data.prebuffer = frames * 90000 / 25; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } int cXineLib::execFuncStream1(const uchar *Data, int Length) { if (!m_connectionEstablished) return Length; return execFuncStream(Data, Length); } int cXineLib::execFuncStream(const uchar *Data, int Length) { if (!isConnected()) return Length; // return true; // { // cMutexLock ioLock(&m_ioMutex); // // if (!checkConnect()) // return false; // } if (m_paused) return 0; if (m_frozen) return -1; if (m_ignore) return Length; cMutexLock dataLock(&m_dataMutex); if (!isConnected()) return Length; int r = xwrite(fd_fifo0, Data, Length); if (Length == r) // fprintf(stderr, "."); if (r < 0) xfprintf(stderr, "X"); return r; #if 0 cMutexLock ioLock(&m_ioMutex); if (!checkConnect()) return false; if (m_frozen) return false; if (m_ignore) return true; dataStream_t dataStream; dataStream.header.func = funcStream; dataStream.header.len = sizeof (dataStream) + Length; assert((sizeof (dataStream) + Length) == dataStream.header.len); if (sizeof (dataStream) == xwrite(fd_fifo, &dataStream, sizeof (dataStream))) { if (Length == xwrite(fd_fifo, Data, Length)) { /* if (m_doBufferAndStart > 0) { m_doBufferAndStart -= Length; if (m_doBufferAndStart <= 0) { if (!execFuncSetSpeed(0) || !execFuncMute(false)) { return false; } } } */ return true; } } ::close(fd_control); ::close(fd_fifo0); fd_fifo0 = -1; return false; #endif } bool cXineLib::execFuncStream0(const uchar *Data, int Length) { if (!isConnected()) return true; // return true; // { // cMutexLock ioLock(&m_ioMutex); // // if (!checkConnect()) // return false; // } cMutexLock dataLock(&m_dataMutex); int done = 0; while (done < Length) { if (!isConnected()) return true; int r = xwrite(fd_fifo0, Data + done, Length - done); if (r < 0) return false; done += r; } return Length == done; } bool cXineLib::execFuncStart() { if (!isConnected()) return false; DLOG("cXineLib::execFuncStart"); cMutexLock ioLock(&m_ioMutex); // if (!checkConnect()) // return false; if (!isConnected()) return false; data_start_t data; data.header.func = func_start; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::osdUpdateLocked(const char *const funcName) { if (m_eventSink && m_eventSink->OsdUpdateLocked()) { // fprintf(stderr, "=== cXineLib::%s -> osdUpdateLocked\n", funcName); return true; } return false; } bool cXineLib::execFuncOsdNew(int window, int x, int y, int width, int height, int w_ref, int h_ref) { if (!isConnected()) return false; DLOG("cXineLib::execFuncOsdNew"); cMutexLock ioLock(&m_ioMutex); // if (!checkConnect()) // return false; if (!isConnected()) return false; data_osd_new_t data; data.header.func = func_osd_new; data.header.len = sizeof (data); data.window = window; data.x = x; data.y = y; data.width = width; data.height = height; data.w_ref = w_ref; data.h_ref = h_ref; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } #if APIVERSNUM < 10307 bool cXineLib::execFuncSetColor(int window, int index, eDvbColor color) { return execFuncSetColor(window, index, &color, 1); } bool cXineLib::execFuncSetColor(int window, int index, eDvbColor *const colors, int numColors) { for (int i = 0; i < numColors; i++) { uint8_t *const c = (uint8_t *)&colors[ i ]; uint8_t h = c[ 0 ]; c[ 0 ] = c[ 2 ]; c[ 2 ] = h; } return execFuncSetColor(window, index, numColors, (uint32_t *)colors); } eDvbColor cXineLib::filterAlpha(eDvbColor color) { if (!m_settings.SupportTransparency()) { uint8_t *const c = (uint8_t *)&color; if (c[ 3 ]) c[ 3 ] = 0xff; } return color; } #else bool cXineLib::execFuncSetColor(int window, int index, tColor color) { return execFuncSetColor(window, index, &color, 1); } bool cXineLib::execFuncSetColor(int window, int index, tColor *const colors, int numColors) { return execFuncSetColor(window, index, numColors, (uint32_t *)colors); } tColor cXineLib::filterAlpha(tColor color) { // fprintf(stderr, "color: 0x%08x", color); if (!m_settings.SupportTransparency()) { uint8_t *const c = (uint8_t *)&color; if (c[ 3 ]) c[ 3 ] = 0xff; } // fprintf(stderr, ", 0x%08x\n", color); return color; } #endif bool cXineLib::execFuncSetColor(int window, int index, int numColors, uint32_t *const colors) { assert(0 <= index && index < 256); assert(0 < numColors && numColors <= 256); assert((index + numColors) <= 256); m_osdFlushRequired = true; if (!isConnected()) return false; DLOG("cXineLib::execFuncSetColor"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; const int sizeofColors = sizeof (*colors) * numColors; data_set_color_t data; data.header.func = func_set_color; data.header.len = sizeof (data) + sizeofColors; data.window = window; data.index = index; data.num = numColors - 1; if (sizeof (data) != xwrite(fd_control, &data, sizeof (data))) return false; if (sizeofColors != xwrite(fd_control, colors, sizeofColors)) return false; return true; } bool cXineLib::execFuncOsdDrawBitmap(int window, const uint8_t *const pData, const int sizeOfPixel, int x, int y, int width, int height, int stride, const tColor *const colors) { m_osdFlushRequired = true; if (!isConnected()) return false; DLOG("cXineLib::execFuncOsdDrawBitmap"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; int bitmapLen = width * height * (((m_settings.OsdMode() != cXineSettings::osdOverlay) && m_capabilities.osd_supports_argb_layer) ? 4 : 1); data_osd_draw_bitmap_t data; data.header.func = func_osd_draw_bitmap; data.header.len = sizeof (data) + bitmapLen; data.window = window; data.x = x; data.y = y; data.width = width; data.height = height; data.argb = (m_settings.OsdMode() != cXineSettings::osdOverlay) && m_capabilities.osd_supports_argb_layer; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { bool failed = false; const uint8_t *row = pData; assert(width <= 4096); uint32_t data[4096]; for (int y = 0; !failed && y < height; y++) { if ((m_settings.OsdMode() != cXineSettings::osdOverlay) && m_capabilities.osd_supports_argb_layer) { uint32_t *pData = &data[0]; if (4 == sizeOfPixel) pData = (uint32_t *)row; else if (!colors) { if (y == 0) memset(data, 0, sizeof (data)); } else { for (int i = 0; i < width; i++) data[i] = filterAlpha(colors[row[i]]); } if (4 * width != xwrite(fd_control, pData, 4 * width)) { failed = true; break; } } else if (1 == sizeOfPixel) { if (width != xwrite(fd_control, row, width)) { failed = true; break; } } else { if (y == 0) memset(data, 0, sizeof (data)); if (width != xwrite(fd_control, data, width)) { failed = true; break; } } row += stride * sizeOfPixel; } if (!failed) return true; } // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncOsdSetPosition(int window, int x, int y) { m_osdFlushRequired = true; if (!isConnected()) return false; DLOG("cXineLib::execFuncOsdSetPosition"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_osd_set_position_t data; data.header.func = func_osd_set_position; data.header.len = sizeof (data); data.window = window; data.x = x; data.y = y; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncOsdShow(int window) { m_osdFlushRequired = true; if (!isConnected()) return false; DLOG("cXineLib::execFuncOsdShow"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_osd_show_t data; data.header.func = func_osd_show; data.header.len = sizeof (data); data.window = window; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncOsdHide(int window) { m_osdFlushRequired = true; if (!isConnected()) return false; DLOG("cXineLib::execFuncOsdHide"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_osd_hide_t data; data.header.func = func_osd_hide; data.header.len = sizeof (data); data.window = window; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncOsdFlush() { if (osdUpdateLocked("execFuncOsdFlush")) return true; if (!m_osdFlushRequired) return true; m_osdFlushRequired = false; if (!isConnected()) return false; DLOG("cXineLib::execFuncOsdFlush"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_osd_flush_t data; data.header.func = func_osd_flush; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; return false; } bool cXineLib::execFuncOsdFree(int window) { m_osdFlushRequired = true; if (!isConnected()) return false; DLOG("cXineLib::execFuncOsdFree"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_osd_free_t data; data.header.func = func_osd_free; data.header.len = sizeof (data); data.window = window; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::pushOut(const uchar id /* = 0xff */) { if (!isConnected()) return false; cMutexLock dataLock(&m_dataMutex); if (!isConnected()) return false; static uchar dataPadding[ 6 + 0xffff ] = { 0x00, 0x00, 0x01, 0xbe, 0xff, 0xff, 0x00 }; dataPadding[ 5 ] = id; int l = 6 + 0xff00 + id; if (l == xwrite(fd_fifo0, &dataPadding, l)) return true; return false; } bool cXineLib::execFuncClear(int n) { if (!isConnected()) return false; DLOG("cXineLib::execFuncClear"); cMutexLock dataLock(&m_dataMutex); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; static uchar id = 0x00; if (id == 0xff) id += 2; else id++; data_clear_t data; data.header.func = func_clear; data.header.len = sizeof (data); data.n = n; data.s = 0; data.i = id; // first clear will discard xine's buffers to make some available for pushOut() if (sizeof (data) != xwrite(fd_control, &data, sizeof (data))) return false; if (!execFuncWait()) return false; if (!pushOut(id)) return false; data.s = 1; // second clear will discard xine's buffers which contain data from pushOut() if (sizeof (data) != xwrite(fd_control, &data, sizeof (data))) return false; return true; } bool cXineLib::execFuncFirstFrame() { if (!isConnected()) return false; DLOG("cXineLib::execFuncFirstFrame"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_first_frame_t data; data.header.func = func_first_frame; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncStillFrame() { if (!isConnected()) return false; DLOG("cXineLib::execFuncStillFrame"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_still_frame_t data; data.header.func = func_still_frame; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncResetAudio() { if (!isConnected()) return false; DLOG("cXineLib::execFuncResetAudio"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_reset_audio_t data; data.header.func = func_reset_audio; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; return false; } bool cXineLib::execFuncFlush(int TimeoutMs /* = -1 */, const bool justWait /* = false */) { if (!isConnected()) return true; DLOG("cXineLib::execFuncFlush"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return true; // if (!checkConnect()) // return false; data_flush_t data; data.header.func = func_flush; data.header.len = sizeof (data); data.ms_timeout = TimeoutMs; data.just_wait = justWait; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { result_union_t resultUnion; off_t n = xread(fd_result, (char *)&resultUnion, sizeof (resultUnion.header)); if (n != sizeof (resultUnion.header)) return true; if (data.header.func != resultUnion.header.func) return true; result_flush_t *result = &resultUnion.flush; n = xread(fd_result, (char *)result + sizeof (result->header), sizeof (*result) - sizeof (result->header)); if (n != sizeof (*result) - sizeof (result->header)) return true; return !result->timed_out; } return true; } bool cXineLib::execFuncMute(bool mute /* = true */) { m_muted = mute; return true; if (!isConnected()) return false; DLOG("cXineLib::execFuncMute"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_mute_t data; data.header.func = func_mute; data.header.len = sizeof (data); data.mute = mute; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncSetVolume(int volume) { m_volume = volume; if (!isConnected()) return false; DLOG("cXineLib::execFuncSetVolume"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_set_volume_t data; data.header.func = func_set_volume; data.header.len = sizeof (data); data.volume = volume * 100 / 0xff; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncSelectAudio(int audioChannel) { m_audioChannel = audioChannel; if (!isConnected()) return false; DLOG("cXineLib::execFuncSelectAudio"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_select_audio_t data; data.header.func = func_select_audio; data.header.len = sizeof (data); data.channels = audioChannel; switch (audioChannel) { case -1: data.channels = 0; // don't touch audio data break; case 0: data.channels = 3; // left + right channel break; } if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncSetSpeed(double speed) { if (!isConnected()) return false; DLOG("cXineLib::execFuncSetSpeed"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_set_speed_t data; data.header.func = func_set_speed; data.header.len = sizeof (data); data.speed = (int)(XINE_FINE_SPEED_NORMAL * speed / 100.0 + 0.5); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; return false; } bool cXineLib::execFuncTrickSpeedMode(bool on) { m_trickSpeedMode = on; if (!isConnected()) return false; DLOG("cXineLib::execFuncTrickSpeedMode"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_trick_speed_mode_t data; data.header.func = func_trick_speed_mode; data.header.len = sizeof (data); data.on = on; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; return false; } /* bool cXineLib::execFuncDelay(int usdelay) { cMutexLock ioLock(&m_ioMutex); if (!checkConnect()) return false; dataDelay_t dataDelay; dataDelay.header.func = funcDelay; dataDelay.header.len = sizeof (dataDelay); dataDelay.usdelay = usdelay; if (sizeof (dataDelay) == xwrite(fd_control, &dataDelay, sizeof (dataDelay))) return true; ::close(fd_control); ::close(fd_fifo0); fd_fifo0 = -1; return false; } */ bool cXineLib::execFuncMetronom(int64_t pts, uint32_t flags /* = 0 */) { if (!isConnected()) return false; DLOG("cXineLib::execFuncMetronom"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_metronom_t data; data.header.func = func_metronom; data.header.len = sizeof (data); data.pts = pts; data.flags = flags; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } bool cXineLib::execFuncWait(const bool id /* = false */) { if (!isConnected()) return false; DLOG("cXineLib::execFuncWait"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_wait_t data; data.header.func = func_wait; data.header.len = sizeof (data); data.id = id; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { result_union_t resultUnion; off_t n = xread(fd_result, (char *)&resultUnion, sizeof (resultUnion.header)); if (n != sizeof (resultUnion.header)) return false; if (data.header.func != resultUnion.header.func) return false; result_wait_t *result = &resultUnion.wait; n = xread(fd_result, (char *)result + sizeof (result->header), sizeof (*result) - sizeof (result->header)); if (n != sizeof (*result) - sizeof (result->header)) return false; return true; } return false; } bool cXineLib::execFuncQueryCapabilities(result_query_capabilities_t &capabilities) { if (!isConnected()) return false; DLOG("cXineLib::execFuncQueryCapabilities"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_query_capabilities_t data; data.header.func = func_query_capabilities; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { result_union_t resultUnion; off_t n = xread(fd_result, (char *)&resultUnion, sizeof (resultUnion.header)); if (n != sizeof (resultUnion.header)) return false; if (data.header.func != resultUnion.header.func) return false; result_query_capabilities_t *result = &resultUnion.query_capabilities; n = xread(fd_result, (char *)result + sizeof (result->header), sizeof (*result) - sizeof (result->header)); if (n != sizeof (*result) - sizeof (result->header)) return false; memcpy(&capabilities, result, sizeof (capabilities)); return true; } return false; } bool cXineLib::execFuncSetup() { if (!isConnected()) return false; DLOG("cXineLib::execFuncSetup"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; // if (!checkConnect()) // return false; data_setup_t data; data.header.func = func_setup; data.header.len = sizeof (data); data.osd_unscaled_blending = (m_settings.OsdMode() == cXineSettings::osdOverlay); switch (m_settings.VolumeMode()) { case cXineSettings::volumeIgnore: data.volume_mode = XINE_VDR_VOLUME_IGNORE; break; case cXineSettings::volumeChangeHW: data.volume_mode = XINE_VDR_VOLUME_CHANGE_HW; break; case cXineSettings::volumeChangeSW: data.volume_mode = XINE_VDR_VOLUME_CHANGE_SW; break; default: assert(false); } switch (m_settings.MuteMode()) { case cXineSettings::muteIgnore: data.mute_mode = XINE_VDR_MUTE_IGNORE; break; case cXineSettings::muteExecute: data.mute_mode = XINE_VDR_MUTE_EXECUTE; break; case cXineSettings::muteSimulate: data.mute_mode = XINE_VDR_MUTE_SIMULATE; break; default: assert(false); } data.image4_3_zoom_x = m_settings.ZoomParams(cXineSettings::image4_3).GetZoomX(); data.image4_3_zoom_y = m_settings.ZoomParams(cXineSettings::image4_3).GetZoomY(); data.image16_9_zoom_x = m_settings.ZoomParams(cXineSettings::image16_9).GetZoomX(); data.image16_9_zoom_y = m_settings.ZoomParams(cXineSettings::image16_9).GetZoomY(); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; // ::close(fd_control); // ::close(fd_fifo0); // fd_fifo0 = -1; return false; } void cXineLib::freeze(bool /* doFreeze */ /* = true */) { // m_frozen = doFreeze; } void cXineLib::ignore(bool /* doIgnore */ /* = true */) { // m_ignore = doIgnore; } void cXineLib::pause(bool doPause /* = true */) { cMutexLock dataLock(&m_dataMutex); internalPaused(doPause); } #ifndef Y4MSCALER #define Y4MSCALER "y4mscaler" #endif #ifndef Y4MTOPPM #define Y4MTOPPM "y4mtoppm" #endif #ifndef PNMTOJPEG #define PNMTOJPEG "pnmtojpeg" #endif uchar *cXineLib::execFuncGrabImage(const char *FileName, int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) { if (!isConnected()) return NULL; DLOG("cXineLib::execFuncGrabImage"); cMutexLock ioLock(&m_ioMutex); int videoX = -1; int videoY = -1; int videoW = -1; int videoH = -1; int zoomX = 100; int zoomY = 100; execFuncVideoSize(videoX, videoY, videoW, videoH, zoomX, zoomY); if (!isConnected()) return NULL; data_grab_image_t data; data.header.func = func_grab_image; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { result_union_t resultUnion; off_t n = xread(fd_result, (char *)&resultUnion, sizeof (resultUnion.header)); if (n != sizeof (resultUnion.header)) return NULL; if (data.header.func != resultUnion.header.func) return NULL; result_grab_image_t *result = &resultUnion.grab_image; n = xread(fd_result, (char *)result + sizeof (result->header), sizeof (*result) - sizeof (result->header)); if (n != sizeof (*result) - sizeof (result->header)) return NULL; const size_t frameSize = result->header.len - sizeof (*result); // ::fprintf(stderr, "frameSize: %d\n", frameSize); if (frameSize <= 0) return NULL; uint8_t *img = (uint8_t *)::malloc(frameSize); if (!img) return NULL; if (frameSize != (size_t)xread(fd_result, img, frameSize)) { ::free(img); return NULL; } if (XINE_IMGFMT_YUY2 == result->format) { uint8_t *img2 = (uint8_t *)::malloc(frameSize); if (!img2) { ::free(img); return NULL; } ::memset(img2, 0x80, frameSize); uint8_t *src = img; uint8_t *dstY = img2; uint8_t *dstU = dstY + result->height * result->width; uint8_t *dstV = dstU + result->height * ((result->width + 1) / 2); for (int y = 0; y < result->height; y++) { for (int x = 0; x < (result->width + 1) / 2; x++) { *dstY++ = *src++; *dstU++ = *src++; *dstY++ = *src++; *dstV++ = *src++; } } ::free(img); img = img2; } FILE *tmpFile = 0; int outfd = -1; if (FileName) { outfd = ::open(FileName, O_CREAT /* | O_EXCL */ | O_TRUNC | O_RDWR, 0644); } else { tmpFile = ::tmpfile(); if (tmpFile) outfd = fileno(tmpFile); } if (-1 == outfd) { if (tmpFile) ::fclose(tmpFile); // removes file, too } else { /* if (-1 == videoX || -1 == videoY || videoW <= 0 || videoH <= 0) { videoX = 0; videoY = 0; videoW = result->width; videoH = result->height; } */ videoX = result->crop_left; videoY = result->crop_top; videoW = result->width - result->crop_left - result->crop_right; videoH = result->height - result->crop_top - result->crop_bottom; int iRn; int iRd; if (videoW > videoH) { iRn = videoH; iRd = (videoW * 20000 + result->ratio) / (2 * result->ratio); } else { iRn = (videoH * 2 * result->ratio + 10000) / 20000; iRd = videoW; } int oRn = SizeY * (m_noSignalStream16x9 ? 16 : 4); int oRd = SizeX * (m_noSignalStream16x9 ? 9 : 3); const char *const chromass = (XINE_IMGFMT_YV12 == result->format) ? "420_MPEG2" : "422"; char *cmd = 0; if (Jpeg) { ::asprintf(&cmd, Y4MSCALER " -I chromass=%s -I active=%dx%d+%d+%d -O chromass=444 -O size=%dx%d -O sar=%d:%d " "| " Y4MTOPPM " -L " "| " PNMTOJPEG " -quality=%d " ">&%d" , chromass, videoW, videoH, videoX, videoY, SizeX, SizeY, oRn, oRd , Quality , outfd); } else { ::asprintf(&cmd, Y4MSCALER " -I chromass=%s -I active=%dx%d+%d+%d -O chromass=444 -O size=%dx%d -O sar=%d:%d " "| " Y4MTOPPM " -L " ">&%d" , chromass, videoW, videoH, videoX, videoY, SizeX, SizeY, oRn, oRd , outfd); } // ::fprintf(stderr, "ratio: %d\n", result->ratio); xfprintf(stderr, "cmd: %s\n", cmd); FILE *f = ::popen(cmd, "w"); if (f) { ::fprintf(f, "YUV4MPEG2 W%d H%d F%d:%d I%c A%d:%d\nFRAME\n" , result->width, result->height , 25, 1 , "ptb"[result->interlaced] , iRn, iRd); if (frameSize == ::fwrite(img, 1, frameSize, f)) { ::pclose(f); // close the pipe here ::free(img); img = NULL; if (tmpFile) // grab the image in one go { Size = (int) lseek (outfd, 0, SEEK_END); lseek (outfd, 0, SEEK_SET); if (Size != -1) { img = (uint8_t *)::malloc(Size); if (img && Size != ::read(outfd, img, Size)) { ::free(img); img = NULL; } } } else // file is persistent so don't care about it's content { Size = -1; img = (uint8_t *)-1; } } else { ::pclose(f); ::free(img); img = NULL; } } else { ::free(img); img = NULL; } ::free(cmd); if (tmpFile) ::fclose(tmpFile); // removes file, too else ::close(outfd); return (uchar *)img; } ::free(img); } return NULL; } bool cXineLib::execFuncGetVersion(int32_t &version) { if (!isConnected()) return false; DLOG("cXineLib::execFuncGetVersion"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_get_version_t data; data.header.func = func_get_version; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { result_union_t resultUnion; off_t n = xread(fd_result, (char *)&resultUnion, sizeof (resultUnion.header)); if (n != sizeof (resultUnion.header)) return false; if (data.header.func != resultUnion.header.func) return false; result_get_version_t *result = &resultUnion.get_version; n = xread(fd_result, (char *)result + sizeof (result->header), sizeof (*result) - sizeof (result->header)); if (n != sizeof (*result) - sizeof (result->header)) return false; version = result->version; return true; } return false; } bool cXineLib::execFuncGetPTS(int64_t &pts, const int TimeoutMs /* = 0 */, bool *const pQueued /* = 0 */) { if (!isConnected()) return false; DLOG("cXineLib::execFuncGetPTS"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_get_pts_t data; data.header.func = func_get_pts; data.header.len = sizeof (data); data.ms_timeout = TimeoutMs; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { result_union_t resultUnion; off_t n = xread(fd_result, (char *)&resultUnion, sizeof (resultUnion.header)); if (n != sizeof (resultUnion.header)) return false; if (data.header.func != resultUnion.header.func) return false; result_get_pts_t *result = &resultUnion.get_pts; n = xread(fd_result, (char *)result + sizeof (result->header), sizeof (*result) - sizeof (result->header)); if (n != sizeof (*result) - sizeof (result->header)) return false; pts = result->pts; if (pQueued) *pQueued = result->queued; return true; } return false; } bool cXineLib::execFuncVideoSize(int &videoLeft, int &videoTop, int &videoWidth, int &videoHeight, int &videoZoomX, int &videoZoomY, double *const pVideoAspect /* = 0 */) { if (!isConnected()) return false; DLOG("cXineLib::execFuncVideoSize"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_video_size_t data; data.header.func = func_video_size; data.header.len = sizeof (data); if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) { result_union_t resultUnion; off_t n = xread(fd_result, (char *)&resultUnion, sizeof (resultUnion.header)); if (n != sizeof (resultUnion.header)) return false; if (data.header.func != resultUnion.header.func) return false; result_video_size_t *result = &resultUnion.video_size; n = xread(fd_result, (char *)result + sizeof (result->header), sizeof (*result) - sizeof (result->header)); if (n != sizeof (*result) - sizeof (result->header)) return false; videoLeft = result->left; videoTop = result->top; videoWidth = result->width; videoHeight = result->height; videoZoomX = result->zoom_x; videoZoomY = result->zoom_y; if (pVideoAspect) *pVideoAspect = result->ratio / 10000.0; return true; } return false; } bool cXineLib::execFuncSetVideoWindow(int x, int y, int w, int h, int wRef, int hRef) { if (!isConnected()) return false; DLOG("cXineLib::execFuncSetVideoWindow"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; data_set_video_window_t data; data.header.func = func_set_video_window; data.header.len = sizeof (data); data.x = x; data.y = y; data.w = w; data.h = h; data.w_ref = wRef; data.h_ref = hRef; if (sizeof (data) == xwrite(fd_control, &data, sizeof (data))) return true; return false; } void cXineLib::ReshowCurrentOSD(const int frameLeft, const int frameTop, const int frameWidth, const int frameHeight, const int frameZoomX, const int frameZoomY, const bool unlockOsdUpdate /* = false */) { if (m_eventSink) m_eventSink->ReshowCurrentOSD(frameLeft, frameTop, frameWidth, frameHeight, frameZoomX, frameZoomY, unlockOsdUpdate); } void cXineLib::LockOsdUpdate() { if (m_eventSink) m_eventSink->LockOsdUpdate(); } void cXineLib::DiscontinuityDetected() { if (m_eventSink) m_eventSink->DiscontinuityDetected(); } bool cXineLib::execFuncPlayExternal(const char *const fileName /* = 0 */) { if (!isConnected()) return false; DLOG("cXineLib::execFuncPlayExternal"); cMutexLock ioLock(&m_ioMutex); if (!isConnected()) return false; int dataSize = (fileName ? (1 + ::strlen(fileName)) : 0); data_play_external_t data; data.header.func = func_play_external; data.header.len = sizeof (data) + dataSize; if (sizeof (data) != xwrite(fd_control, &data, sizeof (data))) return false; if (dataSize != xwrite(fd_control, fileName, dataSize)) return false; return true; } void cXineLib::enableExternal(const bool enable /* = true */) { m_external.enable(enable); } void cXineLib::ExternalStreamFinished() { m_external.externalStreamFinished(); } }; xine-0.9.4/HISTORY0000644000000000000000000012606411540204421012253 0ustar rootrootVDR Plugin 'xine' Revision History ---------------------------------- 2011-03-16: Version 0.9.4 - The contained xine-lib-1.1 patch is outdated but might still work. The patch will get updated in 0.9.5. - A couple of xine-lib-1.2 patches is still waiting to get commited. vdr-xine-0.9.4 should be able to work without them. - Fixed a couple of things in xine-lib-1.2 and VDPAU. - Fixed compilation for VDR-1.2.x and VDR-1.6.x. - Updated Makefile to VDR-1.7.17. - Added support for VDR-1.7.17s TrueColor OSD. - Added support for VDR-1.7.12s changed PCR handling. - Added support for VDR-1.7.11s semantic of MakePrimaryDevice. - Resolved a segfault regarding VDR-1.7.x and cutting marks in radio recordings (thanks to Joachim Wilke for providing a fix). - Included Lithuanian translation (thanks to Valdemaras Pipiras). - Included Slovak translation (thanks to Milan Hrala). - Included Chinese and Taiwanese translations (thanks to NanFeng). - Fixed MANUAL about centre_cut_out_mode (thanks to Vorg on #xine-vdpau). 2009-06-17: Version 0.9.3 - Updated MANUAL accordingly (e. g. some information about xine engine buffer sizes). - Implemented VDR-1.7.8s cDevice::GetOsdSize() and changed cDevice::GetVideoSize() accordingly. - Implemented video window support while showing an OSD for VDPAU to support plugins like yaepghd. - Clipped settings in setup page to reasonable values while editing to prevent rendering an unreadable OSD. - Introduced separate live TV buffer values for SD and HD video as well as audio to allow faster "zapping" on SD channels while still having large buffers for HD video. - Fixed setup page display while changing OSD extent. - Fixed a memory leak in processing TS still images. - Fixed a bug in processing TS still images which caused the tail of the image to get lost and hence the image not to get shown. - Updated it_IT.po (thanks to Diego Pierotto for providing the translations). 2009-05-03: Version 0.9.2 - Updated MANUAL accordingly. - Added setup options to define OSD extent. - Implemented VDR-1.7.7s cDevice::GetVideoSize() to return an OSDs maximum extent. 2009-04-12: Version 0.9.1 - Adapted PlayTs() to semantic of VDR-1.7.5. - Fixed compilation for VDR 1.2.x, 1.4.x, 1.6.x and 1.7.x. - Added support for tIndex being uint16_t (not tested yet). - Changed trick speed modes to match GetSTC(). - Fixed buffer calculation after discontinuity which caused endless slow motion. - Changed GetSTC() to match VDR 1.7.5's requirements. - Implemented TS support to StillPicture(). - Fixed crash due to calling PlayTs() from different threads. - Fixed crashes due to negative OSD coordinates caused by some skins. - Fixed INSTALL regarding xine-ui cvs repository and how to apply the patches as they have been created differently since release 0.9.0. 2009-01-12: Version 0.9.0 - Updated INSTALL and MANUAL accordingly. - Fixed several multithreading issues which got triggered on SMP systems (thanks to Edgar (gimli) Hucek for reporting issues and testing fixes). - Added support for VDR-1.7.3 besides TS still pictures. - Pulled patched VDR-1.7.2's remux.[ch] for a quick adaption to to VDR-1.7.3 (thanks to Klaus Schmidinger for his approval). - Fixed PTS handling in xine-lib's FFmpeg decoder for recent FFmpeg versions which should provide better A/V sync now. - Added patch sets to input_vdr.c which allow to omit parts of the MRL, e. g. vdr:// should be sufficient now (thanks to Ludwig Nussel for providing them). - Make use of new OSD functionality in xine-lib-1.2. For now an OSD implementation must provide both new features to have vdr-xine make use of it. - Extended OSD support in xine-lib-1.2 to allow truecolor OSDs as well as hardware scaled OSDs. The new functionality is currently only provided by VDPAU enabled installations. - Added frame grabbing support for accelerated frame formats to xine-lib. Previously, xine-lib exitted the app when frame grabbing was requested for accelerated frame formats like xxmc. You'll now get a green image in that case. Reading back the content of an accelerated frame is currently only provided by VDPAU enabled installations. - Hacked support for VDR-1.7.1's new cDevice::PlayTs() method. - Grab image nolonger needs pnmcut to select the relevant area of the TV image. Besides that, grab image respects the aspect ratio you select in VDR's setup menu, i. e. a 16:9 setup and a 16:9 broadcast will grab you a image without black borders. A mismatch of aspect ratios will add black borders to the image to match the visual impression you get on your TV set. Additionally, the grabbing of field pictures was corrected with regard to color upsampling. - A script mkNoSignal.sh is provided to create custom logos. Just provide PNG files named noSignal16x9*.png (and ...4x3... respectively) and have the tools mentioned in INSTALL in your path. You'll have to move your custom logos then over the default files. - The vdr-xine logo is now available in two aspect ratios (4:3 and 16:9) as more channels provide 16:9 content. Then vdr-xine selects the logo which matches VDR's DVB setup option video format. - Added the ability to detect discontinuities in live TV and to start a new buffering period in such a case. This will help to resume to fluent playback from bad weather conditions or when disconnecting and reattaching the antenna cable. - Updated it_IT.po (thanks to Diego Pierotto for providing the translations). 2008-03-10: Version 0.8.2 - Fixed optional parameter processing for parameter -p. As getopt() requires that optional arguments must follow the option switch without any delimiters, I've updated MANUAL accordingly, i. e. all options are documented without delimiters. Furthermore you'll get an error message when specifying the argument for -p incorrectly. - Fixed compilation of patched xine-lib-1.1 (thanks to Darren Salt and Fabian Foerg for contributing fixes for this issue). - Provided xine_get_current_frame_alloc() to xine-lib which allows retrieving the current frame image without the need to pause the stream for a second call to the function after allocating sufficient memory. - Fixed retrieving only valid PTS from xine. xine's metronom is now intercepted to monitor PTS changes after which PTS are considered to be valid. - Fixed handling VDR's clear command. Adding sync points to the stream makes sure that all data on the way to xine up to sync point gets properly dropped. - Added Russian translation (thanks to Oleg Roitburd for providing ru_RU.po). 2008-01-01: Version 0.8.1 - Updated documentation mentioning binary and devel packages for xine-lib-1.2, so compiling xine can be ommited for some distributions. - Updated INSTALL regarding XINE VDR VERSION MISMATCH. - The xine-lib.patch for xine-lib-1.1.x nolonger contains any FFmpeg fixes for H.264 decoding. It is recommended to install a recent FFmpeg and to add --with-external-ffmpeg when configuring xine-lib. - Contributed some xine-lib options for H.264 decoding. - Adopted name and files changes of vdr plugin in xine. - Updated it_IT.po and xineI18n.h (thanks to Diego Pierotto for providing the translations). - Implemented makefile switch VDR_XINE_VERIFY_BITMAP_DIRTY to turn off dirty bitmap verification and hence safing calls to memcpy() and memcmp() for OSD operations. - Removed TCP_NODELAY for the video stream socket to reduce network load. - Fixed OSD scaling for HD subtitles. The scaler nolonger allocates a fixed size buffer for a 720x576 OSD but guesses typical maximum OSD sizes, i. e. 720x576, 1280x720, 1920x1080. 2007-10-24: Version 0.8.0 - Frame duration was not set correctly in xine-lib for H.264 frames, causing frame drops an non fluent playback. - Added network patch (thanks to Tobias Grimm for supplying it). Although I had planned to optimize it to just use one port and only two sockets, I decided to release it in its current state instead of waiting further years. I've just modified it to use a more xine-like MRL netvdr://host:port#demux:mpeg_pes where port is optional (defaults to 18701). The fifo MRL should now be written vdr://tmp/vdr-xine/stream#demux:mpeg_pes, although a single slash will still work, too. - Updated et_EE.po (thanks to Arthur Konovalov for supplying the file). - Fixed a bug in vdr-xine's buffer handling for live TV mode which got most noticeable when switching back from a H.264 channel to a MPEG2 channel, especially when the machine was not capable of playing the H.264 stream in real time. As the buffer was not cleared when switching channels, old H.264 data was sent to xine instead of new MPEG2 data, which caused xine to incorrectly switch to H.264 mode. This behavior could lead to a crash in FFmpeg later. - Fixed OSD handling in vdr-xine as certain subtitles could cause a memory corruption in xine (thanks to Arthur Konovalov for providing sample recordings). 2007-10-17: Version 0.7.12 - Updated INSTALL and MANUAL accordingly. - Added support for VDR-1.5.10's new key kSubtitles and fixed a deadlock due to setting audio volume when changing the primary device. - Added a new configuration option "Connection interacts with EIT scanner" as requested by Boguslaw Juza (see MANUAL for details). - Added some patches to xine-lib regarding H.264 support, as FFmpeg's decoder improved very much during the past weeks. It supports now multithreaded decoding (you need to enable at least 2 threads in xine-ui to make use of it) and almost complete PAFF support which is used for interlaced images. These new features are only available, when xine-lib is configured to use an external FFmpeg installation. - Added support for VDR-1.5.9's OSD levels. - Fixed compiler warnings when compiled with gcc-4.2.x (thanks to Joerg Bornkessel for suppling the patch). - Exchanged noSignal.pes (thanks to Markus Nissl for supplying the new image). 2007-08-30: Version 0.7.11 - Added preliminary support for OSD changes in VDR-1.5.9. - Added preliminary support for H.264 streams. xine uses FFmpeg for H.264 decoding and as the decoder isn't rock stable at the moment and doesn't support all H.264 features like interlacing, it is likely that some frames do not get decoded or moreover that xine crashes. - Removed some unnecessary calls to clear xine's buffer when switching between pause and play. - Spent a lot of time fixing some issues in xine-lib. Integrated a big bunch of my xine-lib patch into xine-lib-1.1.8. All patches are now part of xine-lib-1.2. - Added the opportunity to change xine's volume in software, like when changing xine's config option gui.audio_mixer_method to Software (thanks to Halim Sahin for the request). - Added the possibility to configure different zoom settings for 4:3 and 16:9 images, which are automatically activated when xine signals a format change (thanks to Detlef Nebermann for the request). 2006-12-10: Version 0.7.10 - Updated xine patches to current CVS (should be compatible with xine release 1.1.3). - Fixed a segfault in SHQ OSD scaling code due to uninitialized variables (thanks to Gregorie Favre for reporting this issue and testing the fixes). - Added noSignal-smallTextAtBottom.mpg to directory data (thanks to John for supplying the file). - Fixed a segfault in xine when there is no demuxer available (e. g. the SuSE 10.1 xine-lib binary lacks support for MPEG2). - Fixed compiler warnings on x86_64 architectures (thanks to Ville Skyttä for compilation assistance). - Added support to xine for the 3 new keys of VDR-1.3.47. - Updated MANUAL accordingly. 2006-04-17: Version 0.7.9 - Updated Finnish translation (thanks to Rolf Ahrenberg for supplying the patch). - Adapted vdr-xine to VDR-1.3.45. - Adapted vdr-xine to VDR-1.3.47. 2006-03-20: Version 0.7.8 - Fixed HISTORY as it mentioned future releases of VDR (thanks to Klaus Schmidinger for reporing this issue). - Fixed sending video data: the first packet after a Clear() got dropped which made editing recordings impossible (thanks to Gregoire Favre for reporting this issue). - Fixed implementation of func_wait as it didn't synchronize VDR and xine as expected. - Adapted vdr-xine to VDR-1.3.44 cOsd::DrawBitmap() (thanks to C.Y.M. for reporting this issue). - Replaced 'prebuffer' by 'buffer' in vdr-xine's setup menu. - Added new setup parameter "buffer hysteresis" which is added to "Live-TV buffer" for determining the initial buffer size which must be achieved before replaying switches to normal speed. It is recommended to reduce your current buffer setting by the value for hysteresis. - Added PTS prediction to stop prebuffer phase much earlier on channels which rarely add PTS to their stream's PES packets. - Changed buffering to monitor the buffer size during a configureable time after the configured buffer size (Live-TV buffer + hysteresis) was initially established. Whenever the buffer size drops below the configured value (of Live- TV buffer frames), hysteresis is increased by an additional frame and the new buffer is established. - The above changes to buffering allow minimizing the buffer for faster zapping on regular channels while not loosing the ability to watch some special (mosaic) channels which required larger buffer settings. - Implemented continuous monitoring to reestablish the buffer once it drops below the configured value. This allows watching the HDTV demo loop on ASTRA HD forever. This feature can be enabled on demand in vdr-xine's setup menu, as it might be a performance issue on less powerful machines. - As VDR-1.2.6 lacks c*Repackers, the above changes to buffering do not work that precisely with this old VDR version. Just increase the buffer values a bit as necessary. - Fixed an out of range access in OSD scaling code by clipping away negative coordinate areas. The segfault was triggered by opening the audio track menu on channels with at least 20 tracks while VDRs default skins where selected. - Patched xine to speed up OSD operation when switching audio tracks in live TV mode or when showing still frames (e. g. when editing cutting marks). This change most likely breaks binary compatibilty to other xine plugins. - Failed to speed up OSD operation in trick speed modes: it would require to copy each frame at least while OSD is active, which might be a performance issue. So for now, the worst timing for a single OSD operation is about 2500 ms in VDR's first slow reverse mode. A possible solution might be X11 OSD overlay, as it is independent of the video frame, so there is no copying involved. But that's still to investigate. - Implemented OSD scaling in regard to xine's video zoom factor. The OSD will now be scaled accordingly for zoom factors > 100 %. As determining the zoom factors is an "expensive" operation in xine, they are only retrieved when some drawing to the OSD is happening. So you won't see an open OSD to immediately change it's size. - Fixed a segfault in SHQ OSD scaling code by setting the allocated memory to 0. - Updated INSTALL to mention a workaround for xine CVS access, as cvs.sourceforge.net was quite unreliable recently. - Fixed compilation for VDR version 1.2.6 which was only possible by dropping auto primary device feature for VDR versions < 1.3.32. - Updated MANUAL accordingly. 2006-02-14: Version 0.7.7 - Updated MANUAL (thanks to Ville Skyttä for supplying the patch). - Added Dutch translation (thanks to Maarten Wisse for supplying the patch). - Fixed auto primary device functionality concerning ActualDevice (thanks to Luca Olivetti for reporting this issue). - Shutting down cOsdProvider when vdr-xine is nolonger primary device to fix an issue where the new primary device doesn't register it's own cOsdProvider instance. - Fixed cXineDevice::SetDigitalAudioDevice() for radio channels with multiple audio tracks (e. g. RADIO INT2). These tracks are not related to each other and therefore syncing these tracks by PTS is impossible and causes huge delays otherwise. - Adopted cXineDevice::GrabImage() to VDR-1.3.38 (based on a patch of Darren Salt). - Fixed some issues with FIFO_DIR containing spaces (thanks to Darren Salt for supplying the patch). - Fixed some warnings about strict-aliasing issues when compiling with gcc-4.1 (thanks to Ville Skyttä for reporting this issue). - Added two new command line arguments (-X / -Y) to change the default image size for GrabImage(). Internally, they are predefined as -X 720 and -Y 576. - Dropped the Audio-Mode setup option for VDR >= 1.3.18 as VDR's audio menu and PlayPes() took over sending just a single audio stream to the output device. - Fixed implementation of cXineDevice::Clear(), which speeds up jumping back and forth in recordings. A stresstest showed that from time to time a deadlock happened in libasound (ALSA) when xine is opening the audio device. You may experience a problem too if you stay on the GREEN button at the beginning of a recording. On my system it caused segfaults, asserts and deadlocks while xine is opening the ALSA audio device. I'd be glad if someone could fix this issue. Anyway, I hope that this change partitially fixes the delay on some systems which happened when switching to trickspeed modes while replaying a recording. - Added support for VDR's new info key. - Tried to get xine's ffmpeg decoder to work with vdr-xine, but failed. - Had xine CVS take over a couple of my xine patches. - Tuned xine-lib's startcode scanner in libmpeg2. - Adapted vdr-xine to VDR-1.3.41 changed detection of transfer mode. - Adapted vdr-xine to VDR-1.3.42 changed cDevice::PlayAudio(). - Replaced noSignal*.pes with noSignal*.mpg. vdr-xine will complain about a missing noSignal.mpg. Just copy the new files from the source directory to the mentioned location as mentioned in INSTALL. - Changed buffering completely. When VDR switches to a channel, xine starts replaying at 12.5 % of normal speed. Then vdr-xine monitors the stream's PTS values and compares them to the value which xine reports. The difference between VDR's and xine's value makes up the currently gained buffer. When the buffer reaches the configured size then xine switches to 100 % speed. ATTENTION: this change requires you to reduce the configured prebuffer in vdr-xine's setup page to about 8 frames! - Updated MANUAL and INSTALL accordingly. 2005-09-11: Version 0.7.6 - Fixed vdr-xine to update xine's audio stream bitrate and decoder after xine has connected to vdr-xine. Before that, xine showed the information of the NOSIGNAL stream. - Several MPEG1 related fixes. - Several fixes concerning playing of externally generated VDR recordings (thanks to Fabian Wolter for supplying a recording). - Fixed compilation for VDR-1.3.32. - Playing a still image could make vdr-xine wait for further video packets (this issue showed up from time to time when using the vdr-radio plugin). 2005-08-14: Version 0.7.5 - Added documentation about gxine's VDR support to MANUAL (thanks to Darren Salt for supplying the patch). - Fixed xine-plugin to update audio stream bitrate and decoder. - Removed singleton enforcement of xine-plugin as it caused problems with xine's post plugin management (suggested by Darren Salt). - Added support to select left / right audio channel by audio post plugin 'vdr_audio'. - Added audio post plugin support to xine-ui and fbxine which was needed for the new audio post plugin vdr_audio. - Renamed video post plugin 'vdr' to 'vdr_video' to match naming scheme, but it also registered as 'vdr' to maintain compatibility. - Created audio post plugin 'upmix_mono' to turn a mono stream into a stereo one with identical data on both sides (requested by Lucian Muresan). - Fixed some endianness problems in OSD code (thanks to Carsten Rietzschel for reporting this issue). - Fixed some compiler warnings with gcc-4.0.1 and gcc-4.1 (thanks to Eric OUEDRAOGO for reporting this issue). - Updated plugin to match interface changes in VDR-1.3.27 (thanks to C.Y.M for reporting this issue). - Fixed implementation of cDevice::Clear() which didn't ensure that really all data (i. e. the data which is just in the FIFO or socket) get's cleared. This might improve switching to trickspeed mode. - Updated MANUAL about using vdr_audio post plugin. 2005-05-08: Version 0.7.4 - Fixed compilation issue with gcc-4.1 (thanks to ollo at vdr-portal for reporting this issue). - Fixed audio output when used with muggle (thanks to Der_Olli at vdr-portal for testing). - Fixed input_vdr for proper "C" coding (thanks to Philip Lawatsch for reporting this issue). - Increased xine-lib's demux_mpeg_pes.c's WRAP_THRESHOLD to 270000 to fix detection of false discontinuities (thanks to Jouni Karvo for reporting this issue). - Fixed xine-lib's xxmc decoder to properly decode radio-plugin's background images. - Updated dvbplayer patch for VDR-1.3.24. 2005-04-11: Version 0.7.3 - Started detection of AFD header in xine to automatically crop out the interesting part of the image later. - Adopted implementation of cXineDevice::SetDigitialAudioDevice() to different calling order in VDR 1.3.23. - Improved cXineDevice::SetDigitalAudioDevice() when replaying recordings. - Added setup option to automatically make vdr-xine the primary device while xine is connected to vdr-xine (requested by Der_Olli at vdr-portal). - Added setup option to consider all semi transparent colors as opaque to make the menu more readable. - Added commandline option '-s' to switch to skin 'curses' while xine is not connected to vdr-xine (requested by Rantanen Teemu). - Added commandline option '-q' to suppress debug messages (useful in combination with option '-s'). - Moved disconnect to cXineDevice::Stop() to get the new options to work. - Fixed all (?) deadlock situations in RPC command processing (e. g. stopping replay while switching a channel). - Fixed deadlocks in vdr-xine's xread(). A possible drawback is that now a disconnect might happen in such a case. - Fixed VDR's I-frame processing which caused disconnects e. g. while moving cut marks in HDTV recordings. vdr-1.2.23-dvbplayer3.patch is highly recommended for proper operation of vdr-xine. - Improved cXineDevice::StillImage() implementation to immediate display the frame (improves moving cut marks). - Fixed cXineDevice::StillImage() to work properly in combination with the plugin vdr-radio. BUG: xine's driver xxmc shows just a black screen on my EPIA MII-6000E. - Reintroduced usleeps() in input_vdr.c for flush, OSD flush and reset audio. sched_yield() simply caused to much CPU load while waiting about 40 ms to reach the expected state. Improves number of dropped frames when switching channels. - Optimized OSD display: VDR's channel display repeatedly sends a dirty OSD which doesn't differ from the previous one. Improves number of dropped frames while switching channel. BUG: it's still unclear whether this causes some OSD artefacts. - Fixed demux_mpeg_pes' discontinuity detection. Previously, when a PTS wrap happend, xine stopped replay for about 26.5 hours. - vdr-xine now nolonger set's xine's metronom directly but tells it's demuxer to do the job. Improves switching channels. - Optimized implementation of cXineDevice::Clear() in input_vdr.c. BUG: it may happen that xine's audio driver ALSA might get into a state of "silence" where it doesn't recover from until you stop replaying the recording. I still didn't find a way to reproduce this but it has to do with trickspeed, pause, play, and probably cut marks. - Fixed post_vdr.c to detect MRL changes for discovering streams sent from VDR, e. g. when xine is not started with the MRL specified on it's command line. BUG: It's possible that xine crashes due to this detection. xine doesn't allocate a different stream for a different MRL, but maybe other players do. I'm not sure whether I managed to increase the streams usage counter properly (by allocating an event queue) until I detect the new stream respectively MRL. BUG: post_vdr doesn't operate when xine's driver xxmc is used due to some limitation/incomplete implementation in xine's plugin interface. - Fixed xine's deinterlacer interface to take care of cropping. Previously the OSD was resizing like mad e. g. between 1920x1080 and 1920x1088. - Added support for VDR's new AUDIO key in xine (thanks to Darren Salt for reporting this issue). 2005-02-27: Version 0.7.2 - Fixed cXineDevice::StillPicture() to support three times larger still image data than before. This was necessary to properly support mp3 plugin's cover image feature (thanks to burki at vdr-portal for reporting this issue). - Removed cXineStatus as vdr-xine now uses a different method of detecting LiveTV. This one works better in combination with mp3 plugin (thanks to Klaus Schmidinger for suggesting the new solution). - Updated xine-lib patch for current CVS (thanks to Grégoire Favre for reporting this issue). 2005-02-13: Version 0.7.1 - Updated finish translation (thanks to Rolf Ahrenberg for supplying the update). - Fixed a typo in checking for VDR versions before 1.3.18. This broke successful compilation for versions between 1.3.8 and 1.3.18 (thanks to Andreas Urban for reporting this issue). - Fixed processing of cDevice::SetDigitalAudio() as it skipped displaying of NO SIGNAL when switching channels. 2005-01-29: Version 0.7.0 - Implemented centre-cut-out mode: this feature will crop away the black bars around a 4:3 image which is broadcast in a 16:9 stream and is displayed on a 4:3 monitor. See MANUAL how to enable this mode. - Fixed OSD scaling for the case where xine cropped the image for displaying. - Integrated patch for image grabbing. You can now specify the absolute path of the utilities for grabbing an image. Furthermore, the patch fixes a possible security issue when the filename for the GRAB command contained special shell characters (thanks to Darren Salt for supplying the patch). - Image grabbing now also requires pnmcut to support cropped images. - Prepended VDR_XINE_ to Makefile variables FIFO_DIR and SET_VIDEO_WINDOW to avoid collisions when vdr-xine's variables are set in VDR's Make.conf. - Added syslog messages when VDR_XINE_FIFO_DIR or any fifo cannot be created. - Added a further config option to vdr-xine's setup menu: you may now specify how VDR's mute requests will be processed in xine: a) ignore b) simulate (set volume to 0 / restore volume) c) execute - Fixed xine part to update UI when volume / muting is changed. - Updated INSTALL, MANUAL and xineI18n.h accordingly. 2005-01-23: Version 0.6.5 - Adapted vdr-xine to the recent changes in VDR-1.3.19. - With VDR-1.3.19, the HDTV patch for VDR is obsolete now. See the file vdr-patches-README.txt on my homepage for details. 2005-01-10: Version 0.6.4 - Adapted vdr-xine to the recent changes in VDR-1.3.18. The xine part has also changed and therefore a rebuild of xine is required. - With VDR-1.3.18 a lot of my patches for VDR are obsolete now. See the file vdr-patches-README.txt on my homepage for details. - Removed the necessity of changing POLLTIMEOUTS_BEFORE_DEVICECLEAR. - Updated INSTALL appropriately. 2004-12-31: Version 0.6.3 - Fixed uninitialized variable in xineRemote.c. Release 0.6.2 showed high CPU load of XineRemote control thread when switching between channels with different resolution. 2004-12-30: Version 0.6.2 - Integrated i18n updates (thanks to Rolf Ahrenberg for supplying the patch). - Fixed processing of MPEG1 streams. vdr-xine should now be able to work together with vdr-analogtv. - Fixed race-condition at shutdown when at client-disconnect vdr-xine accessed the already destructed xineRemote. - Fixed invalid pointer access in input_vdr.c (thanks to Darren Salt for supplying the patch). - Fixed some OSD update issues that happend when xine asked for an update and VDR was drawing on the OSD at the same time. - Prepared xine part for integration into xine-lib-1.0.x. - Synced SPU processing. DVD subtitles should now nolonger be several seconds ahead of their expected presentation time. - Updated MANUAL to reflect name changes in xine's config file. 2004-12-10: Version 0.6.1 - Added vdr-xine option "-i NUM" which can be used to get two or more vdr-xine instances running on the same machine (in different VDR instances). The same option is available as "--vdr-instance=NUM" to xineplayer but there it must be the first argument to not conflict with any other options from mplayer. - Fixed cXineDevice::StillPicture() to automatically add an PES video header if the given data doesn't contain one. As a result vdr-streamdev-* is now able to show an image while pausing Live-TV (thanks to Darren Salt for some hints on optimized coding). - Fixed some special cases of dolby audio processing introduced in 0.5.3 (thanks to Markus Maierhofer for reporting this issue). - Moved option OSD_SCALING_MODE and DONT_CHANGE_XINE_VOLUME from Makefile to vdr-xine's setup. Therefore the same binary package can now be run on less powerful machines too. - Integrated a large piece of xine-lib.patch into xine-lib-cvs. As a result the configure option --disable-exact-blending is gone and is now available as runtime configureable option "video.disable_exact_osd_alpha_blending". - Updated xineI18n.h. Feel free to supply updates for other languages too. - Updated INSTALL and MANUAL accordingly. - Introduced xineCommon.h to handle poisend VDR source. - Prepared xineI18n.h for additional languages. - Added a setup option for adjusting OSD gamma correction when OSD scaling is necessary. - Fixed xine part to no longer freeze when one connects to stale vdr-xine FIFOs. - Fixed xine part to immediately react on the "stop" button in case where VDR doesn't send a stream to xine. - The error message about missing noSignal.pes is now also logged via syslog as there was otherwise no indicater found in the logfile why VDR terminated that early. 2004-11-02: Version 0.6.0 - Implemented playback of external sources. This is done using the well known mplayer plugin in combination with my mplayer wrapper called "xineplayer". Just setup mplayer plugin as it's documentation states. Then edit it's "mplayer.sh" and replace "mplayer" by "xineplayer". xineplayer get's built into vdr-xine's source directory. You'll have to copy it to a "bin" directory which is part of your PATH or you might want to specifiy it's full path in mplayer.sh. For this first release you'll have to select mplayer plugin's "TRADITIONAL" mode to get it working with vdr-xine. - Exact but CPU intensive OSD blending can now be disabled in xine-lib by giving the configure switch "--disable-exact-blending" to "./autogen.sh". - Fixed high CPU load of vdr-xine when xine was disconnected while a currently replayed recording was paused. - Removed DATA_DIR. "noSignal.pes" is now expected below VDR's config files directory, i. e. .../plugins/xine/noSignal.pes. - Added OSD scaling mode "auto SHQ" which automatically chooses SHQ mode for low resolution streams (width < 360 respectively height < 288). This mode is now the one which is preselected in Makefile. - Updated INSTALL and MANUAL accordingly. 2004-10-24: Version 0.5.3 *** NO NEED TO RECOMPILE xine-lib / xine-ui THIS TIME *** - Fixed dolby audio processing to get recordings (and former live stream) of channel "ProSiebenHD" to play properly. - Fixed extraction of PTS values (ignore PTS of value 0) resulting in better prebuffering (thanks to Cristiano Bozzi for reporting this issue). - Changed data processing to comply with VDR's new buffer handling since release 1.3.13 (thanks to Klaus Schmidinger for assistance). - Scrambled flag on PES packets is now cleared to not have xine terminate playback due to bad reception quality respectively early data transmission while device is still tuneing (thanks to mvdbeek at vdr-portal for reporting this issue). - Replaced all usleep() calls by cMutex / cCondVar substitutes. - Added paragraph to INSTALL (BUILDING XINE) about required versions of autoconf suite for building xine (requested by Frank99 at vdr-portal). 2004-10-09: Version 0.5.2 - Fixed xine-lib/src/vdr/input_vdr.c for compliance with C coding. It should now compile properly with gcc-2.95.3 (thanks to Jouni Karvo for reporting this issue). - Implemented cPlugin::CommandLineHelp() (suggested by ronnykornexl at vdr-portal). - Updated INSTALL and pointed to xine-cvs archives on my homepage. - Updated INSTALL and documented "ERROR: remote control XineRemote not ready!" which appears in VDR's logfile. - Updated INSTALL and mentioned delay of about 10 seconds until VDR enters "Phase 2" of remote learning mode after pressing a remote key in "Phase 1". - Fixed compile error of xine-lib's video_out_xvmc.c which was caused by my OSD patches (thanks to Kron at vdr-portal for reporting this issue). - Fixed a buffer overrun in my patches to xine-lib which caused xine to die occasionally when VDR's OSD was active and/or operated (thanks to Joerg Knitter for intensively testing vdr-xine). - Tried to create a patch for xine-lib that complains when applied to RC6 but all rejects should only address the newly added driver xxmc. Therefore they can simply be ignored. - Currently, xine-ui.patch is empty but I didn't want to remove this file as doing so might break some people's build scripts. 2004-10-04: Version 0.5.1 - Updated documentation (thanks to Darren Salt for supplying the patch). - OSD scaling is now using frame resolution instead of stream resolution. This should make the OSD show up more often already at the right resoltion. For almost immediate response to resolution changes it is required to run xine with --post vdr. - Completed implementation of cDevice::GrabImage() method. It now supports xine's different image formats and honors the different aspect ratio of 16:9 broadcasts. - Added paragraph to INSTALL about additional programs which are needed to successfully grab images. - Added paragraph to INSTALL about remote learning mode. - Synchronized OSD output. Hideing an OSD and grabing an image immediately afterwards leaded to a snapshot which still showed the OSD. - Probably fixed audio replay on channels with still video. Prebuffer time is now extended dynamically by the difference between audio and video time stamps on the channel in question. - Fixed prebuffering after toggleing audio channel. - Reorganized directory structure of the xine part once again. xine-ui now needs the configure switch "--enable-vdr-keys" to support vdr-xine's remote. - Improved OSD scaling which took over month to optimize. See Makefile for OSD_SCALING_HQ. One might also choose OSD_SCALING_SHQ but be warned about the CPU load it causes. - xine-lib-1-RC6 should be sufficient for this release but my patches will complain about the recently added driver xxmc. 2004-08-08: Version 0.5.0 - Added paragraph to INSTALL about using the correct xine-config in Makefile (suggested by Luca Olivetti). - Fixed SEGV (in remote control code) which happend when VDR was exiting (thanks to Mattias Grönlund for supplying the patch). - Added a Makefile switch DONT_CHANGE_XINE_VOLUME to have vdr-xine never change xine's volume control (e. g. when connecting xine to VDR), as this might lead to (un)muting the wrong audio channels (requested by Jouni Karvo). - Fixed quickly switching between play / pause. There is still a lockup left to be fixed that appears on some machines when jumping between cutting marks (thanks to peter_weber69 at vdr-portal for testing the fix). - Added video scaling by xine post plugin 'vdr' to support yaepg plugin. One will have to add "--post vdr" to the command line or select the post plugin via the user interface. As yaepg patches VDR, you'll have to tell vdr-xine whether you want to support yaepg by enabling SET_VIDEO_WINDOW in Makefile. - Looking forward to having the xine part integrated into CVS, the locations of my plugins in xine-lib's source tree have changed. INSTALL was updated to the new patch procedure. A current CVS source tree is required (can be found on my home page, too). 2004-07-25: Version 0.4.3 - Fixed xineI18n.h for compatibility with VDR-1.3.[01]. The Russian translation entries were introduced with VDR-1.3.2 (reported by Rolf Ahrenberg). - Implemented OSD scaling. Currently, the scaled OSD is of "bad" quality, but as a start, it's better than nothing :-) - Tried to fix the issues which were recently introduced by soft-start, but it's likely that the fixes still don't work for less powerful machines. In such a case, locate softStartCalcSpeed() in xineDevice.c and try differnt methods (0..3). Please drop me a line on success respectively failure. 2004-07-04: Version 0.4.2 - Integrated swedish translation supplied by Tomas Prybil. - Fixed xineI18n.h for compatibility with VDR-1.3.6 (thanks to ronnykornexl at vdr-portal for reporting this issue). - Followed http://www.vdr-wiki.de/wiki/index.php/MANUAL-DE#Patches for creating respectively naming xine patches (thanks to ronnykornexl at vdr-portal). - New feature for Live-TV mode: "soft-start" replays the stream in slow motion while buffering. Useful for zapping, where you'll see an image of the current program quite early. The stream speed starts now at 50 %, slows down to 25 % and then speeds up to 100 %. This happens within the prebuffer time, so you should increase it (e. g. to 50) if you experience problems. 2004-06-27: Version 0.4.1 - Fixed audio handling of the previous release as it broke playing mp3s (thanks to Jouni Karvo for reporting this issue). - Fixed xine's linear PCM decoder to support more sample rates. Previously, mp3 files did often play to fast. - DVDs should play properly now, independent of the dolby setting. - Disabled debug code. Please remove all /tmp/frame* files which might have been created by release 0.4.0. - Integrated remote control patch for fbxine (provided by Dirk Meyer), but I'm still waiting for an example on how to bind the keys in fbxine. - Added a section to MANUAL about xine's command line and useful options (suggested by Jouni Karvo). - VDR: Improved performance. Please apply the vdr-1.3.11 patches which are available on my homepage. - VDR: Fixed recording replay when reaching the end of a recording in trickspeed mode. VDR switched to play mode instead of ending replay. 2004-06-21: Version 0.4.0 - Integrated patch which implements cDevice::GetSPUDecoder() (thanks to Sven Goethel for supplying the patch). - Fixed INSTALL again for correct quoting (thanks to Johannes Schoeller for reporting this issue). - Updated xineI18n.h for now 18 languages in VDR 1.3.x (thanks to Achim Tuffentsammer for the hint). - Added a paragraph to INSTALL for users of full featured cards (suggested by Tuomas Jormola). - Rewrote INSTALL in various places, to contain much helpful information discussed previously on the mailing list. - Integrated patch for supplying xine the default MRL of VDR (thanks to Darren Salt). - Added cDevice::Flush() to work around xine's large buffers. - xine-lib-1-rc5 should be sufficient to get the plugin working. - Improved a lot regarding interoperability with plugin vdr-dvd. To make use of these changes, you'll have to adjust your '.xine/config'. See MANUAL for more information. - It is recommended to enable dolby when using vdr-dvd. 2004-05-27: Version 0.3.4 - Fixed INSTALL to suggest a correctly quoted 'runvdr' command for running the plugin for the first time (thanks to peter_weber69 and anonymous (aka Ronny) on VDR Portal for pointing this out). - Fixed xineOsd.c to show OSD in initial remote key learning mode (thanks to chris281080 on VDR Portal for determining this issue). - Added a warning message to xineLib.c, in the case where 'noSignal.pes' can't be opened, e. g. if DATA_DIR is not set correctly in Makefile. - Fixed Makefile to use the default DATA_DIR, not my personal one. 2004-05-22: Version 0.3.3 - Fixed input_plugin/input_vdr.c to return proper values for cDevice::GetSTC() (thanks to Pekka Virtanen for supplying a test plugin). 2004-05-22: Version 0.3.2 - Fixed xineRemote.c to be compatible with API of VDR 1.2.6. 2004-05-21: Version 0.3.1 - Updated xine-lib.patch to support all VDR remote keys. - Patch file renamed to xine.patch, as xine-ui is modified too. - Updated INSTALL to reflect new patch file name and patching of xine-ui. - Updated MANUAL to tell command line args and xine's keybindings. 2004-05-21: Version 0.3.0 - Changed code sequence to follow the C language properly (reported by Jouni Karvo). - Updated Finnish translation and added empty Russian translations while maintaining compatibility with VDR 1.2.x (reported by Rolf Ahrenberg). - Dropped (later maybe configurable) sending MUTE to xine, as it's not really necessary for normal operation of VDR (requested by Jouni Karvo). - Fixed sending dolby audio to xine (requires up-to-date xine-lib CVS). - Added support for VDR-1.3.7's new OSD interface. - Integrated patch from Simon Truss to send keystrokes from xine to VDR for controlling VDR from within xine. - Enhanced compatibility of xine part to support xine's older API for VIA-enhanced xine (thanks to Vincenzo Memeo). 2004-05-05: Version 0.2.2 - Added define _GNU_SOURCE to Makefile (just like for new plugins). - DATA_DIR in Makefile contained path of my system. - OSD update in xine is now optimized to VDR's dirty OSD area. - Implemented cDevice::GetSTC(). - Removed some unnecessary PES packet requirements for trickspeed mode. - Moved image conversion from xine to VDR for cDevice::GrabImage() implementation. - Added support for dolby audio (i. e. cDevice::PlayAudio(): untested due to lack of hardware). - Added configuration option to enable dolby audio (default: off). - When xine connects to VDR, volume and muting are adjusted to fit VDR's values. 2004-04-23: Version 0.2.1 - Corrected some FIXMEs for certain recordings. - Audio is now unmuted when leaving a recording in trickmode speed. 2004-04-22: Version 0.2.0 - Added trickspeed mode. - Added image grabbing (requires y4mscaler, y4mtoppm and pnmtojpeg). - Added automatic selection of prebuffer mode. - Dropped config option 'xine.modeReplay.prebufferFrames'. - Dropped config option 'xine.usageModeDefault'. - Dropped main menu entry. - Partly integrated Makefile patch from Darren Salt. - Fixed some stuff regarding latest CVS xine. 2004-02-03: Version 0.1.2 - Fixed a compile error in xine - Fixed a compile error in VDR 2003-12-21: Version 0.1.1 - Changed version numbering for easier update recognition. - Updated to latest xine API version number. - Fixed VDR's high CPU load when no xine connected. - Added Finnish translation supplied by Rolf Ahrenberg. - Supplied a completely black 'noSignal.pes' to prevent the monitor from damage while listening to radio stations for a long time. 2003-12-17: Version 0.0.3 (aka 0.1.0) - Improved channel switching by using xine's prebuffering. - Prebuffer settings configurable for Live-TV and Replay. - Changed directories for plugin's files as suggested on ML. - Supports xine's unscaled OSD (configurable). - Shows a currently open OSD on connect with xine. - Shows an OSD in remote key learning mode. - Addresses some show stoppers due to recent xine changes. - Supports i18n. 2003-09-08: Version 0.0.2 - Minor fixes. - Improved channel switching. - Dropped dependencies to X11. 2003-09-07: Version 0.0.1 - Initial release. 2003-06-23: Version 0.0.0 - Announcement. xine-0.9.4/xineOsd.h0000644000000000000000000001006711537737736012776 0ustar rootroot #ifndef __XINEOSD_H #define __XINEOSD_H #include "xineCommon.h" #if APIVERSNUM < 10307 #include #else #include #endif #include namespace PluginXine { class cXineDevice; class cXineLib; #if APIVERSNUM < 10307 class cXineOsd : public cOsdBase #else class cXineOsd : public cOsd #endif { cXineDevice &m_xineDevice; cXineLib &m_xineLib; cMutex &m_osdMutex; const int m_extentWidth; const int m_extentHeight; #if APIVERSNUM < 10307 bool m_windowVisible[ MAXNUMWINDOWS ]; #endif #if APIVERSNUM >= 10717 cPixmapMemory *m_pRawOsd; #endif void callSendWindow(const int maxOsdWidth, const int maxOsdHeight, const int videoLeft, const int videoTop, const int videoWidth, const int videoHeight, const int videoZoomX, const int videoZoomY, const bool dontOptimize = false); #if APIVERSNUM < 10307 virtual bool OpenWindow(cWindow *Window); virtual void CommitWindow(cWindow *Window); virtual void ShowWindow(cWindow *Window); virtual void HideWindow(cWindow *Window, bool Hide); virtual void MoveWindow(cWindow *Window, int x, int y); virtual void CloseWindow(cWindow *Window); #else virtual void SetActive(bool On); #if APIVERSNUM >= 10717 virtual cPixmap *CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null); virtual void DestroyPixmap(cPixmap *Pixmap); virtual void DrawImage(const cPoint &Point, const cImage &Image); virtual void DrawImage(const cPoint &Point, int ImageHandle); #endif virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); virtual void SaveRegion(int x1, int y1, int x2, int y2); virtual void RestoreRegion(void); virtual eOsdError SetPalette(const cPalette &Palette, int Area); virtual void DrawPixel(int x, int y, tColor Color); #if APIVERSNUM < 10327 virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0); #elif APIVERSNUM < 10344 virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool ReplacePalette = false); #else virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool ReplacePalette = false, bool Overlay = false); #endif virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault); virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color); virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0); virtual void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type); virtual void Flush(void); #endif void HideOsd(); void GetMaxOsdSize(int &maxOsdWidth, int &maxOsdHeight); public: #if APIVERSNUM < 10509 cXineOsd(cXineDevice &device, int w, int h, int x, int y); #else cXineOsd(cXineDevice &device, int w, int h, int x, int y, uint Level); #endif virtual ~cXineOsd(); void ReshowCurrentOsd(const bool dontOptimize, const int frameLeft = -1, const int frameTop = -1, const int frameWidth = -1, const int frameHeight = -1, const int frameZoomX = -1, const int frameZoomY = -1); friend class cXineLib; }; class cXineOsdMutexLock { cMutexLock *m_pOsdLock; #if APIVERSNUM >= 10717 cPixmapMutexLock *m_pPixmapMutexLock; #endif public: cXineOsdMutexLock(cMutex *const pOsdMutex); ~cXineOsdMutexLock(); }; #if APIVERSNUM >= 10307 class cXineOsdProvider : public cOsdProvider { cXineDevice &m_xineDevice; public: cXineOsdProvider(cXineDevice &xineDevice); #if APIVERSNUM < 10509 virtual cOsd *CreateOsd(int Left, int Top); #else virtual cOsd *CreateOsd(int Left, int Top, uint Level); virtual cOsd *CreateOsd(int ExtentWidth, int ExtentHeight, int Left, int Top, uint Level); #endif #if APIVERSNUM >= 10717 virtual bool ProvidesTrueColor(void); #endif }; #endif }; #endif //__XINEOSD_H xine-0.9.4/xineSetupPage.c0000644000000000000000000000441411537740324014123 0ustar rootroot #include "xineCommon.h" #include "xineSetupPage.h" #include "xineSettings.h" #include "xineLib.h" namespace PluginXine { void cXineSetupPage::Store() { m_globalSettings.Store(this); bool setupDiffers = m_globalSettings.setupDiffers(m_localSettings); #if APIVERSNUM >= 10707 bool lockOsdUpdate = m_globalSettings.OsdExtentParams() != m_localSettings.OsdExtentParams(); #endif m_localSettings = m_globalSettings; if (!setupDiffers) return; m_xineLib->execFuncSetup(); #if APIVERSNUM >= 10707 if (lockOsdUpdate) { m_xineLib->LockOsdUpdate(); cOsdProvider::UpdateOsdSize(true); SetDisplayMenu(); } m_xineLib->ReshowCurrentOSD(lockOsdUpdate); #else m_xineLib->ReshowCurrentOSD(); #endif } cXineSetupPage::cXineSetupPage(cXineLib *const xineLib, cXineSettings &settings) : cMenuSetupPage() , m_xineLib(xineLib) , m_globalSettings(settings) , m_localSettings(settings) { m_globalSettings.Create(this); } cXineSetupPage::~cXineSetupPage() { bool setupDiffers = m_globalSettings.setupDiffers(m_localSettings); #if APIVERSNUM >= 10707 bool lockOsdUpdate = m_globalSettings.OsdExtentParams() != m_localSettings.OsdExtentParams(); #endif m_globalSettings = m_localSettings; if (!setupDiffers) return; m_xineLib->execFuncSetup(); #if APIVERSNUM >= 10707 if (lockOsdUpdate) { m_xineLib->LockOsdUpdate(); cOsdProvider::UpdateOsdSize(true); SetDisplayMenu(); } m_xineLib->ReshowCurrentOSD(lockOsdUpdate); #else m_xineLib->ReshowCurrentOSD(); #endif } eOSState cXineSetupPage::ProcessKey(eKeys Key) { const cXineSettings prevSettings = m_globalSettings; eOSState state = cMenuSetupPage::ProcessKey(Key); if (prevSettings.setupDiffers(m_globalSettings)) { m_xineLib->execFuncSetup(); #if APIVERSNUM >= 10707 bool lockOsdUpdate = prevSettings.OsdExtentParams() != m_globalSettings.OsdExtentParams(); if (lockOsdUpdate) { m_xineLib->LockOsdUpdate(); cOsdProvider::UpdateOsdSize(true); SetDisplayMenu(); Display(); } m_xineLib->ReshowCurrentOSD(lockOsdUpdate); #else m_xineLib->ReshowCurrentOSD(); #endif } return state; } }; xine-0.9.4/xineOsd.c0000644000000000000000000004127411537742522012762 0ustar rootroot #include "xineCommon.h" #include "xineOsd.h" #include "xineDevice.h" #include "xineLib.h" namespace PluginXine { #if APIVERSNUM < 10307 bool cXineOsd::OpenWindow(cWindow *Window) { cXineOsdMutexLock osdLock(&m_osdMutex); m_windowVisible[ Window->Handle() ] = false; int maxOsdWidth, maxOsdHeight; GetMaxOsdSize(maxOsdWidth, maxOsdHeight); return m_xineLib.OpenWindow(this, Window, maxOsdWidth, maxOsdHeight); } void cXineOsd::CommitWindow(cWindow *Window) { cXineOsdMutexLock osdLock(&m_osdMutex); m_xineLib.CommitWindow(this, Window); } void cXineOsd::ShowWindow(cWindow *Window) { cXineOsdMutexLock osdLock(&m_osdMutex); m_windowVisible[ Window->Handle() ] = true; m_xineLib.ShowWindow(this, Window); } void cXineOsd::HideWindow(cWindow *Window, bool Hide) { cXineOsdMutexLock osdLock(&m_osdMutex); m_windowVisible[ Window->Handle() ] = !Hide; m_xineLib.HideWindow(this, Window, Hide); } void cXineOsd::MoveWindow(cWindow *Window, int x, int y) { cXineOsdMutexLock osdLock(&m_osdMutex); m_xineLib.MoveWindow(this, Window, x, y); } void cXineOsd::CloseWindow(cWindow *Window) { cXineOsdMutexLock osdLock(&m_osdMutex); m_windowVisible[ Window->Handle() ] = false; m_xineLib.CloseWindow(this, Window); } #endif static int vl = -1, vt = -1, vw = -1, vh = -1, zx = -1, zy = -1; void cXineOsd::ReshowCurrentOsd(const bool dontOptimize, const int frameLeft /* = -1 */, const int frameTop /* = -1 */, const int frameWidth /* = -1 */, const int frameHeight /* = -1 */, const int frameZoomX /* = -1 */, const int frameZoomY /* = -1 */) { cXineOsdMutexLock osdLock(&m_osdMutex); int maxOsdWidth, maxOsdHeight; GetMaxOsdSize(maxOsdWidth, maxOsdHeight); #if APIVERSNUM < 10307 (void)vl; (void)vt; (void)vw; (void)vh; (void)zx; (void)zy; for (int i = 0; i < NumWindows(); i++) { cWindow *const window = GetWindowNr(i); if (!window) continue; if (dontOptimize) m_xineLib.OpenWindow(this, window, maxOsdWidth, maxOsdHeight); m_xineLib.CommitWindow(this, window, !dontOptimize); if (m_windowVisible[ i ]) Show(window->Handle()); else Hide(window->Handle()); } #else #ifdef SET_VIDEO_WINDOW m_xineLib.SetVideoWindow(maxOsdWidth, maxOsdHeight, vidWin, dontOptimize); #endif int videoLeft = frameLeft; int videoTop = frameTop; int videoWidth = frameWidth; int videoHeight = frameHeight; int videoZoomX = frameZoomX; int videoZoomY = frameZoomY; if (frameLeft < 0 || frameTop < 0 || frameWidth < 0 || frameHeight < 0 || frameZoomX < 0 || frameZoomY < 0) { m_xineLib.execFuncVideoSize(videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY); } // ::fprintf(stderr, "frame: %d x %d, %d x %d\n", videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY); vl = videoLeft; vt = videoTop; vw = videoWidth; vh = videoHeight; zx = videoZoomX; zy = videoZoomY; callSendWindow(maxOsdWidth, maxOsdHeight, videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY, dontOptimize); #endif m_xineLib.execFuncOsdFlush(); } #if APIVERSNUM < 10509 cXineOsd::cXineOsd(cXineDevice &xineDevice, int w, int h, int x, int y) #else cXineOsd::cXineOsd(cXineDevice &xineDevice, int w, int h, int x, int y, uint Level) #endif #if APIVERSNUM < 10307 : cOsdBase(x, y) #elif APIVERSNUM < 10509 : cOsd(x, y) #else : cOsd(x, y, Level) #endif , m_xineDevice(xineDevice) , m_xineLib(xineDevice.m_xineLib) , m_osdMutex(xineDevice.m_osdMutex) , m_extentWidth(w) , m_extentHeight(h) #if APIVERSNUM >= 10717 , m_pRawOsd(0) #endif { //static int cnt = 0; //fprintf(stderr, "x: %d, y: %d, cnt: %d\n", x, y, cnt); // if (x == 58 && y == 46 && cnt++ == 5) { char cmd[500]; sprintf(cmd, "ddd /soft/vdr-" APIVERSION "/bin/vdr %d", getpid()); system(cmd); sleep(10); } //should never happen! #if APIVERSNUM < 10307 ::memset(m_windowVisible, 0, sizeof (m_windowVisible)); #endif } cXineOsd::~cXineOsd() { #if APIVERSNUM < 10509 HideOsd(); #else cXineOsdMutexLock osdLock(&m_osdMutex); if (Active()) SetActive(false); #endif #if APIVERSNUM >= 10717 delete m_pRawOsd; #endif } void cXineOsd::HideOsd() { cXineOsdMutexLock osdLock(&m_osdMutex); #if APIVERSNUM < 10307 for (int i = 0; i < MAXNUMWINDOWS; i++) m_xineLib.CloseWindow(this, i); #else int maxOsdWidth, maxOsdHeight; GetMaxOsdSize(maxOsdWidth, maxOsdHeight); tArea defaultWindow; defaultWindow.x1 = 0; defaultWindow.y1 = 0; defaultWindow.x2 = 0; defaultWindow.y2 = 0; defaultWindow.bpp = 0; m_xineLib.SetVideoWindow(maxOsdWidth, maxOsdHeight, defaultWindow); for (int i = 0; i < MAXNUMWINDOWS; i++) m_xineLib.SendWindow(maxOsdWidth, maxOsdHeight, this, i); #endif m_xineDevice.OnFreeOsd(this); m_xineLib.execFuncOsdFlush(); // m_xineLib.execFuncGrabImage("/tmp/grab.jpg", true, 50, 1000, 1000); } #if APIVERSNUM >= 10307 void cXineOsd::SetActive(bool On) { #if APIVERSNUM >= 10509 cXineOsdMutexLock osdLock(&m_osdMutex); if (On == Active()) return; cOsd::SetActive(On); if (!m_xineDevice.ChangeCurrentOsd(this, On)) return; if (On) m_xineLib.ReshowCurrentOSD(); else HideOsd(); #endif } #endif #if APIVERSNUM >= 10717 cPixmap *cXineOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort /* = cRect::Null */) { cXineOsdMutexLock osdLock(&m_osdMutex); return cOsd::CreatePixmap(Layer, ViewPort, DrawPort); } void cXineOsd::DestroyPixmap(cPixmap *Pixmap) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DestroyPixmap(Pixmap); } void cXineOsd::DrawImage(const cPoint &Point, const cImage &Image) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawImage(Point, Image); } void cXineOsd::DrawImage(const cPoint &Point, int ImageHandle) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawImage(Point, ImageHandle); } #endif #if APIVERSNUM >= 10307 eOsdError cXineOsd::CanHandleAreas(const tArea *Areas, int NumAreas) { // fprintf(stderr, "cXineOsd::CanHandleAreas(%p, %d)\n", Areas, NumAreas); const eOsdError retVal = cOsd::CanHandleAreas(Areas, NumAreas); if (oeOk != retVal) return retVal; for (int i = 0; i < NumAreas; i++) { const tArea &a = Areas[ i ]; /* fprintf(stderr, "Areas[ %d ]: (%d,%d)-(%d,%d)@%d\n" , i , a.x1 , a.y1 , a.x2 , a.y2 , a.bpp); */ assert(a.x1 <= a.x2); assert(a.y1 <= a.y2); if (1 != a.bpp && 2 != a.bpp && 4 != a.bpp && 8 != a.bpp #if APIVERSNUM >= 10717 && (32 != a.bpp || !m_xineDevice.SupportsTrueColorOSD()) #endif ) { return oeBppNotSupported; } } return oeOk; } eOsdError cXineOsd::SetAreas(const tArea *Areas, int NumAreas) { cXineOsdMutexLock osdLock(&m_osdMutex); return cOsd::SetAreas(Areas, NumAreas); } void cXineOsd::SaveRegion(int x1, int y1, int x2, int y2) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::SaveRegion(x1, y1, x2, y2); } void cXineOsd::RestoreRegion(void) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::RestoreRegion(); } eOsdError cXineOsd::SetPalette(const cPalette &Palette, int Area) { cXineOsdMutexLock osdLock(&m_osdMutex); return cOsd::SetPalette(Palette, Area); } void cXineOsd::DrawPixel(int x, int y, tColor Color) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawPixel(x, y, Color); } #if APIVERSNUM < 10327 void cXineOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg /* = 0 */, tColor ColorBg /* = 0 */) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawBitmap(x, y, Bitmap, ColorFg, ColorBg); // fprintf(stderr, "drawbitmap\n"); } #elif APIVERSNUM < 10344 void cXineOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg /* = 0 */, tColor ColorBg /* = 0 */, bool ReplacePalette /* = false */) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette); // fprintf(stderr, "drawbitmap\n"); } #else void cXineOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg /* = 0 */, tColor ColorBg /* = 0 */, bool ReplacePalette /* = false */, bool Overlay /* = false */) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette, Overlay); // fprintf(stderr, "drawbitmap: (%d x %d) at (%d, %d) in (%d x %d)\n", Bitmap.Width(), Bitmap.Height(), x, y, Width(), Height()); } #endif void cXineOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width /* = 0 */, int Height /* = 0 */, int Alignment /* = taDefault */) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment); } void cXineOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawRectangle(x1, y1, x2, y2, Color); } void cXineOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants /* = 0 */) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawEllipse(x1, y1, x2, y2, Color, Quadrants); } void cXineOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) { cXineOsdMutexLock osdLock(&m_osdMutex); cOsd::DrawSlope(x1, y1, x2, y2, Color, Type); } void cXineOsd::Flush(void) { // ::fprintf(stderr, "Flush ---: %s\n", ::ctime(&(const time_t &)::time(0))); cXineOsdMutexLock osdLock(&m_osdMutex); // ::fprintf(stderr, "Flush +++: %s\n", ::ctime(&(const time_t &)::time(0))); #if APIVERSNUM >= 10509 if (!Active()) return; #endif // static int cnt = 0; // fprintf(stderr, "cXineOsd::Flush() %d\n", cnt++); int maxOsdWidth, maxOsdHeight; GetMaxOsdSize(maxOsdWidth, maxOsdHeight); #ifdef SET_VIDEO_WINDOW m_xineLib.SetVideoWindow(maxOsdWidth, maxOsdHeight, vidWin); #endif int videoLeft = -1; int videoTop = -1; int videoWidth = -1; int videoHeight = -1; int videoZoomX = -1; int videoZoomY = -1; m_xineLib.execFuncVideoSize(videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY); if (videoLeft < 0) videoLeft = 0; if (videoTop < 0) videoTop = 0; if (videoZoomX < 0) videoZoomX = 100; if (videoZoomY < 0) videoZoomY = 100; if (vl != videoLeft || vt != videoTop || vw != videoWidth || vh != videoHeight || zx != videoZoomX || zy != videoZoomY) { xfprintf(stderr, "frame: (%d, %d)-(%d, %d), zoom: (%.2lf, %.2lf)\n", videoLeft, videoTop, videoWidth, videoHeight, videoZoomX / 100.0, videoZoomY / 100.0); // ::fprintf(stderr, "video: %d x %d, %d x %d @ %d %% x %d %%\n", videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY); vl = videoLeft; vt = videoTop; vw = videoWidth; vh = videoHeight; zx = videoZoomX; zy = videoZoomY; } callSendWindow(maxOsdWidth, maxOsdHeight, videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY); m_xineLib.execFuncOsdFlush(); } void cXineOsd::callSendWindow(const int maxOsdWidth, const int maxOsdHeight, const int videoLeft, const int videoTop, const int videoWidth, const int videoHeight, const int videoZoomX, const int videoZoomY, const bool dontOptimize /* = false */) { #if APIVERSNUM >= 10717 bool head = false; if (IsTrueColor()) { if (m_pRawOsd) { if (m_pRawOsd->DrawPort().Width() != Width() || m_pRawOsd->DrawPort().Height() != Height()) { delete m_pRawOsd; m_pRawOsd = 0; } } if (!m_pRawOsd) { m_pRawOsd = new cPixmapMemory(0, cRect(0, 0, Width(), Height())); m_pRawOsd->Clear(); if (!head) { head = true; // fprintf(stderr, "OSD: -c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c-c\n"); } } while (cPixmapMemory *pm = RenderPixmaps()) { if (!head) { head = true; // fprintf(stderr, "OSD: --------------------------------------------------\n"); } /* fprintf(stderr, "OSD: X: %4d, Y: %4d, W: %4d, H: %4d, X: %4d, Y: %4d, W: %4d, H: %4d\n" , pm->ViewPort().X() , pm->ViewPort().Y() , pm->ViewPort().Width() , pm->ViewPort().Height() , pm->DrawPort().X() , pm->DrawPort().Y() , pm->DrawPort().Width() , pm->DrawPort().Height() ); */ m_pRawOsd->Copy(pm, pm->DrawPort().Shifted(-pm->DrawPort().Point()), pm->ViewPort().Point()); delete pm; } } else if (m_pRawOsd) { delete m_pRawOsd; m_pRawOsd = 0; /* if (head) fprintf(stderr, "OSD: =d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d=d\n"); */ } if (m_pRawOsd) { cXinePixmapMemoryAdapter adapter(m_pRawOsd); /* cXineAdapter *bm = adapter; int x0, y0, x1, y1; bool d = bm->Dirty(x0, y0, x1, y1); if (head) fprintf(stderr, d ? "OSD: x: %4d, y: %4d, w: %4d, h: %4d, x0: %4d, y0: %4d, x1: %4d, y1: %4d\n" : "OSD: x: %4d, y: %4d, w: %4d, h: %4d\n" , bm->X0() , bm->Y0() , bm->Width() , bm->Height() , x0 , y0 , x1 , y1 ); */ m_xineLib.SendWindow(maxOsdWidth, maxOsdHeight, this, 0, adapter, videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY, dontOptimize); for (int i = 1; i < MAXNUMWINDOWS; i++) m_xineLib.SendWindow(maxOsdWidth, maxOsdHeight, this, i); /* if (head) fprintf(stderr, "OSD: ==================================================\n"); */ } else #endif { for (int i = 0; i < MAXNUMWINDOWS; i++) { cXineBitmapAdapter adapter(GetBitmap(i)); m_xineLib.SendWindow(maxOsdWidth, maxOsdHeight, this, i, adapter, videoLeft, videoTop, videoWidth, videoHeight, videoZoomX, videoZoomY, dontOptimize); } } } void cXineOsd::GetMaxOsdSize(int &maxOsdWidth, int &maxOsdHeight) { maxOsdWidth = m_extentWidth; maxOsdHeight = m_extentHeight; return; /* maxOsdWidth = 0; maxOsdHeight = 0; for (int i = 0; i < MAXNUMWINDOWS; i++) { cBitmap *const p = GetBitmap(i); if (!p) continue; int w = Left() + p->X0() + p->Width(); int h = Top() + p->Y0() + p->Height(); if (maxOsdWidth < w) maxOsdWidth = w; if (maxOsdHeight < h) maxOsdHeight = h; } //fprintf(stderr, "GetMaxOsdSize(%d, %d)\n", maxOsdWidth, maxOsdHeight); #if APIVERSNUM >= 10707 if (maxOsdWidth <= m_xineDevice.Settings().OsdExtentParams().GetOsdExtentWidth() && maxOsdHeight <= m_xineDevice.Settings().OsdExtentParams().GetOsdExtentHeight()) { maxOsdWidth = m_xineDevice.Settings().OsdExtentParams().GetOsdExtentWidth(); maxOsdHeight = m_xineDevice.Settings().OsdExtentParams().GetOsdExtentHeight(); } else #endif if (maxOsdWidth > 1920 || maxOsdHeight > 1080) { if (maxOsdWidth < 1920) maxOsdWidth = 1920; if (maxOsdHeight < 1080) maxOsdHeight = 1080; } else if (maxOsdWidth > 1280 || maxOsdHeight > 720) { maxOsdWidth = 1920; maxOsdHeight = 1080; } else if (maxOsdWidth > 720 || maxOsdHeight > 576) { maxOsdWidth = 1280; maxOsdHeight = 720; } else { maxOsdWidth = 720; maxOsdHeight = 576; } */ } cXineOsdMutexLock::cXineOsdMutexLock(cMutex *const pOsdMutex) : m_pOsdLock(0) #if APIVERSNUM >= 10717 , m_pPixmapMutexLock(0) #endif { #if APIVERSNUM >= 10717 m_pPixmapMutexLock = new cPixmapMutexLock; #endif m_pOsdLock = new cMutexLock(pOsdMutex); } cXineOsdMutexLock::~cXineOsdMutexLock() { delete m_pOsdLock; #if APIVERSNUM >= 10717 delete m_pPixmapMutexLock; #endif } cXineOsdProvider::cXineOsdProvider(cXineDevice &xineDevice) : cOsdProvider() , m_xineDevice(xineDevice) { } #if APIVERSNUM < 10509 cOsd *cXineOsdProvider::CreateOsd(int Left, int Top) { return m_xineDevice.NewOsd(0, 0, Left, Top); } #else cOsd *cXineOsdProvider::CreateOsd(int Left, int Top, uint Level) { return m_xineDevice.NewOsd(0, 0, Left, Top, Level); } cOsd *cXineOsdProvider::CreateOsd(int ExtentWidth, int ExtentHeight, int Left, int Top, uint Level) { return m_xineDevice.NewOsd(ExtentWidth, ExtentHeight, Left, Top, Level); } #endif #if APIVERSNUM >= 10717 bool cXineOsdProvider::ProvidesTrueColor() { return true; } #endif #endif }; xine-0.9.4/vdr172h264parser.h0000644000000000000000000003400711166172122014207 0ustar rootroot #if APIVERSNUM >= 10703 /* * h264parser.h: a minimalistic H.264 video stream parser * * See the main source file 'vdr.c' for copyright information and * how to reach the author. */ #ifndef __VDR172H264PARSER_H #define __VDR172H264PARSER_H namespace vdr172 { namespace H264 { // --- cException ---------------------------------------------------------- class cException { private: cString message; public: cException(const cString &Message) { message = Message; } const cString &Message(void) const { return message; } }; // --- cBitReader ---------------------------------------------------------- class cBitReader { public: class cBookMark { private: uint8_t *data; int count; uint32_t bits; uint32_t bitsAvail; int countZeros; cBookMark(void) {} friend class cBitReader; }; private: cBookMark bm; uint8_t NextByte(void); uint32_t ReadBits(uint32_t n); public: cBitReader(uint8_t *Data, int Count); uint32_t u(uint32_t n) { return ReadBits(n); } // read n bits as unsigned number uint32_t ue(void); // read Exp-Golomb coded unsigned number int32_t se(void); // read Exp-Golomb coded signed number uint32_t GetBitsAvail(void) { return (bm.bitsAvail & 0x07); } bool GetBytesAvail(void) { return (bm.count > 0); } const cBookMark BookMark(void) const { return bm; } void BookMark(const cBookMark &b) { bm = b; } }; inline cBitReader::cBitReader(unsigned char *Data, int Count) { bm.data = Data; bm.count = Count; bm.bitsAvail = 0; bm.countZeros = 0; } inline uint8_t cBitReader::NextByte(void) { if (bm.count < 1) // there is no more data left in this NAL unit throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); // detect 00 00 00, 00 00 01 and 00 00 03 and handle them if (*bm.data == 0x00) { if (bm.countZeros >= 3) // 00 00 00: the current NAL unit should have been terminated already before this sequence throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); // increase the zero counter as we have a zero byte bm.countZeros++; } else { if (bm.countZeros >= 2) { if (*bm.data == 0x01) // 00 00 01: the current NAL unit should have been terminated already before this sequence throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); if (*bm.data == 0x03) { // 00 00 03 xx: the emulation prevention byte 03 needs to be removed and xx must be returned if (bm.count < 2) throw new cException("ERROR: H264::cBitReader::NextByte(): premature end of data"); // drop 03 and xx will be returned below bm.count--; bm.data++; } } // reset the zero counter as we had a non zero byte bm.countZeros = 0; } bm.count--; return *bm.data++; } inline uint32_t cBitReader::ReadBits(uint32_t n) { // fill the "shift register" bits with sufficient data while (n > bm.bitsAvail) { bm.bits <<= 8; bm.bits |= NextByte(); bm.bitsAvail += 8; if (bm.bitsAvail > 24) { // a further turn will overflow bitbuffer if (n <= bm.bitsAvail) break; // service non overflowing request if (n <= 32) // split overflowing reads into concatenated reads return (ReadBits(16) << 16) | ReadBits(n - 16); // cannot read more than 32 bits at once throw new cException("ERROR: H264::cBitReader::ReadBits(): bitbuffer overflow"); } } // return n most significant bits bm.bitsAvail -= n; return (bm.bits >> bm.bitsAvail) & (((uint32_t)1 << n) - 1); } inline uint32_t cBitReader::ue(void) { // read and decode an Exp-Golomb coded unsigned number // // bitstring resulting number // 1 0 // 0 1 x 1 ... 2 // 0 0 1 x y 3 ... 6 // 0 0 0 1 x y z 7 ... 14 // ... int LeadingZeroBits = 0; while (ReadBits(1) == 0) LeadingZeroBits++; if (LeadingZeroBits == 0) return 0; if (LeadingZeroBits >= 32) throw new cException("ERROR: H264::cBitReader::ue(): overflow"); return ((uint32_t)1 << LeadingZeroBits) - 1 + ReadBits(LeadingZeroBits); } inline int32_t cBitReader::se(void) { // read and decode an Exp-Golomb coded signed number // // unsigned value resulting signed value // 0 0 // 1 +1 // 2 -1 // 3 +2 // 4 -2 // ... uint32_t r = ue(); if (r > 0xFFFFFFFE) throw new cException("ERROR: H264::cBitReader::se(): overflow"); return (1 - 2 * (r & 1)) * ((r + 1) / 2); } // --- cPictureTiming ------------------------------------------------------ class cPictureTiming { private: friend class cContext; bool defined; public: cPictureTiming(void) { memset(this, 0, sizeof (*this)); } bool Defined(void) const { return defined; } uint32_t pic_struct; }; // --- cSequenceParameterSet ----------------------------------------------- class cSequenceParameterSet { private: friend class cContext; bool defined; uint32_t log2MaxFrameNum; uint32_t log2MaxPicOrderCntLsb; uint32_t cpbRemovalDelayLength; uint32_t dpbOutputDelayLength; public: cSequenceParameterSet(void); bool Defined(void) { return defined; } void log2_max_frame_num_minus4(uint32_t Value) { log2MaxFrameNum = Value + 4; } uint32_t log2_max_frame_num_minus4(void) const { return log2MaxFrameNum - 4; } uint32_t log2_max_frame_num(void) const { return log2MaxFrameNum; } void log2_max_pic_order_cnt_lsb_minus4(uint32_t Value) { log2MaxPicOrderCntLsb = Value + 4; } uint32_t log2_max_pic_order_cnt_lsb_minus4(void) const { return log2MaxPicOrderCntLsb - 4; } uint32_t log2_max_pic_order_cnt_lsb(void) const { return log2MaxPicOrderCntLsb; } void cpb_removal_delay_length_minus1(uint32_t Value) { cpbRemovalDelayLength = Value + 1; } uint32_t cpb_removal_delay_length_minus1(void) const { return cpbRemovalDelayLength - 1; } uint32_t cpb_removal_delay_length(void) const { return cpbRemovalDelayLength; } void dpb_output_delay_length_minus1(uint32_t Value) { dpbOutputDelayLength = Value + 1; } uint32_t dpb_output_delay_length_minus1(void) const { return dpbOutputDelayLength - 1; } uint32_t dpb_output_delay_length(void) const { return dpbOutputDelayLength; } uint32_t seq_parameter_set_id; uint32_t pic_order_cnt_type; uint32_t delta_pic_order_always_zero_flag; uint32_t frame_mbs_only_flag; uint32_t timing_info_present_flag; uint32_t num_units_in_tick; uint32_t time_scale; uint32_t fixed_frame_rate_flag; uint32_t nal_hrd_parameters_present_flag; uint32_t vcl_hrd_parameters_present_flag; uint32_t pic_struct_present_flag; cPictureTiming pic_timing_sei; }; inline cSequenceParameterSet::cSequenceParameterSet(void) { memset(this, 0, sizeof (*this)); log2_max_frame_num_minus4(0); log2_max_pic_order_cnt_lsb_minus4(0); cpb_removal_delay_length_minus1(23); dpb_output_delay_length_minus1(23); } // --- cPictureParameterSet ------------------------------------------------ class cPictureParameterSet { private: friend class cContext; bool defined; public: cPictureParameterSet(void) { memset(this, 0, sizeof (*this)); } bool Defined(void) { return defined; } uint32_t pic_parameter_set_id; uint32_t seq_parameter_set_id; uint32_t pic_order_present_flag; }; // --- cSliceHeader -------------------------------------------------------- class cSliceHeader { private: friend class cContext; bool defined; bool isFirstSliceOfCurrentAccessUnit; uint32_t picOrderCntType; uint32_t nalRefIdc; uint32_t nalUnitType; public: cSliceHeader(void) { memset(this, 0, sizeof (*this)); } bool Defined(void) const { return defined; } bool IsFirstSliceOfCurrentAccessUnit(void) const { return isFirstSliceOfCurrentAccessUnit; } void nal_ref_idc(uint32_t Value) { nalRefIdc = Value; } uint32_t nal_ref_idc(void) const { return nalRefIdc; } void nal_unit_type(uint32_t Value) { nalUnitType = Value; } uint32_t nal_unit_type(void) const { return nalUnitType; } uint32_t slice_type; uint32_t pic_parameter_set_id; uint32_t frame_num; uint32_t field_pic_flag; uint32_t bottom_field_flag; uint32_t idr_pic_id; uint32_t pic_order_cnt_lsb; int32_t delta_pic_order_cnt_bottom; int32_t delta_pic_order_cnt[2]; enum eAccessUnitType { Frame = 0, TopField, BottomField }; eAccessUnitType GetAccessUnitType() const { return (eAccessUnitType)(field_pic_flag + bottom_field_flag); } }; // --- cContext ------------------------------------------------------------ class cContext { private: cSequenceParameterSet spsStore[32]; cPictureParameterSet ppsStore[256]; cSequenceParameterSet *sps; // active Sequence Parameter Set cPictureParameterSet *pps; // active Picture Parameter Set cSliceHeader sh; public: cContext(void) { sps = 0; pps = 0; } void Define(cSequenceParameterSet &SPS); void Define(cPictureParameterSet &PPS); void Define(cSliceHeader &SH); void Define(cPictureTiming &PT); void ActivateSPS(uint32_t ID); void ActivatePPS(uint32_t ID); const cSequenceParameterSet *ActiveSPS(void) const { return sps; } const cPictureParameterSet *ActivePPS(void) const { return pps; } const cSliceHeader *CurrentSlice(void) const { return sh.Defined() ? &sh : 0; } int GetFramesPerSec(void) const; }; inline void cContext::ActivateSPS(uint32_t ID) { if (ID >= (sizeof (spsStore) / sizeof (*spsStore))) throw new cException("ERROR: H264::cContext::ActivateSPS(): id out of range"); if (!spsStore[ID].Defined()) throw new cException("ERROR: H264::cContext::ActivateSPS(): requested SPS is undefined"); sps = &spsStore[ID]; } inline void cContext::ActivatePPS(uint32_t ID) { if (ID >= (sizeof (ppsStore) / sizeof (*ppsStore))) throw new cException("ERROR: H264::cContext::ActivatePPS(): id out of range"); if (!ppsStore[ID].Defined()) throw new cException("ERROR: H264::cContext::ActivatePPS(): requested PPS is undefined"); pps = &ppsStore[ID]; ActivateSPS(pps->seq_parameter_set_id); } inline void cContext::Define(cSequenceParameterSet &SPS) { if (SPS.seq_parameter_set_id >= (sizeof (spsStore) / sizeof (*spsStore))) throw new cException("ERROR: H264::cContext::DefineSPS(): id out of range"); SPS.defined = true; spsStore[SPS.seq_parameter_set_id] = SPS; } inline void cContext::Define(cPictureParameterSet &PPS) { if (PPS.pic_parameter_set_id >= (sizeof (ppsStore) / sizeof (*ppsStore))) throw new cException("ERROR: H264::cContext::DefinePPS(): id out of range"); PPS.defined = true; ppsStore[PPS.pic_parameter_set_id] = PPS; } inline void cContext::Define(cSliceHeader &SH) { SH.defined = true; SH.picOrderCntType = ActiveSPS()->pic_order_cnt_type; // ITU-T Rec. H.264 (03/2005): 7.4.1.2.4 SH.isFirstSliceOfCurrentAccessUnit = !sh.Defined() || (sh.frame_num != SH.frame_num) || (sh.pic_parameter_set_id != SH.pic_parameter_set_id) || (sh.field_pic_flag != SH.field_pic_flag) || (sh.bottom_field_flag != SH.bottom_field_flag) || (sh.nalRefIdc != SH.nalRefIdc && (sh.nalRefIdc == 0 || SH.nalRefIdc == 0)) || (sh.picOrderCntType == 0 && SH.picOrderCntType == 0 && (sh.pic_order_cnt_lsb != SH.pic_order_cnt_lsb || sh.delta_pic_order_cnt_bottom != SH.delta_pic_order_cnt_bottom)) || (sh.picOrderCntType == 1 && SH.picOrderCntType == 1 && (sh.delta_pic_order_cnt[0] != SH.delta_pic_order_cnt[0] || sh.delta_pic_order_cnt[1] != SH.delta_pic_order_cnt[1])) || (sh.nalUnitType != SH.nalUnitType && (sh.nalUnitType == 5 || SH.nalUnitType == 5)) || (sh.nalUnitType == 5 && SH.nalUnitType == 5 && sh.idr_pic_id != SH.idr_pic_id); sh = SH; } inline void cContext::Define(cPictureTiming &PT) { PT.defined = true; ((cSequenceParameterSet *)ActiveSPS())->pic_timing_sei = PT; } // --- cSimpleBuffer ------------------------------------------------------- class cSimpleBuffer { private: uchar *data; int size; int avail; int gotten; public: cSimpleBuffer(int Size); ~cSimpleBuffer(); int Size(void) { return size; } int Available(void) { return avail; } int Free(void) { return size - avail; } int Put(const uchar *Data, int Count); uchar *Get(int &Count); void Del(int Count); void Clear(void); }; // --- cParser ------------------------------------------------------------- class cParser { private: bool syncing; bool omitPicTiming; cContext context; cSimpleBuffer nalUnitDataBuffer; void hrd_parameters(cSequenceParameterSet &SPS, cBitReader &br); void ParseSequenceParameterSet(uint8_t *Data, int Count); void ParsePictureParameterSet(uint8_t *Data, int Count); void ParseSlice(uint8_t *Data, int Count); void reserved_sei_message(uint32_t payloadSize, cBitReader &br); void pic_timing(uint32_t payloadSize, cBitReader &br); void buffering_period(uint32_t payloadSize, cBitReader &br); void sei_payload(uint32_t payloadType, uint32_t payloadSize, cBitReader &br); void sei_message(cBitReader &br); void ParseSEI(uint8_t *Data, int Count); public: cParser(bool OmitPicTiming = true); const cContext &Context(void) const { return context; } void PutNalUnitData(const uchar *Data, int Count); void Reset(void); void Process(void); }; } } #endif // __VDR172H264PARSER_H #endif xine-0.9.4/xineSetupPage.h0000644000000000000000000000114110155676234014125 0ustar rootroot #ifndef __XINESETUPPAGE_H #define __XINESETUPPAGE_H #include "xineCommon.h" #include #include "xineSettings.h" namespace PluginXine { class cXineLib; class cXineSetupPage : public cMenuSetupPage { cXineLib *const m_xineLib; cXineSettings &m_globalSettings; cXineSettings m_localSettings; protected: virtual void Store(); public: cXineSetupPage(cXineLib *const xineLib, cXineSettings &settings); virtual ~cXineSetupPage(); virtual eOSState ProcessKey(eKeys Key); friend class cXineSettings; }; }; #endif //__XINESETUPPAGE_H xine-0.9.4/xineplayer.c0000644000000000000000000000417710705223031013513 0ustar rootroot #include "xineCommon.h" #include "xineExternal.h" static int fdControl = -1; static int fdResult = -1; bool writeString(const char *s) { int l = ::strlen(s); while (l) { int r = ::write(fdControl, s, l); if (r < 0) { ::perror("xineplayer: writeString() failed"); return false; } l -= r; s += r; } return true; } bool cmdPlay(char *const mrl) { if (!::writeString("play ")) return false; if (!::writeString(mrl)) return false; if (!::writeString("\n")) return false; return true; } bool waitResult() { char s; int r = ::read(fdResult, &s, 1); if (r < 0) { ::perror("xineplayer: waitResult() failed"); return false; } return (1 == r); } bool communicate(char *const mrl) { if (!::cmdPlay(mrl)) return false; if (!::waitResult()) return false; return true; } #define ARG_VDR_XINE_INSTANCE "--vdr-xine-instance=" int main(int argc, char *argv[]) { if (argc < 2) { usage: ::fprintf(stderr, "usage: xineplayer [ " ARG_VDR_XINE_INSTANCE "N ] [ options ] mrl\n"); return 1; } int instanceNo = -1; if (0 == ::strncmp(argv[ 1 ], ARG_VDR_XINE_INSTANCE, ::strlen(ARG_VDR_XINE_INSTANCE))) { instanceNo = ::atoi(argv[ 1 ] + ::strlen(ARG_VDR_XINE_INSTANCE)); if (instanceNo < 0) goto usage; if (argc < 3) goto usage; } string fifoDir = FIFO_DIR; if (instanceNo >= 0) { char s[ 20 ]; ::sprintf(s, "%d", instanceNo); fifoDir += s; } string fifoNameExtControl = fifoDir + FIFO_NAME_EXT_CONTROL; string fifoNameExtResult = fifoDir + FIFO_NAME_EXT_RESULT; fdResult = ::open(fifoNameExtResult.c_str(), O_RDONLY); if (-1 == fdResult) { ::perror(("xineplayer: opening '" + fifoNameExtResult + "' failed").c_str()); ::close(fdControl); return 1; } fdControl = ::open(fifoNameExtControl.c_str(), O_WRONLY); if (-1 == fdControl) { ::perror(("xineplayer: opening '" + fifoNameExtControl + "' failed").c_str()); return 1; } bool result = ::communicate(argv[ argc - 1 ]); ::close(fdControl); ::close(fdResult); return !result; } xine-0.9.4/COPYING0000644000000000000000000004310610505273226012226 0ustar rootroot GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.