diff -uNr xine-lib.orig/configure.ac xine-lib/configure.ac
--- xine-lib.orig/configure.ac	2008-03-04 20:22:53.000000000 +0100
+++ xine-lib/configure.ac	2008-03-11 19:40:02.000000000 +0100
@@ -2749,6 +2749,7 @@
 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]],[[]])
@@ -2791,7 +2792,7 @@
 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
@@ -2996,6 +2997,7 @@
 echo "   - eq              - eq2"
 echo "   - boxblur         - denoise3d"
 echo "   - unsharp         - tvtime"
+echo "   - vdr"
 echo "  * SFX:"
 echo "   - goom            - oscope"
 echo "   - fftscope        - mosaico"
diff -uNr xine-lib.orig/src/Makefile.am xine-lib/src/Makefile.am
--- xine-lib.orig/src/Makefile.am	2007-04-17 20:46:41.000000000 +0200
+++ xine-lib/src/Makefile.am	2008-03-11 19:40:02.000000000 +0100
@@ -26,4 +26,5 @@
 	libfaad \
         libmusepack \
 	post \
-	combined
+	combined \
+	vdr
diff -uNr xine-lib.orig/src/vdr/combined_vdr.c xine-lib/src/vdr/combined_vdr.c
--- xine-lib.orig/src/vdr/combined_vdr.c	1970-01-01 01:00:00.000000000 +0100
+++ xine-lib/src/vdr/combined_vdr.c	2008-03-11 19:40:02.000000000 +0100
@@ -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 -uNr xine-lib.orig/src/vdr/combined_vdr.h xine-lib/src/vdr/combined_vdr.h
--- xine-lib.orig/src/vdr/combined_vdr.h	1970-01-01 01:00:00.000000000 +0100
+++ xine-lib/src/vdr/combined_vdr.h	2008-03-11 19:40:02.000000000 +0100
@@ -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 -uNr xine-lib.orig/src/vdr/input_vdr.c xine-lib/src/vdr/input_vdr.c
--- xine-lib.orig/src/vdr/input_vdr.c	1970-01-01 01:00:00.000000000 +0100
+++ xine-lib/src/vdr/input_vdr.c	2008-03-11 19:41:31.000000000 +0100
@@ -0,0 +1,2214 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <sys/socket.h>
+#include <resolv.h>
+#include <netdb.h>
+
+#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 "/var/lib/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;
+
+
+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;
+
+  xine_osd_t         *osd_window[ VDR_MAX_NUM_WINDOWS ];
+  uint8_t            *osd_buffer;
+  uint32_t            osd_buffer_size;
+  uint8_t             osd_unscaled_blending;
+
+  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;
+
+  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;
+};
+
+
+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 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)\n", data->x, data->y, data->width, data->height));
+
+      fprintf(stderr, "vdr: osdnew %d\n", data->window);
+*/    
+      if (data->window >= VDR_MAX_NUM_WINDOWS)
+        return -1;
+      
+      if (0 != this->osd_window[ data->window ])
+        return -1;
+      
+      this->osd_window[ data->window ] = xine_osd_new(this->stream
+                                                     , data->x
+                                                     , data->y
+                                                     , data->width
+                                                     , data->height);
+      
+      if (0 == this->osd_window[ data->window ])
+        return -1;
+    }
+    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_window[ data->window ])
+        xine_osd_free(this->osd_window[ data->window ]);
+      
+      this->osd_window[ data->window ] = 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_window[ data->window ])
+      {
+        if (this->osd_unscaled_blending)
+          xine_osd_show_unscaled(this->osd_window[ data->window ], 0);
+        else
+          xine_osd_show(this->osd_window[ data->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_window[ data->window ])
+      {
+        if (this->osd_unscaled_blending)
+          xine_osd_show_unscaled(this->osd_window[ data->window ], 0);
+        else
+          xine_osd_show(this->osd_window[ data->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_window[ data->window ])
+        xine_osd_set_position(this->osd_window[ data->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\n", data->window);    
+*/      
+      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_window[ data->window ])
+        xine_osd_draw_bitmap(this->osd_window[ data->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_window[ data->window ])
+      {
+        uint32_t color[ 256 ];
+        uint8_t trans[ 256 ];
+        
+        xine_osd_get_palette(this->osd_window[ data->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_window[ data->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;
+        vdr_set_video_window_data_t event_data;
+        
+        event_data.x = data->x;
+        event_data.y = data->y;
+        event_data.w = data->w;
+        event_data.h = data->h;
+        event_data.w_ref = data->w_ref;
+        event_data.h_ref = data->h_ref;
+        
+        event.type = XINE_EVENT_VDR_SETVIDEOWINDOW;
+        event.data = &event_data;
+        event.data_length = sizeof (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)
+      {
+fprintf(stderr, "���������������������������������\n");
+        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 = 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:
+    {
+      READ_DATA_OR_FAIL(set_volume, lprintf("got SETVOLUME\n"));
+
+      {
+        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;
+          };
+        }
+        
+        if (change_volume)
+        {
+          report_change = 1;
+          xine_set_param(this->stream, param_volume, this->last_volume);
+        }
+
+        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);
+          
+          event.type        = XINE_EVENT_AUDIO_LEVEL;
+          event.data        = &data;
+          event.data_length = sizeof (data);
+          
+          xine_event_send(this->stream, &event);
+        }
+      }
+    }
+    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;
+      }
+    }
+    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;
+        
+        uint8_t *img   = 0;
+        int frame_size = 0;
+        int width      = 0;
+        int height     = 0;       
+        int ratio_code = 0;
+        int format     = 0;
+ 
+        if (xine_get_current_frame_alloc(this->stream, &width, &height, &ratio_code, &format, &img, &frame_size))
+        {
+          if (ratio_code == XINE_VO_ASPECT_SQUARE)
+            ratio_code = 10000;
+          else if (ratio_code == XINE_VO_ASPECT_4_3)
+            ratio_code = 13333;
+          else if (ratio_code == XINE_VO_ASPECT_ANAMORPHIC)
+            ratio_code = 17778;
+          else if (ratio_code == XINE_VO_ASPECT_DVB)
+            ratio_code = 21100;
+        }
+
+        if (!img)
+        {
+          frame_size = 0,
+          width      = 0;
+          height     = 0;
+          ratio_code = 0;
+        }          
+        
+        {
+          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_size;
+          
+          result_grab_image.width  = width;
+          result_grab_image.height = height;
+          result_grab_image.ratio  = ratio_code;
+          result_grab_image.format = format;
+          
+          if (sizeof (result_grab_image) == vdr_write(this->fh_result, &result_grab_image, sizeof (result_grab_image)))
+          {
+            if (!frame_size || (frame_size == vdr_write(this->fh_result, img, frame_size)))
+              ret_val = 0;
+          }
+        }
+        
+        free(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);
+        
+        result_get_pts.pts = (this->last_disc_type == DISC_STREAMSTART) ? -2 : (xine_get_current_vpts(this->stream) - this->stream->metronom->get_option(this->stream->metronom, METRONOM_VPTS_OFFSET));
+        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;
+    
+  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;
+  
+  while (!failed
+    && !this->rpc_thread_shutdown)
+  {
+    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);
+      }
+    }
+  }
+
+  /* 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 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_NOCAP; /* 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);
+
+  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_window[ i ])
+      continue;
+
+    xine_osd_hide(this->osd_window[ i ], 0);
+    xine_osd_free(this->osd_window[ i ]);
+  }
+
+  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;
+
+  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;
+  int preview_size = (this->preview_size > MAX_PREVIEW_SIZE) ? MAX_PREVIEW_SIZE : this->preview_size;
+  (void)preview_size;
+/*
+  switch (data_type)
+  {
+  case INPUT_OPTIONAL_DATA_PREVIEW:
+    memcpy (data, this->preview, preview_size);
+    return preview_size;
+  }
+*/  
+  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 = strdup (mrl_to_fifo (this->mrl));
+
+  _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 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;
+    }
+
+    this->rpc_thread_shutdown = 0;
+    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;
+  }
+  else 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 = 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);
+    }
+  }
+
+  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 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_got_video_frame(metronom_t *self, vo_frame_t *frame)
+{
+  vdr_metronom_t *this = (vdr_metronom_t *)self;
+  this->stream_metronom->got_video_frame(this->stream_metronom, frame);
+}
+
+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_handle_audio_discontinuity(metronom_t *self, int type, int64_t disc_off)
+{
+  vdr_metronom_t *this = (vdr_metronom_t *)self;
+  this->stream_metronom->handle_audio_discontinuity(this->stream_metronom, type, disc_off);
+  this->input->last_disc_type = type;
+}
+
+static void vdr_metronom_handle_video_discontinuity(metronom_t *self, int type, int64_t disc_off)
+{
+  vdr_metronom_t *this = (vdr_metronom_t *)self;
+  this->stream_metronom->handle_video_discontinuity(this->stream_metronom, type, disc_off);
+  this->input->last_disc_type = type;
+}
+
+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_window, 0, sizeof (this->osd_window));
+
+  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->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;
+
+  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 -uNr xine-lib.orig/src/vdr/Makefile.am xine-lib/src/vdr/Makefile.am
--- xine-lib.orig/src/vdr/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ xine-lib/src/vdr/Makefile.am	2008-03-11 19:40:02.000000000 +0100
@@ -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 -uNr xine-lib.orig/src/vdr/post_vdr_audio.c xine-lib/src/vdr/post_vdr_audio.c
--- xine-lib.orig/src/vdr/post_vdr_audio.c	1970-01-01 01:00:00.000000000 +0100
+++ xine-lib/src/vdr/post_vdr_audio.c	2008-03-11 19:40:02.000000000 +0100
@@ -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 -uNr xine-lib.orig/src/vdr/post_vdr_video.c xine-lib/src/vdr/post_vdr_video.c
--- xine-lib.orig/src/vdr/post_vdr_video.c	1970-01-01 01:00:00.000000000 +0100
+++ xine-lib/src/vdr/post_vdr_video.c	2008-03-11 19:40:02.000000000 +0100
@@ -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 -uNr xine-lib.orig/src/vdr/vdr.h xine-lib/src/vdr/vdr.h
--- xine-lib.orig/src/vdr/vdr.h	1970-01-01 01:00:00.000000000 +0100
+++ xine-lib/src/vdr/vdr.h	2008-03-11 19:40:02.000000000 +0100
@@ -0,0 +1,617 @@
+/*
+ * 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 802
+
+
+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
+};
+
+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;
+}
+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;
+}
+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;
+}
+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;
+}
+result_grab_image_t;
+
+
+
+typedef struct __attribute__((packed)) data_get_pts_s
+{
+  data_header_t header;
+}
+data_get_pts_t;
+
+
+
+typedef struct __attribute__((packed)) result_get_pts_s
+{
+  result_header_t header;
+
+  int64_t pts;
+}
+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 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_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_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_union_t;
+
+
+
+#endif /* __VDR_H */
+
diff -uNr xine-lib.orig/src/video_out/video_out_xvmc.c xine-lib/src/video_out/video_out_xvmc.c
--- xine-lib.orig/src/video_out/video_out_xvmc.c	2008-01-02 20:19:39.000000000 +0100
+++ xine-lib/src/video_out/video_out_xvmc.c	2008-03-11 19:40:02.000000000 +0100
@@ -487,9 +487,9 @@
 				     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");
@@ -562,6 +562,7 @@
     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 -uNr xine-lib.orig/src/video_out/video_out_xxmc.c xine-lib/src/video_out/video_out_xxmc.c
--- xine-lib.orig/src/video_out/video_out_xxmc.c	2007-12-27 19:58:53.000000000 +0100
+++ xine-lib/src/video_out/video_out_xxmc.c	2008-03-11 19:40:02.000000000 +0100
@@ -368,15 +368,15 @@
 
   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;
   }
@@ -407,7 +407,7 @@
 {
 
   xxmc_frame_t
-    *frame = (xxmc_frame_t *) this_gen;
+    *frame = XXMC_FRAME(this_gen);
   xxmc_driver_t
     *driver = (xxmc_driver_t *) this_gen->driver;
 
@@ -455,6 +455,7 @@
     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;
@@ -569,6 +570,7 @@
   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");
@@ -1216,10 +1218,17 @@
 				 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) ||
@@ -1235,7 +1244,7 @@
     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) {
@@ -1249,6 +1258,33 @@
 
     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) {
@@ -1256,7 +1292,7 @@
       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 -uNr xine-lib.orig/src/video_out/xvmc_mocomp.c xine-lib/src/video_out/xvmc_mocomp.c
--- xine-lib.orig/src/video_out/xvmc_mocomp.c	2007-11-11 00:31:04.000000000 +0100
+++ xine-lib/src/video_out/xvmc_mocomp.c	2008-03-11 19:40:02.000000000 +0100
@@ -70,9 +70,9 @@
 				     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 -uNr xine-lib.orig/src/video_out/xvmc_vld.c xine-lib/src/video_out/xvmc_vld.c
--- xine-lib.orig/src/video_out/xvmc_vld.c	2007-11-11 00:31:04.000000000 +0100
+++ xine-lib/src/video_out/xvmc_vld.c	2008-03-11 19:40:02.000000000 +0100
@@ -32,12 +32,12 @@
 {
   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) 
 {
   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 -uNr xine-lib.orig/src/xine-engine/accel_xvmc.h xine-lib/src/xine-engine/accel_xvmc.h
--- xine-lib.orig/src/xine-engine/accel_xvmc.h	2007-11-11 00:31:04.000000000 +0100
+++ xine-lib/src/xine-engine/accel_xvmc.h	2008-03-11 19:40:03.000000000 +0100
@@ -65,6 +65,7 @@
 
 
 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],
@@ -74,6 +75,9 @@
 			   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 @@
   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 -uNr xine-lib.orig/src/xine-engine/post.c xine-lib/src/xine-engine/post.c
--- xine-lib.orig/src/xine-engine/post.c	2007-11-11 00:31:04.000000000 +0100
+++ xine-lib/src/xine-engine/post.c	2008-03-11 19:40:03.000000000 +0100
@@ -147,6 +147,14 @@
   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;
@@ -190,6 +198,7 @@
   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,
@@ -200,6 +209,7 @@
   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;
 }
@@ -221,6 +231,7 @@
   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;
@@ -380,10 +391,11 @@
     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)
@@ -700,6 +712,7 @@
   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,
@@ -710,6 +723,7 @@
   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 -uNr xine-lib.orig/src/xine-engine/post.h xine-lib/src/xine-engine/post.h
--- xine-lib.orig/src/xine-engine/post.h	2007-11-11 00:31:04.000000000 +0100
+++ xine-lib/src/xine-engine/post.h	2008-03-11 19:40:03.000000000 +0100
@@ -177,6 +177,13 @@
   /* 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 -uNr xine-lib.orig/src/xine-engine/video_out.c xine-lib/src/xine-engine/video_out.c
--- xine-lib.orig/src/xine-engine/video_out.c	2008-01-02 20:19:40.000000000 +0100
+++ xine-lib/src/xine-engine/video_out.c	2008-03-11 19:40:03.000000000 +0100
@@ -132,6 +132,9 @@
   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 @@
     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 @@
     }
     
     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 @@
 		"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 @@
   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);
 }
 
@@ -1695,6 +1730,15 @@
   }
 }
 
+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
  * while cropping. maybe someday this will be an automatic post plugin.
  */
@@ -1790,6 +1834,7 @@
   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 @@
       "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 -uNr xine-lib.orig/src/xine-engine/video_out.h xine-lib/src/xine-engine/video_out.h
--- xine-lib.orig/src/xine-engine/video_out.h	2007-12-27 19:58:53.000000000 +0100
+++ xine-lib/src/xine-engine/video_out.h	2008-03-11 19:40:03.000000000 +0100
@@ -202,6 +202,9 @@
   /* 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 -uNr xine-lib.orig/src/xine-engine/video_overlay.h xine-lib/src/xine-engine/video_overlay.h
--- xine-lib.orig/src/xine-engine/video_overlay.h	2007-11-11 00:31:04.000000000 +0100
+++ xine-lib/src/xine-engine/video_overlay.h	2008-03-11 19:40:03.000000000 +0100
@@ -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 -uNr xine-lib.orig/src/xine-engine/xine.c xine-lib/src/xine-engine/xine.c
--- xine-lib.orig/src/xine-engine/xine.c	2008-03-10 19:35:33.000000000 +0100
+++ xine-lib/src/xine-engine/xine.c	2008-03-11 19:40:03.000000000 +0100
@@ -294,8 +294,37 @@
     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 @@
   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);
   
@@ -515,6 +547,7 @@
   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)) {
@@ -525,6 +558,7 @@
   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;
 }
@@ -539,6 +573,7 @@
   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)) {
@@ -549,6 +584,7 @@
   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;
 }
@@ -2254,3 +2290,83 @@
 
   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 -uNr xine-lib.orig/src/xine-engine/xine_internal.h xine-lib/src/xine-engine/xine_internal.h
--- xine-lib.orig/src/xine-engine/xine_internal.h	2007-11-11 00:31:04.000000000 +0100
+++ xine-lib/src/xine-engine/xine_internal.h	2008-03-11 19:40:03.000000000 +0100
@@ -166,6 +166,9 @@
    * 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;
@@ -182,6 +185,7 @@
     pthread_t holder;
   } *holder_threads;
   unsigned        holder_thread_count;
+  pthread_mutex_t port_rewiring_lock;
 #endif
 };
 
@@ -370,6 +374,15 @@
  */
 
 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;
 
